Adding a circular progress bar for preloader icons
Change-Id: I1b5ba61c01a16a8cb5d3f9e31f827f8c99a1ffc9
This commit is contained in:
parent
f599ccfe96
commit
3484638cad
|
@ -52,3 +52,8 @@
|
||||||
-keep class com.android.launcher3.MemoryDumpActivity {
|
-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 |
|
@ -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>
|
|
|
@ -136,7 +136,8 @@ public class BubbleTextView extends TextView {
|
||||||
setContentDescription(info.contentDescription);
|
setContentDescription(info.contentDescription);
|
||||||
}
|
}
|
||||||
setTag(info);
|
setTag(info);
|
||||||
if (info.isPromise()) {
|
|
||||||
|
if (info.wasPromise) {
|
||||||
applyState();
|
applyState();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -431,42 +432,55 @@ public class BubbleTextView extends TextView {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void applyState() {
|
public void applyState() {
|
||||||
int alpha = getResources().getInteger(R.integer.promise_icon_alpha);
|
final int progressLevel;
|
||||||
final int state = getState();
|
final int state = getState();
|
||||||
if (DEBUG) Log.d(TAG, "applying icon state: " + state);
|
if (DEBUG) Log.d(TAG, "applying icon state: " + state);
|
||||||
|
|
||||||
switch(state) {
|
switch(state) {
|
||||||
case ShortcutInfo.PACKAGE_STATE_DEFAULT:
|
case ShortcutInfo.PACKAGE_STATE_DEFAULT:
|
||||||
super.setText(mDefaultText);
|
super.setText(mDefaultText);
|
||||||
alpha = 255;
|
progressLevel = 100;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ShortcutInfo.PACKAGE_STATE_ENQUEUED:
|
case ShortcutInfo.PACKAGE_STATE_ENQUEUED:
|
||||||
setText(R.string.package_state_enqueued);
|
setText(R.string.package_state_enqueued);
|
||||||
|
progressLevel = 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ShortcutInfo.PACKAGE_STATE_DOWNLOADING:
|
case ShortcutInfo.PACKAGE_STATE_DOWNLOADING:
|
||||||
setText(R.string.package_state_downloading);
|
setText(R.string.package_state_downloading);
|
||||||
|
// TODO(sunnygoyal): fix progress
|
||||||
|
progressLevel = 30;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ShortcutInfo.PACKAGE_STATE_INSTALLING:
|
case ShortcutInfo.PACKAGE_STATE_INSTALLING:
|
||||||
setText(R.string.package_state_installing);
|
setText(R.string.package_state_installing);
|
||||||
|
progressLevel = 100;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ShortcutInfo.PACKAGE_STATE_ERROR:
|
case ShortcutInfo.PACKAGE_STATE_ERROR:
|
||||||
setText(R.string.package_state_error);
|
setText(R.string.package_state_error);
|
||||||
|
progressLevel = 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ShortcutInfo.PACKAGE_STATE_UNKNOWN:
|
case ShortcutInfo.PACKAGE_STATE_UNKNOWN:
|
||||||
default:
|
default:
|
||||||
|
progressLevel = 0;
|
||||||
setText(R.string.package_state_unknown);
|
setText(R.string.package_state_unknown);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (DEBUG) Log.d(TAG, "setting icon alpha to: " + alpha);
|
|
||||||
Drawable[] drawables = getCompoundDrawables();
|
Drawable[] drawables = getCompoundDrawables();
|
||||||
for (int i = 0; i < drawables.length; i++) {
|
Drawable top = drawables[1];
|
||||||
if (drawables[i] != null) {
|
if ((top != null) && !(top instanceof PreloadIconDrawable)) {
|
||||||
drawables[i].setAlpha(alpha);
|
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();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -605,7 +605,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
|
||||||
computePreviewDrawingParams(mAnimParams.drawable);
|
computePreviewDrawingParams(mAnimParams.drawable);
|
||||||
} else {
|
} else {
|
||||||
v = (TextView) items.get(0);
|
v = (TextView) items.get(0);
|
||||||
d = v.getCompoundDrawables()[1];
|
d = getTopDrawable(v);
|
||||||
computePreviewDrawingParams(d);
|
computePreviewDrawingParams(d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -614,7 +614,7 @@ public class FolderIcon extends FrameLayout implements FolderListener {
|
||||||
for (int i = nItemsInPreview - 1; i >= 0; i--) {
|
for (int i = nItemsInPreview - 1; i >= 0; i--) {
|
||||||
v = (TextView) items.get(i);
|
v = (TextView) items.get(i);
|
||||||
if (!mHiddenItems.contains(v.getTag())) {
|
if (!mHiddenItems.contains(v.getTag())) {
|
||||||
d = v.getCompoundDrawables()[1];
|
d = getTopDrawable(v);
|
||||||
mParams = computePreviewItemDrawingParams(i, mParams);
|
mParams = computePreviewItemDrawingParams(i, mParams);
|
||||||
mParams.drawable = d;
|
mParams.drawable = d;
|
||||||
drawPreviewItem(canvas, mParams);
|
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,
|
private void animateFirstItem(final Drawable d, int duration, final boolean reverse,
|
||||||
final Runnable onCompleteRunnable) {
|
final Runnable onCompleteRunnable) {
|
||||||
final PreviewItemDrawingParams finalParams = computePreviewItemDrawingParams(0, null);
|
final PreviewItemDrawingParams finalParams = computePreviewItemDrawingParams(0, null);
|
||||||
|
|
|
@ -3047,6 +3047,7 @@ public class LauncherModel extends BroadcastReceiver
|
||||||
info.setIcon(mIconCache.getIcon(intent, info.title.toString(), info.user));
|
info.setIcon(mIconCache.getIcon(intent, info.title.toString(), info.user));
|
||||||
info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
|
info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
|
||||||
info.restoredIntent = intent;
|
info.restoredIntent = intent;
|
||||||
|
info.wasPromise = true;
|
||||||
info.setState(ShortcutInfo.PACKAGE_STATE_UNKNOWN);
|
info.setState(ShortcutInfo.PACKAGE_STATE_UNKNOWN);
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -16,13 +16,9 @@
|
||||||
|
|
||||||
package com.android.launcher3;
|
package com.android.launcher3;
|
||||||
|
|
||||||
import android.content.ComponentName;
|
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
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.graphics.Bitmap;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
@ -96,6 +92,11 @@ public class ShortcutInfo extends ItemInfo {
|
||||||
*/
|
*/
|
||||||
Intent restoredIntent;
|
Intent restoredIntent;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is set once to indicate that it was a promise info at some point of its life.
|
||||||
|
*/
|
||||||
|
boolean wasPromise = false;
|
||||||
|
|
||||||
ShortcutInfo() {
|
ShortcutInfo() {
|
||||||
itemType = LauncherSettings.BaseLauncherColumns.ITEM_TYPE_SHORTCUT;
|
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) {
|
Bitmap icon, UserHandleCompat user) {
|
||||||
this();
|
this();
|
||||||
this.intent = intent;
|
this.intent = intent;
|
||||||
|
|
Loading…
Reference in New Issue