diff options
-rw-r--r-- | res/values/strings.xml | 17 | ||||
-rw-r--r-- | src/com/android/launcher3/BubbleTextView.java | 34 | ||||
-rw-r--r-- | src/com/android/launcher3/Folder.java | 1 | ||||
-rw-r--r-- | src/com/android/launcher3/Launcher.java | 52 | ||||
-rw-r--r-- | src/com/android/launcher3/LauncherModel.java | 7 | ||||
-rw-r--r-- | src/com/android/launcher3/ShortcutInfo.java | 32 | ||||
-rw-r--r-- | src/com/android/launcher3/Workspace.java | 41 |
7 files changed, 159 insertions, 25 deletions
diff --git a/res/values/strings.xml b/res/values/strings.xml index ad3a1c4fb..b7f4505a9 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -277,4 +277,21 @@ s --> <string name="package_state_unknown">Unknown</string> <!-- Label on an icon that references an uninstalled package, for which restore from market has failed. [CHAR_LIMIT=15] --> <string name="package_state_error">Not restored</string> + + <!-- Button for abandoned promises dialog, that removes all abandoned promise icons. --> + <string name="abandoned_clean_all">Remove All</string> + <!-- Button for abandoned promises dialog, to removes this abandoned promise icon. --> + <string name="abandoned_clean_this">Remove</string> + <!-- Button for abandoned promise dialog, to search in the market for the missing package. --> + <string name="abandoned_search">Search</string> + <!-- Title for abandoned promise dialog. --> + <string name="abandoned_promises_title">This Package is not Installed</string> + <!-- Explanation for abandoned promise dialog. --> + <plurals name="abandoned_promises_explanation"> + <item quantity="one">The package for this icon is not installed. You many remove it, or + attempt to search for the package and install it manually.</item> + <item quantity="other">The package for this icon is not installed. You many remove all + similarly broken icons, remove only this icon, or attempt to search for the package and + install it manually.</item> + </plurals> </resources> diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index 95300c133..992ac5897 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -74,8 +74,6 @@ public class BubbleTextView extends TextView { private CheckLongPressHelper mLongPressHelper; private int mInstallState; - private int mState; - private CharSequence mDefaultText = ""; public BubbleTextView(Context context) { @@ -124,10 +122,9 @@ public class BubbleTextView extends TextView { Drawable iconDrawable = Utilities.createIconDrawable(b); setCompoundDrawables(null, iconDrawable, null, null); setCompoundDrawablePadding(grid.iconDrawablePaddingPx); - setText(info.title); setTag(info); if (info.isPromise()) { - setState(ShortcutInfo.PACKAGE_STATE_UNKNOWN); // TODO: persist this state somewhere + applyState(); } } @@ -150,6 +147,11 @@ public class BubbleTextView extends TextView { LauncherModel.checkItemInfo((ItemInfo) tag); } super.setTag(tag); + if (tag instanceof ShortcutInfo) { + final ShortcutInfo info = (ShortcutInfo) tag; + mDefaultText = info.title; + setText(mDefaultText); + } } @Override @@ -415,19 +417,12 @@ public class BubbleTextView extends TextView { mLongPressHelper.cancelLongPress(); } - public void setState(int state) { - if (mState == ShortcutInfo.PACKAGE_STATE_DEFAULT && mState != state) { - mDefaultText = getText(); - } - mState = state; - applyState(); - } - - private void applyState() { + public void applyState() { int alpha = getResources().getInteger(R.integer.promise_icon_alpha); - if (DEBUG) Log.d(TAG, "applying icon state: " + mState); + final int state = getState(); + if (DEBUG) Log.d(TAG, "applying icon state: " + state); - switch(mState) { + switch(state) { case ShortcutInfo.PACKAGE_STATE_DEFAULT: super.setText(mDefaultText); alpha = 255; @@ -462,4 +457,13 @@ public class BubbleTextView extends TextView { } } } + + private int getState() { + if (! (getTag() instanceof ShortcutInfo)) { + return ShortcutInfo.PACKAGE_STATE_DEFAULT; + } else { + ShortcutInfo info = (ShortcutInfo) getTag(); + return info.getState(); + } + } } diff --git a/src/com/android/launcher3/Folder.java b/src/com/android/launcher3/Folder.java index e900c2b5a..896be70cf 100644 --- a/src/com/android/launcher3/Folder.java +++ b/src/com/android/launcher3/Folder.java @@ -575,6 +575,7 @@ public class Folder extends LinearLayout implements DragSource, View.OnClickList textView.setTextColor(getResources().getColor(R.color.folder_items_text_color)); textView.setShadowsEnabled(false); textView.setGlowColor(getResources().getColor(R.color.folder_items_glow_color)); + textView.applyState(); textView.setOnClickListener(this); textView.setOnLongClickListener(this); diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index f8c9f7b38..4ca3c5092 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -28,6 +28,7 @@ import android.annotation.TargetApi; import android.app.Activity; import android.app.ActivityManager; import android.app.ActivityOptions; +import android.app.AlertDialog; import android.app.SearchManager; import android.appwidget.AppWidgetHostView; import android.appwidget.AppWidgetManager; @@ -38,6 +39,7 @@ import android.content.ComponentCallbacks2; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; @@ -2517,7 +2519,7 @@ public class Launcher extends Activity * * @param v The view that was clicked. Must be a tagged with a {@link ShortcutInfo}. */ - protected void onClickAppShortcut(View v) { + protected void onClickAppShortcut(final View v) { if (LOGD) Log.d(TAG, "onClickAppShortcut"); Object tag = v.getTag(); if (!(tag instanceof ShortcutInfo)) { @@ -2541,7 +2543,55 @@ public class Launcher extends Activity } } + // Check for abandoned promise + if (shortcut.isAbandoned() && v instanceof BubbleTextView) { + final ArrayList<BubbleTextView> abandoned = + mWorkspace.getAbandonedPromises(new ArrayList<BubbleTextView>()); + AlertDialog.Builder builder = new AlertDialog.Builder(this); + builder.setTitle(R.string.abandoned_promises_title); + builder.setMessage(getResources().getQuantityString( + R.plurals.abandoned_promises_explanation, abandoned.size())); + builder.setPositiveButton(R.string.abandoned_search, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + startAppShortcutActivity(v); + } + } + ); + if (abandoned.size() > 1) { + builder.setNegativeButton(R.string.abandoned_clean_all, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + final UserHandleCompat user = UserHandleCompat.myUserHandle(); + mWorkspace.removeAbandonedPromises(abandoned, user); + } + } + ); + } + builder.setNeutralButton(R.string.abandoned_clean_this, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + final BubbleTextView bubble = (BubbleTextView) v; + final UserHandleCompat user = UserHandleCompat.myUserHandle(); + mWorkspace.removeAbandonedPromise(bubble, user); + } + }); + builder.create().show(); + return; + } + // Start activities + startAppShortcutActivity(v); + } + + private void startAppShortcutActivity(View v) { + Object tag = v.getTag(); + if (!(tag instanceof ShortcutInfo)) { + throw new IllegalArgumentException("Input must be a Shortcut"); + } + final ShortcutInfo shortcut = (ShortcutInfo) tag; + final Intent intent = shortcut.intent; + int[] pos = new int[2]; v.getLocationOnScreen(pos); intent.setSourceBounds(new Rect(pos[0], pos[1], diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 07389c9a7..4d0ec78dd 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -2975,6 +2975,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.setState(ShortcutInfo.PACKAGE_STATE_UNKNOWN); return info; } @@ -3088,6 +3089,9 @@ public class LauncherModel extends BroadcastReceiver if (i instanceof ShortcutInfo) { ShortcutInfo info = (ShortcutInfo) i; ComponentName cn = info.intent.getComponent(); + if (info.restoredIntent != null) { + cn = info.restoredIntent.getComponent(); + } if (cn != null && f.filterItem(null, info, cn)) { filtered.add(info); } @@ -3095,6 +3099,9 @@ public class LauncherModel extends BroadcastReceiver FolderInfo info = (FolderInfo) i; for (ShortcutInfo s : info.contents) { ComponentName cn = s.intent.getComponent(); + if (s.restoredIntent != null) { + cn = s.restoredIntent.getComponent(); + } if (cn != null && f.filterItem(info, s, cn)) { filtered.add(s); } diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java index f40cf9fa1..a84a90354 100644 --- a/src/com/android/launcher3/ShortcutInfo.java +++ b/src/com/android/launcher3/ShortcutInfo.java @@ -36,22 +36,22 @@ import java.util.Arrays; */ public class ShortcutInfo extends ItemInfo { - /** This package is not installed, and there is no other information available. */ + /** {@link #mState} meaning this package is not installed, and there is no other information. */ public static final int PACKAGE_STATE_UNKNOWN = -2; - /** This package is not installed, because installation failed. */ + /** {@link #mState} meaning this package is not installed, because installation failed. */ public static final int PACKAGE_STATE_ERROR = -1; - /** This package is installed. This is the typical case */ + /** {@link #mState} meaning this package is installed. This is the typical case. */ public static final int PACKAGE_STATE_DEFAULT = 0; - /** This package is not installed, but some external entity has promised to install it. */ + /** {@link #mState} meaning some external entity has promised to install this package. */ public static final int PACKAGE_STATE_ENQUEUED = 1; - /** This package is not installed, but some external entity is downloading it. */ + /** {@link #mState} meaning but some external entity is downloading this package. */ public static final int PACKAGE_STATE_DOWNLOADING = 2; - /** This package is not installed, but some external entity is installing it. */ + /** {@link #mState} meaning some external entity is installing this package. */ public static final int PACKAGE_STATE_INSTALLING = 3; /** @@ -82,6 +82,11 @@ public class ShortcutInfo extends ItemInfo { */ private Bitmap mIcon; + /** + * The installation state of the package that this shortcut represents. + */ + protected int mState; + long firstInstallTime; int flags = 0; @@ -110,6 +115,7 @@ public class ShortcutInfo extends ItemInfo { if (restoredIntent != null) { intent = restoredIntent; restoredIntent = null; + mState = PACKAGE_STATE_DEFAULT; } } @@ -218,5 +224,19 @@ public class ShortcutInfo extends ItemInfo { && pkgName != null && pkgName.equals(restoredIntent.getComponent().getPackageName()); } + + public boolean isAbandoned() { + return isPromise() + && (mState == ShortcutInfo.PACKAGE_STATE_ERROR + || mState == ShortcutInfo.PACKAGE_STATE_UNKNOWN); + } + + public int getState() { + return mState; + } + + public void setState(int state) { + mState = state; + } } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 6afea8268..74ef1d46b 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -98,8 +98,8 @@ public class Workspace extends SmoothPagedView private static final float ALPHA_CUTOFF_THRESHOLD = 0.01f; - private static final boolean MAP_NO_RECURSE = false; - private static final boolean MAP_RECURSE = true; + static final boolean MAP_NO_RECURSE = false; + static final boolean MAP_RECURSE = true; // These animators are used to fade the children's outlines private ObjectAnimator mChildrenOutlineFadeInAnimation; @@ -4857,6 +4857,40 @@ public class Workspace extends SmoothPagedView }); } + ArrayList<BubbleTextView> getAbandonedPromises(final ArrayList<BubbleTextView> abandoned) { + mapOverShortcuts(Workspace.MAP_RECURSE, new Workspace.ShortcutOperator() { + @Override + public boolean evaluate(ItemInfo info, View view, View parent) { + if (info instanceof ShortcutInfo + && ((ShortcutInfo) info).isAbandoned() + && view instanceof BubbleTextView) { + abandoned.add((BubbleTextView) view); + } + return false; + } + }); + return abandoned; + } + public void removeAbandonedPromise(BubbleTextView view, UserHandleCompat user) { + ArrayList<BubbleTextView> views = new ArrayList<BubbleTextView>(1); + views.add(view); + removeAbandonedPromises(views, user); + } + + public void removeAbandonedPromises(ArrayList<BubbleTextView> views, UserHandleCompat user) { + HashSet<ComponentName> cns = new HashSet<ComponentName>(views.size()); + for (final BubbleTextView bubble : views) { + if (bubble.getTag() != null && bubble.getTag() instanceof ShortcutInfo) { + final ShortcutInfo shortcut = (ShortcutInfo) bubble.getTag(); + if (shortcut.isAbandoned()) { + cns.add(shortcut.getRestoredIntent().getComponent()); + LauncherModel.deleteItemFromDatabase(mLauncher, shortcut); + } + } + } + removeItemsByComponentName(cns, user); + } + public void updatePackageState(final String pkgName, final int state) { mapOverShortcuts(MAP_RECURSE, new ShortcutOperator() { @Override @@ -4864,7 +4898,8 @@ public class Workspace extends SmoothPagedView if (info instanceof ShortcutInfo && ((ShortcutInfo) info).isPromiseFor(pkgName) && v instanceof BubbleTextView) { - ((BubbleTextView)v).setState(state); + ((ShortcutInfo) info).setState(state); + ((BubbleTextView)v).applyState(); } // process all the shortcuts return false; |