Adding a circular progress bar for preloader icons

Change-Id: I1b5ba61c01a16a8cb5d3f9e31f827f8c99a1ffc9
This commit is contained in:
Sunny Goyal 2014-07-09 00:09:28 -07:00
parent f599ccfe96
commit 3484638cad
9 changed files with 208 additions and 36 deletions

View File

@ -52,3 +52,8 @@
-keep class com.android.launcher3.MemoryDumpActivity {
*;
}
-keep class com.android.launcher3.PreloadIconDrawable {
public float getAnimationProgress();
public void setAnimationProgress(float);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -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>

View 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();
}
}
}

View 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);

View 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;
}

View File

@ -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;
}
}

View File

@ -16,13 +16,9 @@
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;