OSDN Git Service

Adding a circular progress bar for preloader icons
authorSunny Goyal <sunnygoyal@google.com>
Wed, 9 Jul 2014 07:09:28 +0000 (00:09 -0700)
committerSunny Goyal <sunnygoyal@google.com>
Wed, 16 Jul 2014 20:37:56 +0000 (13:37 -0700)
Change-Id: I1b5ba61c01a16a8cb5d3f9e31f827f8c99a1ffc9

proguard.flags
res/drawable-xxhdpi/bg_preloader.png [new file with mode: 0644]
res/drawable-xxhdpi/bg_preloader_progress.png [new file with mode: 0644]
res/values/integers.xml [deleted file]
src/com/android/launcher3/BubbleTextView.java
src/com/android/launcher3/FolderIcon.java
src/com/android/launcher3/LauncherModel.java
src/com/android/launcher3/PreloadIconDrawable.java [new file with mode: 0644]
src/com/android/launcher3/ShortcutInfo.java

index a922e91..0b28c0e 100644 (file)
@@ -52,3 +52,8 @@
 -keep class com.android.launcher3.MemoryDumpActivity {
   *;
 }
+
+-keep class com.android.launcher3.PreloadIconDrawable {
+  public float getAnimationProgress();
+  public void setAnimationProgress(float);
+}
diff --git a/res/drawable-xxhdpi/bg_preloader.png b/res/drawable-xxhdpi/bg_preloader.png
new file mode 100644 (file)
index 0000000..56b8060
Binary files /dev/null and b/res/drawable-xxhdpi/bg_preloader.png differ
diff --git a/res/drawable-xxhdpi/bg_preloader_progress.png b/res/drawable-xxhdpi/bg_preloader_progress.png
new file mode 100644 (file)
index 0000000..443afe9
Binary files /dev/null and b/res/drawable-xxhdpi/bg_preloader_progress.png differ
diff --git a/res/values/integers.xml b/res/values/integers.xml
deleted file mode 100644 (file)
index 7d26d85..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-* Copyright (C) 2014 The Android Open Source Project
-*
-* Licensed under the Apache License, Version 2.0 (the "License");
-* you may not use this file except in compliance with the License.
-* You may obtain a copy of the License at
-*
-*      http://www.apache.org/licenses/LICENSE-2.0
-*
-* Unless required by applicable law or agreed to in writing, software
-* distributed under the License is distributed on an "AS IS" BASIS,
-* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-* See the License for the specific language governing permissions and
-* limitations under the License.
-*/
--->
-
-<resources>
-    <integer name="promise_icon_alpha">127</integer>
-</resources>
\ No newline at end of file
index 57dcea0..3f619a8 100644 (file)
@@ -136,7 +136,8 @@ public class BubbleTextView extends TextView {
             setContentDescription(info.contentDescription);
         }
         setTag(info);
-        if (info.isPromise()) {
+
+        if (info.wasPromise) {
             applyState();
         }
     }
