mirror of
https://github.com/ankidroid/Anki-Android.git
synced 2024-09-20 12:02:16 +02:00
[Kotlin Migration] CustomTabActivityHelper
This commit is contained in:
parent
3359678588
commit
1ad753659d
@ -43,7 +43,7 @@ permission notice:
|
||||
// Example of class name: "/com/ichi2/anki/UIUtils.kt"
|
||||
// Ensure that it starts with '/' (slash)
|
||||
def source = Source.MAIN
|
||||
def className = "/com/ichi2/compat/customtabs/CustomTabActivityHelper.kt"
|
||||
def className = ""
|
||||
|
||||
enum Source {
|
||||
MAIN("/src/main/java"),
|
||||
|
@ -12,77 +12,38 @@
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
package com.ichi2.compat.customtabs
|
||||
|
||||
package com.ichi2.compat.customtabs;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
|
||||
import androidx.annotation.CheckResult;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.browser.customtabs.CustomTabsClient;
|
||||
import androidx.browser.customtabs.CustomTabsIntent;
|
||||
import androidx.browser.customtabs.CustomTabsServiceConnection;
|
||||
import androidx.browser.customtabs.CustomTabsSession;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import timber.log.Timber;
|
||||
|
||||
import android.app.Activity
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import androidx.annotation.CheckResult
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.browser.customtabs.CustomTabsClient
|
||||
import androidx.browser.customtabs.CustomTabsIntent
|
||||
import androidx.browser.customtabs.CustomTabsServiceConnection
|
||||
import androidx.browser.customtabs.CustomTabsSession
|
||||
import com.ichi2.utils.KotlinCleanup
|
||||
import timber.log.Timber
|
||||
|
||||
/**
|
||||
* This is a helper class to manage the connection to the Custom Tabs Service.
|
||||
*/
|
||||
public class CustomTabActivityHelper implements ServiceConnectionCallback {
|
||||
private static boolean sCustomTabsFailed = false;
|
||||
@Nullable
|
||||
private CustomTabsSession mCustomTabsSession;
|
||||
@Nullable
|
||||
private CustomTabsClient mClient;
|
||||
@Nullable
|
||||
private CustomTabsServiceConnection mConnection;
|
||||
|
||||
/**
|
||||
* Opens the URL on a Custom Tab if possible. Otherwise fallsback to opening it on a WebView.
|
||||
*
|
||||
* @param activity The host activity.
|
||||
* @param customTabsIntent a CustomTabsIntent to be used if Custom Tabs is available.
|
||||
* @param uri the Uri to be opened.
|
||||
* @param fallback a CustomTabFallback to be used if Custom Tabs is not available.
|
||||
*/
|
||||
public static void openCustomTab(@NonNull Activity activity,
|
||||
CustomTabsIntent customTabsIntent,
|
||||
Uri uri,
|
||||
CustomTabFallback fallback) {
|
||||
String packageName = CustomTabsHelper.getPackageNameToUse(activity);
|
||||
|
||||
//If we cant find a package name or there was a serious failure during init, we don't support
|
||||
//Chrome Custom Tabs. So, we fallback to the webview
|
||||
if (packageName == null || sCustomTabsFailed) {
|
||||
if (fallback != null) {
|
||||
fallback.openUri(activity, uri);
|
||||
} else {
|
||||
Timber.e("A version of Chrome supporting custom tabs was not available, and the fallback was null");
|
||||
}
|
||||
} else {
|
||||
customTabsIntent.intent.setPackage(packageName);
|
||||
customTabsIntent.launchUrl(activity, uri);
|
||||
}
|
||||
}
|
||||
class CustomTabActivityHelper : ServiceConnectionCallback {
|
||||
private var mCustomTabsSession: CustomTabsSession? = null
|
||||
private var mClient: CustomTabsClient? = null
|
||||
private var mConnection: CustomTabsServiceConnection? = null
|
||||
|
||||
/**
|
||||
* Unbinds the Activity from the Custom Tabs Service.
|
||||
* @param activity the activity that is connected to the service.
|
||||
*/
|
||||
public void unbindCustomTabsService(Activity activity) {
|
||||
if (mConnection == null) return;
|
||||
activity.unbindService(mConnection);
|
||||
mClient = null;
|
||||
mCustomTabsSession = null;
|
||||
mConnection = null;
|
||||
fun unbindCustomTabsService(activity: Activity) {
|
||||
if (mConnection == null) return
|
||||
mConnection.let { activity.unbindService(it!!) }
|
||||
mClient = null
|
||||
mCustomTabsSession = null
|
||||
mConnection = null
|
||||
}
|
||||
|
||||
/**
|
||||
@ -90,103 +51,132 @@ public class CustomTabActivityHelper implements ServiceConnectionCallback {
|
||||
*
|
||||
* @return a CustomTabsSession.
|
||||
*/
|
||||
public CustomTabsSession getSession() {
|
||||
if (mClient == null) {
|
||||
mCustomTabsSession = null;
|
||||
} else if (mCustomTabsSession == null) {
|
||||
mCustomTabsSession = mClient.newSession(null);
|
||||
val session: CustomTabsSession?
|
||||
get() {
|
||||
if (mClient == null) {
|
||||
mCustomTabsSession = null
|
||||
} else if (mCustomTabsSession == null) {
|
||||
mCustomTabsSession = mClient!!.newSession(null)
|
||||
}
|
||||
return mCustomTabsSession
|
||||
}
|
||||
return mCustomTabsSession;
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds the Activity to the Custom Tabs Service.
|
||||
* @param activity the activity to be binded to the service.
|
||||
*/
|
||||
public void bindCustomTabsService(Activity activity) {
|
||||
if (mClient != null) return;
|
||||
|
||||
String packageName = CustomTabsHelper.getPackageNameToUse(activity);
|
||||
if (packageName == null) return;
|
||||
|
||||
mConnection = new ServiceConnection(this);
|
||||
@KotlinCleanup("make activity nonnull")
|
||||
fun bindCustomTabsService(activity: Activity?) {
|
||||
if (mClient != null) return
|
||||
val packageName = CustomTabsHelper.getPackageNameToUse(activity) ?: return
|
||||
mConnection = ServiceConnection(this)
|
||||
try {
|
||||
CustomTabsClient.bindCustomTabsService(activity, packageName, mConnection);
|
||||
} catch (SecurityException e) {
|
||||
Timber.w(e, "CustomTabsService bind attempt failed, using fallback");
|
||||
disableCustomTabHandler();
|
||||
CustomTabsClient.bindCustomTabsService(activity!!, packageName, mConnection!!)
|
||||
} catch (e: SecurityException) {
|
||||
Timber.w(e, "CustomTabsService bind attempt failed, using fallback")
|
||||
disableCustomTabHandler()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private void disableCustomTabHandler() {
|
||||
Timber.i("Disabling custom tab handler and using fallback");
|
||||
sCustomTabsFailed = true;
|
||||
mClient = null;
|
||||
mCustomTabsSession = null;
|
||||
mConnection = null;
|
||||
private fun disableCustomTabHandler() {
|
||||
Timber.i("Disabling custom tab handler and using fallback")
|
||||
sCustomTabsFailed = true
|
||||
mClient = null
|
||||
mCustomTabsSession = null
|
||||
mConnection = null
|
||||
}
|
||||
|
||||
/**
|
||||
* @see {@link CustomTabsSession#mayLaunchUrl(Uri, Bundle, List)}.
|
||||
* @return true if call to mayLaunchUrl was accepted.
|
||||
*/
|
||||
public boolean mayLaunchUrl(Uri uri, Bundle extras, List<Bundle> otherLikelyBundles) {
|
||||
if (mClient == null) return false;
|
||||
|
||||
CustomTabsSession session = getSession();
|
||||
if (session == null) return false;
|
||||
|
||||
return session.mayLaunchUrl(uri, extras, otherLikelyBundles);
|
||||
fun mayLaunchUrl(uri: Uri?, extras: Bundle?, otherLikelyBundles: List<Bundle?>?): Boolean {
|
||||
if (mClient == null) return false
|
||||
val session = session ?: return false
|
||||
return session.mayLaunchUrl(uri, extras, otherLikelyBundles)
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onServiceConnected(CustomTabsClient client) {
|
||||
override fun onServiceConnected(client: CustomTabsClient) {
|
||||
try {
|
||||
mClient = client;
|
||||
mClient = client
|
||||
try {
|
||||
mClient.warmup(0L);
|
||||
} catch (IllegalStateException e) {
|
||||
mClient!!.warmup(0L)
|
||||
} catch (e: IllegalStateException) {
|
||||
// Issue 5337 - some browsers like TorBrowser don't adhere to Android 8 background limits
|
||||
// They will crash as they attempt to start services. warmup failure shouldn't be fatal though.
|
||||
Timber.w(e, "Ignoring CustomTabs implementation that doesn't conform to Android 8 background limits");
|
||||
Timber.w(e, "Ignoring CustomTabs implementation that doesn't conform to Android 8 background limits")
|
||||
}
|
||||
getSession();
|
||||
} catch (SecurityException e) {
|
||||
//#6142 - A securityException here means that we're not able to load the CustomTabClient at all, whereas
|
||||
//the IllegalStateException was a failure, but could be continued from
|
||||
Timber.w(e, "CustomTabsService bind attempt failed, using fallback");
|
||||
disableCustomTabHandler();
|
||||
session
|
||||
} catch (e: SecurityException) {
|
||||
// #6142 - A securityException here means that we're not able to load the CustomTabClient at all, whereas
|
||||
// the IllegalStateException was a failure, but could be continued from
|
||||
Timber.w(e, "CustomTabsService bind attempt failed, using fallback")
|
||||
disableCustomTabHandler()
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onServiceDisconnected() {
|
||||
mClient = null;
|
||||
mCustomTabsSession = null;
|
||||
override fun onServiceDisconnected() {
|
||||
mClient = null
|
||||
mCustomTabsSession = null
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* To be used as a fallback to open the Uri when Custom Tabs is not available.
|
||||
*/
|
||||
public interface CustomTabFallback {
|
||||
interface CustomTabFallback {
|
||||
/**
|
||||
*
|
||||
* @param activity The Activity that wants to open the Uri.
|
||||
* @param uri The uri to be opened by the fallback.
|
||||
*/
|
||||
void openUri(Activity activity, Uri uri);
|
||||
@KotlinCleanup("make activity nonnull")
|
||||
fun openUri(activity: Activity?, uri: Uri?)
|
||||
}
|
||||
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.NONE) @CheckResult
|
||||
boolean isFailed() {
|
||||
return sCustomTabsFailed && mClient == null;
|
||||
}
|
||||
@get:CheckResult
|
||||
@get:VisibleForTesting(otherwise = VisibleForTesting.NONE)
|
||||
val isFailed: Boolean
|
||||
get() = sCustomTabsFailed && mClient == null
|
||||
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
|
||||
public static void resetFailed() {
|
||||
sCustomTabsFailed = false;
|
||||
companion object {
|
||||
private var sCustomTabsFailed = false
|
||||
|
||||
/**
|
||||
* Opens the URL on a Custom Tab if possible. Otherwise fallsback to opening it on a WebView.
|
||||
*
|
||||
* @param activity The host activity.
|
||||
* @param customTabsIntent a CustomTabsIntent to be used if Custom Tabs is available.
|
||||
* @param uri the Uri to be opened.
|
||||
* @param fallback a CustomTabFallback to be used if Custom Tabs is not available.
|
||||
*/
|
||||
@KotlinCleanup("Consider nullability")
|
||||
@JvmStatic
|
||||
fun openCustomTab(
|
||||
activity: Activity,
|
||||
customTabsIntent: CustomTabsIntent?,
|
||||
uri: Uri?,
|
||||
fallback: CustomTabFallback?
|
||||
) {
|
||||
val packageName = CustomTabsHelper.getPackageNameToUse(activity)
|
||||
|
||||
// If we cant find a package name or there was a serious failure during init, we don't support
|
||||
// Chrome Custom Tabs. So, we fallback to the webview
|
||||
if (packageName == null || sCustomTabsFailed) {
|
||||
if (fallback != null) {
|
||||
fallback.openUri(activity, uri)
|
||||
} else {
|
||||
Timber.e("A version of Chrome supporting custom tabs was not available, and the fallback was null")
|
||||
}
|
||||
} else {
|
||||
customTabsIntent!!.intent.setPackage(packageName)
|
||||
customTabsIntent.launchUrl(activity, uri!!)
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
|
||||
fun resetFailed() {
|
||||
sCustomTabsFailed = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user