Add UI message when queued for wifi due to size.
Steve Howard [Sat, 21 Aug 2010 00:45:03 +0000 (17:45 -0700)]
As it stands, when a download is paused because it's too big to
proceed over mobile, and must proceed over wifi, it looks like any
other paused download, with no indication of why it's paused.  That
may be passable for most other reasons for pausing a download, but it
seems too confusing for this case.  So this change adds a simple
string message that replaces the progress bar when a download is
paused for this reason (the icon also changes to a warning).

The implementation isn't beautiful and could use some improvement, but
I think it's acceptable and necessary.  The exact UI design and
wording are certainly open to change.

Change-Id: I753d57f463e2614b5694bdc178d2a51066da8ca3

res/layout/status_bar_ongoing_event_progress_bar.xml
res/values/strings.xml
src/com/android/providers/downloads/DownloadInfo.java
src/com/android/providers/downloads/DownloadNotification.java
src/com/android/providers/downloads/DownloadService.java
src/com/android/providers/downloads/DownloadThread.java

index c6a71d4..e3a60b2 100644 (file)
                     android:paddingLeft="5dp"
                     />
             </LinearLayout>
+            <!-- Only one of progress_bar and paused_text will be visible. -->
             <ProgressBar android:id="@+id/progress_bar"
                 style="?android:attr/progressBarStyleHorizontal"
-                android:layout_width="match_parent" 
+                android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:layout_alignParentBottom="true"
                 android:paddingBottom="8dp"
                 android:paddingRight="25dp"
                 />
+            <TextView android:id="@+id/paused_text"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:layout_alignParentBottom="true"
+                android:paddingBottom="8dp"
+                android:singleLine="true"
+                android:textSize="14sp"
+                android:paddingLeft="5dp"
+                android:textColor="#ff000000"
+                />
         </RelativeLayout>
     </LinearLayout>
         
index 62ba38f..b0d95ce 100644 (file)
         application, a content marketplace. -->
     <string name="notification_download_failed">Download unsuccessful</string>
 
+    <!-- When a download is paused because it's too large to download over a
+        mobile connection, and wifi is unavailable, this string is displayed in
+        the system notification for the running download, beneath the download
+        title and description.  Note that such a download could have been
+        initiated by a variety of applications, including (but not limited to)
+        the browser, an email application, a content marketplace.
+        [CHAR LIMIT=24] -->
+    <string name="notification_need_wifi_for_size">Need wifi due to size</string>
+
 
 </resources>
index 02d6ae3..4380059 100644 (file)
@@ -63,6 +63,9 @@ public class DownloadInfo {
     public boolean mIsPublicApi;
     public int mAllowedNetworkTypes;
     public boolean mAllowRoaming;
+    public String mTitle;
+    public String mDescription;
+    public String mPausedReason;
 
     public int mFuzz;
 
@@ -119,6 +122,9 @@ public class DownloadInfo {
                 cursor.getColumnIndexOrThrow(Downloads.Impl.COLUMN_ALLOWED_NETWORK_TYPES));
         mAllowRoaming = cursor.getInt(
                 cursor.getColumnIndexOrThrow(Downloads.Impl.COLUMN_ALLOW_ROAMING)) != 0;
+        mTitle = cursor.getString(cursor.getColumnIndexOrThrow(Downloads.Impl.COLUMN_TITLE));
+        mDescription =
+            cursor.getString(cursor.getColumnIndexOrThrow(Downloads.Impl.COLUMN_DESCRIPTION));
         mFuzz = Helpers.sRandom.nextInt(1001);
 
         readRequestHeaders(mId);
@@ -312,7 +318,12 @@ public class DownloadInfo {
                 return false;
             }
         }
