Bug 1356517 - Add deep links to handle events. r?grisha draft
authorNevin Chen <cnevinchen@gmail.com>
Tue, 25 Apr 2017 18:01:34 +0800
changeset 570518 c01a5e6515305fce7b4e7a96cf44c42c2631e16d
parent 570508 6df3dfa9bed3efd3650d594c71a40a8aa34bfa7f
child 626512 fa75ddb56dcd138016d62f2bd0526ae460187f1d
push id56514
push userbmo:cnevinchen@gmail.com
push dateSat, 29 Apr 2017 01:27:35 +0000
reviewersgrisha
bugs1356517
milestone55.0a1
Bug 1356517 - Add deep links to handle events. r?grisha MozReview-Commit-ID: 4OQTrbEdVtQ
mobile/android/base/AndroidManifest.xml.in
mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
mobile/android/base/java/org/mozilla/gecko/LauncherActivity.java
mobile/android/base/java/org/mozilla/gecko/deeplink/DeepLinkContract.java
mobile/android/base/moz.build
--- a/mobile/android/base/AndroidManifest.xml.in
+++ b/mobile/android/base/AndroidManifest.xml.in
@@ -93,16 +93,17 @@
             <intent-filter>
                 <action android:name="android.intent.action.VIEW" />
                 <category android:name="android.intent.category.DEFAULT" />
                 <category android:name="android.intent.category.BROWSABLE" />
                 <data android:scheme="http" />
                 <data android:scheme="https" />
                 <data android:scheme="about" />
                 <data android:scheme="javascript" />
+                <data android:scheme="firefox" />
             </intent-filter>
             <intent-filter>
                 <action android:name="android.intent.action.VIEW" />
                 <category android:name="android.intent.category.BROWSABLE" />
                 <category android:name="android.intent.category.DEFAULT" />
                 <data android:scheme="file" />
                 <data android:scheme="http" />
                 <data android:scheme="https" />
--- a/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
+++ b/mobile/android/base/java/org/mozilla/gecko/BrowserApp.java
@@ -211,17 +211,17 @@ public class BrowserApp extends GeckoApp
     // TODO: Replace with kinto endpoint.
     private static final String SWITCHBOARD_SERVER = "https://firefox.settings.services.mozilla.com/v1/buckets/fennec/collections/experiments/records";
 
     private static final String STATE_ABOUT_HOME_TOP_PADDING = "abouthome_top_padding";
 
     private static final String BROWSER_SEARCH_TAG = "browser_search";
 
     // Request ID for startActivityForResult.
-    private static final int ACTIVITY_REQUEST_PREFERENCES = 1001;
+    public static final int ACTIVITY_REQUEST_PREFERENCES = 1001;
     private static final int ACTIVITY_REQUEST_TAB_QUEUE = 2001;
     public static final int ACTIVITY_REQUEST_FIRST_READERVIEW_BOOKMARK = 3001;
     public static final int ACTIVITY_RESULT_FIRST_READERVIEW_BOOKMARKS_GOTO_BOOKMARKS = 3002;
     public static final int ACTIVITY_RESULT_FIRST_READERVIEW_BOOKMARKS_IGNORE = 3003;
     public static final int ACTIVITY_REQUEST_TRIPLE_READERVIEW = 4001;
     public static final int ACTIVITY_RESULT_TRIPLE_READERVIEW_ADD_BOOKMARK = 4002;
     public static final int ACTIVITY_RESULT_TRIPLE_READERVIEW_IGNORE = 4003;
 
--- a/mobile/android/base/java/org/mozilla/gecko/LauncherActivity.java
+++ b/mobile/android/base/java/org/mozilla/gecko/LauncherActivity.java
@@ -1,45 +1,68 @@
 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
  * This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 package org.mozilla.gecko;
 
 import android.app.Activity;
-import android.content.Context;
 import android.content.Intent;
+import android.net.Uri;
 import android.os.Bundle;
 import android.support.annotation.NonNull;
 import android.support.customtabs.CustomTabsIntent;
+import android.util.Log;
 
+import org.mozilla.gecko.home.HomeConfig;
 import org.mozilla.gecko.webapps.WebAppActivity;
 import org.mozilla.gecko.webapps.WebAppIndexer;
 import org.mozilla.gecko.customtabs.CustomTabsActivity;
 import org.mozilla.gecko.db.BrowserContract;
 import org.mozilla.gecko.mozglue.SafeIntent;
 import org.mozilla.gecko.preferences.GeckoPreferences;
 import org.mozilla.gecko.tabqueue.TabQueueHelper;
 import org.mozilla.gecko.tabqueue.TabQueueService;
 
