summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--AndroidManifest.xml10
-rw-r--r--res/drawable-hdpi/widget_container_holo.9.pngbin369 -> 0 bytes
-rw-r--r--res/drawable-mdpi/widget_container_holo.9.pngbin285 -> 0 bytes
-rw-r--r--res/drawable-xhdpi/widget_container_holo.9.pngbin460 -> 0 bytes
-rw-r--r--res/drawable-xxhdpi/bg_preloader.pngbin3785 -> 0 bytes
-rw-r--r--res/drawable-xxhdpi/bg_preloader_progress.pngbin1131 -> 0 bytes
-rw-r--r--res/drawable-xxhdpi/widget_container_holo.9.pngbin1558 -> 0 bytes
-rw-r--r--res/drawable/bg_appwidget_not_ready.xml24
-rw-r--r--res/layout/appwidget_not_ready.xml16
-rw-r--r--res/values/colors.xml1
-rw-r--r--res/values/config.xml4
-rw-r--r--res/values/strings.xml5
-rw-r--r--src/com/android/launcher3/IconCache.java72
-rw-r--r--src/com/android/launcher3/Launcher.java12
-rw-r--r--src/com/android/launcher3/LauncherAppWidgetHost.java16
-rw-r--r--src/com/android/launcher3/LauncherAppWidgetInfo.java5
-rw-r--r--src/com/android/launcher3/LauncherBackupHelper.java3
-rw-r--r--src/com/android/launcher3/LauncherModel.java4
-rw-r--r--src/com/android/launcher3/PendingAppWidgetHostView.java153
-rw-r--r--src/com/android/launcher3/Workspace.java87
20 files changed, 315 insertions, 97 deletions
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 47e9368a2..3633c8c96 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -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"
diff --git a/res/drawable-hdpi/widget_container_holo.9.png b/res/drawable-hdpi/widget_container_holo.9.png
deleted file mode 100644
index 8c15a7c86..000000000
--- a/res/drawable-hdpi/widget_container_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-mdpi/widget_container_holo.9.png b/res/drawable-mdpi/widget_container_holo.9.png
deleted file mode 100644
index db24457d6..000000000
--- a/res/drawable-mdpi/widget_container_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xhdpi/widget_container_holo.9.png b/res/drawable-xhdpi/widget_container_holo.9.png
deleted file mode 100644
index 1313fe70d..000000000
--- a/res/drawable-xhdpi/widget_container_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/bg_preloader.png b/res/drawable-xxhdpi/bg_preloader.png
deleted file mode 100644
index 56b80607d..000000000
--- a/res/drawable-xxhdpi/bg_preloader.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/bg_preloader_progress.png b/res/drawable-xxhdpi/bg_preloader_progress.png
deleted file mode 100644
index 443afe936..000000000
--- a/res/drawable-xxhdpi/bg_preloader_progress.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable-xxhdpi/widget_container_holo.9.png b/res/drawable-xxhdpi/widget_container_holo.9.png
deleted file mode 100644
index 8f79920c4..000000000
--- a/res/drawable-xxhdpi/widget_container_holo.9.png
+++ /dev/null
Binary files differ
diff --git a/res/drawable/bg_appwidget_not_ready.xml b/res/drawable/bg_appwidget_not_ready.xml
deleted file mode 100644
index a8b56c2f8..000000000
--- a/res/drawable/bg_appwidget_not_ready.xml
+++ /dev/null
@@ -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> \ No newline at end of file
diff --git a/res/layout/appwidget_not_ready.xml b/res/layout/appwidget_not_ready.xml
index f5f2aab21..be7c33b36 100644
--- a/res/layout/appwidget_not_ready.xml
+++ b/res/layout/appwidget_not_ready.xml
@@ -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" />
diff --git a/res/values/colors.xml b/res/values/colors.xml
index 41f38921f..4e64d4184 100644
--- a/res/values/colors.xml
+++ b/res/values/colors.xml
@@ -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>
diff --git a/res/values/config.xml b/res/values/config.xml
index a16f265a8..750a6aa72 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -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>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 286e04fe5..aa5f7b626 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -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>
diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java
index 221df583b..06b77756d 100644
--- a/src/com/android/launcher3/IconCache.java
+++ b/src/com/android/launcher3/IconCache.java
@@ -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);
+ }
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 5eedc8a3a..062e84838 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -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();
diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java
index fa5e38f8b..a309f268c 100644
--- a/src/com/android/launcher3/LauncherAppWidgetHost.java
+++ b/src/com/android/launcher3/LauncherAppWidgetHost.java
@@ -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();
+ }
}
}
diff --git a/src/com/android/launcher3/LauncherAppWidgetInfo.java b/src/com/android/launcher3/LauncherAppWidgetInfo.java
index c1535abae..47554824f 100644
--- a/src/com/android/launcher3/LauncherAppWidgetInfo.java
+++ b/src/com/android/launcher3/LauncherAppWidgetInfo.java
@@ -67,6 +67,11 @@ public class LauncherAppWidgetInfo extends ItemInfo {
*/
int restoreStatus;
+ /**
+ * Indicates the installation progress of the widget provider
+ */
+ int installProgress;
+
private boolean mHasNotifiedInitialWidgetSizeChanged;
/**
diff --git a/src/com/android/launcher3/LauncherBackupHelper.java b/src/com/android/launcher3/LauncherBackupHelper.java
index aecf9b019..64e82c754 100644
--- a/src/com/android/launcher3/LauncherBackupHelper.java
+++ b/src/com/android/launcher3/LauncherBackupHelper.java
@@ -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);
}
}
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 109a70090..4c9d1a700 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -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();
diff --git a/src/com/android/launcher3/PendingAppWidgetHostView.java b/src/com/android/launcher3/PendingAppWidgetHostView.java
index 048e9f8c3..04014366c 100644
--- a/src/com/android/launcher3/PendingAppWidgetHostView.java
+++ b/src/com/android/launcher3/PendingAppWidgetHostView.java
@@ -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;
+
+ private Bitmap mIcon;
+ private PreloadIconDrawable mDrawable;
+
+ private Drawable mCenterDrawable;
+ private Drawable mTopCornerDrawable;
+
+ private boolean mDrawableSizeChanged;
- public PendingAppWidgetHostView(Context context, int restoreStatus) {
+ 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
- public boolean isReinflateRequired() {
- // Re inflate is required if the the widget is restored.
- return mRestoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED;
+ protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+ super.onSizeChanged(w, h, oldw, oldh);
+ mDrawableSizeChanged = true;
}
- private void applyState() {
- if (mDefaultView != null) {
+ 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()) {
- mDefaultView.setText(R.string.gadget_setup_text);
+ mCenterDrawable = getResources().getDrawable(R.drawable.ic_setting);
+ mTopCornerDrawable = new FastBitmapDrawable(mIcon);
} else {
- mDefaultView.setText(R.string.gadget_pending_text);
+ 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
+ protected boolean verifyDrawable(Drawable who) {
+ return (who == mDrawable) || super.verifyDrawable(who);
+ }
+
+ 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);
+ }
+ }
+
}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index cd6fcca1e..8b3a51496 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -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);
+ }
+ }
+ }
+ }
}