package com.customtabplugin; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.graphics.Color; import android.net.Uri; import android.os.Bundle; import android.os.Environment; import android.support.annotation.ColorInt; import android.support.customtabs.CustomTabsClient; import android.support.customtabs.CustomTabsIntent; import android.support.customtabs.CustomTabsSession; import android.support.v4.app.ActivityCompat; import android.support.v4.app.ActivityOptionsCompat; import android.text.TextUtils; import android.util.Log; import org.apache.cordova.CallbackContext; import org.apache.cordova.CordovaInterface; import org.apache.cordova.CordovaPlugin; import org.apache.cordova.CordovaWebView; import org.apache.cordova.PluginResult; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import org.chromium.customtabsclient.shared.CustomTabsHelper; public class ChromeCustomTabPlugin extends CordovaPlugin{ public static final String TAG = "ChromeCustomTabPlugin"; public static final int CUSTOM_TAB_REQUEST_CODE = 1; private CustomTabServiceHelper mCustomTabPluginHelper; private boolean wasConnected; private CallbackContext callbackContext; private Bundle mStartAnimationBundle; @Override public void initialize(CordovaInterface cordova, CordovaWebView webView) { super.initialize(cordova, webView); mCustomTabPluginHelper = new CustomTabServiceHelper(cordova.getActivity()); } @Override public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException { final Context context = this.cordova.getContext(); switch (action) { case "isAvailable": callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, mCustomTabPluginHelper.isAvailable())); return true; case "show": { final JSONObject options = args.getJSONObject(0); final String url = options.optString("url"); if(TextUtils.isEmpty(url)){ JSONObject result = new JSONObject(); result.put("error", "expected argument 'url' to be non empty string."); PluginResult pluginResult = new PluginResult(PluginResult.Status.ERROR, result); callbackContext.sendPluginResult(pluginResult); return true; } final String toolbarColor = options.optString("toolbarColor"); final Boolean showDefaultShareMenuItem = options.optBoolean("showDefaultShareMenuItem"); String transition = ""; mStartAnimationBundle = null; final Boolean animated = options.optBoolean("animated", true); if(animated) transition = options.optString("transition", "slide"); PluginResult pluginResult; JSONObject result = new JSONObject(); if(isAvailable()) { try { this.show(url, getColor(toolbarColor), showDefaultShareMenuItem, transition); result.put("event", "loaded"); pluginResult = new PluginResult(PluginResult.Status.OK, result); pluginResult.setKeepCallback(true); this.callbackContext = callbackContext; } catch (Exception ex) { result.put("error", ex.getMessage()); pluginResult = new PluginResult(PluginResult.Status.ERROR, result); } } else { result.put("error", "custom tabs are not available"); pluginResult = new PluginResult(PluginResult.Status.ERROR, result); } callbackContext.sendPluginResult(pluginResult); return true; } case "getViewHandlerPackages": { PluginResult pluginResult; final JSONObject result = new JSONObject(); result.put("defaultHandler", CustomTabsHelper.getDefaultViewHandlerPackageName(context)); result.put("customTabsImplementations", new JSONArray(CustomTabsHelper.getPackagesSupportingCustomTabs(context))); pluginResult = new PluginResult(PluginResult.Status.OK, result); callbackContext.sendPluginResult(pluginResult); return true; } case "useCustomTabsImplementation": { PluginResult pluginResult; JSONObject result = new JSONObject(); final String packageName = args.optString(0); if(TextUtils.isEmpty(packageName)) { result.put("error", "expected argument 'packageName' to be non empty string."); pluginResult = new PluginResult(PluginResult.Status.ERROR, result); } else { try { mCustomTabPluginHelper.setPackageNameToBind(packageName, context); pluginResult = new PluginResult(PluginResult.Status.OK, true); } catch (CustomTabsHelper.InvalidPackageException e) { pluginResult = new PluginResult(PluginResult.Status.ERROR, "Invalid package: "+packageName); } } callbackContext.sendPluginResult(pluginResult); return true; } case "connectToService": { if (bindCustomTabsService()) callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, true)); else callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, "Failed to connect to service")); return true; } case "warmUp": { if (warmUp()) { callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, true)); } else { callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, "Failed to warm up service")); } return true; } case "mayLaunchUrl": { final String url = args.getString(0); if(mayLaunchUrl(url)){ callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, true)); } else { callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR,String.format("Failed prepare to launch url: %s", url))); } return true; } } return false; } private boolean isAvailable(){ return mCustomTabPluginHelper.isAvailable(); } private void show(String url, @ColorInt int toolbarColor, boolean showDefaultShareMenuItem, String transition) { CustomTabsIntent.Builder builder = new CustomTabsIntent.Builder(getSession()) .setToolbarColor(toolbarColor); if(showDefaultShareMenuItem) builder.addDefaultShareMenuItem(); if(!TextUtils.isEmpty(transition)) addTransition(builder, transition); CustomTabsIntent customTabsIntent = builder.build(); String packageName = CustomTabsHelper.getPackageNameToUse(cordova.getActivity()); if ( packageName != null ) { customTabsIntent.intent.setPackage(packageName); } startCustomTabActivity(url, customTabsIntent.intent); } private void addTransition(CustomTabsIntent.Builder builder, String transition) { final String animType = "anim"; switch (transition){ case ("slide"): default: mStartAnimationBundle = ActivityOptionsCompat.makeCustomAnimation( cordova.getActivity(), getIdentifier("slide_in_right", animType), getIdentifier("slide_out_left", animType)).toBundle(); builder.setExitAnimations(cordova.getActivity(), getIdentifier("slide_in_left", animType), getIdentifier("slide_out_right", animType)); } } private void startCustomTabActivity(String url, Intent intent) { intent.setData(Uri.parse(url)); if(mStartAnimationBundle == null) cordova.startActivityForResult(this, intent, CUSTOM_TAB_REQUEST_CODE); else { cordova.setActivityResultCallback(this); ActivityCompat.startActivityForResult(cordova.getActivity(), intent, CUSTOM_TAB_REQUEST_CODE, mStartAnimationBundle); } } private boolean warmUp(){ boolean success = false; final CustomTabsClient client = mCustomTabPluginHelper.getClient(); if (client != null) success = client.warmup(0); return success; } private boolean mayLaunchUrl(String url){ boolean success = false; if (mCustomTabPluginHelper.getClient() != null) { CustomTabsSession session = getSession(); success = session.mayLaunchUrl(Uri.parse(url), null, null); } return success; } private int getColor(String color) { if(TextUtils.isEmpty(color)) return Color.LTGRAY; try { return Color.parseColor(color); } catch (NumberFormatException ex) { Log.i(TAG, String.format("Unable to parse Color: %s", color)); return Color.LTGRAY; } } private CustomTabsSession getSession() { return mCustomTabPluginHelper.getSession(); } private boolean bindCustomTabsService() { return mCustomTabPluginHelper.bindCustomTabsService(cordova.getActivity()); } private boolean unbindCustomTabsService() { return mCustomTabPluginHelper.unbindCustomTabsService(cordova.getActivity()); } @Override public void onActivityResult(int requestCode, int resultCode, Intent intent) { super.onActivityResult(requestCode, resultCode, intent); if(requestCode == CUSTOM_TAB_REQUEST_CODE){ JSONObject result = new JSONObject(); try { result.put("event", "closed"); } catch (JSONException e) { e.printStackTrace(); } if(callbackContext != null){ callbackContext.success(result); callbackContext = null; } } } private int getIdentifier(String name, String type) { final Activity activity = cordova.getActivity(); return activity.getResources().getIdentifier(name, type, activity.getPackageName()); } @Override public void onStop() { wasConnected = unbindCustomTabsService(); super.onStop(); } @Override public void onStart() { if(wasConnected){ bindCustomTabsService(); } super.onStart(); } @Override public void onDestroy() { super.onDestroy(); mCustomTabPluginHelper.setConnectionCallback(null); } }