Merge "Launch activity if click on a TaskView which has the task running on different display wit root."
diff --git a/go/quickstep/src/com/android/launcher3/AppSharing.java b/go/quickstep/src/com/android/launcher3/AppSharing.java
index 7c66f6e..c252fba 100644
--- a/go/quickstep/src/com/android/launcher3/AppSharing.java
+++ b/go/quickstep/src/com/android/launcher3/AppSharing.java
@@ -22,6 +22,9 @@
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
+import android.os.Process;
+import android.os.UserHandle;
+import android.os.UserManager;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
@@ -98,14 +101,19 @@
* The Share App system shortcut, used to initiate p2p sharing of a given app
*/
public final class Share extends SystemShortcut<Launcher> {
- private PopupDataProvider mPopupDataProvider;
+ private final PopupDataProvider mPopupDataProvider;
+ private final boolean mSharingEnabledForUser;
public Share(Launcher target, ItemInfo itemInfo) {
super(R.drawable.ic_share, R.string.app_share_drop_target_label, target, itemInfo);
mPopupDataProvider = target.getPopupDataProvider();
- if (ENABLE_SHAREABILITY_CHECK) {
- mShareabilityMgr = AppShareabilityManager.INSTANCE.get(target);
+ mSharingEnabledForUser = bluetoothSharingEnabled(target);
+ if (!mSharingEnabledForUser) {
+ setEnabled(false);
+ } else if (ENABLE_SHAREABILITY_CHECK) {
+ mShareabilityMgr =
+ AppShareabilityManager.INSTANCE.get(target.getApplicationContext());
checkShareability(/* requestUpdateIfUnknown */ true);
}
}
@@ -144,7 +152,12 @@
sendIntent.setType(APP_MIME_TYPE);
sendIntent.setComponent(ComponentName.unflattenFromString(mSharingComponent));
- mTarget.startActivitySafely(view, sendIntent, mItemInfo);
+ UserHandle user = mItemInfo.user;
+ if (user != null && !user.equals(Process.myUserHandle())) {
+ mTarget.startActivityAsUser(sendIntent, user);
+ } else {
+ mTarget.startActivitySafely(view, sendIntent, mItemInfo);
+ }
AbstractFloatingView.closeAllOpenViews(mTarget);
}
@@ -170,8 +183,15 @@
}
}
+ private boolean bluetoothSharingEnabled(Context context) {
+ return !context.getSystemService(UserManager.class)
+ .hasUserRestriction(UserManager.DISALLOW_BLUETOOTH_SHARING, mItemInfo.user);
+ }
+
private void showCannotShareToast(Context context) {
- CharSequence text = context.getText(R.string.toast_p2p_app_not_shareable);
+ CharSequence text = (mSharingEnabledForUser)
+ ? context.getText(R.string.toast_p2p_app_not_shareable)
+ : context.getText(R.string.blocked_by_policy);
int duration = Toast.LENGTH_SHORT;
Toast.makeText(context, text, duration).show();
}
diff --git a/go/quickstep/src/com/android/launcher3/model/AppShareabilityManager.java b/go/quickstep/src/com/android/launcher3/model/AppShareabilityManager.java
index cf80c35..0d0f700 100644
--- a/go/quickstep/src/com/android/launcher3/model/AppShareabilityManager.java
+++ b/go/quickstep/src/com/android/launcher3/model/AppShareabilityManager.java
@@ -47,7 +47,7 @@
* Each app's status is retrieved from the Play Store's API. Statuses are cached in order
* to limit extraneous calls to that API (which can be time-consuming).
*/
-public final class AppShareabilityManager {
+public class AppShareabilityManager {
@Retention(SOURCE)
@IntDef({
ShareabilityStatus.UNKNOWN,
diff --git a/quickstep/res/drawable/ic_sysbar_back_kids.xml b/quickstep/res/drawable/ic_sysbar_back_kids.xml
new file mode 100644
index 0000000..ac6d49b
--- /dev/null
+++ b/quickstep/res/drawable/ic_sysbar_back_kids.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M21,11.4v1c0,1 -0.8,1.8 -1.8,1.8l0,0h-8.8l2.7,2.7c0.7,0.7 0.7,1.8 0,2.5c0,0 0,0 0,0l-0.7,0.7c-0.7,0.7 -1.8,0.7 -2.5,0c0,0 0,0 0,0l-5.9,-5.9C2.6,13 2.6,11 3.9,9.7l5.9,-5.9c0.7,-0.7 1.8,-0.7 2.5,0c0,0 0,0 0,0l0.7,0.7c0.7,0.7 0.7,1.8 0,2.5c0,0 0,0 0,0l-2.6,2.6h8.7C20.2,9.6 21,10.5 21,11.4z"/>
+</vector>
diff --git a/quickstep/res/drawable/ic_sysbar_home_kids.xml b/quickstep/res/drawable/ic_sysbar_home_kids.xml
new file mode 100644
index 0000000..2397e70
--- /dev/null
+++ b/quickstep/res/drawable/ic_sysbar_home_kids.xml
@@ -0,0 +1,9 @@
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24">
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M22.7,11l-7.9,-7c-1.6,-1.4 -3.9,-1.4 -5.5,0L7.4,5.7V4.1C7.4,3.5 6.9,3 6.3,3H5.2C4.5,3 4,3.5 4,4.1c0,0 0,0 0,0v4.6L1.4,11c-0.5,0.4 -0.5,1.1 -0.1,1.5c0,0 0,0 0,0c0.2,0.2 0.5,0.4 0.8,0.4H4v5c0,1.3 1,2.3 2.3,2.3c0,0 0,0 0,0h11.4c1.3,0 2.3,-1 2.3,-2.3v-5h2c0.6,0 1.1,-0.5 1.1,-1.1C23,11.5 22.9,11.2 22.7,11L22.7,11zM14.3,15.6c0,0.6 -0.5,1.1 -1.1,1.2h-2.3c-0.6,0 -1.1,-0.5 -1.1,-1.1v-1.3c0,-0.6 0.5,-1.1 1.1,-1.1c0,0 0,0 0,0h2.3c0.6,0 1.1,0.5 1.1,1.1c0,0 0,0 0,0L14.3,15.6z"/>
+</vector>
diff --git a/quickstep/res/values/dimens.xml b/quickstep/res/values/dimens.xml
index c9e33a7..0e5ba2f 100644
--- a/quickstep/res/values/dimens.xml
+++ b/quickstep/res/values/dimens.xml
@@ -231,4 +231,10 @@
<dimen name="taskbar_stashed_handle_height">4dp</dimen>
<dimen name="taskbar_edu_wave_anim_trans_y">25dp</dimen>
<dimen name="taskbar_edu_wave_anim_trans_y_return_overshoot">4dp</dimen>
+ <dimen name="taskbar_nav_buttons_width_kids">88dp</dimen>
+ <dimen name="taskbar_nav_buttons_height_kids">40dp</dimen>
+ <dimen name="taskbar_nav_buttons_corner_radius_kids">40dp</dimen>
+ <dimen name="taskbar_back_button_left_margin_kids">96dp</dimen>
+ <dimen name="taskbar_home_button_left_margin_kids">48dp</dimen>
+ <dimen name="taskbar_icon_size_kids">32dp</dimen>
</resources>
diff --git a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
index 4269f2d..65b346a 100644
--- a/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
+++ b/quickstep/src/com/android/launcher3/BaseQuickstepLauncher.java
@@ -336,7 +336,8 @@
getSystemService(DeviceStateManager.class),
getSystemService(SensorManager.class),
getMainThreadHandler(),
- getMainExecutor()
+ getMainExecutor(),
+ /* tracingTagPrefix= */ "launcher"
);
mLauncherUnfoldAnimationController = new LauncherUnfoldAnimationController(
@@ -507,7 +508,7 @@
ActivityOptionsCompat.setLauncherSourceInfo(
activityOptions.options, mLastTouchUpTime);
}
- activityOptions.options.setSplashscreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_ICON);
+ activityOptions.options.setSplashScreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_ICON);
addLaunchCookie(item, activityOptions.options);
return activityOptions;
}
diff --git a/quickstep/src/com/android/launcher3/model/PredictionUpdateTask.java b/quickstep/src/com/android/launcher3/model/PredictionUpdateTask.java
index b0fba3d..9c3daea 100644
--- a/quickstep/src/com/android/launcher3/model/PredictionUpdateTask.java
+++ b/quickstep/src/com/android/launcher3/model/PredictionUpdateTask.java
@@ -34,7 +34,6 @@
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
-import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
@@ -60,13 +59,13 @@
Utilities.getDevicePrefs(context).edit()
.putBoolean(LAST_PREDICTION_ENABLED_STATE, !mTargets.isEmpty()).apply();
- FixedContainerItems fci = mPredictorState.items;
- Set<UserHandle> usersForChangedShortcuts = new HashSet<>(fci.items.stream()
- .filter(info -> info.itemType == ITEM_TYPE_DEEP_SHORTCUT)
- .map(info -> info.user)
- .collect(Collectors.toSet()));
- fci.items.clear();
+ Set<UserHandle> usersForChangedShortcuts =
+ dataModel.extraItems.get(mPredictorState.containerId).items.stream()
+ .filter(info -> info.itemType == ITEM_TYPE_DEEP_SHORTCUT)
+ .map(info -> info.user)
+ .collect(Collectors.toSet());
+ FixedContainerItems fci = new FixedContainerItems(mPredictorState.containerId);
for (AppTarget target : mTargets) {
WorkspaceItemInfo itemInfo;
ShortcutInfo si = target.getShortcutInfo();
@@ -109,6 +108,7 @@
fci.items.add(itemInfo);
}
+ dataModel.extraItems.put(fci.containerId, fci);
bindExtraContainerItems(fci);
usersForChangedShortcuts.forEach(
u -> dataModel.updateShortcutPinnedState(app.getContext(), u));
diff --git a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
index da18357..0e534f4 100644
--- a/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
+++ b/quickstep/src/com/android/launcher3/model/QuickstepModelDelegate.java
@@ -119,18 +119,19 @@
WorkspaceItemFactory allAppsFactory = new WorkspaceItemFactory(
mApp, ums, pinnedShortcuts, mIDP.numDatabaseAllAppsColumns);
- mAllAppsState.items.setItems(
+ FixedContainerItems allAppsItems = new FixedContainerItems(mAllAppsState.containerId,
mAllAppsState.storage.read(mApp.getContext(), allAppsFactory, ums.allUsers::get));
- mDataModel.extraItems.put(CONTAINER_PREDICTION, mAllAppsState.items);
+ mDataModel.extraItems.put(mAllAppsState.containerId, allAppsItems);
WorkspaceItemFactory hotseatFactory =
new WorkspaceItemFactory(mApp, ums, pinnedShortcuts, mIDP.numDatabaseHotseatIcons);
- mHotseatState.items.setItems(
+ FixedContainerItems hotseatItems = new FixedContainerItems(mHotseatState.containerId,
mHotseatState.storage.read(mApp.getContext(), hotseatFactory, ums.allUsers::get));
- mDataModel.extraItems.put(CONTAINER_HOTSEAT_PREDICTION, mHotseatState.items);
+ mDataModel.extraItems.put(mHotseatState.containerId, hotseatItems);
// Widgets prediction isn't used frequently. And thus, it is not persisted on disk.
- mDataModel.extraItems.put(CONTAINER_WIDGETS_PREDICTION, mWidgetsRecommendationState.items);
+ mDataModel.extraItems.put(mWidgetsRecommendationState.containerId,
+ new FixedContainerItems(mWidgetsRecommendationState.containerId));
mActive = true;
}
@@ -371,14 +372,14 @@
static class PredictorState {
- public final FixedContainerItems items;
+ public final int containerId;
public final PersistedItemArray<ItemInfo> storage;
public AppPredictor predictor;
private List<AppTarget> mLastTargets;
- PredictorState(int container, String storageName) {
- items = new FixedContainerItems(container);
+ PredictorState(int containerId, String storageName) {
+ this.containerId = containerId;
storage = new PersistedItemArray<>(storageName);
mLastTargets = Collections.emptyList();
}
diff --git a/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
index 4be83dc..9cd9d85 100644
--- a/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
+++ b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
@@ -59,8 +59,8 @@
Map<PackageUserKey, List<WidgetItem>> allWidgets =
dataModel.widgetsModel.getAllWidgetsWithoutShortcuts();
- FixedContainerItems fixedContainerItems = mPredictorState.items;
- fixedContainerItems.items.clear();
+ FixedContainerItems fixedContainerItems =
+ new FixedContainerItems(mPredictorState.containerId);
if (FeatureFlags.ENABLE_LOCAL_RECOMMENDED_WIDGETS_FILTER.get()) {
for (AppTarget app : mTargets) {
@@ -100,6 +100,7 @@
}
}
}
+ dataModel.extraItems.put(mPredictorState.containerId, fixedContainerItems);
bindExtraContainerItems(fixedContainerItems);
// Don't store widgets prediction to disk because it is not used frequently.
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index a4ecd8b..002a86c 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -45,10 +45,12 @@
import android.content.pm.ActivityInfo.Config;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
+import android.graphics.Color;
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.Region.Op;
import android.graphics.drawable.AnimatedVectorDrawable;
+import android.graphics.drawable.PaintDrawable;
import android.util.Property;
import android.view.Gravity;
import android.view.MotionEvent;
@@ -58,6 +60,7 @@
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
+import android.widget.LinearLayout;
import com.android.launcher3.LauncherAnimUtils;
import com.android.launcher3.R;
@@ -128,6 +131,7 @@
private View mA11yButton;
private int mSysuiStateFlags;
private View mBackButton;
+ private View mHomeButton;
private FloatingRotationButton mFloatingRotationButton;
public NavbarButtonsViewController(TaskbarActivityContext context, FrameLayout navButtonsView) {
@@ -171,6 +175,7 @@
// Force nav buttons (specifically back button) to be visible during setup wizard.
boolean isInSetup = !mContext.isUserSetupComplete();
+ boolean isInKidsMode = mContext.isNavBarKidsModeActive();
boolean alwaysShowButtons = isThreeButtonNav || isInSetup;
// Make sure to remove nav bar buttons translation when notification shade is expanded or
@@ -208,6 +213,64 @@
& Configuration.UI_MODE_NIGHT_MASK;
boolean isDarkTheme = mode == Configuration.UI_MODE_NIGHT_YES;
mTaskbarNavButtonDarkIntensity.updateValue(isDarkTheme ? 0 : 1);
+ } else if (isInKidsMode) {
+ int iconSize = mContext.getResources().getDimensionPixelSize(
+ R.dimen.taskbar_icon_size_kids);
+ int buttonWidth = mContext.getResources().getDimensionPixelSize(
+ R.dimen.taskbar_nav_buttons_width_kids);
+ int buttonHeight = mContext.getResources().getDimensionPixelSize(
+ R.dimen.taskbar_nav_buttons_height_kids);
+ int buttonRadius = mContext.getResources().getDimensionPixelSize(
+ R.dimen.taskbar_nav_buttons_corner_radius_kids);
+ int paddingleft = (buttonWidth - iconSize) / 2;
+ int paddingRight = paddingleft;
+ int paddingTop = (buttonHeight - iconSize) / 2;
+ int paddingBottom = paddingTop;
+
+ // Update icons
+ ((ImageView) mBackButton).setImageDrawable(
+ mBackButton.getContext().getDrawable(R.drawable.ic_sysbar_back_kids));
+ ((ImageView) mBackButton).setScaleType(ImageView.ScaleType.FIT_CENTER);
+ mBackButton.setPadding(paddingleft, paddingTop, paddingRight, paddingBottom);
+ ((ImageView) mHomeButton).setImageDrawable(
+ mHomeButton.getContext().getDrawable(R.drawable.ic_sysbar_home_kids));
+ ((ImageView) mHomeButton).setScaleType(ImageView.ScaleType.FIT_CENTER);
+ mHomeButton.setPadding(paddingleft, paddingTop, paddingRight, paddingBottom);
+
+ // Home button layout
+ LinearLayout.LayoutParams homeLayoutparams = new LinearLayout.LayoutParams(
+ buttonWidth,
+ buttonHeight
+ );
+ int homeButtonLeftMargin = mContext.getResources().getDimensionPixelSize(
+ R.dimen.taskbar_home_button_left_margin_kids);
+ homeLayoutparams.setMargins(homeButtonLeftMargin, 0, 0, 0);
+ mHomeButton.setLayoutParams(homeLayoutparams);
+
+ // Back button layout
+ LinearLayout.LayoutParams backLayoutParams = new LinearLayout.LayoutParams(
+ buttonWidth,
+ buttonHeight
+ );
+ int backButtonLeftMargin = mContext.getResources().getDimensionPixelSize(
+ R.dimen.taskbar_back_button_left_margin_kids);
+ backLayoutParams.setMargins(backButtonLeftMargin, 0, 0, 0);
+ mBackButton.setLayoutParams(backLayoutParams);
+
+ // Button backgrounds
+ int whiteWith10PctAlpha = Color.argb(0.1f, 1, 1, 1);
+ PaintDrawable buttonBackground = new PaintDrawable(whiteWith10PctAlpha);
+ buttonBackground.setCornerRadius(buttonRadius);
+ mHomeButton.setBackground(buttonBackground);
+ mBackButton.setBackground(buttonBackground);
+
+ // Update alignment within taskbar
+ FrameLayout.LayoutParams navButtonsLayoutParams = (FrameLayout.LayoutParams)
+ mNavButtonContainer.getLayoutParams();
+ navButtonsLayoutParams.setMarginStart(navButtonsLayoutParams.getMarginEnd() / 2);
+ navButtonsLayoutParams.setMarginEnd(navButtonsLayoutParams.getMarginStart());
+ navButtonsLayoutParams.gravity = Gravity.CENTER;
+ mNavButtonContainer.requestLayout();
}
// Animate taskbar background when any of these flags are enabled
@@ -276,9 +339,9 @@
&& ((flags & FLAG_KEYGUARD_VISIBLE) == 0 || showingOnKeyguard);
}));
boolean isRtl = Utilities.isRtl(mContext.getResources());
- mPropertyHolders.add(new StatePropertyHolder(
- mBackButton, flags -> (flags & FLAG_IME_VISIBLE) != 0, View.ROTATION,
- isRtl ? 90 : -90, 0));
+ mPropertyHolders.add(new StatePropertyHolder(mBackButton,
+ flags -> (flags & FLAG_IME_VISIBLE) != 0 && !mContext.isNavBarKidsModeActive(),
+ View.ROTATION, isRtl ? 90 : -90, 0));
// Translate back button to be at end/start of other buttons for keyguard
int navButtonSize = mContext.getResources().getDimensionPixelSize(
R.dimen.taskbar_nav_buttons_size);
@@ -289,16 +352,16 @@
// home and recents buttons
- View homeButton = addButton(R.drawable.ic_sysbar_home, BUTTON_HOME, navContainer,
+ mHomeButton = addButton(R.drawable.ic_sysbar_home, BUTTON_HOME, navContainer,
navButtonController, R.id.home);
- mPropertyHolders.add(new StatePropertyHolder(homeButton,
+ mPropertyHolders.add(new StatePropertyHolder(mHomeButton,
flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0 &&
(flags & FLAG_DISABLE_HOME) == 0));
View recentsButton = addButton(R.drawable.ic_sysbar_recent, BUTTON_RECENTS,
navContainer, navButtonController, R.id.recent_apps);
mPropertyHolders.add(new StatePropertyHolder(recentsButton,
- flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0 &&
- (flags & FLAG_DISABLE_RECENTS) == 0));
+ flags -> (flags & FLAG_KEYGUARD_VISIBLE) == 0 && (flags & FLAG_DISABLE_RECENTS) == 0
+ && !mContext.isNavBarKidsModeActive()));
// A11y button
mA11yButton = addButton(R.drawable.ic_sysbar_accessibility_button, BUTTON_A11Y,
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index b1f027a..1198dc5 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -112,6 +112,7 @@
private final boolean mIsSafeModeEnabled;
private final boolean mIsUserSetupComplete;
+ private final boolean mIsNavBarKidsMode;
private boolean mIsDestroyed = false;
// The flag to know if the window is excluded from magnification region computation.
private boolean mIsExcludeFromMagnificationRegion = false;
@@ -129,6 +130,8 @@
() -> getPackageManager().isSafeMode());
mIsUserSetupComplete = SettingsCache.INSTANCE.get(this).getValue(
Settings.Secure.getUriFor(Settings.Secure.USER_SETUP_COMPLETE), 0);
+ mIsNavBarKidsMode = SettingsCache.INSTANCE.get(this).getValue(
+ Settings.Secure.getUriFor(Settings.Secure.NAV_BAR_KIDS_MODE), 0);
final Resources resources = getResources();
float taskbarIconSize = resources.getDimension(R.dimen.taskbar_icon_size);
@@ -385,7 +388,8 @@
| SYSUI_STATE_QUICK_SETTINGS_EXPANDED;
onNotificationShadeExpandChanged((systemUiStateFlags & shadeExpandedFlags) != 0, fromInit);
mControllers.taskbarViewController.setRecentsButtonDisabled(
- mControllers.navbarButtonsViewController.isRecentsDisabled());
+ mControllers.navbarButtonsViewController.isRecentsDisabled()
+ || isNavBarKidsModeActive());
mControllers.stashedHandleViewController.setIsHomeButtonDisabled(
mControllers.navbarButtonsViewController.isHomeDisabled());
mControllers.taskbarKeyguardController.updateStateForSysuiFlags(systemUiStateFlags);
@@ -587,6 +591,10 @@
return mIsUserSetupComplete;
}
+ protected boolean isNavBarKidsModeActive() {
+ return mIsNavBarKidsMode && isThreeButtonNav();
+ }
+
/**
* Called when we determine the touchable region.
*
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index 7f2e37c..a262674 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -63,11 +63,15 @@
private static final Uri USER_SETUP_COMPLETE_URI = Settings.Secure.getUriFor(
Settings.Secure.USER_SETUP_COMPLETE);
+ private static final Uri NAV_BAR_KIDS_MODE = Settings.Secure.getUriFor(
+ Settings.Secure.NAV_BAR_KIDS_MODE);
+
private final Context mContext;
private final DisplayController mDisplayController;
private final SysUINavigationMode mSysUINavigationMode;
private final TaskbarNavButtonController mNavButtonController;
private final SettingsCache.OnChangeListener mUserSetupCompleteListener;
+ private final SettingsCache.OnChangeListener mNavBarKidsModeListener;
private final ComponentCallbacks mComponentCallbacks;
private final SimpleBroadcastReceiver mShutdownReceiver;
@@ -97,6 +101,7 @@
mNavButtonController = new TaskbarNavButtonController(service,
SystemUiProxy.INSTANCE.get(mContext), new Handler());
mUserSetupCompleteListener = isUserSetupComplete -> recreateTaskbar();
+ mNavBarKidsModeListener = isNavBarKidsMode -> recreateTaskbar();
mComponentCallbacks = new ComponentCallbacks() {
private Configuration mOldConfig = mContext.getResources().getConfiguration();
@@ -126,6 +131,8 @@
mSysUINavigationMode.addModeChangeListener(this);
SettingsCache.INSTANCE.get(mContext).register(USER_SETUP_COMPLETE_URI,
mUserSetupCompleteListener);
+ SettingsCache.INSTANCE.get(mContext).register(NAV_BAR_KIDS_MODE,
+ mNavBarKidsModeListener);
mContext.registerComponentCallbacks(mComponentCallbacks);
mShutdownReceiver.register(mContext, Intent.ACTION_SHUTDOWN);
@@ -289,6 +296,8 @@
mSysUINavigationMode.removeModeChangeListener(this);
SettingsCache.INSTANCE.get(mContext).unregister(USER_SETUP_COMPLETE_URI,
mUserSetupCompleteListener);
+ SettingsCache.INSTANCE.get(mContext).unregister(NAV_BAR_KIDS_MODE,
+ mNavBarKidsModeListener);
mContext.unregisterComponentCallbacks(mComponentCallbacks);
mContext.unregisterReceiver(mShutdownReceiver);
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
index c85b256..0eaea83 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/BaseRecentsViewStateController.java
@@ -29,12 +29,9 @@
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_HORIZONTAL_OFFSET;
import static com.android.quickstep.views.RecentsView.RECENTS_GRID_PROGRESS;
import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
-import static com.android.quickstep.views.RecentsView.TASK_PRIMARY_SPLIT_TRANSLATION;
-import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_SPLIT_TRANSLATION;
import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_TRANSLATION;
import android.util.FloatProperty;
-import android.util.Pair;
import androidx.annotation.NonNull;
@@ -43,7 +40,6 @@
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.statemanager.StateManager.StateHandler;
import com.android.launcher3.states.StateAnimationConfig;
-import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.quickstep.views.RecentsView;
/**
@@ -100,15 +96,6 @@
config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_X, LINEAR));
setter.setFloat(mRecentsView, TASK_SECONDARY_TRANSLATION, 0f,
config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, LINEAR));
- PagedOrientationHandler orientationHandler =
- ((RecentsView) mLauncher.getOverviewPanel()).getPagedOrientationHandler();
- Pair<FloatProperty, FloatProperty> taskViewsFloat =
- orientationHandler.getSplitSelectTaskOffset(
- TASK_PRIMARY_SPLIT_TRANSLATION, TASK_SECONDARY_SPLIT_TRANSLATION,
- mLauncher.getDeviceProfile());
- setter.setFloat(mRecentsView, taskViewsFloat.first,
- toState.getSplitSelectTranslation(mLauncher), LINEAR);
- setter.setFloat(mRecentsView, taskViewsFloat.second, 0, LINEAR);
setter.setFloat(mRecentsView, getContentAlphaProperty(), toState.overviewUi ? 1 : 0,
config.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT));
diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
index 1cf50f7..c058aa7 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepInteractionHandler.java
@@ -71,7 +71,7 @@
}
}
activityOptions.options.setPendingIntentLaunchFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- activityOptions.options.setSplashscreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_EMPTY);
+ activityOptions.options.setSplashScreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_EMPTY);
Object itemInfo = hostView.getTag();
if (itemInfo instanceof ItemInfo) {
mLauncher.addLaunchCookie((ItemInfo) itemInfo, activityOptions.options);
diff --git a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
index 19897a1..32ce1c4 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/RecentsViewStateController.java
@@ -23,13 +23,17 @@
import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
import static com.android.quickstep.views.RecentsView.TASK_MODALNESS;
+import static com.android.quickstep.views.RecentsView.TASK_PRIMARY_SPLIT_TRANSLATION;
+import static com.android.quickstep.views.RecentsView.TASK_SECONDARY_SPLIT_TRANSLATION;
import static com.android.quickstep.views.TaskView.FLAG_UPDATE_ALL;
import android.annotation.TargetApi;
import android.os.Build;
import android.util.FloatProperty;
+import android.util.Pair;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.LauncherState;
@@ -37,6 +41,7 @@
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.states.StateAnimationConfig;
+import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.quickstep.views.ClearAllButton;
import com.android.quickstep.views.LauncherRecentsView;
@@ -67,11 +72,7 @@
// DepthController to prevent optimizations which might occlude the layers behind
mLauncher.getDepthController().setHasContentBehindLauncher(state.overviewUi);
- if (isSplitSelectionState(state)) {
- mRecentsView.applySplitPrimaryScrollOffset();
- } else {
- mRecentsView.resetSplitPrimaryScrollOffset();
- }
+ handleSplitSelectionState(state, null);
}
@Override
@@ -92,16 +93,7 @@
builder.addListener(AnimatorListeners.forSuccessCallback(() ->
mLauncher.getDepthController().setHasContentBehindLauncher(toState.overviewUi)));
- // Create or dismiss split screen select animations
- LauncherState currentState = mLauncher.getStateManager().getState();
- if (isSplitSelectionState(toState) && !isSplitSelectionState(currentState)) {
- builder.add(mRecentsView.createSplitSelectInitAnimation().buildAnim());
- }
- if (isSplitSelectionState(toState)) {
- mRecentsView.applySplitPrimaryScrollOffset();
- } else {
- mRecentsView.resetSplitPrimaryScrollOffset();
- }
+ handleSplitSelectionState(toState, builder);
setAlphas(builder, config, toState);
builder.setFloat(mRecentsView, FULLSCREEN_PROGRESS,
@@ -109,10 +101,52 @@
}
/**
- * @return true if {@param toState} is {@link LauncherState#OVERVIEW_SPLIT_SELECT}
+ * Create or dismiss split screen select animations.
+ * @param builder if null then this will run the split select animations right away, otherwise
+ * will add animations to builder.
*/
- private boolean isSplitSelectionState(@NonNull LauncherState toState) {
- return toState == OVERVIEW_SPLIT_SELECT;
+ private void handleSplitSelectionState(@NonNull LauncherState toState,
+ @Nullable PendingAnimation builder) {
+ LauncherState currentState = mLauncher.getStateManager().getState();
+ boolean animate = builder != null;
+ PagedOrientationHandler orientationHandler =
+ ((RecentsView) mLauncher.getOverviewPanel()).getPagedOrientationHandler();
+ Pair<FloatProperty, FloatProperty> taskViewsFloat =
+ orientationHandler.getSplitSelectTaskOffset(
+ TASK_PRIMARY_SPLIT_TRANSLATION, TASK_SECONDARY_SPLIT_TRANSLATION,
+ mLauncher.getDeviceProfile());
+
+ if (isSplitSelectionState(currentState, toState)) {
+ // Animation to "dismiss" selected taskView
+ PendingAnimation splitSelectInitAnimation =
+ mRecentsView.createSplitSelectInitAnimation();
+ // Add properties to shift remaining taskViews to get out of placeholder view
+ splitSelectInitAnimation.setFloat(mRecentsView, taskViewsFloat.first,
+ toState.getSplitSelectTranslation(mLauncher), LINEAR);
+ splitSelectInitAnimation.setFloat(mRecentsView, taskViewsFloat.second, 0, LINEAR);
+
+ if (!animate && isSplitSelectionState(currentState, toState)) {
+ splitSelectInitAnimation.buildAnim().start();
+ } else if (animate &&
+ isSplitSelectionState(currentState, toState)) {
+ builder.add(splitSelectInitAnimation.buildAnim());
+ }
+ }
+
+ if (isSplitSelectionState(currentState, toState)) {
+ mRecentsView.applySplitPrimaryScrollOffset();
+ } else {
+ mRecentsView.resetSplitPrimaryScrollOffset();
+ }
+ }
+
+ /**
+ * @return true if {@param toState} is {@link LauncherState#OVERVIEW_SPLIT_SELECT}
+ * and {@param fromState} is not {@link LauncherState#OVERVIEW_SPLIT_SELECT}
+ */
+ private boolean isSplitSelectionState(@NonNull LauncherState fromState,
+ @NonNull LauncherState toState) {
+ return fromState != OVERVIEW_SPLIT_SELECT && toState == OVERVIEW_SPLIT_SELECT;
}
private void setAlphas(PropertySetter propertySetter, StateAnimationConfig config,
diff --git a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
index 0e2fbbc..75cf5cb 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/states/QuickstepAtomicAnimationFactory.java
@@ -17,6 +17,7 @@
import static android.view.View.VISIBLE;
+import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.HINT_STATE;
import static com.android.launcher3.LauncherState.HINT_STATE_TWO_BUTTON;
import static com.android.launcher3.LauncherState.NORMAL;
@@ -44,6 +45,10 @@
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_SCALE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_WORKSPACE_TRANSLATE;
+import static com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController.ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD;
+import static com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController.ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD;
+import static com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController.ALL_APPS_SCRIM_OPAQUE_THRESHOLD;
+import static com.android.launcher3.uioverrides.touchcontrollers.PortraitStatesTouchController.ALL_APPS_SCRIM_VISIBLE_THRESHOLD;
import static com.android.quickstep.views.RecentsView.RECENTS_SCALE_PROPERTY;
import android.animation.ValueAnimator;
@@ -52,6 +57,7 @@
import com.android.launcher3.Hotseat;
import com.android.launcher3.LauncherState;
import com.android.launcher3.Workspace;
+import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.quickstep.SysUINavigationMode;
@@ -171,6 +177,13 @@
mHintToNormalDuration = (int) va.getDuration();
}
config.duration = Math.max(config.duration, mHintToNormalDuration);
+ } else if (fromState == ALL_APPS && toState == NORMAL) {
+ config.setInterpolator(ANIM_ALL_APPS_FADE, Interpolators.clampToProgress(DEACCEL,
+ 1 - ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD,
+ 1 - ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD));
+ config.setInterpolator(ANIM_SCRIM_FADE, Interpolators.clampToProgress(DEACCEL,
+ 1 - ALL_APPS_SCRIM_OPAQUE_THRESHOLD,
+ 1 - ALL_APPS_SCRIM_VISIBLE_THRESHOLD));
}
}
}
diff --git a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
index 3c83d25..59ade49 100644
--- a/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
+++ b/quickstep/src/com/android/launcher3/uioverrides/touchcontrollers/PortraitStatesTouchController.java
@@ -52,22 +52,22 @@
/**
* The progress at which all apps content will be fully visible.
*/
- protected static final float ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD = 0.8f;
+ public static final float ALL_APPS_CONTENT_FADE_MAX_CLAMPING_THRESHOLD = 0.8f;
/**
* Minimum clamping progress for fading in all apps content
*/
- protected static final float ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD = 0.5f;
+ public static final float ALL_APPS_CONTENT_FADE_MIN_CLAMPING_THRESHOLD = 0.5f;
/**
* Minimum clamping progress for fading in all apps scrim
*/
- protected static final float ALL_APPS_SCRIM_VISIBLE_THRESHOLD = .1f;
+ public static final float ALL_APPS_SCRIM_VISIBLE_THRESHOLD = .1f;
/**
* Maximum clamping progress for opaque all apps scrim
*/
- protected static final float ALL_APPS_SCRIM_OPAQUE_THRESHOLD = .5f;
+ public static final float ALL_APPS_SCRIM_OPAQUE_THRESHOLD = .5f;
private final PortraitOverviewStateTouchHelper mOverviewPortraitStateTouchHelper;
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index c7f1edd..9f1e47f 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -1001,7 +1001,7 @@
}
if (mRecentsAnimationTargets != null) {
TaskViewUtils.setSplitAuxiliarySurfacesShown(mRecentsAnimationTargets.nonApps,
- true /*shown*/, true /*animate*/);
+ true /*shown*/, false /*animate*/);
}
break;
}
@@ -1654,7 +1654,7 @@
if (mRecentsAnimationTargets != null) {
TaskViewUtils.setSplitAuxiliarySurfacesShown(mRecentsAnimationTargets.nonApps,
- true /*shown*/, true /*animate*/);
+ true /*shown*/, false /*animate*/);
}
// Leave the pending invisible flag, as it may be used by wallpaper open animation.
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index 01f6e23..ed0623d 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -20,6 +20,7 @@
import static com.android.launcher3.logging.StatsLogManager.LAUNCHER_STATE_OVERVIEW;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
+import android.annotation.Nullable;
import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.content.Intent;
@@ -397,11 +398,15 @@
* while STATE_RECENTS_ANIMATION_CANCELED state is being set, and the caller is responsible for
* calling {@link RecentsAnimationController#cleanupScreenshot()}.
*/
+ @Nullable
HashMap<Integer, ThumbnailData> consumeRecentsAnimationCanceledSnapshot() {
- HashMap<Integer, ThumbnailData> data =
- new HashMap<Integer, ThumbnailData>(mRecentsAnimationCanceledSnapshots);
- mRecentsAnimationCanceledSnapshots = null;
- return data;
+ if (mRecentsAnimationCanceledSnapshots != null) {
+ HashMap<Integer, ThumbnailData> data =
+ new HashMap<Integer, ThumbnailData>(mRecentsAnimationCanceledSnapshots);
+ mRecentsAnimationCanceledSnapshots = null;
+ return data;
+ }
+ return null;
}
void setSwipeUpStartTimeMs(long uptimeMs) {
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index 103f350..306e864 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -255,7 +255,7 @@
final ActivityOptionsWrapper activityOptions = new ActivityOptionsWrapper(
ActivityOptionsCompat.makeRemoteAnimation(adapterCompat),
onEndCallback);
- activityOptions.options.setSplashscreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_ICON);
+ activityOptions.options.setSplashScreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_ICON);
return activityOptions;
}
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java
index 8a923bf..b428b67 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.java
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java
@@ -34,7 +34,6 @@
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.Log;
-import android.util.Slog;
import android.view.MotionEvent;
import android.view.RemoteAnimationAdapter;
import android.view.RemoteAnimationTarget;
@@ -772,10 +771,8 @@
public ArrayList<GroupedRecentTaskInfo> getRecentTasks(int numTasks, int userId) {
if (mRecentTasks != null) {
try {
- final GroupedRecentTaskInfo[] tasks = mRecentTasks.getRecentTasks(numTasks,
- RECENT_IGNORE_UNAVAILABLE, userId);
- Log.d("b/206648922", "getRecentTasks(" + numTasks + "): result=" + tasks);
- return new ArrayList<>(Arrays.asList(tasks));
+ return new ArrayList<>(Arrays.asList(mRecentTasks.getRecentTasks(numTasks,
+ RECENT_IGNORE_UNAVAILABLE, userId)));
} catch (RemoteException e) {
Log.w(TAG, "Failed call getRecentTasks", e);
}
diff --git a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
index 0246849..276e1c2 100644
--- a/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskOverlayFactory.java
@@ -23,6 +23,7 @@
import static com.android.quickstep.views.OverviewActionsView.DISABLED_ROTATED;
import android.annotation.SuppressLint;
+import android.app.ActivityManager;
import android.content.Context;
import android.graphics.Insets;
import android.graphics.Matrix;
@@ -54,6 +55,7 @@
import com.android.quickstep.views.TaskView.TaskIdAttributeContainer;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
import java.util.ArrayList;
import java.util.List;
@@ -115,8 +117,9 @@
* Does NOT add split options in the following scenarios:
* * The taskView to add split options is already showing split screen tasks
* * There aren't at least 2 tasks in overview to show split options for
+ * * Device is in "Lock task mode"
* * The taskView to show split options for is the focused task AND we haven't started
- * scrolling in overview (if we haven't scrolled, there's a split overview action so
+ * scrolling in overview (if we haven't scrolled, there's a split overview action button so
* we don't need this menu option)
*/
private static void addSplitOptions(List<SystemShortcut> outShortcuts,
@@ -130,7 +133,11 @@
boolean isFocusedTask = deviceProfile.overviewShowAsGrid && taskView.isFocusedTask();
boolean isTaskInExpectedScrollPosition =
recentsView.isTaskInExpectedScrollPosition(recentsView.indexOfChild(taskView));
- if (taskViewHasMultipleTasks || notEnoughTasksToSplit ||
+ ActivityManager activityManager =
+ (ActivityManager) taskView.getContext().getSystemService(Context.ACTIVITY_SERVICE);
+ boolean isLockTaskMode = activityManager.isInLockTaskMode();
+
+ if (taskViewHasMultipleTasks || notEnoughTasksToSplit || isLockTaskMode ||
(isFocusedTask && isTaskInExpectedScrollPosition)) {
return;
}
diff --git a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
index cbdbdb5..ecb30e5 100644
--- a/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
+++ b/quickstep/src/com/android/quickstep/TaskShortcutFactory.java
@@ -190,7 +190,7 @@
ActivityOptions options = mFactory.makeLaunchOptions(mTarget);
if (options != null) {
- options.setSplashscreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_ICON);
+ options.setSplashScreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_ICON);
}
if (options != null
&& ActivityManagerWrapper.getInstance().startActivityFromRecents(taskId,
@@ -242,6 +242,7 @@
}
}
+ /** @Deprecated */
TaskShortcutFactory SPLIT_SCREEN = new MultiWindowFactory(R.drawable.ic_split_screen,
R.string.recent_task_option_split_screen, LAUNCHER_SYSTEM_SHORTCUT_SPLIT_SCREEN_TAP) {
diff --git a/quickstep/src/com/android/quickstep/TaskViewUtils.java b/quickstep/src/com/android/quickstep/TaskViewUtils.java
index 14f3ec4..5d9a537 100644
--- a/quickstep/src/com/android/quickstep/TaskViewUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskViewUtils.java
@@ -84,6 +84,7 @@
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams;
import java.util.ArrayList;
+import java.util.List;
/**
* Utility class for helpful methods related to {@link TaskView} objects and their tasks.
@@ -606,13 +607,13 @@
}
SurfaceControl.Transaction t = new SurfaceControl.Transaction();
- SurfaceControl[] auxiliarySurfaces = new SurfaceControl[nonApps.length];
+ List<SurfaceControl> auxiliarySurfaces = new ArrayList<>(nonApps.length);
boolean hasSurfaceToAnimate = false;
for (int i = 0; i < nonApps.length; ++i) {
final RemoteAnimationTargetCompat targ = nonApps[i];
final SurfaceControl leash = targ.leash.getSurfaceControl();
if (targ.windowType == TYPE_DOCK_DIVIDER && leash != null) {
- auxiliarySurfaces[i] = leash;
+ auxiliarySurfaces.add(leash);
hasSurfaceToAnimate = true;
}
}
diff --git a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
index d310893..5253e8c 100644
--- a/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
+++ b/quickstep/src/com/android/quickstep/util/SplitSelectStateController.java
@@ -64,7 +64,6 @@
private @StagePosition int mStagePosition;
private Task mInitialTask;
private Task mSecondTask;
- private Rect mInitialBounds;
private boolean mRecentsAnimationRunning;
/** If not null, this is the TaskView we want to launch from */
@Nullable
@@ -86,7 +85,6 @@
Rect initialBounds) {
mInitialTask = task;
mStagePosition = stagePosition;
- mInitialBounds = initialBounds;
}
/**
@@ -229,7 +227,6 @@
mInitialTask = null;
mSecondTask = null;
mStagePosition = SplitConfigurationOptions.STAGE_POSITION_UNDEFINED;
- mInitialBounds = null;
mRecentsAnimationRunning = false;
mLaunchingTaskView = null;
}
@@ -241,8 +238,4 @@
public boolean isSplitSelectActive() {
return mInitialTask != null && mSecondTask == null;
}
-
- public Rect getInitialBounds() {
- return mInitialBounds;
- }
}
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index 10efa3e..e863a89 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -1144,17 +1144,6 @@
// Reset the running task when leaving overview since it can still have a reference to
// its thumbnail
mTmpRunningTasks = null;
- // Remove grouped tasks and recycle once we exit overview
- int taskCount = getTaskViewCount();
- for (int i = 0; i < taskCount; i++) {
- View v = getTaskViewAt(i);
- if (!(v instanceof GroupedTaskView)) {
- return;
- }
- GroupedTaskView gtv = (GroupedTaskView) v;
- gtv.onTaskListVisibilityChanged(false);
- removeView(gtv);
- }
mSplitBoundsConfig = null;
}
updateLocusId();
@@ -2185,17 +2174,14 @@
*/
protected boolean shouldAddStubTaskView(RunningTaskInfo[] runningTaskInfos) {
if (runningTaskInfos.length > 1) {
- // * Always create new view for GroupedTaskView
- // * Remove existing associated taskViews for tasks currently in split
- for (RunningTaskInfo rti : runningTaskInfos) {
- TaskView taskView = getTaskViewByTaskId(rti.taskId);
- if (taskView == null) {
- continue;
- }
- taskView.onTaskListVisibilityChanged(false);
- removeView(taskView);
- }
- return true;
+ TaskView primaryTaskView = getTaskViewByTaskId(runningTaskInfos[0].taskId);
+ TaskView secondaryTaskView = getTaskViewByTaskId(runningTaskInfos[1].taskId);
+ int leftTopTaskViewId =
+ (primaryTaskView == null) ? -1 : primaryTaskView.getTaskViewId();
+ int rightBottomTaskViewId =
+ (secondaryTaskView == null) ? -1 : secondaryTaskView.getTaskViewId();
+ // Add a new stub view if both taskIds don't match any taskViews
+ return leftTopTaskViewId != rightBottomTaskViewId || leftTopTaskViewId == -1;
}
RunningTaskInfo runningTaskInfo = runningTaskInfos[0];
return runningTaskInfo != null && getTaskViewByTaskId(runningTaskInfo.taskId) == null;
@@ -2246,7 +2232,7 @@
measure(makeMeasureSpec(getMeasuredWidth(), EXACTLY),
makeMeasureSpec(getMeasuredHeight(), EXACTLY));
layout(getLeft(), getTop(), getRight(), getBottom());
- } else if (!needGroupTaskView && getTaskViewByTaskId(taskInfo.taskId) != null) {
+ } else if (getTaskViewByTaskId(taskInfo.taskId) != null) {
runningTaskViewId = getTaskViewByTaskId(taskInfo.taskId).getTaskViewId();
}
@@ -3994,7 +3980,7 @@
}
/** TODO(b/181707736) More gracefully handle exiting split selection state */
- private void resetFromSplitSelectionState() {
+ protected void resetFromSplitSelectionState() {
if (mSplitHiddenTaskViewIndex == -1) {
return;
}
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index c9cd313..2a442a7 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -472,7 +472,9 @@
stubInfo.user = componentKey.user;
stubInfo.intent = new Intent().setComponent(componentKey.componentName);
stubInfo.title = task.title;
- stubInfo.screenId = getRecentsView().indexOfChild(this);
+ if (getRecentsView() != null) {
+ stubInfo.screenId = getRecentsView().indexOfChild(this);
+ }
return stubInfo;
}
diff --git a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
index 8d489e3..318f04e 100644
--- a/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
+++ b/quickstep/tests/src/com/android/quickstep/NavigationModeSwitchRule.java
@@ -220,7 +220,7 @@
if (!condition) {
final AssertionError assertionError = new AssertionError(message);
if (description != null) {
- FailureWatcher.onError(launcher.getDevice(), description, assertionError);
+ FailureWatcher.onError(launcher, description, assertionError);
}
throw assertionError;
}
diff --git a/res/raw/downgrade_schema.json b/res/raw/downgrade_schema.json
index 14eac9f..b8d0c6f 100644
--- a/res/raw/downgrade_schema.json
+++ b/res/raw/downgrade_schema.json
@@ -2,8 +2,9 @@
// Note: Comments are not supported in JSON schema, but android parser is lenient.
// Maximum DB version supported by this schema
- "version" : 30,
+ "version" : 31,
+ "downgrade_to_30" : [],
"downgrade_to_29" : [],
"downgrade_to_28" : [
"ALTER TABLE favorites RENAME TO temp_favorites;",
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index 08570eb..6db0af3 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -133,6 +133,8 @@
<attr name="numRows" format="integer" />
<attr name="numColumns" format="integer" />
+ <!-- numSearchContainerColumns defaults to numColumns, if not specified -->
+ <attr name="numSearchContainerColumns" format="integer" />
<!-- numFolderRows & numFolderColumns defaults to numRows & numColumns, if not specified -->
<attr name="numFolderRows" format="integer" />
<attr name="numFolderColumns" format="integer" />
@@ -182,76 +184,95 @@
<attr name="minHeightDps" format="float" />
<!-- These min cell values are only used if GridDisplayOption#isScalable is true -->
- <attr name="minCellHeightDps" format="float" />
- <attr name="minCellWidthDps" format="float" />
- <!-- twoPanelPortraitMinCellWidthDps defaults to minCellHeightDps, if not specified -->
- <attr name="twoPanelPortraitMinCellHeightDps" format="float" />
- <!-- twoPanelPortraitMinCellHeightDps defaults to minCellWidthDps, if not specified -->
- <attr name="twoPanelPortraitMinCellWidthDps" format="float" />
- <!-- twoPanelLandscapeMinCellHeightDps defaults to minCellHeightDps, if not specified -->
- <attr name="twoPanelLandscapeMinCellHeightDps" format="float" />
- <!-- twoPanelLandscapeMinCellWidthDps defaults to minCellWidthDps, if not specified -->
- <attr name="twoPanelLandscapeMinCellWidthDps" format="float" />
+ <attr name="minCellHeight" format="float" />
+ <attr name="minCellWidth" format="float" />
+ <!-- defaults to minCellHeight, if not specified -->
+ <attr name="minCellHeightTwoPanelPortrait" format="float" />
+ <!-- defaults to minCellWidth, if not specified -->
+ <attr name="minCellWidthTwoPanelPortrait" format="float" />
+ <!-- defaults to minCellHeight, if not specified -->
+ <attr name="minCellHeightTwoPanelLandscape" format="float" />
+ <!-- defaults to minCellWidth, if not specified -->
+ <attr name="minCellWidthTwoPanelLandscape" format="float" />
<!-- These border spaces are only used if GridDisplayOption#isScalable is true -->
<!-- space to be used horizontally and vertically -->
- <attr name="borderSpaceDps" format="float" />
- <!-- space to the right of the cell, defaults to borderSpaceDps if not specified -->
- <attr name="borderSpaceHorizontalDps" format="float" />
- <!-- space below the cell, defaults to borderSpaceDps if not specified -->
- <attr name="borderSpaceVerticalDps" format="float" />
+ <attr name="borderSpace" format="float" />
+ <!-- space to the right of the cell, defaults to borderSpace if not specified -->
+ <attr name="borderSpaceHorizontal" format="float" />
+ <!-- space below the cell, defaults to borderSpace if not specified -->
+ <attr name="borderSpaceVertical" format="float" />
<!-- space to be used horizontally and vertically in two panels,
- defaults to borderSpaceDps if not specified -->
- <attr name="twoPanelPortraitBorderSpaceDps" format="float" />
+ defaults to borderSpace if not specified -->
+ <attr name="borderSpaceTwoPanelPortrait" format="float" />
<!-- space to the right of the cell in two panels, defaults to
- twoPanelPortraitBorderSpaceDps if not specified -->
- <attr name="twoPanelPortraitBorderSpaceHorizontalDps" format="float" />
- <!-- space below the cell in two panels, defaults to twoPanelPortraitBorderSpaceDps
+ borderSpaceTwoPanelPortrait if not specified -->
+ <attr name="borderSpaceTwoPanelPortraitHorizontal" format="float" />
+ <!-- space below the cell in two panels, defaults to borderSpaceTwoPanelPortrait
if not specified -->
- <attr name="twoPanelPortraitBorderSpaceVerticalDps" format="float" />
+ <attr name="borderSpaceTwoPanelPortraitVertical" format="float" />
<!-- space to be used horizontally and vertically in two panels,
- defaults to borderSpaceDps if not specified -->
- <attr name="twoPanelLandscapeBorderSpaceDps" format="float" />
+ defaults to borderSpace if not specified -->
+ <attr name="borderSpaceTwoPanelLandscape" format="float" />
<!-- space to the right of the cell in two panels, defaults to
- twoPanelLandscapeBorderSpaceDps if not specified -->
- <attr name="twoPanelLandscapeBorderSpaceHorizontalDps" format="float" />
- <!-- space below the cell in two panels, defaults to twoPanelLandscapeBorderSpaceDps
+ borderSpaceTwoPanelLandscape if not specified -->
+ <attr name="borderSpaceTwoPanelLandscapeHorizontal" format="float" />
+ <!-- space below the cell in two panels, defaults to borderSpaceTwoPanelLandscape
if not specified -->
- <attr name="twoPanelLandscapeBorderSpaceVerticalDps" format="float" />
+ <attr name="borderSpaceTwoPanelLandscapeVertical" format="float" />
- <!-- allAppsCellSpacingDps defaults to borderSpaceDps, if not specified -->
- <attr name="allAppsCellSpacingDps" format="float" />
+ <!-- defaults to borderSpace, if not specified -->
+ <attr name="allAppsBorderSpace" format="float" />
+ <!-- defaults to allAppsBorderSpace, if not specified -->
+ <attr name="allAppsBorderSpaceTwoPanelPortrait" format="float" />
+ <!-- defaults to allAppsBorderSpace, if not specified -->
+ <attr name="allAppsBorderSpaceTwoPanelLandscape" format="float" />
<!-- The following values are only enabled if grid is supported. -->
- <!-- allAppsIconSize defaults to iconSize, if not specified -->
+ <!-- defaults to iconImageSize, if not specified -->
<attr name="allAppsIconSize" format="float" />
- <!-- allAppsIconTextSize defaults to iconTextSize, if not specified -->
+ <!-- defaults to allAppsIconSize, if not specified -->
+ <attr name="allAppsIconSizeTwoPanelPortrait" format="float" />
+ <!-- defaults to allAppsIconSize, if not specified -->
+ <attr name="allAppsIconSizeTwoPanelLandscape" format="float" />
+ <!-- defaults to iconTextSize, if not specified -->
<attr name="allAppsIconTextSize" format="float" />
+ <!-- defaults to allAppsIconTextSize, if not specified -->
+ <attr name="allAppsIconTextSizeTwoPanelPortrait" format="float" />
+ <!-- defaults to allAppsIconTextSize, if not specified -->
+ <attr name="allAppsIconTextSizeTwoPanelLandscape" format="float" />
+
+ <!-- defaults to borderSpaceDps, if not specified -->
+ <attr name="hotseatBorderSpace" format="float" />
+ <!-- defaults to hotseatBorderSpace, if not specified -->
+ <attr name="hotseatBorderSpaceTwoPanelLandscape" format="float" />
+ <!-- defaults to hotseatBorderSpace, if not specified -->
+ <attr name="hotseatBorderSpaceTwoPanelPortrait" format="float" />
<attr name="iconImageSize" format="float" />
- <!-- landscapeIconSize defaults to iconImageSize, if not specified -->
- <attr name="landscapeIconSize" format="float" />
- <!-- twoPanelPortraitIconSize defaults to iconImageSize, if not specified -->
- <attr name="twoPanelPortraitIconSize" format="float" />
- <!-- twoPanelLandscapeIconSize defaults to iconImageSize, if not specified -->
- <attr name="twoPanelLandscapeIconSize" format="float" />
+ <!-- defaults to iconImageSize, if not specified -->
+ <attr name="iconSizeLandscape" format="float" />
+ <!-- defaults to iconSize, if not specified -->
+ <attr name="iconSizeTwoPanelPortrait" format="float" />
+ <!-- defaults to iconSize, if not specified -->
+ <attr name="iconSizeTwoPanelLandscape" format="float" />
<attr name="iconTextSize" format="float" />
- <!-- landscapeIconTextSize defaults to iconTextSize, if not specified -->
- <attr name="landscapeIconTextSize" format="float" />
- <!-- twoPanelPortraitIconTextSize defaults to iconTextSize, if not specified -->
- <attr name="twoPanelPortraitIconTextSize" format="float" />
- <!-- twoPanelLandscapeIconTextSize defaults to iconTextSize, if not specified -->
- <attr name="twoPanelLandscapeIconTextSize" format="float" />
+ <!-- defaults to iconTextSize, if not specified -->
+ <attr name="iconTextSizeLandscape" format="float" />
+ <!-- defaults to iconTextSize, if not specified -->
+ <attr name="iconTextSizeTwoPanelPortrait" format="float" />
+ <!-- defaults to iconTextSize, if not specified -->
+ <attr name="iconTextSizeTwoPanelLandscape" format="float" />
<!-- If set, this display option is used to determine the default grid -->
<attr name="canBeDefault" format="boolean" />
<!-- Margin on left and right of the workspace when GridDisplayOption#isScalable is true -->
<attr name="horizontalMargin" format="float"/>
- <!-- twoPanelLandscapeHorizontalMargin defaults to horizontalMargin if not specified -->
- <attr name="twoPanelLandscapeHorizontalMargin" format="float"/>
- <!-- twoPanelPortraitHorizontalMargin defaults to horizontalMargin if not specified -->
- <attr name="twoPanelPortraitHorizontalMargin" format="float"/>
+ <!-- defaults to horizontalMargin if not specified -->
+ <attr name="horizontalMarginTwoPanelLandscape" format="float"/>
+ <!-- defaults to horizontalMargin if not specified -->
+ <attr name="horizontalMarginTwoPanelPortrait" format="float"/>
</declare-styleable>
<declare-styleable name="CellLayout">
diff --git a/res/xml/backupscheme.xml b/res/xml/backupscheme.xml
index 299e92e..0f0dde2 100644
--- a/res/xml/backupscheme.xml
+++ b/res/xml/backupscheme.xml
@@ -2,6 +2,11 @@
<full-backup-content xmlns:android="http://schemas.android.com/apk/res/android">
<include domain="database" path="launcher.db" />
+ <include domain="database" path="launcher_6_by_5.db" />
+ <include domain="database" path="launcher_4_by_5.db" />
+ <include domain="database" path="launcher_4_by_4.db" />
+ <include domain="database" path="launcher_3_by_3.db" />
+ <include domain="database" path="launcher_2_by_2.db" />
<include domain="sharedpref" path="com.android.launcher3.prefs.xml" />
<include domain="file" path="downgrade_schema.json" />
diff --git a/res/xml/device_profiles.xml b/res/xml/device_profiles.xml
index 08698e7..dd201e5 100644
--- a/res/xml/device_profiles.xml
+++ b/res/xml/device_profiles.xml
@@ -140,6 +140,7 @@
launcher:name="6_by_5"
launcher:numRows="5"
launcher:numColumns="6"
+ launcher:numSearchContainerColumns="3"
launcher:numFolderRows="3"
launcher:numFolderColumns="3"
launcher:numHotseatIcons="6"
@@ -152,14 +153,14 @@
launcher:name="Tablet"
launcher:minWidthDps="900"
launcher:minHeightDps="820"
- launcher:minCellHeightDps="104"
- launcher:minCellWidthDps="80"
+ launcher:minCellHeight="104"
+ launcher:minCellWidth="80"
launcher:iconImageSize="60"
launcher:iconTextSize="14"
- launcher:borderSpaceDps="16"
+ launcher:borderSpace="16"
launcher:allAppsIconSize="60"
launcher:allAppsIconTextSize="14"
- launcher:allAppsCellSpacingDps="16"
+ launcher:allAppsBorderSpace="16"
launcher:canBeDefault="true" />
</grid-option>
diff --git a/res/xml/grayscale_icon_map.xml b/res/xml/grayscale_icon_map.xml
new file mode 100644
index 0000000..f6383ce
--- /dev/null
+++ b/res/xml/grayscale_icon_map.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright (C) 2022 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.
+-->
+<icons></icons>
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 624afe1..ab8e5e7 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -161,12 +161,13 @@
public final int hotseatBarSidePaddingStartPx;
public final int hotseatBarSidePaddingEndPx;
public final int hotseatQsbHeight;
+ public final int hotseatBorderSpace;
public final float qsbBottomMarginOriginalPx;
public int qsbBottomMarginPx;
// All apps
- public Point allAppsCellSpacePx;
+ public Point allAppsBorderSpacePx;
public int allAppsOpenVerticalTranslate;
public int allAppsCellHeightPx;
public int allAppsCellWidthPx;
@@ -290,9 +291,9 @@
folderContentPaddingTop = res.getDimensionPixelSize(R.dimen.folder_content_padding_top);
cellLayoutBorderSpacePx = getCellLayoutBorderSpace(inv);
- allAppsCellSpacePx = new Point(
- pxFromDp(inv.borderSpaces[InvariantDeviceProfile.INDEX_ALL_APPS].x, mMetrics, 1f),
- pxFromDp(inv.borderSpaces[InvariantDeviceProfile.INDEX_ALL_APPS].y, mMetrics, 1f));
+ allAppsBorderSpacePx = new Point(
+ pxFromDp(inv.allAppsBorderSpaces[mTypeIndex].x, mMetrics),
+ pxFromDp(inv.allAppsBorderSpaces[mTypeIndex].y, mMetrics));
cellLayoutBorderSpaceOriginalPx = new Point(cellLayoutBorderSpacePx);
folderCellLayoutBorderSpaceOriginalPx = pxFromDp(inv.folderBorderSpace, mMetrics, 1f);
folderCellLayoutBorderSpacePx = new Point(folderCellLayoutBorderSpaceOriginalPx,
@@ -349,8 +350,9 @@
hotseatBarSidePaddingStartPx = isVerticalBarLayout() ? workspacePageIndicatorHeight : 0;
hotseatExtraVerticalSize =
res.getDimensionPixelSize(R.dimen.dynamic_grid_hotseat_extra_vertical_size);
+ hotseatBorderSpace = pxFromDp(inv.hotseatBorderSpaces[mTypeIndex], mMetrics);
updateHotseatIconSize(
- pxFromDp(inv.iconSize[InvariantDeviceProfile.INDEX_DEFAULT], mMetrics, 1f));
+ pxFromDp(inv.iconSize[InvariantDeviceProfile.INDEX_DEFAULT], mMetrics));
qsbBottomMarginOriginalPx = isScalableGrid
? res.getDimensionPixelSize(R.dimen.scalable_grid_qsb_bottom_margin)
@@ -596,7 +598,7 @@
private void updateAllAppsWidth() {
if (isTwoPanels) {
int usedWidth = (allAppsCellWidthPx * numShownAllAppsColumns)
- + (allAppsCellSpacePx.x * (numShownAllAppsColumns + 1));
+ + (allAppsBorderSpacePx.x * (numShownAllAppsColumns + 1));
allAppsLeftRightPadding = Math.max(1, (availableWidthPx - usedWidth) / 2);
} else {
allAppsLeftRightPadding =
@@ -695,9 +697,9 @@
// All apps
if (numShownAllAppsColumns != inv.numColumns) {
allAppsIconSizePx =
- pxFromDp(inv.iconSize[InvariantDeviceProfile.INDEX_ALL_APPS], mMetrics);
+ pxFromDp(inv.allAppsIconSize[mTypeIndex], mMetrics);
allAppsIconTextSizePx =
- pxFromSp(inv.iconTextSize[InvariantDeviceProfile.INDEX_ALL_APPS], mMetrics);
+ pxFromSp(inv.allAppsIconTextSize[mTypeIndex], mMetrics);
allAppsIconDrawablePaddingPx = iconDrawablePaddingOriginalPx;
autoResizeAllAppsCells();
} else {
@@ -706,7 +708,7 @@
allAppsIconDrawablePaddingPx = iconDrawablePaddingPx;
allAppsCellHeightPx = getCellSize().y;
}
- allAppsCellWidthPx = allAppsIconSizePx + allAppsIconDrawablePaddingPx;
+ allAppsCellWidthPx = allAppsIconSizePx + (2 * allAppsIconDrawablePaddingPx);
updateAllAppsWidth();
if (isVerticalLayout) {
@@ -895,14 +897,10 @@
int hotseatTopDiff = hotseatHeight - taskbarOffset;
int endOffset = ApiWrapper.getHotseatEndOffset(context);
- int requiredWidth = iconSizePx * numShownHotseatIcons;
+ int requiredWidth = iconSizePx * numShownHotseatIcons
+ + hotseatBorderSpace * (numShownHotseatIcons - 1);
- Resources res = context.getResources();
- float taskbarIconSize = res.getDimension(R.dimen.taskbar_icon_size);
- float taskbarIconSpacing = 2 * res.getDimension(R.dimen.taskbar_icon_spacing);
- int maxSize = (int) (requiredWidth
- * (taskbarIconSize + taskbarIconSpacing) / taskbarIconSize);
- int hotseatSize = Math.min(maxSize, availableWidthPx - endOffset);
+ int hotseatSize = Math.min(requiredWidth, availableWidthPx - endOffset);
int sideSpacing = (availableWidthPx - hotseatSize) / 2;
mHotseatPadding.set(sideSpacing, hotseatTopDiff, sideSpacing, taskbarOffset);
@@ -1063,8 +1061,10 @@
writer.println(prefix + "\tisScalableGrid:" + isScalableGrid);
- writer.println(prefix + "\tinv.numColumns: " + inv.numColumns);
writer.println(prefix + "\tinv.numRows: " + inv.numRows);
+ writer.println(prefix + "\tinv.numColumns: " + inv.numColumns);
+ writer.println(prefix + "\tinv.numSearchContainerColumns: "
+ + inv.numSearchContainerColumns);
writer.println(prefix + "\tminCellSize: " + inv.minCellSize[mTypeIndex] + "dp");
diff --git a/src/com/android/launcher3/InvariantDeviceProfile.java b/src/com/android/launcher3/InvariantDeviceProfile.java
index ff7a90c..94325d0 100644
--- a/src/com/android/launcher3/InvariantDeviceProfile.java
+++ b/src/com/android/launcher3/InvariantDeviceProfile.java
@@ -94,18 +94,18 @@
// Used for arrays to specify different sizes (e.g. border spaces, width/height) in different
// constraints
- static final int COUNT_SIZES = 5;
+ static final int COUNT_SIZES = 4;
static final int INDEX_DEFAULT = 0;
static final int INDEX_LANDSCAPE = 1;
static final int INDEX_TWO_PANEL_PORTRAIT = 2;
static final int INDEX_TWO_PANEL_LANDSCAPE = 3;
- static final int INDEX_ALL_APPS = 4;
/**
* Number of icons per row and column in the workspace.
*/
public int numRows;
public int numColumns;
+ public int numSearchContainerColumns;
/**
* Number of icons per row and column in the folder.
@@ -122,9 +122,14 @@
public PointF[] borderSpaces;
public float folderBorderSpace;
+ public float[] hotseatBorderSpaces;
public float[] horizontalMargin;
+ public float[] allAppsIconSize;
+ public float[] allAppsIconTextSize;
+ public PointF[] allAppsBorderSpaces;
+
private SparseArray<TypedValue> mExtraAttrs;
/**
@@ -300,6 +305,7 @@
GridOption closestProfile = displayOption.grid;
numRows = closestProfile.numRows;
numColumns = closestProfile.numColumns;
+ numSearchContainerColumns = closestProfile.numSearchContainerColumns;
dbFile = closestProfile.dbFile;
defaultLayoutId = closestProfile.defaultLayoutId;
demoModeLayoutId = closestProfile.demoModeLayoutId;
@@ -331,14 +337,18 @@
numShownHotseatIcons = closestProfile.numHotseatIcons;
numDatabaseHotseatIcons = deviceType == TYPE_MULTI_DISPLAY
? closestProfile.numDatabaseHotseatIcons : closestProfile.numHotseatIcons;
+ hotseatBorderSpaces = displayOption.hotseatBorderSpaces;
numAllAppsColumns = closestProfile.numAllAppsColumns;
numDatabaseAllAppsColumns = deviceType == TYPE_MULTI_DISPLAY
? closestProfile.numDatabaseAllAppsColumns : closestProfile.numAllAppsColumns;
+ allAppsBorderSpaces = displayOption.allAppsBorderSpaces;
+ allAppsIconSize = displayOption.allAppsIconSizes;
+ allAppsIconTextSize = displayOption.allAppsIconTextSizes;
if (!Utilities.isGridOptionsEnabled(context)) {
- iconSize[INDEX_ALL_APPS] = iconSize[INDEX_DEFAULT];
- iconTextSize[INDEX_ALL_APPS] = iconTextSize[INDEX_DEFAULT];
+ allAppsIconSize = iconSize;
+ allAppsIconTextSize = iconTextSize;
}
if (devicePaddingId != 0) {
@@ -393,8 +403,8 @@
private Object[] toModelState() {
return new Object[]{
- numColumns, numRows, numDatabaseHotseatIcons, iconBitmapSize, fillResIconDpi,
- numDatabaseAllAppsColumns, dbFile};
+ numColumns, numRows, numSearchContainerColumns, numDatabaseHotseatIcons,
+ iconBitmapSize, fillResIconDpi, numDatabaseAllAppsColumns, dbFile};
}
private void onConfigChanged(Context context) {
@@ -671,6 +681,7 @@
public final String name;
public final int numRows;
public final int numColumns;
+ public final int numSearchContainerColumns;
public final boolean isEnabled;
private final int numFolderRows;
@@ -697,6 +708,8 @@
name = a.getString(R.styleable.GridDisplayOption_name);
numRows = a.getInt(R.styleable.GridDisplayOption_numRows, 0);
numColumns = a.getInt(R.styleable.GridDisplayOption_numColumns, 0);
+ numSearchContainerColumns = a.getInt(
+ R.styleable.GridDisplayOption_numSearchContainerColumns, numColumns);
dbFile = a.getString(R.styleable.GridDisplayOption_dbFile);
defaultLayoutId = a.getResourceId(deviceType == TYPE_MULTI_DISPLAY && a.hasValue(
@@ -756,10 +769,15 @@
private float folderBorderSpace;
private final PointF[] borderSpaces = new PointF[COUNT_SIZES];
private final float[] horizontalMargin = new float[COUNT_SIZES];
+ private final float[] hotseatBorderSpaces = new float[COUNT_SIZES];
private final float[] iconSizes = new float[COUNT_SIZES];
private final float[] textSizes = new float[COUNT_SIZES];
+ private final float[] allAppsIconSizes = new float[COUNT_SIZES];
+ private final float[] allAppsIconTextSizes = new float[COUNT_SIZES];
+ private final PointF[] allAppsBorderSpaces = new PointF[COUNT_SIZES];
+
DisplayOption(GridOption grid, Context context, AttributeSet attrs) {
this.grid = grid;
@@ -773,97 +791,129 @@
float x;
float y;
- x = a.getFloat(R.styleable.ProfileDisplayOption_minCellWidthDps, 0);
- y = a.getFloat(R.styleable.ProfileDisplayOption_minCellHeightDps, 0);
+ x = a.getFloat(R.styleable.ProfileDisplayOption_minCellWidth, 0);
+ y = a.getFloat(R.styleable.ProfileDisplayOption_minCellHeight, 0);
minCellSize[INDEX_DEFAULT] = new PointF(x, y);
minCellSize[INDEX_LANDSCAPE] = new PointF(x, y);
- minCellSize[INDEX_ALL_APPS] = new PointF(x, y);
- x = a.getFloat(R.styleable.ProfileDisplayOption_twoPanelPortraitMinCellWidthDps,
+ x = a.getFloat(R.styleable.ProfileDisplayOption_minCellWidthTwoPanelPortrait,
minCellSize[INDEX_DEFAULT].x);
- y = a.getFloat(R.styleable.ProfileDisplayOption_twoPanelPortraitMinCellHeightDps,
+ y = a.getFloat(R.styleable.ProfileDisplayOption_minCellHeightTwoPanelPortrait,
minCellSize[INDEX_DEFAULT].y);
minCellSize[INDEX_TWO_PANEL_PORTRAIT] = new PointF(x, y);
- x = a.getFloat(R.styleable.ProfileDisplayOption_twoPanelLandscapeMinCellWidthDps,
+ x = a.getFloat(R.styleable.ProfileDisplayOption_minCellWidthTwoPanelLandscape,
minCellSize[INDEX_DEFAULT].x);
- y = a.getFloat(R.styleable.ProfileDisplayOption_twoPanelLandscapeMinCellHeightDps,
+ y = a.getFloat(R.styleable.ProfileDisplayOption_minCellHeightTwoPanelLandscape,
minCellSize[INDEX_DEFAULT].y);
minCellSize[INDEX_TWO_PANEL_LANDSCAPE] = new PointF(x, y);
- float borderSpace = a.getFloat(R.styleable.ProfileDisplayOption_borderSpaceDps, 0);
- float twoPanelPortraitBorderSpaceDps = a.getFloat(
- R.styleable.ProfileDisplayOption_twoPanelPortraitBorderSpaceDps, borderSpace);
- float twoPanelLandscapeBorderSpaceDps = a.getFloat(
- R.styleable.ProfileDisplayOption_twoPanelLandscapeBorderSpaceDps, borderSpace);
+ float borderSpace = a.getFloat(R.styleable.ProfileDisplayOption_borderSpace, 0);
+ float borderSpaceTwoPanelPortrait = a.getFloat(
+ R.styleable.ProfileDisplayOption_borderSpaceTwoPanelPortrait, borderSpace);
+ float borderSpaceTwoPanelLandscape = a.getFloat(
+ R.styleable.ProfileDisplayOption_borderSpaceTwoPanelLandscape, borderSpace);
- x = a.getFloat(R.styleable.ProfileDisplayOption_borderSpaceHorizontalDps, borderSpace);
- y = a.getFloat(R.styleable.ProfileDisplayOption_borderSpaceVerticalDps, borderSpace);
+ x = a.getFloat(R.styleable.ProfileDisplayOption_borderSpaceHorizontal, borderSpace);
+ y = a.getFloat(R.styleable.ProfileDisplayOption_borderSpaceVertical, borderSpace);
borderSpaces[INDEX_DEFAULT] = new PointF(x, y);
borderSpaces[INDEX_LANDSCAPE] = new PointF(x, y);
x = a.getFloat(
- R.styleable.ProfileDisplayOption_twoPanelPortraitBorderSpaceHorizontalDps,
- twoPanelPortraitBorderSpaceDps);
+ R.styleable.ProfileDisplayOption_borderSpaceTwoPanelPortraitHorizontal,
+ borderSpaceTwoPanelPortrait);
y = a.getFloat(
- R.styleable.ProfileDisplayOption_twoPanelPortraitBorderSpaceVerticalDps,
- twoPanelPortraitBorderSpaceDps);
+ R.styleable.ProfileDisplayOption_borderSpaceTwoPanelPortraitVertical,
+ borderSpaceTwoPanelPortrait);
borderSpaces[INDEX_TWO_PANEL_PORTRAIT] = new PointF(x, y);
x = a.getFloat(
- R.styleable.ProfileDisplayOption_twoPanelLandscapeBorderSpaceHorizontalDps,
- twoPanelLandscapeBorderSpaceDps);
+ R.styleable.ProfileDisplayOption_borderSpaceTwoPanelLandscapeHorizontal,
+ borderSpaceTwoPanelLandscape);
y = a.getFloat(
- R.styleable.ProfileDisplayOption_twoPanelLandscapeBorderSpaceVerticalDps,
- twoPanelLandscapeBorderSpaceDps);
+ R.styleable.ProfileDisplayOption_borderSpaceTwoPanelLandscapeVertical,
+ borderSpaceTwoPanelLandscape);
borderSpaces[INDEX_TWO_PANEL_LANDSCAPE] = new PointF(x, y);
- x = y = a.getFloat(R.styleable.ProfileDisplayOption_allAppsCellSpacingDps,
- borderSpace);
- borderSpaces[INDEX_ALL_APPS] = new PointF(x, y);
folderBorderSpace = borderSpace;
+ x = y = a.getFloat(R.styleable.ProfileDisplayOption_allAppsBorderSpace,
+ borderSpace);
+ allAppsBorderSpaces[INDEX_DEFAULT] = new PointF(x, y);
+ allAppsBorderSpaces[INDEX_LANDSCAPE] = new PointF(x, y);
+ x = y = a.getFloat(
+ R.styleable.ProfileDisplayOption_allAppsBorderSpaceTwoPanelPortrait,
+ allAppsBorderSpaces[INDEX_DEFAULT].x);
+ allAppsBorderSpaces[INDEX_TWO_PANEL_PORTRAIT] = new PointF(x, y);
+ x = y = a.getFloat(
+ R.styleable.ProfileDisplayOption_allAppsBorderSpaceTwoPanelLandscape,
+ allAppsBorderSpaces[INDEX_DEFAULT].x);
+ allAppsBorderSpaces[INDEX_TWO_PANEL_LANDSCAPE] = new PointF(x, y);
+
iconSizes[INDEX_DEFAULT] =
a.getFloat(R.styleable.ProfileDisplayOption_iconImageSize, 0);
iconSizes[INDEX_LANDSCAPE] =
- a.getFloat(R.styleable.ProfileDisplayOption_landscapeIconSize,
- iconSizes[INDEX_DEFAULT]);
- iconSizes[INDEX_ALL_APPS] =
- a.getFloat(R.styleable.ProfileDisplayOption_allAppsIconSize,
+ a.getFloat(R.styleable.ProfileDisplayOption_iconSizeLandscape,
iconSizes[INDEX_DEFAULT]);
iconSizes[INDEX_TWO_PANEL_PORTRAIT] =
- a.getFloat(R.styleable.ProfileDisplayOption_twoPanelPortraitIconSize,
+ a.getFloat(R.styleable.ProfileDisplayOption_iconSizeTwoPanelPortrait,
iconSizes[INDEX_DEFAULT]);
iconSizes[INDEX_TWO_PANEL_LANDSCAPE] =
- a.getFloat(R.styleable.ProfileDisplayOption_twoPanelLandscapeIconSize,
+ a.getFloat(R.styleable.ProfileDisplayOption_iconSizeTwoPanelLandscape,
iconSizes[INDEX_DEFAULT]);
+ allAppsIconSizes[INDEX_DEFAULT] = a.getFloat(
+ R.styleable.ProfileDisplayOption_allAppsIconSize, iconSizes[INDEX_DEFAULT]);
+ allAppsIconSizes[INDEX_LANDSCAPE] = allAppsIconSizes[INDEX_DEFAULT];
+ allAppsIconSizes[INDEX_TWO_PANEL_PORTRAIT] = a.getFloat(
+ R.styleable.ProfileDisplayOption_allAppsIconSizeTwoPanelPortrait,
+ allAppsIconSizes[INDEX_DEFAULT]);
+ allAppsIconSizes[INDEX_TWO_PANEL_LANDSCAPE] = a.getFloat(
+ R.styleable.ProfileDisplayOption_allAppsIconSizeTwoPanelLandscape,
+ allAppsIconSizes[INDEX_DEFAULT]);
+
textSizes[INDEX_DEFAULT] =
a.getFloat(R.styleable.ProfileDisplayOption_iconTextSize, 0);
textSizes[INDEX_LANDSCAPE] =
- a.getFloat(R.styleable.ProfileDisplayOption_landscapeIconTextSize,
- textSizes[INDEX_DEFAULT]);
- textSizes[INDEX_ALL_APPS] =
- a.getFloat(R.styleable.ProfileDisplayOption_allAppsIconTextSize,
+ a.getFloat(R.styleable.ProfileDisplayOption_iconTextSizeLandscape,
textSizes[INDEX_DEFAULT]);
textSizes[INDEX_TWO_PANEL_PORTRAIT] =
- a.getFloat(R.styleable.ProfileDisplayOption_twoPanelPortraitIconTextSize,
+ a.getFloat(R.styleable.ProfileDisplayOption_iconTextSizeTwoPanelPortrait,
textSizes[INDEX_DEFAULT]);
textSizes[INDEX_TWO_PANEL_LANDSCAPE] =
- a.getFloat(R.styleable.ProfileDisplayOption_twoPanelLandscapeIconTextSize,
+ a.getFloat(R.styleable.ProfileDisplayOption_iconTextSizeTwoPanelLandscape,
textSizes[INDEX_DEFAULT]);
+ allAppsIconTextSizes[INDEX_DEFAULT] = a.getFloat(
+ R.styleable.ProfileDisplayOption_allAppsIconTextSize, textSizes[INDEX_DEFAULT]);
+ allAppsIconTextSizes[INDEX_LANDSCAPE] = allAppsIconTextSizes[INDEX_DEFAULT];
+ allAppsIconTextSizes[INDEX_TWO_PANEL_PORTRAIT] = a.getFloat(
+ R.styleable.ProfileDisplayOption_allAppsIconTextSizeTwoPanelPortrait,
+ allAppsIconTextSizes[INDEX_DEFAULT]);
+ allAppsIconTextSizes[INDEX_TWO_PANEL_LANDSCAPE] = a.getFloat(
+ R.styleable.ProfileDisplayOption_allAppsIconTextSizeTwoPanelLandscape,
+ allAppsIconTextSizes[INDEX_DEFAULT]);
+
horizontalMargin[INDEX_DEFAULT] = a.getFloat(
R.styleable.ProfileDisplayOption_horizontalMargin, 0);
horizontalMargin[INDEX_LANDSCAPE] = horizontalMargin[INDEX_DEFAULT];
- horizontalMargin[INDEX_ALL_APPS] = horizontalMargin[INDEX_DEFAULT];
horizontalMargin[INDEX_TWO_PANEL_LANDSCAPE] = a.getFloat(
- R.styleable.ProfileDisplayOption_twoPanelLandscapeHorizontalMargin,
+ R.styleable.ProfileDisplayOption_horizontalMarginTwoPanelLandscape,
horizontalMargin[INDEX_DEFAULT]);
horizontalMargin[INDEX_TWO_PANEL_PORTRAIT] = a.getFloat(
- R.styleable.ProfileDisplayOption_twoPanelPortraitHorizontalMargin,
+ R.styleable.ProfileDisplayOption_horizontalMarginTwoPanelPortrait,
horizontalMargin[INDEX_DEFAULT]);
+ hotseatBorderSpaces[INDEX_DEFAULT] = a.getFloat(
+ R.styleable.ProfileDisplayOption_hotseatBorderSpace, borderSpace);
+ hotseatBorderSpaces[INDEX_LANDSCAPE] = hotseatBorderSpaces[INDEX_DEFAULT];
+ hotseatBorderSpaces[INDEX_TWO_PANEL_LANDSCAPE] = a.getFloat(
+ R.styleable.ProfileDisplayOption_hotseatBorderSpaceTwoPanelLandscape,
+ hotseatBorderSpaces[INDEX_DEFAULT]);
+ hotseatBorderSpaces[INDEX_TWO_PANEL_PORTRAIT] = a.getFloat(
+ R.styleable.ProfileDisplayOption_hotseatBorderSpaceTwoPanelPortrait,
+ hotseatBorderSpaces[INDEX_DEFAULT]);
+
a.recycle();
}
@@ -881,6 +931,9 @@
textSizes[i] = 0;
borderSpaces[i] = new PointF();
minCellSize[i] = new PointF();
+ allAppsIconSizes[i] = 0;
+ allAppsIconTextSizes[i] = 0;
+ allAppsBorderSpaces[i] = new PointF();
}
}
@@ -893,6 +946,11 @@
minCellSize[i].x *= w;
minCellSize[i].y *= w;
horizontalMargin[i] *= w;
+ hotseatBorderSpaces[i] *= w;
+ allAppsIconSizes[i] *= w;
+ allAppsIconTextSizes[i] *= w;
+ allAppsBorderSpaces[i].x *= w;
+ allAppsBorderSpaces[i].y *= w;
}
folderBorderSpace *= w;
@@ -909,6 +967,11 @@
minCellSize[i].x += p.minCellSize[i].x;
minCellSize[i].y += p.minCellSize[i].y;
horizontalMargin[i] += p.horizontalMargin[i];
+ hotseatBorderSpaces[i] += p.hotseatBorderSpaces[i];
+ allAppsIconSizes[i] += p.allAppsIconSizes[i];
+ allAppsIconTextSizes[i] += p.allAppsIconTextSizes[i];
+ allAppsBorderSpaces[i].x += p.allAppsBorderSpaces[i].x;
+ allAppsBorderSpaces[i].y += p.allAppsBorderSpaces[i].y;
}
folderBorderSpace += p.folderBorderSpace;
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 7abdef8..bed4fa9 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -209,7 +209,7 @@
import com.android.launcher3.widget.custom.CustomWidgetManager;
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
import com.android.launcher3.widget.picker.WidgetsFullSheet;
-import com.android.systemui.plugins.OverlayPlugin;
+import com.android.systemui.plugins.LauncherOverlayPlugin;
import com.android.systemui.plugins.PluginListener;
import com.android.systemui.plugins.shared.LauncherExterns;
import com.android.systemui.plugins.shared.LauncherOverlayManager;
@@ -232,8 +232,8 @@
* Default launcher application.
*/
public class Launcher extends StatefulActivity<LauncherState> implements LauncherExterns,
- Callbacks, InvariantDeviceProfile.OnIDPChangeListener, PluginListener<OverlayPlugin>,
- LauncherOverlayCallbacks {
+ Callbacks, InvariantDeviceProfile.OnIDPChangeListener,
+ PluginListener<LauncherOverlayPlugin>, LauncherOverlayCallbacks {
public static final String TAG = "Launcher";
public static final ActivityTracker<Launcher> ACTIVITY_TRACKER = new ActivityTracker<>();
@@ -534,7 +534,7 @@
}
mOverlayManager = getDefaultOverlay();
PluginManagerWrapper.INSTANCE.get(this).addPluginListener(this,
- OverlayPlugin.class, false /* allowedMultiple */);
+ LauncherOverlayPlugin.class, false /* allowedMultiple */);
mRotationHelper.initialize();
TraceHelper.INSTANCE.endSection(traceToken);
@@ -560,12 +560,12 @@
}
@Override
- public void onPluginConnected(OverlayPlugin overlayManager, Context context) {
+ public void onPluginConnected(LauncherOverlayPlugin overlayManager, Context context) {
switchOverlay(() -> overlayManager.createOverlayManager(this, this));
}
@Override
- public void onPluginDisconnected(OverlayPlugin plugin) {
+ public void onPluginDisconnected(LauncherOverlayPlugin plugin) {
switchOverlay(this::getDefaultOverlay);
}
diff --git a/src/com/android/launcher3/LauncherAppState.java b/src/com/android/launcher3/LauncherAppState.java
index 10023b4..4501159 100644
--- a/src/com/android/launcher3/LauncherAppState.java
+++ b/src/com/android/launcher3/LauncherAppState.java
@@ -36,6 +36,7 @@
import com.android.launcher3.graphics.IconShape;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.IconProvider;
+import com.android.launcher3.icons.LauncherIconProvider;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.notification.NotificationListener;
import com.android.launcher3.pm.InstallSessionHelper;
@@ -61,7 +62,7 @@
private final Context mContext;
private final LauncherModel mModel;
- private final IconProvider mIconProvider;
+ private final LauncherIconProvider mIconProvider;
private final IconCache mIconCache;
private final InvariantDeviceProfile mInvariantDeviceProfile;
private final RunnableList mOnTerminateCallback = new RunnableList();
@@ -98,7 +99,7 @@
Intent.ACTION_MANAGED_PROFILE_UNAVAILABLE,
Intent.ACTION_MANAGED_PROFILE_UNLOCKED);
if (FeatureFlags.IS_STUDIO_BUILD) {
- modelChangeReceiver.register(mContext, ACTION_FORCE_ROLOAD);
+ modelChangeReceiver.register(mContext, Context.RECEIVER_EXPORTED, ACTION_FORCE_ROLOAD);
}
mOnTerminateCallback.add(() -> mContext.unregisterReceiver(modelChangeReceiver));
@@ -138,7 +139,7 @@
mContext = context;
mInvariantDeviceProfile = InvariantDeviceProfile.INSTANCE.get(context);
- mIconProvider = new IconProvider(context, Themes.isThemedIconEnabled(context));
+ mIconProvider = new LauncherIconProvider(context);
mIconCache = new IconCache(mContext, mInvariantDeviceProfile,
iconCacheFileName, mIconProvider);
mModel = new LauncherModel(context, this, mIconCache, new AppFilter(mContext),
diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java
index 68e19cb..e004a4b 100644
--- a/src/com/android/launcher3/LauncherProvider.java
+++ b/src/com/android/launcher3/LauncherProvider.java
@@ -97,7 +97,7 @@
* Represents the schema of the database. Changes in scheme need not be backwards compatible.
* When increasing the scheme version, ensure that downgrade_schema.json is updated
*/
- public static final int SCHEMA_VERSION = 30;
+ public static final int SCHEMA_VERSION = 31;
public static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".settings";
public static final String KEY_LAYOUT_PROVIDER_AUTHORITY = "KEY_LAYOUT_PROVIDER_AUTHORITY";
@@ -864,6 +864,19 @@
Favorites.SCREEN, IntArray.wrap(-777, -778)), null);
}
case 30: {
+ if (FeatureFlags.QSB_ON_FIRST_SCREEN) {
+ // Clean up first row in screen 0 as it might contain junk data.
+ Log.d(TAG, "Cleaning up first row");
+ db.delete(Favorites.TABLE_NAME,
+ String.format(Locale.ENGLISH,
+ "%1$s = %2$d AND %3$s = %4$d AND %5$s = %6$d",
+ Favorites.SCREEN, 0,
+ Favorites.CONTAINER, Favorites.CONTAINER_DESKTOP,
+ Favorites.CELLY, 0), null);
+ }
+ return;
+ }
+ case 31: {
// DB Upgraded successfully
return;
}
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index ac194d2..b062b91 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -76,9 +76,8 @@
import com.android.launcher3.dragndrop.FolderAdaptiveIcon;
import com.android.launcher3.graphics.GridCustomizationsProvider;
import com.android.launcher3.graphics.TintedDrawableSpan;
-import com.android.launcher3.icons.BitmapInfo;
-import com.android.launcher3.icons.FastBitmapDrawable;
import com.android.launcher3.icons.ShortcutCachingLogic;
+import com.android.launcher3.icons.ThemedIconDrawable;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.SearchActionItemInfo;
@@ -87,6 +86,7 @@
import com.android.launcher3.shortcuts.ShortcutRequest;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.PackageManagerHelper;
+import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.BaseDragLayer;
import com.android.launcher3.widget.PendingAddShortcutInfo;
@@ -123,8 +123,9 @@
public static final boolean ATLEAST_R = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R;
- public static final boolean ATLEAST_S = BuildCompat.isAtLeastS()
- || Build.VERSION.SDK_INT >= Build.VERSION_CODES.S;
+ public static final boolean ATLEAST_S = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S;
+
+ public static final boolean ATLEAST_T = BuildCompat.isAtLeastT();
/**
* Set on a motion event dispatched from the nav bar. See {@link MotionEvent#setEdgeFlags(int)}.
@@ -672,11 +673,19 @@
* @param outObj this is set to the internal data associated with {@param info},
* eg {@link LauncherActivityInfo} or {@link ShortcutInfo}.
*/
+ @TargetApi(Build.VERSION_CODES.TIRAMISU)
public static Drawable getFullDrawable(Context context, ItemInfo info, int width, int height,
Object[] outObj) {
Drawable icon = loadFullDrawableWithoutTheme(context, info, width, height, outObj);
- if (icon instanceof BitmapInfo.Extender) {
- icon = ((BitmapInfo.Extender) icon).getThemedDrawable(context);
+ if (ATLEAST_T && icon instanceof AdaptiveIconDrawable) {
+ AdaptiveIconDrawable aid = (AdaptiveIconDrawable) icon.mutate();
+ Drawable mono = aid.getMonochrome();
+ if (mono != null && Themes.isThemedIconEnabled(context)) {
+ int[] colors = ThemedIconDrawable.getColors(context);
+ mono = mono.mutate();
+ mono.setTint(colors[1]);
+ return new AdaptiveIconDrawable(new ColorDrawable(colors[0]), mono);
+ }
}
return icon;
}
@@ -719,8 +728,7 @@
return icon;
} else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_SEARCH_ACTION
&& info instanceof SearchActionItemInfo) {
- return new AdaptiveIconDrawable(
- new FastBitmapDrawable(((SearchActionItemInfo) info).bitmap), null);
+ return ((SearchActionItemInfo) info).bitmap.newIcon(context);
} else {
return null;
}
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 281dfea..65006ff 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -585,8 +585,8 @@
int cellVSpan = FeatureFlags.EXPANDED_SMARTSPACE.get()
? EXPANDED_SMARTSPACE_HEIGHT : DEFAULT_SMARTSPACE_HEIGHT;
- CellLayout.LayoutParams lp = new CellLayout.LayoutParams(0, 0, firstPage.getCountX(),
- cellVSpan);
+ int cellHSpan = mLauncher.getDeviceProfile().inv.numSearchContainerColumns;
+ CellLayout.LayoutParams lp = new CellLayout.LayoutParams(0, 0, cellHSpan, cellVSpan);
lp.canReorder = false;
if (!firstPage.addViewToCellLayout(mQsb, 0, R.id.search_container_workspace, lp, true)) {
Log.e(TAG, "Failed to add to item at (0, 0) to CellLayout");
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index abf002f..88ce57e 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -133,23 +133,25 @@
mObjectMap.put(InvariantDeviceProfile.INSTANCE, idp);
mObjectMap.put(LauncherAppState.INSTANCE,
new LauncherAppState(this, null /* iconCacheFileName */));
-
}
- public LauncherIcons newLauncherIcons(Context context, boolean shapeDetection) {
+ /**
+ * Creates a new LauncherIcons for the preview, skipping the global pool
+ */
+ public LauncherIcons newLauncherIcons(Context context) {
LauncherIconsForPreview launcherIconsForPreview = mIconPool.poll();
if (launcherIconsForPreview != null) {
return launcherIconsForPreview;
}
return new LauncherIconsForPreview(context, mIdp.fillResIconDpi, mIdp.iconBitmapSize,
- -1 /* poolId */, shapeDetection);
+ -1 /* poolId */);
}
private final class LauncherIconsForPreview extends LauncherIcons {
private LauncherIconsForPreview(Context context, int fillResIconDpi, int iconBitmapSize,
- int poolId, boolean shapeDetection) {
- super(context, fillResIconDpi, iconBitmapSize, poolId, shapeDetection);
+ int poolId) {
+ super(context, fillResIconDpi, iconBitmapSize, poolId);
}
@Override
@@ -456,10 +458,10 @@
}
IntArray ranks = getMissingHotseatRanks(currentWorkspaceItems,
mDp.numShownHotseatIcons);
- FixedContainerItems hotseatpredictions =
+ FixedContainerItems hotseatPredictions =
dataModel.extraItems.get(CONTAINER_HOTSEAT_PREDICTION);
- List<ItemInfo> predictions = hotseatpredictions == null
- ? Collections.emptyList() : hotseatpredictions.items;
+ List<ItemInfo> predictions = hotseatPredictions == null
+ ? Collections.emptyList() : hotseatPredictions.items;
int count = Math.min(ranks.size(), predictions.size());
for (int i = 0; i < count; i++) {
int rank = ranks.get(i);
diff --git a/src/com/android/launcher3/icons/LauncherIconProvider.java b/src/com/android/launcher3/icons/LauncherIconProvider.java
new file mode 100644
index 0000000..a7379bb
--- /dev/null
+++ b/src/com/android/launcher3/icons/LauncherIconProvider.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2022 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.icons;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.content.res.XmlResourceParser;
+import android.text.TextUtils;
+import android.util.ArrayMap;
+import android.util.Log;
+
+import com.android.launcher3.R;
+import com.android.launcher3.util.Themes;
+
+import org.xmlpull.v1.XmlPullParser;
+
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * Extension of {@link IconProvider} with support for overriding theme icons
+ */
+public class LauncherIconProvider extends IconProvider {
+
+ private static final String TAG_ICON = "icon";
+ private static final String ATTR_PACKAGE = "package";
+ private static final String ATTR_DRAWABLE = "drawable";
+
+ private static final String TAG = "LIconProvider";
+ private static final Map<String, ThemeData> DISABLED_MAP = Collections.emptyMap();
+
+ private Map<String, ThemeData> mThemedIconMap;
+ private boolean mSupportsIconTheme;
+
+ public LauncherIconProvider(Context context) {
+ super(context);
+ setIconThemeSupported(Themes.isThemedIconEnabled(context));
+ }
+
+ /**
+ * Enables or disables icon theme support
+ */
+ public void setIconThemeSupported(boolean isSupported) {
+ mSupportsIconTheme = isSupported;
+ mThemedIconMap = isSupported ? null : DISABLED_MAP;
+ }
+
+ @Override
+ protected ThemeData getThemeDataForPackage(String packageName) {
+ return getThemedIconMap().get(packageName);
+ }
+
+ @Override
+ public String getSystemIconState() {
+ return super.getSystemIconState() + (mSupportsIconTheme ? ",with-theme" : ",no-theme");
+ }
+
+ private Map<String, ThemeData> getThemedIconMap() {
+ if (mThemedIconMap != null) {
+ return mThemedIconMap;
+ }
+ ArrayMap<String, ThemeData> map = new ArrayMap<>();
+ Resources res = mContext.getResources();
+ try (XmlResourceParser parser = res.getXml(R.xml.grayscale_icon_map)) {
+ final int depth = parser.getDepth();
+ int type;
+ while ((type = parser.next()) != XmlPullParser.START_TAG
+ && type != XmlPullParser.END_DOCUMENT);
+
+ while (((type = parser.next()) != XmlPullParser.END_TAG
+ || parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
+ if (type != XmlPullParser.START_TAG) {
+ continue;
+ }
+ if (TAG_ICON.equals(parser.getName())) {
+ String pkg = parser.getAttributeValue(null, ATTR_PACKAGE);
+ int iconId = parser.getAttributeResourceValue(null, ATTR_DRAWABLE, 0);
+ if (iconId != 0 && !TextUtils.isEmpty(pkg)) {
+ map.put(pkg, new ThemeData(res, iconId));
+ }
+ }
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Unable to parse icon map", e);
+ }
+ mThemedIconMap = map;
+ return mThemedIconMap;
+ }
+}
diff --git a/src/com/android/launcher3/icons/LauncherIcons.java b/src/com/android/launcher3/icons/LauncherIcons.java
index bf7897e..5508c49 100644
--- a/src/com/android/launcher3/icons/LauncherIcons.java
+++ b/src/com/android/launcher3/icons/LauncherIcons.java
@@ -21,6 +21,7 @@
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.graphics.IconShape;
import com.android.launcher3.graphics.LauncherPreviewRenderer;
+import com.android.launcher3.util.Themes;
/**
* Wrapper class to provide access to {@link BaseIconFactory} and also to provide pool of this class
@@ -32,18 +33,13 @@
private static LauncherIcons sPool;
private static int sPoolId = 0;
- public static LauncherIcons obtain(Context context) {
- return obtain(context, IconShape.getShape().enableShapeDetection());
- }
-
/**
* Return a new Message instance from the global pool. Allows us to
* avoid allocating new objects in many cases.
*/
- public static LauncherIcons obtain(Context context, boolean shapeDetection) {
+ public static LauncherIcons obtain(Context context) {
if (context instanceof LauncherPreviewRenderer.PreviewContext) {
- return ((LauncherPreviewRenderer.PreviewContext) context).newLauncherIcons(context,
- shapeDetection);
+ return ((LauncherPreviewRenderer.PreviewContext) context).newLauncherIcons(context);
}
int poolId;
@@ -58,8 +54,7 @@
}
InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(context);
- return new LauncherIcons(context, idp.fillResIconDpi, idp.iconBitmapSize, poolId,
- shapeDetection);
+ return new LauncherIcons(context, idp.fillResIconDpi, idp.iconBitmapSize, poolId);
}
public static void clearPool() {
@@ -73,9 +68,9 @@
private LauncherIcons next;
- protected LauncherIcons(Context context, int fillResIconDpi, int iconBitmapSize, int poolId,
- boolean shapeDetection) {
- super(context, fillResIconDpi, iconBitmapSize, shapeDetection);
+ protected LauncherIcons(Context context, int fillResIconDpi, int iconBitmapSize, int poolId) {
+ super(context, fillResIconDpi, iconBitmapSize, IconShape.getShape().enableShapeDetection());
+ mMonoIconEnabled = Themes.isThemedIconEnabled(context);
mPoolId = poolId;
}
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 8b7bebc..a7cfdde 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -372,6 +372,9 @@
@UiEvent(doc = "Notification dismissed by swiping right.")
LAUNCHER_NOTIFICATION_DISMISSED(652),
+ @UiEvent(doc = "Current grid size is changed to 6.")
+ LAUNCHER_GRID_SIZE_6(930),
+
@UiEvent(doc = "Current grid size is changed to 5.")
LAUNCHER_GRID_SIZE_5(662),
diff --git a/src/com/android/launcher3/model/DeviceGridState.java b/src/com/android/launcher3/model/DeviceGridState.java
index fa11d4e..08c3149 100644
--- a/src/com/android/launcher3/model/DeviceGridState.java
+++ b/src/com/android/launcher3/model/DeviceGridState.java
@@ -22,6 +22,7 @@
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_GRID_SIZE_3;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_GRID_SIZE_4;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_GRID_SIZE_5;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_GRID_SIZE_6;
import android.content.Context;
import android.content.SharedPreferences;
@@ -84,6 +85,8 @@
public LauncherEvent getWorkspaceSizeEvent() {
if (!TextUtils.isEmpty(mGridSizeString)) {
switch (mGridSizeString.charAt(0)) {
+ case '6':
+ return LAUNCHER_GRID_SIZE_6;
case '5':
return LAUNCHER_GRID_SIZE_5;
case '4':
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index 08b38e8..739a64a 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -457,11 +457,13 @@
if (!occupied.containsKey(item.screenId)) {
GridOccupancy screen = new GridOccupancy(countX + 1, countY + 1);
- if (item.screenId == Workspace.FIRST_SCREEN_ID) {
- // Mark the first row as occupied (if the feature is enabled)
- // in order to account for the QSB.
+ if (item.screenId == Workspace.FIRST_SCREEN_ID && FeatureFlags.QSB_ON_FIRST_SCREEN) {
+ // Mark the first X columns (X is width of the search container) in the first row as
+ // occupied (if the feature is enabled) in order to account for the search
+ // container.
+ int spanX = mIDP.numSearchContainerColumns;
int spanY = FeatureFlags.EXPANDED_SMARTSPACE.get() ? 2 : 1;
- screen.markCells(0, 0, countX + 1, spanY, FeatureFlags.QSB_ON_FIRST_SCREEN);
+ screen.markCells(0, 0, spanX, spanY, true);
}
occupied.put(item.screenId, screen);
}
diff --git a/src/com/android/launcher3/util/SimpleBroadcastReceiver.java b/src/com/android/launcher3/util/SimpleBroadcastReceiver.java
index 465a0e8..4dfa5cc 100644
--- a/src/com/android/launcher3/util/SimpleBroadcastReceiver.java
+++ b/src/com/android/launcher3/util/SimpleBroadcastReceiver.java
@@ -39,10 +39,17 @@
* Helper method to register multiple actions
*/
public void register(Context context, String... actions) {
+ register(context, 0, actions);
+ }
+
+ /**
+ * Helper method to register multiple actions with one or more {@code flags}.
+ */
+ public void register(Context context, int flags, String... actions) {
IntentFilter filter = new IntentFilter();
for (String action : actions) {
filter.addAction(action);
}
- context.registerReceiver(this, filter);
+ context.registerReceiver(this, filter, flags);
}
}
diff --git a/src_plugins/com/android/systemui/plugins/OverlayPlugin.java b/src_plugins/com/android/systemui/plugins/LauncherOverlayPlugin.java
similarity index 88%
rename from src_plugins/com/android/systemui/plugins/OverlayPlugin.java
rename to src_plugins/com/android/systemui/plugins/LauncherOverlayPlugin.java
index 1edb692..9e22355 100644
--- a/src_plugins/com/android/systemui/plugins/OverlayPlugin.java
+++ b/src_plugins/com/android/systemui/plugins/LauncherOverlayPlugin.java
@@ -24,8 +24,8 @@
/**
* Implement this interface to add a -1 content on the home screen.
*/
-@ProvidesInterface(action = OverlayPlugin.ACTION, version = OverlayPlugin.VERSION)
-public interface OverlayPlugin extends Plugin {
+@ProvidesInterface(action = LauncherOverlayPlugin.ACTION, version = LauncherOverlayPlugin.VERSION)
+public interface LauncherOverlayPlugin extends Plugin {
String ACTION = "com.android.systemui.action.PLUGIN_LAUNCHER_OVERLAY";
int VERSION = 1;
diff --git a/tests/Android.bp b/tests/Android.bp
index 3670c37..8def20f 100644
--- a/tests/Android.bp
+++ b/tests/Android.bp
@@ -32,6 +32,7 @@
srcs: [
"src/com/android/launcher3/ui/AbstractLauncherUiTest.java",
"src/com/android/launcher3/ui/PortraitLandscapeRunner.java",
+ "src/com/android/launcher3/util/TestUtil.java",
"src/com/android/launcher3/util/Wait.java",
"src/com/android/launcher3/util/WidgetUtils.java",
"src/com/android/launcher3/util/rule/FailureWatcher.java",
@@ -71,6 +72,11 @@
platform_apis: true,
}
+android_library {
+ name: "Launcher3TestResources",
+ resource_dirs: ["res"],
+}
+
android_test {
name: "Launcher3Tests",
srcs: [
diff --git a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
index 5b940a8..f193e24 100644
--- a/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
+++ b/tests/src/com/android/launcher3/ui/TaplTestsLauncher3.java
@@ -38,6 +38,7 @@
import com.android.launcher3.tapl.FolderIcon;
import com.android.launcher3.tapl.Widgets;
import com.android.launcher3.tapl.Workspace;
+import com.android.launcher3.util.TestUtil;
import com.android.launcher3.widget.picker.WidgetsFullSheet;
import com.android.launcher3.widget.picker.WidgetsRecyclerView;
@@ -50,6 +51,7 @@
@RunWith(AndroidJUnit4.class)
public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
private static final String APP_NAME = "LauncherTestApp";
+ private static final String DUMMY_APP_NAME = "Aardwolf";
@Before
public void setUp() throws Exception {
@@ -435,7 +437,49 @@
}
}
+ private void verifyAppUninstalledFromAllApps(Workspace workspace, String appName) {
+ final AllApps allApps = workspace.switchToAllApps();
+ allApps.freeze();
+ try {
+ assertNull(appName + " app was found on all apps after being uninstalled",
+ allApps.tryGetAppIcon(appName));
+ } finally {
+ allApps.unfreeze();
+ }
+ }
+
+ @Test
+ @PortraitLandscape
+ public void testUninstallFromWorkspace() throws Exception {
+ TestUtil.installDummyApp();
+ try {
+ verifyAppUninstalledFromAllApps(
+ createShortcutIfNotExist(DUMMY_APP_NAME).uninstall(), DUMMY_APP_NAME);
+ } finally {
+ TestUtil.uninstallDummyApp();
+ }
+ }
+
+ @Test
+ @PortraitLandscape
+ public void testUninstallFromAllApps() throws Exception {
+ TestUtil.installDummyApp();
+ try {
+ Workspace workspace = mLauncher.getWorkspace();
+ final AllApps allApps = workspace.switchToAllApps();
+ allApps.freeze();
+ try {
+ workspace = allApps.getAppIcon(DUMMY_APP_NAME).uninstall();
+ } finally {
+ allApps.unfreeze();
+ }
+ verifyAppUninstalledFromAllApps(workspace, DUMMY_APP_NAME);
+ } finally {
+ TestUtil.uninstallDummyApp();
+ }
+ }
+
public static String getAppPackageName() {
return getInstrumentation().getContext().getPackageName();
}
-}
+}
\ No newline at end of file
diff --git a/tests/src/com/android/launcher3/ui/workspace/TwoPanelWorkspaceTest.java b/tests/src/com/android/launcher3/ui/workspace/TwoPanelWorkspaceTest.java
index b048cd4..122a130 100644
--- a/tests/src/com/android/launcher3/ui/workspace/TwoPanelWorkspaceTest.java
+++ b/tests/src/com/android/launcher3/ui/workspace/TwoPanelWorkspaceTest.java
@@ -19,6 +19,7 @@
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
+import static org.junit.Assume.assumeTrue;
import androidx.test.filters.LargeTest;
import androidx.test.runner.AndroidJUnit4;
@@ -47,20 +48,12 @@
@RunWith(AndroidJUnit4.class)
public class TwoPanelWorkspaceTest extends AbstractLauncherUiTest {
- Workspace mWorkspace;
-
@Before
public void setUp() throws Exception {
super.setUp();
TaplTestsLauncher3.initialize(this);
- mWorkspace = mLauncher.getWorkspace();
- }
- @Test
- public void testDragIconToRightPanel() {
- if (!mLauncher.isTwoPanels()) {
- return;
- }
+ assumeTrue(mLauncher.isTwoPanels());
// Pre verifying the screens
executeOnLauncher(launcher -> {
@@ -68,8 +61,15 @@
assertItemsOnPage(launcher, 0, "Play Store", "Maps");
assertPageEmpty(launcher, 1);
});
+ }
- mWorkspace.dragIcon(mWorkspace.getHotseatAppIcon("Chrome"), 1);
+ @Test
+ // TODO(b/197631877) Enable in portrait.
+ // @PortraitLandscape
+ public void testDragIconToRightPanel() {
+ Workspace workspace = mLauncher.getWorkspace();
+
+ workspace.dragIcon(workspace.getHotseatAppIcon("Chrome"), 1);
executeOnLauncher(launcher -> {
assertPagesExist(launcher, 0, 1);
@@ -79,19 +79,67 @@
}
@Test
- public void testDragIconToPage2() {
- if (!mLauncher.isTwoPanels()) {
- return;
- }
+ // TODO(b/197631877) Enable in portrait.
+ // @PortraitLandscape
+ public void testSinglePageDragIconWhenMultiplePageScrollingIsPossible() {
+ Workspace workspace = mLauncher.getWorkspace();
- // Pre verifying the screens
+ workspace.dragIcon(workspace.getHotseatAppIcon("Chrome"), 2);
+
+ workspace.flingBackward();
+
+ workspace.dragIcon(workspace.getWorkspaceAppIcon("Maps"), 3);
+
executeOnLauncher(launcher -> {
- assertPagesExist(launcher, 0, 1);
- assertItemsOnPage(launcher, 0, "Play Store", "Maps");
+ assertPagesExist(launcher, 0, 1, 2, 3);
+ assertItemsOnPage(launcher, 0, "Play Store");
assertPageEmpty(launcher, 1);
+ assertItemsOnPage(launcher, 2, "Chrome");
+ assertItemsOnPage(launcher, 3, "Maps");
});
- mWorkspace.dragIcon(mWorkspace.getWorkspaceAppIcon("Maps"), 2);
+ workspace.dragIcon(workspace.getWorkspaceAppIcon("Maps"), 3);
+
+ executeOnLauncher(launcher -> {
+ assertPagesExist(launcher, 0, 1, 2, 3, 4, 5);
+ assertItemsOnPage(launcher, 0, "Play Store");
+ assertPageEmpty(launcher, 1);
+ assertItemsOnPage(launcher, 2, "Chrome");
+ assertPageEmpty(launcher, 3);
+ assertPageEmpty(launcher, 4);
+ assertItemsOnPage(launcher, 5, "Maps");
+ });
+
+ workspace.dragIcon(workspace.getWorkspaceAppIcon("Maps"), -1);
+
+ executeOnLauncher(launcher -> {
+ assertPagesExist(launcher, 0, 1, 2, 3);
+ assertItemsOnPage(launcher, 0, "Play Store");
+ assertPageEmpty(launcher, 1);
+ assertItemsOnPage(launcher, 2, "Chrome");
+ assertItemsOnPage(launcher, 3, "Maps");
+ });
+
+ workspace.dragIcon(workspace.getWorkspaceAppIcon("Maps"), -1);
+
+ workspace.flingForward();
+
+ workspace.dragIcon(workspace.getWorkspaceAppIcon("Chrome"), -2);
+
+ executeOnLauncher(launcher -> {
+ assertPagesExist(launcher, 0, 1);
+ assertItemsOnPage(launcher, 0, "Chrome", "Play Store");
+ assertItemsOnPage(launcher, 1, "Maps");
+ });
+ }
+
+ @Test
+ // TODO(b/197631877) Enable in portrait.
+ // @PortraitLandscape
+ public void testDragIconToPage2() {
+ Workspace workspace = mLauncher.getWorkspace();
+
+ workspace.dragIcon(workspace.getWorkspaceAppIcon("Maps"), 2);
executeOnLauncher(launcher -> {
assertPagesExist(launcher, 0, 1, 2, 3);
@@ -103,19 +151,12 @@
}
@Test
+ // TODO(b/197631877) Enable in portrait.
+ // @PortraitLandscape
public void testDragIconToPage3() {
- if (!mLauncher.isTwoPanels()) {
- return;
- }
+ Workspace workspace = mLauncher.getWorkspace();
- // Pre verifying the screens
- executeOnLauncher(launcher -> {
- assertPagesExist(launcher, 0, 1);
- assertItemsOnPage(launcher, 0, "Play Store", "Maps");
- assertPageEmpty(launcher, 1);
- });
-
- mWorkspace.dragIcon(mWorkspace.getHotseatAppIcon("Phone"), 3);
+ workspace.dragIcon(workspace.getHotseatAppIcon("Phone"), 3);
executeOnLauncher(launcher -> {
assertPagesExist(launcher, 0, 1, 2, 3);
@@ -126,22 +167,61 @@
});
}
-
@Test
- public void testEmptyPageDoesNotGetRemovedIfPagePairIsNotEmpty() {
- if (!mLauncher.isTwoPanels()) {
- return;
- }
+ // TODO(b/197631877) Enable in portrait.
+ // @PortraitLandscape
+ public void testMultiplePageDragIcon() {
+ Workspace workspace = mLauncher.getWorkspace();
- // Pre verifying the screens
+ workspace.dragIcon(workspace.getHotseatAppIcon("Messages"), 2);
+
+ workspace.flingBackward();
+
+ workspace.dragIcon(workspace.getWorkspaceAppIcon("Maps"), 5);
+
executeOnLauncher(launcher -> {
- assertPagesExist(launcher, 0, 1);
- assertItemsOnPage(launcher, 0, "Play Store", "Maps");
+ assertPagesExist(launcher, 0, 1, 2, 3, 4, 5);
+ assertItemsOnPage(launcher, 0, "Play Store");
assertPageEmpty(launcher, 1);
+ assertItemsOnPage(launcher, 2, "Messages");
+ assertPageEmpty(launcher, 3);
+ assertPageEmpty(launcher, 4);
+ assertItemsOnPage(launcher, 5, "Maps");
});
- mWorkspace.dragIcon(mWorkspace.getWorkspaceAppIcon("Maps"), 3);
- mWorkspace.dragIcon(mWorkspace.getHotseatAppIcon("Chrome"), 0);
+ workspace.flingBackward();
+
+ workspace.dragIcon(workspace.getWorkspaceAppIcon("Messages"), 4);
+
+ executeOnLauncher(launcher -> {
+ assertPagesExist(launcher, 0, 1, 4, 5, 6, 7);
+ assertItemsOnPage(launcher, 0, "Play Store");
+ assertPageEmpty(launcher, 1);
+ assertPageEmpty(launcher, 4);
+ assertItemsOnPage(launcher, 5, "Maps");
+ assertItemsOnPage(launcher, 6, "Messages");
+ assertPageEmpty(launcher, 7);
+ });
+
+ workspace.dragIcon(workspace.getWorkspaceAppIcon("Messages"), -3);
+
+ executeOnLauncher(launcher -> {
+ assertPagesExist(launcher, 0, 1, 4, 5);
+ assertItemsOnPage(launcher, 0, "Play Store");
+ assertItemsOnPage(launcher, 1, "Messages");
+ assertPageEmpty(launcher, 4);
+ assertItemsOnPage(launcher, 5, "Maps");
+ });
+ }
+
+ @Test
+ // TODO(b/197631877) Enable in portrait.
+ // @PortraitLandscape
+ public void testEmptyPageDoesNotGetRemovedIfPagePairIsNotEmpty() {
+ Workspace workspace = mLauncher.getWorkspace();
+
+ workspace.dragIcon(workspace.getWorkspaceAppIcon("Maps"), 3);
+ workspace.dragIcon(workspace.getHotseatAppIcon("Chrome"), 0);
executeOnLauncher(launcher -> {
assertPagesExist(launcher, 0, 1, 2, 3);
@@ -151,7 +231,7 @@
assertItemsOnPage(launcher, 3, "Maps");
});
- mWorkspace.dragIcon(mWorkspace.getWorkspaceAppIcon("Maps"), -1);
+ workspace.dragIcon(workspace.getWorkspaceAppIcon("Maps"), -1);
executeOnLauncher(launcher -> {
assertPagesExist(launcher, 0, 1, 2, 3);
@@ -163,8 +243,8 @@
// Move Chrome to the right panel as well, to make sure pages are not deleted whichever
// page is the empty one
- mWorkspace.flingForward();
- mWorkspace.dragIcon(mWorkspace.getWorkspaceAppIcon("Chrome"), 1);
+ workspace.flingForward();
+ workspace.dragIcon(workspace.getWorkspaceAppIcon("Chrome"), 1);
executeOnLauncher(launcher -> {
assertPagesExist(launcher, 0, 1, 2, 3);
@@ -175,22 +255,14 @@
});
}
-
@Test
+ // TODO(b/197631877) Enable in portrait.
+ // @PortraitLandscape
public void testEmptyPagesGetRemovedIfBothPagesAreEmpty() {
- if (!mLauncher.isTwoPanels()) {
- return;
- }
+ Workspace workspace = mLauncher.getWorkspace();
- // Pre verifying the screens
- executeOnLauncher(launcher -> {
- assertPagesExist(launcher, 0, 1);
- assertItemsOnPage(launcher, 0, "Play Store", "Maps");
- assertPageEmpty(launcher, 1);
- });
-
- mWorkspace.dragIcon(mWorkspace.getWorkspaceAppIcon("Play Store"), 2);
- mWorkspace.dragIcon(mWorkspace.getHotseatAppIcon("Camera"), 1);
+ workspace.dragIcon(workspace.getWorkspaceAppIcon("Play Store"), 2);
+ workspace.dragIcon(workspace.getHotseatAppIcon("Camera"), 1);
executeOnLauncher(launcher -> {
assertPagesExist(launcher, 0, 1, 2, 3);
@@ -200,9 +272,9 @@
assertItemsOnPage(launcher, 3, "Camera");
});
- mWorkspace.dragIcon(mWorkspace.getWorkspaceAppIcon("Camera"), -1);
- mWorkspace.flingForward();
- mWorkspace.dragIcon(mWorkspace.getWorkspaceAppIcon("Play Store"), -2);
+ workspace.dragIcon(workspace.getWorkspaceAppIcon("Camera"), -1);
+ workspace.flingForward();
+ workspace.dragIcon(workspace.getWorkspaceAppIcon("Play Store"), -2);
executeOnLauncher(launcher -> {
assertPagesExist(launcher, 0, 1);
@@ -212,20 +284,13 @@
}
@Test
+ // TODO(b/197631877) Enable in portrait.
+ // @PortraitLandscape
public void testMiddleEmptyPagesGetRemoved() {
- if (!mLauncher.isTwoPanels()) {
- return;
- }
+ Workspace workspace = mLauncher.getWorkspace();
- // Pre verifying the screens
- executeOnLauncher(launcher -> {
- assertPagesExist(launcher, 0, 1);
- assertItemsOnPage(launcher, 0, "Play Store", "Maps");
- assertPageEmpty(launcher, 1);
- });
-
- mWorkspace.dragIcon(mWorkspace.getWorkspaceAppIcon("Maps"), 2);
- mWorkspace.dragIcon(mWorkspace.getHotseatAppIcon("Messages"), 3);
+ workspace.dragIcon(workspace.getWorkspaceAppIcon("Maps"), 2);
+ workspace.dragIcon(workspace.getHotseatAppIcon("Messages"), 3);
executeOnLauncher(launcher -> {
assertPagesExist(launcher, 0, 1, 2, 3, 4, 5);
@@ -237,8 +302,8 @@
assertItemsOnPage(launcher, 5, "Messages");
});
- mWorkspace.flingBackward();
- mWorkspace.dragIcon(mWorkspace.getWorkspaceAppIcon("Maps"), 2);
+ workspace.flingBackward();
+ workspace.dragIcon(workspace.getWorkspaceAppIcon("Maps"), 2);
executeOnLauncher(launcher -> {
assertPagesExist(launcher, 0, 1, 4, 5);
diff --git a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
index 65aaa24..657f213 100644
--- a/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
+++ b/tests/src/com/android/launcher3/util/rule/FailureWatcher.java
@@ -84,7 +84,7 @@
@Override
protected void failed(Throwable e, Description description) {
- onError(mDevice, description, e);
+ onError(mLauncher, description, e);
}
static File diagFile(Description description, String prefix, String ext) {
@@ -93,7 +93,9 @@
+ description.getMethodName() + "." + ext);
}
- public static void onError(UiDevice device, Description description, Throwable e) {
+ public static void onError(LauncherInstrumentation launcher, Description description,
+ Throwable e) {
+ final UiDevice device = launcher.getDevice();
Log.d("b/196820244", "onError 1");
if (device == null) return;
Log.d("b/196820244", "onError 2");
@@ -128,6 +130,11 @@
}
dumpCommand("logcat -d -s TestRunner", diagFile(description, "FilteredLogcat", "txt"));
+
+ // Dump bugreport
+ if (launcher.getSystemAnomalyMessage(false, false) != null) {
+ dumpCommand("bugreportz -s", diagFile(description, "Bugreport", "zip"));
+ }
}
private static void dumpStringCommand(String cmd, OutputStream out) throws IOException {
diff --git a/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java b/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java
index 0582bc9..98eb32e 100644
--- a/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java
+++ b/tests/tapl/com/android/launcher3/tapl/AddToHomeScreenPrompt.java
@@ -22,8 +22,6 @@
import androidx.test.uiautomator.BySelector;
import androidx.test.uiautomator.UiObject2;
-import com.android.launcher3.testing.TestProtocol;
-
import java.util.regex.Pattern;
public class AddToHomeScreenPrompt {
@@ -44,19 +42,10 @@
public void addAutomatically() {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
- if (mLauncher.getNavigationModel()
- != LauncherInstrumentation.NavigationModel.THREE_BUTTON) {
- if (!mLauncher.isLauncher3()) {
- mLauncher.expectEvent(
- TestProtocol.SEQUENCE_TIS,
- LauncherInstrumentation.EVENT_TOUCH_DOWN_TIS);
- mLauncher.expectEvent(
- TestProtocol.SEQUENCE_TIS, LauncherInstrumentation.EVENT_TOUCH_UP_TIS);
- }
- }
- mLauncher.waitForObjectInContainer(
- mWidgetCell.getParent().getParent().getParent().getParent(),
- By.text(ADD_AUTOMATICALLY)).click();
+ mLauncher.clickObject(
+ mLauncher.waitForObjectInContainer(
+ mWidgetCell.getParent().getParent().getParent().getParent(),
+ By.text(ADD_AUTOMATICALLY)));
mLauncher.waitUntilLauncherObjectGone(getSelector());
}
}
diff --git a/tests/tapl/com/android/launcher3/tapl/AllApps.java b/tests/tapl/com/android/launcher3/tapl/AllApps.java
index 78301e4..c1b0220 100644
--- a/tests/tapl/com/android/launcher3/tapl/AllApps.java
+++ b/tests/tapl/com/android/launcher3/tapl/AllApps.java
@@ -22,6 +22,7 @@
import android.widget.TextView;
import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
import androidx.test.uiautomator.By;
import androidx.test.uiautomator.BySelector;
import androidx.test.uiautomator.Direction;
@@ -51,8 +52,8 @@
mLauncher.waitForObjectInContainer(appListRecycler, By.clazz(TextView.class));
verifyNotFrozen("All apps freeze flags upon opening all apps");
mIconHeight = mLauncher.getTestInfo(
- TestProtocol.REQUEST_ICON_HEIGHT).
- getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ TestProtocol.REQUEST_ICON_HEIGHT)
+ .getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
}
@Override
@@ -98,14 +99,14 @@
}
/**
- * Finds an icon. Fails if the icon doesn't exist. Scrolls the app list when needed to make
- * sure the icon is visible.
+ * Finds an icon. If the icon doesn't exist, return null.
+ * Scrolls the app list when needed to make sure the icon is visible.
*
* @param appName name of the app.
- * @return The app.
+ * @return The app if found, and null if not found.
*/
- @NonNull
- public AppIcon getAppIcon(String appName) {
+ @Nullable
+ public AppIcon tryGetAppIcon(String appName) {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"getting app icon " + appName + " on all apps")) {
@@ -150,19 +151,34 @@
}
verifyActiveContainer();
}
-
// Ignore bottom offset selection here as there might not be any scroll more scroll
// region available.
- mLauncher.assertTrue("Unable to scroll to a clickable icon: " + appName,
- hasClickableIcon(allAppsContainer, appListRecycler, appIconSelector,
- deviceHeight));
+ if (hasClickableIcon(
+ allAppsContainer, appListRecycler, appIconSelector, deviceHeight)) {
- final UiObject2 appIcon = mLauncher.waitForObjectInContainer(appListRecycler,
- appIconSelector);
- return new AppIcon(mLauncher, appIcon);
+ final UiObject2 appIcon = mLauncher.waitForObjectInContainer(appListRecycler,
+ appIconSelector);
+ return new AllAppsAppIcon(mLauncher, appIcon);
+ } else {
+ return null;
+ }
}
}
+ /**
+ * Finds an icon. Fails if the icon doesn't exist. Scrolls the app list when needed to make
+ * sure the icon is visible.
+ *
+ * @param appName name of the app.
+ * @return The app.
+ */
+ @NonNull
+ public AppIcon getAppIcon(String appName) {
+ AppIcon appIcon = tryGetAppIcon(appName);
+ mLauncher.assertNotNull("Unable to scroll to a clickable icon: " + appName, appIcon);
+ return appIcon;
+ }
+
private void scrollBackToBeginning() {
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to scroll back in all apps")) {
@@ -195,7 +211,7 @@
private int getAllAppsScroll() {
return mLauncher.getTestInfo(
- TestProtocol.REQUEST_APPS_LIST_SCROLL_Y)
+ TestProtocol.REQUEST_APPS_LIST_SCROLL_Y)
.getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
}
@@ -253,4 +269,4 @@
if (testInfo == null) return;
mLauncher.assertEquals(message, 0, testInfo.getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD));
}
-}
+}
\ No newline at end of file
diff --git a/tests/tapl/com/android/launcher3/tapl/AllAppsAppIcon.java b/tests/tapl/com/android/launcher3/tapl/AllAppsAppIcon.java
new file mode 100644
index 0000000..03d387e
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/AllAppsAppIcon.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2022 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.tapl;
+
+import androidx.test.uiautomator.UiObject2;
+
+import java.util.regex.Pattern;
+
+/**
+ * App icon in all apps.
+ */
+final class AllAppsAppIcon extends AppIcon {
+
+ private static final Pattern LONG_CLICK_EVENT = Pattern.compile("onAllAppsItemLongClick");
+
+ AllAppsAppIcon(LauncherInstrumentation launcher, UiObject2 icon) {
+ super(launcher, icon);
+ }
+
+ @Override
+ protected Pattern getLongClickEvent() {
+ return LONG_CLICK_EVENT;
+ }
+}
diff --git a/tests/tapl/com/android/launcher3/tapl/AppIcon.java b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
index 6da59da..8fa9e12 100644
--- a/tests/tapl/com/android/launcher3/tapl/AppIcon.java
+++ b/tests/tapl/com/android/launcher3/tapl/AppIcon.java
@@ -32,9 +32,7 @@
/**
* App icon, whether in all apps or in workspace/
*/
-public final class AppIcon extends Launchable implements FolderDragTarget {
-
- private static final Pattern LONG_CLICK_EVENT = Pattern.compile("onAllAppsItemLongClick");
+public abstract class AppIcon extends Launchable implements FolderDragTarget {
AppIcon(LauncherInstrumentation launcher, UiObject2 icon) {
super(launcher, icon);
@@ -44,13 +42,15 @@
return By.clazz(TextView.class).text(appName).pkg(launcher.getLauncherPackageName());
}
+ protected abstract Pattern getLongClickEvent();
+
/**
* Long-clicks the icon to open its menu.
*/
public AppIconMenu openMenu() {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
return new AppIconMenu(mLauncher, mLauncher.clickAndGet(
- mObject, "popup_container", LONG_CLICK_EVENT));
+ mObject, "popup_container", getLongClickEvent()));
}
}
@@ -60,7 +60,7 @@
public AppIconMenu openDeepShortcutMenu() {
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
return new AppIconMenu(mLauncher, mLauncher.clickAndGet(
- mObject, "deep_shortcuts_container", LONG_CLICK_EVENT));
+ mObject, "deep_shortcuts_container", getLongClickEvent()));
}
}
@@ -89,7 +89,7 @@
@Override
protected void addExpectedEventsForLongClick() {
- mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT);
+ mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, getLongClickEvent());
}
@Override
@@ -122,4 +122,20 @@
}
return null;
}
-}
+
+ /**
+ * Uninstall the appIcon by dragging it to the 'uninstall' drop point of the drop_target_bar.
+ *
+ * @return validated workspace after the existing appIcon being uninstalled.
+ */
+ public Workspace uninstall() {
+ try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck();
+ LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
+ "uninstalling app icon")) {
+ return Workspace.uninstallAppIcon(
+ mLauncher, this,
+ this::addExpectedEventsForLongClick
+ );
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/tapl/com/android/launcher3/tapl/Folder.java b/tests/tapl/com/android/launcher3/tapl/Folder.java
index dba308d..f046b6e 100644
--- a/tests/tapl/com/android/launcher3/tapl/Folder.java
+++ b/tests/tapl/com/android/launcher3/tapl/Folder.java
@@ -43,7 +43,7 @@
public AppIcon getAppIcon(String appName) {
try (LauncherInstrumentation.Closable ignored = mLauncher.addContextLayer(
"Want to get app icon in folder")) {
- return new AppIcon(mLauncher,
+ return new WorkspaceAppIcon(mLauncher,
mLauncher.waitForObjectInContainer(
mContainer,
AppIcon.getAppIconSelector(appName, mLauncher)));
diff --git a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
index 2564e97..bc2afaf 100644
--- a/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
+++ b/tests/tapl/com/android/launcher3/tapl/LauncherInstrumentation.java
@@ -426,7 +426,7 @@
}
}
- private String getSystemAnomalyMessage(
+ public String getSystemAnomalyMessage(
boolean ignoreNavmodeChangeStates, boolean ignoreOnlySystemUiViews) {
try {
{
@@ -1281,13 +1281,11 @@
}
int getRightGestureStartOnScreen() {
- return getRealDisplaySize().x - getWindowInsets().right;
+ return getRealDisplaySize().x - getWindowInsets().right - 1;
}
- void clickLauncherObject(UiObject2 object) {
- waitForObjectEnabled(object, "clickLauncherObject");
- expectEvent(TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_TOUCH_DOWN);
- expectEvent(TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_TOUCH_UP);
+ void clickObject(UiObject2 object) {
+ waitForObjectEnabled(object, "clickObject");
if (!isLauncher3() && getNavigationModel() != NavigationModel.THREE_BUTTON) {
expectEvent(TestProtocol.SEQUENCE_TIS, LauncherInstrumentation.EVENT_TOUCH_DOWN_TIS);
expectEvent(TestProtocol.SEQUENCE_TIS, LauncherInstrumentation.EVENT_TOUCH_UP_TIS);
@@ -1295,6 +1293,12 @@
object.click();
}
+ void clickLauncherObject(UiObject2 object) {
+ expectEvent(TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_TOUCH_DOWN);
+ expectEvent(TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_TOUCH_UP);
+ clickObject(object);
+ }
+
void scrollToLastVisibleRow(
UiObject2 container,
Collection<UiObject2> items,
diff --git a/tests/tapl/com/android/launcher3/tapl/Workspace.java b/tests/tapl/com/android/launcher3/tapl/Workspace.java
index 3f0d7fd..16987e9 100644
--- a/tests/tapl/com/android/launcher3/tapl/Workspace.java
+++ b/tests/tapl/com/android/launcher3/tapl/Workspace.java
@@ -16,10 +16,13 @@
package com.android.launcher3.tapl;
+import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_SCROLLED;
+
import static com.android.launcher3.testing.TestProtocol.ALL_APPS_STATE_ORDINAL;
import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL;
import static com.android.launcher3.testing.TestProtocol.SPRING_LOADED_STATE_ORDINAL;
+import static junit.framework.TestCase.assertNotNull;
import static junit.framework.TestCase.assertTrue;
import android.graphics.Point;
@@ -31,8 +34,11 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.test.uiautomator.By;
+import androidx.test.uiautomator.BySelector;
import androidx.test.uiautomator.Direction;
+import androidx.test.uiautomator.UiDevice;
import androidx.test.uiautomator.UiObject2;
+import androidx.test.uiautomator.Until;
import com.android.launcher3.testing.TestProtocol;
@@ -49,6 +55,7 @@
private static final int DEFAULT_DRAG_STEPS = 10;
private static final String DROP_BAR_RES_ID = "drop_target_bar";
private static final String DELETE_TARGET_TEXT_ID = "delete_target_text";
+ private static final String UNINSTALL_TARGET_TEXT_ID = "uninstall_target_text";
static final Pattern EVENT_CTRL_W_DOWN = Pattern.compile(
"Key event: KeyEvent.*?action=ACTION_DOWN.*?keyCode=KEYCODE_W"
@@ -56,7 +63,7 @@
static final Pattern EVENT_CTRL_W_UP = Pattern.compile(
"Key event: KeyEvent.*?action=ACTION_UP.*?keyCode=KEYCODE_W"
+ ".*?metaState=META_CTRL_ON");
- private static final Pattern LONG_CLICK_EVENT = Pattern.compile("onWorkspaceItemLongClick");
+ static final Pattern LONG_CLICK_EVENT = Pattern.compile("onWorkspaceItemLongClick");
private final UiObject2 mHotseat;
@@ -81,8 +88,8 @@
final int windowCornerRadius = (int) Math.ceil(mLauncher.getWindowCornerRadius());
final int startY = deviceHeight - Math.max(bottomGestureMargin, windowCornerRadius) - 1;
final int swipeHeight = mLauncher.getTestInfo(
- TestProtocol.REQUEST_HOME_TO_ALL_APPS_SWIPE_HEIGHT).
- getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
+ TestProtocol.REQUEST_HOME_TO_ALL_APPS_SWIPE_HEIGHT)
+ .getInt(TestProtocol.TEST_INFO_RESPONSE_FIELD);
LauncherInstrumentation.log(
"switchToAllApps: deviceHeight = " + deviceHeight + ", startY = " + startY
+ ", swipeHeight = " + swipeHeight + ", slop = "
@@ -116,7 +123,7 @@
final UiObject2 workspace = verifyActiveContainer();
final UiObject2 icon = workspace.findObject(
AppIcon.getAppIconSelector(appName, mLauncher));
- return icon != null ? new AppIcon(mLauncher, icon) : null;
+ return icon != null ? new WorkspaceAppIcon(mLauncher, icon) : null;
}
}
@@ -131,7 +138,7 @@
public AppIcon getWorkspaceAppIcon(String appName) {
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"want to get a workspace icon")) {
- return new AppIcon(mLauncher,
+ return new WorkspaceAppIcon(mLauncher,
mLauncher.waitForObjectInContainer(
verifyActiveContainer(),
AppIcon.getAppIconSelector(appName, mLauncher)));
@@ -165,15 +172,22 @@
}
/**
- * Drags an icon to the (currentPage + pageDelta) page if the page already exists.
- * If the target page doesn't exist, the icon will be put onto an existing page that is the
- * closest to the target page.
+ * Drags an icon to the (currentPage + pageDelta) page.
+ * If the target page doesn't exist yet, a new page will be created.
+ * In case the target page can't be created (e.g. existing pages are 0, 1, current: 0,
+ * pageDelta: 3, the latest page that can be created is 2) the icon will be dragged onto the
+ * page that can be created and is closest to the target page.
*
* @param appIcon - icon to drag.
* @param pageDelta - how many pages should the icon be dragged from the current page.
- * It can be a negative value.
+ * It can be a negative value. currentPage + pageDelta should be greater
+ * than or equal to 0.
*/
public void dragIcon(AppIcon appIcon, int pageDelta) {
+ if (mHotseat.getVisibleBounds().height() > mHotseat.getVisibleBounds().width()) {
+ throw new UnsupportedOperationException(
+ "dragIcon does NOT support dragging when the hotseat is on the side.");
+ }
try (LauncherInstrumentation.Closable e = mLauncher.eventsCheck()) {
final UiObject2 workspace = verifyActiveContainer();
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
@@ -205,7 +219,7 @@
@NonNull
public AppIcon getHotseatAppIcon(String appName) {
- return new AppIcon(mLauncher, mLauncher.waitForObjectInContainer(
+ return new WorkspaceAppIcon(mLauncher, mLauncher.waitForObjectInContainer(
mHotseat, AppIcon.getAppIconSelector(appName, mLauncher)));
}
@@ -215,12 +229,13 @@
}
/*
- * Get the center point of the delete icon in the drop target bar.
+ * Get the center point of the delete/uninstall icon in the drop target bar.
*/
- private Point getDeleteDropPoint() {
- return mLauncher.waitForObjectInContainer(
- mLauncher.waitForLauncherObject(DROP_BAR_RES_ID),
- DELETE_TARGET_TEXT_ID).getVisibleCenter();
+ private static Point getDropPointFromDropTargetBar(
+ LauncherInstrumentation launcher, String targetId) {
+ return launcher.waitForObjectInContainer(
+ launcher.waitForLauncherObject(DROP_BAR_RES_ID),
+ targetId).getVisibleCenter();
}
/**
@@ -235,7 +250,7 @@
"removing app icon from workspace")) {
dragIconToWorkspace(
mLauncher, appIcon,
- () -> getDeleteDropPoint(),
+ () -> getDropPointFromDropTargetBar(mLauncher, DELETE_TARGET_TEXT_ID),
true, /* decelerating */
appIcon.getLongPressIndicator(),
() -> mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, LONG_CLICK_EVENT),
@@ -249,6 +264,47 @@
}
/**
+ * Uninstall the appIcon by dragging it to the 'uninstall' drop point of the drop_target_bar.
+ *
+ * @param launcher the root TAPL instrumentation object of {@link LauncherInstrumentation} type.
+ * @param appIcon to be uninstalled.
+ * @param expectLongClickEvents the runnable to be executed to verify expected longclick event.
+ * @return validated workspace after the existing appIcon being uninstalled.
+ */
+ static Workspace uninstallAppIcon(LauncherInstrumentation launcher, AppIcon appIcon,
+ Runnable expectLongClickEvents) {
+ try (LauncherInstrumentation.Closable c = launcher.addContextLayer(
+ "uninstalling app icon")) {
+ dragIconToWorkspace(
+ launcher, appIcon,
+ () -> getDropPointFromDropTargetBar(launcher, UNINSTALL_TARGET_TEXT_ID),
+ true, /* decelerating */
+ appIcon.getLongPressIndicator(),
+ expectLongClickEvents,
+ null);
+
+ launcher.waitUntilLauncherObjectGone(DROP_BAR_RES_ID);
+
+ final BySelector installerAlert = By.text(Pattern.compile(
+ "Do you want to uninstall this app\\?",
+ Pattern.DOTALL | Pattern.MULTILINE));
+ final UiDevice device = launcher.getDevice();
+ assertTrue("uninstall alert is not shown", device.wait(
+ Until.hasObject(installerAlert), LauncherInstrumentation.WAIT_TIME_MS));
+ final UiObject2 ok = device.findObject(By.text("OK"));
+ assertNotNull("OK button is not shown", ok);
+ launcher.clickObject(ok);
+ assertTrue("Uninstall alert is not dismissed after clicking OK", device.wait(
+ Until.gone(installerAlert), LauncherInstrumentation.WAIT_TIME_MS));
+
+ try (LauncherInstrumentation.Closable c1 = launcher.addContextLayer(
+ "uninstalled app by dragging to the drop bar")) {
+ return new Workspace(launcher);
+ }
+ }
+ }
+
+ /**
* Finds folder icons in the current workspace.
*
* @return a list of folder icons.
@@ -338,9 +394,11 @@
while (targetDest.x > displayX || targetDest.x < 0) {
int edgeX = targetDest.x > 0 ? displayX : 0;
Point screenEdge = new Point(edgeX, targetDest.y);
- launcher.movePointer(dragStart, screenEdge, DEFAULT_DRAG_STEPS, isDecelerating,
- downTime, true, LauncherInstrumentation.GestureScope.INSIDE);
- launcher.waitForIdle(); // Wait for the page change to happen
+ Point finalDragStart = dragStart;
+ executeAndWaitForPageScroll(launcher,
+ () -> launcher.movePointer(finalDragStart, screenEdge, DEFAULT_DRAG_STEPS,
+ isDecelerating, downTime, true,
+ LauncherInstrumentation.GestureScope.INSIDE));
targetDest.x += displayX * (targetDest.x > 0 ? -1 : 1);
dragStart = screenEdge;
}
@@ -353,6 +411,13 @@
}
}
+ private static void executeAndWaitForPageScroll(LauncherInstrumentation launcher,
+ Runnable command) {
+ launcher.executeAndWaitForEvent(command,
+ event -> event.getEventType() == TYPE_VIEW_SCROLLED,
+ () -> "Page scroll didn't happen", "Scrolling page");
+ }
+
/**
* Flings to get to screens on the right. Waits for scrolling and a possible overscroll
* recoil to complete.
diff --git a/tests/tapl/com/android/launcher3/tapl/WorkspaceAppIcon.java b/tests/tapl/com/android/launcher3/tapl/WorkspaceAppIcon.java
new file mode 100644
index 0000000..5f4e469
--- /dev/null
+++ b/tests/tapl/com/android/launcher3/tapl/WorkspaceAppIcon.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2022 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.tapl;
+
+import androidx.test.uiautomator.UiObject2;
+
+import java.util.regex.Pattern;
+
+/**
+ * App icon in workspace.
+ */
+final class WorkspaceAppIcon extends AppIcon {
+
+ WorkspaceAppIcon(LauncherInstrumentation launcher, UiObject2 icon) {
+ super(launcher, icon);
+ }
+
+ @Override
+ protected Pattern getLongClickEvent() {
+ return Workspace.LONG_CLICK_EVENT;
+ }
+}