@@ -431,42 +432,55 @@ public class BubbleTextView extends TextView {
     }
 
     public void applyState() {
-        int alpha = getResources().getInteger(R.integer.promise_icon_alpha);
+        final int progressLevel;
         final int state = getState();
         if (DEBUG) Log.d(TAG, "applying icon state: " + state);
 
         switch(state) {
             case ShortcutInfo.PACKAGE_STATE_DEFAULT:
                 super.setText(mDefaultText);
-                alpha = 255;
+                progressLevel = 100;
                 break;
 
             case ShortcutInfo.PACKAGE_STATE_ENQUEUED:
                 setText(R.string.package_state_enqueued);
+                progressLevel = 0;
                 break;
 
             case ShortcutInfo.PACKAGE_STATE_DOWNLOADING:
                 setText(R.string.package_state_downloading);
+                // TODO(sunnygoyal): fix progress
+                progressLevel = 30;
                 break;
 
             case ShortcutInfo.PACKAGE_STATE_INSTALLING:
                 setText(R.string.package_state_installing);
+                progressLevel = 100;
                 break;
 
             case ShortcutInfo.PACKAGE_STATE_ERROR:
                 setText(R.string.package_state_error);
+                progressLevel = 0;
                 break;
 
             case ShortcutInfo.PACKAGE_STATE_UNKNOWN:
             default:
+                progressLevel = 0;
                 setText(R.string.package_state_unknown);
                 break;
         }
-        if (DEBUG) Log.d(TAG, "setting icon alpha to: " + alpha);
+
         Drawable[] drawables = getCompoundDrawables();
-        for (int i = 0; i < drawables.length; i++) {
-            if (drawables[i] != null) {
-                drawables[i].setAlpha(alpha);
+        Drawable top = drawables[1];
+        if ((top != null) && !(top instanceof PreloadIconDrawable)) {
+            top = new PreloadIconDrawable(top, getResources());
+            setCompoundDrawables(drawables[0], top, drawables[2], drawables[3]);
+        }
+        if (top != null) {
+            top.setLevel(progressLevel);
+            if ((top instanceof PreloadIconDrawable)
+                    && (state == ShortcutInfo.PACKAGE_STATE_DEFAULT)) {
+                ((PreloadIconDrawable) top).maybePerformFinishedAnimation();
             }
         }
     }
index ab8976a..4f674f5 100644 (file)
@@ -605,7 +605,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
             computePreviewDrawingParams(mAnimParams.drawable);
         } else {
             v = (TextView) items.get(0);
-            d = v.getCompoundDrawables()[1];
+            d = getTopDrawable(v);
             computePreviewDrawingParams(d);
         }
 
@@ -614,7 +614,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
             for (int i = nItemsInPreview - 1; i >= 0; i--) {
                 v = (TextView) items.get(i);
                 if (!mHiddenItems.contains(v.getTag())) {
-                    d = v.getCompoundDrawables()[1];
+                    d = getTopDrawable(v);
                     mParams = computePreviewItemDrawingParams(i, mParams);
                     mParams.drawable = d;
                     drawPreviewItem(canvas, mParams);
@@ -625,6 +625,11 @@ public class FolderIcon extends FrameLayout implements FolderListener {
         }
     }
 
+    private Drawable getTopDrawable(TextView v) {
+        Drawable d = v.getCompoundDrawables()[1];
+        return (d instanceof PreloadIconDrawable) ? ((PreloadIconDrawable) d).mIcon : d;
+    }
+
     private void animateFirstItem(final Drawable d, int duration, final boolean reverse,
             final Runnable onCompleteRunnable) {
         final PreviewItemDrawingParams finalParams = computePreviewItemDrawingParams(0, null);
index f1e73eb..b01db71 100644 (file)
@@ -3047,6 +3047,7 @@ public class LauncherModel extends BroadcastReceiver
         info.setIcon(mIconCache.getIcon(intent, info.title.toString(), info.user));
         info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
         info.restoredIntent = intent;
+        info.wasPromise = true;
         info.setState(ShortcutInfo.PACKAGE_STATE_UNKNOWN);
         return info;
     }
diff --git a/src/com/android/launcher3/PreloadIconDrawable.java b/src/com/android/launcher3/PreloadIconDrawable.java
new file mode 100644 (file)
index 0000000..d9365cc
--- /dev/null
@@ -0,0 +1,168 @@
+package com.android.launcher3;
+
+import android.animation.ObjectAnimator;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.Path;
+import android.graphics.PixelFormat;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+
+class PreloadIconDrawable extends Drawable {
+    private static final float ANIMATION_PROGRESS_STOPPED = -1.0f;
+    private static final float ANIMATION_PROGRESS_STARTED = 0f;
+    private static final float ANIMATION_PROGRESS_COMPLETED = 1.0f;
+
+    private static final float ICON_SCALE_FACTOR = 0.6f;
+
+    private static Bitmap sProgressBg, sProgressFill;
+
+    private final Rect mCanvasClipRect = new Rect();
+    private final RectF mRect = new RectF();
+    private final Path mProgressPath = new Path();
+    private final Paint mPaint = new Paint(Paint.FILTER_BITMAP_FLAG);
+
+    final Drawable mIcon;
+
+    /**
+     * Indicates the progress of the preloader [0-100]. If it goes above 100, only the icon
+     * is shown with no progress bar.
+     */
+    private int mProgress = 0;
+    private boolean mPathChanged;
+
+    private float mAnimationProgress = ANIMATION_PROGRESS_STOPPED;
+    private ObjectAnimator mAnimator;
+
+    public PreloadIconDrawable(Drawable icon, Resources res) {
+        mIcon = icon;
+
+        setBounds(icon.getBounds());
+        mPathChanged = false;
+
+        if (sProgressBg == null) {
+            sProgressBg = BitmapFactory.decodeResource(res, R.drawable.bg_preloader);
+        }
+        if (sProgressFill == null) {
+            sProgressFill = BitmapFactory.decodeResource(res, R.drawable.bg_preloader_progress);
+        }
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        final Rect r = getBounds();
+        if (canvas.getClipBounds(mCanvasClipRect) && !Rect.intersects(mCanvasClipRect, r)) {
+            // The draw region has been clipped.
+            return;
+        }
+        final float iconScale;
+
+        if ((mAnimationProgress >= ANIMATION_PROGRESS_STARTED)
+                && (mAnimationProgress < ANIMATION_PROGRESS_COMPLETED)) {
+            mPaint.setAlpha((int) ((1 - mAnimationProgress) * 255));
+            canvas.drawBitmap(sProgressBg, null, r, mPaint);
+            canvas.drawBitmap(sProgressFill, null, r, mPaint);
+            iconScale = ICON_SCALE_FACTOR + (1 - ICON_SCALE_FACTOR) * mAnimationProgress;
+
+        } else if (mAnimationProgress == ANIMATION_PROGRESS_STOPPED) {
+            mPaint.setAlpha(255);
+            iconScale = ICON_SCALE_FACTOR;
+            canvas.drawBitmap(sProgressBg, null, r, mPaint);
+
+            if (mProgress >= 100) {
+                canvas.drawBitmap(sProgressFill, null, r, mPaint);
+            } else if (mProgress > 0) {
+                if (mPathChanged) {
+                    mProgressPath.reset();
+                    mProgressPath.moveTo(r.exactCenterX(), r.centerY());
+
+                    mRect.set(r);
+                    mProgressPath.arcTo(mRect, -90, mProgress * 3.6f);
+                    mProgressPath.close();
+                    mPathChanged = false;
+                }
+
+                canvas.save();
+                canvas.clipPath(mProgressPath);
+                canvas.drawBitmap(sProgressFill, null, r, mPaint);
+                canvas.restore();
+            }
+        } else {
+            iconScale = 1;
+        }
+
+        canvas.save();
+        canvas.scale(iconScale, iconScale, r.exactCenterX(), r.exactCenterY());
+        mIcon.draw(canvas);
+        canvas.restore();
+    }
+
+    @Override
+    protected void onBoundsChange(Rect bounds) {
+        mIcon.setBounds(bounds);
+        mPathChanged = true;
+    }
+
+    @Override
+    public int getOpacity() {
+        return PixelFormat.TRANSLUCENT;
+    }
+
+    @Override
+    public void setAlpha(int alpha) {
+        mIcon.setAlpha(alpha);
+    }
+
+    @Override
+    public void setColorFilter(ColorFilter cf) {
+        mIcon.setColorFilter(cf);
+    }
+
+    @Override
+    protected boolean onLevelChange(int level) {
+        mProgress = level;
+        mPathChanged = true;
+
+        // Stop Animation
+        if (mAnimator != null) {
+            mAnimator.cancel();
+            mAnimator = null;
+        }
+        mAnimationProgress = ANIMATION_PROGRESS_STOPPED;
+
+        invalidateSelf();
+        return true;
+    }
+
+    /**
+     * Runs the finish animation if it is has not been run after last level change.
+     */
+    public void maybePerformFinishedAnimation() {
+        if (mAnimationProgress > ANIMATION_PROGRESS_STOPPED) {
+            return;
+        }
+        if (mAnimator != null) {
+            mAnimator.cancel();
+        }
+        setAnimationProgress(ANIMATION_PROGRESS_STARTED);
+        mAnimator = ObjectAnimator.ofFloat(this, "animationProgress",
+                ANIMATION_PROGRESS_STARTED, ANIMATION_PROGRESS_COMPLETED);
+        mAnimator.start();
+    }
+
+    public void setAnimationProgress(float progress) {
+        if (progress != mAnimationProgress) {
+            mAnimationProgress = progress;
+            invalidateSelf();
+        }
+    }
+
+    public float getAnimationProgress() {
+        return mAnimationProgress;
+    }
+}
index d2573a4..7e1f0d6 100644 (file)
 
 package com.android.launcher3;
 
-import android.content.ComponentName;
 import android.content.ContentValues;
 import android.content.Context;
 import android.content.Intent;
-import android.content.pm.PackageInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.PackageManager.NameNotFoundException;
 import android.graphics.Bitmap;
 import android.util.Log;
 
@@ -96,6 +92,11 @@ public class ShortcutInfo extends ItemInfo {
      */
     Intent restoredIntent;
 
+    /**
+     * This is set once to indicate that it was a promise info at some point of its life.
+     */
+    boolean wasPromise = false;
+
     ShortcutInfo() {
         itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_SHORTCUT;
     }
@@ -119,7 +120,7 @@ public class ShortcutInfo extends ItemInfo {
         }
     }
 
-    ShortcutInfo(Intent intent, CharSequence title, String contentDescrition,
+    ShortcutInfo(Intent intent, CharSequence title, String contentDescription,
             Bitmap icon, UserHandleCompat user) {
         this();
         this.intent = intent;