summaryrefslogtreecommitdiffstats
path: root/src/com
diff options
context:
space:
mode:
Diffstat (limited to 'src/com')
-rw-r--r--src/com/android/launcher3/CellLayout.java2
-rw-r--r--src/com/android/launcher3/Launcher.java237
-rw-r--r--src/com/android/launcher3/LauncherAppState.java1
-rw-r--r--src/com/android/launcher3/LauncherAppWidgetHost.java15
-rw-r--r--src/com/android/launcher3/LauncherAppWidgetHostView.java4
-rw-r--r--src/com/android/launcher3/LauncherModel.java15
-rw-r--r--src/com/android/launcher3/LauncherProvider.java13
-rw-r--r--src/com/android/launcher3/LauncherProviderChangeListener.java2
-rw-r--r--src/com/android/launcher3/PagedView.java6
-rw-r--r--src/com/android/launcher3/PinchAnimationManager.java8
-rw-r--r--src/com/android/launcher3/QsbContainerView.java271
-rw-r--r--src/com/android/launcher3/SearchDropTargetBar.java75
-rw-r--r--src/com/android/launcher3/Utilities.java36
-rw-r--r--src/com/android/launcher3/Workspace.java61
-rw-r--r--src/com/android/launcher3/model/GridSizeMigrationTask.java38
-rw-r--r--src/com/android/launcher3/util/GridOccupancy.java2
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;
}
}