summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AndroidManifest.xml7
-rw-r--r--res/drawable/bg_appwidget_not_ready.xml24
-rw-r--r--res/layout/appwidget_not_ready.xml28
-rw-r--r--res/values/colors.xml1
-rw-r--r--res/values/strings.xml3
-rw-r--r--src/com/android/launcher3/AppWidgetsRestoredReceiver.java90
-rw-r--r--src/com/android/launcher3/DeleteDropTarget.java2
-rw-r--r--src/com/android/launcher3/Launcher.java24
-rw-r--r--src/com/android/launcher3/LauncherAppWidgetHostView.java26
-rw-r--r--src/com/android/launcher3/LauncherAppWidgetInfo.java22
-rw-r--r--src/com/android/launcher3/LauncherModel.java76
-rw-r--r--src/com/android/launcher3/Workspace.java64
12 files changed, 327 insertions, 40 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 67d86d2fa..49efc5d89 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -188,6 +188,13 @@
</intent-filter>
</receiver>
+ <!-- Intent received used to initialize a restored widget -->
+ <receiver android:name="com.android.launcher3.AppWidgetsRestoredReceiver" >
+ <intent-filter>
+ <action android:name="android.appwidget.action.APPWIDGET_HOST_RESTORED"/>
+ </intent-filter>
+ </receiver>
+
<!-- New user initialization; set up initial wallpaper -->
<receiver
android:name="com.android.launcher3.UserInitializeReceiver"
diff --git a/res/drawable/bg_appwidget_not_ready.xml b/res/drawable/bg_appwidget_not_ready.xml
new file mode 100644
index 000000000..a8b56c2f8
--- /dev/null
+++ b/res/drawable/bg_appwidget_not_ready.xml
@@ -0,0 +1,24 @@
+<?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.
+-->
+
+<shape xmlns:android="http://schemas.android.com/apk/res/android"
+ android:shape="rectangle" >
+
+ <corners android:radius="5dp" />
+
+ <solid android:color="@android:color/white" />
+
+</shape> \ No newline at end of file
diff --git a/res/layout/appwidget_not_ready.xml b/res/layout/appwidget_not_ready.xml
new file mode 100644
index 000000000..f5f2aab21
--- /dev/null
+++ b/res/layout/appwidget_not_ready.xml
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2009 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.
+-->
+
+<TextView xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:paddingLeft="20dip"
+ android:paddingRight="20dip"
+ android:layout_margin="10dip"
+ android:gravity="center"
+ android:background="@drawable/bg_appwidget_not_ready"
+ android:textAppearance="?android:attr/textAppearanceMediumInverse"
+ android:textColor="@color/appwidget_not_ready_color"
+ android:text="@string/gadget_pending_text"
+ />
diff --git a/res/values/colors.xml b/res/values/colors.xml
index d1d33a0f6..27a5b61ee 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -26,6 +26,7 @@
<color name="bubble_dark_background">#20000000</color>
<color name="appwidget_error_color">#FCCC</color>
+ <color name="appwidget_not_ready_color">#F48F</color>
<color name="workspace_all_apps_and_delete_zone_text_color">#CCFFFFFF</color>
<color name="workspace_all_apps_and_delete_zone_text_shadow_color">#A0000000</color>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 7c5ee9c86..1ee1be6fd 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -190,6 +190,9 @@ s -->
<!-- Text to show user in place of a gadget when we can't display it properly -->
<string name="gadget_error_text">Problem loading widget</string>
+ <!-- Text to show user in place of a gadget when it is not yet ready/initialized. -->
+ <string name="gadget_pending_text" translatable="false">Widget not ready</string>
+
<!-- Text to inform the user that they can't uninstall a system application -->
<string name="uninstall_system_app_text">This is a system app and can\'t be uninstalled.</string>
diff --git a/src/com/android/launcher3/AppWidgetsRestoredReceiver.java b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
new file mode 100644
index 000000000..9fef7f934
--- /dev/null
+++ b/src/com/android/launcher3/AppWidgetsRestoredReceiver.java
@@ -0,0 +1,90 @@
+package com.android.launcher3;
+
+import android.appwidget.AppWidgetHost;
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProviderInfo;
+import android.content.BroadcastReceiver;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.os.AsyncTask;
+import android.util.Log;
+
+import com.android.launcher3.LauncherSettings.Favorites;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class AppWidgetsRestoredReceiver extends BroadcastReceiver {
+
+ private static final String TAG = "AppWidgetsRestoredReceiver";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (AppWidgetManager.ACTION_APPWIDGET_HOST_RESTORED.equals(intent.getAction())) {
+ int[] oldIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_OLD_IDS);
+ int[] newIds = intent.getIntArrayExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS);
+ if (oldIds.length == newIds.length) {
+ restoreAppWidgetIds(context, oldIds, newIds);
+ } else {
+ Log.e(TAG, "Invalid host restored received");
+ }
+ }
+ }
+
+ /**
+ * Updates the app widgets whose id has changed during the restore process.
+ */
+ static void restoreAppWidgetIds(Context context, int[] oldWidgetIds, int[] newWidgetIds) {
+ final ContentResolver cr = context.getContentResolver();
+ final List<Integer> idsToRemove = new ArrayList<>();
+ final AppWidgetManager widgets = AppWidgetManager.getInstance(context);
+
+ for (int i = 0; i < oldWidgetIds.length; i++) {
+ Log.i(TAG, "Widget state restore id " + oldWidgetIds[i] + " => " + newWidgetIds[i]);
+
+ final AppWidgetProviderInfo provider = widgets.getAppWidgetInfo(newWidgetIds[i]);
+
+ ContentValues values = new ContentValues();
+ values.put(LauncherSettings.Favorites.APPWIDGET_ID, newWidgetIds[i]);
+ values.put(LauncherSettings.Favorites.RESTORED, LauncherModel.isValidProvider(provider)
+ ? LauncherAppWidgetInfo.RESTORE_COMPLETED
+ : LauncherAppWidgetInfo.RESTORE_PROVIDER_PENDING);
+
+ String[] widgetIdParams = new String[] { Integer.toString(oldWidgetIds[i]) };
+
+ int result = cr.update(Favorites.CONTENT_URI, values,
+ "appWidgetId=? and restored=1", widgetIdParams);
+ if (result == 0) {
+ Cursor cursor = cr.query(Favorites.CONTENT_URI,
+ new String[] {Favorites.APPWIDGET_ID},
+ "appWidgetId=?", widgetIdParams, null);
+ try {
+ if (!cursor.moveToFirst()) {
+ // The widget no long exists.
+ idsToRemove.add(newWidgetIds[i]);
+ }
+ } finally {
+ cursor.close();
+ }
+ }
+ }
+ // Unregister the widget IDs which are not present on the workspace. This could happen
+ // when a widget place holder is removed from workspace, before this method is called.
+ if (!idsToRemove.isEmpty()) {
+ final AppWidgetHost appWidgetHost =
+ new AppWidgetHost(context, Launcher.APPWIDGET_HOST_ID);
+ new AsyncTask<Void, Void, Void>() {
+ public Void doInBackground(Void ... args) {
+ for (Integer id : idsToRemove) {
+ appWidgetHost.deleteAppWidgetId(id);
+ Log.e(TAG, "Widget no longer present, appWidgetId=" + id);
+ }
+ return null;
+ }
+ }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);
+ }
+ }
+}
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index 3d45432c2..4c3388e05 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -326,7 +326,7 @@ public class DeleteDropTarget extends ButtonDropTarget {
final LauncherAppWidgetInfo launcherAppWidgetInfo = (LauncherAppWidgetInfo) item;
final LauncherAppWidgetHost appWidgetHost = mLauncher.getAppWidgetHost();
- if (appWidgetHost != null) {
+ if ((appWidgetHost != null) && launcherAppWidgetInfo.isWidgetIdValid()) {
// Deleting an app widget ID is a void call but writes to disk before returning
// to the caller...
new AsyncTask<Void, Void, Void>() {
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index d3a436237..5e05f927d 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -89,6 +89,7 @@ import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
+import android.view.Window;
import android.view.WindowManager;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.AccelerateDecelerateInterpolator;
@@ -102,6 +103,8 @@ import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
+import com.android.launcher3.DropTarget.DragObject;
+import com.android.launcher3.PagedView.PageSwitchListener;
import com.android.launcher3.compat.LauncherActivityInfoCompat;
import com.android.launcher3.compat.LauncherAppsCompat;
import com.android.launcher3.compat.UserHandleCompat;
@@ -4387,13 +4390,20 @@ public class Launcher extends Activity
}
final Workspace workspace = mWorkspace;
- final int appWidgetId = item.appWidgetId;
- final AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
- if (DEBUG_WIDGETS) {
- Log.d(TAG, "bindAppWidget: id=" + item.appWidgetId + " belongs to component " + appWidgetInfo.provider);
- }
+ final AppWidgetProviderInfo appWidgetInfo;
+ if (item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) {
+ final int appWidgetId = item.appWidgetId;
+ appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
+ if (DEBUG_WIDGETS) {
+ Log.d(TAG, "bindAppWidget: id=" + item.appWidgetId + " belongs to component " + appWidgetInfo.provider);
+ }
- item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
+ item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
+ } else {
+ appWidgetInfo = null;
+ item.hostView = new LauncherAppWidgetHostView(this, false);
+ item.hostView.updateAppWidget(null);
+ }
item.hostView.setTag(item);
item.onBindAppWidget(this);
@@ -4553,7 +4563,7 @@ public class Launcher extends Activity
}
if (mWorkspace != null) {
- mWorkspace.updateShortcuts(apps);
+ mWorkspace.updateShortcutsAndWidgets(apps);
}
if (!LauncherAppState.isDisableAllApps() &&
diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java
index f47fd13ec..7eb005255 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHostView.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java
@@ -18,6 +18,7 @@ package com.android.launcher3;
import android.appwidget.AppWidgetHostView;
import android.content.Context;
+import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
@@ -39,12 +40,28 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView implements Touc
private float mSlop;
+ private boolean mWidgetReady;
+
public LauncherAppWidgetHostView(Context context) {
+ this(context, true);
+ }
+
+ public LauncherAppWidgetHostView(Context context, boolean widgetReady) {
super(context);
mContext = context;
mLongPressHelper = new CheckLongPressHelper(this);
mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
mDragLayer = ((Launcher) context).getDragLayer();
+ mWidgetReady = widgetReady;
+ }
+
+ @Override
+ public void updateAppWidgetSize(Bundle newOptions, int minWidth, int minHeight, int maxWidth,
+ int maxHeight) {
+ // If the widget is not yet ready, the app widget size cannot be updated.
+ if (mWidgetReady) {
+ super.updateAppWidgetSize(newOptions, minWidth, minHeight, maxWidth, maxHeight);
+ }
}
@Override
@@ -53,6 +70,15 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView implements Touc
}
@Override
+ protected View getDefaultView() {
+ if (mWidgetReady) {
+ return super.getDefaultView();
+ } else {
+ return mInflater.inflate(R.layout.appwidget_not_ready, this, false);
+ }
+ }
+
+ @Override
public void updateAppWidget(RemoteViews remoteViews) {
// Store the orientation in which the widget was inflated
mPreviousOrientation = mContext.getResources().getConfiguration().orientation;
diff --git a/src/com/android/launcher3/LauncherAppWidgetInfo.java b/src/com/android/launcher3/LauncherAppWidgetInfo.java
index bec1f9eb3..b3ac12b37 100644
--- a/src/com/android/launcher3/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetInfo.java
@@ -28,6 +28,18 @@ import com.android.launcher3.compat.UserHandleCompat;
*/
public class LauncherAppWidgetInfo extends ItemInfo {
+ public static final int RESTORE_COMPLETED = 0;
+
+ /**
+ * This is set during the package backup creation.
+ */
+ public static final int RESTORE_REMAP_PENDING = 1;
+
+ /**
+ * Widget provider is not yet installed.
+ */
+ public static final int RESTORE_PROVIDER_PENDING = 2;
+
/**
* Indicates that the widget hasn't been instantiated yet.
*/
@@ -45,6 +57,11 @@ public class LauncherAppWidgetInfo extends ItemInfo {
int minWidth = -1;
int minHeight = -1;
+ /**
+ * Indicates the restore status of the widget.
+ */
+ int restoreStatus;
+
private boolean mHasNotifiedInitialWidgetSizeChanged;
/**
@@ -64,6 +81,7 @@ public class LauncherAppWidgetInfo extends ItemInfo {
spanY = -1;
// We only support app widgets on current user.
user = UserHandleCompat.myUserHandle();
+ restoreStatus = RESTORE_COMPLETED;
}
@Override
@@ -101,4 +119,8 @@ public class LauncherAppWidgetInfo extends ItemInfo {
super.unbind();
hostView = null;
}
+
+ public final boolean isWidgetIdValid() {
+ return restoreStatus != RESTORE_REMAP_PENDING;
+ }
}
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index a8bace8e2..db5e7cf68 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -2125,31 +2125,54 @@ public class LauncherModel extends BroadcastReceiver
// Read all Launcher-specific widget details
int appWidgetId = c.getInt(appWidgetIdIndex);
String savedProvider = c.getString(appWidgetProviderIndex);
-
id = c.getLong(idIndex);
- final AppWidgetProviderInfo provider =
- widgets.getAppWidgetInfo(appWidgetId);
-
- if (!isSafeMode && (provider == null || provider.provider == null ||
- provider.provider.getPackageName() == null)) {
- String log = "Deleting widget that isn't installed anymore: id="
- + id + " appWidgetId=" + appWidgetId;
+ final int restoreStatus = c.getInt(restoredIndex);
+ final boolean restorePending = Utilities.isLmp()
+ && (restoreStatus ==
+ LauncherAppWidgetInfo.RESTORE_REMAP_PENDING);
+ final boolean providerPending = Utilities.isLmp()
+ && (restoreStatus ==
+ LauncherAppWidgetInfo.RESTORE_PROVIDER_PENDING);
+
+ // Do not try to get the provider if restore is pending, as the
+ // widget id is invalid, and it might point to some other provider.
+ final AppWidgetProviderInfo provider = restorePending ? null
+ : widgets.getAppWidgetInfo(appWidgetId);
+ boolean providerValid = isValidProvider(provider);
+
+ // Skip provider check,
+ // 1. when the widget id re-map is pending
+ // 2. provider is pending install for a restored widget
+ if (!isSafeMode && !providerPending && !restorePending
+ && !providerValid) {
+ String log = "Deleting widget that isn't installed anymore: "
+ + "id=" + id + " appWidgetId=" + appWidgetId;
Log.e(TAG, log);
Launcher.addDumpLog(TAG, log, false);
itemsToRemove.add(id);
} else {
- appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
- provider.provider);
+ if (providerValid) {
+ appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
+ provider.provider);
+ int[] minSpan =
+ Launcher.getMinSpanForWidget(context, provider);
+ appWidgetInfo.minSpanX = minSpan[0];
+ appWidgetInfo.minSpanY = minSpan[1];
+ } else {
+ Log.v(TAG, "Widget restore pending id=" + id
+ + " appWidgetId=" + appWidgetId
+ + " status =" + restoreStatus);
+ appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
+ ComponentName.unflattenFromString(savedProvider));
+ appWidgetInfo.restoreStatus = restoreStatus;
+ }
appWidgetInfo.id = id;
appWidgetInfo.screenId = c.getInt(screenIndex);
appWidgetInfo.cellX = c.getInt(cellXIndex);
appWidgetInfo.cellY = c.getInt(cellYIndex);
appWidgetInfo.spanX = c.getInt(spanXIndex);
appWidgetInfo.spanY = c.getInt(spanYIndex);
- int[] minSpan = Launcher.getMinSpanForWidget(context, provider);
- appWidgetInfo.minSpanX = minSpan[0];
- appWidgetInfo.minSpanY = minSpan[1];
container = c.getInt(containerIndex);
if (container != LauncherSettings.Favorites.CONTAINER_DESKTOP &&
@@ -2169,14 +2192,20 @@ public class LauncherModel extends BroadcastReceiver
}
break;
}
- String providerName = provider.provider.flattenToString();
- if (!providerName.equals(savedProvider)) {
- ContentValues values = new ContentValues();
- values.put(LauncherSettings.Favorites.APPWIDGET_PROVIDER,
- providerName);
- String where = BaseColumns._ID + "= ?";
- String[] args = {Integer.toString(c.getInt(idIndex))};
- contentResolver.update(contentUri, values, where, args);
+
+ if (providerValid) {
+ String providerName = provider.provider.flattenToString();
+
+ if (!providerName.equals(savedProvider) || providerPending) {
+ ContentValues values = new ContentValues();
+ values.put(LauncherSettings.Favorites.APPWIDGET_PROVIDER,
+ providerName);
+ values.put(LauncherSettings.Favorites.RESTORED,
+ LauncherAppWidgetInfo.RESTORE_COMPLETED);
+ String where = BaseColumns._ID + "= ?";
+ String[] args = {Long.toString(id)};
+ contentResolver.update(contentUri, values, where, args);
+ }
}
sBgItemsIdMap.put(appWidgetInfo.id, appWidgetInfo);
sBgAppWidgets.add(appWidgetInfo);
@@ -3605,6 +3634,11 @@ public class LauncherModel extends BroadcastReceiver
}
};
+ static boolean isValidProvider(AppWidgetProviderInfo provider) {
+ return (provider != null) && (provider.provider != null)
+ && (provider.provider.getPackageName() != null);
+ }
+
public void dumpState() {
Log.d(TAG, "mCallbacks=" + mCallbacks);
AppInfo.dumpApplicationInfoList(TAG, "mAllAppsList.data", mBgAllAppsList.data);
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 267dde800..1e264588c 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -30,6 +30,8 @@ import android.app.WallpaperManager;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetProviderInfo;
import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.ContentValues;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Resources;
@@ -47,6 +49,7 @@ import android.net.Uri;
import android.os.AsyncTask;
import android.os.IBinder;
import android.os.Parcelable;
+import android.provider.BaseColumns;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.util.Log;
@@ -61,15 +64,16 @@ import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;
import android.widget.TextView;
-import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.FolderIcon.FolderRingAnimator;
import com.android.launcher3.Launcher.CustomContentCallbacks;
import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.compat.UserHandleCompat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
+import java.util.Set;
/**
* The workspace is a wide area with a wallpaper and a finite number of pages.
@@ -4574,7 +4578,7 @@ public class Workspace extends SmoothPagedView
public Folder getFolderForTag(final Object tag) {
final Folder[] value = new Folder[1];
- mapOverShortcuts(MAP_NO_RECURSE, new ShortcutOperator() {
+ mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
@Override
public boolean evaluate(ItemInfo info, View v, View parent) {
if (v instanceof Folder) {
@@ -4592,7 +4596,7 @@ public class Workspace extends SmoothPagedView
public View getViewForTag(final Object tag) {
final View[] value = new View[1];
- mapOverShortcuts(MAP_NO_RECURSE, new ShortcutOperator() {
+ mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
@Override
public boolean evaluate(ItemInfo info, View v, View parent) {
if (v.getTag() == tag) {
@@ -4606,7 +4610,7 @@ public class Workspace extends SmoothPagedView
}
void clearDropTargets() {
- mapOverShortcuts(MAP_NO_RECURSE, new ShortcutOperator() {
+ mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
@Override
public boolean evaluate(ItemInfo info, View v, View parent) {
if (v instanceof DropTarget) {
@@ -4760,9 +4764,9 @@ public class Workspace extends SmoothPagedView
}
}
- interface ShortcutOperator {
+ interface ItemOperator {
/**
- * Process the next shortcut, possibly with side-effect on {@link ShortcutOperator#value}.
+ * Process the next itemInfo, possibly with side-effect on {@link ItemOperator#value}.
*
* @param info info for the shortcut
* @param view view for the shortcut
@@ -4773,12 +4777,12 @@ public class Workspace extends SmoothPagedView
}
/**
- * Map the operator over the shortcuts, return the first-non-null value.
+ * Map the operator over the shortcuts and widgets, return the first-non-null value.
*
* @param recurse true: iterate over folder children. false: op get the folders themselves.
* @param op the operator to map over the shortcuts
*/
- void mapOverShortcuts(boolean recurse, ShortcutOperator op) {
+ void mapOverItems(boolean recurse, ItemOperator op) {
ArrayList<ShortcutAndWidgetContainer> containers = getAllShortcutAndWidgetContainers();
final int containerCount = containers.size();
for (int containerIdx = 0; containerIdx < containerCount; containerIdx++) {
@@ -4809,14 +4813,16 @@ public class Workspace extends SmoothPagedView
}
}
- void updateShortcuts(ArrayList<AppInfo> apps) {
+ void updateShortcutsAndWidgets(ArrayList<AppInfo> apps) {
// Create a map of the apps to test against
final HashMap<ComponentName, AppInfo> appsMap = new HashMap<ComponentName, AppInfo>();
+ final HashSet<String> pkgNames = new HashSet<>();
for (AppInfo ai : apps) {
appsMap.put(ai.componentName, ai);
+ pkgNames.add(ai.componentName.getPackageName());
}
- mapOverShortcuts(MAP_RECURSE, new ShortcutOperator() {
+ mapOverItems(MAP_RECURSE, new ItemOperator() {
@Override
public boolean evaluate(ItemInfo info, View v, View parent) {
if (info instanceof ShortcutInfo) {
@@ -4829,6 +4835,8 @@ public class Workspace extends SmoothPagedView
return false;
}
});
+
+ restorePendingWidgets(pkgNames);
}
public void removeAbandonedPromise(BubbleTextView abandonedIcon, UserHandleCompat user) {
@@ -4844,7 +4852,7 @@ public class Workspace extends SmoothPagedView
}
public void updatePackageState(final String pkgName, final int state) {
- mapOverShortcuts(MAP_RECURSE, new ShortcutOperator() {
+ mapOverItems(MAP_RECURSE, new ItemOperator() {
@Override
public boolean evaluate(ItemInfo info, View v, View parent) {
if (info instanceof ShortcutInfo
@@ -4857,6 +4865,40 @@ public class Workspace extends SmoothPagedView
return false;
}
});
+
+ if (state == ShortcutInfo.PACKAGE_STATE_DEFAULT) {
+ // Update any pending widget
+ HashSet<String> packages = new HashSet<>();
+ packages.add(pkgName);
+ restorePendingWidgets(packages);
+ }
+ }
+
+ private void restorePendingWidgets(final Set<String> installedPackaged) {
+ final ContentResolver contentResolver = getContext().getContentResolver();
+ // Iterate non recursively as widgets can't be inside a folder.
+ mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
+
+ @Override
+ public boolean evaluate(ItemInfo info, View v, View parent) {
+ if (info instanceof LauncherAppWidgetInfo) {
+ LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) info;
+ if (widgetInfo.restoreStatus == LauncherAppWidgetInfo.RESTORE_PROVIDER_PENDING
+ && installedPackaged.contains(widgetInfo.providerName.getPackageName())) {
+ // Package installed. Update pending widgets.
+ ContentValues values = new ContentValues();
+ values.put(LauncherSettings.Favorites.RESTORED,
+ LauncherAppWidgetInfo.RESTORE_COMPLETED);
+ String where = BaseColumns._ID + "= ?";
+ String[] args = {Long.toString(widgetInfo.id)};
+ contentResolver.update(LauncherSettings.Favorites.CONTENT_URI,
+ values, where, args);
+ }
+ }
+ // process all the widget
+ return false;
+ }
+ });
}
private void moveToScreen(int page, boolean animate) {