Bug 1241887 - (Part 2) UpdateService: Show notification if permission is missing. r=nalexander draft
authorSebastian Kaspari <s.kaspari@gmail.com>
Fri, 22 Jan 2016 16:30:36 +0100
changeset 324429 3385b1c22de95c1597ef2619b536f31ccf9d597a
parent 324428 8199e0a07ecb1e06a1c0a1d9f452cb11a802747f
child 513382 ef462b2f7fd4510b0d986321e3d1a5354fe5fd35
push id9907
push users.kaspari@gmail.com
push dateFri, 22 Jan 2016 20:05:28 +0000
reviewersnalexander
bugs1241887
milestone46.0a1
Bug 1241887 - (Part 2) UpdateService: Show notification if permission is missing. r=nalexander
mobile/android/base/java/org/mozilla/gecko/updater/UpdateService.java
mobile/android/base/locales/en-US/android_strings.dtd
mobile/android/base/resources/values/ids.xml
mobile/android/base/strings.xml.in
--- a/mobile/android/base/java/org/mozilla/gecko/updater/UpdateService.java
+++ b/mobile/android/base/java/org/mozilla/gecko/updater/UpdateService.java
@@ -6,36 +6,40 @@
 package org.mozilla.gecko.updater;
 
 import org.mozilla.gecko.AppConstants;
 import org.mozilla.gecko.CrashHandler;
 import org.mozilla.gecko.R;
 
 import org.mozilla.apache.commons.codec.binary.Hex;
 
+import org.mozilla.gecko.permissions.Permissions;
 import org.w3c.dom.Document;
 import org.w3c.dom.Node;
 import org.w3c.dom.NodeList;
 
+import android.Manifest;
 import android.app.AlarmManager;
 import android.app.IntentService;
 import android.app.Notification;
 import android.app.NotificationManager;
 import android.app.PendingIntent;
 import android.app.Service;
 import android.content.Context;
 import android.content.Intent;
 import android.content.SharedPreferences;
 import android.net.ConnectivityManager;
 import android.net.NetworkInfo;
 import android.net.Uri;
 import android.net.wifi.WifiManager;
 import android.net.wifi.WifiManager.WifiLock;
 import android.os.Environment;
+import android.provider.Settings;
 import android.support.v4.app.NotificationManagerCompat;
+import android.support.v4.content.ContextCompat;
 import android.support.v4.net.ConnectivityManagerCompat;
 import android.support.v4.app.NotificationCompat;
 import android.support.v4.app.NotificationCompat.Builder;
 import android.util.Log;
 
 import java.io.BufferedInputStream;
 import java.io.BufferedOutputStream;
 import java.io.File;
@@ -175,17 +179,17 @@ public class UpdateService extends Inten
         } else {
             super.onStartCommand(intent, flags, startId);
         }
 
         return Service.START_REDELIVER_INTENT;
     }
 
     @Override
