summaryrefslogtreecommitdiffstats
path: root/src/com/android/launcher3/Launcher.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/launcher3/Launcher.java')
-rw-r--r--src/com/android/launcher3/Launcher.java1804
1 files changed, 1242 insertions, 562 deletions
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index c22a6bf3f..42ec4fb48 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -22,11 +22,13 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
+import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.annotation.TargetApi;
import android.app.Activity;
import android.app.ActivityManager;
import android.app.ActivityOptions;
+import android.app.AlertDialog;
import android.app.SearchManager;
import android.appwidget.AppWidgetHostView;
import android.appwidget.AppWidgetManager;
@@ -37,6 +39,7 @@ import android.content.ComponentCallbacks2;
import android.content.ComponentName;
import android.content.ContentResolver;
import android.content.Context;
+import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
@@ -44,25 +47,25 @@ 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.Configuration;
import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.Bitmap;
import android.graphics.Canvas;
+import android.graphics.Color;
import android.graphics.Point;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
+import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.os.StrictMode;
import android.os.SystemClock;
-import android.provider.Settings;
import android.speech.RecognizerIntent;
import android.text.Selection;
import android.text.SpannableStringBuilder;
@@ -70,6 +73,7 @@ import android.text.TextUtils;
import android.text.method.TextKeyListener;
import android.util.DisplayMetrics;
import android.util.Log;
+import android.view.ContextThemeWrapper;
import android.view.Display;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
@@ -81,13 +85,16 @@ import android.view.Surface;
import android.view.View;
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;
+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.Interpolator;
import android.view.inputmethod.InputMethodManager;
import android.widget.Advanceable;
import android.widget.FrameLayout;
@@ -96,6 +103,14 @@ import android.widget.TextView;
import android.widget.Toast;
import com.android.launcher3.DropTarget.DragObject;
+import com.android.launcher3.PagedView.PageSwitchListener;
+import com.android.launcher3.compat.AppWidgetManagerCompat;
+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 java.io.DataInputStream;
import java.io.DataOutputStream;
@@ -105,6 +120,9 @@ import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
import java.text.DateFormat;
import java.util.ArrayList;
import java.util.Collection;
@@ -113,13 +131,12 @@ import java.util.HashMap;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
-
/**
* Default launcher application.
*/
public class Launcher extends Activity
implements View.OnClickListener, OnLongClickListener, LauncherModel.Callbacks,
- View.OnTouchListener {
+ View.OnTouchListener, PageSwitchListener, LauncherProviderChangeListener {
static final String TAG = "Launcher";
static final boolean LOGD = false;
@@ -133,12 +150,12 @@ public class Launcher extends Activity
private static final int REQUEST_CREATE_SHORTCUT = 1;
private static final int REQUEST_CREATE_APPWIDGET = 5;
- private static final int REQUEST_PICK_APPLICATION = 6;
private static final int REQUEST_PICK_SHORTCUT = 7;
private static final int REQUEST_PICK_APPWIDGET = 9;
private static final int REQUEST_PICK_WALLPAPER = 10;
private static final int REQUEST_BIND_APPWIDGET = 11;
+ private static final int REQUEST_RECONFIGURE_APPWIDGET = 12;
/**
* IntentStarter uses request codes starting with this. This must be greater than all activity
@@ -189,9 +206,13 @@ public class Launcher extends Activity
// Type: int[]
private static final String RUNTIME_STATE_VIEW_IDS = "launcher.view_ids";
-
+ static final String INTRO_SCREEN_DISMISSED = "launcher.intro_screen_dismissed";
static final String FIRST_RUN_ACTIVITY_DISPLAYED = "launcher.first_run_activity_displayed";
+ static final String FIRST_LOAD_COMPLETE = "launcher.first_load_complete";
+ static final String ACTION_FIRST_LOAD_COMPLETE =
+ "com.android.launcher3.action.FIRST_LOAD_COMPLETE";
+
private static final String TOOLBAR_ICON_METADATA_NAME = "com.android.launcher.toolbar_icon";
private static final String TOOLBAR_SEARCH_ICON_METADATA_NAME =
"com.android.launcher.toolbar_search_icon";
@@ -208,10 +229,12 @@ public class Launcher extends Activity
private State mState = State.WORKSPACE;
private AnimatorSet mStateAnimation;
+ private boolean mIsSafeModeEnabled;
+
static final int APPWIDGET_HOST_ID = 1024;
public static final int EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT = 300;
- public static final int EXIT_SPRINGLOADED_MODE_SHORT_TIMEOUT_FOLDER_CLOSE = 400;
private static final int ON_ACTIVITY_RESULT_ANIMATION_DELAY = 500;
+ private static final int ACTIVITY_START_DELAY = 1000;
private static final Object sLock = new Object();
private static int sScreen = DEFAULT_SCREEN;
@@ -223,6 +246,7 @@ public class Launcher extends Activity
private static int NEW_APPS_PAGE_MOVE_DELAY = 500;
private static int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5;
private static int NEW_APPS_ANIMATION_DELAY = 500;
+ private static final int SINGLE_FRAME_DELAY = 16;
private final BroadcastReceiver mCloseSystemDialogsReceiver
= new CloseSystemDialogsIntentReceiver();
@@ -236,9 +260,8 @@ public class Launcher extends Activity
private DragLayer mDragLayer;
private DragController mDragController;
private View mWeightWatcher;
- private LauncherClings mLauncherClings;
- private AppWidgetManager mAppWidgetManager;
+ private AppWidgetManagerCompat mAppWidgetManager;
private LauncherAppWidgetHost mAppWidgetHost;
private ItemInfo mPendingAddInfo = new ItemInfo();
@@ -278,9 +301,6 @@ public class Launcher extends Activity
private ArrayList<Runnable> mBindOnResumeCallbacks = new ArrayList<Runnable>();
private ArrayList<Runnable> mOnResumeCallbacks = new ArrayList<Runnable>();
- // Keep track of whether the user has left launcher
- private static boolean sPausedFromUserAction = false;
-
private Bundle mSavedInstanceState;
private LauncherModel mModel;
@@ -312,10 +332,6 @@ public class Launcher extends Activity
// External icons saved in case of resource changes, orientation, etc.
private static Drawable.ConstantState[] sGlobalSearchIcon = new Drawable.ConstantState[2];
private static Drawable.ConstantState[] sVoiceSearchIcon = new Drawable.ConstantState[2];
- private static Drawable.ConstantState[] sAppMarketIcon = new Drawable.ConstantState[2];
-
- private Intent mAppMarketIntent = null;
- private static final boolean DISABLE_MARKET_BUTTON = true;
private Drawable mWorkspaceBackgroundDrawable;
@@ -352,8 +368,7 @@ public class Launcher extends Activity
}
};
- private static ArrayList<PendingAddArguments> sPendingAddList
- = new ArrayList<PendingAddArguments>();
+ private static PendingAddArguments sPendingAddItem;
public static boolean sForceEnableRotation = isPropertyEnabled(FORCE_ENABLE_ROTATION_PROPERTY);
@@ -364,10 +379,13 @@ public class Launcher extends Activity
long screenId;
int cellX;
int cellY;
+ int appWidgetId;
}
private Stats mStats;
+ FocusIndicatorView mFocusHandler;
+
static boolean isPropertyEnabled(String propertyName) {
return Log.isLoggable(propertyName, Log.VERBOSE);
}
@@ -393,7 +411,7 @@ public class Launcher extends Activity
LauncherAppState.setApplicationContext(getApplicationContext());
LauncherAppState app = LauncherAppState.getInstance();
-
+ LauncherAppState.getLauncherProvider().setLauncherProviderChangeListener(this);
// Determine the dynamic grid properties
Point smallestSize = new Point();
Point largestSize = new Point();
@@ -414,16 +432,16 @@ public class Launcher extends Activity
// the LauncherApplication should call this, but in case of Instrumentation it might not be present yet
mSharedPrefs = getSharedPreferences(LauncherAppState.getSharedPreferencesKey(),
Context.MODE_PRIVATE);
+ mIsSafeModeEnabled = getPackageManager().isSafeMode();
mModel = app.setLauncher(this);
mIconCache = app.getIconCache();
mIconCache.flushInvalidIcons(grid);
mDragController = new DragController(this);
- mLauncherClings = new LauncherClings(this);
mInflater = getLayoutInflater();
mStats = new Stats(this);
- mAppWidgetManager = AppWidgetManager.getInstance(this);
+ mAppWidgetManager = AppWidgetManagerCompat.getInstance(this);
mAppWidgetHost = new LauncherAppWidgetHost(this, APPWIDGET_HOST_ID);
mAppWidgetHost.startListening();
@@ -438,7 +456,6 @@ public class Launcher extends Activity
Environment.getExternalStorageDirectory() + "/launcher");
}
-
checkForLocaleChange();
setContentView(R.layout.launcher);
@@ -457,7 +474,7 @@ public class Launcher extends Activity
}
if (!mRestoring) {
- if (DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE || sPausedFromUserAction) {
+ if (DISABLE_SYNCHRONOUS_BINDING_CURRENT_PAGE) {
// If the user leaves launcher, then we should just load items asynchronously when
// they return.
mModel.startLoader(true, PagedView.INVALID_RESTORE_PAGE);
@@ -480,25 +497,16 @@ public class Launcher extends Activity
// On large interfaces, we want the screen to auto-rotate based on the current orientation
unlockScreenOrientation(true);
- // The two first run cling paths are mutually exclusive, if the launcher is preinstalled
- // on the device, then we always show the first run cling experience (or if there is no
- // launcher2). Otherwise, we prompt the user upon started for migration
- showFirstRunActivity();
- if (mLauncherClings.shouldShowFirstRunOrMigrationClings()) {
- if (mModel.canMigrateFromOldLauncherDb(this)) {
- mLauncherClings.showMigrationCling();
- } else {
- mLauncherClings.showFirstRunCling();
- }
+ if (shouldShowIntroScreen()) {
+ showIntroScreen();
} else {
- mLauncherClings.removeFirstRunAndMigrationClings();
+ showFirstRunActivity();
+ showFirstRunClings();
}
}
- protected void onUserLeaveHint() {
- super.onUserLeaveHint();
- sPausedFromUserAction = true;
- }
+ @Override
+ public void onLauncherProviderChange() { }
/** To be overriden by subclasses to hint to Launcher that we have custom content */
protected boolean hasCustomContentToLeft() {
@@ -514,21 +522,6 @@ public class Launcher extends Activity
}
/**
- * To be overridden by subclasses to indicate that there is an activity to launch
- * before showing the standard launcher experience.
- */
- protected boolean hasFirstRunActivity() {
- return false;
- }
-
- /**
- * To be overridden by subclasses to launch any first run activity
- */
- protected Intent getFirstRunActivity() {
- return null;
- }
-
- /**
* Invoked by subclasses to signal a change to the {@link #addCustomContentToLeft} value to
* ensure the custom content page is added or removed if necessary.
*/
@@ -552,11 +545,7 @@ public class Launcher extends Activity
boolean voiceVisible = false;
// If we have a saved version of these external icons, we load them up immediately
int coi = getCurrentOrientationIndexForGlobalIcons();
- if (sGlobalSearchIcon[coi] == null || sVoiceSearchIcon[coi] == null ||
- sAppMarketIcon[coi] == null) {
- if (!DISABLE_MARKET_BUTTON) {
- updateAppMarketIcon();
- }
+ if (sGlobalSearchIcon[coi] == null || sVoiceSearchIcon[coi] == null) {
searchVisible = updateGlobalSearchIcon();
voiceVisible = updateVoiceSearchIcon(searchVisible);
}
@@ -568,9 +557,6 @@ public class Launcher extends Activity
updateVoiceSearchIcon(sVoiceSearchIcon[coi]);
voiceVisible = true;
}
- if (!DISABLE_MARKET_BUTTON && sAppMarketIcon[coi] != null) {
- updateAppMarketIcon(sAppMarketIcon[coi]);
- }
if (mSearchDropTargetBar != null) {
mSearchDropTargetBar.onSearchPackagesChanged(searchVisible, voiceVisible);
}
@@ -703,19 +689,20 @@ public class Launcher extends Activity
}
}
- /**
- * Copied from View -- the View version of the method isn't called
- * anywhere else in our process and only exists for API level 17+,
- * so it's ok to keep our own version with no API requirement.
- */
public static int generateViewId() {
- for (;;) {
- final int result = sNextGeneratedId.get();
- // aapt-generated IDs have the high byte nonzero; clamp to the range under that.
- int newValue = result + 1;
- if (newValue > 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0.
- if (sNextGeneratedId.compareAndSet(result, newValue)) {
- return result;
+ if (Build.VERSION.SDK_INT >= 17) {
+ return View.generateViewId();
+ } else {
+ // View.generateViewId() is not available. The following fallback logic is a copy
+ // of its implementation.
+ for (;;) {
+ final int result = sNextGeneratedId.get();
+ // aapt-generated IDs have the high byte nonzero; clamp to the range under that.
+ int newValue = result + 1;
+ if (newValue > 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0.
+ if (sNextGeneratedId.compareAndSet(result, newValue)) {
+ return result;
+ }
}
}
}
@@ -735,40 +722,39 @@ public class Launcher extends Activity
* Returns whether we should delay spring loaded mode -- for shortcuts and widgets that have
* a configuration step, this allows the proper animations to run after other transitions.
*/
- private boolean completeAdd(PendingAddArguments args) {
- boolean result = false;
+ private long completeAdd(PendingAddArguments args) {
+ long screenId = args.screenId;
+ if (args.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+ // When the screen id represents an actual screen (as opposed to a rank) we make sure
+ // that the drop page actually exists.
+ screenId = ensurePendingDropLayoutExists(args.screenId);
+ }
+
switch (args.requestCode) {
- case REQUEST_PICK_APPLICATION:
- completeAddApplication(args.intent, args.container, args.screenId, args.cellX,
- args.cellY);
- break;
- case REQUEST_PICK_SHORTCUT:
- processShortcut(args.intent);
- break;
case REQUEST_CREATE_SHORTCUT:
- completeAddShortcut(args.intent, args.container, args.screenId, args.cellX,
+ completeAddShortcut(args.intent, args.container, screenId, args.cellX,
args.cellY);
- result = true;
break;
case REQUEST_CREATE_APPWIDGET:
- int appWidgetId = args.intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1);
- completeAddAppWidget(appWidgetId, args.container, args.screenId, null, null);
- result = true;
+ completeAddAppWidget(args.appWidgetId, args.container, screenId, null, null);
+ break;
+ case REQUEST_RECONFIGURE_APPWIDGET:
+ completeRestoreAppWidget(args.appWidgetId);
break;
}
// Before adding this resetAddInfo(), after a shortcut was added to a workspace screen,
// if you turned the screen off and then back while in All Apps, Launcher would not
// return to the workspace. Clearing mAddInfo.container here fixes this issue
resetAddInfo();
- return result;
+ return screenId;
}
@Override
protected void onActivityResult(
final int requestCode, final int resultCode, final Intent data) {
// Reset the startActivity waiting flag
- mWaitingForResult = false;
- int pendingAddWidgetId = mPendingAddWidgetId;
+ setWaitingForResult(false);
+ final int pendingAddWidgetId = mPendingAddWidgetId;
mPendingAddWidgetId = -1;
Runnable exitSpringLoaded = new Runnable() {
@@ -784,7 +770,7 @@ public class Launcher extends Activity
data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) : -1;
if (resultCode == RESULT_CANCELED) {
completeTwoStageWidgetDrop(RESULT_CANCELED, appWidgetId);
- mWorkspace.removeExtraEmptyScreen(true, exitSpringLoaded,
+ mWorkspace.removeExtraEmptyScreenDelayed(true, exitSpringLoaded,
ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
} else if (resultCode == RESULT_OK) {
addAppWidgetImpl(appWidgetId, mPendingAddInfo, null,
@@ -801,6 +787,7 @@ public class Launcher extends Activity
boolean isWidgetDrop = (requestCode == REQUEST_PICK_APPWIDGET ||
requestCode == REQUEST_CREATE_APPWIDGET);
+ final boolean workspaceLocked = isWorkspaceLocked();
// We have special handling for widgets
if (isWidgetDrop) {
final int appWidgetId;
@@ -813,33 +800,66 @@ public class Launcher extends Activity
}
final int result;
- final Runnable onComplete;
if (appWidgetId < 0 || resultCode == RESULT_CANCELED) {
- Log.e(TAG, "Error: appWidgetId (EXTRA_APPWIDGET_ID) was not returned from the \\" +
- "widget configuration activity.");
+ Log.e(TAG, "Error: appWidgetId (EXTRA_APPWIDGET_ID) was not " +
+ "returned from the widget configuration activity.");
result = RESULT_CANCELED;
completeTwoStageWidgetDrop(result, appWidgetId);
- onComplete = new Runnable() {
+ final Runnable onComplete = new Runnable() {
@Override
public void run() {
exitSpringLoadedDragModeDelayed(false, 0, null);
}
};
+ if (workspaceLocked) {
+ // No need to remove the empty screen if we're mid-binding, as the
+ // the bind will not add the empty screen.
+ mWorkspace.postDelayed(onComplete, ON_ACTIVITY_RESULT_ANIMATION_DELAY);
+ } else {
+ mWorkspace.removeExtraEmptyScreenDelayed(true, onComplete,
+ ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
+ }
} else {
- result = resultCode;
- final CellLayout dropLayout =
- (CellLayout) mWorkspace.getScreenWithId(mPendingAddInfo.screenId);
- dropLayout.setDropPending(true);
- onComplete = new Runnable() {
- @Override
- public void run() {
- completeTwoStageWidgetDrop(result, appWidgetId);
- dropLayout.setDropPending(false);
+ if (!workspaceLocked) {
+ if (mPendingAddInfo.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
+ // When the screen id represents an actual screen (as opposed to a rank)
+ // we make sure that the drop page actually exists.
+ mPendingAddInfo.screenId =
+ ensurePendingDropLayoutExists(mPendingAddInfo.screenId);
}
- };
+ final CellLayout dropLayout = mWorkspace.getScreenWithId(mPendingAddInfo.screenId);
+
+ dropLayout.setDropPending(true);
+ final Runnable onComplete = new Runnable() {
+ @Override
+ public void run() {
+ completeTwoStageWidgetDrop(resultCode, appWidgetId);
+ dropLayout.setDropPending(false);
+ }
+ };
+ mWorkspace.removeExtraEmptyScreenDelayed(true, onComplete,
+ ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
+ } else {
+ PendingAddArguments args = preparePendingAddArgs(requestCode, data, appWidgetId,
+ mPendingAddInfo);
+ sPendingAddItem = args;
+ }
+ }
+ return;
+ }
+
+ if (requestCode == REQUEST_RECONFIGURE_APPWIDGET) {
+ if (resultCode == RESULT_OK) {
+ // Update the widget view.
+ PendingAddArguments args = preparePendingAddArgs(requestCode, data,
+ pendingAddWidgetId, mPendingAddInfo);
+ if (workspaceLocked) {
+ sPendingAddItem = args;
+ } else {
+ completeAdd(args);
+ }
}
- mWorkspace.removeExtraEmptyScreen(true, onComplete, ON_ACTIVITY_RESULT_ANIMATION_DELAY,
- false);
+ // Leave the widget in the pending state if the user canceled the configure.
return;
}
@@ -849,27 +869,54 @@ public class Launcher extends Activity
// For example, the user would PICK_SHORTCUT for "Music playlist", and we
// launch over to the Music app to actually CREATE_SHORTCUT.
if (resultCode == RESULT_OK && mPendingAddInfo.container != ItemInfo.NO_ID) {
- final PendingAddArguments args = new PendingAddArguments();
- args.requestCode = requestCode;
- args.intent = data;
- args.container = mPendingAddInfo.container;
- args.screenId = mPendingAddInfo.screenId;
- args.cellX = mPendingAddInfo.cellX;
- args.cellY = mPendingAddInfo.cellY;
+ final PendingAddArguments args = preparePendingAddArgs(requestCode, data, -1,
+ mPendingAddInfo);
if (isWorkspaceLocked()) {
- sPendingAddList.add(args);
+ sPendingAddItem = args;
} else {
completeAdd(args);
+ mWorkspace.removeExtraEmptyScreenDelayed(true, exitSpringLoaded,
+ ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
}
- mWorkspace.removeExtraEmptyScreen(true, exitSpringLoaded,
- ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
} else if (resultCode == RESULT_CANCELED) {
- mWorkspace.removeExtraEmptyScreen(true, exitSpringLoaded,
+ mWorkspace.removeExtraEmptyScreenDelayed(true, exitSpringLoaded,
ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
}
mDragLayer.clearAnimatedView();
}
+ private PendingAddArguments preparePendingAddArgs(int requestCode, Intent data, int
+ appWidgetId, ItemInfo info) {
+ PendingAddArguments args = new PendingAddArguments();
+ args.requestCode = requestCode;
+ args.intent = data;
+ args.container = info.container;
+ args.screenId = info.screenId;
+ args.cellX = info.cellX;
+ args.cellY = info.cellY;
+ args.appWidgetId = appWidgetId;
+ return args;
+ }
+
+ /**
+ * Check to see if a given screen id exists. If not, create it at the end, return the new id.
+ *
+ * @param screenId the screen id to check
+ * @return the new screen, or screenId if it exists
+ */
+ private long ensurePendingDropLayoutExists(long screenId) {
+ CellLayout dropLayout =
+ (CellLayout) mWorkspace.getScreenWithId(screenId);
+ if (dropLayout == null) {
+ // it's possible that the add screen was removed because it was
+ // empty and a re-bind occurred
+ mWorkspace.addExtraEmptyScreen();
+ return mWorkspace.commitExtraEmptyScreen();
+ } else {
+ return screenId;
+ }
+ }
+
private void completeTwoStageWidgetDrop(final int resultCode, final int appWidgetId) {
CellLayout cellLayout =
(CellLayout) mWorkspace.getScreenWithId(mPendingAddInfo.screenId);
@@ -938,9 +985,8 @@ public class Launcher extends Activity
setWorkspaceBackground(mState == State.WORKSPACE);
mPaused = false;
- sPausedFromUserAction = false;
if (mRestoring || mOnResumeNeedsLoad) {
- mWorkspaceLoading = true;
+ setWorkspaceLoading(true);
mModel.startLoader(true, PagedView.INVALID_RESTORE_PAGE);
mRestoring = false;
mOnResumeNeedsLoad = false;
@@ -981,10 +1027,6 @@ public class Launcher extends Activity
// Resets the previous workspace icon press state
mWaitingForResume.setStayPressed(false);
}
- if (mAppsCustomizeContent != null) {
- // Resets the previous all apps icon press state
- mAppsCustomizeContent.resetDrawableState();
- }
// It is possible that widgets can receive updates while launcher is not in the foreground.
// Consequently, the widgets will be inflated in the orientation of the foreground activity
@@ -1010,17 +1052,20 @@ public class Launcher extends Activity
// It is also poassible that onShow will instead be called slightly after first layout
// if PagedView#setRestorePage was set to the custom content page in onCreate().
if (mWorkspace.isOnOrMovingToCustomContent()) {
- mWorkspace.getCustomContentCallbacks().onShow();
+ mWorkspace.getCustomContentCallbacks().onShow(true);
}
}
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;
@@ -1054,23 +1099,24 @@ public class Launcher extends Activity
}
public interface CustomContentCallbacks {
- // Custom content is completely shown
- public void onShow();
+ // Custom content is completely shown. {@code fromResume} indicates whether this was caused
+ // by a onResume or by scrolling otherwise.
+ public void onShow(boolean fromResume);
// Custom content is completely hidden
public void onHide();
// Custom content scroll progress changed. From 0 (not showing) to 1 (fully showing).
public void onScrollProgressChanged(float progress);
+
+ // Indicates whether the user is allowed to scroll away from the custom content.
+ boolean isScrollingAllowed();
}
protected boolean hasSettings() {
return false;
}
- protected void startSettings() {
- }
-
public interface QSBScroller {
public void setScrollY(int scrollY);
}
@@ -1089,7 +1135,9 @@ public class Launcher extends Activity
@Override
public Object onRetainNonConfigurationInstance() {
// Flag the loader to stop early before switching
- mModel.stopLoader();
+ if (mModel.isCurrentCallbacks(this)) {
+ mModel.stopLoader();
+ }
if (mAppsCustomizeContent != null) {
mAppsCustomizeContent.surrender();
}
@@ -1196,7 +1244,7 @@ public class Launcher extends Activity
mPendingAddInfo.spanY = savedState.getInt(RUNTIME_STATE_PENDING_ADD_SPAN_Y);
mPendingAddWidgetInfo = savedState.getParcelable(RUNTIME_STATE_PENDING_ADD_WIDGET_INFO);
mPendingAddWidgetId = savedState.getInt(RUNTIME_STATE_PENDING_ADD_WIDGET_ID);
- mWaitingForResult = true;
+ setWaitingForResult(true);
mRestoring = true;
}
@@ -1231,8 +1279,10 @@ public class Launcher extends Activity
final DragController dragController = mDragController;
mLauncherView = findViewById(R.id.launcher);
+ mFocusHandler = (FocusIndicatorView) findViewById(R.id.focus_indicator);
mDragLayer = (DragLayer) findViewById(R.id.drag_layer);
mWorkspace = (Workspace) mDragLayer.findViewById(R.id.workspace);
+ mWorkspace.setPageSwitchListener(this);
mPageIndicators = mDragLayer.findViewById(R.id.page_indicator);
mLauncherView.setSystemUiVisibility(
@@ -1255,7 +1305,7 @@ public class Launcher extends Activity
@Override
public void onClick(View arg0) {
if (!mWorkspace.isSwitchingState()) {
- showAllApps(true, AppsCustomizePagedView.ContentType.Widgets, true);
+ onClickAddWidgetButton(arg0);
}
}
});
@@ -1266,7 +1316,7 @@ public class Launcher extends Activity
@Override
public void onClick(View arg0) {
if (!mWorkspace.isSwitchingState()) {
- startWallpaper();
+ onClickWallpaperPicker(arg0);
}
}
});
@@ -1278,9 +1328,9 @@ public class Launcher extends Activity
@Override
public void onClick(View arg0) {
if (!mWorkspace.isSwitchingState()) {
- startSettings();
- }
+ onClickSettingsButton(arg0);
}
+ }
});
settingsButton.setOnTouchListener(getHapticFeedbackTouchListener());
} else {
@@ -1334,6 +1384,17 @@ public class Launcher extends Activity
}
/**
+ * Sets the all apps button. This method is called from {@link Hotseat}.
+ */
+ public void setAllAppsButton(View allAppsButton) {
+ mAllAppsButton = allAppsButton;
+ }
+
+ public View getAllAppsButton() {
+ return mAllAppsButton;
+ }
+
+ /**
* Creates a view representing a shortcut.
*
* @param info The data structure describing the shortcut.
@@ -1356,44 +1417,13 @@ public class Launcher extends Activity
*/
View createShortcut(int layoutResId, ViewGroup parent, ShortcutInfo info) {
BubbleTextView favorite = (BubbleTextView) mInflater.inflate(layoutResId, parent, false);
- favorite.applyFromShortcutInfo(info, mIconCache);
+ favorite.applyFromShortcutInfo(info, mIconCache, true);
favorite.setOnClickListener(this);
+ favorite.setOnFocusChangeListener(mFocusHandler);
return favorite;
}
/**
- * Add an application shortcut to the workspace.
- *
- * @param data The intent describing the application.
- * @param cellInfo The position on screen where to create the shortcut.
- */
- void completeAddApplication(Intent data, long container, long screenId, int cellX, int cellY) {
- final int[] cellXY = mTmpAddItemCellCoordinates;
- final CellLayout layout = getCellLayout(container, screenId);
-
- // First we check if we already know the exact location where we want to add this item.
- if (cellX >= 0 && cellY >= 0) {
- cellXY[0] = cellX;
- cellXY[1] = cellY;
- } else if (!layout.findCellForSpan(cellXY, 1, 1)) {
- showOutOfSpaceMessage(isHotseatLayout(layout));
- return;
- }
-
- final ShortcutInfo info = mModel.getShortcutInfo(getPackageManager(), data, this);
-
- if (info != null) {
- info.setActivity(this, data.getComponent(), Intent.FLAG_ACTIVITY_NEW_TASK |
- Intent.FLAG_ACTIVITY_RESET_TASK_IF_NEEDED);
- info.container = ItemInfo.NO_ID;
- mWorkspace.addApplicationShortcut(info, layout, container, screenId, cellXY[0], cellXY[1],
- isWorkspaceLocked(), cellX, cellY);
- } else {
- Log.e(TAG, "Couldn't find ActivityInfo for selected application: " + data);
- }
- }
-
- /**
* Add a shortcut to the workspace.
*
* @param data The intent describing the shortcut.
@@ -1543,6 +1573,7 @@ public class Launcher extends Activity
launcherInfo.spanY = spanXY[1];
launcherInfo.minSpanX = mPendingAddInfo.minSpanX;
launcherInfo.minSpanY = mPendingAddInfo.minSpanY;
+ launcherInfo.user = mAppWidgetManager.getUser(appWidgetInfo);
LauncherModel.addItemToDatabase(this, launcherInfo,
container, screenId, cellXY[0], cellXY[1], false);
@@ -1595,6 +1626,9 @@ public class Launcher extends Activity
mModel.startLoader(false, PagedView.INVALID_RESTORE_PAGE,
LauncherModel.LOADER_FLAG_CLEAR_WORKSPACE
| LauncherModel.LOADER_FLAG_MIGRATE_SHORTCUTS);
+ } else if (LauncherAppsCompat.ACTION_MANAGED_PROFILE_ADDED.equals(action)
+ || LauncherAppsCompat.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) {
+ getModel().forceReload();
}
}
};
@@ -1607,16 +1641,61 @@ public class Launcher extends Activity
final IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_SCREEN_OFF);
filter.addAction(Intent.ACTION_USER_PRESENT);
+ // For handling managed profiles
+ filter.addAction(LauncherAppsCompat.ACTION_MANAGED_PROFILE_ADDED);
+ filter.addAction(LauncherAppsCompat.ACTION_MANAGED_PROFILE_REMOVED);
if (ENABLE_DEBUG_INTENTS) {
filter.addAction(DebugIntents.DELETE_DATABASE);
filter.addAction(DebugIntents.MIGRATE_DATABASE);
}
registerReceiver(mReceiver, filter);
FirstFrameAnimatorHelper.initializeDrawListener(getWindow().getDecorView());
+ setupTransparentSystemBarsForLmp();
mAttached = true;
mVisible = true;
}
+ /**
+ * Sets up transparent navigation and status bars in LMP.
+ * This method is a no-op for other platform versions.
+ */
+ @TargetApi(19)
+ private void setupTransparentSystemBarsForLmp() {
+ // TODO(sansid): use the APIs directly when compiling against L sdk.
+ // Currently we use reflection to access the flags and the API to set the transparency
+ // on the System bars.
+ if (Utilities.isLmpOrAbove()) {
+ try {
+ getWindow().getAttributes().systemUiVisibility |=
+ (View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
+ getWindow().clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS
+ | WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION);
+ Field drawsSysBackgroundsField = WindowManager.LayoutParams.class.getField(
+ "FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS");
+ getWindow().addFlags(drawsSysBackgroundsField.getInt(null));
+
+ Method setStatusBarColorMethod =
+ Window.class.getDeclaredMethod("setStatusBarColor", int.class);
+ Method setNavigationBarColorMethod =
+ Window.class.getDeclaredMethod("setNavigationBarColor", int.class);
+ setStatusBarColorMethod.invoke(getWindow(), Color.TRANSPARENT);
+ setNavigationBarColorMethod.invoke(getWindow(), Color.TRANSPARENT);
+ } catch (NoSuchFieldException e) {
+ Log.w(TAG, "NoSuchFieldException while setting up transparent bars");
+ } catch (NoSuchMethodException ex) {
+ Log.w(TAG, "NoSuchMethodException while setting up transparent bars");
+ } catch (IllegalAccessException e) {
+ Log.w(TAG, "IllegalAccessException while setting up transparent bars");
+ } catch (IllegalArgumentException e) {
+ Log.w(TAG, "IllegalArgumentException while setting up transparent bars");
+ } catch (InvocationTargetException e) {
+ Log.w(TAG, "InvocationTargetException while setting up transparent bars");
+ } finally {}
+ }
+ }
+
@Override
public void onDetachedFromWindow() {
super.onDetachedFromWindow();
@@ -1667,11 +1746,6 @@ public class Launcher extends Activity
}
});
}
- // When Launcher comes back to foreground, a different Activity might be responsible for
- // the app market intent, so refresh the icon
- if (!DISABLE_MARKET_BUTTON) {
- updateAppMarketIcon();
- }
clearTypedText();
}
}
@@ -1778,10 +1852,6 @@ public class Launcher extends Activity
return mModel;
}
- public LauncherClings getLauncherClings() {
- return mLauncherClings;
- }
-
protected SharedPreferences getSharedPrefs() {
return mSharedPrefs;
}
@@ -1790,7 +1860,7 @@ public class Launcher extends Activity
getWindow().closeAllPanels();
// Whatever we were doing is hereby canceled.
- mWaitingForResult = false;
+ setWaitingForResult(false);
}
@Override
@@ -1930,8 +2000,13 @@ public class Launcher extends Activity
// Stop callbacks from LauncherModel
LauncherAppState app = (LauncherAppState.getInstance());
- mModel.stopLoader();
- app.setLauncher(null);
+
+ // It's possible to receive onDestroy after a new Launcher activity has
+ // been created. In this case, don't interfere with the new Launcher.
+ if (mModel.isCurrentCallbacks(this)) {
+ mModel.stopLoader();
+ app.setLauncher(null);
+ }
try {
mAppWidgetHost.stopListening();
@@ -1959,6 +2034,7 @@ public class Launcher extends Activity
mWorkspace = null;
mDragController = null;
+ PackageInstallerCompat.getInstance(this).onStop();
LauncherAnimUtils.onDestroyActivity();
}
@@ -1968,7 +2044,9 @@ public class Launcher extends Activity
@Override
public void startActivityForResult(Intent intent, int requestCode) {
- if (requestCode >= 0) mWaitingForResult = true;
+ if (requestCode >= 0) {
+ setWaitingForResult(true);
+ }
super.startActivityForResult(intent, requestCode);
}
@@ -1995,14 +2073,25 @@ public class Launcher extends Activity
sourceBounds = mSearchDropTargetBar.getSearchBarBounds();
}
- startSearch(initialQuery, selectInitialQuery,
+ boolean clearTextImmediately = startSearch(initialQuery, selectInitialQuery,
appSearchData, sourceBounds);
+ if (clearTextImmediately) {
+ clearTypedText();
+ }
}
- public void startSearch(String initialQuery,
+ /**
+ * Start a text search.
+ *
+ * @return {@code true} if the search will start immediately, so any further keypresses
+ * will be handled directly by the search UI. {@code false} if {@link Launcher} should continue
+ * to buffer keypresses.
+ */
+ public boolean startSearch(String initialQuery,
boolean selectInitialQuery, Bundle appSearchData, Rect sourceBounds) {
startGlobalSearch(initialQuery, selectInitialQuery,
appSearchData, sourceBounds);
+ return false;
}
/**
@@ -2082,6 +2171,24 @@ public class Launcher extends Activity
return mWorkspaceLoading;
}
+ private void setWorkspaceLoading(boolean value) {
+ boolean isLocked = isWorkspaceLocked();
+ mWorkspaceLoading = value;
+ if (isLocked != isWorkspaceLocked()) {
+ onWorkspaceLockedChanged();
+ }
+ }
+
+ private void setWaitingForResult(boolean value) {
+ boolean isLocked = isWorkspaceLocked();
+ mWaitingForResult = value;
+ if (isLocked != isWorkspaceLocked()) {
+ onWorkspaceLockedChanged();
+ }
+ }
+
+ protected void onWorkspaceLockedChanged() { }
+
private void resetAddInfo() {
mPendingAddInfo.container = ItemInfo.NO_ID;
mPendingAddInfo.screenId = -1;
@@ -2104,10 +2211,9 @@ public class Launcher extends Activity
mPendingAddWidgetId = appWidgetId;
// Launch over to configure widget, if needed
- Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_CONFIGURE);
- intent.setComponent(appWidgetInfo.configure);
- intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
- Utilities.startActivityForResultSafely(this, intent, REQUEST_CREATE_APPWIDGET);
+ mAppWidgetManager.startConfigActivity(appWidgetInfo, appWidgetId, this,
+ mAppWidgetHost, REQUEST_CREATE_APPWIDGET);
+
} else {
// Otherwise just add it
Runnable onComplete = new Runnable() {
@@ -2120,7 +2226,7 @@ public class Launcher extends Activity
};
completeAddAppWidget(appWidgetId, info.container, info.screenId, boundWidget,
appWidgetInfo);
- mWorkspace.removeExtraEmptyScreen(true, onComplete, delay, false);
+ mWorkspace.removeExtraEmptyScreenDelayed(true, onComplete, delay, false);
}
}
@@ -2191,14 +2297,8 @@ public class Launcher extends Activity
appWidgetId = getAppWidgetHost().allocateAppWidgetId();
Bundle options = info.bindOptions;
- boolean success = false;
- if (options != null) {
- success = mAppWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,
- info.componentName, options);
- } else {
- success = mAppWidgetManager.bindAppWidgetIdIfAllowed(appWidgetId,
- info.componentName);
- }
+ boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(
+ appWidgetId, info.info, options);
if (success) {
addAppWidgetImpl(appWidgetId, info, null, info.info);
} else {
@@ -2206,6 +2306,8 @@ public class Launcher extends Activity
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, appWidgetId);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, info.componentName);
+ mAppWidgetManager.getUser(mPendingAddWidgetInfo)
+ .addToIntent(intent, AppWidgetManager.EXTRA_APPWIDGET_PROVIDER_PROFILE);
// TODO: we need to make sure that this accounts for the options bundle.
// intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_OPTIONS, options);
startActivityForResult(intent, REQUEST_BIND_APPWIDGET);
@@ -2214,21 +2316,7 @@ public class Launcher extends Activity
}
void processShortcut(Intent intent) {
- // Handle case where user selected "Applications"
- String applicationName = getResources().getString(R.string.group_applications);
- String shortcutName = intent.getStringExtra(Intent.EXTRA_SHORTCUT_NAME);
-
- if (applicationName != null && applicationName.equals(shortcutName)) {
- Intent mainIntent = new Intent(Intent.ACTION_MAIN, null);
- mainIntent.addCategory(Intent.CATEGORY_LAUNCHER);
-
- Intent pickIntent = new Intent(Intent.ACTION_PICK_ACTIVITY);
- pickIntent.putExtra(Intent.EXTRA_INTENT, mainIntent);
- pickIntent.putExtra(Intent.EXTRA_TITLE, getText(R.string.title_select_application));
- Utilities.startActivityForResultSafely(this, pickIntent, REQUEST_PICK_APPLICATION);
- } else {
- Utilities.startActivityForResultSafely(this, intent, REQUEST_CREATE_SHORTCUT);
- }
+ Utilities.startActivityForResultSafely(this, intent, REQUEST_CREATE_SHORTCUT);
}
void processWallpaper(Intent intent) {
@@ -2260,12 +2348,6 @@ public class Launcher extends Activity
sFolders.remove(folder.id);
}
- protected void startWallpaper() {
- final Intent pickWallpaper = new Intent(Intent.ACTION_SET_WALLPAPER);
- pickWallpaper.setComponent(getWallpaperPickerComponent());
- startActivityForResult(pickWallpaper, REQUEST_PICK_WALLPAPER);
- }
-
protected ComponentName getWallpaperPickerComponent() {
return new ComponentName(getPackageName(), LauncherWallpaperPickerActivity.class.getName());
}
@@ -2369,59 +2451,63 @@ public class Launcher extends Activity
Object tag = v.getTag();
if (tag instanceof ShortcutInfo) {
- // Open shortcut
- final ShortcutInfo shortcut = (ShortcutInfo) tag;
- final Intent intent = shortcut.intent;
-
- // Check for special shortcuts
- if (intent.getComponent() != null) {
- final String shortcutClass = intent.getComponent().getClassName();
-
- if (shortcutClass.equals(WidgetAdder.class.getName())) {
- onClickAddWidgetButton();
- return;
- } else if (shortcutClass.equals(MemoryDumpActivity.class.getName())) {
- MemoryDumpActivity.startDump(this);
- return;
- } else if (shortcutClass.equals(ToggleWeightWatcher.class.getName())) {
- toggleShowWeightWatcher();
- return;
- }
- }
-
- // Start activities
- int[] pos = new int[2];
- v.getLocationOnScreen(pos);
- intent.setSourceBounds(new Rect(pos[0], pos[1],
- pos[0] + v.getWidth(), pos[1] + v.getHeight()));
-
- boolean success = startActivitySafely(v, intent, tag);
-
- mStats.recordLaunch(intent, shortcut);
-
- if (success && v instanceof BubbleTextView) {
- mWaitingForResume = (BubbleTextView) v;
- mWaitingForResume.setStayPressed(true);
- }
+ onClickAppShortcut(v);
} else if (tag instanceof FolderInfo) {
if (v instanceof FolderIcon) {
- FolderIcon fi = (FolderIcon) v;
- handleFolderClick(fi);
+ onClickFolderIcon(v);
}
} else if (v == mAllAppsButton) {
- if (isAllAppsVisible()) {
- showWorkspace(true);
- } else {
- onClickAllAppsButton(v);
+ onClickAllAppsButton(v);
+ } else if (tag instanceof AppInfo) {
+ startAppShortcutOrInfoActivity(v);
+ } else if (tag instanceof LauncherAppWidgetInfo) {
+ if (v instanceof PendingAppWidgetHostView) {
+ onClickPendingWidget((PendingAppWidgetHostView) v);
}
}
}
+ public void onClickPagedViewIcon(View v) {
+ startAppShortcutOrInfoActivity(v);
+ }
+
public boolean onTouch(View v, MotionEvent event) {
return false;
}
/**
+ * Event handler for the app widget view which has not fully restored.
+ */
+ public void onClickPendingWidget(final PendingAppWidgetHostView v) {
+ final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) v.getTag();
+ if (v.isReadyForClickSetup()) {
+ int widgetId = info.appWidgetId;
+ AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(widgetId);
+ if (appWidgetInfo != null) {
+ mPendingAddWidgetInfo = appWidgetInfo;
+ mPendingAddInfo.copyFrom(info);
+ mPendingAddWidgetId = widgetId;
+
+ AppWidgetManagerCompat.getInstance(this).startConfigActivity(appWidgetInfo,
+ info.appWidgetId, this, mAppWidgetHost, REQUEST_RECONFIGURE_APPWIDGET);
+ }
+ } else if (info.installProgress < 0) {
+ // The install has not been queued
+ final String packageName = info.providerName.getPackageName();
+ showBrokenAppInstallDialog(packageName,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ startActivitySafely(v, LauncherModel.getMarketIntent(packageName), info);
+ }
+ });
+ } else {
+ // Download has started.
+ final String packageName = info.providerName.getPackageName();
+ startActivitySafely(v, LauncherModel.getMarketIntent(packageName), info);
+ }
+ }
+
+ /**
* Event handler for the search button
*
* @param v The view that was clicked.
@@ -2467,18 +2553,180 @@ public class Launcher extends Activity
*
* @param v The view that was clicked.
*/
- public void onClickAllAppsButton(View v) {
- showAllApps(true, AppsCustomizePagedView.ContentType.Applications, false);
+ protected void onClickAllAppsButton(View v) {
+ if (LOGD) Log.d(TAG, "onClickAllAppsButton");
+ if (isAllAppsVisible()) {
+ showWorkspace(true);
+ } else {
+ showAllApps(true, AppsCustomizePagedView.ContentType.Applications, false);
+ }
+ }
+
+ private void showBrokenAppInstallDialog(final String packageName,
+ DialogInterface.OnClickListener onSearchClickListener) {
+ new AlertDialog.Builder(new ContextThemeWrapper(this, android.R.style.Theme_DeviceDefault))
+ .setTitle(R.string.abandoned_promises_title)
+ .setMessage(R.string.abandoned_promise_explanation)
+ .setPositiveButton(R.string.abandoned_search, onSearchClickListener)
+ .setNeutralButton(R.string.abandoned_clean_this,
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ final UserHandleCompat user = UserHandleCompat.myUserHandle();
+ mWorkspace.removeAbandonedPromise(packageName, user);
+ }
+ })
+ .create().show();
+ return;
+ }
+
+ /**
+ * Event handler for an app shortcut click.
+ *
+ * @param v The view that was clicked. Must be a tagged with a {@link ShortcutInfo}.
+ */
+ protected void onClickAppShortcut(final View v) {
+ if (LOGD) Log.d(TAG, "onClickAppShortcut");
+ Object tag = v.getTag();
+ if (!(tag instanceof ShortcutInfo)) {
+ throw new IllegalArgumentException("Input must be a Shortcut");
+ }
+
+ // Open shortcut
+ final ShortcutInfo shortcut = (ShortcutInfo) tag;
+ final Intent intent = shortcut.intent;
+
+ // Check for special shortcuts
+ if (intent.getComponent() != null) {
+ final String shortcutClass = intent.getComponent().getClassName();
+
+ if (shortcutClass.equals(MemoryDumpActivity.class.getName())) {
+ MemoryDumpActivity.startDump(this);
+ return;
+ } else if (shortcutClass.equals(ToggleWeightWatcher.class.getName())) {
+ toggleShowWeightWatcher();
+ return;
+ }
+ }
+
+ // Check for abandoned promise
+ if ((v instanceof BubbleTextView)
+ && shortcut.isPromise()
+ && !shortcut.hasStatusFlag(ShortcutInfo.FLAG_INSTALL_SESSION_ACTIVE)) {
+ showBrokenAppInstallDialog(
+ shortcut.getTargetComponent().getPackageName(),
+ new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ startAppShortcutOrInfoActivity(v);
+ }
+ });
+ return;
+ }
+
+ // Start activities
+ startAppShortcutOrInfoActivity(v);
+ }
+
+ private void startAppShortcutOrInfoActivity(View v) {
+ Object tag = v.getTag();
+ final ShortcutInfo shortcut;
+ final Intent intent;
+ if (tag instanceof ShortcutInfo) {
+ shortcut = (ShortcutInfo) tag;
+ intent = shortcut.intent;
+ int[] pos = new int[2];
+ v.getLocationOnScreen(pos);
+ intent.setSourceBounds(new Rect(pos[0], pos[1],
+ pos[0] + v.getWidth(), pos[1] + v.getHeight()));
+
+ } else if (tag instanceof AppInfo) {
+ shortcut = null;
+ intent = ((AppInfo) tag).intent;
+ } else {
+ throw new IllegalArgumentException("Input must be a Shortcut or AppInfo");
+ }
+
+ boolean success = startActivitySafely(v, intent, tag);
+ mStats.recordLaunch(intent, shortcut);
+
+ if (success && v instanceof BubbleTextView) {
+ mWaitingForResume = (BubbleTextView) v;
+ mWaitingForResume.setStayPressed(true);
+ }
+ }
+
+ /**
+ * Event handler for a folder icon click.
+ *
+ * @param v The view that was clicked. Must be an instance of {@link FolderIcon}.
+ */
+ protected void onClickFolderIcon(View v) {
+ if (LOGD) Log.d(TAG, "onClickFolder");
+ if (!(v instanceof FolderIcon)){
+ throw new IllegalArgumentException("Input must be a FolderIcon");
+ }
+
+ FolderIcon folderIcon = (FolderIcon) v;
+ final FolderInfo info = folderIcon.getFolderInfo();
+ Folder openFolder = mWorkspace.getFolderForTag(info);
+
+ // If the folder info reports that the associated folder is open, then verify that
+ // it is actually opened. There have been a few instances where this gets out of sync.
+ if (info.opened && openFolder == null) {
+ Log.d(TAG, "Folder info marked as open, but associated folder is not open. Screen: "
+ + info.screenId + " (" + info.cellX + ", " + info.cellY + ")");
+ info.opened = false;
+ }
+
+ if (!info.opened && !folderIcon.getFolder().isDestroyed()) {
+ // Close any open folder
+ closeFolder();
+ // Open the requested folder
+ openFolder(folderIcon);
+ } else {
+ // Find the open folder...
+ int folderScreen;
+ if (openFolder != null) {
+ folderScreen = mWorkspace.getPageForView(openFolder);
+ // .. and close it
+ closeFolder(openFolder);
+ if (folderScreen != mWorkspace.getCurrentPage()) {
+ // Close any folder open on the current screen
+ closeFolder();
+ // Pull the folder onto this screen
+ openFolder(folderIcon);
+ }
+ }
+ }
}
/**
* Event handler for the (Add) Widgets button that appears after a long press
* on the home screen.
*/
- protected void onClickAddWidgetButton() {
+ protected void onClickAddWidgetButton(View view) {
+ if (LOGD) Log.d(TAG, "onClickAddWidgetButton");
showAllApps(true, AppsCustomizePagedView.ContentType.Widgets, true);
}
+ /**
+ * Event handler for the wallpaper picker button that appears after a long press
+ * on the home screen.
+ */
+ protected void onClickWallpaperPicker(View v) {
+ if (LOGD) Log.d(TAG, "onClickWallpaperPicker");
+ final Intent pickWallpaper = new Intent(Intent.ACTION_SET_WALLPAPER);
+ pickWallpaper.setComponent(getWallpaperPickerComponent());
+ startActivityForResult(pickWallpaper, REQUEST_PICK_WALLPAPER);
+ }
+
+ /**
+ * Event handler for a click on the settings button that appears after a long press
+ * on the home screen.
+ */
+ protected void onClickSettingsButton(View v) {
+ if (LOGD) Log.d(TAG, "onClickSettingsButton");
+ }
+
public void onTouchDownAllAppsButton(View v) {
// Provide the same haptic feedback that the system offers for virtual keys.
v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY);
@@ -2504,15 +2752,7 @@ public class Launcher extends Activity
return mHapticFeedbackTouchListener;
}
- public void onClickAppMarketButton(View v) {
- if (!DISABLE_MARKET_BUTTON) {
- if (mAppMarketIntent != null) {
- startActivitySafely(v, mAppMarketIntent, "app market");
- } else {
- Log.e(TAG, "Invalid app market intent.");
- }
- }
- }
+ public void onDragStarted(View view) {}
/**
* Called when the user stops interacting with the launcher.
@@ -2531,17 +2771,24 @@ public class Launcher extends Activity
*/
protected void onInteractionBegin() {}
- void startApplicationDetailsActivity(ComponentName componentName) {
+ void startApplicationDetailsActivity(ComponentName componentName, UserHandleCompat user) {
String packageName = componentName.getPackageName();
- Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
- Uri.fromParts("package", packageName, null));
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK |
- Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
- startActivitySafely(null, intent, "startApplicationDetailsActivity");
+ try {
+ LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(this);
+ UserManagerCompat userManager = UserManagerCompat.getInstance(this);
+ launcherApps.showAppDetailsForProfile(componentName, user);
+ } catch (SecurityException e) {
+ Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
+ Log.e(TAG, "Launcher does not have permission to launch settings");
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
+ Log.e(TAG, "Unable to launch settings");
+ }
}
// returns true if the activity was started
- boolean startApplicationUninstallActivity(ComponentName componentName, int flags) {
+ boolean startApplicationUninstallActivity(ComponentName componentName, int flags,
+ UserHandleCompat user) {
if ((flags & AppInfo.DOWNLOADED_FLAG) == 0) {
// System applications cannot be installed. For now, show a toast explaining that.
// We may give them the option of disabling apps this way.
@@ -2555,6 +2802,9 @@ public class Launcher extends Activity
Intent.ACTION_DELETE, Uri.fromParts("package", packageName, className));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+ if (user != null) {
+ user.addToIntent(intent, Intent.EXTRA_USER);
+ }
startActivity(intent);
return true;
}
@@ -2562,19 +2812,35 @@ public class Launcher extends Activity
boolean startActivity(View v, Intent intent, Object tag) {
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-
try {
// Only launch using the new animation if the shortcut has not opted out (this is a
// private contract between launcher and may be ignored in the future).
boolean useLaunchAnimation = (v != null) &&
!intent.hasExtra(INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION);
+ LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(this);
+ UserManagerCompat userManager = UserManagerCompat.getInstance(this);
+
+ UserHandleCompat user = null;
+ if (intent.hasExtra(AppInfo.EXTRA_PROFILE)) {
+ long serialNumber = intent.getLongExtra(AppInfo.EXTRA_PROFILE, -1);
+ user = userManager.getUserForSerialNumber(serialNumber);
+ }
+
+ Bundle optsBundle = null;
if (useLaunchAnimation) {
- ActivityOptions opts = ActivityOptions.makeScaleUpAnimation(v, 0, 0,
- v.getMeasuredWidth(), v.getMeasuredHeight());
+ ActivityOptions opts = Utilities.isLmpOrAbove() ?
+ ActivityOptions.makeCustomAnimation(this, R.anim.task_open_enter, R.anim.no_anim) :
+ ActivityOptions.makeScaleUpAnimation(v, 0, 0, v.getMeasuredWidth(), v.getMeasuredHeight());
+ optsBundle = opts.toBundle();
+ }
- startActivity(intent, opts.toBundle());
+ if (user == null || user.equals(UserHandleCompat.myUserHandle())) {
+ // Could be launching some bookkeeping activity
+ startActivity(intent, optsBundle);
} else {
- startActivity(intent);
+ // TODO Component can be null when shortcuts are supported for secondary user
+ launcherApps.startActivityForProfile(intent.getComponent(), user,
+ intent.getSourceBounds(), optsBundle);
}
return true;
} catch (SecurityException e) {
@@ -2589,6 +2855,10 @@ public class Launcher extends Activity
boolean startActivitySafely(View v, Intent intent, Object tag) {
boolean success = false;
+ if (mIsSafeModeEnabled && !Utilities.isSystemApp(this, intent)) {
+ Toast.makeText(this, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
+ return false;
+ }
try {
success = startActivity(v, intent, tag);
} catch (ActivityNotFoundException e) {
@@ -2598,40 +2868,6 @@ public class Launcher extends Activity
return success;
}
- private void handleFolderClick(FolderIcon folderIcon) {
- final FolderInfo info = folderIcon.getFolderInfo();
- Folder openFolder = mWorkspace.getFolderForTag(info);
-
- // If the folder info reports that the associated folder is open, then verify that
- // it is actually opened. There have been a few instances where this gets out of sync.
- if (info.opened && openFolder == null) {
- Log.d(TAG, "Folder info marked as open, but associated folder is not open. Screen: "
- + info.screenId + " (" + info.cellX + ", " + info.cellY + ")");
- info.opened = false;
- }
-
- if (!info.opened && !folderIcon.getFolder().isDestroyed()) {
- // Close any open folder
- closeFolder();
- // Open the requested folder
- openFolder(folderIcon);
- } else {
- // Find the open folder...
- int folderScreen;
- if (openFolder != null) {
- folderScreen = mWorkspace.getPageForView(openFolder);
- // .. and close it
- closeFolder(openFolder);
- if (folderScreen != mWorkspace.getCurrentPage()) {
- // Close any folder open on the current screen
- closeFolder();
- // Pull the folder onto this screen
- openFolder(folderIcon);
- }
- }
- }
- }
-
/**
* This method draws the FolderIcon to an ImageView and then adds and positions that ImageView
* in the DragLayer in the exact absolute location of the original FolderIcon.
@@ -2703,7 +2939,10 @@ public class Launcher extends Activity
ObjectAnimator oa = LauncherAnimUtils.ofPropertyValuesHolder(mFolderIconImageView, alpha,
scaleX, scaleY);
- oa.setDuration(getResources().getInteger(R.integer.config_folderAnimDuration));
+ if (Utilities.isLmpOrAbove()) {
+ oa.setInterpolator(new LogDecelerateInterpolator(100, 0));
+ }
+ oa.setDuration(getResources().getInteger(R.integer.config_folderExpandDuration));
oa.start();
}
@@ -2720,7 +2959,7 @@ public class Launcher extends Activity
copyFolderIconToImage(fi);
ObjectAnimator oa = LauncherAnimUtils.ofPropertyValuesHolder(mFolderIconImageView, alpha,
scaleX, scaleY);
- oa.setDuration(getResources().getInteger(R.integer.config_folderAnimDuration));
+ oa.setDuration(getResources().getInteger(R.integer.config_folderExpandDuration));
oa.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@@ -2773,9 +3012,6 @@ public class Launcher extends Activity
folder.dismissEditingName();
}
closeFolder(folder);
-
- // Dismiss the folder cling
- mLauncherClings.dismissFolderCling(null);
}
}
@@ -2808,23 +3044,22 @@ public class Launcher extends Activity
} else {
return false;
}
+ } else {
+ return false;
}
}
- if (!(v instanceof CellLayout)) {
- v = (View) v.getParent().getParent();
- }
-
- resetAddInfo();
- CellLayout.CellInfo longClickCellInfo = (CellLayout.CellInfo) v.getTag();
- // This happens when long clicking an item with the dpad/trackball
- if (longClickCellInfo == null) {
- return true;
+ CellLayout.CellInfo longClickCellInfo = null;
+ View itemUnderLongClick = null;
+ if (v.getTag() instanceof ItemInfo) {
+ ItemInfo info = (ItemInfo) v.getTag();
+ longClickCellInfo = new CellLayout.CellInfo(v, info);;
+ itemUnderLongClick = longClickCellInfo.cell;
+ resetAddInfo();
}
// The hotseat touch handling does not go through Workspace, and we always allow long press
// on hotseat items.
- final View itemUnderLongClick = longClickCellInfo.cell;
final boolean inHotseat = isHotseatLayout(v);
boolean allowLongPress = inHotseat || mWorkspace.allowLongPress();
if (allowLongPress && !mDragController.isDragging()) {
@@ -2832,7 +3067,6 @@ public class Launcher extends Activity
// User long pressed on empty space
mWorkspace.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS,
HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING);
- // Disabling reordering until we sort out some issues.
if (mWorkspace.isInOverviewMode()) {
mWorkspace.startReordering(v);
} else {
@@ -2876,22 +3110,12 @@ public class Launcher extends Activity
return (mState == State.APPS_CUSTOMIZE) || (mOnResumeState == State.APPS_CUSTOMIZE);
}
- /**
- * Helper method for the cameraZoomIn/cameraZoomOut animations
- * @param view The view being animated
- * @param scaleFactor The scale factor used for the zoom
- */
- private void setPivotsForZoom(View view, float scaleFactor) {
- view.setPivotX(view.getWidth() / 2.0f);
- view.setPivotY(view.getHeight() / 2.0f);
- }
-
private void setWorkspaceBackground(boolean workspace) {
mLauncherView.setBackground(workspace ?
mWorkspaceBackgroundDrawable : null);
}
- void updateWallpaperVisibility(boolean visible) {
+ protected void changeWallpaperVisiblity(boolean visible) {
int wpflags = visible ? WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER : 0;
int curflags = getWindow().getAttributes().flags
& WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER;
@@ -2980,6 +3204,7 @@ public class Launcher extends Activity
AppsCustomizePagedView.ContentType contentType = mAppsCustomizeContent.getContentType();
showAppsCustomizeHelper(animated, springLoaded, contentType);
}
+
private void showAppsCustomizeHelper(final boolean animated, final boolean springLoaded,
final AppsCustomizePagedView.ContentType contentType) {
if (mStateAnimation != null) {
@@ -2987,98 +3212,178 @@ public class Launcher extends Activity
mStateAnimation.cancel();
mStateAnimation = null;
}
+
+ boolean material = Utilities.isLmpOrAbove();
+
final Resources res = getResources();
final int duration = res.getInteger(R.integer.config_appsCustomizeZoomInTime);
final int fadeDuration = res.getInteger(R.integer.config_appsCustomizeFadeInTime);
+ final int revealDuration = res.getInteger(R.integer.config_appsCustomizeRevealTime);
+ final int itemsAlphaStagger =
+ res.getInteger(R.integer.config_appsCustomizeItemsAlphaStagger);
+
final float scale = (float) res.getInteger(R.integer.config_appsCustomizeZoomScaleFactor);
final View fromView = mWorkspace;
final AppsCustomizeTabHost toView = mAppsCustomizeTabHost;
- final int startDelay =
- res.getInteger(R.integer.config_workspaceAppsCustomizeAnimationStagger);
- setPivotsForZoom(toView, scale);
+ final ArrayList<View> layerViews = new ArrayList<View>();
- // Shrink workspaces away if going to AppsCustomize from workspace
+ Workspace.State workspaceState = contentType == AppsCustomizePagedView.ContentType.Widgets ?
+ Workspace.State.OVERVIEW_HIDDEN : Workspace.State.NORMAL_HIDDEN;
Animator workspaceAnim =
- mWorkspace.getChangeStateAnimation(Workspace.State.SMALL, animated);
+ mWorkspace.getChangeStateAnimation(workspaceState, animated, layerViews);
if (!LauncherAppState.isDisableAllApps()
|| contentType == AppsCustomizePagedView.ContentType.Widgets) {
// Set the content type for the all apps/widgets space
mAppsCustomizeTabHost.setContentTypeImmediate(contentType);
}
- if (animated) {
- toView.setScaleX(scale);
- toView.setScaleY(scale);
- final LauncherViewPropertyAnimator scaleAnim = new LauncherViewPropertyAnimator(toView);
- scaleAnim.
- scaleX(1f).scaleY(1f).
- setDuration(duration).
- setInterpolator(new Workspace.ZoomOutInterpolator());
-
- toView.setVisibility(View.VISIBLE);
- toView.setAlpha(0f);
- final ObjectAnimator alphaAnim = LauncherAnimUtils
- .ofFloat(toView, "alpha", 0f, 1f)
- .setDuration(fadeDuration);
- alphaAnim.setInterpolator(new DecelerateInterpolator(1.5f));
- alphaAnim.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- if (animation == null) {
- throw new RuntimeException("animation is null");
- }
- float t = (Float) animation.getAnimatedValue();
- dispatchOnLauncherTransitionStep(fromView, t);
- dispatchOnLauncherTransitionStep(toView, t);
- }
- });
+ // If for some reason our views aren't initialized, don't animate
+ boolean initialized = getAllAppsButton() != null;
- // toView should appear right at the end of the workspace shrink
- // animation
+ if (animated && initialized) {
mStateAnimation = LauncherAnimUtils.createAnimatorSet();
- mStateAnimation.play(scaleAnim).after(startDelay);
- mStateAnimation.play(alphaAnim).after(startDelay);
+ final AppsCustomizePagedView content = (AppsCustomizePagedView)
+ toView.findViewById(R.id.apps_customize_pane_content);
+
+ final View page = content.getPageAt(content.getCurrentPage());
+ final View revealView = toView.findViewById(R.id.fake_page);
+
+ final float initialPanelAlpha = 1f;
+
+ final boolean isWidgetTray = contentType == AppsCustomizePagedView.ContentType.Widgets;
+ if (isWidgetTray) {
+ revealView.setBackground(res.getDrawable(R.drawable.quantum_panel_dark));
+ } else {
+ revealView.setBackground(res.getDrawable(R.drawable.quantum_panel));
+ }
+
+ // Hide the real page background, and swap in the fake one
+ content.setPageBackgroundsVisible(false);
+ revealView.setVisibility(View.VISIBLE);
+ // We need to hide this view as the animation start will be posted.
+ revealView.setAlpha(0);
+
+ int width = revealView.getMeasuredWidth();
+ int height = revealView.getMeasuredHeight();
+ float revealRadius = (float) Math.sqrt((width * width) / 4 + (height * height) / 4);
+
+ revealView.setTranslationY(0);
+ revealView.setTranslationX(0);
+
+ // Get the y delta between the center of the page and the center of the all apps button
+ int[] allAppsToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView,
+ getAllAppsButton(), null);
+
+ float alpha = 0;
+ float xDrift = 0;
+ float yDrift = 0;
+ if (material) {
+ alpha = isWidgetTray ? 0.3f : 1f;
+ yDrift = isWidgetTray ? height / 2 : allAppsToPanelDelta[1];
+ xDrift = isWidgetTray ? 0 : allAppsToPanelDelta[0];
+ } else {
+ yDrift = 2 * height / 3;
+ xDrift = 0;
+ }
+ final float initAlpha = alpha;
+
+ revealView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ layerViews.add(revealView);
+ PropertyValuesHolder panelAlpha = PropertyValuesHolder.ofFloat("alpha", initAlpha, 1f);
+ PropertyValuesHolder panelDriftY =
+ PropertyValuesHolder.ofFloat("translationY", yDrift, 0);
+ PropertyValuesHolder panelDriftX =
+ PropertyValuesHolder.ofFloat("translationX", xDrift, 0);
+
+ ObjectAnimator panelAlphaAndDrift = ObjectAnimator.ofPropertyValuesHolder(revealView,
+ panelAlpha, panelDriftY, panelDriftX);
+
+ panelAlphaAndDrift.setDuration(revealDuration);
+ panelAlphaAndDrift.setInterpolator(new LogDecelerateInterpolator(100, 0));
+
+ mStateAnimation.play(panelAlphaAndDrift);
+
+ if (page != null) {
+ page.setVisibility(View.VISIBLE);
+ page.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ layerViews.add(page);
+
+ ObjectAnimator pageDrift = ObjectAnimator.ofFloat(page, "translationY", yDrift, 0);
+ page.setTranslationY(yDrift);
+ pageDrift.setDuration(revealDuration);
+ pageDrift.setInterpolator(new LogDecelerateInterpolator(100, 0));
+ pageDrift.setStartDelay(itemsAlphaStagger);
+ mStateAnimation.play(pageDrift);
+
+ page.setAlpha(0f);
+ ObjectAnimator itemsAlpha = ObjectAnimator.ofFloat(page, "alpha", 0f, 1f);
+ itemsAlpha.setDuration(revealDuration);
+ itemsAlpha.setInterpolator(new AccelerateInterpolator(1.5f));
+ itemsAlpha.setStartDelay(itemsAlphaStagger);
+ mStateAnimation.play(itemsAlpha);
+ }
+
+ View pageIndicators = toView.findViewById(R.id.apps_customize_page_indicator);
+ pageIndicators.setAlpha(0.01f);
+ ObjectAnimator indicatorsAlpha =
+ ObjectAnimator.ofFloat(pageIndicators, "alpha", 1f);
+ indicatorsAlpha.setDuration(revealDuration);
+ mStateAnimation.play(indicatorsAlpha);
+
+ if (material) {
+ final View allApps = getAllAppsButton();
+ int allAppsButtonSize = LauncherAppState.getInstance().
+ getDynamicGrid().getDeviceProfile().allAppsButtonVisualSize;
+ float startRadius = isWidgetTray ? 0 : allAppsButtonSize / 2;
+ Animator reveal = ViewAnimationUtils.createCircularReveal(revealView, width / 2,
+ height / 2, startRadius, revealRadius);
+ reveal.setDuration(revealDuration);
+ reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
+
+ reveal.addListener(new AnimatorListenerAdapter() {
+ public void onAnimationStart(Animator animation) {
+ if (!isWidgetTray) {
+ allApps.setVisibility(View.INVISIBLE);
+ }
+ }
+ public void onAnimationEnd(Animator animation) {
+ if (!isWidgetTray) {
+ allApps.setVisibility(View.VISIBLE);
+ }
+ }
+ });
+ mStateAnimation.play(reveal);
+ }
mStateAnimation.addListener(new AnimatorListenerAdapter() {
@Override
- public void onAnimationStart(Animator animation) {
- // Prepare the position
- toView.setTranslationX(0.0f);
- toView.setTranslationY(0.0f);
- toView.setVisibility(View.VISIBLE);
- toView.bringToFront();
- }
- @Override
public void onAnimationEnd(Animator animation) {
dispatchOnLauncherTransitionEnd(fromView, animated, false);
dispatchOnLauncherTransitionEnd(toView, animated, false);
+ revealView.setVisibility(View.INVISIBLE);
+ revealView.setLayerType(View.LAYER_TYPE_NONE, null);
+ if (page != null) {
+ page.setLayerType(View.LAYER_TYPE_NONE, null);
+ }
+ content.setPageBackgroundsVisible(true);
+
// Hide the search bar
if (mSearchDropTargetBar != null) {
mSearchDropTargetBar.hideSearchBar(false);
}
}
+
});
if (workspaceAnim != null) {
mStateAnimation.play(workspaceAnim);
}
- boolean delayAnim = false;
-
dispatchOnLauncherTransitionPrepare(fromView, animated, false);
dispatchOnLauncherTransitionPrepare(toView, animated, false);
-
- // If any of the objects being animated haven't been measured/laid out
- // yet, delay the animation until we get a layout pass
- if ((((LauncherTransitionable) toView).getContent().getMeasuredWidth() == 0) ||
- (mWorkspace.getMeasuredWidth() == 0) ||
- (toView.getMeasuredWidth() == 0)) {
- delayAnim = true;
- }
-
final AnimatorSet stateAnimation = mStateAnimation;
final Runnable startAnimRunnable = new Runnable() {
public void run() {
@@ -3086,23 +3391,28 @@ public class Launcher extends Activity
// we waited for a layout/draw pass
if (mStateAnimation != stateAnimation)
return;
- setPivotsForZoom(toView, scale);
dispatchOnLauncherTransitionStart(fromView, animated, false);
dispatchOnLauncherTransitionStart(toView, animated, false);
- LauncherAnimUtils.startAnimationAfterNextDraw(mStateAnimation, toView);
+
+ revealView.setAlpha(initAlpha);
+ if (Utilities.isLmpOrAbove()) {
+ for (int i = 0; i < layerViews.size(); i++) {
+ View v = layerViews.get(i);
+ if (v != null) {
+ boolean attached = true;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ attached = v.isAttachedToWindow();
+ }
+ if (attached) v.buildLayer();
+ }
+ }
+ }
+ mStateAnimation.start();
}
};
- if (delayAnim) {
- final ViewTreeObserver observer = toView.getViewTreeObserver();
- observer.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
- public void onGlobalLayout() {
- startAnimRunnable.run();
- toView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
- }
- });
- } else {
- startAnimRunnable.run();
- }
+ toView.bringToFront();
+ toView.setVisibility(View.VISIBLE);
+ toView.post(startAnimRunnable);
} else {
toView.setTranslationX(0.0f);
toView.setTranslationY(0.0f);
@@ -3139,54 +3449,184 @@ public class Launcher extends Activity
mStateAnimation.cancel();
mStateAnimation = null;
}
+
+ boolean material = Utilities.isLmpOrAbove();
Resources res = getResources();
final int duration = res.getInteger(R.integer.config_appsCustomizeZoomOutTime);
- final int fadeOutDuration =
- res.getInteger(R.integer.config_appsCustomizeFadeOutTime);
+ final int fadeOutDuration = res.getInteger(R.integer.config_appsCustomizeFadeOutTime);
+ final int revealDuration = res.getInteger(R.integer.config_appsCustomizeConcealTime);
+ final int itemsAlphaStagger =
+ res.getInteger(R.integer.config_appsCustomizeItemsAlphaStagger);
+
final float scaleFactor = (float)
res.getInteger(R.integer.config_appsCustomizeZoomScaleFactor);
final View fromView = mAppsCustomizeTabHost;
final View toView = mWorkspace;
Animator workspaceAnim = null;
+ final ArrayList<View> layerViews = new ArrayList<View>();
+
if (toState == Workspace.State.NORMAL) {
- int stagger = res.getInteger(R.integer.config_appsCustomizeWorkspaceAnimationStagger);
workspaceAnim = mWorkspace.getChangeStateAnimation(
- toState, animated, stagger, -1);
+ toState, animated, layerViews);
} else if (toState == Workspace.State.SPRING_LOADED ||
toState == Workspace.State.OVERVIEW) {
workspaceAnim = mWorkspace.getChangeStateAnimation(
- toState, animated);
- }
-
- setPivotsForZoom(fromView, scaleFactor);
- showHotseat(animated);
- if (animated) {
- final LauncherViewPropertyAnimator scaleAnim =
- new LauncherViewPropertyAnimator(fromView);
- scaleAnim.
- scaleX(scaleFactor).scaleY(scaleFactor).
- setDuration(duration).
- setInterpolator(new Workspace.ZoomInInterpolator());
-
- final ObjectAnimator alphaAnim = LauncherAnimUtils
- .ofFloat(fromView, "alpha", 1f, 0f)
- .setDuration(fadeOutDuration);
- alphaAnim.setInterpolator(new AccelerateDecelerateInterpolator());
- alphaAnim.addUpdateListener(new AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- float t = 1f - (Float) animation.getAnimatedValue();
- dispatchOnLauncherTransitionStep(fromView, t);
- dispatchOnLauncherTransitionStep(toView, t);
- }
- });
+ toState, animated, layerViews);
+ }
+
+ // If for some reason our views aren't initialized, don't animate
+ boolean initialized = getAllAppsButton() != null;
+ if (animated && initialized) {
mStateAnimation = LauncherAnimUtils.createAnimatorSet();
+ if (workspaceAnim != null) {
+ mStateAnimation.play(workspaceAnim);
+ }
- dispatchOnLauncherTransitionPrepare(fromView, animated, true);
- dispatchOnLauncherTransitionPrepare(toView, animated, true);
- mAppsCustomizeContent.stopScrolling();
+ final AppsCustomizePagedView content = (AppsCustomizePagedView)
+ fromView.findViewById(R.id.apps_customize_pane_content);
+
+ final View page = content.getPageAt(content.getNextPage());
+
+ // We need to hide side pages of the Apps / Widget tray to avoid some ugly edge cases
+ int count = content.getChildCount();
+ for (int i = 0; i < count; i++) {
+ View child = content.getChildAt(i);
+ if (child != page) {
+ child.setVisibility(View.INVISIBLE);
+ }
+ }
+ final View revealView = fromView.findViewById(R.id.fake_page);
+
+ // hideAppsCustomizeHelper is called in some cases when it is already hidden
+ // don't perform all these no-op animations. In particularly, this was causing
+ // the all-apps button to pop in and out.
+ if (fromView.getVisibility() == View.VISIBLE) {
+ AppsCustomizePagedView.ContentType contentType = content.getContentType();
+ final boolean isWidgetTray =
+ contentType == AppsCustomizePagedView.ContentType.Widgets;
+
+ if (isWidgetTray) {
+ revealView.setBackground(res.getDrawable(R.drawable.quantum_panel_dark));
+ } else {
+ revealView.setBackground(res.getDrawable(R.drawable.quantum_panel));
+ }
+
+ int width = revealView.getMeasuredWidth();
+ int height = revealView.getMeasuredHeight();
+ float revealRadius = (float) Math.sqrt((width * width) / 4 + (height * height) / 4);
+
+ // Hide the real page background, and swap in the fake one
+ revealView.setVisibility(View.VISIBLE);
+ content.setPageBackgroundsVisible(false);
+
+ final View allAppsButton = getAllAppsButton();
+ revealView.setTranslationY(0);
+ int[] allAppsToPanelDelta = Utilities.getCenterDeltaInScreenSpace(revealView,
+ allAppsButton, null);
+
+ float xDrift = 0;
+ float yDrift = 0;
+ if (material) {
+ yDrift = isWidgetTray ? height / 2 : allAppsToPanelDelta[1];
+ xDrift = isWidgetTray ? 0 : allAppsToPanelDelta[0];
+ } else {
+ yDrift = 5 * height / 4;
+ xDrift = 0;
+ }
+
+ revealView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+ TimeInterpolator decelerateInterpolator = material ?
+ new LogDecelerateInterpolator(100, 0) :
+ new LogDecelerateInterpolator(30, 0);
+
+ // The vertical motion of the apps panel should be delayed by one frame
+ // from the conceal animation in order to give the right feel. We correpsondingly
+ // shorten the duration so that the slide and conceal end at the same time.
+ ObjectAnimator panelDriftY = LauncherAnimUtils.ofFloat(revealView, "translationY",
+ 0, yDrift);
+ panelDriftY.setDuration(revealDuration - SINGLE_FRAME_DELAY);
+ panelDriftY.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
+ panelDriftY.setInterpolator(decelerateInterpolator);
+ mStateAnimation.play(panelDriftY);
+
+ ObjectAnimator panelDriftX = LauncherAnimUtils.ofFloat(revealView, "translationX",
+ 0, xDrift);
+ panelDriftX.setDuration(revealDuration - SINGLE_FRAME_DELAY);
+ panelDriftX.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
+ panelDriftX.setInterpolator(decelerateInterpolator);
+ mStateAnimation.play(panelDriftX);
+
+ if (isWidgetTray || !material) {
+ float finalAlpha = material ? 0.4f : 0f;
+ revealView.setAlpha(1f);
+ ObjectAnimator panelAlpha = LauncherAnimUtils.ofFloat(revealView, "alpha",
+ 1f, finalAlpha);
+ panelAlpha.setDuration(revealDuration);
+ panelAlpha.setInterpolator(material ? decelerateInterpolator :
+ new AccelerateInterpolator(1.5f));
+ mStateAnimation.play(panelAlpha);
+ }
+
+ if (page != null) {
+ page.setLayerType(View.LAYER_TYPE_HARDWARE, null);
+
+ ObjectAnimator pageDrift = LauncherAnimUtils.ofFloat(page, "translationY",
+ 0, yDrift);
+ page.setTranslationY(0);
+ pageDrift.setDuration(revealDuration - SINGLE_FRAME_DELAY);
+ pageDrift.setInterpolator(decelerateInterpolator);
+ pageDrift.setStartDelay(itemsAlphaStagger + SINGLE_FRAME_DELAY);
+ mStateAnimation.play(pageDrift);
+
+ page.setAlpha(1f);
+ ObjectAnimator itemsAlpha = LauncherAnimUtils.ofFloat(page, "alpha", 1f, 0f);
+ itemsAlpha.setDuration(100);
+ itemsAlpha.setInterpolator(decelerateInterpolator);
+ mStateAnimation.play(itemsAlpha);
+ }
+
+ View pageIndicators = fromView.findViewById(R.id.apps_customize_page_indicator);
+ pageIndicators.setAlpha(1f);
+ ObjectAnimator indicatorsAlpha =
+ LauncherAnimUtils.ofFloat(pageIndicators, "alpha", 0f);
+ indicatorsAlpha.setDuration(revealDuration);
+ indicatorsAlpha.setInterpolator(new DecelerateInterpolator(1.5f));
+ mStateAnimation.play(indicatorsAlpha);
+
+ width = revealView.getMeasuredWidth();
+
+ if (material) {
+ if (!isWidgetTray) {
+ allAppsButton.setVisibility(View.INVISIBLE);
+ }
+ int allAppsButtonSize = LauncherAppState.getInstance().
+ getDynamicGrid().getDeviceProfile().allAppsButtonVisualSize;
+ float finalRadius = isWidgetTray ? 0 : allAppsButtonSize / 2;
+ Animator reveal =
+ LauncherAnimUtils.createCircularReveal(revealView, width / 2,
+ height / 2, revealRadius, finalRadius);
+ reveal.setInterpolator(new LogDecelerateInterpolator(100, 0));
+ reveal.setDuration(revealDuration);
+ reveal.setStartDelay(itemsAlphaStagger);
+
+ reveal.addListener(new AnimatorListenerAdapter() {
+ public void onAnimationEnd(Animator animation) {
+ revealView.setVisibility(View.INVISIBLE);
+ if (!isWidgetTray) {
+ allAppsButton.setVisibility(View.VISIBLE);
+ }
+ }
+ });
+
+ mStateAnimation.play(reveal);
+ }
+
+ dispatchOnLauncherTransitionPrepare(fromView, animated, true);
+ dispatchOnLauncherTransitionPrepare(toView, animated, true);
+ mAppsCustomizeContent.stopScrolling();
+ }
mStateAnimation.addListener(new AnimatorListenerAdapter() {
@Override
@@ -3197,17 +3637,57 @@ public class Launcher extends Activity
if (onCompleteRunnable != null) {
onCompleteRunnable.run();
}
+
+ revealView.setLayerType(View.LAYER_TYPE_NONE, null);
+ if (page != null) {
+ page.setLayerType(View.LAYER_TYPE_NONE, null);
+ }
+ content.setPageBackgroundsVisible(true);
+ // Unhide side pages
+ int count = content.getChildCount();
+ for (int i = 0; i < count; i++) {
+ View child = content.getChildAt(i);
+ child.setVisibility(View.VISIBLE);
+ }
+
+ // Reset page transforms
+ if (page != null) {
+ page.setTranslationX(0);
+ page.setTranslationY(0);
+ page.setAlpha(1);
+ }
+ content.setCurrentPage(content.getNextPage());
+
mAppsCustomizeContent.updateCurrentPageScroll();
}
});
- mStateAnimation.playTogether(scaleAnim, alphaAnim);
- if (workspaceAnim != null) {
- mStateAnimation.play(workspaceAnim);
- }
- dispatchOnLauncherTransitionStart(fromView, animated, true);
- dispatchOnLauncherTransitionStart(toView, animated, true);
- LauncherAnimUtils.startAnimationAfterNextDraw(mStateAnimation, toView);
+ final AnimatorSet stateAnimation = mStateAnimation;
+ final Runnable startAnimRunnable = new Runnable() {
+ public void run() {
+ // Check that mStateAnimation hasn't changed while
+ // we waited for a layout/draw pass
+ if (mStateAnimation != stateAnimation)
+ return;
+ dispatchOnLauncherTransitionStart(fromView, animated, false);
+ dispatchOnLauncherTransitionStart(toView, animated, false);
+
+ if (Utilities.isLmpOrAbove()) {
+ for (int i = 0; i < layerViews.size(); i++) {
+ View v = layerViews.get(i);
+ if (v != null) {
+ boolean attached = true;
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
+ attached = v.isAttachedToWindow();
+ }
+ if (attached) v.buildLayer();
+ }
+ }
+ }
+ mStateAnimation.start();
+ }
+ };
+ fromView.post(startAnimRunnable);
} else {
fromView.setVisibility(View.GONE);
dispatchOnLauncherTransitionPrepare(fromView, animated, true);
@@ -3236,10 +3716,7 @@ public class Launcher extends Activity
}
void showWorkspace(boolean animated, Runnable onCompleteRunnable) {
- if (mWorkspace.isInOverviewMode()) {
- mWorkspace.exitOverviewMode(animated);
- }
- if (mState != State.WORKSPACE) {
+ if (mState != State.WORKSPACE || mWorkspace.getState() != Workspace.State.NORMAL) {
boolean wasInSpringLoadedMode = (mState != State.WORKSPACE);
mWorkspace.setVisibility(View.VISIBLE);
hideAppsCustomizeHelper(Workspace.State.NORMAL, animated, false, onCompleteRunnable);
@@ -3288,7 +3765,13 @@ public class Launcher extends Activity
mAppsCustomizeTabHost.reset();
}
showAppsCustomizeHelper(animated, false, contentType);
- mAppsCustomizeTabHost.requestFocus();
+ mAppsCustomizeTabHost.post(new Runnable() {
+ @Override
+ public void run() {
+ // We post this in-case the all apps view isn't yet constructed.
+ mAppsCustomizeTabHost.requestFocus();
+ }
+ });
// Change the state *after* we've called all the transition code
mState = State.APPS_CUSTOMIZE;
@@ -3328,7 +3811,6 @@ public class Launcher extends Activity
}
}
}, delay);
-
}
void exitSpringLoadedDragMode() {
@@ -3350,25 +3832,6 @@ public class Launcher extends Activity
}
/**
- * Shows the hotseat area.
- */
- void showHotseat(boolean animated) {
- if (!LauncherAppState.getInstance().isScreenLarge()) {
- if (animated) {
- if (mHotseat.getAlpha() != 1f) {
- int duration = 0;
- if (mSearchDropTargetBar != null) {
- duration = mSearchDropTargetBar.getTransitionInDuration();
- }
- mHotseat.animate().alpha(1f).setDuration(duration);
- }
- } else {
- mHotseat.setAlpha(1f);
- }
- }
- }
-
- /**
* Hides the hotseat area.
*/
void hideHotseat(boolean animated) {
@@ -3621,44 +4084,6 @@ public class Launcher extends Activity
public void disableVoiceButtonProxy(boolean disabled) {
updateVoiceButtonProxyVisible(disabled);
}
- /**
- * Sets the app market icon
- */
- private void updateAppMarketIcon() {
- if (!DISABLE_MARKET_BUTTON) {
- final View marketButton = findViewById(R.id.market_button);
- Intent intent = new Intent(Intent.ACTION_MAIN).addCategory(Intent.CATEGORY_APP_MARKET);
- // Find the app market activity by resolving an intent.
- // (If multiple app markets are installed, it will return the ResolverActivity.)
- ComponentName activityName = intent.resolveActivity(getPackageManager());
- if (activityName != null) {
- int coi = getCurrentOrientationIndexForGlobalIcons();
- mAppMarketIntent = intent;
- sAppMarketIcon[coi] = updateTextButtonWithIconFromExternalActivity(
- R.id.market_button, activityName, R.drawable.ic_launcher_market_holo,
- TOOLBAR_ICON_METADATA_NAME);
- marketButton.setVisibility(View.VISIBLE);
- } else {
- // We should hide and disable the view so that we don't try and restore the visibility
- // of it when we swap between drag & normal states from IconDropTarget subclasses.
- marketButton.setVisibility(View.GONE);
- marketButton.setEnabled(false);
- }
- }
- }
-
- private void updateAppMarketIcon(Drawable.ConstantState d) {
- if (!DISABLE_MARKET_BUTTON) {
- // Ensure that the new drawable we are creating has the approprate toolbar icon bounds
- Resources r = getResources();
- Drawable marketIconDrawable = d.newDrawable(r);
- int w = r.getDimensionPixelSize(R.dimen.toolbar_external_icon_width);
- int h = r.getDimensionPixelSize(R.dimen.toolbar_external_icon_height);
- marketIconDrawable.setBounds(0, 0, w, h);
-
- updateTextButtonWithDrawable(R.id.market_button, marketIconDrawable);
- }
- }
@Override
public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
@@ -3667,7 +4092,7 @@ public class Launcher extends Activity
text.clear();
// Populate event with a fake title based on the current state.
if (mState == State.APPS_CUSTOMIZE) {
- text.add(mAppsCustomizeTabHost.getCurrentTabView().getContentDescription());
+ text.add(mAppsCustomizeTabHost.getContentTag());
} else {
text.add(getString(R.string.all_apps_home_button_label));
}
@@ -3775,7 +4200,7 @@ public class Launcher extends Activity
* Implementation of the method from LauncherModel.Callbacks.
*/
public void startBinding() {
- mWorkspaceLoading = true;
+ setWorkspaceLoading(true);
// If we're starting binding all over again, clear any bind calls we'd postponed in
// the past (see waitUntilResume) -- we don't need them since we're starting binding
@@ -3875,7 +4300,7 @@ public class Launcher extends Activity
}
// Remove the extra empty screen
- mWorkspace.removeExtraEmptyScreen(false, null);
+ mWorkspace.removeExtraEmptyScreen(false, false);
if (!LauncherAppState.isDisableAllApps() &&
addedApps != null && mAppsCustomizeContent != null) {
@@ -3926,7 +4351,15 @@ public class Launcher extends Activity
if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
CellLayout cl = mWorkspace.getScreenWithId(item.screenId);
if (cl != null && cl.isOccupied(item.cellX, item.cellY)) {
- throw new RuntimeException("OCCUPIED");
+ View v = cl.getChildAt(item.cellX, item.cellY);
+ Object tag = v.getTag();
+ String desc = "Collision while binding workspace item: " + item
+ + ". Collides with " + tag;
+ if (LauncherAppState.isDogfoodBuild()) {
+ throw (new RuntimeException(desc));
+ } else {
+ Log.d(TAG, desc);
+ }
}
}
@@ -4021,13 +4454,74 @@ public class Launcher extends Activity
}
final Workspace workspace = mWorkspace;
- final int appWidgetId = item.appWidgetId;
- final AppWidgetProviderInfo appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
- if (DEBUG_WIDGETS) {
- Log.d(TAG, "bindAppWidget: id=" + item.appWidgetId + " belongs to component " + appWidgetInfo.provider);
+ AppWidgetProviderInfo appWidgetInfo;
+ if (((item.restoreStatus & LauncherAppWidgetInfo.FLAG_PROVIDER_NOT_READY) == 0) &&
+ ((item.restoreStatus & LauncherAppWidgetInfo.FLAG_ID_NOT_VALID) != 0)) {
+
+ appWidgetInfo = mModel.findAppWidgetProviderInfoWithComponent(this, item.providerName);
+ if (appWidgetInfo == null) {
+ if (DEBUG_WIDGETS) {
+ Log.d(TAG, "Removing restored widget: id=" + item.appWidgetId
+ + " belongs to component " + item.providerName
+ + ", as the povider is null");
+ }
+ LauncherModel.deleteItemFromDatabase(this, item);
+ return;
+ }
+ // Note: This assumes that the id remap broadcast is received before this step.
+ // If that is not the case, the id remap will be ignored and user may see the
+ // click to setup view.
+ PendingAddWidgetInfo pendingInfo = new PendingAddWidgetInfo(appWidgetInfo, null, null);
+ pendingInfo.spanX = item.spanX;
+ pendingInfo.spanY = item.spanY;
+ pendingInfo.minSpanX = item.minSpanX;
+ pendingInfo.minSpanY = item.minSpanY;
+ Bundle options =
+ AppsCustomizePagedView.getDefaultOptionsForWidget(this, pendingInfo);
+
+ int newWidgetId = mAppWidgetHost.allocateAppWidgetId();
+ boolean success = mAppWidgetManager.bindAppWidgetIdIfAllowed(
+ newWidgetId, appWidgetInfo, options);
+
+ // TODO consider showing a permission dialog when the widget is clicked.
+ if (!success) {
+ mAppWidgetHost.deleteAppWidgetId(newWidgetId);
+ if (DEBUG_WIDGETS) {
+ Log.d(TAG, "Removing restored widget: id=" + item.appWidgetId
+ + " belongs to component " + item.providerName
+ + ", as the launcher is unable to bing a new widget id");
+ }
+ LauncherModel.deleteItemFromDatabase(this, item);
+ return;
+ }
+
+ item.appWidgetId = newWidgetId;
+
+ // If the widget has a configure activity, it is still needs to set it up, otherwise
+ // the widget is ready to go.
+ item.restoreStatus = (appWidgetInfo.configure == null)
+ ? LauncherAppWidgetInfo.RESTORE_COMPLETED
+ : LauncherAppWidgetInfo.FLAG_UI_NOT_READY;
+
+ LauncherModel.updateItemInDatabase(this, item);
}
- item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
+ if (item.restoreStatus == LauncherAppWidgetInfo.RESTORE_COMPLETED) {
+ final int appWidgetId = item.appWidgetId;
+ appWidgetInfo = mAppWidgetManager.getAppWidgetInfo(appWidgetId);
+ if (DEBUG_WIDGETS) {
+ Log.d(TAG, "bindAppWidget: id=" + item.appWidgetId + " belongs to component " + appWidgetInfo.provider);
+ }
+
+ item.hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
+ } else {
+ appWidgetInfo = null;
+ PendingAppWidgetHostView view = new PendingAppWidgetHostView(this, item);
+ view.updateIcon(mIconCache);
+ item.hostView = view;
+ item.hostView.updateAppWidget(null);
+ item.hostView.setOnClickListener(this);
+ }
item.hostView.setTag(item);
item.onBindAppWidget(this);
@@ -4044,6 +4538,26 @@ public class Launcher extends Activity
}
}
+ /**
+ * Restores a pending widget.
+ *
+ * @param appWidgetId The app widget id
+ * @param cellInfo The position on screen where to create the widget.
+ */
+ private void completeRestoreAppWidget(final int appWidgetId) {
+ LauncherAppWidgetHostView view = mWorkspace.getWidgetForAppWidgetId(appWidgetId);
+ if ((view == null) || !(view instanceof PendingAppWidgetHostView)) {
+ Log.e(TAG, "Widget update called, when the widget no longer exists.");
+ return;
+ }
+
+ LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) view.getTag();
+ info.restoreStatus = LauncherAppWidgetInfo.RESTORE_COMPLETED;
+
+ mWorkspace.reinflateWidgetsIfNecessary();
+ LauncherModel.updateItemInDatabase(this, info);
+ }
+
public void onPageBoundSynchronously(int page) {
mSynchronouslyBoundPages.add(page);
}
@@ -4071,24 +4585,44 @@ public class Launcher extends Activity
mWorkspace.restoreInstanceStateForRemainingPages();
+ setWorkspaceLoading(false);
+ sendLoadingCompleteBroadcastIfNecessary();
+
// If we received the result of any pending adds while the loader was running (e.g. the
// widget configuration forced an orientation change), process them now.
- for (int i = 0; i < sPendingAddList.size(); i++) {
- completeAdd(sPendingAddList.get(i));
- }
- sPendingAddList.clear();
+ if (sPendingAddItem != null) {
+ final long screenId = completeAdd(sPendingAddItem);
- // Update the market app icon as necessary (the other icons will be managed in response to
- // package changes in bindSearchablesChanged()
- if (!DISABLE_MARKET_BUTTON) {
- updateAppMarketIcon();
+ // TODO: this moves the user to the page where the pending item was added. Ideally,
+ // the screen would be guaranteed to exist after bind, and the page would be set through
+ // the workspace restore process.
+ mWorkspace.post(new Runnable() {
+ @Override
+ public void run() {
+ mWorkspace.snapToScreenId(screenId);
+ }
+ });
+ sPendingAddItem = null;
}
- mWorkspaceLoading = false;
if (upgradePath) {
mWorkspace.getUniqueComponents(true, null);
mIntentsOnWorkspaceFromUpgradePath = mWorkspace.getUniqueComponents(true, null);
}
+ PackageInstallerCompat.getInstance(this).onFinishBind();
+ mModel.recheckRestoredItems(this);
+ }
+
+ private void sendLoadingCompleteBroadcastIfNecessary() {
+ if (!mSharedPrefs.getBoolean(FIRST_LOAD_COMPLETE, false)) {
+ String permission =
+ getResources().getString(R.string.receive_first_load_broadcast_permission);
+ Intent intent = new Intent(ACTION_FIRST_LOAD_COMPLETE);
+ sendBroadcast(intent, permission);
+ SharedPreferences.Editor editor = mSharedPrefs.edit();
+ editor.putBoolean(FIRST_LOAD_COMPLETE, true);
+ editor.apply();
+ }
}
public boolean isAllAppsButtonRank(int rank) {
@@ -4176,7 +4710,7 @@ public class Launcher extends Activity
}
if (mWorkspace != null) {
- mWorkspace.updateShortcuts(apps);
+ mWorkspace.updateShortcutsAndWidgets(apps);
}
if (!LauncherAppState.isDisableAllApps() &&
@@ -4186,6 +4720,48 @@ public class Launcher extends Activity
}
/**
+ * Packages were restored
+ */
+ public void bindAppsRestored(final ArrayList<AppInfo> apps) {
+ Runnable r = new Runnable() {
+ public void run() {
+ bindAppsRestored(apps);
+ }
+ };
+ if (waitUntilResume(r)) {
+ return;
+ }
+
+ if (mWorkspace != null) {
+ mWorkspace.updateShortcutsAndWidgets(apps);
+ }
+ }
+
+ /**
+ * Update the state of a package, typically related to install state.
+ *
+ * Implementation of the method from LauncherModel.Callbacks.
+ */
+ @Override
+ public void updatePackageState(ArrayList<PackageInstallInfo> installInfo) {
+ if (mWorkspace != null) {
+ mWorkspace.updatePackageState(installInfo);
+ }
+ }
+
+ /**
+ * Update the label and icon of all the icons in a package
+ *
+ * Implementation of the method from LauncherModel.Callbacks.
+ */
+ @Override
+ public void updatePackageBadge(String packageName) {
+ if (mWorkspace != null) {
+ mWorkspace.updatePackageBadge(packageName, UserHandleCompat.myUserHandle());
+ }
+ }
+
+ /**
* A package was uninstalled. We take both the super set of packageNames
* in addition to specific applications to remove, the reason being that
* this can be called when a package is updated as well. In that scenario,
@@ -4195,10 +4771,10 @@ public class Launcher extends Activity
* Implementation of the method from LauncherModel.Callbacks.
*/
public void bindComponentsRemoved(final ArrayList<String> packageNames,
- final ArrayList<AppInfo> appInfos) {
+ final ArrayList<AppInfo> appInfos, final UserHandleCompat user) {
Runnable r = new Runnable() {
public void run() {
- bindComponentsRemoved(packageNames, appInfos);
+ bindComponentsRemoved(packageNames, appInfos, user);
}
};
if (waitUntilResume(r)) {
@@ -4206,10 +4782,10 @@ public class Launcher extends Activity
}
if (!packageNames.isEmpty()) {
- mWorkspace.removeItemsByPackageName(packageNames);
+ mWorkspace.removeItemsByPackageName(packageNames, user);
}
if (!appInfos.isEmpty()) {
- mWorkspace.removeItemsByApplicationInfo(appInfos);
+ mWorkspace.removeItemsByApplicationInfo(appInfos, user);
}
// Notify the drag controller
@@ -4307,7 +4883,7 @@ public class Launcher extends Activity
* @param hint the hint to be displayed in the search bar.
*/
protected void onSearchBarHintChanged(String hint) {
- mLauncherClings.updateSearchBarHint(hint);
+
}
protected boolean isLauncherPreinstalled() {
@@ -4325,6 +4901,17 @@ public class Launcher extends Activity
}
}
+ /**
+ * This method indicates whether or not we should suggest default wallpaper dimensions
+ * when our wallpaper cropper was not yet used to set a wallpaper.
+ */
+ protected boolean overrideWallpaperDimensions() {
+ return true;
+ }
+
+ protected boolean shouldClingFocusHotseatApp() {
+ return false;
+ }
protected String getFirstRunClingSearchBarHint() {
return "";
}
@@ -4347,23 +4934,19 @@ public class Launcher extends Activity
return "";
}
- public void dismissFirstRunCling(View v) {
- mLauncherClings.dismissFirstRunCling(v);
- }
- public void dismissMigrationClingCopyApps(View v) {
- mLauncherClings.dismissMigrationClingCopyApps(v);
- }
- public void dismissMigrationClingUseDefault(View v) {
- mLauncherClings.dismissMigrationClingUseDefault(v);
- }
- public void dismissMigrationWorkspaceCling(View v) {
- mLauncherClings.dismissMigrationWorkspaceCling(v);
- }
- public void dismissWorkspaceCling(View v) {
- mLauncherClings.dismissWorkspaceCling(v);
+ /**
+ * To be overridden by subclasses to indicate that there is an activity to launch
+ * before showing the standard launcher experience.
+ */
+ protected boolean hasFirstRunActivity() {
+ return false;
}
- public void dismissFolderCling(View v) {
- mLauncherClings.dismissFolderCling(v);
+
+ /**
+ * To be overridden by subclasses to launch any first run activity
+ */
+ protected Intent getFirstRunActivity() {
+ return null;
}
private boolean shouldRunFirstRunActivity() {
@@ -4371,15 +4954,21 @@ public class Launcher extends Activity
!mSharedPrefs.getBoolean(FIRST_RUN_ACTIVITY_DISPLAYED, false);
}
- public void showFirstRunActivity() {
+ protected boolean hasRunFirstRunActivity() {
+ return mSharedPrefs.getBoolean(FIRST_RUN_ACTIVITY_DISPLAYED, false);
+ }
+
+ public boolean showFirstRunActivity() {
if (shouldRunFirstRunActivity() &&
hasFirstRunActivity()) {
Intent firstRunIntent = getFirstRunActivity();
if (firstRunIntent != null) {
startActivity(firstRunIntent);
markFirstRunActivityShown();
+ return true;
}
}
+ return false;
}
private void markFirstRunActivityShown() {
@@ -4388,6 +4977,77 @@ public class Launcher extends Activity
editor.apply();
}
+ /**
+ * To be overridden by subclasses to indicate that there is an in-activity full-screen intro
+ * screen that must be displayed and dismissed.
+ */
+ protected boolean hasDismissableIntroScreen() {
+ return false;
+ }
+
+ /**
+ * Full screen intro screen to be shown and dismissed before the launcher can be used.
+ */
+ protected View getIntroScreen() {
+ return null;
+ }
+
+ /**
+ * To be overriden by subclasses to indicate whether the in-activity intro screen has been
+ * dismissed. This method is ignored if #hasDismissableIntroScreen returns false.
+ */
+ private boolean shouldShowIntroScreen() {
+ return hasDismissableIntroScreen() &&
+ !mSharedPrefs.getBoolean(INTRO_SCREEN_DISMISSED, false);
+ }
+
+ protected void showIntroScreen() {
+ View introScreen = getIntroScreen();
+ changeWallpaperVisiblity(false);
+ if (introScreen != null) {
+ mDragLayer.showOverlayView(introScreen);
+ }
+ }
+
+ public void dismissIntroScreen() {
+ markIntroScreenDismissed();
+ if (showFirstRunActivity()) {
+ // We delay hiding the intro view until the first run activity is showing. This
+ // avoids a blip.
+ mWorkspace.postDelayed(new Runnable() {
+ @Override
+ public void run() {
+ mDragLayer.dismissOverlayView();
+ showFirstRunClings();
+ }
+ }, ACTIVITY_START_DELAY);
+ } else {
+ mDragLayer.dismissOverlayView();
+ showFirstRunClings();
+ }
+ changeWallpaperVisiblity(true);
+ }
+
+ private void markIntroScreenDismissed() {
+ SharedPreferences.Editor editor = mSharedPrefs.edit();
+ editor.putBoolean(INTRO_SCREEN_DISMISSED, true);
+ editor.apply();
+ }
+
+ private void showFirstRunClings() {
+ // The two first run cling paths are mutually exclusive, if the launcher is preinstalled
+ // on the device, then we always show the first run cling experience (or if there is no
+ // launcher2). Otherwise, we prompt the user upon started for migration
+ LauncherClings launcherClings = new LauncherClings(this);
+ if (launcherClings.shouldShowFirstRunOrMigrationClings()) {
+ if (mModel.canMigrateFromOldLauncherDb(this)) {
+ launcherClings.showMigrationCling();
+ } else {
+ launcherClings.showLongPressCling(true);
+ }
+ }
+ }
+
void showWorkspaceSearchAndHotseat() {
if (mWorkspace != null) mWorkspace.setAlpha(1f);
if (mHotseat != null) mHotseat.setAlpha(1f);
@@ -4402,24 +5062,44 @@ public class Launcher extends Activity
if (mSearchDropTargetBar != null) mSearchDropTargetBar.hideSearchBar(false);
}
-
public ItemInfo createAppDragInfo(Intent appLaunchIntent) {
- ResolveInfo ri = getPackageManager().resolveActivity(appLaunchIntent, 0);
- if (ri == null) {
+ // Called from search suggestion, not supported in other profiles.
+ final UserHandleCompat myUser = UserHandleCompat.myUserHandle();
+ LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(this);
+ LauncherActivityInfoCompat activityInfo = launcherApps.resolveActivity(appLaunchIntent,
+ myUser);
+ if (activityInfo == null) {
return null;
}
- return new AppInfo(getPackageManager(), ri, mIconCache, null);
+ return new AppInfo(this, activityInfo, myUser, mIconCache, null);
}
public ItemInfo createShortcutDragInfo(Intent shortcutIntent, CharSequence caption,
Bitmap icon) {
- return new ShortcutInfo(shortcutIntent, caption, icon);
+ // Called from search suggestion, not supported in other profiles.
+ return createShortcutDragInfo(shortcutIntent, caption, icon,
+ UserHandleCompat.myUserHandle());
+ }
+
+ public ItemInfo createShortcutDragInfo(Intent shortcutIntent, CharSequence caption,
+ Bitmap icon, UserHandleCompat user) {
+ UserManagerCompat userManager = UserManagerCompat.getInstance(this);
+ CharSequence contentDescription = userManager.getBadgedLabelForUser(caption, user);
+ return new ShortcutInfo(shortcutIntent, caption, contentDescription, icon, user);
+ }
+
+ protected void moveWorkspaceToDefaultScreen() {
+ mWorkspace.moveToDefaultScreen(false);
}
public void startDrag(View dragView, ItemInfo dragInfo, DragSource source) {
dragView.setTag(dragInfo);
- mWorkspace.onDragStartedWithItem(dragView);
- mWorkspace.beginDragShared(dragView, source);
+ mWorkspace.onExternalDragStartedWithItem(dragView);
+ mWorkspace.beginExternalDragShared(dragView, source);
+ }
+
+ @Override
+ public void onPageSwitch(View newPage, int newPageIndex) {
}
/**