mirror of
https://github.com/gsantner/dandelion
synced 2024-11-25 13:52:08 +01:00
Merge pull request #67 from Diaspora-for-Android/customTabs
Custom tabs
This commit is contained in:
commit
ef0e1b792a
9 changed files with 481 additions and 58 deletions
|
@ -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'
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,8 @@ import android.app.AlarmManager;
|
|||
import android.app.AlertDialog;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.graphics.drawable.LayerDrawable;
|
||||
import android.support.v4.view.MenuItemCompat;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
|
@ -33,7 +35,7 @@ import android.content.IntentFilter;
|
|||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Configuration;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.LayerDrawable;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
|
@ -42,12 +44,12 @@ 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;
|
||||
import android.support.v4.content.LocalBroadcastManager;
|
||||
import android.support.v4.view.GravityCompat;
|
||||
import android.support.v4.view.MenuItemCompat;
|
||||
import android.support.v4.widget.DrawerLayout;
|
||||
import android.support.v7.app.ActionBarDrawerToggle;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
|
@ -79,9 +81,13 @@ import com.github.dfa.diaspora_android.R;
|
|||
import com.github.dfa.diaspora_android.data.AppSettings;
|
||||
import com.github.dfa.diaspora_android.data.PodUserProfile;
|
||||
import com.github.dfa.diaspora_android.listener.WebUserProfileChangedListener;
|
||||
import com.github.dfa.diaspora_android.receivers.OpenExternalLinkReceiver;
|
||||
import com.github.dfa.diaspora_android.receivers.UpdateTitleReceiver;
|
||||
import com.github.dfa.diaspora_android.ui.BadgeDrawable;
|
||||
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;
|
||||
|
@ -114,6 +120,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";
|
||||
|
@ -126,12 +133,15 @@ 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;
|
||||
private PodUserProfile podUserProfile;
|
||||
private final Handler uiHandler = new Handler();
|
||||
private CustomWebViewClient webViewClient;
|
||||
private OpenExternalLinkReceiver brOpenExternalLink;
|
||||
private BroadcastReceiver brSetTitle;
|
||||
private Snackbar snackbarExitApp;
|
||||
private Snackbar snackbarNewNotification;
|
||||
private Snackbar snackbarNoInternet;
|
||||
|
@ -189,6 +199,7 @@ public class MainActivity extends AppCompatActivity
|
|||
podUserProfile.setCallbackHandler(uiHandler);
|
||||
podUserProfile.setListener(this);
|
||||
urls = new DiasporaUrlHelper(appSettings);
|
||||
customTabActivityHelper = new CustomTabActivityHelper();
|
||||
|
||||
setupUI(savedInstanceState);
|
||||
|
||||
|
@ -200,24 +211,42 @@ public class MainActivity extends AppCompatActivity
|
|||
} else if (appSettings.wasProxyEnabled()) {
|
||||
resetProxy();
|
||||
}
|
||||
|
||||
brOpenExternalLink = new OpenExternalLinkReceiver(this);
|
||||
brSetTitle = new UpdateTitleReceiver(app, urls, new UpdateTitleReceiver.TitleCallback() {
|
||||
@Override
|
||||
public void setTitle(int rId) {
|
||||
MainActivity.this.setTitle(rId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTitle(String title) {
|
||||
MainActivity.this.setTitle(title);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void setupUI(Bundle savedInstanceState) {
|
||||
Log.i(App.TAG, "MainActivity.setupUI()");
|
||||
ButterKnife.bind(this);
|
||||
if (webviewPlaceholder.getChildCount() != 0) {
|
||||
Log.v(App.TAG, "remove child views from webViewPlaceholder");
|
||||
webviewPlaceholder.removeAllViews();
|
||||
} else {
|
||||
Log.v(App.TAG, "webViewPlaceholder had no child views");
|
||||
}
|
||||
|
||||
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);
|
||||
this.webView = (ContextMenuWebView) webviewHolder.findViewById(R.id.webView);
|
||||
((LinearLayout)webView.getParent()).removeView(webView);
|
||||
setupWebView(savedInstanceState);
|
||||
} else {
|
||||
Log.v(App.TAG, "Reuse old WebView to avoid reloading page");
|
||||
}
|
||||
ButterKnife.bind(this);
|
||||
if (webviewPlaceholder.getChildCount() != 0) {
|
||||
webviewPlaceholder.removeAllViews();
|
||||
}
|
||||
|
||||
Log.v(App.TAG, "Add WebView to placeholder");
|
||||
webviewPlaceholder.addView(webView);
|
||||
// Setup toolbar
|
||||
|
@ -689,52 +718,24 @@ public class MainActivity extends AppCompatActivity
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* BroadcastReceiver that updates the title of the activity based on which url is currently loaded
|
||||
*/
|
||||
private final BroadcastReceiver brSetTitle = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String url = intent.getStringExtra(EXTRA_URL);
|
||||
if (url != null && url.startsWith(urls.getPodUrl())) {
|
||||
String subUrl = url.substring((urls.getPodUrl()).length());
|
||||
Log.v(App.TAG, "MainActivity.brSetTitle.onReceive(): Set title for subUrl "+subUrl);
|
||||
if (subUrl.startsWith(DiasporaUrlHelper.SUBURL_STREAM)) {
|
||||
setTitle(R.string.nav_stream);
|
||||
} else if (subUrl.startsWith(DiasporaUrlHelper.SUBURL_POSTS)) {
|
||||
setTitle(R.string.diaspora);
|
||||
} else if (subUrl.startsWith(DiasporaUrlHelper.SUBURL_NOTIFICATIONS)) {
|
||||
setTitle(R.string.notifications);
|
||||
} else if (subUrl.startsWith(DiasporaUrlHelper.SUBURL_CONVERSATIONS)) {
|
||||
setTitle(R.string.conversations);
|
||||
} else if (subUrl.startsWith(DiasporaUrlHelper.SUBURL_NEW_POST)) {
|
||||
setTitle(R.string.new_post);
|
||||
} else if (subUrl.startsWith(DiasporaUrlHelper.SUBURL_PEOPLE + appSettings.getProfileId())) {
|
||||
setTitle(R.string.nav_profile);
|
||||
} else if (subUrl.startsWith(DiasporaUrlHelper.SUBURL_ACTIVITY)) {
|
||||
setTitle(R.string.nav_activities);
|
||||
} else if (subUrl.startsWith(DiasporaUrlHelper.SUBURL_LIKED)) {
|
||||
setTitle(R.string.nav_liked);
|
||||
} else if (subUrl.startsWith(DiasporaUrlHelper.SUBURL_COMMENTED)) {
|
||||
setTitle(R.string.nav_commented);
|
||||
} else if (subUrl.startsWith(DiasporaUrlHelper.SUBURL_MENTIONS)) {
|
||||
setTitle(R.string.nav_mentions);
|
||||
} else if (subUrl.startsWith(DiasporaUrlHelper.SUBURL_PUBLIC)) {
|
||||
setTitle(R.string.public_);
|
||||
} else if (urls.isAspectUrl(url)){
|
||||
setTitle(urls.getAspectNameFromUrl(url, app));
|
||||
}
|
||||
} else {
|
||||
Log.w(App.TAG, "MainActivity.brSetTitle.onReceive(): Invalid url: "+url);
|
||||
}
|
||||
}
|
||||
};
|
||||
@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();
|
||||
}
|
||||
|
||||
|
@ -744,19 +745,20 @@ 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
|
||||
public boolean onCreateOptionsMenu(Menu menu) {
|
||||
Log.v(App.TAG, "MainActivity.onCreateOptionsMenu()");
|
||||
getMenuInflater().inflate(R.menu.main__menu_top, menu);
|
||||
return super.onCreateOptionsMenu(menu);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPrepareOptionsMenu(Menu menu) {
|
||||
Log.i(App.TAG, "MainActivity.onPrepareOptionsMenu()");
|
||||
MenuItem item;
|
||||
|
||||
if ((item = menu.findItem(R.id.action_notifications)) != null) {
|
||||
LayerDrawable icon = (LayerDrawable) item.getIcon();
|
||||
BadgeDrawable.setBadgeCount(this, icon, podUserProfile.getNotificationCount());
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
package com.github.dfa.diaspora_android.receivers;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.support.customtabs.CustomTabsIntent;
|
||||
|
||||
import com.github.dfa.diaspora_android.App;
|
||||
import com.github.dfa.diaspora_android.R;
|
||||
import com.github.dfa.diaspora_android.activity.MainActivity;
|
||||
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.Log;
|
||||
|
||||
/**
|
||||
* BroadcastReceiver that opens links in a Chrome CustomTab
|
||||
* Created by vanitas on 11.09.16.
|
||||
*/
|
||||
public class OpenExternalLinkReceiver extends BroadcastReceiver {
|
||||
private final Activity parent;
|
||||
|
||||
public OpenExternalLinkReceiver(Activity parent) {
|
||||
this.parent = parent;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String url = intent.getStringExtra(MainActivity.EXTRA_URL);
|
||||
Log.v(App.TAG, "OpenExternalLinkReceiver.onReceive(): url");
|
||||
if(url != null) {
|
||||
CustomTabsIntent.Builder intentBuilder = new CustomTabsIntent.Builder();
|
||||
if(Build.VERSION.SDK_INT >= 23) {
|
||||
intentBuilder.setToolbarColor(context.getResources().getColor(R.color.colorPrimary, context.getTheme()));
|
||||
} else {
|
||||
intentBuilder.setToolbarColor(context.getResources().getColor(R.color.colorPrimary));
|
||||
}
|
||||
intentBuilder.setStartAnimations(context, android.R.anim.slide_in_left, android.R.anim.fade_out);
|
||||
intentBuilder.setExitAnimations(context, android.R.anim.fade_in, android.R.anim.slide_out_right);
|
||||
Bitmap backButtonIcon = BitmapFactory.decodeResource(context.getResources(),
|
||||
R.drawable.ic_arrow_back_white_24px);
|
||||
intentBuilder.setCloseButtonIcon(backButtonIcon);
|
||||
CustomTabActivityHelper.openCustomTab(parent, intentBuilder.build(), Uri.parse(url), new BrowserFallback());
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
package com.github.dfa.diaspora_android.receivers;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
||||
import com.github.dfa.diaspora_android.App;
|
||||
import com.github.dfa.diaspora_android.R;
|
||||
import com.github.dfa.diaspora_android.activity.MainActivity;
|
||||
import com.github.dfa.diaspora_android.data.AppSettings;
|
||||
import com.github.dfa.diaspora_android.util.DiasporaUrlHelper;
|
||||
import com.github.dfa.diaspora_android.util.Log;
|
||||
|
||||
/**
|
||||
* BroadcastReceiver used to update the title of the MainActivity depending on the url of the webview
|
||||
* Created by vanitas on 11.09.16.
|
||||
*/
|
||||
public class UpdateTitleReceiver extends BroadcastReceiver {
|
||||
private DiasporaUrlHelper urls;
|
||||
private AppSettings appSettings;
|
||||
private App app;
|
||||
private TitleCallback callback;
|
||||
|
||||
public UpdateTitleReceiver(App app, DiasporaUrlHelper urls, TitleCallback callback) {
|
||||
this.urls = urls;
|
||||
this.app = app;
|
||||
this.appSettings = app.getSettings();
|
||||
this.callback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
String url = intent.getStringExtra(MainActivity.EXTRA_URL);
|
||||
if (url != null && url.startsWith(urls.getPodUrl())) {
|
||||
String subUrl = url.substring((urls.getPodUrl()).length());
|
||||
Log.v(App.TAG, "UpdateTitleReceiver.onReceive(): Set title for subUrl "+subUrl);
|
||||
if (subUrl.startsWith(DiasporaUrlHelper.SUBURL_STREAM)) {
|
||||
setTitle(R.string.nav_stream);
|
||||
} else if (subUrl.startsWith(DiasporaUrlHelper.SUBURL_POSTS)) {
|
||||
setTitle(R.string.diaspora);
|
||||
} else if (subUrl.startsWith(DiasporaUrlHelper.SUBURL_NOTIFICATIONS)) {
|
||||
setTitle(R.string.notifications);
|
||||
} else if (subUrl.startsWith(DiasporaUrlHelper.SUBURL_CONVERSATIONS)) {
|
||||
setTitle(R.string.conversations);
|
||||
} else if (subUrl.startsWith(DiasporaUrlHelper.SUBURL_NEW_POST)) {
|
||||
setTitle(R.string.new_post);
|
||||
} else if (subUrl.startsWith(DiasporaUrlHelper.SUBURL_PEOPLE + appSettings.getProfileId())) {
|
||||
setTitle(R.string.nav_profile);
|
||||
} else if (subUrl.startsWith(DiasporaUrlHelper.SUBURL_ACTIVITY)) {
|
||||
setTitle(R.string.nav_activities);
|
||||
} else if (subUrl.startsWith(DiasporaUrlHelper.SUBURL_LIKED)) {
|
||||
setTitle(R.string.nav_liked);
|
||||
} else if (subUrl.startsWith(DiasporaUrlHelper.SUBURL_COMMENTED)) {
|
||||
setTitle(R.string.nav_commented);
|
||||
} else if (subUrl.startsWith(DiasporaUrlHelper.SUBURL_MENTIONS)) {
|
||||
setTitle(R.string.nav_mentions);
|
||||
} else if (subUrl.startsWith(DiasporaUrlHelper.SUBURL_PUBLIC)) {
|
||||
setTitle(R.string.public_);
|
||||
} else if (urls.isAspectUrl(url)){
|
||||
setTitle(urls.getAspectNameFromUrl(url, app));
|
||||
}
|
||||
} else {
|
||||
Log.w(App.TAG, "UpdateTitleReceiver.onReceive(): Invalid url: "+url);
|
||||
}
|
||||
}
|
||||
|
||||
private void setTitle(int rId) {
|
||||
callback.setTitle(rId);
|
||||
}
|
||||
|
||||
private void setTitle(String title) {
|
||||
callback.setTitle(title);
|
||||
}
|
||||
|
||||
public interface TitleCallback {
|
||||
void setTitle(int Rid);
|
||||
void setTitle(String title);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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};
|
||||
}
|
||||
}
|
|
@ -1,12 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
android:orientation="vertical" android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent">
|
||||
<com.github.dfa.diaspora_android.ui.ContextMenuWebView
|
||||
android:id="@+id/webView"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent" />
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_centerVertical="true" />
|
||||
|
||||
</LinearLayout>
|
Loading…
Reference in a new issue