1
0
Fork 0
mirror of https://github.com/gsantner/dandelion synced 2024-12-22 19:08:03 +01:00

Use customTabs to open external links. This is a very basic implementation

This commit is contained in:
vanitasvitae 2016-09-11 12:35:16 +02:00
parent 0b0b0198df
commit ae2cefd6a5
6 changed files with 331 additions and 4 deletions

View file

@ -48,6 +48,7 @@ dependencies {
compile 'com.jakewharton:butterknife:8.0.1'
compile 'info.guardianproject.netcipher:netcipher:2.0.0-alpha1'
compile 'info.guardianproject.netcipher:netcipher-webkit:2.0.0-alpha1'
compile "com.android.support:customtabs:24.2.0"
apt 'com.jakewharton:butterknife-compiler:8.0.1'
}

View file

@ -41,6 +41,7 @@ import android.os.Handler;
import android.os.StrictMode;
import android.provider.MediaStore;
import android.support.annotation.NonNull;
import android.support.customtabs.CustomTabsIntent;
import android.support.design.widget.AppBarLayout;
import android.support.design.widget.NavigationView;
import android.support.design.widget.Snackbar;
@ -79,6 +80,8 @@ import com.github.dfa.diaspora_android.data.PodUserProfile;
import com.github.dfa.diaspora_android.listener.WebUserProfileChangedListener;
import com.github.dfa.diaspora_android.ui.ContextMenuWebView;
import com.github.dfa.diaspora_android.ui.CustomWebViewClient;
import com.github.dfa.diaspora_android.util.CustomTabHelpers.BrowserFallback;
import com.github.dfa.diaspora_android.util.CustomTabHelpers.CustomTabActivityHelper;
import com.github.dfa.diaspora_android.util.DiasporaUrlHelper;
import com.github.dfa.diaspora_android.util.Helpers;
import com.github.dfa.diaspora_android.util.Log;
@ -111,6 +114,7 @@ public class MainActivity extends AppCompatActivity
public static final int REQUEST_CODE__ACCESS_EXTERNAL_STORAGE = 124;
public static final String ACTION_OPEN_URL = "com.github.dfa.diaspora_android.MainActivity.open_url";
public static final String ACTION_OPEN_EXTERNAL_URL = "com.github.dfa.diaspora_android.MainActivity.open_external_url";
public static final String ACTION_CHANGE_ACCOUNT = "com.github.dfa.diaspora_android.MainActivity.change_account";
public static final String ACTION_CLEAR_CACHE = "com.github.dfa.diaspora_android.MainActivity.clear_cache";
public static final String ACTION_UPDATE_TITLE_FROM_URL = "com.github.dfa.diaspora_android.MainActivity.set_title";
@ -123,6 +127,7 @@ public class MainActivity extends AppCompatActivity
private ValueCallback<Uri[]> imageUploadFilePathCallbackNew;
private ValueCallback<Uri> imageUploadFilePathCallbackOld;
private String mCameraPhotoPath;
private CustomTabActivityHelper customTabActivityHelper;
private WebSettings webSettings;
private AppSettings appSettings;
private DiasporaUrlHelper urls;
@ -186,6 +191,7 @@ public class MainActivity extends AppCompatActivity
podUserProfile.setCallbackHandler(uiHandler);
podUserProfile.setListener(this);
urls = new DiasporaUrlHelper(appSettings);
customTabActivityHelper = new CustomTabActivityHelper();
if (appSettings.isProxyEnabled()) {
if (!setProxy(appSettings.getProxyHost(), appSettings.getProxyPort())) {
@ -203,7 +209,7 @@ public class MainActivity extends AppCompatActivity
Log.i(App.TAG, "MainActivity.setupUI()");
boolean newWebView = (webView == null);
if(newWebView) {
Log.v(App.TAG, "Webview was null. Create new one.");
Log.v(App.TAG, "WebView was null. Create new one.");
View webviewHolder = getLayoutInflater().inflate(R.layout.webview, this.contentLayout, false);
webView = (ContextMenuWebView) webviewHolder.findViewById(R.id.webView);
((LinearLayout)webView.getParent()).removeView(webView);
@ -727,11 +733,40 @@ public class MainActivity extends AppCompatActivity
}
};
private final BroadcastReceiver brOpenExternalLink = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String url = intent.getStringExtra(EXTRA_URL);
if(url != null) {
CustomTabsIntent.Builder intentBuilder = new CustomTabsIntent.Builder();
if(Build.VERSION.SDK_INT >= 23) {
intentBuilder.setToolbarColor(getResources().getColor(R.color.colorPrimary, getTheme()));
} else {
intentBuilder.setToolbarColor(getResources().getColor(R.color.colorPrimary));
}
CustomTabActivityHelper.openCustomTab(MainActivity.this, intentBuilder.build(), Uri.parse(url), new BrowserFallback());
}
}
};
@Override
protected void onStart() {
super.onStart();
customTabActivityHelper.bindCustomTabsService(this);
}
@Override
protected void onStop() {
super.onStop();
customTabActivityHelper.unbindCustomTabsService(this);
}
@Override
protected void onPause() {
Log.v(App.TAG, "MainActivity.onPause()");
Log.v(App.TAG, "Unregister BroadcastReceivers");
LocalBroadcastManager.getInstance(this).unregisterReceiver(brSetTitle);
LocalBroadcastManager.getInstance(this).unregisterReceiver(brOpenExternalLink);
super.onPause();
}
@ -741,6 +776,7 @@ public class MainActivity extends AppCompatActivity
super.onResume();
Log.v(App.TAG, "Register BroadcastReceivers");
LocalBroadcastManager.getInstance(this).registerReceiver(brSetTitle, new IntentFilter(ACTION_UPDATE_TITLE_FROM_URL));
LocalBroadcastManager.getInstance(this).registerReceiver(brOpenExternalLink, new IntentFilter(ACTION_OPEN_EXTERNAL_URL));
}
@Override

