Moving the QSB to the workspace grid.
am: da4fe1a624
* commit 'da4fe1a6244457f144e0a331cada3ada17157809':
Moving the QSB to the workspace grid.
Change-Id: Ibfd0109be932430047c1dc177f62e71d42dd3d1f
This commit is contained in:
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<com.android.launcher3.QsbContainerView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/qsb_container"
|
||||
android:padding="0dp" />
|
||||
@@ -0,0 +1,54 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
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.
|
||||
-->
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:layout_margin="16dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:background="@drawable/quantum_panel_shape"
|
||||
android:elevation="2dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:id="@+id/btn_qsb_search"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:text="@string/abandoned_search"
|
||||
android:textColor="@color/quantum_panel_text_color"
|
||||
android:textAppearance="?android:textAppearanceMedium"
|
||||
android:clickable="true"
|
||||
android:background="?android:attr/selectableItemBackground" />
|
||||
<ImageView
|
||||
android:layout_width="48dp"
|
||||
android:id="@+id/btn_qsb_setup"
|
||||
android:clickable="true"
|
||||
android:visibility="gone"
|
||||
android:layout_height="match_parent"
|
||||
android:src="@drawable/ic_setting"
|
||||
android:tint="@color/quantum_panel_text_color"
|
||||
android:contentDescription="@string/gadget_setup_text"
|
||||
android:padding="8dp"
|
||||
android:background="?android:attr/selectableItemBackground" />
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
@@ -2464,19 +2463,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.
|
||||
*
|
||||
@@ -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);
|
||||
@@ -3614,16 +3501,6 @@ public class Launcher extends Activity
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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() {
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,6 +10,4 @@ public interface LauncherProviderChangeListener {
|
||||
public void onLauncherProviderChange();
|
||||
|
||||
public void onExtractedColorsChanged();
|
||||
|
||||
public void onAppWidgetHostReset();
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
@@ -602,39 +599,6 @@ public final class Utilities {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 :
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -205,10 +205,13 @@ public class BindWidgetTest extends LauncherInstrumentationTestCase {
|
||||
*/
|
||||
private void setupAndVerifyContents(
|
||||
LauncherAppWidgetInfo item, Class<?> widgetClass, String desc) {
|
||||
// Add new screen
|
||||
long screenId = LauncherSettings.Settings.call(
|
||||
mResolver, LauncherSettings.Settings.METHOD_NEW_SCREEN_ID)
|
||||
.getLong(LauncherSettings.Settings.EXTRA_VALUE);
|
||||
long screenId = Workspace.FIRST_SCREEN_ID;
|
||||
// Update the screen id counter for the provider.
|
||||
LauncherSettings.Settings.call(mResolver, LauncherSettings.Settings.METHOD_NEW_SCREEN_ID);
|
||||
|
||||
if (screenId > Workspace.FIRST_SCREEN_ID) {
|
||||
screenId = Workspace.FIRST_SCREEN_ID;
|
||||
}
|
||||
ContentValues v = new ContentValues();
|
||||
v.put(LauncherSettings.WorkspaceScreens._ID, screenId);
|
||||
v.put(LauncherSettings.WorkspaceScreens.SCREEN_RANK, 0);
|
||||
@@ -263,7 +266,7 @@ public class BindWidgetTest extends LauncherInstrumentationTestCase {
|
||||
item.minSpanY = info.minSpanY;
|
||||
item.user = mWidgetManager.getUser(info);
|
||||
item.cellX = 0;
|
||||
item.cellY = 0;
|
||||
item.cellY = 1;
|
||||
item.container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
|
||||
|
||||
if (bindWidget) {
|
||||
@@ -319,7 +322,7 @@ public class BindWidgetTest extends LauncherInstrumentationTestCase {
|
||||
item.minSpanX = 2;
|
||||
item.minSpanY = 2;
|
||||
item.cellX = 0;
|
||||
item.cellY = 0;
|
||||
item.cellY = 1;
|
||||
item.container = LauncherSettings.Favorites.CONTAINER_DESKTOP;
|
||||
return item;
|
||||
}
|
||||
|
||||
@@ -208,6 +208,57 @@ public class GridSizeMigrationTaskTest extends ProviderTestCase2<TestLauncherPro
|
||||
}});
|
||||
}
|
||||
|
||||
public void testWorkspace_first_row_blocked() throws Exception {
|
||||
// The first screen has one item on the 4th column which needs moving, as the first row
|
||||
// will be kept empty.
|
||||
long[][][] ids = createGrid(new int[][][]{{
|
||||
{ -1, -1, -1, -1},
|
||||
{ 3, 1, 7, 0},
|
||||
{ 8, 7, 7, -1},
|
||||
{ 5, 2, 7, -1},
|
||||
}}, 0);
|
||||
|
||||
new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, new HashMap<String, Point>(),
|
||||
new Point(4, 4), new Point(3, 4)).migrateWorkspace();
|
||||
|
||||
// Items in the second column of the first screen should get placed on a new screen.
|
||||
verifyWorkspace(new long[][][] {{
|
||||
{ -1, -1, -1},
|
||||
{ids[0][1][0], ids[0][1][1], ids[0][1][2]},
|
||||
{ids[0][2][0], ids[0][2][1], ids[0][2][2]},
|
||||
{ids[0][3][0], ids[0][3][1], ids[0][3][2]},
|
||||
}, {
|
||||
{ids[0][1][3]},
|
||||
}});
|
||||
}
|
||||
|
||||
public void testWorkspace_items_moved_to_empty_first_row() throws Exception {
|
||||
// Items will get moved to the next screen to keep the first screen empty.
|
||||
long[][][] ids = createGrid(new int[][][]{{
|
||||
{ -1, -1, -1, -1},
|
||||
{ 0, 1, 0, 0},
|
||||
{ 8, 7, 7, -1},
|
||||
{ 5, 6, 7, -1},
|
||||
}}, 0);
|
||||
|
||||
new GridSizeMigrationTask(getMockContext(), mIdp, mValidPackages, new HashMap<String, Point>(),
|
||||
new Point(4, 4), new Point(3, 3)).migrateWorkspace();
|
||||
|
||||
// Items in the second column of the first screen should get placed on a new screen.
|
||||
verifyWorkspace(new long[][][] {{
|
||||
{ -1, -1, -1},
|
||||
{ids[0][2][0], ids[0][2][1], ids[0][2][2]},
|
||||
{ids[0][3][0], ids[0][3][1], ids[0][3][2]},
|
||||
}, {
|
||||
{ids[0][1][1], ids[0][1][0], ids[0][1][2]},
|
||||
{ids[0][1][3]},
|
||||
}});
|
||||
}
|
||||
|
||||
private long[][][] createGrid(int[][][] typeArray) throws Exception {
|
||||
return createGrid(typeArray, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the DB with dummy elements to represent the provided grid structure.
|
||||
* @param typeArray A 3d array of item types. {@see #addItem(int, long, long, int, int)} for
|
||||
@@ -215,14 +266,18 @@ public class GridSizeMigrationTaskTest extends ProviderTestCase2<TestLauncherPro
|
||||
* two represent the workspace grid.
|
||||
* @return the same grid representation where each entry is the corresponding item id.
|
||||
*/
|
||||
private long[][][] createGrid(int[][][] typeArray) throws Exception {
|
||||
private long[][][] createGrid(int[][][] typeArray, long startScreen) throws Exception {
|
||||
LauncherSettings.Settings.call(getMockContentResolver(),
|
||||
LauncherSettings.Settings.METHOD_CREATE_EMPTY_DB);
|
||||
long[][][] ids = new long[typeArray.length][][];
|
||||
|
||||
for (int i = 0; i < typeArray.length; i++) {
|
||||
// Add screen to DB
|
||||
long screenId = LauncherSettings.Settings.call(getMockContentResolver(),
|
||||
LauncherSettings.Settings.METHOD_NEW_SCREEN_ID)
|
||||
.getLong(LauncherSettings.Settings.EXTRA_VALUE);
|
||||
long screenId = startScreen + i;
|
||||
|
||||
// Keep the screen id counter up to date
|
||||
LauncherSettings.Settings.call(getMockContentResolver(),
|
||||
LauncherSettings.Settings.METHOD_NEW_SCREEN_ID);
|
||||
|
||||
ContentValues v = new ContentValues();
|
||||
v.put(LauncherSettings.WorkspaceScreens._ID, screenId);
|
||||
@@ -270,7 +325,8 @@ public class GridSizeMigrationTaskTest extends ProviderTestCase2<TestLauncherPro
|
||||
} else {
|
||||
assertEquals(1, c.getCount());
|
||||
c.moveToNext();
|
||||
assertEquals(id, c.getLong(0));
|
||||
assertEquals(String.format("Failed to verify item ad %d %d, %d", i, y, x),
|
||||
id, c.getLong(0));
|
||||
total++;
|
||||
}
|
||||
c.close();
|
||||
|
||||
Reference in New Issue
Block a user