summaryrefslogtreecommitdiffstats
path: root/src/com
diff options
context:
space:
mode:
authorSunny Goyal <sunnygoyal@google.com>2014-07-22 13:48:29 -0700
committerSunny Goyal <sunnygoyal@google.com>2014-08-01 14:45:39 -0700
commite755d469d40b95e763a9dcb67d0e4f511d1948dd (patch)
treeefeb5b14ebf555a41f37bac96b8eb198e61c7899 /src/com
parentc5b6ac7215329d9f827731a43c330451f38b9965 (diff)
downloadandroid_packages_apps_Trebuchet-e755d469d40b95e763a9dcb67d0e4f511d1948dd.tar.gz
android_packages_apps_Trebuchet-e755d469d40b95e763a9dcb67d0e4f511d1948dd.tar.bz2
android_packages_apps_Trebuchet-e755d469d40b95e763a9dcb67d0e4f511d1948dd.zip
Implementing a package install progress listener for L
issue: 15835307 Change-Id: I71aaea087963f2e0e1206447190cbe23c174057d
Diffstat (limited to 'src/com')
-rw-r--r--src/com/android/launcher3/BubbleTextView.java93
-rw-r--r--src/com/android/launcher3/Launcher.java19
-rw-r--r--src/com/android/launcher3/LauncherAppState.java13
-rw-r--r--src/com/android/launcher3/LauncherModel.java7
-rw-r--r--src/com/android/launcher3/ShortcutInfo.java29
-rw-r--r--src/com/android/launcher3/Workspace.java41
-rw-r--r--src/com/android/launcher3/compat/LauncherAppsCompat.java7
-rw-r--r--src/com/android/launcher3/compat/PackageInstallerCompat.java67
-rw-r--r--src/com/android/launcher3/compat/PackageInstallerCompatV16.java170
-rw-r--r--src/com/android/launcher3/compat/PackageInstallerCompatVL.java159
10 files changed, 500 insertions, 105 deletions
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index ffa7ec37e..ab94814c3 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -434,65 +434,48 @@ public class BubbleTextView extends TextView {
}
public void applyState() {
- final int progressLevel;
- final int state = getState();
- if (DEBUG) Log.d(TAG, "applying icon state: " + state);
-
- switch(state) {
- case ShortcutInfo.PACKAGE_STATE_DEFAULT:
- super.setText(mDefaultText);
- progressLevel = 100;
- break;
-
- case ShortcutInfo.PACKAGE_STATE_ENQUEUED:
- setText(R.string.package_state_enqueued);
- progressLevel = 0;
- break;
-
- case ShortcutInfo.PACKAGE_STATE_DOWNLOADING:
- setText(R.string.package_state_downloading);
- // TODO(sunnygoyal): fix progress
- progressLevel = 30;
- break;
-
- case ShortcutInfo.PACKAGE_STATE_INSTALLING:
- setText(R.string.package_state_installing);
- progressLevel = 100;
- break;
+ if (getTag() instanceof ShortcutInfo) {
+ ShortcutInfo info = (ShortcutInfo) getTag();
+ final int state = info.getState();
+
+ final int progressLevel;
+ if (DEBUG) Log.d(TAG, "applying icon state: " + state);
+
+ switch(state) {
+ case ShortcutInfo.PACKAGE_STATE_DEFAULT:
+ progressLevel = 100;
+ break;
+
+ case ShortcutInfo.PACKAGE_STATE_INSTALLING:
+ setText(R.string.package_state_installing);
+ progressLevel = info.getProgress();
+ break;
+
+ case ShortcutInfo.PACKAGE_STATE_ERROR:
+ case ShortcutInfo.PACKAGE_STATE_UNKNOWN:
+ default:
+ progressLevel = 0;
+ setText(R.string.package_state_unknown);
+ break;
+ }
- case ShortcutInfo.PACKAGE_STATE_ERROR:
- setText(R.string.package_state_error);
- progressLevel = 0;
- break;
+ Drawable[] drawables = getCompoundDrawables();
+ Drawable top = drawables[1];
+ if (top != null) {
+ final PreloadIconDrawable preloadDrawable;
+ if (top instanceof PreloadIconDrawable) {
+ preloadDrawable = (PreloadIconDrawable) top;
+ } else {
+ preloadDrawable = new PreloadIconDrawable(top, getResources());
+ setCompoundDrawables(drawables[0], preloadDrawable, drawables[2], drawables[3]);
+ }
- case ShortcutInfo.PACKAGE_STATE_UNKNOWN:
- default:
- progressLevel = 0;
- setText(R.string.package_state_unknown);
- break;
- }
+ preloadDrawable.setLevel(progressLevel);
+ if (state == ShortcutInfo.PACKAGE_STATE_DEFAULT) {
+ preloadDrawable.maybePerformFinishedAnimation();
+ }
- Drawable[] drawables = getCompoundDrawables();
- Drawable top = drawables[1];
- if ((top != null) && !(top instanceof PreloadIconDrawable)) {
- top = new PreloadIconDrawable(top, getResources());
- setCompoundDrawables(drawables[0], top, drawables[2], drawables[3]);
- }
- if (top != null) {
- top.setLevel(progressLevel);
- if ((top instanceof PreloadIconDrawable)
- && (state == ShortcutInfo.PACKAGE_STATE_DEFAULT)) {
- ((PreloadIconDrawable) top).maybePerformFinishedAnimation();
}
}
}
-
- private int getState() {
- if (! (getTag() instanceof ShortcutInfo)) {
- return ShortcutInfo.PACKAGE_STATE_DEFAULT;
- } else {
- ShortcutInfo info = (ShortcutInfo) getTag();
- return info.getState();
- }
- }
}
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index a5bf690d7..b5bed4cdf 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -82,10 +82,9 @@ import android.view.Menu;
import android.view.MotionEvent;
import android.view.Surface;
import android.view.View;
-import android.view.ViewAnimationUtils;
-import android.view.Window;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
+import android.view.ViewAnimationUtils;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
@@ -93,9 +92,7 @@ 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;
@@ -107,10 +104,10 @@ import com.android.launcher3.DropTarget.DragObject;
import com.android.launcher3.PagedView.PageSwitchListener;
import com.android.launcher3.compat.LauncherActivityInfoCompat;
import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.PackageInstallerCompat;
+import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.compat.UserManagerCompat;
-import com.android.launcher3.DropTarget.DragObject;
-import com.android.launcher3.PagedView.PageSwitchListener;
import java.io.DataInputStream;
import java.io.DataOutputStream;
@@ -1055,12 +1052,15 @@ public class Launcher extends Activity
}
mWorkspace.updateInteractionForState();
mWorkspace.onResume();
+
+ PackageInstallerCompat.getInstance(this).onResume();
}
@Override
protected void onPause() {
// Ensure that items added to Launcher are queued until Launcher returns
InstallShortcutReceiver.enableInstallQueue();
+ PackageInstallerCompat.getInstance(this).onPause();
super.onPause();
mPaused = true;
@@ -2028,6 +2028,7 @@ public class Launcher extends Activity
mWorkspace = null;
mDragController = null;
+ PackageInstallerCompat.getInstance(this).onStop();
LauncherAnimUtils.onDestroyActivity();
}
@@ -4478,6 +4479,7 @@ public class Launcher extends Activity
mWorkspace.getUniqueComponents(true, null);
mIntentsOnWorkspaceFromUpgradePath = mWorkspace.getUniqueComponents(true, null);
}
+ PackageInstallerCompat.getInstance(this).onFinishBind();
}
private void sendLoadingCompleteBroadcastIfNecessary() {
@@ -4591,9 +4593,10 @@ public class Launcher extends Activity
*
* Implementation of the method from LauncherModel.Callbacks.
*/
- public void updatePackageState(String pkgName, int state) {
+ @Override
+ public void updatePackageState(ArrayList<PackageInstallInfo> installInfo) {
if (mWorkspace != null) {
- mWorkspace.updatePackageState(pkgName, state);
+ mWorkspace.updatePackageState(installInfo);
}
}
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 27bcd8172..4ab4e4bea 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -17,7 +17,11 @@
package com.android.launcher3;
import android.app.SearchManager;
-import android.content.*;
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.database.ContentObserver;
@@ -25,8 +29,10 @@ import android.os.Handler;
import android.util.Log;
import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
import java.lang.ref.WeakReference;
+import java.util.ArrayList;
public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks {
private static final String TAG = "LauncherAppState";
@@ -251,8 +257,7 @@ public class LauncherAppState implements DeviceProfile.DeviceProfileCallbacks {
return getInstance().mBuildInfo.isDogfoodBuild();
}
- public void setPackageState(String pkgName, int state) {
- if (DEBUG) Log.d(TAG, "setPackageState(" + pkgName + ", " + state + ")");
- mModel.setPackageState(pkgName, state);
+ public void setPackageState(ArrayList<PackageInstallInfo> installInfo) {
+ mModel.setPackageState(installInfo);
}
}
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index e115bf11c..86edaef44 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -55,6 +55,7 @@ import android.util.Pair;
import com.android.launcher3.InstallWidgetReceiver.WidgetMimeTypeHandlerData;
import com.android.launcher3.compat.LauncherActivityInfoCompat;
import com.android.launcher3.compat.LauncherAppsCompat;
+import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
import com.android.launcher3.compat.UserHandleCompat;
import com.android.launcher3.compat.UserManagerCompat;
@@ -195,7 +196,7 @@ public class LauncherModel extends BroadcastReceiver
ArrayList<ItemInfo> addAnimated,
ArrayList<AppInfo> addedApps);
public void bindAppsUpdated(ArrayList<AppInfo> apps);
- public void updatePackageState(String pkgName, int state);
+ public void updatePackageState(ArrayList<PackageInstallInfo> installInfo);
public void bindComponentsRemoved(ArrayList<String> packageNames,
ArrayList<AppInfo> appInfos, UserHandleCompat user);
public void bindPackagesUpdated(ArrayList<Object> widgetsAndShortcuts);
@@ -332,13 +333,13 @@ public class LauncherModel extends BroadcastReceiver
return null;
}
- public void setPackageState(final String pkgName, final int state) {
+ public void setPackageState(final ArrayList<PackageInstallInfo> installInfo) {
// Process the updated package state
Runnable r = new Runnable() {
public void run() {
Callbacks callbacks = mCallbacks != null ? mCallbacks.get() : null;
if (callbacks != null) {
- callbacks.updatePackageState(pkgName, state);
+ callbacks.updatePackageState(installInfo);
}
}
};
diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java
index dc019b272..612b0a5aa 100644
--- a/src/com/android/launcher3/ShortcutInfo.java
+++ b/src/com/android/launcher3/ShortcutInfo.java
@@ -42,13 +42,7 @@ public class ShortcutInfo extends ItemInfo {
public static final int PACKAGE_STATE_DEFAULT = 0;
/** {@link #mState} meaning some external entity has promised to install this package. */
- public static final int PACKAGE_STATE_ENQUEUED = 1;
-
- /** {@link #mState} meaning but some external entity is downloading this package. */
- public static final int PACKAGE_STATE_DOWNLOADING = 2;
-
- /** {@link #mState} meaning some external entity is installing this package. */
- public static final int PACKAGE_STATE_INSTALLING = 3;
+ public static final int PACKAGE_STATE_INSTALLING = 1;
/**
* The intent used to start the application.
@@ -89,6 +83,11 @@ public class ShortcutInfo extends ItemInfo {
*/
protected int mState;
+ /**
+ * The installation progress [0-100] of the package that this shortcut represents.
+ */
+ protected int mProgress;
+
long firstInstallTime;
int flags = 0;
@@ -237,16 +236,24 @@ public class ShortcutInfo extends ItemInfo {
public boolean isAbandoned() {
return isPromise()
- && (mState == ShortcutInfo.PACKAGE_STATE_ERROR
- || mState == ShortcutInfo.PACKAGE_STATE_UNKNOWN);
+ && (mState == PACKAGE_STATE_ERROR
+ || mState == PACKAGE_STATE_UNKNOWN);
}
- public int getState() {
- return mState;
+ public int getProgress() {
+ return mProgress;
+ }
+
+ public void setProgress(int progress) {
+ mProgress = progress;
}
public void setState(int state) {
mState = state;
}
+
+ public int getState() {
+ return mState;
+ }
}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 6d9b34e21..7c8708b50 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -67,6 +67,7 @@ import android.widget.TextView;
import com.android.launcher3.FolderIcon.FolderRingAnimator;
import com.android.launcher3.Launcher.CustomContentCallbacks;
import com.android.launcher3.LauncherSettings.Favorites;
+import com.android.launcher3.compat.PackageInstallerCompat.PackageInstallInfo;
import com.android.launcher3.compat.UserHandleCompat;
import java.util.ArrayList;
@@ -4864,26 +4865,32 @@ public class Workspace extends SmoothPagedView
}
}
- public void updatePackageState(final String pkgName, final int state) {
- mapOverItems(MAP_RECURSE, new ItemOperator() {
- @Override
- public boolean evaluate(ItemInfo info, View v, View parent) {
- if (info instanceof ShortcutInfo
- && ((ShortcutInfo) info).isPromiseFor(pkgName)
- && v instanceof BubbleTextView) {
- ((ShortcutInfo) info).setState(state);
- ((BubbleTextView)v).applyState();
+ public void updatePackageState(ArrayList<PackageInstallInfo> installInfos) {
+ HashSet<String> completedPackages = new HashSet<>();
+
+ for (final PackageInstallInfo installInfo : installInfos) {
+ mapOverItems(MAP_RECURSE, new ItemOperator() {
+ @Override
+ public boolean evaluate(ItemInfo info, View v, View parent) {
+ if (info instanceof ShortcutInfo
+ && ((ShortcutInfo) info).isPromiseFor(installInfo.packageName)
+ && v instanceof BubbleTextView) {
+ ((ShortcutInfo) info).setProgress(installInfo.progress);
+ ((ShortcutInfo) info).setState(installInfo.state);
+ ((BubbleTextView)v).applyState();
+ }
+ // process all the shortcuts
+ return false;
}
- // process all the shortcuts
- return false;
+ });
+
+ if (installInfo.state == ShortcutInfo.PACKAGE_STATE_DEFAULT) {
+ completedPackages.add(installInfo.packageName);
}
- });
+ }
- if (state == ShortcutInfo.PACKAGE_STATE_DEFAULT) {
- // Update any pending widget
- HashSet<String> packages = new HashSet<String>();
- packages.add(pkgName);
- restorePendingWidgets(packages);
+ if (!completedPackages.isEmpty()) {
+ restorePendingWidgets(completedPackages);
}
}
diff --git a/src/com/android/launcher3/compat/LauncherAppsCompat.java b/src/com/android/launcher3/compat/LauncherAppsCompat.java
index 8d978d4a3..cc0203d27 100644
--- a/src/com/android/launcher3/compat/LauncherAppsCompat.java
+++ b/src/com/android/launcher3/compat/LauncherAppsCompat.java
@@ -20,17 +20,10 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.graphics.Rect;
-import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle;
-import android.os.UserHandle;
-import android.os.UserManager;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.HashMap;
import java.util.List;
-import java.util.Map;
public abstract class LauncherAppsCompat {
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompat.java b/src/com/android/launcher3/compat/PackageInstallerCompat.java
new file mode 100644
index 000000000..89a2157eb
--- /dev/null
+++ b/src/com/android/launcher3/compat/PackageInstallerCompat.java
@@ -0,0 +1,67 @@
+/*
+ * 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.compat;
+
+import android.content.Context;
+
+import com.android.launcher3.Utilities;
+
+public abstract class PackageInstallerCompat {
+
+ private static final Object sInstanceLock = new Object();
+ private static PackageInstallerCompat sInstance;
+
+ public static PackageInstallerCompat getInstance(Context context) {
+ synchronized (sInstanceLock) {
+ if (sInstance == null) {
+ if (Utilities.isLmp()) {
+ sInstance = new PackageInstallerCompatVL(context);
+ } else {
+ sInstance = new PackageInstallerCompatV16(context) { };
+ }
+ }
+ return sInstance;
+ }
+ }
+
+ public abstract void onPause();
+
+ public abstract void onResume();
+
+ public abstract void onFinishBind();
+
+ public abstract void onStop();
+
+ public abstract void recordPackageUpdate(String packageName, int state, int progress);
+
+ public static final class PackageInstallInfo {
+ public final String packageName;
+
+ public int state;
+ public int progress;
+
+ public PackageInstallInfo(String packageName) {
+ this.packageName = packageName;
+ }
+
+ public PackageInstallInfo(String packageName, int state, int progress) {
+ this.packageName = packageName;
+ this.state = state;
+ this.progress = progress;
+ }
+ }
+}
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompatV16.java b/src/com/android/launcher3/compat/PackageInstallerCompatV16.java
new file mode 100644
index 000000000..653a88c7d
--- /dev/null
+++ b/src/com/android/launcher3/compat/PackageInstallerCompatV16.java
@@ -0,0 +1,170 @@
+/*
+ * 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.compat;
+
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.ShortcutInfo;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.json.JSONStringer;
+import org.json.JSONTokener;
+
+import java.util.ArrayList;
+
+public class PackageInstallerCompatV16 extends PackageInstallerCompat {
+
+ private static final String TAG = "PackageInstallerCompatV16";
+ private static final boolean DEBUG = false;
+
+ private static final String KEY_PROGRESS = "progress";
+ private static final String KEY_STATE = "state";
+
+ private static final String PREFS =
+ "com.android.launcher3.compat.PackageInstallerCompatV16.queue";
+
+ protected final SharedPreferences mPrefs;
+
+ boolean mUseQueue;
+ boolean mFinishedBind;
+ boolean mReplayPending;
+
+ PackageInstallerCompatV16(Context context) {
+ mPrefs = context.getSharedPreferences(PREFS, Context.MODE_PRIVATE);
+ }
+
+ @Override
+ public void onPause() {
+ mUseQueue = true;
+ if (DEBUG) Log.d(TAG, "updates paused");
+ }
+
+ @Override
+ public void onResume() {
+ mUseQueue = false;
+ if (mFinishedBind) {
+ replayUpdates();
+ }
+ }
+
+ @Override
+ public void onFinishBind() {
+ mFinishedBind = true;
+ if (!mUseQueue) {
+ replayUpdates();
+ }
+ }
+
+ @Override
+ public void onStop() { }
+
+ private void replayUpdates() {
+ if (DEBUG) Log.d(TAG, "updates resumed");
+ LauncherAppState app = LauncherAppState.getInstanceNoCreate();
+ if (app == null) {
+ mReplayPending = true; // try again later
+ if (DEBUG) Log.d(TAG, "app is null, delaying send");
+ return;
+ }
+ mReplayPending = false;
+ ArrayList<PackageInstallInfo> updates = new ArrayList<>();
+ for (String packageName: mPrefs.getAll().keySet()) {
+ final String json = mPrefs.getString(packageName, null);
+ if (!TextUtils.isEmpty(json)) {
+ updates.add(infoFromJson(packageName, json));
+ }
+ }
+ if (!updates.isEmpty()) {
+ sendUpdate(app, updates);
+ }
+ }
+
+ /**
+ * This should be called by the implementations to register a package update.
+ */
+ @Override
+ public synchronized void recordPackageUpdate(String packageName, int state, int progress) {
+ SharedPreferences.Editor editor = mPrefs.edit();
+ PackageInstallInfo installInfo = new PackageInstallInfo(packageName);
+ installInfo.progress = progress;
+ installInfo.state = state;
+ if (state == ShortcutInfo.PACKAGE_STATE_DEFAULT) {
+ // no longer necessary to track this package
+ editor.remove(packageName);
+ if (DEBUG) Log.d(TAG, "no longer tracking " + packageName);
+ } else {
+ editor.putString(packageName, infoToJson(installInfo));
+ if (DEBUG)
+ Log.d(TAG, "saved state: " + infoToJson(installInfo)
+ + " for package: " + packageName);
+
+ }
+ editor.commit();
+
+ if (!mUseQueue) {
+ if (mReplayPending) {
+ replayUpdates();
+ } else {
+ LauncherAppState app = LauncherAppState.getInstanceNoCreate();
+ ArrayList<PackageInstallInfo> update = new ArrayList<>();
+ update.add(installInfo);
+ sendUpdate(app, update);
+ }
+ }
+ }
+
+ private void sendUpdate(LauncherAppState app, ArrayList<PackageInstallInfo> updates) {
+ if (app == null) {
+ mReplayPending = true; // try again later
+ if (DEBUG) Log.d(TAG, "app is null, delaying send");
+ } else {
+ app.setPackageState(updates);
+ }
+ }
+
+ private static PackageInstallInfo infoFromJson(String packageName, String json) {
+ PackageInstallInfo info = new PackageInstallInfo(packageName);
+ try {
+ JSONObject object = (JSONObject) new JSONTokener(json).nextValue();
+ info.state = object.getInt(KEY_STATE);
+ info.progress = object.getInt(KEY_PROGRESS);
+ } catch (JSONException e) {
+ Log.e(TAG, "failed to deserialize app state update", e);
+ }
+ return info;
+ }
+
+ private static String infoToJson(PackageInstallInfo info) {
+ String value = null;
+ try {
+ JSONStringer json = new JSONStringer()
+ .object()
+ .key(KEY_STATE).value(info.state)
+ .key(KEY_PROGRESS).value(info.progress)
+ .endObject();
+ value = json.toString();
+ } catch (JSONException e) {
+ Log.e(TAG, "failed to serialize app state update", e);
+ }
+ return value;
+ }
+}
diff --git a/src/com/android/launcher3/compat/PackageInstallerCompatVL.java b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
new file mode 100644
index 000000000..7f6302f14
--- /dev/null
+++ b/src/com/android/launcher3/compat/PackageInstallerCompatVL.java
@@ -0,0 +1,159 @@
+/*
+ * 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.compat;
+
+import android.content.Context;
+import android.content.pm.InstallSessionInfo;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageInstaller.SessionCallback;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.ShortcutInfo;
+
+import java.util.ArrayList;
+
+public class PackageInstallerCompatVL extends PackageInstallerCompat {
+
+ private static final String TAG = "PackageInstallerCompatVL";
+ private static final boolean DEBUG = false;
+
+ private final SparseArray<InstallSessionInfo> mPendingReplays = new SparseArray<>();
+ private final PackageInstaller mInstaller;
+
+ private boolean mResumed;
+ private boolean mBound;
+
+ PackageInstallerCompatVL(Context context) {
+ mInstaller = context.getPackageManager().getPackageInstaller();
+
+ mResumed = false;
+ mBound = false;
+
+ mInstaller.addSessionCallback(mCallback);
+ // On start, send updates for all active sessions
+ for (InstallSessionInfo info : mInstaller.getAllSessions()) {
+ mPendingReplays.append(info.getSessionId(), info);
+ }
+ }
+
+ @Override
+ public void onStop() {
+ mInstaller.removeSessionCallback(mCallback);
+ }
+
+ @Override
+ public void onFinishBind() {
+ mBound = true;
+ replayUpdates(null);
+ }
+
+ @Override
+ public void onPause() {
+ mResumed = false;
+ }
+
+ @Override
+ public void onResume() {
+ mResumed = true;
+ replayUpdates(null);
+ }
+
+ @Override
+ public void recordPackageUpdate(String packageName, int state, int progress) {
+ // No op
+ }
+
+ private void replayUpdates(PackageInstallInfo newInfo) {
+ if (DEBUG) Log.d(TAG, "updates resumed");
+ if (!mResumed || !mBound) {
+ // Not yet ready
+ return;
+ }
+ if ((mPendingReplays.size() == 0) && (newInfo == null)) {
+ // Nothing to update
+ return;
+ }
+
+ LauncherAppState app = LauncherAppState.getInstanceNoCreate();
+ if (app == null) {
+ // Try again later
+ if (DEBUG) Log.d(TAG, "app is null, delaying send");
+ return;
+ }
+
+ ArrayList<PackageInstallInfo> updates = new ArrayList<>();
+ if (newInfo != null) {
+ updates.add(newInfo);
+ }
+ for (int i = mPendingReplays.size() - 1; i > 0; i--) {
+ InstallSessionInfo session = mPendingReplays.valueAt(i);
+ if (session.getAppPackageName() != null) {
+ updates.add(new PackageInstallInfo(session.getAppPackageName(),
+ ShortcutInfo.PACKAGE_STATE_INSTALLING,
+ (int) (session.getProgress() * 100)));
+ }
+ }
+ mPendingReplays.clear();
+ if (!updates.isEmpty()) {
+ app.setPackageState(updates);
+ }
+ }
+
+ private final SessionCallback mCallback = new SessionCallback() {
+
+ @Override
+ public void onCreated(int sessionId) {
+ InstallSessionInfo session = mInstaller.getSessionInfo(sessionId);
+ if (session != null) {
+ mPendingReplays.put(sessionId, session);
+ replayUpdates(null);
+ }
+ }
+
+ @Override
+ public void onFinished(int sessionId, boolean success) {
+ mPendingReplays.remove(sessionId);
+ InstallSessionInfo session = mInstaller.getSessionInfo(sessionId);
+ if ((session != null) && (session.getAppPackageName() != null)) {
+ // Replay all updates with a one time update for this installed package. No
+ // need to store this record for future updates, as the app list will get
+ // refreshed on resume.
+ replayUpdates(new PackageInstallInfo(session.getAppPackageName(),
+ success ? ShortcutInfo.PACKAGE_STATE_DEFAULT
+ : ShortcutInfo.PACKAGE_STATE_ERROR, 0));
+ }
+ }
+
+ @Override
+ public void onProgressChanged(int sessionId, float progress) {
+ InstallSessionInfo session = mInstaller.getSessionInfo(sessionId);
+ if (session != null) {
+ mPendingReplays.put(sessionId, session);
+ replayUpdates(null);
+ }
+ }
+
+ @Override
+ public void onOpened(int sessionId) { }
+
+ @Override
+ public void onClosed(int sessionId) { }
+
+ };
+}