View file

@ -20,11 +20,13 @@ package com.github.dfa.diaspora_android.ui;
import android.content.Intent;
import android.net.Uri;
import android.support.v4.content.LocalBroadcastManager;
import android.webkit.CookieManager;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import com.github.dfa.diaspora_android.App;
import com.github.dfa.diaspora_android.activity.MainActivity;
public class CustomWebViewClient extends WebViewClient {
private final App app;
@ -37,9 +39,9 @@ public class CustomWebViewClient extends WebViewClient {
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (!url.contains(app.getSettings().getPodDomain())) {
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
app.getApplicationContext().startActivity(i);
Intent i = new Intent(MainActivity.ACTION_OPEN_EXTERNAL_URL);
i.putExtra(MainActivity.EXTRA_URL, url);
LocalBroadcastManager.getInstance(app.getApplicationContext()).sendBroadcast(i);
return true;
}
return false;

View file

@ -0,0 +1,18 @@
package com.github.dfa.diaspora_android.util.CustomTabHelpers;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
/**
* Adapted from https://medium.com/ribot-labs/exploring-chrome-customs-tabs-on-android-ef427effe2f4
*/
public class BrowserFallback implements CustomTabActivityHelper.CustomTabFallback {
@Override
public void openUri(Activity activity, Uri uri) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(uri);
activity.startActivity(intent);
}
}

View file

@ -0,0 +1,149 @@
package com.github.dfa.diaspora_android.util.CustomTabHelpers;
import android.app.Activity;
import android.content.ComponentName;
import android.net.Uri;
import android.os.Bundle;
import android.support.customtabs.CustomTabsClient;
import android.support.customtabs.CustomTabsIntent;
import android.support.customtabs.CustomTabsServiceConnection;
import android.support.customtabs.CustomTabsSession;
import android.util.Log;
import java.util.List;
/**
* Adapted from https://medium.com/ribot-labs/exploring-chrome-customs-tabs-on-android-ef427effe2f4
*/
public class CustomTabActivityHelper {
private CustomTabsSession mCustomTabsSession;
private CustomTabsClient mClient;
private CustomTabsServiceConnection mConnection;
private ConnectionCallback mConnectionCallback;
/**
* 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(Activity activity,
CustomTabsIntent customTabsIntent,
Uri uri,
CustomTabFallback fallback) {
String packageName = CustomTabsHelper.getPackageNameToUse(activity);
//If we cant find a package name, it means there's no browser that supports
//Chrome Custom Tabs installed. So, we fallback to the webview
if (packageName == null) {
if (fallback != null) {
fallback.openUri(activity, uri);
}
} else {
customTabsIntent.intent.setPackage(packageName);
customTabsIntent.launchUrl(activity, uri);
}
}
/**
* 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;
}
/**
* Creates or retrieves an exiting CustomTabsSession
*
* @return a CustomTabsSession
*/
public CustomTabsSession getSession() {
if (mClient == null) {
mCustomTabsSession = null;
} else if (mCustomTabsSession == null) {
mCustomTabsSession = mClient.newSession(null);
}
return mCustomTabsSession;
}
/**
* Register a Callback to be called when connected or disconnected from the Custom Tabs Service
* @param connectionCallback
*/
public void setConnectionCallback(ConnectionCallback connectionCallback) {
this.mConnectionCallback = connectionCallback;
}
/**
* 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 CustomTabsServiceConnection() {
@Override
public void onCustomTabsServiceConnected(ComponentName name, CustomTabsClient client) {
mClient = client;
mClient.warmup(0L);
if (mConnectionCallback != null) mConnectionCallback.onCustomTabsConnected();
//Initialize a session as soon as possible.
getSession();
}
@Override
public void onServiceDisconnected(ComponentName name) {
mClient = null;
if (mConnectionCallback != null) mConnectionCallback.onCustomTabsDisconnected();
}
};
CustomTabsClient.bindCustomTabsService(activity, packageName, mConnection);
}
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);
}
/**
* A Callback for when the service is connected or disconnected. Use those callbacks to
* handle UI changes when the service is connected or disconnected
*/
public interface ConnectionCallback {
/**
* Called when the service is connected
*/
void onCustomTabsConnected();
/**
* Called when the service is disconnected
*/
void onCustomTabsDisconnected();
}
/**
* To be used as a fallback to open the Uri when Custom Tabs is not available
*/
public 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);
}
}

