Adding app widget restore support in Launcher3 for android L
When the app is restored, it displays placeholders for all pending widgets. These placeholders can be moved and removed similar to a widget (size is fixed to what defined in backup). Once the system notifies the launcher of the new widget ids, the place holders are replaced with actual widgets. issue: 10779035 Change-Id: I68cbe4da01e9ca2978cb4471a7c645d2aa592055
This commit is contained in:
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
@@ -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"
|
||||
/>
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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>() {
|
||||
|
||||
@@ -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() &&
|
||||
|
||||
@@ -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
|
||||
@@ -52,6 +69,15 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView implements Touc
|
||||
return mInflater.inflate(R.layout.appwidget_error, this, false);
|
||||
}
|
||||
|
||||
@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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
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);
|
||||
|
||||
if (!isSafeMode && (provider == null || provider.provider == null ||
|
||||
provider.provider.getPackageName() == null)) {
|
||||
String log = "Deleting widget that isn't installed anymore: id="
|
||||
+ id + " appWidgetId=" + appWidgetId;
|
||||
// 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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user