+import static org.mozilla.gecko.BrowserApp.ACTIVITY_REQUEST_PREFERENCES;
+import static org.mozilla.gecko.deeplink.DeepLinkContract.DEEP_LINK_SCHEME;
+import static org.mozilla.gecko.deeplink.DeepLinkContract.LINK_BOOKMARK_LIST;
+import static org.mozilla.gecko.deeplink.DeepLinkContract.LINK_DEFAULT_BROWSER;
+import static org.mozilla.gecko.deeplink.DeepLinkContract.LINK_HISTORY_LIST;
+import static org.mozilla.gecko.deeplink.DeepLinkContract.LINK_PREFERENCES;
+import static org.mozilla.gecko.deeplink.DeepLinkContract.LINK_PREFERENCES_ACCESSIBILITY;
+import static org.mozilla.gecko.deeplink.DeepLinkContract.LINK_PREFERENCES_NOTIFICATIONS;
+import static org.mozilla.gecko.deeplink.DeepLinkContract.LINK_PREFERENCES_PRIAVACY;
+import static org.mozilla.gecko.deeplink.DeepLinkContract.LINK_PREFERENCES_SEARCH;
+import static org.mozilla.gecko.deeplink.DeepLinkContract.LINK_SAVE_AS_PDF;
+import static org.mozilla.gecko.deeplink.DeepLinkContract.LINK_SIGN_UP;
+import static org.mozilla.gecko.deeplink.DeepLinkContract.SUMO_DEFAULT_BROWSER;
+
 /**
  * Activity that receives incoming Intents and dispatches them to the appropriate activities (e.g. browser, custom tabs, web app).
  */
 public class LauncherActivity extends Activity {
+
+    private static final String TAG = LauncherActivity.class.getSimpleName();
+
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
         GeckoAppShell.ensureCrashHandling();
 
         final SafeIntent safeIntent = new SafeIntent(getIntent());
 
+        // Is this deep link?
+        if (isDeepLink(safeIntent)) {
+            dispatchDeepLink(safeIntent);
+
         // Is this web app?
-        if (isWebAppIntent(safeIntent)) {
+        } else if (isWebAppIntent(safeIntent)) {
             dispatchWebAppIntent();
 
         // If it's not a view intent, it won't be a custom tabs intent either. Just launch!
         } else if (!isViewIntentWithURL(safeIntent)) {
             dispatchNormalIntent();
 
         // Is this a custom tabs intent, and are custom tabs enabled?
         } else if (AppConstants.MOZ_ANDROID_CUSTOM_TABS && isCustomTabsIntent(safeIntent)
@@ -76,16 +99,26 @@ public class LauncherActivity extends Ac
         Intent intent = new Intent(getIntent());
         intent.setClassName(getApplicationContext(), AppConstants.MOZ_ANDROID_BROWSER_INTENT_CLASS);
 
         filterFlags(intent);
 
         startActivity(intent);
     }
 
+    private void dispatchUrlIntent(@NonNull String url) {
+        Intent intent = new Intent(getIntent());
+        intent.setData(Uri.parse(url));
+        intent.setClassName(getApplicationContext(), AppConstants.MOZ_ANDROID_BROWSER_INTENT_CLASS);
+
+        filterFlags(intent);
+
+        startActivity(intent);
+    }
+
     private void dispatchCustomTabsIntent() {
         Intent intent = new Intent(getIntent());
         intent.setClassName(getApplicationContext(), CustomTabsActivity.class.getName());
 
         filterFlags(intent);
 
         startActivity(intent);
     }
@@ -122,9 +155,75 @@ public class LauncherActivity extends Ac
 
     private static boolean isWebAppIntent(@NonNull final SafeIntent safeIntent) {
         return GeckoApp.ACTION_WEBAPP.equals(safeIntent.getAction());
     }
 
     private boolean isCustomTabsEnabled() {
         return GeckoSharedPrefs.forApp(this).getBoolean(GeckoPreferences.PREFS_CUSTOM_TABS, false);
     }
+
+    private boolean isDeepLink(SafeIntent intent) {
+        if (intent == null || intent.getData() == null || intent.getData().getScheme() == null
+                || intent.getAction() == null) {
+            return false;
+        }
+        boolean schemeMatched = intent.getData().getScheme().equalsIgnoreCase(DEEP_LINK_SCHEME);
+        boolean actionMatched = intent.getAction().equals(Intent.ACTION_VIEW);
+        return schemeMatched && actionMatched;
+    }
+
+    private void dispatchDeepLink(SafeIntent intent) {
+        if (intent == null || intent.getData() == null || intent.getData().getHost() == null) {
+            return;
+        }
+        final String host = intent.getData().getHost();
+
+        switch (host) {
+            case LINK_DEFAULT_BROWSER:
+                GeckoSharedPrefs.forApp(this).edit().putBoolean(GeckoPreferences.PREFS_DEFAULT_BROWSER, true).apply();
+
+                if (AppConstants.Versions.feature24Plus) {
+                    // We are special casing the link to set the default browser here: On old Android versions we
+                    // link to a SUMO page but on new Android versions we can link to the default app settings where
+                    // the user can actually set a default browser (Bug 1312686).
+                    final Intent changeDefaultApps = new Intent("android.settings.MANAGE_DEFAULT_APPS_SETTINGS");
+                    startActivity(changeDefaultApps);
+                } else {
+                    dispatchUrlIntent(SUMO_DEFAULT_BROWSER);
+                }
+                break;
+            case LINK_SAVE_AS_PDF:
+                EventDispatcher.getInstance().dispatch("SaveAs:PDF", null);
+                break;
+            case LINK_BOOKMARK_LIST:
+                String bookmarks = AboutPages.getURLForBuiltinPanelType(HomeConfig.PanelType.BOOKMARKS);
+                dispatchUrlIntent(bookmarks);
+                break;
+            case LINK_HISTORY_LIST:
+                String history = AboutPages.getURLForBuiltinPanelType(HomeConfig.PanelType.COMBINED_HISTORY);
+                dispatchUrlIntent(history);
+                break;
+            case LINK_SIGN_UP:
+                dispatchUrlIntent(AboutPages.ACCOUNTS + "?action=signup");
+                break;
+            case LINK_PREFERENCES:
+                Intent settingsIntent = new Intent(this, GeckoPreferences.class);
+
+                // We want to know when the Settings activity returns, because
+                // we might need to redisplay based on a locale change.
+                startActivityForResult(settingsIntent, ACTIVITY_REQUEST_PREFERENCES);
+                break;
+            case LINK_PREFERENCES_PRIAVACY:
+            case LINK_PREFERENCES_SEARCH:
+            case LINK_PREFERENCES_NOTIFICATIONS:
+            case LINK_PREFERENCES_ACCESSIBILITY:
+                settingsIntent = new Intent(this, GeckoPreferences.class);
+                GeckoPreferences.setResourceToOpen(settingsIntent, host);
+                startActivityForResult(settingsIntent, ACTIVITY_REQUEST_PREFERENCES);
+                break;
+            default:
+                Log.w(TAG, "unrecognized deep links");
+        }
+
+    }
+
 }
new file mode 100644
--- /dev/null
+++ b/mobile/android/base/java/org/mozilla/gecko/deeplink/DeepLinkContract.java
@@ -0,0 +1,26 @@
+/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*-
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+package org.mozilla.gecko.deeplink;
+
+// This class defines the contract when using deep links
+public class DeepLinkContract {
+
+    // Sumo page for setting Fennec as default browser
+    public static final String SUMO_DEFAULT_BROWSER = "https://support.mozilla.org/kb/make-firefox-default-browser-android?utm_source=inproduct&amp;utm_medium=settings&amp;utm_campaign=mobileandroid";
+    public static final String DEEP_LINK_SCHEME = "firefox";
+
+    public static final String LINK_DEFAULT_BROWSER = "default_browser";
+    public static final String LINK_SAVE_AS_PDF = "save_as_pdf";
+    public static final String LINK_BOOKMARK_LIST = "bookmark_list";
+    public static final String LINK_HISTORY_LIST = "history_list";
+    public static final String LINK_SIGN_UP = "sign_up";
+    public static final String LINK_PREFERENCES = "preferences";
+    public static final String LINK_PREFERENCES_PRIAVACY = "preferences_privacy";
+    public static final String LINK_PREFERENCES_SEARCH = "preferences_search";
+    public static final String LINK_PREFERENCES_NOTIFICATIONS = "preferences_notifications";
+    public static final String LINK_PREFERENCES_ACCESSIBILITY = "preferences_accessibility";
+
+}
--- a/mobile/android/base/moz.build
+++ b/mobile/android/base/moz.build
@@ -520,16 +520,17 @@ gbjar.sources += ['java/org/mozilla/geck
     'db/SQLiteBridgeContentProvider.java',
     'db/SuggestedSites.java',
     'db/Table.java',
     'db/TabsAccessor.java',
     'db/TabsProvider.java',
     'db/UrlAnnotations.java',
     'db/URLImageDataTable.java',
     'db/URLMetadata.java',
+    'deeplink/DeepLinkContract.java',
     'delegates/BookmarkStateChangeDelegate.java',
     'delegates/BrowserAppDelegate.java',
     'delegates/BrowserAppDelegateWithReference.java',
     'delegates/OfflineTabStatusDelegate.java',
     'delegates/ScreenshotDelegate.java',
     'delegates/TabsTrayVisibilityAwareDelegate.java',
     'DevToolsAuthHelper.java',
     'distribution/Distribution.java',