View file

@ -0,0 +1,121 @@
package com.github.dfa.diaspora_android.util.CustomTabHelpers;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.net.Uri;
import android.support.customtabs.CustomTabsService;
import android.text.TextUtils;
import android.util.Log;
import java.util.ArrayList;
import java.util.List;
/**
* Helper class for Custom Tabs. Adapted from https://medium.com/ribot-labs/exploring-chrome-customs-tabs-on-android-ef427effe2f4
*/
public class CustomTabsHelper {
private static final String TAG = "CustomTabsHelper";
static final String STABLE_PACKAGE = "com.android.chrome";
static final String BETA_PACKAGE = "com.chrome.beta";
static final String DEV_PACKAGE = "com.chrome.dev";
static final String LOCAL_PACKAGE = "com.google.android.apps.chrome";
private static final String EXTRA_CUSTOM_TABS_KEEP_ALIVE =
"android.support.customtabs.extra.KEEP_ALIVE";
private static String sPackageNameToUse;
private CustomTabsHelper() {}
/**
* Goes through all apps that handle VIEW intents and have a warmup service. Picks
* the one chosen by the user if there is one, otherwise makes a best effort to return a
* valid package name.
*
* This is <strong>not</strong> threadsafe.
*
* @param context {@link Context} to use for accessing {@link PackageManager}.
* @return The package name recommended to use for connecting to custom tabs related components.
*/
public static String getPackageNameToUse(Context context) {
if (sPackageNameToUse != null) return sPackageNameToUse;
PackageManager pm = context.getPackageManager();
// Get default VIEW intent handler.
Intent activityIntent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://www.example.com"));
ResolveInfo defaultViewHandlerInfo = pm.resolveActivity(activityIntent, 0);
String defaultViewHandlerPackageName = null;
if (defaultViewHandlerInfo != null) {
defaultViewHandlerPackageName = defaultViewHandlerInfo.activityInfo.packageName;
}
// Get all apps that can handle VIEW intents.
List<ResolveInfo> resolvedActivityList = pm.queryIntentActivities(activityIntent, 0);
List<String> packagesSupportingCustomTabs = new ArrayList<>();
for (ResolveInfo info : resolvedActivityList) {
Intent serviceIntent = new Intent();
serviceIntent.setAction(CustomTabsService.ACTION_CUSTOM_TABS_CONNECTION);
serviceIntent.setPackage(info.activityInfo.packageName);
if (pm.resolveService(serviceIntent, 0) != null) {
packagesSupportingCustomTabs.add(info.activityInfo.packageName);
}
}
// Now packagesSupportingCustomTabs contains all apps that can handle both VIEW intents
// and service calls.
if (packagesSupportingCustomTabs.isEmpty()) {
sPackageNameToUse = null;
} else if (packagesSupportingCustomTabs.size() == 1) {
sPackageNameToUse = packagesSupportingCustomTabs.get(0);
} else if (!TextUtils.isEmpty(defaultViewHandlerPackageName)
&& !hasSpecializedHandlerIntents(context, activityIntent)
&& packagesSupportingCustomTabs.contains(defaultViewHandlerPackageName)) {
sPackageNameToUse = defaultViewHandlerPackageName;
} else if (packagesSupportingCustomTabs.contains(STABLE_PACKAGE)) {
sPackageNameToUse = STABLE_PACKAGE;
} else if (packagesSupportingCustomTabs.contains(BETA_PACKAGE)) {
sPackageNameToUse = BETA_PACKAGE;
} else if (packagesSupportingCustomTabs.contains(DEV_PACKAGE)) {
sPackageNameToUse = DEV_PACKAGE;
} else if (packagesSupportingCustomTabs.contains(LOCAL_PACKAGE)) {
sPackageNameToUse = LOCAL_PACKAGE;
}
return sPackageNameToUse;
}
/**
* Used to check whether there is a specialized handler for a given intent.
* @param intent The intent to check with.
* @return Whether there is a specialized handler for the given intent.
*/
private static boolean hasSpecializedHandlerIntents(Context context, Intent intent) {
try {
PackageManager pm = context.getPackageManager();
List<ResolveInfo> handlers = pm.queryIntentActivities(
intent,
PackageManager.GET_RESOLVED_FILTER);
if (handlers == null || handlers.size() == 0) {
return false;
}
for (ResolveInfo resolveInfo : handlers) {
IntentFilter filter = resolveInfo.filter;
if (filter == null) continue;
if (filter.countDataAuthorities() == 0 || filter.countDataPaths() == 0) continue;
if (resolveInfo.activityInfo == null) continue;
return true;
}
} catch (RuntimeException e) {
Log.e(TAG, "Runtime exception while getting specialized handlers");
}
return false;
}
/**
* @return All possible chrome package names that provide custom tabs feature.
*/
public static String[] getPackages() {
return new String[]{"", STABLE_PACKAGE, BETA_PACKAGE, DEV_PACKAGE, LOCAL_PACKAGE};
}
}