-        return isSizeAllowedForNetwork(networkType);
+        if (!isSizeAllowedForNetwork(networkType)) {
+            mPausedReason = mContext.getResources().getString(
+                    R.string.notification_need_wifi_for_size);
+            return false;
+        }
+        return true;
     }
 
     /**
index 76bffbc..2c30644 100644 (file)
@@ -20,12 +20,14 @@ import android.app.Notification;
 import android.app.PendingIntent;
 import android.content.Context;
 import android.content.Intent;
-import android.database.Cursor;
 import android.net.Uri;
 import android.provider.Downloads;
+import android.util.Log;
+import android.view.View;
 import android.widget.RemoteViews;
 
 import java.util.HashMap;
+import java.util.List;
 
 /**
  * This class handles the updating of the Notification Manager for the
@@ -62,17 +64,18 @@ class DownloadNotification {
      */
     static class NotificationItem {
         int mId;  // This first db _id for the download for the app
-        int mTotalCurrent = 0;
-        int mTotalTotal = 0;
+        long mTotalCurrent = 0;
+        long mTotalTotal = 0;
         int mTitleCount = 0;
         String mPackageName;  // App package name
         String mDescription;
         String[] mTitles = new String[2]; // download titles.
+        String mPausedText = null;
 
         /*
          * Add a second download to this notification item.
          */
-        void addItem(String title, int currentBytes, int totalBytes) {
+        void addItem(String title, long currentBytes, long totalBytes) {
             mTotalCurrent += currentBytes;
             if (totalBytes <= 0 || mTotalTotal == -1) {
                 mTotalTotal = -1;
@@ -101,72 +104,57 @@ class DownloadNotification {
     /*
      * Update the notification ui.
      */
-    public void updateNotification() {
-        updateActiveNotification();
-        updateCompletedNotification();
+    public void updateNotification(List<DownloadInfo> downloads) {
+        updateActiveNotification(downloads);
+        updateCompletedNotification(downloads);
     }
 
-    private void updateActiveNotification() {
-        // Active downloads
-        Cursor c = mContext.getContentResolver().query(
-                Downloads.Impl.CONTENT_URI, new String [] {
-                        Downloads.Impl._ID,
-                        Downloads.Impl.COLUMN_TITLE,
-                        Downloads.Impl.COLUMN_DESCRIPTION,
-                        Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE,
-                        Downloads.Impl.COLUMN_NOTIFICATION_CLASS,
-                        Downloads.Impl.COLUMN_CURRENT_BYTES,
-                        Downloads.Impl.COLUMN_TOTAL_BYTES,
-                        Downloads.Impl.COLUMN_STATUS
-                },
-                WHERE_RUNNING, null, Downloads.Impl._ID);
-
-        if (c == null) {
-            return;
-        }
-
-        // Columns match projection in query above
-        final int idColumn = 0;
-        final int titleColumn = 1;
-        final int descColumn = 2;
-        final int ownerColumn = 3;
-        final int classOwnerColumn = 4;
-        final int currentBytesColumn = 5;
-        final int totalBytesColumn = 6;
-        final int statusColumn = 7;
-
+    private void updateActiveNotification(List<DownloadInfo> downloads) {
         // Collate the notifications
         mNotifications.clear();
-        for (c.moveToFirst(); !c.isAfterLast(); c.moveToNext()) {
-            String packageName = c.getString(ownerColumn);
-            int max = c.getInt(totalBytesColumn);
-            int progress = c.getInt(currentBytesColumn);
-            long id = c.getLong(idColumn);
-            String title = c.getString(titleColumn);
+        for (DownloadInfo download : downloads) {
+            if (!isActiveAndVisible(download)) {
+                continue;
+            }
+            String packageName = download.mPackage;
+            long max = download.mTotalBytes;
+            long progress = download.mCurrentBytes;
+            long id = download.mId;
+            String title = download.mTitle;
             if (title == null || title.length() == 0) {
                 title = mContext.getResources().getString(
                         R.string.download_unknown_title);
             }
+
+            NotificationItem item;
             if (mNotifications.containsKey(packageName)) {
-                mNotifications.get(packageName).addItem(title, progress, max);
+                item = mNotifications.get(packageName);
+                item.addItem(title, progress, max);
             } else {
-                NotificationItem item = new NotificationItem();
+                item = new NotificationItem();
                 item.mId = (int) id;
                 item.mPackageName = packageName;
-                item.mDescription = c.getString(descColumn);
-                String className = c.getString(classOwnerColumn);
+                item.mDescription = download.mDescription;
+                String className = download.mClass;
                 item.addItem(title, progress, max);
                 mNotifications.put(packageName, item);
             }
-
+            if (hasPausedReason(download) && item.mPausedText == null) {
+                item.mPausedText = download.mPausedReason;
+            }
         }
-        c.close();
 
         // Add the notifications
         for (NotificationItem item : mNotifications.values()) {
             // Build the notification object
             Notification n = new Notification();
-            n.icon = android.R.drawable.stat_sys_download;
+
+            boolean hasPausedText = (item.mPausedText != null);
+            int iconResource = android.R.drawable.stat_sys_download;
+            if (hasPausedText) {
+                iconResource = android.R.drawable.stat_sys_warning;
+            }
+            n.icon = iconResource;
 
             n.flags |= Notification.FLAG_ONGOING_EVENT;
 
@@ -188,14 +176,20 @@ class DownloadNotification {
                         item.mDescription);
             }
             expandedView.setTextViewText(R.id.title, title);
-            expandedView.setProgressBar(R.id.progress_bar,
-                    item.mTotalTotal,
-                    item.mTotalCurrent,
-                    item.mTotalTotal == -1);
+
+            if (hasPausedText) {
+                expandedView.setViewVisibility(R.id.progress_bar, View.GONE);
+                expandedView.setTextViewText(R.id.paused_text, item.mPausedText);
+            } else {
+                expandedView.setViewVisibility(R.id.paused_text, View.GONE);
+                expandedView.setProgressBar(R.id.progress_bar,
+                        (int) item.mTotalTotal,
+                        (int) item.mTotalCurrent,
+                        item.mTotalTotal == -1);
+            }
             expandedView.setTextViewText(R.id.progress_text,
                     getDownloadingText(item.mTotalTotal, item.mTotalCurrent));
-            expandedView.setImageViewResource(R.id.appIcon,
-                    android.R.drawable.stat_sys_download);
+            expandedView.setImageViewResource(R.id.appIcon, iconResource);
             n.contentView = expandedView;
 
             Intent intent = new Intent(Constants.ACTION_LIST);
@@ -211,46 +205,21 @@ class DownloadNotification {
         }
     }
 
-    private void updateCompletedNotification() {
-        // Completed downloads
-        Cursor c = mContext.getContentResolver().query(
-                Downloads.Impl.CONTENT_URI, new String [] {
-                        Downloads.Impl._ID,
-                        Downloads.Impl.COLUMN_TITLE,
-                        Downloads.Impl.COLUMN_DESCRIPTION,
-                        Downloads.Impl.COLUMN_NOTIFICATION_PACKAGE,
-                        Downloads.Impl.COLUMN_NOTIFICATION_CLASS,
-                        Downloads.Impl.COLUMN_CURRENT_BYTES,
-                        Downloads.Impl.COLUMN_TOTAL_BYTES,
-                        Downloads.Impl.COLUMN_STATUS,
-                        Downloads.Impl.COLUMN_LAST_MODIFICATION,
-                        Downloads.Impl.COLUMN_DESTINATION
-                },
-                WHERE_COMPLETED, null, Downloads.Impl._ID);
-
-        if (c == null) {
-            return;
-        }
+    private boolean hasPausedReason(DownloadInfo download) {
+        return download.mStatus == Downloads.STATUS_RUNNING_PAUSED && download.mPausedReason != null;
+    }
 
-        // Columns match projection in query above
-        final int idColumn = 0;
-        final int titleColumn = 1;
-        final int descColumn = 2;
-        final int ownerColumn = 3;
-        final int classOwnerColumn = 4;
-        final int currentBytesColumn = 5;
-        final int totalBytesColumn = 6;
-        final int statusColumn = 7;
-        final int lastModColumnId = 8;
-        final int destinationColumnId = 9;
-
-        for (c.moveToFirst(); !c.isAfterLast(); c.moveToNext()) {
+    private void updateCompletedNotification(List<DownloadInfo> downloads) {
+        for (DownloadInfo download : downloads) {
+            if (!isCompleteAndVisible(download)) {
+                return;
+            }
             // Add the notifications
             Notification n = new Notification();
             n.icon = android.R.drawable.stat_sys_download_done;
 
-            long id = c.getLong(idColumn);
-            String title = c.getString(titleColumn);
+            long id = download.mId;
+            String title = download.mTitle;
             if (title == null || title.length() == 0) {
                 title = mContext.getResources().getString(
                         R.string.download_unknown_title);
@@ -258,14 +227,14 @@ class DownloadNotification {
             Uri contentUri = Uri.parse(Downloads.Impl.CONTENT_URI + "/" + id);
             String caption;
             Intent intent;
-            if (Downloads.Impl.isStatusError(c.getInt(statusColumn))) {
+            if (Downloads.Impl.isStatusError(download.mStatus)) {
                 caption = mContext.getResources()
                         .getString(R.string.notification_download_failed);
                 intent = new Intent(Constants.ACTION_LIST);
             } else {
                 caption = mContext.getResources()
                         .getString(R.string.notification_download_complete);
-                if (c.getInt(destinationColumnId) == Downloads.Impl.DESTINATION_EXTERNAL) {
+                if (download.mDestination == Downloads.Impl.DESTINATION_EXTERNAL) {
                     intent = new Intent(Constants.ACTION_OPEN);
                 } else {
                     intent = new Intent(Constants.ACTION_LIST);
@@ -275,7 +244,7 @@ class DownloadNotification {
                     DownloadReceiver.class.getName());
             intent.setData(contentUri);
 
-            n.when = c.getLong(lastModColumnId);
+            n.when = download.mLastMod;
             n.setLatestEventInfo(mContext, title, caption,
                     PendingIntent.getBroadcast(mContext, 0, intent, 0));
 
@@ -285,9 +254,18 @@ class DownloadNotification {
             intent.setData(contentUri);
             n.deleteIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
 
-            mSystemFacade.postNotification(c.getInt(idColumn), n);
+            mSystemFacade.postNotification(download.mId, n);
         }
-        c.close();
+    }
+
+    private boolean isActiveAndVisible(DownloadInfo download) {
+        return 100 <= download.mStatus && download.mStatus < 200
+                && download.mVisibility != Downloads.VISIBILITY_HIDDEN;
+    }
+
+    private boolean isCompleteAndVisible(DownloadInfo download) {
+        return download.mStatus >= 200
+                && download.mVisibility == Downloads.VISIBILITY_VISIBLE_NOTIFY_COMPLETED;
     }
 
     /*
index c9443fd..6d9ee22 100644 (file)
@@ -220,7 +220,7 @@ public class DownloadService extends Service {
 
         mNotifier = new DownloadNotification(this, mSystemFacade);
         mSystemFacade.cancelAllNotifications();
-        mNotifier.updateNotification();
+        mNotifier.updateNotification(mDownloads);
 
         trimDatabase();
         removeSpuriousFiles();
@@ -453,7 +453,7 @@ public class DownloadService extends Service {
                     }
                 }
 
-                mNotifier.updateNotification();
+                mNotifier.updateNotification(mDownloads);
 
                 if (mustScan) {
                     if (!mMediaScannerConnecting) {
index f0aed3f..8a8a0da 100644 (file)
@@ -145,6 +145,7 @@ public class DownloadThread extends Thread {
         AndroidHttpClient client = null;
         PowerManager.WakeLock wakeLock = null;
         int finalStatus = Downloads.Impl.STATUS_UNKNOWN_ERROR;
+        mInfo.mPausedReason = null;
 
         try {
             PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE);