From e7b8cd9e4f8d38d0445f3a032fafd44332f70878 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Wed, 27 Aug 2014 14:04:33 -0700 Subject: Improving restored widget behavior > Clicking a broken widget install shows a dialog similar to an app icon > Clicking remove on the dialog removed all components for the corresponding package > Widget pending view shows 'Setup' text instead of icon, if there is enough space Change-Id: I82ec0a1ee9542c1e3b860e6e00798a80450dce3c --- src/com/android/launcher3/Launcher.java | 50 ++++++--- .../android/launcher3/LauncherAppWidgetInfo.java | 2 +- src/com/android/launcher3/LauncherModel.java | 116 ++++++++++++--------- .../launcher3/PendingAppWidgetHostView.java | 82 ++++++++++++--- src/com/android/launcher3/Workspace.java | 15 +-- 5 files changed, 170 insertions(+), 95 deletions(-) diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index e134d1b4d..147500125 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -2455,9 +2455,9 @@ public class Launcher extends Activity /** * Event handler for the app widget view which has not fully restored. */ - public void onClickPendingWidget(PendingAppWidgetHostView v) { + public void onClickPendingWidget(final PendingAppWidgetHostView v) { + final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) v.getTag(); if (v.isReadyForClickSetup()) { - LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) v.getTag(); int widgetId = info.appWidgetId; AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(widgetId); if (appWidgetInfo != null) { @@ -2468,6 +2468,19 @@ public class Launcher extends Activity AppWidgetManagerCompat.getInstance(this).startConfigActivity(appWidgetInfo, info.appWidgetId, this, mAppWidgetHost, REQUEST_RECONFIGURE_APPWIDGET); } + } else if (info.installProgress < 0) { + // The install has not been queued + final String packageName = info.providerName.getPackageName(); + showBrokenAppInstallDialog(packageName, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + startActivitySafely(v, LauncherModel.getMarketIntent(packageName), info); + } + }); + } else { + // Download has started. + final String packageName = info.providerName.getPackageName(); + startActivitySafely(v, LauncherModel.getMarketIntent(packageName), info); } } @@ -2526,6 +2539,23 @@ public class Launcher extends Activity } } + private void showBrokenAppInstallDialog(final String packageName, + DialogInterface.OnClickListener onSearchClickListener) { + new AlertDialog.Builder(this) + .setTitle(R.string.abandoned_promises_title) + .setMessage(R.string.abandoned_promise_explanation) + .setPositiveButton(R.string.abandoned_search, onSearchClickListener) + .setNeutralButton(R.string.abandoned_clean_this, + new DialogInterface.OnClickListener() { + public void onClick(DialogInterface dialog, int id) { + final UserHandleCompat user = UserHandleCompat.myUserHandle(); + mWorkspace.removeAbandonedPromise(packageName, user); + } + }) + .create().show(); + return; + } + /** * Event handler for an app shortcut click. * @@ -2557,25 +2587,13 @@ public class Launcher extends Activity // Check for abandoned promise if (shortcut.isAbandoned() && v instanceof BubbleTextView) { - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle(R.string.abandoned_promises_title); - builder.setMessage(R.string.abandoned_promise_explanation); - builder.setPositiveButton(R.string.abandoned_search, + showBrokenAppInstallDialog( + shortcut.getRestoredIntent().getComponent().getPackageName(), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { startAppShortcutOrInfoActivity(v); } - } - ); - 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; } diff --git a/src/com/android/launcher3/LauncherAppWidgetInfo.java b/src/com/android/launcher3/LauncherAppWidgetInfo.java index 47554824f..50528b1bd 100644 --- a/src/com/android/launcher3/LauncherAppWidgetInfo.java +++ b/src/com/android/launcher3/LauncherAppWidgetInfo.java @@ -70,7 +70,7 @@ public class LauncherAppWidgetInfo extends ItemInfo { /** * Indicates the installation progress of the widget provider */ - int installProgress; + int installProgress = -1; private boolean mHasNotifiedInitialWidgetSizeChanged; diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index bcb45011e..5e8e2ad0e 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -1077,45 +1077,73 @@ public class LauncherModel extends BroadcastReceiver | ((int) screen & 0xFF) << 16 | (localCellX & 0xFF) << 8 | (localCellY & 0xFF); } + /** + * Removes all the items from the database corresponding to the specified package. + */ + static void deletePackageFromDatabase(Context context, final String pn, + final UserHandleCompat user) { + ItemInfoFilter filter = new ItemInfoFilter() { + @Override + public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn) { + return cn.getPackageName().equals(pn) && info.user.equals(user); + } + }; + ArrayList infos = filterItemInfos(sBgItemsIdMap.values(), filter); + deleteItemsFromDatabase(context, infos); + } + /** * Removes the specified item from the database * @param context * @param item */ static void deleteItemFromDatabase(Context context, final ItemInfo item) { + ArrayList items = new ArrayList(); + items.add(item); + deleteItemsFromDatabase(context, items); + } + + /** + * Removes the specified items from the database + * @param context + * @param item + */ + static void deleteItemsFromDatabase(Context context, final ArrayList items) { final ContentResolver cr = context.getContentResolver(); - final Uri uriToDelete = LauncherSettings.Favorites.getContentUri(item.id, false); Runnable r = new Runnable() { public void run() { - cr.delete(uriToDelete, null, null); + for (ItemInfo item : items) { + final Uri uri = LauncherSettings.Favorites.getContentUri(item.id, false); + cr.delete(uri, null, null); - // Lock on mBgLock *after* the db operation - synchronized (sBgLock) { - switch (item.itemType) { - case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: - sBgFolders.remove(item.id); - for (ItemInfo info: sBgItemsIdMap.values()) { - if (info.container == item.id) { - // We are deleting a folder which still contains items that - // think they are contained by that folder. - String msg = "deleting a folder (" + item + ") which still " + - "contains items (" + info + ")"; - Log.e(TAG, msg); + // Lock on mBgLock *after* the db operation + synchronized (sBgLock) { + switch (item.itemType) { + case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: + sBgFolders.remove(item.id); + for (ItemInfo info: sBgItemsIdMap.values()) { + if (info.container == item.id) { + // We are deleting a folder which still contains items that + // think they are contained by that folder. + String msg = "deleting a folder (" + item + ") which still " + + "contains items (" + info + ")"; + Log.e(TAG, msg); + } } - } - sBgWorkspaceItems.remove(item); - break; - case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: - case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: - sBgWorkspaceItems.remove(item); - break; - case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: - sBgAppWidgets.remove((LauncherAppWidgetInfo) item); - break; + sBgWorkspaceItems.remove(item); + break; + case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION: + case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: + sBgWorkspaceItems.remove(item); + break; + case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET: + sBgAppWidgets.remove((LauncherAppWidgetInfo) item); + break; + } + sBgItemsIdMap.remove(item.id); + sBgDbIconCache.remove(item); } - sBgItemsIdMap.remove(item.id); - sBgDbIconCache.remove(item); } } }; @@ -2982,17 +3010,12 @@ public class LauncherModel extends BroadcastReceiver } // Remove all the components associated with this package for (String pn : removedPackageNames) { - ArrayList infos = getItemInfoForPackageName(pn, mUser); - for (ItemInfo i : infos) { - deleteItemFromDatabase(context, i); - } + deletePackageFromDatabase(context, pn, mUser); } // Remove all the specific components for (AppInfo a : removedApps) { ArrayList infos = getItemInfoForComponentName(a.componentName, mUser); - for (ItemInfo i : infos) { - deleteItemFromDatabase(context, i); - } + deleteItemsFromDatabase(context, infos); } if (!removedPackageNames.isEmpty() || !removedApps.isEmpty()) { // Remove any queued items from the install queue @@ -3101,17 +3124,17 @@ public class LauncherModel extends BroadcastReceiver * to the market page for the item. */ private Intent getRestoredItemIntent(Cursor c, Context context, Intent intent) { - final boolean debug = false; ComponentName componentName = intent.getComponent(); - Intent marketIntent = new Intent(Intent.ACTION_VIEW); - Uri marketUri = new Uri.Builder() + return getMarketIntent(componentName.getPackageName()); + } + + static Intent getMarketIntent(String packageName) { + return new Intent(Intent.ACTION_VIEW) + .setData(new Uri.Builder() .scheme("market") .authority("details") - .appendQueryParameter("id", componentName.getPackageName()) - .build(); - if (debug) Log.d(TAG, "manufactured intent uri: " + marketUri.toString()); - marketIntent.setData(marketUri); - return marketIntent; + .appendQueryParameter("id", packageName) + .build()); } /** @@ -3236,17 +3259,6 @@ public class LauncherModel extends BroadcastReceiver return new ArrayList(filtered); } - private ArrayList getItemInfoForPackageName(final String pn, - final UserHandleCompat user) { - ItemInfoFilter filter = new ItemInfoFilter() { - @Override - public boolean filterItem(ItemInfo parent, ItemInfo info, ComponentName cn) { - return cn.getPackageName().equals(pn) && info.user.equals(user); - } - }; - return filterItemInfos(sBgItemsIdMap.values(), filter); - } - private ArrayList getItemInfoForComponentName(final ComponentName cname, final UserHandleCompat user) { ItemInfoFilter filter = new ItemInfoFilter() { diff --git a/src/com/android/launcher3/PendingAppWidgetHostView.java b/src/com/android/launcher3/PendingAppWidgetHostView.java index 04014366c..d23a33033 100644 --- a/src/com/android/launcher3/PendingAppWidgetHostView.java +++ b/src/com/android/launcher3/PendingAppWidgetHostView.java @@ -24,6 +24,10 @@ import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Bundle; +import android.text.Layout; +import android.text.StaticLayout; +import android.text.TextPaint; +import android.util.TypedValue; import android.view.View; import android.view.View.OnClickListener; @@ -46,12 +50,19 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implemen private boolean mDrawableSizeChanged; + private final TextPaint mPaint; + private Layout mSetupTextLayout; + public PendingAppWidgetHostView(Context context, LauncherAppWidgetInfo info) { super(context); mInfo = info; mStartState = info.restoreStatus; mIconLookupIntent = new Intent().setComponent(info.providerName); + mPaint = new TextPaint(); + mPaint.setColor(0xFFFFFFFF); + mPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, + getDeviceProfile().iconTextSizePx, getResources().getDisplayMetrics())); setBackgroundResource(R.drawable.quantum_panel_dark); setWillNotDraw(false); } @@ -127,7 +138,7 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implemen public void applyState() { if (mDrawable != null) { - mDrawable.setLevel(mInfo.installProgress); + mDrawable.setLevel(Math.max(mInfo.installProgress, 0)); } } @@ -165,27 +176,66 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implemen mDrawable.draw(canvas); } else if ((mCenterDrawable != null) && (mTopCornerDrawable != null)) { if (mDrawableSizeChanged) { - int iconSize = getResources().getDimensionPixelSize(R.dimen.app_icon_size); + DeviceProfile grid = getDeviceProfile(); + int iconSize = grid.iconSizePx; int paddingTop = getPaddingTop(); + int paddingBottom = getPaddingBottom(); int paddingLeft = getPaddingLeft(); - - int size = Math.min(iconSize, Math.min( - getWidth() - paddingLeft - getPaddingRight(), - getHeight() - paddingTop - getPaddingBottom())); - mRect.set(0, 0, size, size); - mRect.offsetTo((getWidth() - mRect.width()) / 2, (getHeight() - mRect.height()) / 2); - mCenterDrawable.setBounds(mRect); - - size = Math.min(size / 2, - Math.max(mRect.top - paddingTop, mRect.left - paddingLeft)); - mTopCornerDrawable.setBounds(paddingLeft, paddingTop, - paddingLeft + size, paddingTop + size); + int paddingRight = getPaddingRight(); + + int availableWidth = getWidth() - paddingLeft - paddingRight; + int availableHeight = getHeight() - paddingTop - paddingBottom; + + // Recreate the setup text. + mSetupTextLayout = new StaticLayout( + getResources().getText(R.string.gadget_setup_text), mPaint, availableWidth, + Layout.Alignment.ALIGN_CENTER, 1, 0, true); + if (mSetupTextLayout.getLineCount() == 1) { + // The text fits in a single line. No need to draw the setup icon. + int size = Math.min(iconSize, Math.min(availableWidth, + availableHeight - mSetupTextLayout.getHeight())); + mRect.set(0, 0, size, size); + mRect.offsetTo((getWidth() - mRect.width()) / 2, + (getHeight() - mRect.height() - mSetupTextLayout.getHeight() + - grid.iconDrawablePaddingPx) / 2); + + mTopCornerDrawable.setBounds(mRect); + + // Update left and top to indicate the position where the text will be drawn. + mRect.left = paddingLeft; + mRect.top = mRect.bottom + grid.iconDrawablePaddingPx; + } else { + // The text can't be drawn in a single line. Draw a setup icon instead. + mSetupTextLayout = null; + int size = Math.min(iconSize, Math.min( + getWidth() - paddingLeft - paddingRight, + getHeight() - paddingTop - paddingBottom)); + mRect.set(0, 0, size, size); + mRect.offsetTo((getWidth() - mRect.width()) / 2, (getHeight() - mRect.height()) / 2); + mCenterDrawable.setBounds(mRect); + + size = Math.min(size / 2, + Math.max(mRect.top - paddingTop, mRect.left - paddingLeft)); + mTopCornerDrawable.setBounds(paddingLeft, paddingTop, + paddingLeft + size, paddingTop + size); + } mDrawableSizeChanged = false; } - mCenterDrawable.draw(canvas); - mTopCornerDrawable.draw(canvas); + if (mSetupTextLayout == null) { + mCenterDrawable.draw(canvas); + mTopCornerDrawable.draw(canvas); + } else { + canvas.save(); + canvas.translate(mRect.left, mRect.top); + mSetupTextLayout.draw(canvas); + canvas.restore(); + mTopCornerDrawable.draw(canvas); + } } } + private DeviceProfile getDeviceProfile() { + return LauncherAppState.getInstance().getDynamicGrid().getDeviceProfile(); + } } diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 866125147..9d9730617 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -4847,16 +4847,11 @@ public class Workspace extends SmoothPagedView restorePendingWidgets(pkgNames); } - public void removeAbandonedPromise(BubbleTextView abandonedIcon, UserHandleCompat user) { - if (abandonedIcon.getTag() != null && abandonedIcon.getTag() instanceof ShortcutInfo) { - final ShortcutInfo shortcut = (ShortcutInfo) abandonedIcon.getTag(); - if (shortcut.isAbandoned()) { - HashSet cns = new HashSet(1); - cns.add(shortcut.getRestoredIntent().getComponent()); - LauncherModel.deleteItemFromDatabase(mLauncher, shortcut); - removeItemsByComponentName(cns, user); - } - } + public void removeAbandonedPromise(String packageName, UserHandleCompat user) { + ArrayList packages = new ArrayList(1); + packages.add(packageName); + LauncherModel.deletePackageFromDatabase(mLauncher, packageName, user); + removeItemsByPackageName(packages, user); } public void updatePackageState(ArrayList installInfos) { -- cgit v1.2.3