-    protected void onHandleIntent (Intent intent) {
+    protected void onHandleIntent (final Intent intent) {
         if (UpdateServiceHelper.ACTION_REGISTER_FOR_UPDATES.equals(intent.getAction())) {
             AutoDownloadPolicy policy = AutoDownloadPolicy.get(
                 intent.getIntExtra(UpdateServiceHelper.EXTRA_AUTODOWNLOAD_NAME,
                                    AutoDownloadPolicy.NONE.value));
 
             if (policy != AutoDownloadPolicy.NONE) {
                 setAutoDownloadPolicy(policy);
             }
@@ -254,52 +258,69 @@ public class UpdateService extends Inten
         manager.cancel(pending);
 
         lastAttempt.setTimeInMillis(lastAttempt.getTimeInMillis() + interval);
         Log.i(LOGTAG, "next update will be at: " + lastAttempt.getTime());
 
         manager.set(AlarmManager.RTC_WAKEUP, lastAttempt.getTimeInMillis(), pending);
     }
 
-    private void startUpdate(int flags) {
+    private void startUpdate(final int flags) {
         setLastAttemptDate();
 
         NetworkInfo netInfo = mConnectivityManager.getActiveNetworkInfo();
         if (netInfo == null || !netInfo.isConnected()) {
             Log.i(LOGTAG, "not connected to the network");
             registerForUpdates(true);
             sendCheckUpdateResult(CheckUpdateResult.NOT_AVAILABLE);
             return;
         }
 
         registerForUpdates(false);
 
-        UpdateInfo info = findUpdate(hasFlag(flags, UpdateServiceHelper.FLAG_REINSTALL));
+        final UpdateInfo info = findUpdate(hasFlag(flags, UpdateServiceHelper.FLAG_REINSTALL));
         boolean haveUpdate = (info != null);
 
         if (!haveUpdate) {
             Log.i(LOGTAG, "no update available");
             sendCheckUpdateResult(CheckUpdateResult.NOT_AVAILABLE);
             return;
         }
 
         Log.i(LOGTAG, "update available, buildID = " + info.buildID);
 
+        Permissions.from(this)
+                .withPermissions(Manifest.permission.WRITE_EXTERNAL_STORAGE)
+                .doNotPrompt()
+                .andFallback(new Runnable() {
+                    @Override
+                    public void run() {
+                        showPermissionNotification();
+                        sendCheckUpdateResult(CheckUpdateResult.NOT_AVAILABLE);
+                    }})
+                .run(new Runnable() {
+                    @Override
+                    public void run() {
+                        startDownload(info, flags);
+                    }});
+    }
+
+    private void startDownload(UpdateInfo info, int flags) {
         AutoDownloadPolicy policy = getAutoDownloadPolicy();
 
         // We only start a download automatically if one of following criteria are met:
         //
         // - We have a FORCE_DOWNLOAD flag passed in
         // - The preference is set to 'always'
         // - The preference is set to 'wifi' and we are using a non-metered network (i.e. the user
         //   is OK with large data transfers occurring)
         //
         boolean shouldStartDownload = hasFlag(flags, UpdateServiceHelper.FLAG_FORCE_DOWNLOAD) ||
-            policy == AutoDownloadPolicy.ENABLED ||
-            (policy == AutoDownloadPolicy.WIFI && !ConnectivityManagerCompat.isActiveNetworkMetered(mConnectivityManager));
+                policy == AutoDownloadPolicy.ENABLED ||
+                (policy == AutoDownloadPolicy.WIFI && !ConnectivityManagerCompat.isActiveNetworkMetered(mConnectivityManager));
 
         if (!shouldStartDownload) {
             Log.i(LOGTAG, "not initiating automatic update download due to policy " + policy.toString());
             sendCheckUpdateResult(CheckUpdateResult.AVAILABLE);
 
             // We aren't autodownloading here, so prompt to start the update
             Intent notificationIntent = new Intent(UpdateServiceHelper.ACTION_DOWNLOAD_UPDATE);
             notificationIntent.setClass(this, UpdateService.class);
@@ -674,16 +695,39 @@ public class UpdateService extends Inten
         }
 
         Intent intent = new Intent(Intent.ACTION_VIEW);
         intent.setDataAndType(Uri.fromFile(updateFile), "application/vnd.android.package-archive");
         intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         startActivity(intent);
     }
 
+    private void showPermissionNotification() {
+        Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
+                Uri.fromParts("package", getPackageName(), null));
+
+        PendingIntent pendingIntent = PendingIntent.getActivity(this, 0, intent, 0);
+
+        NotificationCompat.BigTextStyle bigTextStyle = new NotificationCompat.BigTextStyle()
+                .bigText(getString(R.string.updater_permission_text));
+
+        Notification notification = new NotificationCompat.Builder(this)
+                .setContentTitle(getString(R.string.updater_permission_title))
+                .setContentText(getString(R.string.updater_permission_text))
+                .setStyle(bigTextStyle)
+                .setAutoCancel(true)
+                .setSmallIcon(R.drawable.ic_status_logo)
+                .setColor(ContextCompat.getColor(this, R.color.rejection_red))
+                .setContentIntent(pendingIntent)
+                .build();
+
+        NotificationManagerCompat.from(this)
+                .notify(R.id.updateServicePermissionNotification, notification);
+    }
+
     private String getLastBuildID() {
         return mPrefs.getString(KEY_LAST_BUILDID, null);
     }
 
     private String getLastHashFunction() {
         return mPrefs.getString(KEY_LAST_HASH_FUNCTION, null);
     }
 
--- a/mobile/android/base/locales/en-US/android_strings.dtd
+++ b/mobile/android/base/locales/en-US/android_strings.dtd
@@ -659,17 +659,24 @@ just addresses the organization to follo
 <!ENTITY updater_downloading_title2 "Downloading &brandShortName;">
 <!ENTITY updater_downloading_title_failed2 "Download failed">
 <!ENTITY updater_downloading_select2 "Touch to apply update once downloaded">
 <!ENTITY updater_downloading_retry2 "Touch to retry">
 
 <!ENTITY updater_apply_title2 "Update available for &brandShortName;">
 <!ENTITY updater_apply_select2 "Touch to update">
 
-<!-- Guest mode -->
+<!-- Localization note (updater_permission_text): This text is shown in a notification and as a snackbar
+     if the app requires a runtime permission to download updates. Currently, the updater only sees
+     remotely advertised updates in the Nightly and Aurora channels. -->
+<!ENTITY updater_permission_text "To download files and updates, allow &brandShortName; permission to access storage.">
+<!-- LOCALIZATION NOTE (updater_permission_allow): This action is shown in a snackbar along with updater_permission_text. -->
+<!ENTITY updater_permission_allow "Allow">
+
+    <!-- Guest mode -->
 <!ENTITY new_guest_session "New Guest Session">
 <!ENTITY exit_guest_session "Exit Guest Session">
 <!ENTITY guest_session_dialog_continue "Continue">
 <!ENTITY guest_session_dialog_cancel "Cancel">
 <!ENTITY new_guest_session_title "&brandShortName; will now restart">
 <!ENTITY new_guest_session_text2 "The person using it will not be able to see any of your personal browsing data (like saved logins, history or bookmarks).\n\nWhen your guest is done, their browsing data will be deleted and your session will be restored.">
 <!ENTITY guest_browsing_notification_title "Guest browsing is enabled">
 <!ENTITY guest_browsing_notification_text "Tap to exit">
--- a/mobile/android/base/resources/values/ids.xml
+++ b/mobile/android/base/resources/values/ids.xml
@@ -11,10 +11,11 @@
     <item type="id" name="original_height"/>
     <item type="id" name="menu_items"/>
     <item type="id" name="menu_margin"/>
     <item type="id" name="recycler_view_click_support" />
     <item type="id" name="range_list"/>
     <item type="id" name="pref_header_general"/>
     <item type="id" name="pref_header_privacy"/>
     <item type="id" name="pref_header_search"/>
+    <item type="id" name="updateServicePermissionNotification" />
 
 </resources>
--- a/mobile/android/base/strings.xml.in
+++ b/mobile/android/base/strings.xml.in
@@ -534,16 +534,19 @@
   <string name="updater_downloading_title">&updater_downloading_title2;</string>
   <string name="updater_downloading_title_failed">&updater_downloading_title_failed2;</string>
   <string name="updater_downloading_select">&updater_downloading_select2;</string>
   <string name="updater_downloading_retry">&updater_downloading_retry2;</string>
 
   <string name="updater_apply_title">&updater_apply_title2;</string>
   <string name="updater_apply_select">&updater_apply_select2;</string>
 
+  <string name="updater_permission_title">&brandShortName;</string>
+  <string name="updater_permission_text">&updater_permission_text;</string>
+
   <!-- Awesomescreen screen -->
   <string name="suggestions_prompt">&suggestions_prompt3;</string>
   <string name="search_bar_item_desc">&search_bar_item_desc;</string>
 
   <string name="suggestion_for_engine">&suggestion_for_engine;</string>
 
   <!-- Set Image Notifications -->
   <string name="set_image_fail">&set_image_fail;</string>