diff options
Diffstat (limited to 'src/com')
-rw-r--r-- | src/com/android/launcher3/CellLayout.java | 2 | ||||
-rw-r--r-- | src/com/android/launcher3/Launcher.java | 237 | ||||
-rw-r--r-- | src/com/android/launcher3/LauncherAppState.java | 1 | ||||
-rw-r--r-- | src/com/android/launcher3/LauncherAppWidgetHost.java | 15 | ||||
-rw-r--r-- | src/com/android/launcher3/LauncherAppWidgetHostView.java | 4 | ||||
-rw-r--r-- | src/com/android/launcher3/LauncherModel.java | 15 | ||||
-rw-r--r-- | src/com/android/launcher3/LauncherProvider.java | 13 | ||||
-rw-r--r-- | src/com/android/launcher3/LauncherProviderChangeListener.java | 2 | ||||
-rw-r--r-- | src/com/android/launcher3/PagedView.java | 6 | ||||
-rw-r--r-- | src/com/android/launcher3/PinchAnimationManager.java | 8 | ||||
-rw-r--r-- | src/com/android/launcher3/QsbContainerView.java | 271 | ||||
-rw-r--r-- | src/com/android/launcher3/SearchDropTargetBar.java | 75 | ||||
-rw-r--r-- | src/com/android/launcher3/Utilities.java | 36 | ||||
-rw-r--r-- | src/com/android/launcher3/Workspace.java | 61 | ||||
-rw-r--r-- | src/com/android/launcher3/model/GridSizeMigrationTask.java | 38 | ||||
-rw-r--r-- | src/com/android/launcher3/util/GridOccupancy.java | 2 |
16 files changed, 430 insertions, 356 deletions
diff --git a/src/com/android/launcher3/CellLayout.java b/src/com/android/launcher3/CellLayout.java index e3bb5fa72..cfaa6a34d 100644 --- a/src/com/android/launcher3/CellLayout.java +++ b/src/com/android/launcher3/CellLayout.java @@ -2708,7 +2708,7 @@ public class CellLayout extends ViewGroup implements BubbleTextShadowHandler { /** * Indicates whether this item can be reordered. Always true except in the case of the - * the AllApps button. + * the AllApps button and QSB place holder. */ public boolean canReorder = true; diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 13e451a27..6a95a9092 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -45,20 +45,16 @@ import android.content.IntentSender; import android.content.SharedPreferences; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.content.pm.ActivityInfo; -import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; -import android.content.pm.PackageManager.NameNotFoundException; import android.content.res.Configuration; import android.database.sqlite.SQLiteDatabase; 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.ColorDrawable; import android.graphics.drawable.Drawable; -import android.net.Uri; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; @@ -113,7 +109,6 @@ import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.logging.LoggerUtils; import com.android.launcher3.logging.UserEventDispatcher; import com.android.launcher3.model.WidgetsModel; -import com.android.launcher3.pageindicators.PageIndicator; import com.android.launcher3.pageindicators.PageIndicatorLine; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.util.ComponentKey; @@ -183,6 +178,9 @@ public class Launcher extends Activity static final String INTENT_EXTRA_IGNORE_LAUNCH_ANIMATION = "com.android.launcher3.intent.extra.shortcut.INGORE_LAUNCH_ANIMATION"; + public static final String ACTION_APPWIDGET_HOST_RESET = + "com.android.launcher3.intent.ACTION_APPWIDGET_HOST_RESET"; + // Type: int private static final String RUNTIME_STATE_CURRENT_SCREEN = "launcher.current_screen"; // Type: int @@ -197,9 +195,6 @@ public class Launcher extends Activity static final String INTRO_SCREEN_DISMISSED = "launcher.intro_screen_dismissed"; static final String FIRST_RUN_ACTIVITY_DISPLAYED = "launcher.first_run_activity_displayed"; - private static final String QSB_WIDGET_ID = "qsb_widget_id"; - private static final String QSB_WIDGET_PROVIDER = "qsb_widget_provider"; - /** The different states that Launcher can be in. */ enum State { NONE, WORKSPACE, WORKSPACE_SPRING_LOADED, APPS, APPS_SPRING_LOADED, WIDGETS, WIDGETS_SPRING_LOADED } @@ -219,8 +214,19 @@ public class Launcher extends Activity private static int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5; @Thunk static int NEW_APPS_ANIMATION_DELAY = 500; - private final BroadcastReceiver mCloseSystemDialogsReceiver - = new CloseSystemDialogsIntentReceiver(); + private final BroadcastReceiver mUiBroadcastReceiver = new BroadcastReceiver() { + + @Override + public void onReceive(Context context, Intent intent) { + if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) { + closeSystemDialogs(); + } else if (ACTION_APPWIDGET_HOST_RESET.equals(intent.getAction())) { + if (mAppWidgetHost != null) { + mAppWidgetHost.startListening(); + } + } + } + }; @Thunk Workspace mWorkspace; private View mLauncherView; @@ -255,8 +261,6 @@ public class Launcher extends Activity @Thunk WidgetsContainerView mWidgetsView; @Thunk WidgetsModel mWidgetsModel; - private AppWidgetHostView mQsb; - private Bundle mSavedState; // We set the state in both onCreate and then onNewIntent in some cases, which causes both // scroll issues (because the workspace may not have been measured yet) and extra work. @@ -464,7 +468,8 @@ public class Launcher extends Activity Selection.setSelection(mDefaultKeySsb, 0); IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS); - registerReceiver(mCloseSystemDialogsReceiver, filter); + filter.addAction(ACTION_APPWIDGET_HOST_RESET); + registerReceiver(mUiBroadcastReceiver, filter); mRotationEnabled = getResources().getBoolean(R.bool.allow_rotation); // In case we are on a device with locked rotation, we should look at preferences to check @@ -739,10 +744,6 @@ public class Launcher extends Activity } else if (resultCode == RESULT_OK) { addAppWidgetImpl(appWidgetId, mPendingAddInfo, null, mPendingAddWidgetInfo, ON_ACTIVITY_RESULT_ANIMATION_DELAY); - - // When the user has granted permission to bind widgets, we should check to see if - // we can inflate the default search bar widget. - getOrCreateQsbBar(); } return; } else if (requestCode == REQUEST_PICK_WALLPAPER) { @@ -1056,7 +1057,6 @@ public class Launcher extends Activity if (!isWorkspaceLoading()) { getWorkspace().reinflateWidgetsIfNecessary(); } - reinflateQSBIfNecessary(); if (DEBUG_RESUME_TIME) { Log.d(TAG, "Time spent in onResume: " + (System.currentTimeMillis() - startTime)); @@ -1368,6 +1368,7 @@ public class Launcher extends Activity mWorkspace.setHapticFeedbackEnabled(false); mWorkspace.setOnLongClickListener(this); mWorkspace.setup(dragController); + mWorkspace.bindAndInitFirstWorkspaceScreen(null /* recycled qsb */); dragController.addDragListener(mWorkspace); // Get the search/delete/uninstall bar @@ -1393,7 +1394,6 @@ public class Launcher extends Activity dragController.addDropTarget(mWorkspace); if (mSearchDropTargetBar != null) { mSearchDropTargetBar.setup(this, dragController); - mSearchDropTargetBar.setQsbSearchBar(getOrCreateQsbBar()); } if (mAppInfoDropTargetBar != null) { mAppInfoDropTargetBar.setup(this, dragController); @@ -1999,7 +1999,7 @@ public class Launcher extends Activity ((AccessibilityManager) getSystemService(ACCESSIBILITY_SERVICE)) .removeAccessibilityStateChangeListener(this); - unregisterReceiver(mCloseSystemDialogsReceiver); + unregisterReceiver(mUiBroadcastReceiver); LauncherAnimUtils.onDestroyActivity(); @@ -2052,10 +2052,9 @@ public class Launcher extends Activity appSearchData = new Bundle(); appSearchData.putString("source", "launcher-search"); } - Rect sourceBounds = new Rect(); - if (mSearchDropTargetBar != null) { - sourceBounds = mSearchDropTargetBar.getSearchBarBounds(); - } + + // TODO send proper bounds. + Rect sourceBounds = null; boolean clearTextImmediately = startSearch(initialQuery, selectInitialQuery, appSearchData, sourceBounds); @@ -2465,19 +2464,6 @@ public class Launcher extends Activity } /** - * Re-listen when widget host is reset. - */ - @Override - public void onAppWidgetHostReset() { - if (mAppWidgetHost != null) { - mAppWidgetHost.startListening(); - } - - // Recreate the QSB, as the widget has been reset. - bindSearchProviderChanged(); - } - - /** * Launches the intent referred by the clicked shortcut. * * @param v The view representing the clicked shortcut. @@ -3497,105 +3483,6 @@ public class Launcher extends Activity return (mLauncherCallbacks != null && mLauncherCallbacks.providesSearch()); } - public View getOrCreateQsbBar() { - if (launcherCallbacksProvidesSearch()) { - return mLauncherCallbacks.getQsbBar(); - } - - if (mQsb == null) { - AppWidgetProviderInfo searchProvider = Utilities.getSearchWidgetProvider(this); - if (searchProvider == null) { - return null; - } - - Bundle opts = new Bundle(); - opts.putInt(AppWidgetManager.OPTION_APPWIDGET_HOST_CATEGORY, - AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX); - - // Determine the min and max dimensions of the widget. - LauncherAppState app = LauncherAppState.getInstance(); - DeviceProfile portraitProfile = app.getInvariantDeviceProfile().portraitProfile; - DeviceProfile landscapeProfile = app.getInvariantDeviceProfile().landscapeProfile; - float density = getResources().getDisplayMetrics().density; - Point searchDimens = portraitProfile.getSearchBarDimensForWidgetOpts(getResources()); - int maxHeight = (int) (searchDimens.y / density); - int minHeight = maxHeight; - int maxWidth = (int) (searchDimens.x / density); - int minWidth = maxWidth; - if (!landscapeProfile.isVerticalBarLayout()) { - searchDimens = landscapeProfile.getSearchBarDimensForWidgetOpts(getResources()); - maxHeight = (int) Math.max(maxHeight, searchDimens.y / density); - minHeight = (int) Math.min(minHeight, searchDimens.y / density); - maxWidth = (int) Math.max(maxWidth, searchDimens.x / density); - minWidth = (int) Math.min(minWidth, searchDimens.x / density); - } - opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, maxHeight); - opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, minHeight); - opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, maxWidth); - opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, minWidth); - if (LOGD) { - Log.d(TAG, "QSB widget options: maxHeight=" + maxHeight + " minHeight=" + minHeight - + " maxWidth=" + maxWidth + " minWidth=" + minWidth); - } - - if (mLauncherCallbacks != null) { - opts.putAll(mLauncherCallbacks.getAdditionalSearchWidgetOptions()); - } - - int widgetId = mSharedPrefs.getInt(QSB_WIDGET_ID, -1); - AppWidgetProviderInfo widgetInfo = mAppWidgetManager.getAppWidgetInfo(widgetId); - if (!searchProvider.provider.flattenToString().equals( - mSharedPrefs.getString(QSB_WIDGET_PROVIDER, null)) - || (widgetInfo == null) - || !widgetInfo.provider.equals(searchProvider.provider)) { - // A valid widget is not already bound. - if (widgetId > -1) { - mAppWidgetHost.deleteAppWidgetId(widgetId); - widgetId = -1; - } - - // Try to bind a new widget - widgetId = mAppWidgetHost.allocateAppWidgetId(); - - if (!AppWidgetManagerCompat.getInstance(this) - .bindAppWidgetIdIfAllowed(widgetId, searchProvider, opts)) { - mAppWidgetHost.deleteAppWidgetId(widgetId); - widgetId = -1; - } - - mSharedPrefs.edit() - .putInt(QSB_WIDGET_ID, widgetId) - .putString(QSB_WIDGET_PROVIDER, searchProvider.provider.flattenToString()) - .apply(); - } - - mAppWidgetHost.setQsbWidgetId(widgetId); - if (widgetId != -1) { - mQsb = mAppWidgetHost.createView(this, widgetId, searchProvider); - mQsb.setId(R.id.qsb_widget); - if (!Utilities.containsAll( - AppWidgetManager.getInstance(this).getAppWidgetOptions(widgetId), opts)) { - // Launcher should not be updating the options often. - FileLog.d(TAG, "Options for QSB were not same"); - mQsb.updateAppWidgetOptions(opts); - } - mQsb.setPadding(0, 0, 0, 0); - mSearchDropTargetBar.addView(mQsb); - mSearchDropTargetBar.setQsbSearchBar(mQsb); - } - } - return mQsb; - } - - private void reinflateQSBIfNecessary() { - if (mQsb instanceof LauncherAppWidgetHostView && - ((LauncherAppWidgetHostView) mQsb).isReinflateRequired()) { - mSearchDropTargetBar.removeView(mQsb); - mQsb = null; - mSearchDropTargetBar.setQsbSearchBar(getOrCreateQsbBar()); - } - } - @Override public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { final boolean result = super.dispatchPopulateAccessibilityEvent(event); @@ -3615,16 +3502,6 @@ public class Launcher extends Activity } /** - * Receives notifications when system dialogs are to be closed. - */ - @Thunk class CloseSystemDialogsIntentReceiver extends BroadcastReceiver { - @Override - public void onReceive(Context context, Intent intent) { - closeSystemDialogs(); - } - } - - /** * If the activity is currently paused, signal that we need to run the passed Runnable * in onResume. * @@ -3729,12 +3606,13 @@ public class Launcher extends Activity @Override public void bindScreens(ArrayList<Long> orderedScreenIds) { - bindAddScreens(orderedScreenIds); - - // If there are no screens, we need to have an empty screen - if (orderedScreenIds.size() == 0) { - mWorkspace.addExtraEmptyScreen(); + // Make sure the first screen is always at the start. + if (orderedScreenIds.indexOf(Workspace.FIRST_SCREEN_ID) != 0) { + orderedScreenIds.remove(Workspace.FIRST_SCREEN_ID); + orderedScreenIds.add(0, Workspace.FIRST_SCREEN_ID); + mModel.updateWorkspaceScreenOrder(this, orderedScreenIds); } + bindAddScreens(orderedScreenIds); // Create the custom content page (this call updates mDefaultScreen which calls // setCurrentPage() so ensure that all pages are added before calling this). @@ -3744,11 +3622,14 @@ public class Launcher extends Activity } } - @Override - public void bindAddScreens(ArrayList<Long> orderedScreenIds) { + private void bindAddScreens(ArrayList<Long> orderedScreenIds) { int count = orderedScreenIds.size(); for (int i = 0; i < count; i++) { - mWorkspace.insertNewWorkspaceScreenBeforeEmptyScreen(orderedScreenIds.get(i)); + long screenId = orderedScreenIds.get(i); + if (screenId != Workspace.FIRST_SCREEN_ID) { + // No need to bind the first screen, as its always bound. + mWorkspace.insertNewWorkspaceScreenBeforeEmptyScreen(screenId); + } } } @@ -3827,24 +3708,6 @@ public class Launcher extends Activity case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT: ShortcutInfo info = (ShortcutInfo) item; view = createShortcut(info); - - /* - * TODO: FIX collision case - */ - if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) { - CellLayout cl = mWorkspace.getScreenWithId(item.screenId); - if (cl != null && cl.isOccupied(item.cellX, item.cellY)) { - View v = cl.getChildAt(item.cellX, item.cellY); - Object tag = v.getTag(); - String desc = "Collision while binding workspace item: " + item - + ". Collides with " + tag; - if (ProviderConfig.IS_DOGFOOD_BUILD) { - throw (new RuntimeException(desc)); - } else { - Log.d(TAG, desc); - } - } - } break; case LauncherSettings.Favorites.ITEM_TYPE_FOLDER: view = FolderIcon.fromXml(R.layout.folder_icon, this, @@ -3855,6 +3718,25 @@ public class Launcher extends Activity throw new RuntimeException("Invalid Item Type"); } + /* + * Remove colliding items. + */ + if (item.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) { + CellLayout cl = mWorkspace.getScreenWithId(item.screenId); + if (cl != null && cl.isOccupied(item.cellX, item.cellY)) { + View v = cl.getChildAt(item.cellX, item.cellY); + Object tag = v.getTag(); + String desc = "Collision while binding workspace item: " + item + + ". Collides with " + tag; + if (ProviderConfig.IS_DOGFOOD_BUILD) { + throw (new RuntimeException(desc)); + } else { + Log.d(TAG, desc); + LauncherModel.deleteItemFromDatabase(this, item); + continue; + } + } + } workspace.addInScreenFromBind(view, item.container, item.screenId, item.cellX, item.cellY, 1, 1); if (animateIcons) { @@ -4154,17 +4036,6 @@ public class Launcher extends Activity return LauncherCallbacks.SEARCH_BAR_HEIGHT_NORMAL; } - public void bindSearchProviderChanged() { - if (mSearchDropTargetBar == null) { - return; - } - if (mQsb != null) { - mSearchDropTargetBar.removeView(mQsb); - mQsb = null; - } - mSearchDropTargetBar.setQsbSearchBar(getOrCreateQsbBar()); - } - /** * A runnable that we can dequeue and re-enqueue when all applications are bound (to prevent * multiple calls to bind the same list.) diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java index 9d889e09a..ae3abbf61 100644 --- a/src/com/android/launcher3/LauncherAppState.java +++ b/src/com/android/launcher3/LauncherAppState.java @@ -105,7 +105,6 @@ public class LauncherAppState { // Register intent receivers IntentFilter filter = new IntentFilter(); filter.addAction(Intent.ACTION_LOCALE_CHANGED); - filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED); // For handling managed profiles filter.addAction(LauncherAppsCompat.ACTION_MANAGED_PROFILE_ADDED); filter.addAction(LauncherAppsCompat.ACTION_MANAGED_PROFILE_REMOVED); diff --git a/src/com/android/launcher3/LauncherAppWidgetHost.java b/src/com/android/launcher3/LauncherAppWidgetHost.java index 8c23ff30d..3bb73813d 100644 --- a/src/com/android/launcher3/LauncherAppWidgetHost.java +++ b/src/com/android/launcher3/LauncherAppWidgetHost.java @@ -37,7 +37,6 @@ public class LauncherAppWidgetHost extends AppWidgetHost { private final ArrayList<Runnable> mProviderChangeListeners = new ArrayList<Runnable>(); - private int mQsbWidgetId = -1; private Launcher mLauncher; public LauncherAppWidgetHost(Launcher launcher, int hostId) { @@ -45,23 +44,9 @@ public class LauncherAppWidgetHost extends AppWidgetHost { mLauncher = launcher; } - public void setQsbWidgetId(int widgetId) { - mQsbWidgetId = widgetId; - } - @Override protected AppWidgetHostView onCreateView(Context context, int appWidgetId, AppWidgetProviderInfo appWidget) { - if (appWidgetId == mQsbWidgetId) { - return new LauncherAppWidgetHostView(context) { - - @Override - protected View getErrorView() { - // For the QSB, show an empty view instead of an error view. - return new View(getContext()); - } - }; - } return new LauncherAppWidgetHostView(context); } diff --git a/src/com/android/launcher3/LauncherAppWidgetHostView.java b/src/com/android/launcher3/LauncherAppWidgetHostView.java index 28557d0a5..53d9c0455 100644 --- a/src/com/android/launcher3/LauncherAppWidgetHostView.java +++ b/src/com/android/launcher3/LauncherAppWidgetHostView.java @@ -54,6 +54,8 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView implements Touc @ViewDebug.ExportedProperty(category = "launcher") private boolean mChildrenFocused; + protected int mErrorViewId = R.layout.appwidget_error; + public LauncherAppWidgetHostView(Context context) { super(context); mContext = context; @@ -68,7 +70,7 @@ public class LauncherAppWidgetHostView extends AppWidgetHostView implements Touc @Override protected View getErrorView() { - return mInflater.inflate(R.layout.appwidget_error, this, false); + return mInflater.inflate(mErrorViewId, this, false); } public void updateLastInflationOrientation() { diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 649f42dd1..557a91a42 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -16,7 +16,6 @@ package com.android.launcher3; -import android.app.SearchManager; import android.appwidget.AppWidgetProviderInfo; import android.content.BroadcastReceiver; import android.content.ComponentName; @@ -177,7 +176,6 @@ public class LauncherModel extends BroadcastReceiver public void bindItems(ArrayList<ItemInfo> shortcuts, int start, int end, boolean forceAnimateIcons); public void bindScreens(ArrayList<Long> orderedScreenIds); - public void bindAddScreens(ArrayList<Long> orderedScreenIds); public void finishBindingItems(); public void bindAppWidget(LauncherAppWidgetInfo info); public void bindAllApplications(ArrayList<AppInfo> apps); @@ -196,7 +194,6 @@ public class LauncherModel extends BroadcastReceiver public void bindAppInfosRemoved(ArrayList<AppInfo> appInfos); public void notifyWidgetProvidersChanged(); public void bindWidgetsModel(WidgetsModel model); - public void bindSearchProviderChanged(); public boolean isAllAppsButtonRank(int rank); public void onPageBoundSynchronously(int page); public void executeOnNextDraw(ViewOnDrawExecutor executor); @@ -1139,11 +1136,6 @@ public class LauncherModel extends BroadcastReceiver if (Intent.ACTION_LOCALE_CHANGED.equals(action)) { // If we have changed locale we need to clear out the labels in all apps/workspace. forceReload(); - } else if (SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED.equals(action)) { - Callbacks callbacks = getCallback(); - if (callbacks != null) { - callbacks.bindSearchProviderChanged(); - } } else if (LauncherAppsCompat.ACTION_MANAGED_PROFILE_ADDED.equals(action) || LauncherAppsCompat.ACTION_MANAGED_PROFILE_REMOVED.equals(action)) { UserManagerCompat.getInstance(context).enableAndResetCache(); @@ -1528,7 +1520,12 @@ public class LauncherModel extends BroadcastReceiver } if (!occupied.containsKey(item.screenId)) { - occupied.put(item.screenId, new GridOccupancy(countX + 1, countY + 1)); + GridOccupancy screen = new GridOccupancy(countX + 1, countY + 1); + if (item.screenId == Workspace.FIRST_SCREEN_ID) { + // Mark the first row as occupied in order to account for the QSB. + screen.markCells(0, 0, countX + 1, 1, true); + } + occupied.put(item.screenId, screen); } final GridOccupancy occupancy = occupied.get(item.screenId); diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index 11d61d02f..49ce06a44 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -611,8 +611,11 @@ public class LauncherProvider extends ContentProvider { // Database was just created, so wipe any previous widgets if (mWidgetHostResetHandler != null) { new AppWidgetHost(mContext, Launcher.APPWIDGET_HOST_ID).deleteHost(); - mWidgetHostResetHandler.sendEmptyMessage( - ChangeListenerWrapper.MSG_APP_WIDGET_HOST_RESET); + mWidgetHostResetHandler.sendMessage(Message.obtain( + mWidgetHostResetHandler, + ChangeListenerWrapper.MSG_APP_WIDGET_HOST_RESET, + mContext + )); } // Set the flag for empty DB @@ -1100,7 +1103,11 @@ public class LauncherProvider extends ContentProvider { mListener.onExtractedColorsChanged(); break; case MSG_APP_WIDGET_HOST_RESET: - mListener.onAppWidgetHostReset(); + Context context = (Context) msg.obj; + if (context != null) { + context.sendBroadcast(new Intent(Launcher.ACTION_APPWIDGET_HOST_RESET) + .setPackage(context.getPackageName())); + } break; } } diff --git a/src/com/android/launcher3/LauncherProviderChangeListener.java b/src/com/android/launcher3/LauncherProviderChangeListener.java index 524befc58..5998dadcd 100644 --- a/src/com/android/launcher3/LauncherProviderChangeListener.java +++ b/src/com/android/launcher3/LauncherProviderChangeListener.java @@ -10,6 +10,4 @@ public interface LauncherProviderChangeListener { public void onLauncherProviderChange(); public void onExtractedColorsChanged(); - - public void onAppWidgetHostReset(); } diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java index fdca9f2fe..4af53d279 100644 --- a/src/com/android/launcher3/PagedView.java +++ b/src/com/android/launcher3/PagedView.java @@ -1653,7 +1653,8 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc if (DEBUG) Log.d(TAG, "mParentDownMotionY: " + mParentDownMotionY); final int pageUnderPointIndex = getNearestHoverOverPageIndex(); - if (pageUnderPointIndex > -1 && pageUnderPointIndex != indexOfChild(mDragView)) { + // Do not allow any page to be moved to 0th position. + if (pageUnderPointIndex > 0 && pageUnderPointIndex != indexOfChild(mDragView)) { mTempVisiblePagesRange[0] = 0; mTempVisiblePagesRange[1] = getPageCount() - 1; getFreeScrollPageRange(mTempVisiblePagesRange); @@ -2171,7 +2172,8 @@ public abstract class PagedView extends ViewGroup implements ViewGroup.OnHierarc public boolean startReordering(View v) { int dragViewIndex = indexOfChild(v); - if (mTouchState != TOUCH_STATE_REST || dragViewIndex == -1) return false; + // Do not allow the first page to be moved around + if (mTouchState != TOUCH_STATE_REST || dragViewIndex <= 0) return false; mTempVisiblePagesRange[0] = 0; mTempVisiblePagesRange[1] = getPageCount() - 1; diff --git a/src/com/android/launcher3/PinchAnimationManager.java b/src/com/android/launcher3/PinchAnimationManager.java index c8c8fa4c8..c1c25195b 100644 --- a/src/com/android/launcher3/PinchAnimationManager.java +++ b/src/com/android/launcher3/PinchAnimationManager.java @@ -150,12 +150,10 @@ public class PinchAnimationManager { animateOverviewPanelButtons(goingTowards == OVERVIEW); } else if (startState == NORMAL) { animateHotseatAndPageIndicator(goingTowards == NORMAL); - animateQsb(goingTowards == NORMAL); } } else if (threshold == PinchThresholdManager.THRESHOLD_TWO) { if (startState == OVERVIEW) { animateHotseatAndPageIndicator(goingTowards == NORMAL); - animateQsb(goingTowards == NORMAL); animateScrim(goingTowards == OVERVIEW); } else if (startState == NORMAL) { animateOverviewPanelButtons(goingTowards == OVERVIEW); @@ -198,12 +196,6 @@ public class PinchAnimationManager { } } - private void animateQsb(boolean show) { - SearchDropTargetBar.State searchBarState = show ? SearchDropTargetBar.State.SEARCH_BAR - : SearchDropTargetBar.State.INVISIBLE; - mLauncher.getSearchDropTargetBar().animateToState(searchBarState, THRESHOLD_ANIM_DURATION); - } - private void animateOverviewPanelButtons(boolean show) { animateShowHideView(INDEX_OVERVIEW_PANEL_BUTTONS, mLauncher.getOverviewPanel(), show); } diff --git a/src/com/android/launcher3/QsbContainerView.java b/src/com/android/launcher3/QsbContainerView.java new file mode 100644 index 000000000..0a112d23e --- /dev/null +++ b/src/com/android/launcher3/QsbContainerView.java @@ -0,0 +1,271 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3; + +import android.app.Activity; +import android.app.Fragment; +import android.app.FragmentManager; +import android.app.SearchManager; +import android.appwidget.AppWidgetManager; +import android.appwidget.AppWidgetProviderInfo; +import android.content.BroadcastReceiver; +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.IntentFilter; +import android.content.SharedPreferences; +import android.graphics.Rect; +import android.os.Bundle; +import android.util.AttributeSet; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; + +import com.android.launcher3.compat.AppWidgetManagerCompat; + +/** + * A frame layout which contains a QSB. This internally uses fragment to bind the view, which + * allows it to contain the logic for {@link Fragment#startActivityForResult(Intent, int)}. + */ +public class QsbContainerView extends FrameLayout { + + private boolean mBound; + + public QsbContainerView(Context context) { + super(context); + } + + public QsbContainerView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public QsbContainerView(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @Override + protected void onAttachedToWindow() { + super.onAttachedToWindow(); + + if (!mBound) { + FragmentManager fm = ((Launcher) getContext()).getFragmentManager(); + fm.beginTransaction().add(R.id.qsb_container, new QsbFragment()).commit(); + mBound = true; + } + } + + @Override + public void setPadding(int left, int top, int right, int bottom) { + super.setPadding(0, 0, 0, 0); + } + + /** + * A fragment to display the QSB. + */ + public static class QsbFragment extends Fragment implements View.OnClickListener { + + private static final int REQUEST_BIND_QSB = 1; + private static final String QSB_WIDGET_ID = "qsb_widget_id"; + + private static int sSavedWidgetId = -1; + + private AppWidgetProviderInfo mWidgetInfo; + private LauncherAppWidgetHostView mQsb; + + private BroadcastReceiver mRebindReceiver = new BroadcastReceiver() { + @Override + public void onReceive(Context context, Intent intent) { + rebindFragment(); + } + }; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + IntentFilter filter = new IntentFilter(Launcher.ACTION_APPWIDGET_HOST_RESET); + filter.addAction(SearchManager.INTENT_GLOBAL_SEARCH_ACTIVITY_CHANGED); + getContext().registerReceiver(mRebindReceiver, filter); + } + + @Override + public View onCreateView( + LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + + if (savedInstanceState != null) { + sSavedWidgetId = savedInstanceState.getInt(QSB_WIDGET_ID, -1); + } + + Launcher launcher = (Launcher) getActivity(); + mWidgetInfo = getSearchWidgetProvider(launcher); + if (mWidgetInfo == null) { + // There is no search provider, just show the default widget. + return getDefaultView(inflater, container, false); + } + + SharedPreferences prefs = Utilities.getPrefs(launcher); + AppWidgetManagerCompat widgetManager = AppWidgetManagerCompat.getInstance(launcher); + LauncherAppWidgetHost widgetHost = launcher.getAppWidgetHost(); + InvariantDeviceProfile idp = LauncherAppState.getInstance().getInvariantDeviceProfile(); + + Bundle opts = new Bundle(); + Rect size = AppWidgetResizeFrame.getWidgetSizeRanges(launcher, idp.numColumns, 1, null); + opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, size.left); + opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, size.top); + opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, size.right); + opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, size.bottom); + + int widgetId = prefs.getInt(QSB_WIDGET_ID, -1); + AppWidgetProviderInfo widgetInfo = widgetManager.getAppWidgetInfo(widgetId); + boolean isWidgetBound = (widgetInfo != null) && + widgetInfo.provider.equals(mWidgetInfo.provider); + + if (!isWidgetBound) { + // widgetId is already bound and its not the correct provider. + // Delete the widget id. + if (widgetId > -1) { + widgetHost.deleteAppWidgetId(widgetId); + widgetId = -1; + } + + widgetId = widgetHost.allocateAppWidgetId(); + isWidgetBound = widgetManager.bindAppWidgetIdIfAllowed(widgetId, mWidgetInfo, opts); + if (!isWidgetBound) { + widgetHost.deleteAppWidgetId(widgetId); + widgetId = -1; + } + } + + if (isWidgetBound) { + mQsb = (LauncherAppWidgetHostView) + widgetHost.createView(launcher, widgetId, mWidgetInfo); + mQsb.setId(R.id.qsb_widget); + mQsb.mErrorViewId = R.layout.qsb_default_view; + + if (!Utilities.containsAll(AppWidgetManager.getInstance(launcher) + .getAppWidgetOptions(widgetId), opts)) { + mQsb.updateAppWidgetOptions(opts); + } + mQsb.setPadding(0, 0, 0, 0); + return mQsb; + } + + // Return a default widget with setup icon. + return getDefaultView(inflater, container, true); + } + + @Override + public void onClick(View view) { + if (view.getId() == R.id.btn_qsb_search) { + getActivity().startSearch("", false, null, true); + } else if (view.getId() == R.id.btn_qsb_setup) { + // Allocate a new widget id for QSB + sSavedWidgetId = ((Launcher) getActivity()) + .getAppWidgetHost().allocateAppWidgetId(); + // Start intent for bind the widget + Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND); + intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, sSavedWidgetId); + intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, mWidgetInfo.provider); + startActivityForResult(intent, REQUEST_BIND_QSB); + } + } + + @Override + public void onSaveInstanceState(Bundle outState) { + super.onSaveInstanceState(outState); + outState.putInt(QSB_WIDGET_ID, sSavedWidgetId); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent data) { + if (requestCode == REQUEST_BIND_QSB) { + if (resultCode == Activity.RESULT_OK) { + int widgetId = data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, + sSavedWidgetId); + Utilities.getPrefs(getContext()).edit().putInt(QSB_WIDGET_ID, widgetId).apply(); + sSavedWidgetId = -1; + rebindFragment(); + } else if (sSavedWidgetId != -1) { + ((Launcher) getActivity()).getAppWidgetHost().deleteAppWidgetId(sSavedWidgetId); + sSavedWidgetId = -1; + } + } + } + + @Override + public void onResume() { + super.onResume(); + if (mQsb != null && mQsb.isReinflateRequired()) { + rebindFragment(); + } + } + + @Override + public void onDestroy() { + getContext().unregisterReceiver(mRebindReceiver); + super.onDestroy(); + } + + private void rebindFragment() { + if (getActivity() != null) { + // Recreate the fragment. This will cause the qsb to be inflated again. + getActivity().getFragmentManager().beginTransaction() + .replace(R.id.qsb_container, new QsbFragment()).commit(); + } + } + + private View getDefaultView(LayoutInflater inflater, ViewGroup parent, boolean showSetup) { + View v = inflater.inflate(R.layout.qsb_default_view, parent, false); + if (showSetup) { + View setupButton = v.findViewById(R.id.btn_qsb_setup); + setupButton.setVisibility(View.VISIBLE); + setupButton.setOnClickListener(this); + } + v.findViewById(R.id.btn_qsb_search).setOnClickListener(this); + return v; + } + } + + /** + * Returns a widget with category {@link AppWidgetProviderInfo#WIDGET_CATEGORY_SEARCHBOX} + * provided by the same package which is set to be global search activity. + * If widgetCategory is not supported, or no such widget is found, returns the first widget + * provided by the package. + */ + public static AppWidgetProviderInfo getSearchWidgetProvider(Context context) { + SearchManager searchManager = + (SearchManager) context.getSystemService(Context.SEARCH_SERVICE); + ComponentName searchComponent = searchManager.getGlobalSearchActivity(); + if (searchComponent == null) return null; + String providerPkg = searchComponent.getPackageName(); + + AppWidgetProviderInfo defaultWidgetForSearchPackage = null; + + AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); + for (AppWidgetProviderInfo info : appWidgetManager.getInstalledProviders()) { + if (info.provider.getPackageName().equals(providerPkg) && info.configure == null) { + if ((info.widgetCategory & AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX) != 0) { + return info; + } else if (defaultWidgetForSearchPackage == null) { + defaultWidgetForSearchPackage = info; + } + } + } + return defaultWidgetForSearchPackage; + } +} diff --git a/src/com/android/launcher3/SearchDropTargetBar.java b/src/com/android/launcher3/SearchDropTargetBar.java index 171dd8771..e43e96c76 100644 --- a/src/com/android/launcher3/SearchDropTargetBar.java +++ b/src/com/android/launcher3/SearchDropTargetBar.java @@ -16,8 +16,6 @@ package com.android.launcher3; -import android.animation.Animator; -import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.TimeInterpolator; @@ -26,8 +24,6 @@ import android.graphics.Rect; import android.util.AttributeSet; import android.view.View; import android.view.ViewDebug; -import android.view.accessibility.AccessibilityManager; -import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; import com.android.launcher3.dragndrop.DragController; @@ -39,32 +35,21 @@ import com.android.launcher3.util.Thunk; */ public class SearchDropTargetBar extends BaseDropTargetBar { - private static final TimeInterpolator MOVE_DOWN_INTERPOLATOR = new DecelerateInterpolator(0.6f); - private static final TimeInterpolator MOVE_UP_INTERPOLATOR = new DecelerateInterpolator(1.5f); - /** The different states that the search bar space can be in. */ public enum State { - INVISIBLE (0f, 0f, 0f), - INVISIBLE_TRANSLATED (0f, 0f, -1f), - SEARCH_BAR (1f, 0f, 0f), - DROP_TARGET (0f, 1f, 0f); + INVISIBLE (0f), + DROP_TARGET (1f); - private final float mSearchBarAlpha; private final float mDropTargetBarAlpha; - private final float mTranslation; - State(float sbAlpha, float dtbAlpha, float translation) { - mSearchBarAlpha = sbAlpha; + State(float dtbAlpha) { mDropTargetBarAlpha = dtbAlpha; - mTranslation = translation; } - } @ViewDebug.ExportedProperty(category = "launcher") - private State mState = State.SEARCH_BAR; - @Thunk View mQSB; + private State mState = State.INVISIBLE; // Drop targets private ButtonDropTarget mDeleteDropTarget; @@ -113,11 +98,7 @@ public class SearchDropTargetBar extends BaseDropTargetBar { @Override public void hideDropTargets() { - animateToState(State.SEARCH_BAR, DEFAULT_DRAG_FADE_DURATION); - } - - public void setQsbSearchBar(View qsb) { - mQSB = qsb; + animateToState(State.INVISIBLE, DEFAULT_DRAG_FADE_DURATION); } /** @@ -140,30 +121,6 @@ public class SearchDropTargetBar extends BaseDropTargetBar { AlphaUpdateListener.updateVisibility(mDropTargetBar, mAccessibilityEnabled); } - if (mQSB != null) { - boolean isVertical = ((Launcher) getContext()).getDeviceProfile() - .isVerticalBarLayout(); - float translation = isVertical ? 0 : mState.mTranslation * getMeasuredHeight(); - - if (duration > 0) { - int translationChange = Float.compare(mQSB.getTranslationY(), translation); - - animateAlpha(mQSB, mState.mSearchBarAlpha, - translationChange == 0 ? DEFAULT_INTERPOLATOR - : (translationChange < 0 ? MOVE_DOWN_INTERPOLATOR - : MOVE_UP_INTERPOLATOR)); - - if (translationChange != 0) { - mCurrentAnimation.play( - ObjectAnimator.ofFloat(mQSB, View.TRANSLATION_Y, translation)); - } - } else { - mQSB.setTranslationY(translation); - mQSB.setAlpha(mState.mSearchBarAlpha); - AlphaUpdateListener.updateVisibility(mQSB, mAccessibilityEnabled); - } - } - // Start the final animation if (duration > 0) { if (animation != null) { @@ -175,30 +132,8 @@ public class SearchDropTargetBar extends BaseDropTargetBar { } } - /** - * @return the bounds of the QSB search bar. - */ - public Rect getSearchBarBounds() { - if (mQSB != null) { - final int[] pos = new int[2]; - mQSB.getLocationOnScreen(pos); - - final Rect rect = new Rect(); - rect.left = pos[0]; - rect.top = pos[1]; - rect.right = pos[0] + mQSB.getWidth(); - rect.bottom = pos[1] + mQSB.getHeight(); - return rect; - } else { - return null; - } - } - @Override public void enableAccessibleDrag(boolean enable) { - if (mQSB != null) { - mQSB.setVisibility(enable ? View.GONE : View.VISIBLE); - } mDeleteDropTarget.enableAccessibleDrag(enable); mUninstallDropTarget.enableAccessibleDrag(enable); } diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index c9d8cce84..4aaa02fd3 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -18,10 +18,7 @@ package com.android.launcher3; import android.annotation.TargetApi; import android.app.Activity; -import android.app.SearchManager; import android.app.WallpaperManager; -import android.appwidget.AppWidgetManager; -import android.appwidget.AppWidgetProviderInfo; import android.content.ActivityNotFoundException; import android.content.ComponentName; import android.content.Context; @@ -603,39 +600,6 @@ public final class Utilities { } /** - * Returns a widget with category {@link AppWidgetProviderInfo#WIDGET_CATEGORY_SEARCHBOX} - * provided by the same package which is set to be global search activity. - * If widgetCategory is not supported, or no such widget is found, returns the first widget - * provided by the package. - */ - @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1) - public static AppWidgetProviderInfo getSearchWidgetProvider(Context context) { - SearchManager searchManager = - (SearchManager) context.getSystemService(Context.SEARCH_SERVICE); - ComponentName searchComponent = searchManager.getGlobalSearchActivity(); - if (searchComponent == null) return null; - String providerPkg = searchComponent.getPackageName(); - - AppWidgetProviderInfo defaultWidgetForSearchPackage = null; - - AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); - for (AppWidgetProviderInfo info : appWidgetManager.getInstalledProviders()) { - if (info.provider.getPackageName().equals(providerPkg)) { - if (ATLEAST_JB_MR1) { - if ((info.widgetCategory & AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX) != 0) { - return info; - } else if (defaultWidgetForSearchPackage == null) { - defaultWidgetForSearchPackage = info; - } - } else { - return info; - } - } - } - return defaultWidgetForSearchPackage; - } - - /** * Compresses the bitmap to a byte array for serialization. */ public static byte[] flattenBitmap(Bitmap bitmap) { diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index 0f0673a91..6f81f59cf 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -73,7 +73,6 @@ import com.android.launcher3.dragndrop.SpringLoadedDragController; import com.android.launcher3.folder.Folder; import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.logging.UserEventDispatcher; -import com.android.launcher3.pageindicators.PageIndicator; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto.Target; import com.android.launcher3.util.LongArrayMap; @@ -109,7 +108,10 @@ public class Workspace extends PagedView private static final boolean MAP_RECURSE = true; // The screen id used for the empty screen always present to the right. - public final static long EXTRA_EMPTY_SCREEN_ID = -201; + public static final long EXTRA_EMPTY_SCREEN_ID = -201; + // The is the first screen. It is always present, even if its empty. + public static final long FIRST_SCREEN_ID = 0; + private final static long CUSTOM_CONTENT_SCREEN_ID = -301; private static final long CUSTOM_CONTENT_GESTURE_DELAY = 200; @@ -180,8 +182,8 @@ public class Workspace extends PagedView // in all apps or customize mode) enum State { - NORMAL (SearchDropTargetBar.State.SEARCH_BAR, false, false), - NORMAL_HIDDEN (SearchDropTargetBar.State.INVISIBLE_TRANSLATED, false, false), + NORMAL (SearchDropTargetBar.State.INVISIBLE, false, false), + NORMAL_HIDDEN (SearchDropTargetBar.State.INVISIBLE, false, false), SPRING_LOADED (SearchDropTargetBar.State.DROP_TARGET, false, true), OVERVIEW (SearchDropTargetBar.State.INVISIBLE, true, true), OVERVIEW_HIDDEN (SearchDropTargetBar.State.INVISIBLE, true, false); @@ -501,6 +503,32 @@ public class Workspace extends PagedView return mTouchState != TOUCH_STATE_REST; } + /** + * Initializes and binds the first page + * @param qsb an exisitng qsb to recycle or null. + */ + public void bindAndInitFirstWorkspaceScreen(View qsb) { + // Add the first page + CellLayout firstPage = insertNewWorkspaceScreen(Workspace.FIRST_SCREEN_ID, 0); + + if (qsb == null) { + // Always add a QSB on the first screen. + qsb = mLauncher.getLayoutInflater().inflate(R.layout.qsb_container, + firstPage, false); + } + + CellLayout.LayoutParams lp = (CellLayout.LayoutParams) qsb.getLayoutParams(); + lp.cellX = 0; + lp.cellY = 0; + lp.cellHSpan = firstPage.getCountX(); + lp.cellVSpan = 1; + lp.canReorder = false; + + if (!firstPage.addViewToCellLayout(qsb, 0, R.id.qsb_container, lp, true)) { + Log.e(TAG, "Failed to add to item at (0, 0) to CellLayout"); + } + } + public void removeAllWorkspaceScreens() { // Disable all layout transitions before removing all pages to ensure that we don't get the // transition animations competing with us changing the scroll when we add pages or the @@ -513,30 +541,39 @@ public class Workspace extends PagedView removeCustomContentPage(); } + // Recycle the QSB widget + View qsb = findViewById(R.id.qsb_container); + if (qsb != null) { + ((ViewGroup) qsb.getParent()).removeView(qsb); + } + // Remove the pages and clear the screen models removeAllViews(); mScreenOrder.clear(); mWorkspaceScreens.clear(); + // Ensure that the first page is always present + bindAndInitFirstWorkspaceScreen(qsb); + // Re-enable the layout transitions enableLayoutTransitions(); } - public long insertNewWorkspaceScreenBeforeEmptyScreen(long screenId) { + public void insertNewWorkspaceScreenBeforeEmptyScreen(long screenId) { // Find the index to insert this view into. If the empty screen exists, then // insert it before that. int insertIndex = mScreenOrder.indexOf(EXTRA_EMPTY_SCREEN_ID); if (insertIndex < 0) { insertIndex = mScreenOrder.size(); } - return insertNewWorkspaceScreen(screenId, insertIndex); + insertNewWorkspaceScreen(screenId, insertIndex); } - public long insertNewWorkspaceScreen(long screenId) { - return insertNewWorkspaceScreen(screenId, getChildCount()); + public void insertNewWorkspaceScreen(long screenId) { + insertNewWorkspaceScreen(screenId, getChildCount()); } - public long insertNewWorkspaceScreen(long screenId, int insertIndex) { + public CellLayout insertNewWorkspaceScreen(long screenId, int insertIndex) { if (mWorkspaceScreens.containsKey(screenId)) { throw new RuntimeException("Screen id " + screenId + " already exists!"); } @@ -558,7 +595,8 @@ public class Workspace extends PagedView if (delegate != null && delegate.isInAccessibleDrag()) { newScreen.enableAccessibleDrag(true, CellLayout.WORKSPACE_ACCESSIBILITY_DRAG); } - return screenId; + + return newScreen; } public void createCustomContentContainer() { @@ -860,7 +898,8 @@ public class Workspace extends PagedView for (int i = 0; i < total; i++) { long id = mWorkspaceScreens.keyAt(i); CellLayout cl = mWorkspaceScreens.valueAt(i); - if (id >= 0 && cl.getShortcutsAndWidgets().getChildCount() == 0) { + // FIRST_SCREEN_ID can never be removed. + if (id > FIRST_SCREEN_ID && cl.getShortcutsAndWidgets().getChildCount() == 0) { removeScreens.add(id); } } diff --git a/src/com/android/launcher3/model/GridSizeMigrationTask.java b/src/com/android/launcher3/model/GridSizeMigrationTask.java index f900790bb..dd11bde6d 100644 --- a/src/com/android/launcher3/model/GridSizeMigrationTask.java +++ b/src/com/android/launcher3/model/GridSizeMigrationTask.java @@ -22,6 +22,7 @@ import com.android.launcher3.LauncherProvider; import com.android.launcher3.LauncherSettings; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.Utilities; +import com.android.launcher3.Workspace; import com.android.launcher3.backup.nano.BackupProtos; import com.android.launcher3.compat.AppWidgetManagerCompat; import com.android.launcher3.compat.PackageInstallerCompat; @@ -221,7 +222,7 @@ public class GridSizeMigrationTask { // {@link #mCarryOver}, to prevent an infinite loop. If no item could be removed, // break the loop and abort migration by throwing an exception. OptimalPlacementSolution placement = new OptimalPlacementSolution( - new GridOccupancy(mTrgX, mTrgY), deepCopy(mCarryOver), true); + new GridOccupancy(mTrgX, mTrgY), deepCopy(mCarryOver), 0, true); placement.find(); if (placement.finalPlacedItems.size() > 0) { long newScreenId = LauncherSettings.Settings.call( @@ -262,13 +263,16 @@ public class GridSizeMigrationTask { * Migrate a particular screen id. * Strategy: * 1) For all possible combinations of row and column, pick the one which causes the least - * data loss: {@link #tryRemove(int, int, ArrayList, float[])} + * data loss: {@link #tryRemove(int, int, int, ArrayList, float[])} * 2) Maintain a list of all lost items before this screen, and add any new item lost from * this screen to that list as well. * 3) If all those items from the above list can be placed on this screen, place them * (otherwise they are placed on a new screen). */ private void migrateScreen(long screenId) { + // If we are migrating the first screen, do not touch the first row. + int startY = screenId == Workspace.FIRST_SCREEN_ID ? 1 : 0; + ArrayList<DbEntry> items = loadWorkspaceEntries(screenId); int removedCol = Integer.MAX_VALUE; @@ -286,10 +290,10 @@ public class GridSizeMigrationTask { // Try removing all possible combinations for (int x = 0; x < mSrcX; x++) { - for (int y = 0; y < mSrcY; y++) { + for (int y = startY; y < mSrcY; y++) { // Use a deep copy when trying out a particular combination as it can change // the underlying object. - ArrayList<DbEntry> itemsOnScreen = tryRemove(x, y, deepCopy(items), outLoss); + ArrayList<DbEntry> itemsOnScreen = tryRemove(x, y, startY, deepCopy(items), outLoss); if ((outLoss[0] < removeWt) || ((outLoss[0] == removeWt) && (outLoss[1] < moveWt))) { removeWt = outLoss[0]; @@ -338,12 +342,13 @@ public class GridSizeMigrationTask { if (!mCarryOver.isEmpty() && removeWt == 0) { // No new items were removed in this step. Try placing all the items on this screen. GridOccupancy occupied = new GridOccupancy(mTrgX, mTrgY); + occupied.markCells(0, 0, mTrgX, startY, true); for (DbEntry item : finalItems) { occupied.markCells(item, true); } OptimalPlacementSolution placement = new OptimalPlacementSolution(occupied, - deepCopy(mCarryOver), true); + deepCopy(mCarryOver), startY, true); placement.find(); if (placement.lowestWeightLoss == 0) { // All items got placed @@ -375,9 +380,10 @@ public class GridSizeMigrationTask { * @param outLoss array of size 2. The first entry is filled with weight loss, and the second * with the overall item movement. */ - private ArrayList<DbEntry> tryRemove(int col, int row, ArrayList<DbEntry> items, - float[] outLoss) { + private ArrayList<DbEntry> tryRemove(int col, int row, int startY, + ArrayList<DbEntry> items, float[] outLoss) { GridOccupancy occupied = new GridOccupancy(mTrgX, mTrgY); + occupied.markCells(0, 0, mTrgX, startY, true); col = mShouldRemoveX ? col : Integer.MAX_VALUE; row = mShouldRemoveY ? row : Integer.MAX_VALUE; @@ -399,7 +405,8 @@ public class GridSizeMigrationTask { } } - OptimalPlacementSolution placement = new OptimalPlacementSolution(occupied, removedItems); + OptimalPlacementSolution placement = + new OptimalPlacementSolution(occupied, removedItems, startY); placement.find(); finalItems.addAll(placement.finalPlacedItems); outLoss[0] = placement.lowestWeightLoss; @@ -415,19 +422,24 @@ public class GridSizeMigrationTask { // linear placement. private final boolean ignoreMove; + // The first row in the grid from where the placement should start. + private final int startY; + float lowestWeightLoss = Float.MAX_VALUE; float lowestMoveCost = Float.MAX_VALUE; ArrayList<DbEntry> finalPlacedItems; - public OptimalPlacementSolution(GridOccupancy occupied, ArrayList<DbEntry> itemsToPlace) { - this(occupied, itemsToPlace, false); + public OptimalPlacementSolution( + GridOccupancy occupied, ArrayList<DbEntry> itemsToPlace, int startY) { + this(occupied, itemsToPlace, startY, false); } public OptimalPlacementSolution(GridOccupancy occupied, ArrayList<DbEntry> itemsToPlace, - boolean ignoreMove) { + int startY, boolean ignoreMove) { this.occupied = occupied; this.itemsToPlace = itemsToPlace; this.ignoreMove = ignoreMove; + this.startY = startY; // Sort the items such that larger widgets appear first followed by 1x1 items Collections.sort(this.itemsToPlace); @@ -477,7 +489,7 @@ public class GridSizeMigrationTask { int myW = me.spanX; int myH = me.spanY; - for (int y = 0; y < mTrgY; y++) { + for (int y = startY; y < mTrgY; y++) { for (int x = 0; x < mTrgX; x++) { float newMoveCost = moveCost; if (x != myX) { @@ -547,7 +559,7 @@ public class GridSizeMigrationTask { int newDistance = Integer.MAX_VALUE; int newX = Integer.MAX_VALUE, newY = Integer.MAX_VALUE; - for (int y = 0; y < mTrgY; y++) { + for (int y = startY; y < mTrgY; y++) { for (int x = 0; x < mTrgX; x++) { if (!occupied.cells[x][y]) { int dist = ignoreMove ? 0 : diff --git a/src/com/android/launcher3/util/GridOccupancy.java b/src/com/android/launcher3/util/GridOccupancy.java index 3f5f0b4ed..6a10b0aa5 100644 --- a/src/com/android/launcher3/util/GridOccupancy.java +++ b/src/com/android/launcher3/util/GridOccupancy.java @@ -77,7 +77,7 @@ public class GridOccupancy { public void markCells(int cellX, int cellY, int spanX, int spanY, boolean value) { if (cellX < 0 || cellY < 0) return; for (int x = cellX; x < cellX + spanX && x < mCountX; x++) { - for (int y = cellY; y < cellY + spanY && y < mCountX; y++) { + for (int y = cellY; y < cellY + spanY && y < mCountY; y++) { cells[x][y] = value; } } |