Adding support to restore widgets even for jelly beans.
> Show 'widget-not-ready' until the widget app is installed > Once the app is installed, bind a new widget id (not required on L if id-remap was received). **Remove the widget if bind failed > If the widget has no configuration screen, show the widget, otherwise show 'setup-widget'. > Clicking 'setup-widget' shows the config screen, and updates the widget on RESULT_OK. issue: 10779035 Change-Id: I2f8b06d09dd6acbc498cdd93edc59c26e5ce17af
This commit is contained in:
parent
6075170b83
commit
ff57227711
|
@ -193,9 +193,12 @@ 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. -->
|
||||
<!-- Text to show user in place of a gadget when it is not yet ready. -->
|
||||
<string name="gadget_pending_text" translatable="false">Widget not ready</string>
|
||||
|
||||
<!-- Text to show user in place of a gadget when it is not yet initialized. -->
|
||||
<string name="gadget_setup_text" translatable="false">Setup widget</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>
|
||||
|
||||
|
|
|
@ -46,17 +46,21 @@ public class AppWidgetsRestoredReceiver extends BroadcastReceiver {
|
|||
Log.i(TAG, "Widget state restore id " + oldWidgetIds[i] + " => " + newWidgetIds[i]);
|
||||
|
||||
final AppWidgetProviderInfo provider = widgets.getAppWidgetInfo(newWidgetIds[i]);
|
||||
final int state;
|
||||
if (LauncherModel.isValidProvider(provider)) {
|
||||
state = LauncherAppWidgetInfo.RESTORE_COMPLETED;
|
||||
} else {
|
||||
state = LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
|
||||
}
|
||||
|
||||
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);
|
||||
values.put(LauncherSettings.Favorites.RESTORED, state);
|
||||
|
||||
String[] widgetIdParams = new String[] { Integer.toString(oldWidgetIds[i]) };
|
||||
|
||||
int result = cr.update(Favorites.CONTENT_URI, values,
|
||||
"appWidgetId=? and restored=1", widgetIdParams);
|
||||
"appWidgetId=? and (restored & 1) = 1", widgetIdParams);
|
||||
if (result == 0) {
|
||||
Cursor cursor = cr.query(Favorites.CONTENT_URI,
|
||||
new String[] {Favorites.APPWIDGET_ID},
|
||||
|
|
|
@ -148,6 +148,8 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
|
|||
LauncherTransitionable {
|
||||
static final String TAG = "AppsCustomizePagedView";
|
||||
|
||||
private static Rect sTmpRect = new Rect();
|
||||
|
||||
/**
|
||||
* The different content types that this paged view can show.
|
||||
*/
|
||||
|
@ -223,8 +225,6 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
|
|||
private ArrayList<Runnable> mDeferredPrepareLoadWidgetPreviewsTasks =
|
||||
new ArrayList<Runnable>();
|
||||
|
||||
private Rect mTmpRect = new Rect();
|
||||
|
||||
WidgetPreviewLoader mWidgetPreviewLoader;
|
||||
|
||||
private boolean mInBulkBind;
|
||||
|
@ -540,26 +540,26 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
|
|||
mLauncher.getWorkspace().beginDragShared(v, this);
|
||||
}
|
||||
|
||||
Bundle getDefaultOptionsForWidget(Launcher launcher, PendingAddWidgetInfo info) {
|
||||
static Bundle getDefaultOptionsForWidget(Launcher launcher, PendingAddWidgetInfo info) {
|
||||
Bundle options = null;
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
AppWidgetResizeFrame.getWidgetSizeRanges(mLauncher, info.spanX, info.spanY, mTmpRect);
|
||||
Rect padding = AppWidgetHostView.getDefaultPaddingForWidget(mLauncher,
|
||||
AppWidgetResizeFrame.getWidgetSizeRanges(launcher, info.spanX, info.spanY, sTmpRect);
|
||||
Rect padding = AppWidgetHostView.getDefaultPaddingForWidget(launcher,
|
||||
info.componentName, null);
|
||||
|
||||
float density = getResources().getDisplayMetrics().density;
|
||||
float density = launcher.getResources().getDisplayMetrics().density;
|
||||
int xPaddingDips = (int) ((padding.left + padding.right) / density);
|
||||
int yPaddingDips = (int) ((padding.top + padding.bottom) / density);
|
||||
|
||||
options = new Bundle();
|
||||
options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH,
|
||||
mTmpRect.left - xPaddingDips);
|
||||
sTmpRect.left - xPaddingDips);
|
||||
options.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT,
|
||||
mTmpRect.top - yPaddingDips);
|
||||
sTmpRect.top - yPaddingDips);
|
||||
options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH,
|
||||
mTmpRect.right - xPaddingDips);
|
||||
sTmpRect.right - xPaddingDips);
|
||||
options.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT,
|
||||
mTmpRect.bottom - yPaddingDips);
|
||||
sTmpRect.bottom - yPaddingDips);
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
|
|
@ -124,6 +124,12 @@ public class ItemInfo {
|
|||
}
|
||||
|
||||
ItemInfo(ItemInfo info) {
|
||||
copyFrom(info);
|
||||
// tempdebug:
|
||||
LauncherModel.checkItemInfo(this);
|
||||
}
|
||||
|
||||
public void copyFrom(ItemInfo info) {
|
||||
id = info.id;
|
||||
cellX = info.cellX;
|
||||
cellY = info.cellY;
|
||||
|
@ -134,8 +140,6 @@ public class ItemInfo {
|
|||
container = info.container;
|
||||
user = info.user;
|
||||
contentDescription = info.contentDescription;
|
||||
// tempdebug:
|
||||
LauncherModel.checkItemInfo(this);
|
||||
}
|
||||
|
||||
public Intent getIntent() {
|
||||
|
|
|
@ -65,7 +65,6 @@ import android.os.Handler;
|
|||
import android.os.Message;
|
||||
import android.os.StrictMode;
|
||||
import android.os.SystemClock;
|
||||
import android.provider.Settings;
|
||||
import android.speech.RecognizerIntent;
|
||||
import android.text.Selection;
|
||||
import android.text.SpannableStringBuilder;
|
||||
|
@ -153,6 +152,7 @@ public class Launcher extends Activity
|
|||
private static final int REQUEST_PICK_WALLPAPER = 10;
|
||||
|
||||
private static final int REQUEST_BIND_APPWIDGET = 11;
|
||||
private static final int REQUEST_RECONFIGURE_APPWIDGET = 12;
|
||||
|
||||
/**
|
||||
* IntentStarter uses request codes starting with this. This must be greater than all activity
|
||||
|
@ -752,6 +752,9 @@ public class Launcher extends Activity
|
|||
case REQUEST_CREATE_APPWIDGET:
|
||||
completeAddAppWidget(args.appWidgetId, args.container, screenId, null, null);
|
||||
break;
|
||||
case REQUEST_RECONFIGURE_APPWIDGET:
|
||||
completeRestoreAppWidget(args.appWidgetId);
|
||||
break;
|
||||
}
|
||||
// Before adding this resetAddInfo(), after a shortcut was added to a workspace screen,
|
||||
// if you turned the screen off and then back while in All Apps, Launcher would not
|
||||
|
@ -854,6 +857,21 @@ public class Launcher extends Activity
|
|||
return;
|
||||
}
|
||||
|
||||
if (requestCode == REQUEST_RECONFIGURE_APPWIDGET) {
|
||||
if (resultCode == RESULT_OK) {
|
||||
// Update the widget view.
|
||||
PendingAddArguments args = preparePendingAddArgs(requestCode, data,
|
||||
pendingAddWidgetId, mPendingAddInfo);
|
||||
if (workspaceLocked) {
|
||||
sPendingAddItem = args;
|
||||
} else {
|
||||
completeAdd(args);
|
||||
}
|
||||
}
|
||||
// Leave the widget in the pending state if the user canceled the configure.
|
||||
return;
|
||||
}
|
||||
|
||||
// The pattern used here is that a user PICKs a specific application,
|
||||
// which, depending on the target, might need to CREATE the actual target.
|
||||
|
||||
|
@ -2446,6 +2464,10 @@ public class Launcher extends Activity
|
|||
}
|
||||
} else if (v == mAllAppsButton) {
|
||||
onClickAllAppsButton(v);
|
||||
} else if (tag instanceof LauncherAppWidgetInfo) {
|
||||
if (v instanceof PendingAppWidgetHostView) {
|
||||
onClickPendingWidget((PendingAppWidgetHostView) v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2453,6 +2475,27 @@ public class Launcher extends Activity
|
|||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Event handler for the app widget view which has not fully restored.
|
||||
*/
|
||||
public void onClickPendingWidget(PendingAppWidgetHostView v) {
|
||||
if (v.isReadyForClickSetup()) {
|
||||
LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) v.getTag();
|
||||
int widgetId = info.appWidgetId;
|
||||
AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(widgetId);
|
||||
if (appWidgetInfo != null) {
|
||||
mPendingAddWidgetInfo = appWidgetInfo;
|
||||
mPendingAddInfo.copyFrom(info);
|
||||
mPendingAddWidgetId = widgetId;
|
||||
|
||||
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
|
||||
intent.setComponent(appWidgetInfo.configure);
|
||||
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, info.appWidgetId);
|
||||
Utilities.startActivityForResultSafely(this, intent, REQUEST_RECONFIGURE_APPWIDGET);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Event handler for the search button
|
||||
*
|
||||
|
@ -4398,7 +4441,64 @@ public class Launcher extends Activity
|
|||
}
|
||||
final Workspace workspace = mWorkspace;
|
||||
|
||||
final AppWidgetProviderInfo appWidgetInfo;
|
||||
AppWidgetProviderInfo appWidgetInfo;
|
||||
if (((item.restoreStatus & LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) == 0) &&
|
||||
((item.restoreStatus & LauncherAppWidgetInfo.FLAG_ID_NOT_VALID) != 0)) {
|
||||
|
||||
appWidgetInfo = mModel.findAppWidgetProviderInfoWithComponent(this, item.providerName);
|
||||
if (appWidgetInfo == null) {
|
||||
if (DEBUG_WIDGETS) {
|
||||
Log.d(TAG, "Removing restored widget: id=" + item.appWidgetId
|
||||
+ " belongs to component " + item.providerName
|
||||
+ ", as the povider is null");
|
||||
}
|
||||
LauncherModel.deleteItemFromDatabase(this, item);
|
||||
return;
|
||||
}
|
||||
// Note: This assumes that the id remap broadcast is received before this step.
|
||||
// If that is not the case, the id remap will be ignored and user may see the
|
||||
// click to setup view.
|
||||
PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(appWidgetInfo, null, null);
|
||||
pendingInfo.spanX = item.spanX;
|
||||
pendingInfo.spanY = item.spanY;
|
||||
pendingInfo.minSpanX = item.minSpanX;
|
||||
pendingInfo.minSpanY = item.minSpanY;
|
||||
Bundle options =
|
||||
AppsCustomizePagedView.getDefaultOptionsForWidget(this, pendingInfo);
|
||||
|
||||
boolean success = false;
|
||||
int newWidgetId = mAppWidgetHost.allocateAppWidgetId();
|
||||
if (options != null) {
|
||||
success = mAppWidgetManager.bindAppWidgetIdIfAllowed(newWidgetId,
|
||||
appWidgetInfo.provider, options);
|
||||
} else {
|
||||
success = mAppWidgetManager.bindAppWidgetIdIfAllowed(newWidgetId,
|
||||
appWidgetInfo.provider);
|
||||
}
|
||||
|
||||
// TODO consider showing a permission dialog when the widget is clicked.
|
||||
if (!success) {
|
||||
mAppWidgetHost.deleteAppWidgetId(newWidgetId);
|
||||
if (DEBUG_WIDGETS) {
|
||||
Log.d(TAG, "Removing restored widget: id=" + item.appWidgetId
|
||||
+ " belongs to component " + item.providerName
|
||||
+ ", as the launcher is unable to bing a new widget id");
|
||||
}
|
||||
LauncherModel.deleteItemFromDatabase(this, item);
|
||||
return;
|
||||
}
|
||||
|
||||
item.appWidgetId = newWidgetId;
|
||||
|
||||
// If the widget has a configure activity, it is still needs to set it up, otherwise
|
||||
// the widget is ready to go.
|
||||
item.restoreStatus = (appWidgetInfo.configure == null)
|
||||
? LauncherAppWidgetInfo.RESTORE_COMPLETED
|
||||
: LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
|
||||
|
||||
LauncherModel.updateItemInDatabase(this, item);
|
||||
}
|
||||
|
||||
if (item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) {
|
||||
final int appWidgetId = item.appWidgetId;
|
||||
appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
|
||||
|
@ -4409,8 +4509,9 @@ public class Launcher extends Activity
|
|||
item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
|
||||
} else {
|
||||
appWidgetInfo = null;
|
||||
item.hostView = new LauncherAppWidgetHostView(this, false);
|
||||
item.hostView = new PendingAppWidgetHostView(this, item.restoreStatus);
|
||||
item.hostView.updateAppWidget(null);
|
||||
item.hostView.setOnClickListener(this);
|
||||
}
|
||||
|
||||
item.hostView.setTag(item);
|
||||
|
@ -4428,6 +4529,29 @@ public class Launcher extends Activity
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores a pending widget.
|
||||
*
|
||||
* @param appWidgetId The app widget id
|
||||
* @param cellInfo The position on screen where to create the widget.
|
||||
*/
|
||||
private void completeRestoreAppWidget(final int appWidgetId) {
|
||||
LauncherAppWidgetHostView view = mWorkspace.getWidgetForAppWidgetId(appWidgetId);
|
||||
if ((view == null) || !(view instanceof PendingAppWidgetHostView)) {
|
||||
Log.e(TAG, "Widget update called, when the widget no longer exists.");
|
||||
return;
|
||||
}
|
||||
|
||||
PendingAppWidgetHostView pendingView = (PendingAppWidgetHostView) view;
|
||||
pendingView.setStatus(LauncherAppWidgetInfo.RESTORE_COMPLETED);
|
||||
|
||||
LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) pendingView.getTag();
|
||||
info.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED;
|
||||
|
||||
mWorkspace.reinflateWidgetsIfNecessary();
|
||||
LauncherModel.updateItemInDatabase(this, info);
|
||||
}
|
||||
|
||||
public void onPageBoundSynchronously(int page) {
|
||||
mSynchronouslyBoundPages.add(page);
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ 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;
|
||||
|
@ -32,36 +31,22 @@ import com.android.launcher3.DragLayer.TouchCompleteListener;
|
|||
* {@inheritDoc}
|
||||
*/
|
||||
public class LauncherAppWidgetHostView extends AppWidgetHostView implements TouchCompleteListener {
|
||||
|
||||
LayoutInflater mInflater;
|
||||
|
||||
private CheckLongPressHelper mLongPressHelper;
|
||||
private LayoutInflater mInflater;
|
||||
private Context mContext;
|
||||
private int mPreviousOrientation;
|
||||
private DragLayer mDragLayer;
|
||||
|
||||
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
|
||||
|
@ -69,15 +54,6 @@ 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
|
||||
|
@ -85,7 +61,8 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView implements Touc
|
|||
super.updateAppWidget(remoteViews);
|
||||
}
|
||||
|
||||
public boolean orientationChangedSincedInflation() {
|
||||
public boolean isReinflateRequired() {
|
||||
// Re-inflate is required if the orientation has changed since last inflated.
|
||||
int orientation = mContext.getResources().getConfiguration().orientation;
|
||||
if (mPreviousOrientation != orientation) {
|
||||
return true;
|
||||
|
|
|
@ -33,12 +33,17 @@ public class LauncherAppWidgetInfo extends ItemInfo {
|
|||
/**
|
||||
* This is set during the package backup creation.
|
||||
*/
|
||||
public static final int RESTORE_REMAP_PENDING = 1;
|
||||
public static final int FLAG_ID_NOT_VALID = 1;
|
||||
|
||||
/**
|
||||
* Widget provider is not yet installed.
|
||||
* Indicates that the provider is not available yet.
|
||||
*/
|
||||
public static final int RESTORE_PROVIDER_PENDING = 2;
|
||||
public static final int FLAG_PROVIDER_NOT_READY = 2;
|
||||
|
||||
/**
|
||||
* Indicates that the widget UI is not yet ready, and user needs to set it up again.
|
||||
*/
|
||||
public static final int FLAG_UI_NOT_READY = 4;
|
||||
|
||||
/**
|
||||
* Indicates that the widget hasn't been instantiated yet.
|
||||
|
@ -89,6 +94,7 @@ public class LauncherAppWidgetInfo extends ItemInfo {
|
|||
super.onAddToDatabase(context, values);
|
||||
values.put(LauncherSettings.Favorites.APPWIDGET_ID, appWidgetId);
|
||||
values.put(LauncherSettings.Favorites.APPWIDGET_PROVIDER, providerName.flattenToString());
|
||||
values.put(LauncherSettings.Favorites.RESTORED, restoreStatus);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -121,6 +127,10 @@ public class LauncherAppWidgetInfo extends ItemInfo {
|
|||
}
|
||||
|
||||
public final boolean isWidgetIdValid() {
|
||||
return restoreStatus != RESTORE_REMAP_PENDING;
|
||||
return (restoreStatus & FLAG_ID_NOT_VALID) == 0;
|
||||
}
|
||||
|
||||
public final boolean hasRestoreFlag(int flag) {
|
||||
return (restoreStatus & flag) == flag;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -874,20 +874,25 @@ public class LauncherBackupHelper implements BackupHelper {
|
|||
values.put(Favorites.INTENT, favorite.intent);
|
||||
}
|
||||
values.put(Favorites.ITEM_TYPE, favorite.itemType);
|
||||
if (favorite.itemType == Favorites.ITEM_TYPE_APPWIDGET) {
|
||||
if (!TextUtils.isEmpty(favorite.appWidgetProvider)) {
|
||||
values.put(Favorites.APPWIDGET_PROVIDER, favorite.appWidgetProvider);
|
||||
}
|
||||
values.put(Favorites.APPWIDGET_ID, favorite.appWidgetId);
|
||||
}
|
||||
|
||||
UserHandleCompat myUserHandle = UserHandleCompat.myUserHandle();
|
||||
long userSerialNumber =
|
||||
UserManagerCompat.getInstance(mContext).getSerialNumberForUser(myUserHandle);
|
||||
values.put(LauncherSettings.Favorites.PROFILE_ID, userSerialNumber);
|
||||
|
||||
// Let LauncherModel know we've been here.
|
||||
values.put(LauncherSettings.Favorites.RESTORED, 1);
|
||||
if (favorite.itemType == Favorites.ITEM_TYPE_APPWIDGET) {
|
||||
if (!TextUtils.isEmpty(favorite.appWidgetProvider)) {
|
||||
values.put(Favorites.APPWIDGET_PROVIDER, favorite.appWidgetProvider);
|
||||
}
|
||||
values.put(Favorites.APPWIDGET_ID, favorite.appWidgetId);
|
||||
values.put(LauncherSettings.Favorites.RESTORED,
|
||||
LauncherAppWidgetInfo.FLAG_ID_NOT_VALID |
|
||||
LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY |
|
||||
LauncherAppWidgetInfo.FLAG_UI_NOT_READY);
|
||||
} else {
|
||||
// Let LauncherModel know we've been here.
|
||||
values.put(LauncherSettings.Favorites.RESTORED, 1);
|
||||
}
|
||||
|
||||
return values;
|
||||
}
|
||||
|
|
|
@ -2128,47 +2128,59 @@ public class LauncherModel extends BroadcastReceiver
|
|||
int appWidgetId = c.getInt(appWidgetIdIndex);
|
||||
String savedProvider = c.getString(appWidgetProviderIndex);
|
||||
id = c.getLong(idIndex);
|
||||
final ComponentName component =
|
||||
ComponentName.unflattenFromString(savedProvider);
|
||||
|
||||
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);
|
||||
final boolean isIdValid = (restoreStatus &
|
||||
LauncherAppWidgetInfo.FLAG_ID_NOT_VALID) == 0;
|
||||
|
||||
// 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);
|
||||
final boolean wasProviderReady = (restoreStatus &
|
||||
LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) == 0;
|
||||
|
||||
// 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) {
|
||||
final AppWidgetProviderInfo provider = isIdValid
|
||||
? widgets.getAppWidgetInfo(appWidgetId)
|
||||
: findAppWidgetProviderInfoWithComponent(context, component);
|
||||
|
||||
final boolean isProviderReady = isValidProvider(provider);
|
||||
if (!isSafeMode && wasProviderReady && !isProviderReady) {
|
||||
String log = "Deleting widget that isn't installed anymore: "
|
||||
+ "id=" + id + " appWidgetId=" + appWidgetId;
|
||||
+ "id=" + id + " appWidgetId=" + appWidgetId;
|
||||
Log.e(TAG, log);
|
||||
Launcher.addDumpLog(TAG, log, false);
|
||||
itemsToRemove.add(id);
|
||||
} else {
|
||||
if (providerValid) {
|
||||
if (isProviderReady) {
|
||||
appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
|
||||
provider.provider);
|
||||
int[] minSpan =
|
||||
Launcher.getMinSpanForWidget(context, provider);
|
||||
appWidgetInfo.minSpanX = minSpan[0];
|
||||
appWidgetInfo.minSpanY = minSpan[1];
|
||||
|
||||
int status = restoreStatus;
|
||||
if (!wasProviderReady) {
|
||||
// If provider was not previously ready, update the
|
||||
// status and UI flag.
|
||||
|
||||
// Id would be valid only if the widget restore broadcast was received.
|
||||
if (isIdValid) {
|
||||
status = LauncherAppWidgetInfo.RESTORE_COMPLETED;
|
||||
} else {
|
||||
status &= ~LauncherAppWidgetInfo
|
||||
.FLAG_PROVIDER_NOT_READY;
|
||||
}
|
||||
}
|
||||
appWidgetInfo.restoreStatus = status;
|
||||
} else {
|
||||
Log.v(TAG, "Widget restore pending id=" + id
|
||||
+ " appWidgetId=" + appWidgetId
|
||||
+ " status =" + restoreStatus);
|
||||
appWidgetInfo = new LauncherAppWidgetInfo(appWidgetId,
|
||||
ComponentName.unflattenFromString(savedProvider));
|
||||
component);
|
||||
appWidgetInfo.restoreStatus = restoreStatus;
|
||||
}
|
||||
|
||||
appWidgetInfo.id = id;
|
||||
appWidgetInfo.screenId = c.getInt(screenIndex);
|
||||
appWidgetInfo.cellX = c.getInt(cellXIndex);
|
||||
|
@ -2195,15 +2207,15 @@ public class LauncherModel extends BroadcastReceiver
|
|||
break;
|
||||
}
|
||||
|
||||
if (providerValid) {
|
||||
if (isProviderReady) {
|
||||
String providerName = provider.provider.flattenToString();
|
||||
|
||||
if (!providerName.equals(savedProvider) || providerPending) {
|
||||
if (!providerName.equals(savedProvider) ||
|
||||
(appWidgetInfo.restoreStatus != restoreStatus)) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(LauncherSettings.Favorites.APPWIDGET_PROVIDER,
|
||||
providerName);
|
||||
values.put(LauncherSettings.Favorites.RESTORED,
|
||||
LauncherAppWidgetInfo.RESTORE_COMPLETED);
|
||||
appWidgetInfo.restoreStatus);
|
||||
String where = BaseColumns._ID + "= ?";
|
||||
String[] args = {Long.toString(id)};
|
||||
contentResolver.update(contentUri, values, where, args);
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
package com.android.launcher3;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.widget.TextView;
|
||||
|
||||
public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implements OnClickListener {
|
||||
|
||||
int mRestoreStatus;
|
||||
|
||||
private TextView mDefaultView;
|
||||
private OnClickListener mClickListener;
|
||||
|
||||
public PendingAppWidgetHostView(Context context, int restoreStatus) {
|
||||
super(context);
|
||||
mRestoreStatus = restoreStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateAppWidgetSize(Bundle newOptions, int minWidth, int minHeight, int maxWidth,
|
||||
int maxHeight) {
|
||||
// No-op
|
||||
}
|
||||
|
||||
@Override
|
||||
protected View getDefaultView() {
|
||||
if (mDefaultView == null) {
|
||||
mDefaultView = (TextView) mInflater.inflate(R.layout.appwidget_not_ready, this, false);
|
||||
mDefaultView.setOnClickListener(this);
|
||||
applyState();
|
||||
}
|
||||
return mDefaultView;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnClickListener(OnClickListener l) {
|
||||
mClickListener = l;
|
||||
}
|
||||
|
||||
public void setStatus(int status) {
|
||||
if (mRestoreStatus != status) {
|
||||
mRestoreStatus = status;
|
||||
applyState();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReinflateRequired() {
|
||||
// Re inflate is required if the the widget is restored.
|
||||
return mRestoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED;
|
||||
}
|
||||
|
||||
private void applyState() {
|
||||
if (mDefaultView != null) {
|
||||
if (isReadyForClickSetup()) {
|
||||
mDefaultView.setText(R.string.gadget_setup_text);
|
||||
} else {
|
||||
mDefaultView.setText(R.string.gadget_pending_text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
// AppWidgetHostView blocks all click events on the root view. Instead handle click events
|
||||
// on the content and pass it along.
|
||||
if (mClickListener != null) {
|
||||
mClickListener.onClick(this);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isReadyForClickSetup() {
|
||||
return (mRestoreStatus & LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) == 0
|
||||
&& (mRestoreStatus & LauncherAppWidgetInfo.FLAG_UI_NOT_READY) != 0;
|
||||
}
|
||||
}
|
|
@ -30,8 +30,6 @@ 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;
|
||||
|
@ -49,7 +47,6 @@ 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;
|
||||
|
@ -75,6 +72,7 @@ import java.util.HashMap;
|
|||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
/**
|
||||
* The workspace is a wide area with a wallpaper and a finite number of pages.
|
||||
|
@ -1119,10 +1117,10 @@ public class Workspace extends SmoothPagedView
|
|||
for (int j = 0; j < itemCount; j++) {
|
||||
View v = swc.getChildAt(j);
|
||||
|
||||
if (v.getTag() instanceof LauncherAppWidgetInfo) {
|
||||
if (v != null && v.getTag() instanceof LauncherAppWidgetInfo) {
|
||||
LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) v.getTag();
|
||||
LauncherAppWidgetHostView lahv = (LauncherAppWidgetHostView) info.hostView;
|
||||
if (lahv != null && lahv.orientationChangedSincedInflation()) {
|
||||
if (lahv != null && lahv.isReinflateRequired()) {
|
||||
mLauncher.removeAppWidget(info);
|
||||
// Remove the current widget which is inflated with the wrong orientation
|
||||
cl.removeView(lahv);
|
||||
|
@ -3258,7 +3256,6 @@ public class Workspace extends SmoothPagedView
|
|||
LauncherAppState app = LauncherAppState.getInstance();
|
||||
DeviceProfile grid = app.getDynamicGrid().getDeviceProfile();
|
||||
|
||||
Resources res = launcher.getResources();
|
||||
Display display = launcher.getWindowManager().getDefaultDisplay();
|
||||
Point smallestSize = new Point();
|
||||
Point largestSize = new Point();
|
||||
|
@ -4591,29 +4588,43 @@ public class Workspace extends SmoothPagedView
|
|||
}
|
||||
|
||||
public Folder getFolderForTag(final Object tag) {
|
||||
final Folder[] value = new Folder[1];
|
||||
mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
|
||||
return (Folder) getFirstMatch(new ItemOperator() {
|
||||
|
||||
@Override
|
||||
public boolean evaluate(ItemInfo info, View v, View parent) {
|
||||
if (v instanceof Folder) {
|
||||
Folder f = (Folder) v;
|
||||
if (f.getInfo() == tag && f.getInfo().opened) {
|
||||
value[0] = f;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
return (v instanceof Folder) && (((Folder) v).getInfo() == tag)
|
||||
&& ((Folder) v).getInfo().opened;
|
||||
}
|
||||
});
|
||||
return value[0];
|
||||
}
|
||||
|
||||
public View getViewForTag(final Object tag) {
|
||||
return getFirstMatch(new ItemOperator() {
|
||||
|
||||
@Override
|
||||
public boolean evaluate(ItemInfo info, View v, View parent) {
|
||||
return info == tag;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public LauncherAppWidgetHostView getWidgetForAppWidgetId(final int appWidgetId) {
|
||||
return (LauncherAppWidgetHostView) getFirstMatch(new ItemOperator() {
|
||||
|
||||
@Override
|
||||
public boolean evaluate(ItemInfo info, View v, View parent) {
|
||||
return (info instanceof LauncherAppWidgetInfo) &&
|
||||
((LauncherAppWidgetInfo) info).appWidgetId == appWidgetId;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private View getFirstMatch(final ItemOperator operator) {
|
||||
final View[] value = new View[1];
|
||||
mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
|
||||
@Override
|
||||
public boolean evaluate(ItemInfo info, View v, View parent) {
|
||||
if (v.getTag() == tag) {
|
||||
if (operator.evaluate(info, v, parent)) {
|
||||
value[0] = v;
|
||||
return true;
|
||||
}
|
||||
|
@ -4895,7 +4906,7 @@ public class Workspace extends SmoothPagedView
|
|||
}
|
||||
|
||||
private void restorePendingWidgets(final Set<String> installedPackaged) {
|
||||
final ContentResolver contentResolver = getContext().getContentResolver();
|
||||
final AtomicBoolean widgetsChanged = new AtomicBoolean(false);
|
||||
// Iterate non recursively as widgets can't be inside a folder.
|
||||
mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
|
||||
|
||||
|
@ -4903,22 +4914,21 @@ public class Workspace extends SmoothPagedView
|
|||
public boolean evaluate(ItemInfo info, View v, View parent) {
|
||||
if (info instanceof LauncherAppWidgetInfo) {
|
||||
LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) info;
|
||||
if (widgetInfo.restoreStatus == LauncherAppWidgetInfo.RESTORE_PROVIDER_PENDING
|
||||
if (widgetInfo.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)
|
||||
&& 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);
|
||||
widgetsChanged.set(true);
|
||||
}
|
||||
}
|
||||
// process all the widget
|
||||
return false;
|
||||
}
|
||||
});
|
||||
if (widgetsChanged.get()) {
|
||||
// Reload layout and update widget status
|
||||
// TODO instead of full reload, just update the specific widgets
|
||||
getContext().getContentResolver()
|
||||
.notifyChange(LauncherSettings.Favorites.CONTENT_URI, null);
|
||||
}
|
||||
}
|
||||
|
||||
private void moveToScreen(int page, boolean animate) {
|
||||
|
|
Loading…
Reference in New Issue