Updating the ui for widget restore flow
> Pending widget show a PreloadIconDrawable to indicate installation progress > Only the concerned widgets are reinflated on package install and not the whole workspace. > Adding support for storing default package icon in IconCache issue: 10779035 issue: 16737660 Change-Id: Id787ae4a5ef72d6e01aeb5a1bae5ab8840037679
This commit is contained in:
+7
-3
@@ -72,11 +72,15 @@
|
||||
|
||||
<application
|
||||
android:name="com.android.launcher3.LauncherApplication"
|
||||
android:label="@string/application_name"
|
||||
android:icon="@mipmap/ic_launcher_home"
|
||||
android:allowBackup="@bool/enable_backup"
|
||||
android:backupAgent="com.android.launcher3.LauncherBackupAgentHelper"
|
||||
android:hardwareAccelerated="true"
|
||||
android:icon="@mipmap/ic_launcher_home"
|
||||
android:label="@string/application_name"
|
||||
android:largeHeap="@bool/config_largeHeap"
|
||||
android:supportsRtl="true">
|
||||
android:restoreAnyVersion="true"
|
||||
android:supportsRtl="true" >
|
||||
|
||||
<activity
|
||||
android:name="com.android.launcher3.Launcher"
|
||||
android:launchMode="singleTask"
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 369 B |
Binary file not shown.
|
Before Width: | Height: | Size: 285 B |
Binary file not shown.
|
Before Width: | Height: | Size: 460 B |
Binary file not shown.
|
Before Width: | Height: | Size: 3.7 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.1 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -1,24 +0,0 @@
|
||||
<?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>
|
||||
@@ -1,5 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2009 The Android Open Source Project
|
||||
<!--
|
||||
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.
|
||||
@@ -14,15 +15,6 @@
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<View 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"
|
||||
/>
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
@@ -26,7 +26,6 @@
|
||||
<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>
|
||||
|
||||
@@ -17,6 +17,10 @@
|
||||
<!-- Max number of page indicators to show -->
|
||||
<integer name="config_maxNumberOfPageIndicatorsToShow">21</integer>
|
||||
|
||||
<!-- App data backup and restore. To enble backup, register with an android backup service.
|
||||
http://developer.android.com/guide/topics/data/backup.html#BackupKey -->
|
||||
<bool name="enable_backup">false</bool>
|
||||
|
||||
<!-- DragController -->
|
||||
<integer name="config_flingToDeleteMinVelocity">-1500</integer>
|
||||
|
||||
|
||||
@@ -193,11 +193,8 @@ 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. -->
|
||||
<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>
|
||||
<string name="gadget_setup_text">Setup</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>
|
||||
|
||||
@@ -16,22 +16,20 @@
|
||||
|
||||
package com.android.launcher3;
|
||||
|
||||
import com.android.launcher3.backup.BackupProtos;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.pm.ResolveInfo;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapFactory;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.launcher3.compat.LauncherActivityInfoCompat;
|
||||
@@ -54,12 +52,15 @@ import java.util.Map.Entry;
|
||||
* Cache of application icons. Icons can be made from any thread.
|
||||
*/
|
||||
public class IconCache {
|
||||
@SuppressWarnings("unused")
|
||||
|
||||
private static final String TAG = "Launcher.IconCache";
|
||||
|
||||
private static final int INITIAL_ICON_CACHE_CAPACITY = 50;
|
||||
private static final String RESOURCE_FILE_PREFIX = "icon_";
|
||||
|
||||
// Empty class name is used for storing package default entry.
|
||||
private static final String EMPTY_CLASS_NAME = ".";
|
||||
|
||||
private static final boolean DEBUG = true;
|
||||
|
||||
private static class CacheEntry {
|
||||
@@ -237,7 +238,7 @@ public class IconCache {
|
||||
HashMap<Object, CharSequence> labelCache) {
|
||||
synchronized (mCache) {
|
||||
CacheEntry entry = cacheLocked(application.componentName, info, labelCache,
|
||||
info.getUser());
|
||||
info.getUser(), false);
|
||||
|
||||
application.title = entry.title;
|
||||
application.iconBitmap = entry.icon;
|
||||
@@ -246,10 +247,10 @@ public class IconCache {
|
||||
}
|
||||
|
||||
public Bitmap getIcon(Intent intent, UserHandleCompat user) {
|
||||
return getIcon(intent, null, user);
|
||||
return getIcon(intent, null, user, true);
|
||||
}
|
||||
|
||||
public Bitmap getIcon(Intent intent, String title, UserHandleCompat user) {
|
||||
public Bitmap getIcon(Intent intent, String title, UserHandleCompat user, boolean usePkgIcon) {
|
||||
synchronized (mCache) {
|
||||
LauncherActivityInfoCompat launcherActInfo =
|
||||
mLauncherApps.resolveActivity(intent, user);
|
||||
@@ -257,11 +258,11 @@ public class IconCache {
|
||||
|
||||
// null info means not installed, but if we have a component from the intent then
|
||||
// we should still look in the cache for restored app icons.
|
||||
if (launcherActInfo == null && component == null) {
|
||||
if (component == null) {
|
||||
return getDefaultIcon(user);
|
||||
}
|
||||
|
||||
CacheEntry entry = cacheLocked(component, launcherActInfo, null, user);
|
||||
CacheEntry entry = cacheLocked(component, launcherActInfo, null, user, usePkgIcon);
|
||||
if (title != null) {
|
||||
entry.title = title;
|
||||
entry.contentDescription = mUserManager.getBadgedLabelForUser(title, user);
|
||||
@@ -284,7 +285,7 @@ public class IconCache {
|
||||
return null;
|
||||
}
|
||||
|
||||
CacheEntry entry = cacheLocked(component, info, labelCache, info.getUser());
|
||||
CacheEntry entry = cacheLocked(component, info, labelCache, info.getUser(), false);
|
||||
return entry.icon;
|
||||
}
|
||||
}
|
||||
@@ -294,7 +295,7 @@ public class IconCache {
|
||||
}
|
||||
|
||||
private CacheEntry cacheLocked(ComponentName componentName, LauncherActivityInfoCompat info,
|
||||
HashMap<Object, CharSequence> labelCache, UserHandleCompat user) {
|
||||
HashMap<Object, CharSequence> labelCache, UserHandleCompat user, boolean usePackageIcon) {
|
||||
CacheKey cacheKey = new CacheKey(componentName, user);
|
||||
CacheEntry entry = mCache.get(cacheKey);
|
||||
if (entry == null) {
|
||||
@@ -324,15 +325,52 @@ public class IconCache {
|
||||
componentName.toShortString());
|
||||
entry.icon = preloaded;
|
||||
} else {
|
||||
if (DEBUG) Log.d(TAG, "using default icon for " +
|
||||
componentName.toShortString());
|
||||
entry.icon = getDefaultIcon(user);
|
||||
if (usePackageIcon) {
|
||||
CacheEntry packageEntry = getEntryForPackage(
|
||||
componentName.getPackageName(), user);
|
||||
if (packageEntry != null && packageEntry.icon != null) {
|
||||
if (DEBUG) Log.d(TAG, "using package default icon for " +
|
||||
componentName.toShortString());
|
||||
entry.icon = packageEntry.icon;
|
||||
}
|
||||
}
|
||||
if (entry.icon == null) {
|
||||
if (DEBUG) Log.d(TAG, "using default icon for " +
|
||||
componentName.toShortString());
|
||||
entry.icon = getDefaultIcon(user);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an entry for the package, which can be used as a fallback entry for various components.
|
||||
*/
|
||||
private CacheEntry getEntryForPackage(String packageName, UserHandleCompat user) {
|
||||
ComponentName cn = getPackageComponent(packageName);
|
||||
CacheKey cacheKey = new CacheKey(cn, user);
|
||||
CacheEntry entry = mCache.get(cacheKey);
|
||||
if (entry == null) {
|
||||
entry = new CacheEntry();
|
||||
mCache.put(cacheKey, entry);
|
||||
|
||||
try {
|
||||
ApplicationInfo info = mPackageManager.getApplicationInfo(packageName, 0);
|
||||
entry.title = info.loadLabel(mPackageManager);
|
||||
entry.icon = Utilities.createIconBitmap(info.loadIcon(mPackageManager), mContext);
|
||||
} catch (NameNotFoundException e) {
|
||||
if (DEBUG) Log.d(TAG, "Application not installed " + packageName);
|
||||
}
|
||||
|
||||
if (entry.icon == null) {
|
||||
entry.icon = getPreloadedIcon(cn, user);
|
||||
}
|
||||
}
|
||||
return entry;
|
||||
}
|
||||
|
||||
public HashMap<ComponentName,Bitmap> getAllIcons() {
|
||||
synchronized (mCache) {
|
||||
HashMap<ComponentName,Bitmap> set = new HashMap<ComponentName,Bitmap>();
|
||||
@@ -471,4 +509,8 @@ public class IconCache {
|
||||
String filename = resourceName.replace(File.separatorChar, '_');
|
||||
return RESOURCE_FILE_PREFIX + filename;
|
||||
}
|
||||
|
||||
static ComponentName getPackageComponent(String packageName) {
|
||||
return new ComponentName(packageName, EMPTY_CLASS_NAME);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@ import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.PropertyValuesHolder;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.animation.ValueAnimator.AnimatorUpdateListener;
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.Activity;
|
||||
import android.app.ActivityManager;
|
||||
@@ -90,10 +89,8 @@ import android.view.ViewTreeObserver.OnGlobalLayoutListener;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
import android.view.accessibility.AccessibilityEvent;
|
||||
import android.view.animation.AccelerateDecelerateInterpolator;
|
||||
import android.view.animation.AccelerateInterpolator;
|
||||
import android.view.animation.DecelerateInterpolator;
|
||||
import android.view.animation.LinearInterpolator;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.Advanceable;
|
||||
import android.widget.FrameLayout;
|
||||
@@ -4445,7 +4442,9 @@ public class Launcher extends Activity
|
||||
item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
|
||||
} else {
|
||||
appWidgetInfo = null;
|
||||
item.hostView = new PendingAppWidgetHostView(this, item.restoreStatus);
|
||||
PendingAppWidgetHostView view = new PendingAppWidgetHostView(this, item);
|
||||
view.updateIcon(mIconCache);
|
||||
item.hostView = view;
|
||||
item.hostView.updateAppWidget(null);
|
||||
item.hostView.setOnClickListener(this);
|
||||
}
|
||||
@@ -4478,10 +4477,7 @@ public class Launcher extends Activity
|
||||
return;
|
||||
}
|
||||
|
||||
PendingAppWidgetHostView pendingView = (PendingAppWidgetHostView) view;
|
||||
pendingView.setStatus(LauncherAppWidgetInfo.RESTORE_COMPLETED);
|
||||
|
||||
LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) pendingView.getTag();
|
||||
LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) view.getTag();
|
||||
info.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED;
|
||||
|
||||
mWorkspace.reinflateWidgetsIfNecessary();
|
||||
|
||||
@@ -22,6 +22,8 @@ import android.appwidget.AppWidgetProviderInfo;
|
||||
import android.content.Context;
|
||||
import android.os.TransactionTooLargeException;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
* Specific {@link AppWidgetHost} that creates our {@link LauncherAppWidgetHostView}
|
||||
* which correctly captures all long-press events. This ensures that users can
|
||||
@@ -29,6 +31,8 @@ import android.os.TransactionTooLargeException;
|
||||
*/
|
||||
public class LauncherAppWidgetHost extends AppWidgetHost {
|
||||
|
||||
private final ArrayList<Runnable> mProviderChangeListeners = new ArrayList<Runnable>();
|
||||
|
||||
Launcher mLauncher;
|
||||
|
||||
public LauncherAppWidgetHost(Launcher launcher, int hostId) {
|
||||
@@ -64,9 +68,21 @@ public class LauncherAppWidgetHost extends AppWidgetHost {
|
||||
clearViews();
|
||||
}
|
||||
|
||||
public void addProviderChangeListener(Runnable callback) {
|
||||
mProviderChangeListeners.add(callback);
|
||||
}
|
||||
|
||||
public void removeProviderChangeListener(Runnable callback) {
|
||||
mProviderChangeListeners.remove(callback);
|
||||
}
|
||||
|
||||
protected void onProvidersChanged() {
|
||||
// Once we get the message that widget packages are updated, we need to rebind items
|
||||
// in AppsCustomize accordingly.
|
||||
mLauncher.bindPackagesUpdated(LauncherModel.getSortedWidgetsAndShortcuts(mLauncher));
|
||||
|
||||
for (Runnable callback : mProviderChangeListeners) {
|
||||
callback.run();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,6 +67,11 @@ public class LauncherAppWidgetInfo extends ItemInfo {
|
||||
*/
|
||||
int restoreStatus;
|
||||
|
||||
/**
|
||||
* Indicates the installation progress of the widget provider
|
||||
*/
|
||||
int installProgress;
|
||||
|
||||
private boolean mHasNotifiedInitialWidgetSizeChanged;
|
||||
|
||||
/**
|
||||
|
||||
@@ -692,6 +692,9 @@ public class LauncherBackupHelper implements BackupHelper {
|
||||
.decodeByteArray(widget.icon.data, 0, widget.icon.data.length);
|
||||
if (icon == null) {
|
||||
Log.w(TAG, "failed to unpack widget icon for " + key.name);
|
||||
} else {
|
||||
IconCache.preloadIcon(mContext, ComponentName.unflattenFromString(widget.provider),
|
||||
icon, widget.icon.dpi);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -3089,7 +3089,7 @@ public class LauncherModel extends BroadcastReceiver
|
||||
info.user = UserHandleCompat.myUserHandle();
|
||||
info.contentDescription = mUserManager.getBadgedLabelForUser(
|
||||
info.title.toString(), info.user);
|
||||
info.setIcon(mIconCache.getIcon(intent, info.title.toString(), info.user));
|
||||
info.setIcon(mIconCache.getIcon(intent, info.title.toString(), info.user, false));
|
||||
info.itemType = LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
|
||||
info.restoredIntent = intent;
|
||||
info.wasPromise = true;
|
||||
@@ -3378,7 +3378,7 @@ public class LauncherModel extends BroadcastReceiver
|
||||
/**
|
||||
* Attempts to find an AppWidgetProviderInfo that matches the given component.
|
||||
*/
|
||||
AppWidgetProviderInfo findAppWidgetProviderInfoWithComponent(Context context,
|
||||
static AppWidgetProviderInfo findAppWidgetProviderInfoWithComponent(Context context,
|
||||
ComponentName component) {
|
||||
List<AppWidgetProviderInfo> widgets =
|
||||
AppWidgetManager.getInstance(context).getInstalledProviders();
|
||||
|
||||
@@ -1,21 +1,59 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
package com.android.launcher3;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources.Theme;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
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 static Theme sPreloaderTheme;
|
||||
|
||||
private TextView mDefaultView;
|
||||
private final Rect mRect = new Rect();
|
||||
private View mDefaultView;
|
||||
private OnClickListener mClickListener;
|
||||
private final LauncherAppWidgetInfo mInfo;
|
||||
private final int mStartState;
|
||||
private final Intent mIconLookupIntent;
|
||||
|
||||
public PendingAppWidgetHostView(Context context, int restoreStatus) {
|
||||
private Bitmap mIcon;
|
||||
private PreloadIconDrawable mDrawable;
|
||||
|
||||
private Drawable mCenterDrawable;
|
||||
private Drawable mTopCornerDrawable;
|
||||
|
||||
private boolean mDrawableSizeChanged;
|
||||
|
||||
public PendingAppWidgetHostView(Context context, LauncherAppWidgetInfo info) {
|
||||
super(context);
|
||||
mRestoreStatus = restoreStatus;
|
||||
mInfo = info;
|
||||
mStartState = info.restoreStatus;
|
||||
mIconLookupIntent = new Intent().setComponent(info.providerName);
|
||||
|
||||
setBackgroundResource(R.drawable.quantum_panel_dark);
|
||||
setWillNotDraw(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -27,7 +65,7 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implemen
|
||||
@Override
|
||||
protected View getDefaultView() {
|
||||
if (mDefaultView == null) {
|
||||
mDefaultView = (TextView) mInflater.inflate(R.layout.appwidget_not_ready, this, false);
|
||||
mDefaultView = mInflater.inflate(R.layout.appwidget_not_ready, this, false);
|
||||
mDefaultView.setOnClickListener(this);
|
||||
applyState();
|
||||
}
|
||||
@@ -39,26 +77,57 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implemen
|
||||
mClickListener = l;
|
||||
}
|
||||
|
||||
public void setStatus(int status) {
|
||||
if (mRestoreStatus != status) {
|
||||
mRestoreStatus = status;
|
||||
applyState();
|
||||
@Override
|
||||
public boolean isReinflateRequired() {
|
||||
// Re inflate is required any time the widget restore status changes
|
||||
return mStartState != mInfo.restoreStatus;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
|
||||
super.onSizeChanged(w, h, oldw, oldh);
|
||||
mDrawableSizeChanged = true;
|
||||
}
|
||||
|
||||
public void updateIcon(IconCache cache) {
|
||||
Bitmap icon = cache.getIcon(mIconLookupIntent, mInfo.user);
|
||||
if (mIcon == icon) {
|
||||
return;
|
||||
}
|
||||
mIcon = icon;
|
||||
if (mDrawable != null) {
|
||||
mDrawable.setCallback(null);
|
||||
mDrawable = null;
|
||||
}
|
||||
if (mIcon != null) {
|
||||
// The view displays two modes, one with a setup icon and another with a preload icon
|
||||
// in the center.
|
||||
if (isReadyForClickSetup()) {
|
||||
mCenterDrawable = getResources().getDrawable(R.drawable.ic_setting);
|
||||
mTopCornerDrawable = new FastBitmapDrawable(mIcon);
|
||||
} else {
|
||||
if (sPreloaderTheme == null) {
|
||||
sPreloaderTheme = getResources().newTheme();
|
||||
sPreloaderTheme.applyStyle(R.style.PreloadIcon, true);
|
||||
}
|
||||
|
||||
FastBitmapDrawable drawable = Utilities.createIconDrawable(mIcon);
|
||||
mDrawable = new PreloadIconDrawable(drawable, sPreloaderTheme);
|
||||
mDrawable.setCallback(this);
|
||||
applyState();
|
||||
}
|
||||
mDrawableSizeChanged = true;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isReinflateRequired() {
|
||||
// Re inflate is required if the the widget is restored.
|
||||
return mRestoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED;
|
||||
protected boolean verifyDrawable(Drawable who) {
|
||||
return (who == mDrawable) || super.verifyDrawable(who);
|
||||
}
|
||||
|
||||
private void applyState() {
|
||||
if (mDefaultView != null) {
|
||||
if (isReadyForClickSetup()) {
|
||||
mDefaultView.setText(R.string.gadget_setup_text);
|
||||
} else {
|
||||
mDefaultView.setText(R.string.gadget_pending_text);
|
||||
}
|
||||
public void applyState() {
|
||||
if (mDrawable != null) {
|
||||
mDrawable.setLevel(mInfo.installProgress);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -72,7 +141,51 @@ public class PendingAppWidgetHostView extends LauncherAppWidgetHostView implemen
|
||||
}
|
||||
|
||||
public boolean isReadyForClickSetup() {
|
||||
return (mRestoreStatus & LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) == 0
|
||||
&& (mRestoreStatus & LauncherAppWidgetInfo.FLAG_UI_NOT_READY) != 0;
|
||||
return (mInfo.restoreStatus & LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) == 0
|
||||
&& (mInfo.restoreStatus & LauncherAppWidgetInfo.FLAG_UI_NOT_READY) != 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDraw(Canvas canvas) {
|
||||
if (mDrawable != null) {
|
||||
if (mDrawableSizeChanged) {
|
||||
int maxSize = LauncherAppState.getInstance().getDynamicGrid()
|
||||
.getDeviceProfile().iconSizePx + 2 * mDrawable.getOutset();
|
||||
int size = Math.min(maxSize, Math.min(
|
||||
getWidth() - getPaddingLeft() - getPaddingRight(),
|
||||
getHeight() - getPaddingTop() - getPaddingBottom()));
|
||||
|
||||
mRect.set(0, 0, size, size);
|
||||
mRect.inset(mDrawable.getOutset(), mDrawable.getOutset());
|
||||
mRect.offsetTo((getWidth() - mRect.width()) / 2, (getHeight() - mRect.height()) / 2);
|
||||
mDrawable.setBounds(mRect);
|
||||
mDrawableSizeChanged = false;
|
||||
}
|
||||
|
||||
mDrawable.draw(canvas);
|
||||
} else if ((mCenterDrawable != null) && (mTopCornerDrawable != null)) {
|
||||
if (mDrawableSizeChanged) {
|
||||
int iconSize = getResources().getDimensionPixelSize(R.dimen.app_icon_size);
|
||||
int paddingTop = getPaddingTop();
|
||||
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);
|
||||
mDrawableSizeChanged = false;
|
||||
}
|
||||
|
||||
mCenterDrawable.draw(canvas);
|
||||
mTopCornerDrawable.draw(canvas);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import android.animation.TimeInterpolator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.animation.ValueAnimator.AnimatorUpdateListener;
|
||||
import android.app.WallpaperManager;
|
||||
import android.appwidget.AppWidgetHost;
|
||||
import android.appwidget.AppWidgetHostView;
|
||||
import android.appwidget.AppWidgetProviderInfo;
|
||||
import android.content.ComponentName;
|
||||
@@ -45,7 +46,10 @@ import android.graphics.Region.Op;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Handler;
|
||||
import android.os.Handler.Callback;
|
||||
import android.os.IBinder;
|
||||
import android.os.Message;
|
||||
import android.os.Parcelable;
|
||||
import android.support.v4.view.ViewCompat;
|
||||
import android.util.AttributeSet;
|
||||
@@ -74,6 +78,7 @@ import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
/**
|
||||
* The workspace is a wide area with a wallpaper and a finite number of pages.
|
||||
@@ -431,7 +436,6 @@ public class Workspace extends SmoothPagedView
|
||||
* Initializes various states for this workspace.
|
||||
*/
|
||||
protected void initWorkspace() {
|
||||
Context context = getContext();
|
||||
mCurrentPage = mDefaultPage;
|
||||
Launcher.setScreen(mCurrentPage);
|
||||
LauncherAppState app = LauncherAppState.getInstance();
|
||||
@@ -4887,7 +4891,14 @@ public class Workspace extends SmoothPagedView
|
||||
((ShortcutInfo) info).setProgress(installInfo.progress);
|
||||
((ShortcutInfo) info).setState(installInfo.state);
|
||||
((BubbleTextView)v).applyState();
|
||||
} else if (v instanceof PendingAppWidgetHostView
|
||||
&& info instanceof LauncherAppWidgetInfo
|
||||
&& ((LauncherAppWidgetInfo) info).providerName.getPackageName()
|
||||
.equals(installInfo.packageName)) {
|
||||
((LauncherAppWidgetInfo) info).installProgress = installInfo.progress;
|
||||
((PendingAppWidgetHostView) v).applyState();
|
||||
}
|
||||
|
||||
// process all the shortcuts
|
||||
return false;
|
||||
}
|
||||
@@ -4904,7 +4915,8 @@ public class Workspace extends SmoothPagedView
|
||||
}
|
||||
|
||||
private void restorePendingWidgets(final Set<String> installedPackaged) {
|
||||
final AtomicBoolean widgetsChanged = new AtomicBoolean(false);
|
||||
final ArrayList<LauncherAppWidgetInfo> changedInfo = new ArrayList<LauncherAppWidgetInfo>();
|
||||
|
||||
// Iterate non recursively as widgets can't be inside a folder.
|
||||
mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
|
||||
|
||||
@@ -4914,18 +4926,28 @@ public class Workspace extends SmoothPagedView
|
||||
LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) info;
|
||||
if (widgetInfo.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY)
|
||||
&& installedPackaged.contains(widgetInfo.providerName.getPackageName())) {
|
||||
widgetsChanged.set(true);
|
||||
|
||||
changedInfo.add(widgetInfo);
|
||||
|
||||
// Remove the provider not ready flag
|
||||
widgetInfo.restoreStatus &= ~LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY;
|
||||
LauncherModel.updateItemInDatabase(getContext(), widgetInfo);
|
||||
}
|
||||
}
|
||||
// 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);
|
||||
if (!changedInfo.isEmpty()) {
|
||||
DeferredWidgetRefresh widgetRefresh = new DeferredWidgetRefresh(changedInfo,
|
||||
mLauncher.getAppWidgetHost());
|
||||
if (LauncherModel.findAppWidgetProviderInfoWithComponent(getContext(),
|
||||
changedInfo.get(0).providerName) != null) {
|
||||
// Re-inflate the widgets which have changed status
|
||||
widgetRefresh.run();
|
||||
} else {
|
||||
// widgetRefresh will automatically run when the packages are updated.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5003,4 +5025,53 @@ public class Workspace extends SmoothPagedView
|
||||
public void getLocationInDragLayer(int[] loc) {
|
||||
mLauncher.getDragLayer().getLocationInDragLayer(this, loc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Used as a workaround to ensure that the AppWidgetService receives the
|
||||
* PACKAGE_ADDED broadcast before updating widgets.
|
||||
*/
|
||||
private class DeferredWidgetRefresh implements Runnable {
|
||||
private final ArrayList<LauncherAppWidgetInfo> mInfos;
|
||||
private final LauncherAppWidgetHost mHost;
|
||||
private final Handler mHandler;
|
||||
|
||||
private boolean mRefreshPending;
|
||||
|
||||
public DeferredWidgetRefresh(ArrayList<LauncherAppWidgetInfo> infos,
|
||||
LauncherAppWidgetHost host) {
|
||||
mInfos = infos;
|
||||
mHost = host;
|
||||
mHandler = new Handler();
|
||||
mRefreshPending = true;
|
||||
|
||||
mHost.addProviderChangeListener(this);
|
||||
// Force refresh after 10 seconds, if we don't get the provider changed event.
|
||||
// This could happen when the provider is no longer available in the app.
|
||||
mHandler.postDelayed(this, 10000);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
mHost.removeProviderChangeListener(this);
|
||||
mHandler.removeCallbacks(this);
|
||||
|
||||
if (!mRefreshPending) {
|
||||
return;
|
||||
}
|
||||
|
||||
mRefreshPending = false;
|
||||
|
||||
for (LauncherAppWidgetInfo info : mInfos) {
|
||||
if (info.hostView instanceof PendingAppWidgetHostView) {
|
||||
PendingAppWidgetHostView view = (PendingAppWidgetHostView) info.hostView;
|
||||
mLauncher.removeAppWidget(info);
|
||||
|
||||
CellLayout cl = (CellLayout) view.getParent().getParent();
|
||||
// Remove the current widget
|
||||
cl.removeView(view);
|
||||
mLauncher.bindAppWidget(info);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user