Merge "Adding overview mode for reordering, widget adding and wallpaper switching" into jb-ub-gel-agar
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index b9f75cf..f885a6c 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -50,6 +50,11 @@
android:label="@string/permlab_write_settings"
android:description="@string/permdesc_write_settings"/>
+ <permission
+ android:name="com.android.launcher3.permission.RECEIVE_LAUNCH_BROADCASTS"
+ android:protectionLevel="signature"
+ />
+
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.SET_WALLPAPER" />
<uses-permission android:name="android.permission.SET_WALLPAPER_HINTS" />
@@ -60,6 +65,7 @@
<uses-permission android:name="com.android.launcher.permission.WRITE_SETTINGS" />
<uses-permission android:name="com.android.launcher3.permission.READ_SETTINGS" />
<uses-permission android:name="com.android.launcher3.permission.WRITE_SETTINGS" />
+ <uses-permission android:name="com.android.launcher3.permission.RECEIVE_LAUNCH_BROADCASTS" />
<application
android:name="com.android.launcher3.LauncherApplication"
diff --git a/print_db.py b/print_db.py
index f0fc45e..6caa7bb 100755
--- a/print_db.py
+++ b/print_db.py
@@ -140,6 +140,10 @@
hotseatIdMap = []
HOTSEAT_SIZE = 0
for d in data:
+ if d["spanX"] is None:
+ d["spanX"] = 1
+ if d["spanY"] is None:
+ d["spanY"] = 1
if d["container"] == CONTAINER_DESKTOP:
if d["screen"] not in screensIdMap:
screensIdMap.append(d["screen"])
diff --git a/src/com/android/launcher3/FolderIcon.java b/src/com/android/launcher3/FolderIcon.java
index 92f126c..fa713a4 100644
--- a/src/com/android/launcher3/FolderIcon.java
+++ b/src/com/android/launcher3/FolderIcon.java
@@ -27,6 +27,7 @@
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
+import android.os.Looper;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.LayoutInflater;
@@ -167,8 +168,6 @@
public float mOuterRingSize;
public float mInnerRingSize;
public FolderIcon mFolderIcon = null;
- public Drawable mOuterRingDrawable = null;
- public Drawable mInnerRingDrawable = null;
public static Drawable sSharedOuterRingDrawable = null;
public static Drawable sSharedInnerRingDrawable = null;
public static int sPreviewSize = -1;
@@ -180,12 +179,14 @@
public FolderRingAnimator(Launcher launcher, FolderIcon folderIcon) {
mFolderIcon = folderIcon;
Resources res = launcher.getResources();
- mOuterRingDrawable = res.getDrawable(R.drawable.portal_ring_outer_holo);
- mInnerRingDrawable = res.getDrawable(R.drawable.portal_ring_inner_holo);
// We need to reload the static values when configuration changes in case they are
// different in another configuration
if (sStaticValuesDirty) {
+ if (Looper.myLooper() != Looper.getMainLooper()) {
+ throw new RuntimeException("FolderRingAnimator loading drawables on non-UI thread "
+ + Thread.currentThread());
+ }
sPreviewSize = res.getDimensionPixelSize(R.dimen.folder_preview_size);
sPreviewPadding = res.getDimensionPixelSize(R.dimen.folder_preview_padding);
sSharedOuterRingDrawable = res.getDrawable(R.drawable.portal_ring_outer_holo);
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 09881b6..6cebe42 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -346,6 +346,8 @@
int cellY;
}
+ private Stats mStats;
+
private static boolean isPropertyEnabled(String propertyName) {
return Log.isLoggable(propertyName, Log.VERBOSE);
}
@@ -379,6 +381,8 @@
mDragController = new DragController(this);
mInflater = getLayoutInflater();
+ mStats = new Stats(this);
+
mAppWidgetManager = AppWidgetManager.getInstance(this);
mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
mAppWidgetHost.startListening();
@@ -2128,7 +2132,8 @@
Object tag = v.getTag();
if (tag instanceof ShortcutInfo) {
// Open shortcut
- final Intent intent = ((ShortcutInfo) tag).intent;
+ final ShortcutInfo shortcut = (ShortcutInfo) tag;
+ final Intent intent = shortcut.intent;
// Check for special shortcuts
if (intent.getComponent() != null) {
@@ -2154,6 +2159,8 @@
boolean success = startActivitySafely(v, intent, tag);
+ mStats.recordLaunch(intent, shortcut);
+
if (success && v instanceof BubbleTextView) {
mWaitingForResume = (BubbleTextView) v;
mWaitingForResume.setStayPressed(true);
@@ -3554,6 +3561,7 @@
public void bindAddScreens(ArrayList<Long> orderedScreenIds) {
int count = orderedScreenIds.size();
for (int i = 0; i < count; i++) {
+ Log.d(TAG, "10249126 - bindAddScreens(" + orderedScreenIds.get(i) + ")");
mWorkspace.insertNewWorkspaceScreenBeforeEmptyScreen(orderedScreenIds.get(i), false);
}
}
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index cec446f..9ec6c35 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -311,6 +311,7 @@
sBgWorkspaceScreens.size());
while (numPagesToAdd > 0) {
long screenId = lp.generateNewScreenId();
+ Log.d(TAG, "10249126 - addAndBindAddedApps(" + screenId + ")");
// Update the model
sBgWorkspaceScreens.add(screenId);
updateWorkspaceScreenOrder(context, sBgWorkspaceScreens);
@@ -1008,6 +1009,7 @@
long screenId = screens.get(i);
v.put(LauncherSettings.WorkspaceScreens._ID, screenId);
v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, i);
+ Log.d(TAG, "10249126 - updateWorkspaceScreenOrder(" + screenId + ", " + i + ")");
values[i] = v;
}
cr.bulkInsert(uri, values);
@@ -1432,6 +1434,7 @@
// Ensure that all the applications that are in the system are represented on the home
// screen.
+ Log.d(TAG, "10249126 - verifyApplications(" + isUpgrade + ")");
if (!isUpgrade) {
verifyApplications();
}
@@ -1495,6 +1498,7 @@
synchronized (sBgLock) {
for (ApplicationInfo app : mBgAllAppsList.data) {
tmpInfos = getItemInfoForComponentName(app.componentName);
+ Log.d(TAG, "10249126 - \t" + app.componentName.getPackageName() + ", " + tmpInfos.isEmpty());
if (tmpInfos.isEmpty()) {
// We are missing an application icon, so add this to the workspace
added.add(app);
@@ -1585,6 +1589,7 @@
sBgItemsIdMap.clear();
sBgDbIconCache.clear();
sBgWorkspaceScreens.clear();
+ Log.d(TAG, "10249126 - loadWorkspace()");
final ArrayList<Long> itemsToRemove = new ArrayList<Long>();
final Uri contentUri = LauncherSettings.Favorites.CONTENT_URI;
@@ -1647,16 +1652,18 @@
try {
intent = Intent.parseUri(intentDescription, 0);
ComponentName cn = intent.getComponent();
- if (!isValidPackage(manager, cn)) {
+ if (!isValidPackageComponent(manager, cn)) {
if (!mAppsCanBeOnRemoveableStorage) {
- // Log the invalid package, and remove it from the database
- Uri uri = LauncherSettings.Favorites.getContentUri(id, false);
+ // Log the invalid package, and remove it from the db
+ Uri uri = LauncherSettings.Favorites.getContentUri(id,
+ false);
contentResolver.delete(uri, null, null);
- Log.e(TAG, "Invalid package removed in loadWorkspace: " + cn);
+ Log.e(TAG, "Invalid package removed: " + cn);
} else {
- // If apps can be on external storage, then we just leave
- // them for the user to remove (maybe add treatment to it)
- Log.e(TAG, "Invalid package found in loadWorkspace: " + cn);
+ // If apps can be on external storage, then we just
+ // leave them for the user to remove (maybe add
+ // visual treatment to it)
+ Log.e(TAG, "Invalid package found: " + cn);
}
continue;
}
@@ -1715,6 +1722,8 @@
// now that we've loaded everthing re-save it with the
// icon in case it disappears somehow.
queueIconToBeChecked(sBgDbIconCache, info, c, iconIndex);
+ } else {
+ throw new RuntimeException("Unexpected null ShortcutInfo");
}
break;
@@ -1826,6 +1835,7 @@
long screenId = item.screenId;
if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP &&
!sBgWorkspaceScreens.contains(screenId)) {
+ Log.d(TAG, "10249126 - loadWorkspace-loadedOldDb(" + screenId + ")");
sBgWorkspaceScreens.add(screenId);
if (screenId > maxScreenId) {
maxScreenId = screenId;
@@ -1859,9 +1869,11 @@
long screenId = sc.getLong(idIndex);
int rank = sc.getInt(rankIndex);
+ Log.d(TAG, "10249126 - loadWorkspace-!loadedOldDb(" + screenId + ", " + rank + ")");
+
orderedScreens.put(rank, screenId);
} catch (Exception e) {
- Log.w(TAG, "Desktop items loading interrupted:", e);
+ Log.w(TAG, "Desktop items loading interrupted - invalid screens: ", e);
}
}
} finally {
@@ -2039,7 +2051,7 @@
private void bindWorkspaceScreens(final Callbacks oldCallbacks,
final ArrayList<Long> orderedScreens) {
-
+ Log.d(TAG, "10249126 - bindWorkspaceScreens()");
final Runnable r = new Runnable() {
@Override
public void run() {
@@ -2535,7 +2547,7 @@
return widgetsAndShortcuts;
}
- private boolean isValidPackage(PackageManager pm, ComponentName cn) {
+ private boolean isValidPackageComponent(PackageManager pm, ComponentName cn) {
if (cn == null) {
return false;
}
@@ -2563,9 +2575,18 @@
public ShortcutInfo getShortcutInfo(PackageManager manager, Intent intent, Context context,
Cursor c, int iconIndex, int titleIndex, HashMap<Object, CharSequence> labelCache) {
ComponentName componentName = intent.getComponent();
- if (!isValidPackage(manager, componentName)) {
+ final ShortcutInfo info = new ShortcutInfo();
+ if (!isValidPackageComponent(manager, componentName)) {
Log.d(TAG, "Invalid package found in getShortcutInfo: " + componentName);
return null;
+ } else {
+ try {
+ PackageInfo pi = manager.getPackageInfo(componentName.getPackageName(), 0);
+ info.initFlagsAndFirstInstallTime(pi);
+ } catch (NameNotFoundException e) {
+ Log.d(TAG, "getPackInfo failed for package " +
+ componentName.getPackageName());
+ }
}
// TODO: See if the PackageManager knows about this case. If it doesn't
@@ -2579,7 +2600,6 @@
// Attempt to use queryIntentActivities to get the ResolveInfo (with IntentFilter info) and
// if that fails, or is ambiguious, fallback to the standard way of getting the resolve info
// via resolveActivity().
- final ShortcutInfo info = new ShortcutInfo();
Bitmap icon = null;
ResolveInfo resolveInfo = null;
ComponentName oldComponent = intent.getComponent();
diff --git a/src/com/android/launcher3/Stats.java b/src/com/android/launcher3/Stats.java
new file mode 100644
index 0000000..ca088f7
--- /dev/null
+++ b/src/com/android/launcher3/Stats.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2012 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.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.util.Log;
+
+import java.io.*;
+import java.util.ArrayList;
+
+public class Stats {
+ private static final boolean DEBUG_BROADCASTS = false;
+ private static final String TAG = "Launcher3/Stats";
+
+ private static final boolean LOCAL_LAUNCH_LOG = true;
+
+ public static final String ACTION_LAUNCH = "com.android.launcher3.action.LAUNCH";
+ public static final String PERM_LAUNCH = "com.android.launcher3.permission.RECEIVE_LAUNCH_BROADCASTS";
+ public static final String EXTRA_INTENT = "intent";
+ public static final String EXTRA_CONTAINER = "container";
+ public static final String EXTRA_SCREEN = "screen";
+ public static final String EXTRA_CELLX = "cellX";
+ public static final String EXTRA_CELLY = "cellY";
+
+ private static final String LOG_FILE_NAME = "launches.log";
+ private static final int LOG_VERSION = 1;
+ private static final int LOG_TAG_VERSION = 0x1;
+ private static final int LOG_TAG_LAUNCH = 0x1000;
+
+ private static final String STATS_FILE_NAME = "stats.log";
+ private static final int STATS_VERSION = 1;
+ private static final int INITIAL_STATS_SIZE = 100;
+
+ // TODO: delayed/batched writes
+ private static final boolean FLUSH_IMMEDIATELY = true;
+
+ private final Launcher mLauncher;
+
+ DataOutputStream mLog;
+
+ ArrayList<String> mIntents;
+ ArrayList<Integer> mHistogram;
+
+ public Stats(Launcher launcher) {
+ mLauncher = launcher;
+
+ loadStats();
+
+ if (LOCAL_LAUNCH_LOG) {
+ try {
+ mLog = new DataOutputStream(mLauncher.openFileOutput(LOG_FILE_NAME, Context.MODE_APPEND));
+ mLog.writeInt(LOG_TAG_VERSION);
+ mLog.writeInt(LOG_VERSION);
+ } catch (FileNotFoundException e) {
+ Log.e(TAG, "unable to create stats log: " + e);
+ mLog = null;
+ } catch (IOException e) {
+ Log.e(TAG, "unable to write to stats log: " + e);
+ mLog = null;
+ }
+ }
+
+ if (DEBUG_BROADCASTS) {
+ launcher.registerReceiver(
+ new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ android.util.Log.v("Stats", "got broadcast: " + intent + " for launched intent: "
+ + intent.getStringExtra(EXTRA_INTENT));
+ }
+ },
+ new IntentFilter(ACTION_LAUNCH),
+ PERM_LAUNCH,
+ null
+ );
+ }
+ }
+
+ public void incrementLaunch(String intentStr) {
+ int pos = mIntents.indexOf(intentStr);
+ if (pos < 0) {
+ mIntents.add(intentStr);
+ mHistogram.add(1);
+ } else {
+ mHistogram.set(pos, mHistogram.get(pos) + 1);
+ }
+ }
+
+ public void recordLaunch(Intent intent, ShortcutInfo shortcut) {
+ intent = new Intent(intent);
+ intent.setSourceBounds(null);
+
+ final String flat = intent.toUri(0);
+
+ mLauncher.sendBroadcast(
+ new Intent(ACTION_LAUNCH)
+ .putExtra(EXTRA_INTENT, flat)
+ .putExtra(EXTRA_CONTAINER, shortcut.container)
+ .putExtra(EXTRA_SCREEN, shortcut.screenId)
+ .putExtra(EXTRA_CELLX, shortcut.cellX)
+ .putExtra(EXTRA_CELLY, shortcut.cellY),
+ PERM_LAUNCH);
+
+ incrementLaunch(flat);
+
+ if (FLUSH_IMMEDIATELY) {
+ saveStats();
+ }
+
+ if (LOCAL_LAUNCH_LOG && mLog != null) {
+ try {
+ mLog.writeInt(LOG_TAG_LAUNCH);
+ mLog.writeLong(System.currentTimeMillis());
+ mLog.writeShort((short) shortcut.container);
+ mLog.writeShort((short) shortcut.screenId);
+ mLog.writeShort((short) shortcut.cellX);
+ mLog.writeShort((short) shortcut.cellY);
+ mLog.writeUTF(flat);
+ if (FLUSH_IMMEDIATELY) {
+ mLog.flush(); // TODO: delayed writes
+ }
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private void saveStats() {
+ DataOutputStream stats = null;
+ try {
+ stats = new DataOutputStream(mLauncher.openFileOutput(STATS_FILE_NAME + ".tmp", Context.MODE_PRIVATE));
+ stats.writeInt(STATS_VERSION);
+ final int N = mHistogram.size();
+ stats.writeInt(N);
+ for (int i=0; i<N; i++) {
+ stats.writeUTF(mIntents.get(i));
+ stats.writeInt(mHistogram.get(i));
+ }
+ stats.close();
+ stats = null;
+ mLauncher.getFileStreamPath(STATS_FILE_NAME + ".tmp")
+ .renameTo(mLauncher.getFileStreamPath(STATS_FILE_NAME));
+ } catch (FileNotFoundException e) {
+ Log.e(TAG, "unable to create stats data: " + e);
+ } catch (IOException e) {
+ Log.e(TAG, "unable to write to stats data: " + e);
+ } finally {
+ if (stats != null) {
+ try {
+ stats.close();
+ } catch (IOException e) { }
+ }
+ }
+ }
+
+ private void loadStats() {
+ mIntents = new ArrayList<String>(INITIAL_STATS_SIZE);
+ mHistogram = new ArrayList<Integer>(INITIAL_STATS_SIZE);
+ DataInputStream stats = null;
+ try {
+ stats = new DataInputStream(mLauncher.openFileInput(STATS_FILE_NAME));
+ final int version = stats.readInt();
+ if (version == STATS_VERSION) {
+ final int N = stats.readInt();
+ for (int i=0; i<N; i++) {
+ final String pkg = stats.readUTF();
+ final int count = stats.readInt();
+ mIntents.add(pkg);
+ mHistogram.add(count);
+ }
+ }
+ } catch (FileNotFoundException e) {
+ // not a problem
+ } catch (IOException e) {
+ // more of a problem
+
+ } finally {
+ if (stats != null) {
+ try {
+ stats.close();
+ } catch (IOException e) { }
+ }
+ }
+ }
+}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 4e95f09..97625f5 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -520,6 +520,7 @@
}
public long insertNewWorkspaceScreen(long screenId, int insertIndex, boolean updateDb) {
+ Log.d(TAG, "10249126 - insertNewWorkspaceScreen(" + screenId + ", " + insertIndex + ", " + updateDb + ")");
CellLayout newScreen = (CellLayout)
mLauncher.getLayoutInflater().inflate(R.layout.workspace_screen, null);
@@ -536,6 +537,7 @@
}
public void createCustomContentPage() {
+ Log.d(TAG, "10249126 - createCustomContentPage()");
CellLayout customScreen = (CellLayout)
mLauncher.getLayoutInflater().inflate(R.layout.workspace_screen, null);
@@ -570,6 +572,7 @@
}
public long commitExtraEmptyScreen() {
+ Log.d(TAG, "10249126 - commitExtraEmptyScreen()");
CellLayout cl = mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID);
mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID);
mScreenOrder.remove(EXTRA_EMPTY_SCREEN_ID);
@@ -591,11 +594,13 @@
}
public CellLayout getScreenWithId(long screenId) {
+ Log.d(TAG, "10249126 - getScreenWithId(" + screenId + ")");
CellLayout layout = mWorkspaceScreens.get(screenId);
return layout;
}
public long getIdForScreen(CellLayout layout) {
+ Log.d(TAG, "10249126 - getIdForScreen()");
Iterator<Long> iter = mWorkspaceScreens.keySet().iterator();
while (iter.hasNext()) {
long id = iter.next();
@@ -607,6 +612,7 @@
}
public int getPageIndexForScreenId(long screenId) {
+ Log.d(TAG, "10249126 - getPageIndexForScreenId(" + screenId + ")");
return indexOfChild(mWorkspaceScreens.get(screenId));
}
@@ -624,6 +630,8 @@
return;
}
+ Log.d(TAG, "10249126 - stripEmptyScreens()");
+
int currentPage = getNextPage();
ArrayList<Long> removeScreens = new ArrayList<Long>();
for (Long id: mWorkspaceScreens.keySet()) {
@@ -635,6 +643,7 @@
int pageShift = 0;
for (Long id: removeScreens) {
+ Log.d(TAG, "10249126 - \tremove(" + id + ")");
CellLayout cl = mWorkspaceScreens.get(id);
mWorkspaceScreens.remove(id);
mScreenOrder.remove(id);
@@ -693,6 +702,8 @@
if (container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
if (getScreenWithId(screenId) == null) {
Log.e(TAG, "Skipping child, screenId " + screenId + " not found");
+ // DEBUGGING - Print out the stack trace to see where we are adding from
+ new Throwable().printStackTrace();
return;
}
}
@@ -1413,6 +1424,7 @@
}
protected void onDetachedFromWindow() {
+ super.onDetachedFromWindow();
mWindowToken = null;
}