Merge "Merging from ub-launcher3-rvc-dev @ build 6396980" into rvc-dev

This commit is contained in:
Hyunyoung Song
2020-04-16 21:08:09 +00:00
committed by Android (Google) Code Review
226 changed files with 2160 additions and 1545 deletions
+8 -4
View File
@@ -63,14 +63,14 @@ message Target {
// Note: proto does not support duplicate enum values, even if they belong to different enum type.
// Hence "FROM" and "TO" prefix added.
enum FromFolderLabelState{
enum FromFolderLabelState {
FROM_FOLDER_LABEL_STATE_UNSPECIFIED = 0;
FROM_EMPTY = 1;
FROM_CUSTOM = 2;
FROM_SUGGESTED = 3;
}
enum ToFolderLabelState{
enum ToFolderLabelState {
TO_FOLDER_LABEL_STATE_UNSPECIFIED = 0;
TO_SUGGESTION0_WITH_VALID_PRIMARY = 1;
TO_SUGGESTION1_WITH_VALID_PRIMARY = 2;
@@ -79,10 +79,14 @@ message Target {
TO_SUGGESTION2_WITH_EMPTY_PRIMARY = 5;
TO_SUGGESTION3_WITH_VALID_PRIMARY = 6;
TO_SUGGESTION3_WITH_EMPTY_PRIMARY = 7;
TO_EMPTY_WITH_VALID_SUGGESTIONS = 8;
TO_EMPTY_WITH_VALID_SUGGESTIONS = 8 [deprecated = true];
TO_EMPTY_WITH_VALID_PRIMARY = 15;
TO_EMPTY_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY = 16;
TO_EMPTY_WITH_EMPTY_SUGGESTIONS = 9;
TO_EMPTY_WITH_SUGGESTIONS_DISABLED = 10;
TO_CUSTOM_WITH_VALID_SUGGESTIONS = 11;
TO_CUSTOM_WITH_VALID_SUGGESTIONS = 11 [deprecated = true];
TO_CUSTOM_WITH_VALID_PRIMARY = 17;
TO_CUSTOM_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY = 18;
TO_CUSTOM_WITH_EMPTY_SUGGESTIONS = 12;
TO_CUSTOM_WITH_SUGGESTIONS_DISABLED = 13;
UNCHANGED = 14;
@@ -18,10 +18,9 @@ package com.android.launcher3.appprediction;
import static com.android.quickstep.InstantAppResolverImpl.COMPONENT_CLASS_MARKER;
import com.android.launcher3.AppInfo;
import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.allapps.AllAppsStore;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.util.ComponentKey;
public class ComponentKeyMapper {
@@ -57,9 +56,8 @@ public class ComponentKeyMapper {
return item;
} else if (getComponentClass().equals(COMPONENT_CLASS_MARKER)) {
return mCache.getInstantApp(componentKey.componentName.getPackageName());
} else if (componentKey instanceof ShortcutKey) {
return mCache.getShortcutInfo((ShortcutKey) componentKey);
} else {
return mCache.getShortcutInfo(componentKey);
}
return null;
}
}
@@ -38,13 +38,14 @@ import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import androidx.annotation.WorkerThread;
import com.android.launcher3.AppInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.allapps.AllAppsStore;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.shortcuts.ShortcutRequest;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.InstantAppResolver;
import java.util.ArrayList;
@@ -76,7 +77,7 @@ public class DynamicItemCache {
private final Runnable mOnUpdateCallback;
private final IconCache mIconCache;
private final Map<ShortcutKey, WorkspaceItemInfo> mShortcuts;
private final Map<ComponentKey, WorkspaceItemInfo> mShortcuts;
private final Map<String, InstantAppItemInfo> mInstantApps;
public DynamicItemCache(Context context, Runnable onUpdateCallback) {
@@ -230,7 +231,7 @@ public class DynamicItemCache {
}
@MainThread
public WorkspaceItemInfo getShortcutInfo(ShortcutKey key) {
public WorkspaceItemInfo getShortcutInfo(ComponentKey key) {
return mShortcuts.get(key);
}
@@ -21,9 +21,9 @@ import static com.android.quickstep.InstantAppResolverImpl.COMPONENT_CLASS_MARKE
import android.content.ComponentName;
import android.content.Intent;
import com.android.launcher3.AppInfo;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
public class InstantAppItemInfo extends AppInfo {
@@ -44,7 +44,7 @@ public class InstantAppItemInfo extends AppInfo {
workspaceItemInfo.status = WorkspaceItemInfo.FLAG_AUTOINSTALL_ICON
| WorkspaceItemInfo.FLAG_RESTORE_STARTED
| WorkspaceItemInfo.FLAG_SUPPORTS_WEB_UI;
workspaceItemInfo.intent.setPackage(componentName.getPackageName());
workspaceItemInfo.getIntent().setPackage(componentName.getPackageName());
return workspaceItemInfo;
}
}
@@ -38,18 +38,14 @@ import android.widget.LinearLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.AppInfo;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.DeviceProfile.OnDeviceProfileChangeListener;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.allapps.AllAppsStore;
import com.android.launcher3.allapps.FloatingHeaderRow;
import com.android.launcher3.allapps.FloatingHeaderView;
@@ -60,6 +56,10 @@ import com.android.launcher3.keyboard.FocusIndicatorHelper;
import com.android.launcher3.keyboard.FocusIndicatorHelper.SimpleFocusIndicatorHelper;
import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.touch.ItemLongClickListener;
import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -28,8 +28,6 @@ import androidx.annotation.NonNull;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.InvariantDeviceProfile.OnIDPChangeListener;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
@@ -40,6 +38,8 @@ import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.allapps.AllAppsStore.OnUpdateListener;
import com.android.launcher3.hybridhotseat.HotseatPredictionController;
import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.ComponentKey;
@@ -27,16 +27,16 @@ import android.view.View;
import androidx.core.app.NotificationCompat;
import com.android.launcher3.CellLayout;
import com.android.launcher3.FolderInfo;
import com.android.launcher3.Hotseat;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.Workspace;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.util.ActivityTracker;
import com.android.launcher3.util.GridOccupancy;
@@ -200,6 +200,7 @@ public class HotseatEduController {
pageId = LauncherSettings.Settings.call(mLauncher.getContentResolver(),
LauncherSettings.Settings.METHOD_NEW_SCREEN_ID)
.getInt(LauncherSettings.Settings.EXTRA_VALUE);
mNewScreens = IntArray.wrap(pageId);
}
for (int i = 0; i < mLauncher.getDeviceProfile().inv.numHotseatIcons; i++) {
View child = mHotseat.getChildAt(i, 0);
@@ -16,8 +16,7 @@
package com.android.launcher3.hybridhotseat;
import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent;
import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType
.HYBRID_HOTSEAT_CANCELED;
import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.HYBRID_HOTSEAT_CANCELED;
import android.animation.PropertyValuesHolder;
import android.content.Context;
@@ -34,10 +33,11 @@ import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.Workspace;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.uioverrides.PredictedAppIcon;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.views.AbstractSlideInView;
@@ -153,6 +153,12 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
private void logUserAction(boolean migrated, int pageIndex) {
LauncherLogProto.Action action = new LauncherLogProto.Action();
LauncherLogProto.Target target = new LauncherLogProto.Target();
int hotseatItemsCount = mLauncher.getHotseat().getShortcutsAndWidgets().getChildCount();
// -1 to exclude smart space
int workspaceItemCount = mLauncher.getWorkspace().getScreenWithId(
Workspace.FIRST_SCREEN_ID).getShortcutsAndWidgets().getChildCount() - 1;
action.type = LauncherLogProto.Action.Type.TOUCH;
action.touch = LauncherLogProto.Action.Touch.TAP;
target.containerType = LauncherLogProto.ContainerType.TIP;
@@ -162,7 +168,7 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
target.rank = MIGRATION_EXPERIMENT_IDENTIFIER;
// encoding migration type on pageIndex
target.pageIndex = pageIndex;
target.cardinality = HotseatPredictionController.MAX_ITEMS_FOR_MIGRATION;
target.cardinality = (workspaceItemCount * 1000) + hotseatItemsCount;
LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target);
UserEventDispatcher.newInstance(getContext()).dispatchUserEvent(event, null);
}
@@ -31,6 +31,7 @@ import android.app.prediction.AppTargetEvent;
import android.app.prediction.AppTargetId;
import android.content.ComponentName;
import android.os.Bundle;
import android.os.Process;
import android.provider.DeviceConfig;
import android.util.Log;
import android.view.View;
@@ -39,22 +40,18 @@ import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.AppInfo;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DragSource;
import com.android.launcher3.DropTarget;
import com.android.launcher3.FolderInfo;
import com.android.launcher3.Hotseat;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.ItemInfoWithIcon;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.allapps.AllAppsStore;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.appprediction.ComponentKeyMapper;
@@ -64,6 +61,12 @@ import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.touch.ItemLongClickListener;
@@ -76,6 +79,7 @@ import com.android.launcher3.util.ComponentKey;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
import java.util.OptionalInt;
import java.util.stream.IntStream;
@@ -91,18 +95,19 @@ public class HotseatPredictionController implements DragController.DragListener,
private static final String TAG = "PredictiveHotseat";
private static final boolean DEBUG = false;
public static final int MAX_ITEMS_FOR_MIGRATION = DeviceConfig.getInt(
DeviceFlag.NAMESPACE_LAUNCHER, "max_homepage_items_for_migration", 5);
//TODO: replace this with AppTargetEvent.ACTION_UNPIN (b/144119543)
private static final int APPTARGET_ACTION_UNPIN = 4;
private static final String PREDICTED_ITEMS_CACHE_KEY = "predicted_item_keys";
private static final String APP_LOCATION_HOTSEAT = "hotseat";
private static final String APP_LOCATION_WORKSPACE = "workspace";
private static final String BUNDLE_KEY_HOTSEAT = "hotseat_apps";
private static final String BUNDLE_KEY_WORKSPACE = "workspace_apps";
private static final String BUNDLE_KEY_PIN_EVENTS = "pin_events";
private static final String PREDICTION_CLIENT = "hotseat";
private DropTarget.DragObject mDragObject;
private int mHotSeatItemsCount;
@@ -119,6 +124,7 @@ public class HotseatPredictionController implements DragController.DragListener,
private AllAppsStore mAllAppsStore;
private AnimatorSet mIconRemoveAnimators;
private boolean mUIUpdatePaused = false;
private boolean mRequiresCacheUpdate = false;
private HotseatEduController mHotseatEduController;
@@ -145,6 +151,7 @@ public class HotseatPredictionController implements DragController.DragListener,
if (mHotseat.isAttachedToWindow()) {
onViewAttachedToWindow(mHotseat);
}
showCachedItems();
}
/**
@@ -294,17 +301,57 @@ public class HotseatPredictionController implements DragController.DragListener,
mAppPredictor.requestPredictionUpdate();
}
private void showCachedItems() {
ArrayList<ComponentKey> componentKeys = getCachedComponentKeys();
mComponentKeyMappers.clear();
for (ComponentKey key : componentKeys) {
mComponentKeyMappers.add(new ComponentKeyMapper(key, mDynamicItemCache));
}
updateDependencies();
fillGapsWithPrediction();
}
private Bundle getAppPredictionContextExtra() {
Bundle bundle = new Bundle();
//TODO: remove this way of reporting items
bundle.putParcelableArrayList(BUNDLE_KEY_HOTSEAT,
getPinnedAppTargetsInViewGroup((mHotseat.getShortcutsAndWidgets())));
bundle.putParcelableArrayList(BUNDLE_KEY_WORKSPACE, getPinnedAppTargetsInViewGroup(
mLauncher.getWorkspace().getScreenWithId(
Workspace.FIRST_SCREEN_ID).getShortcutsAndWidgets()));
ArrayList<AppTargetEvent> pinEvents = new ArrayList<>();
getPinEventsForViewGroup(pinEvents, mHotseat.getShortcutsAndWidgets(),
APP_LOCATION_HOTSEAT);
getPinEventsForViewGroup(pinEvents, mLauncher.getWorkspace().getScreenWithId(
Workspace.FIRST_SCREEN_ID).getShortcutsAndWidgets(), APP_LOCATION_WORKSPACE);
bundle.putParcelableArrayList(BUNDLE_KEY_PIN_EVENTS, pinEvents);
return bundle;
}
private ArrayList<AppTargetEvent> getPinEventsForViewGroup(ArrayList<AppTargetEvent> pinEvents,
ViewGroup views, String root) {
for (int i = 0; i < views.getChildCount(); i++) {
View child = views.getChildAt(i);
final AppTargetEvent event;
if (child.getTag() instanceof ItemInfo && getAppTargetFromInfo(
(ItemInfo) child.getTag()) != null) {
ItemInfo info = (ItemInfo) child.getTag();
event = wrapAppTargetWithLocation(getAppTargetFromInfo(info),
AppTargetEvent.ACTION_PIN, info);
} else {
CellLayout.LayoutParams params = (CellLayout.LayoutParams) views.getLayoutParams();
event = wrapAppTargetWithLocation(getBlockAppTarget(), AppTargetEvent.ACTION_PIN,
root, 0, params.cellX, params.cellY, params.cellHSpan, params.cellVSpan);
}
pinEvents.add(event);
}
return pinEvents;
}
private ArrayList<AppTarget> getPinnedAppTargetsInViewGroup(ViewGroup viewGroup) {
ArrayList<AppTarget> pinnedApps = new ArrayList<>();
for (int i = 0; i < viewGroup.getChildCount(); i++) {
@@ -320,6 +367,7 @@ public class HotseatPredictionController implements DragController.DragListener,
private void setPredictedApps(List<AppTarget> appTargets) {
mComponentKeyMappers.clear();
StringBuilder predictionLog = new StringBuilder("predictedApps: [\n");
ArrayList<ComponentKey> componentKeys = new ArrayList<>();
for (AppTarget appTarget : appTargets) {
ComponentKey key;
if (appTarget.getShortcutInfo() != null) {
@@ -328,6 +376,7 @@ public class HotseatPredictionController implements DragController.DragListener,
key = new ComponentKey(new ComponentName(appTarget.getPackageName(),
appTarget.getClassName()), appTarget.getUser());
}
componentKeys.add(key);
predictionLog.append(key.toString());
predictionLog.append(",rank:");
predictionLog.append(appTarget.getRank());
@@ -342,6 +391,35 @@ public class HotseatPredictionController implements DragController.DragListener,
} else if (mHotseatEduController != null) {
mHotseatEduController.setPredictedApps(mapToWorkspaceItemInfo(mComponentKeyMappers));
}
// should invalidate cache if AiAi sends empty list of AppTargets
if (appTargets.isEmpty()) {
mRequiresCacheUpdate = true;
}
cachePredictionComponentKeys(componentKeys);
}
private void cachePredictionComponentKeys(ArrayList<ComponentKey> componentKeys) {
if (!mRequiresCacheUpdate) return;
StringBuilder builder = new StringBuilder();
for (ComponentKey componentKey : componentKeys) {
builder.append(componentKey);
builder.append("\n");
}
mLauncher.getDevicePrefs().edit().putString(PREDICTED_ITEMS_CACHE_KEY,
builder.toString()).apply();
mRequiresCacheUpdate = false;
}
private ArrayList<ComponentKey> getCachedComponentKeys() {
String cachedBlob = mLauncher.getDevicePrefs().getString(PREDICTED_ITEMS_CACHE_KEY, "");
ArrayList<ComponentKey> results = new ArrayList<>();
for (String line : cachedBlob.split("\n")) {
ComponentKey key = ComponentKey.fromString(line);
if (key != null) {
results.add(key);
}
}
return results;
}
private void updateDependencies() {
@@ -367,6 +445,7 @@ public class HotseatPredictionController implements DragController.DragListener,
icon.pin(workspaceItemInfo);
AppTarget appTarget = getAppTargetFromItemInfo(workspaceItemInfo);
notifyItemAction(appTarget, APP_LOCATION_HOTSEAT, AppTargetEvent.ACTION_PIN);
mRequiresCacheUpdate = true;
}
private List<WorkspaceItemInfo> mapToWorkspaceItemInfo(
@@ -533,6 +612,7 @@ public class HotseatPredictionController implements DragController.DragListener,
}
mDragObject = null;
fillGapsWithPrediction(true, this::removeOutlineDrawings);
mRequiresCacheUpdate = true;
}
@Nullable
@@ -606,6 +686,9 @@ public class HotseatPredictionController implements DragController.DragListener,
if (isReady()) return;
int hotseatItemsCount = mHotseat.getShortcutsAndWidgets().getChildCount();
int maxItems = DeviceConfig.getInt(
DeviceFlag.NAMESPACE_LAUNCHER, "max_homepage_items_for_migration", 5);
// -1 to exclude smart space
int workspaceItemCount = mLauncher.getWorkspace().getScreenWithId(
Workspace.FIRST_SCREEN_ID).getShortcutsAndWidgets().getChildCount() - 1;
@@ -614,7 +697,7 @@ public class HotseatPredictionController implements DragController.DragListener,
// open spots in their hotseat and have more than maxItems in their hotseat + workspace
if (hotseatItemsCount == mHotSeatItemsCount && workspaceItemCount + hotseatItemsCount
> MAX_ITEMS_FOR_MIGRATION) {
> maxItems) {
mLauncher.getSharedPrefs().edit().putBoolean(HotseatEduController.KEY_HOTSEAT_EDU_SEEN,
true).apply();
@@ -626,8 +709,8 @@ public class HotseatPredictionController implements DragController.DragListener,
// temporarily encode details in log target (go/hotseat_migration)
target.rank = 2;
target.cardinality = MAX_ITEMS_FOR_MIGRATION;
target.pageIndex = (workspaceItemCount * 1000) + hotseatItemsCount;
target.cardinality = (workspaceItemCount * 1000) + hotseatItemsCount;
target.pageIndex = maxItems;
LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target);
UserEventDispatcher.newInstance(mLauncher).dispatchUserEvent(event, null);
@@ -689,4 +772,52 @@ public class HotseatPredictionController implements DragController.DragListener,
return new AppTarget.Builder(new AppTargetId("app:" + cn.getPackageName()),
cn.getPackageName(), info.user).setClassName(cn.getClassName()).build();
}
private AppTarget getAppTargetFromInfo(ItemInfo info) {
if (info == null) return null;
if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
&& info instanceof LauncherAppWidgetInfo
&& ((LauncherAppWidgetInfo) info).providerName != null) {
ComponentName cn = ((LauncherAppWidgetInfo) info).providerName;
return new AppTarget.Builder(new AppTargetId("widget:" + cn.getPackageName()),
cn.getPackageName(), info.user).setClassName(cn.getClassName()).build();
} else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
&& info.getTargetComponent() != null) {
ComponentName cn = info.getTargetComponent();
return new AppTarget.Builder(new AppTargetId("app:" + cn.getPackageName()),
cn.getPackageName(), info.user).setClassName(cn.getClassName()).build();
} else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
&& info instanceof WorkspaceItemInfo) {
ShortcutKey shortcutKey = ShortcutKey.fromItemInfo(info);
//TODO: switch to using full shortcut info
return new AppTarget.Builder(new AppTargetId("shortcut:" + shortcutKey.getId()),
shortcutKey.componentName.getPackageName(), shortcutKey.user).build();
} else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
return new AppTarget.Builder(new AppTargetId("folder:" + info.id),
mLauncher.getPackageName(), info.user).build();
}
return null;
}
private AppTargetEvent wrapAppTargetWithLocation(AppTarget target, int action, ItemInfo info) {
return wrapAppTargetWithLocation(target, action,
info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT
? APP_LOCATION_HOTSEAT : APP_LOCATION_WORKSPACE, info.screenId, info.cellX,
info.cellY, info.spanX, info.spanY);
}
private AppTargetEvent wrapAppTargetWithLocation(AppTarget target, int action, String root,
int screenId, int x, int y, int spanX, int spanY) {
return new AppTargetEvent.Builder(target, action).setLaunchLocation(
String.format(Locale.ENGLISH, "%s/%d/[%d,%d]/[%d,%d]", root, screenId, x, y, spanX,
spanY)).build();
}
/**
* A helper method to generate an AppTarget that's used to communicate workspace layout
*/
private AppTarget getBlockAppTarget() {
return new AppTarget.Builder(new AppTargetId("block"),
mLauncher.getPackageName(), Process.myUserHandle()).build();
}
}
@@ -33,14 +33,14 @@ import androidx.core.graphics.ColorUtils;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.graphics.IconPalette;
import com.android.launcher3.hybridhotseat.HotseatPredictionController;
import com.android.launcher3.icons.IconNormalizer;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.touch.ItemLongClickListener;
import com.android.launcher3.views.DoubleShadowBubbleTextView;
@@ -136,9 +136,11 @@ public class PredictedAppIcon extends DoubleShadowBubbleTextView implements
@Override
public void addSupportedAccessibilityActions(AccessibilityNodeInfo accessibilityNodeInfo) {
accessibilityNodeInfo.addAction(
new AccessibilityNodeInfo.AccessibilityAction(PIN_PREDICTION,
getContext().getText(R.string.pin_prediction)));
if (!mIsPinned) {
accessibilityNodeInfo.addAction(
new AccessibilityNodeInfo.AccessibilityAction(PIN_PREDICTION,
getContext().getText(R.string.pin_prediction)));
}
}
@Override
@@ -28,14 +28,14 @@ import androidx.annotation.Nullable;
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.hybridhotseat.HotseatPredictionController;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.uioverrides.touchcontrollers.FlingAndHoldTouchController;
import com.android.launcher3.uioverrides.touchcontrollers.LandscapeEdgeSwipeController;
@@ -46,11 +46,8 @@ import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.model.PagedViewOrientedState;
import com.android.launcher3.states.RotationHelper;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.touch.PortraitPagedViewHandler;
import com.android.launcher3.util.VibratorWrapper;
import com.android.launcher3.views.FloatingIconView;
import com.android.quickstep.BaseActivityInterface.HomeAnimationFactory;
@@ -59,6 +56,7 @@ import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.AppWindowAnimationHelper;
import com.android.quickstep.util.AppWindowAnimationHelper.TransformParams;
import com.android.quickstep.util.RecentsOrientedState;
import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
@@ -127,7 +125,7 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
protected boolean mCanceled;
protected int mFinishingRecentsAnimationForNewTaskId = -1;
private PagedViewOrientedState mOrientedState;
private RecentsOrientedState mOrientedState;
protected BaseSwipeUpHandler(Context context, RecentsAnimationDeviceState deviceState,
GestureState gestureState, InputConsumerController inputConsumer) {
@@ -199,18 +197,12 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
}
protected void startNewTask(int successStateFlag, Consumer<Boolean> resultCallback) {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NO_START_FROM_RECENTS, "startNewTask1");
}
// Launch the task user scrolled to (mRecentsView.getNextPage()).
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
// We finish recents animation inside launchTask() when live tile is enabled.
mRecentsView.getNextPageTaskView().launchTask(false /* animate */,
true /* freezeTaskList */);
} else {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NO_START_FROM_RECENTS, "startNewTask2");
}
int taskId = mRecentsView.getNextPageTaskView().getTask().key.id;
if (!mCanceled) {
TaskView nextTask = mRecentsView.getTaskView(taskId);
@@ -337,9 +329,11 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
// TODO(b/150300347): The first recents animation after launcher is started with the
// foreground app not in landscape will look funky until that bug is fixed
displayRotation = mOrientedState.getDisplayRotation();
RectF tempRectF = new RectF(TEMP_RECT);
mOrientedState.mapRectFromNormalOrientation(tempRectF, dp.widthPx, dp.heightPx);
tempRectF.roundOut(TEMP_RECT);
}
RotationHelper.getTargetRectForRotation(TEMP_RECT, dp.widthPx, dp.heightPx,
displayRotation);
mAppWindowAnimationHelper.updateTargetRect(TEMP_RECT);
if (mDeviceState.isFullyGesturalNavMode()) {
// We can drag all the way to the top of the screen.
@@ -434,7 +428,7 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
protected PagedOrientationHandler getOrientationHandler() {
if (mOrientedState == null) {
return new PortraitPagedViewHandler();
return PagedOrientationHandler.PORTRAIT;
}
return mOrientedState.getOrientationHandler();
}
@@ -455,8 +449,8 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
.setTargetSet(mRecentsAnimationTargets)
.setLauncherOnTop(false)));
if (isFloatingIconView) {
RotationHelper.mapInverseRectFromNormalOrientation(startRect,
mDp.widthPx, mDp.heightPx, mOrientedState.getDisplayRotation());
mOrientedState.mapInverseRectFromNormalOrientation(
startRect, mDp.widthPx, mDp.heightPx);
}
RectFSpringAnim anim = new RectFSpringAnim(startRect, targetRect, mContext);
if (isFloatingIconView) {
@@ -487,8 +481,8 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
rotatedRect.set(currentRect);
if (isFloatingIconView) {
RotationHelper.mapRectFromNormalOrientation(rotatedRect,
mDp.widthPx, mDp.heightPx, mOrientedState.getDisplayRotation());
mOrientedState.mapRectFromNormalOrientation(
rotatedRect, mDp.widthPx, mDp.heightPx);
mTransformParams.setCornerRadius(endRadius * progress + startRadius
* (1f - progress));
}
@@ -33,8 +33,6 @@ import androidx.annotation.Nullable;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.touch.PortraitPagedViewHandler;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.quickstep.fallback.FallbackRecentsView;
import com.android.quickstep.util.ActivityInitListener;
@@ -40,9 +40,7 @@ import android.view.MotionEvent;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.touch.LandscapePagedViewHandler;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.touch.PortraitPagedViewHandler;
import com.android.launcher3.util.ObjectWrapper;
import com.android.quickstep.BaseActivityInterface.HomeAnimationFactory;
import com.android.quickstep.GestureState.GestureEndTarget;
@@ -503,8 +501,8 @@ public class FallbackSwipeHandler extends BaseSwipeUpHandler<RecentsActivity, Fa
PagedOrientationHandler orientationHandler = mRecentsView != null
? mRecentsView.getPagedOrientationHandler()
: (mDp.isLandscape
? new LandscapePagedViewHandler()
: new PortraitPagedViewHandler());
? PagedOrientationHandler.LANDSCAPE
: PagedOrientationHandler.PORTRAIT);
return HomeAnimationFactory
.getDefaultWindowTargetRect(orientationHandler, mDp);
}
@@ -984,9 +984,6 @@ public class LauncherSwipeHandler<T extends BaseDraggingActivity>
windowAnim.addListener(new AnimationSuccessListener() {
@Override
public void onAnimationSuccess(Animator animator) {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NO_START_FROM_RECENTS, "onAnimationSuccess");
}
if (mRecentsAnimationController == null) {
// If the recents animation is interrupted, we still end the running
// animation (not canceled) so this is still called. In that case, we can
@@ -1210,9 +1207,6 @@ public class LauncherSwipeHandler<T extends BaseDraggingActivity>
}
private void switchToScreenshot() {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NO_START_FROM_RECENTS, "switchToScreenshot");
}
final int runningTaskId = mGestureState.getRunningTaskId();
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
if (mRecentsAnimationController != null) {
@@ -35,8 +35,8 @@ import android.view.View;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.model.WellbeingModel;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.popup.SystemShortcut.AppInfo;
import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -71,7 +71,7 @@ public interface TaskShortcutFactory {
WorkspaceItemInfo dummyInfo = new WorkspaceItemInfo();
dummyInfo.intent = new Intent();
ComponentName component = task.getTopComponent();
dummyInfo.intent.setComponent(component);
dummyInfo.getIntent().setComponent(component);
dummyInfo.user = UserHandle.of(task.key.userId);
dummyInfo.title = TaskUtils.getTitle(view.getContext(), task);
return dummyInfo;
@@ -33,8 +33,8 @@ import android.view.View;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Utilities;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.statehandlers.DepthController;
import com.android.quickstep.util.AppWindowAnimationHelper;
import com.android.quickstep.util.MultiValueUpdateListener;
@@ -26,11 +26,12 @@ import static com.android.quickstep.GestureState.DEFAULT_STATE;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_INPUT_MONITOR;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_ASSISTANT;
import android.annotation.TargetApi;
import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.PendingIntent;
import android.app.RemoteAction;
import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
@@ -38,6 +39,7 @@ import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.graphics.Region;
import android.graphics.drawable.Icon;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
@@ -46,6 +48,7 @@ import android.util.Log;
import android.view.Choreographer;
import android.view.InputEvent;
import android.view.MotionEvent;
import android.view.accessibility.AccessibilityManager;
import androidx.annotation.BinderThread;
import androidx.annotation.Nullable;
@@ -53,8 +56,8 @@ import androidx.annotation.UiThread;
import androidx.annotation.WorkerThread;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.DiscoveryBounce;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.model.AppLaunchTracker;
@@ -64,6 +67,7 @@ import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.tracing.nano.LauncherTraceProto;
import com.android.launcher3.tracing.nano.TouchInteractionServiceProto;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.util.OnboardingPrefs;
import com.android.launcher3.util.TraceHelper;
import com.android.quickstep.inputconsumers.AccessibilityInputConsumer;
import com.android.quickstep.inputconsumers.AssistantInputConsumer;
@@ -126,6 +130,12 @@ public class TouchInteractionService extends Service implements PluginListener<O
private static final String HAS_ENABLED_QUICKSTEP_ONCE = "launcher.has_enabled_quickstep_once";
private static final int MAX_BACK_NOTIFICATION_COUNT = 3;
/**
* System Action ID to show all apps. This ID should follow the ones in
* com.android.systemui.accessibility.SystemActions.
*/
private static final int SYSTEM_ACTION_ID_ALL_APPS = 100;
private int mBackGestureNotificationCounter = -1;
@Nullable
private OverscrollPlugin mOverscrollPlugin;
@@ -351,6 +361,9 @@ public class TouchInteractionService extends Service implements PluginListener<O
PluginManagerWrapper.INSTANCE.get(getBaseContext()).addPluginListener(this,
OverscrollPlugin.class, false /* allowMultiple */);
mOverviewComponentObserver.setOverviewChangeListener(this::onOverviewTargetChange);
onOverviewTargetChange(mOverviewComponentObserver.isHomeAndOverviewSame());
}
private void resetHomeBounceSeenOnQuickstepEnabledFirstTime() {
@@ -365,11 +378,29 @@ public class TouchInteractionService extends Service implements PluginListener<O
if (!sharedPrefs.getBoolean(HAS_ENABLED_QUICKSTEP_ONCE, true)) {
sharedPrefs.edit()
.putBoolean(HAS_ENABLED_QUICKSTEP_ONCE, true)
.putBoolean(DiscoveryBounce.HOME_BOUNCE_SEEN, false)
.putBoolean(OnboardingPrefs.HOME_BOUNCE_SEEN, false)
.apply();
}
}
private void onOverviewTargetChange(boolean isHomeAndOverviewSame) {
AccessibilityManager am = getSystemService(AccessibilityManager.class);
if (isHomeAndOverviewSame) {
Intent intent = new Intent(mOverviewComponentObserver.getHomeIntent())
.setAction(Intent.ACTION_ALL_APPS);
RemoteAction allAppsAction = new RemoteAction(
Icon.createWithResource(this, R.drawable.ic_apps),
getString(R.string.all_apps_label),
getString(R.string.all_apps_label),
PendingIntent.getActivity(this, SYSTEM_ACTION_ID_ALL_APPS, intent,
PendingIntent.FLAG_UPDATE_CURRENT));
am.registerSystemAction(allAppsAction, SYSTEM_ACTION_ID_ALL_APPS);
} else {
am.unregisterSystemAction(SYSTEM_ACTION_ID_ALL_APPS);
}
}
@UiThread
private void onSystemUiFlagsChanged() {
if (mDeviceState.isUserUnlocked()) {
@@ -408,6 +439,9 @@ public class TouchInteractionService extends Service implements PluginListener<O
ProtoTracer.INSTANCE.get(TouchInteractionService.this).stop();
ProtoTracer.INSTANCE.get(this).remove(this);
getSystemService(AccessibilityManager.class)
.unregisterSystemAction(SYSTEM_ACTION_ID_ALL_APPS);
sConnected = false;
super.onDestroy();
}
@@ -451,7 +485,7 @@ public class TouchInteractionService extends Service implements PluginListener<O
mConsumer.onConsumerAboutToBeSwitched();
mConsumer = newConsumer(prevGestureState, newGestureState, event);
ActiveGestureLog.INSTANCE.addLog("setInputConsumer", mConsumer.getType());
ActiveGestureLog.INSTANCE.addLog("setInputConsumer: " + mConsumer.getName());
mUncheckedConsumer = mConsumer;
} else if (mDeviceState.isUserUnlocked()
&& mDeviceState.isFullyGesturalNavMode()
@@ -481,14 +515,14 @@ public class TouchInteractionService extends Service implements PluginListener<O
}
ActiveGestureLog.INSTANCE.addLog("onMotionEvent", event.getActionMasked());
boolean cleanUpConsumer = (action == ACTION_UP || action == ACTION_CANCEL)
&& mConsumer != null
&& !mConsumer.getActiveConsumerInHierarchy().isConsumerDetachedFromGesture();
mUncheckedConsumer.onMotionEvent(event);
if (action == ACTION_UP || action == ACTION_CANCEL) {
if (mConsumer != null && !mConsumer.isConsumerDetachedFromGesture()) {
onConsumerInactive(mConsumer);
}
if (cleanUpConsumer) {
reset();
}
TraceHelper.INSTANCE.endFlagsOverride(traceToken);
}
@@ -496,7 +530,7 @@ public class TouchInteractionService extends Service implements PluginListener<O
GestureState gestureState = new GestureState(mOverviewComponentObserver,
ActiveGestureLog.INSTANCE.generateAndSetLogId());
gestureState.updateRunningTask(TraceHelper.whitelistIpcs("getRunningTask.0",
() -> mAM.getRunningTask(true /* filterOnlyVisibleRecents */)));
() -> mAM.getRunningTask(false /* filterOnlyVisibleRecents */)));
return gestureState;
}
@@ -505,6 +539,8 @@ public class TouchInteractionService extends Service implements PluginListener<O
boolean canStartSystemGesture = mDeviceState.canStartSystemGesture();
if (!mDeviceState.isUserUnlocked()) {
Log.d(TAG, "User locked. Can start system gesture? " + canStartSystemGesture
+ " sysUiFlags: " + mDeviceState.getSystemUiStateFlags());
if (canStartSystemGesture) {
// This handles apps launched in direct boot mode (e.g. dialer) as well as apps
// launched while device is locked even after exiting direct boot mode (e.g. camera).
@@ -596,10 +632,18 @@ public class TouchInteractionService extends Service implements PluginListener<O
return createDeviceLockedInputConsumer(gestureState);
}
RunningTaskInfo runningTask = gestureState.getRunningTask();
ComponentName homeComponent = mOverviewComponentObserver.getHomeIntent().getComponent();
boolean forceOverviewInputConsumer = runningTask != null
&& runningTask.baseIntent.getComponent().equals(homeComponent);
boolean forceOverviewInputConsumer = false;
if (AssistantUtilities.isExcludedAssistant(gestureState.getRunningTask())) {
// In the case where we are in the excluded assistant state, ignore it and treat the
// running activity as the task behind the assistant
gestureState.updateRunningTask(TraceHelper.whitelistIpcs("getRunningTask.assistant",
() -> mAM.getRunningTask(true /* filterOnlyVisibleRecents */)));
ComponentName homeComponent = mOverviewComponentObserver.getHomeIntent().getComponent();
ComponentName runningComponent =
gestureState.getRunningTask().baseIntent.getComponent();
forceOverviewInputConsumer =
runningComponent != null && runningComponent.equals(homeComponent);
}
if (previousGestureState.getFinishingRecentsAnimationTaskId() > 0) {
// If the finish animation was interrupted, then continue using the other activity input
@@ -678,15 +722,21 @@ public class TouchInteractionService extends Service implements PluginListener<O
}
/**
* To be called by the consumer when it's no longer active.
* To be called by the consumer when it's no longer active. This can be called by any consumer
* in the hierarchy at any point during the gesture (ie. if a delegate consumer starts
* intercepting touches, the base consumer can try to call this).
*/
private void onConsumerInactive(InputConsumer caller) {
if (mConsumer != null && mConsumer.isInConsumerHierarchy(caller)) {
mConsumer = mUncheckedConsumer = mResetGestureInputConsumer;
mGestureState = new GestureState();
if (mConsumer != null && mConsumer.getActiveConsumerInHierarchy() == caller) {
reset();
}
}
private void reset() {
mConsumer = mUncheckedConsumer = mResetGestureInputConsumer;
mGestureState = new GestureState();
}
private void preloadOverview(boolean fromInit) {
if (!mDeviceState.isUserUnlocked()) {
return;
@@ -25,13 +25,11 @@ public abstract class DelegateInputConsumer implements InputConsumer {
}
@Override
public boolean isConsumerDetachedFromGesture() {
return mDelegate.isConsumerDetachedFromGesture();
}
@Override
public boolean isInConsumerHierarchy(InputConsumer candidate) {
return this == candidate || mDelegate.isInConsumerHierarchy(candidate);
public InputConsumer getActiveConsumerInHierarchy() {
if (mState == STATE_ACTIVE) {
return this;
}
return mDelegate.getActiveConsumerInHierarchy();
}
@Override
@@ -19,8 +19,8 @@ import android.content.Context;
import androidx.annotation.NonNull;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.appprediction.PredictionUiStateManager;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import java.util.ArrayList;
@@ -15,6 +15,13 @@
*/
package com.android.quickstep.util;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.systemui.shared.system.QuickStepContract.getWindowCornerRadius;
import static com.android.systemui.shared.system.QuickStepContract.supportsRoundedCornersOnWindows;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Resources;
@@ -33,7 +40,6 @@ import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.model.PagedViewOrientedState;
import com.android.launcher3.views.BaseDragLayer;
import com.android.quickstep.RemoteAnimationTargets;
import com.android.quickstep.SystemUiProxy;
@@ -47,13 +53,6 @@ import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.
import com.android.systemui.shared.system.TransactionCompat;
import com.android.systemui.shared.system.WindowManagerWrapper;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.systemui.shared.system.QuickStepContract.getWindowCornerRadius;
import static com.android.systemui.shared.system.QuickStepContract.supportsRoundedCornersOnWindows;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
/**
* Utility class to handle window clip animation
*/
@@ -86,7 +85,7 @@ public class AppWindowAnimationHelper {
private final Rect mTmpRect = new Rect();
private final RectF mTmpRectF = new RectF();
private final RectF mCurrentRectWithInsets = new RectF();
private PagedViewOrientedState mOrientedState;
private RecentsOrientedState mOrientedState;
// Corner radius of windows, in pixels
private final float mWindowCornerRadius;
// Corner radius of windows when they're in overview mode.
@@ -105,7 +104,7 @@ public class AppWindowAnimationHelper {
private TargetAlphaProvider mTaskAlphaCallback = (t, a) -> a;
private TargetAlphaProvider mBaseAlphaCallback = (t, a) -> 1;
public AppWindowAnimationHelper(PagedViewOrientedState orientedState, Context context) {
public AppWindowAnimationHelper(RecentsOrientedState orientedState, Context context) {
Resources res = context.getResources();
mOrientedState = orientedState;
mWindowCornerRadius = getWindowCornerRadius(res);
@@ -78,21 +78,6 @@ public class ClearAllButton extends Button implements PageCallbacks {
}
}
public void onLayoutChanged() {
if (mParent == null) {
return;
}
setRotation(mParent.getPagedOrientationHandler().getDegreesRotated());
}
public void setRtl(boolean rtl) {
if (mIsRtl == rtl) {
return;
}
mIsRtl = rtl;
invalidate();
}
public void setVisibilityAlpha(float alpha) {
if (mVisibilityAlpha != alpha) {
mVisibilityAlpha = alpha;
@@ -100,8 +100,8 @@ import com.android.launcher3.anim.SpringProperty;
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.states.RotationHelper;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.touch.PagedOrientationHandler.CurveProperties;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
@@ -122,6 +122,7 @@ import com.android.quickstep.TaskUtils;
import com.android.quickstep.ViewUtils;
import com.android.quickstep.util.AppWindowAnimationHelper;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.util.RecentsOrientedState;
import com.android.systemui.plugins.ResourceProvider;
import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
import com.android.systemui.shared.recents.model.Task;
@@ -171,6 +172,8 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
}
};
protected final RecentsOrientedState mOrientationState = new RecentsOrientedState();
private OrientationEventListener mOrientationListener;
private int mPreviousRotation;
protected RecentsAnimationController mRecentsAnimationController;
@@ -383,7 +386,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
mOrientationListener = new OrientationEventListener(getContext()) {
@Override
public void onOrientationChanged(int i) {
int rotation = RotationHelper.getRotationFromDegrees(i);
int rotation = RecentsOrientedState.getRotationForUserDegreesRotated(i);
if (mPreviousRotation != rotation) {
animateRecentsRotationInPlace(rotation);
if (rotation == 0) {
@@ -505,6 +508,29 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
mHasVisibleTaskData.delete(taskView.getTask().key.id);
mTaskViewPool.recycle(taskView);
}
updateTaskStartIndex(child);
}
@Override
public void onViewAdded(View child) {
super.onViewAdded(child);
child.setAlpha(mContentAlpha);
// RecentsView is set to RTL in the constructor when system is using LTR. Here we set the
// child direction back to match system settings.
child.setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_LTR : View.LAYOUT_DIRECTION_RTL);
updateTaskStartIndex(child);
}
private void updateTaskStartIndex(View affectingView) {
if (!(affectingView instanceof TaskView) && !(affectingView instanceof ClearAllButton)) {
int childCount = getChildCount();
mTaskViewStartIndex = 0;
while (mTaskViewStartIndex < childCount
&& !(getChildAt(mTaskViewStartIndex) instanceof TaskView)) {
mTaskViewStartIndex++;
}
}
}
public boolean isTaskViewVisible(TaskView tv) {
@@ -661,7 +687,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
final int pageIndex = requiredTaskCount - i - 1 + mTaskViewStartIndex;
final Task task = tasks.get(i);
final TaskView taskView = (TaskView) getChildAt(pageIndex);
taskView.bind(task, mLayoutRotation);
taskView.bind(task, mOrientationState);
}
if (mNextPage == INVALID_PAGE) {
@@ -710,11 +736,15 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
int currentIndex = indexOfChild(taskView);
TaskView previousTask = getTaskViewAt(currentIndex - 1);
TaskView nextTask = getTaskViewAt(currentIndex + 1);
float alpha = isTaskOverlayModal ? 0.0f : 1.0f;
if (previousTask != null) {
previousTask.setVisibility(isTaskOverlayModal ? View.INVISIBLE : View.VISIBLE);
previousTask.animate().alpha(alpha)
.translationX(isTaskOverlayModal ? previousTask.getWidth() / 2 : 0);
}
if (nextTask != null) {
nextTask.setVisibility(isTaskOverlayModal ? View.INVISIBLE : View.VISIBLE);
nextTask.animate().alpha(alpha)
.translationX(isTaskOverlayModal ? -nextTask.getWidth() / 2 : 0);
}
}
@@ -819,7 +849,8 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
final int pageCount = getPageCount();
for (int i = 0; i < pageCount; i++) {
View page = getPageAt(i);
mScrollState.updateInterpolation(mOrientationHandler.getChildStart(page), mPageSpacing);
mScrollState.updateInterpolation(mOrientationHandler.getChildStartWithTranslation(page),
mPageSpacing);
((PageCallbacks) page).onPageScroll(mScrollState);
}
}
@@ -973,7 +1004,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
AnimatorSet pa = setRecentsChangedOrientation(true);
pa.addListener(AnimationSuccessListener.forRunnable(() -> {
updateLayoutRotation(newRotation);
setLayoutRotation(newRotation, mOrientationState.getDisplayRotation());
mActivity.getDragLayer().recreateControllers();
rotateAllChildTasks();
setRecentsChangedOrientation(false).start();
@@ -999,8 +1030,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
private void rotateAllChildTasks() {
for (int i = 0; i < getTaskViewCount(); i++) {
TaskView taskView = getTaskViewAt(i);
taskView.setOverviewRotation(mLayoutRotation);
getTaskViewAt(i).setOrientationState(mOrientationState);
}
}
@@ -1041,7 +1071,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
new ComponentName(getContext(), getClass()), 0, 0), null, null, "", "", 0, 0,
false, true, false, false, new ActivityManager.TaskDescription(), 0,
new ComponentName("", ""), false);
taskView.bind(mTmpRunningTask, mLayoutRotation);
taskView.bind(mTmpRunningTask, mOrientationState);
}
boolean runningTaskTileHidden = mRunningTaskTileHidden;
@@ -1207,7 +1237,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
/**
* Updates linearInterpolation for the provided child position
*/
public void updateInterpolation(int childStart, int pageSpacing) {
public void updateInterpolation(float childStart, int pageSpacing) {
float pageCenter = childStart + halfPageSize;
float distanceFromScreenCenter = screenCenter - pageCenter;
float distanceToReachEdge = halfScreenSize + halfPageSize + pageSpacing;
@@ -1530,22 +1560,28 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
}
}
@Override
public void setLayoutRotation(int touchRotation, int displayRotation) {
if (!FeatureFlags.ENABLE_FIXED_ROTATION_TRANSFORM.get()) {
return;
if (mOrientationState.update(touchRotation, displayRotation)) {
mOrientationHandler = mOrientationState.getOrientationHandler();
mIsRtl = mOrientationHandler.getRecentsRtlSetting(getResources());
setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
mClearAllButton.setRotation(mOrientationHandler.getDegreesRotated());
requestLayout();
}
super.setLayoutRotation(touchRotation, displayRotation);
mClearAllButton.onLayoutChanged();
mIsRtl = mOrientationHandler.getRecentsRtlSetting(getResources());
setLayoutDirection(mIsRtl ? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
}
@Override
public void onViewAdded(View child) {
super.onViewAdded(child);
child.setAlpha(mContentAlpha);
public void disableMultipleLayoutRotations(boolean disable) {
mOrientationState.disableMultipleOrientations(disable);
mOrientationHandler = mOrientationState.getOrientationHandler();
requestLayout();
}
public RecentsOrientedState getPagedViewOrientedState() {
return mOrientationState;
}
public PagedOrientationHandler getPagedOrientationHandler() {
return mOrientationHandler;
}
@Nullable
@@ -1986,8 +2022,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
public Consumer<MotionEvent> getEventDispatcher(float navbarRotation) {
float degreesRotated;
if (navbarRotation == 0) {
degreesRotated = mOrientationState.areMultipleLayoutOrientationsDisabled() ? 0 :
RotationHelper.getDegreesFromRotation(mLayoutRotation);
degreesRotated = mOrientationState.getTouchRotationDegrees();
} else {
degreesRotated = -navbarRotation;
}
@@ -2001,14 +2036,14 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
return e -> {
if (navbarRotation != 0
&& !mOrientationState.areMultipleLayoutOrientationsDisabled()) {
RotationHelper.transformEventForNavBar(e, true);
mOrientationState.flipVertical(e);
super.onTouchEvent(e);
RotationHelper.transformEventForNavBar(e, false);
mOrientationState.flipVertical(e);
return;
}
RotationHelper.transformEvent(-degreesRotated, e, true);
mOrientationState.transformEvent(-degreesRotated, e, true);
super.onTouchEvent(e);
RotationHelper.transformEvent(-degreesRotated, e, false);
mOrientationState.transformEvent(-degreesRotated, e, false);
};
}
@@ -2056,37 +2091,11 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
}
}
@Override
public void addView(View child, int index) {
// RecentsView is set to RTL in the constructor when system is using LTR. Here we set the
// child direction back to match system settings.
child.setLayoutDirection(
Utilities.isRtl(getResources())
? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
super.addView(child, index);
if (isExtraCardView(child, index)) {
mTaskViewStartIndex++;
}
}
@Override
public void removeView(View view) {
if (isExtraCardView(view, indexOfChild(view))) {
mTaskViewStartIndex--;
}
super.removeView(view);
}
@Nullable
protected DepthController getDepthController() {
return null;
}
private boolean isExtraCardView(View view, int index) {
return !(view instanceof TaskView) && !(view instanceof ClearAllButton)
&& index <= mTaskViewStartIndex;
}
/**
* Used to register callbacks for when our empty message state changes.
*
@@ -2131,13 +2140,15 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
&& SysUINavigationMode.removeShelfFromOverview(mActivity)) {
mActionsView = ((ViewGroup) getParent()).findViewById(R.id.overview_actions_view);
if (mActionsView != null) {
Rect rect = new Rect();
getTaskSize(rect);
InsettableFrameLayout.LayoutParams layoutParams =
new InsettableFrameLayout.LayoutParams(rect.width(),
new InsettableFrameLayout.LayoutParams(LayoutParams.MATCH_PARENT,
getResources().getDimensionPixelSize(
R.dimen.overview_actions_height));
layoutParams.gravity = Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL;
int margin = getResources().getDimensionPixelSize(
R.dimen.overview_actions_horizontal_margin);
layoutParams.setMarginStart(margin);
layoutParams.setMarginEnd(margin);
mActionsView.setLayoutParams(layoutParams);
showActionsView();
}
@@ -64,7 +64,6 @@ import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.states.RotationHelper;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.PagedOrientationHandler;
@@ -79,6 +78,7 @@ import com.android.quickstep.TaskOverlayFactory;
import com.android.quickstep.TaskThumbnailCache;
import com.android.quickstep.TaskUtils;
import com.android.quickstep.util.LayoutUtils;
import com.android.quickstep.util.RecentsOrientedState;
import com.android.quickstep.util.TaskCornerRadius;
import com.android.quickstep.views.RecentsView.PageCallbacks;
import com.android.quickstep.views.RecentsView.ScrollState;
@@ -119,19 +119,6 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
private static final List<Rect> SYSTEM_GESTURE_EXCLUSION_RECT =
Collections.singletonList(new Rect());
public static final FloatProperty<TaskView> FULLSCREEN_PROGRESS =
new FloatProperty<TaskView>("fullscreenProgress") {
@Override
public void setValue(TaskView taskView, float v) {
taskView.setFullscreenProgress(v);
}
@Override
public Float get(TaskView taskView) {
return taskView.mFullscreenProgress;
}
};
private static final FloatProperty<TaskView> FOCUS_TRANSITION =
new FloatProperty<TaskView>("focusTransition") {
@Override
@@ -180,6 +167,7 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
private float mStableAlpha = 1;
private boolean mShowScreenshot;
private boolean mRunningModalAnimation = false;
// The current background requests to load the task thumbnail and icon
private TaskThumbnailCache.ThumbnailLoadRequest mThumbnailLoadRequest;
@@ -262,17 +250,39 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
/** Updates UI based on whether the task is modal. */
public void updateUiForModalTask() {
boolean isOverlayModal = isTaskOverlayModal();
mRunningModalAnimation = true;
if (getRecentsView() != null) {
getRecentsView().updateUiForModalTask(this, isOverlayModal);
}
// Hide footers when overlay is modal.
// Hides footers and icon when overlay is modal.
if (isOverlayModal) {
for (FooterWrapper footer : mFooters) {
if (footer != null) {
footer.animateHide();
}
}
mIconView.animate().alpha(0.0f);
} else {
mIconView.animate().alpha(1.0f);
}
// Sets animations for modal UI. We will remove the margins to zoom in the snapshot.
float topMargin = getResources().getDimension(R.dimen.task_thumbnail_top_margin);
float bottomMargin =
getResources().getDimension(R.dimen.task_thumbnail_bottom_margin_with_actions);
float newHeight = mSnapshotView.getHeight() + topMargin + bottomMargin;
float scale = isOverlayModal ? newHeight / mSnapshotView.getHeight() : 1.0f;
float centerDifference = (bottomMargin - topMargin) / 2;
float translationY = isOverlayModal ? centerDifference : 0;
this.animate().scaleX(scale).scaleY(scale).translationY(translationY)
.withEndAction(new Runnable() {
@Override
public void run() {
setCurveScale(scale);
mRunningModalAnimation = false;
}
});
}
public TaskMenuView getMenuView() {
@@ -286,11 +296,11 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
/**
* Updates this task view to the given {@param task}.
*/
public void bind(Task task, int recentsRotation) {
public void bind(Task task, RecentsOrientedState orientedState) {
cancelPendingLoadTasks();
mTask = task;
mSnapshotView.bind(task);
setOverviewRotation(recentsRotation);
setOrientationState(orientedState);
}
public Task getTask() {
@@ -459,14 +469,15 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
}
}
void setOverviewRotation(int iconRotation) {
PagedOrientationHandler orientationHandler = getRecentsView().getPagedOrientationHandler();
public void setOrientationState(RecentsOrientedState orientationState) {
int iconRotation = orientationState.getTouchRotation();
PagedOrientationHandler orientationHandler = orientationState.getOrientationHandler();
boolean isRtl = orientationHandler.getRecentsRtlSetting(getResources());
LayoutParams snapshotParams = (LayoutParams) mSnapshotView.getLayoutParams();
snapshotParams.bottomMargin = LayoutUtils.thumbnailBottomMargin(getContext());
int thumbnailPadding = (int) getResources().getDimension(R.dimen.task_thumbnail_top_margin);
LayoutParams iconParams = (LayoutParams) mIconView.getLayoutParams();
int rotation = RotationHelper.getDegreesFromRotation(iconRotation);
int rotation = orientationState.getTouchRotationDegrees();
switch (iconRotation) {
case Surface.ROTATION_90:
iconParams.gravity = (isRtl ? END : START) | CENTER_VERTICAL;
@@ -579,11 +590,15 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
@Override
public void onPageScroll(ScrollState scrollState) {
// Don't do anything if it's modal.
if (mRunningModalAnimation || isTaskOverlayModal()) {
return;
}
float curveInterpolation =
CURVE_INTERPOLATOR.getInterpolation(scrollState.linearInterpolation);
float curveScaleForCurveInterpolation = getCurveScaleForCurveInterpolation(
curveInterpolation);
mSnapshotView.setDimAlpha(curveInterpolation * MAX_PAGE_SCRIM_ALPHA);
setCurveScale(curveScaleForCurveInterpolation);
+2
View File
@@ -17,6 +17,7 @@
<resources>
<dimen name="task_thumbnail_top_margin">24dp</dimen>
<dimen name="task_thumbnail_bottom_margin_with_actions">44dp</dimen>
<dimen name="task_thumbnail_half_top_margin">12dp</dimen>
<dimen name="task_thumbnail_icon_size">48dp</dimen>
<!-- For screens without rounded corners -->
@@ -24,6 +25,7 @@
<!-- Overrideable in overlay that provides the Overview Actions. -->
<dimen name="overview_actions_height">110dp</dimen>
<dimen name="overview_actions_horizontal_margin">16dp</dimen>
<dimen name="recents_page_spacing">10dp</dimen>
<dimen name="recents_clear_all_deadzone_vertical_margin">70dp</dimen>
@@ -17,14 +17,7 @@ package com.android.launcher3;
import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
import static com.android.launcher3.AbstractFloatingView.TYPE_HIDE_BACK_BUTTON;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.allapps.DiscoveryBounce.BOUNCE_MAX_COUNT;
import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_COUNT;
import static com.android.launcher3.allapps.DiscoveryBounce.HOME_BOUNCE_SEEN;
import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_COUNT;
import static com.android.launcher3.allapps.DiscoveryBounce.SHELF_BOUNCE_SEEN;
import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_HOME_KEY;
@@ -32,13 +25,13 @@ import android.animation.AnimatorSet;
import android.animation.ValueAnimator;
import android.content.Intent;
import android.content.IntentSender;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.view.View;
import com.android.launcher3.LauncherState.ScaleAndTranslation;
import com.android.launcher3.LauncherStateManager.StateHandler;
import com.android.launcher3.accessibility.SystemActions;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.WellbeingModel;
import com.android.launcher3.popup.SystemShortcut;
@@ -48,12 +41,14 @@ import com.android.launcher3.statehandlers.BackButtonAlphaHandler;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.uioverrides.RecentsViewStateController;
import com.android.launcher3.util.OnboardingPrefs;
import com.android.launcher3.util.UiThreadHelper;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.SysUINavigationMode.Mode;
import com.android.quickstep.SysUINavigationMode.NavigationModeChangeListener;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.QuickstepOnboardingPrefs;
import com.android.quickstep.util.RemoteAnimationProvider;
import com.android.quickstep.util.RemoteFadeOutAnimationListener;
import com.android.quickstep.util.ShelfPeekAnim;
@@ -70,7 +65,6 @@ public abstract class BaseQuickstepLauncher extends Launcher
implements NavigationModeChangeListener {
private DepthController mDepthController = new DepthController(this);
protected SystemActions mSystemActions;
/**
* Reusable command for applying the back button alpha on the background thread.
@@ -86,48 +80,8 @@ public abstract class BaseQuickstepLauncher extends Launcher
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mSystemActions = new SystemActions(this);
SysUINavigationMode.INSTANCE.get(this).addModeChangeListener(this);
if (!getSharedPrefs().getBoolean(HOME_BOUNCE_SEEN, false)) {
getStateManager().addStateListener(new LauncherStateManager.StateListener() {
@Override
public void onStateTransitionStart(LauncherState toState) { }
@Override
public void onStateTransitionComplete(LauncherState finalState) {
boolean swipeUpEnabled = SysUINavigationMode.INSTANCE
.get(BaseQuickstepLauncher.this).getMode().hasGestures;
LauncherState prevState = getStateManager().getLastState();
if (((swipeUpEnabled && finalState == OVERVIEW) || (!swipeUpEnabled
&& finalState == ALL_APPS && prevState == NORMAL) || BOUNCE_MAX_COUNT
<= getSharedPrefs().getInt(HOME_BOUNCE_COUNT, 0))) {
getSharedPrefs().edit().putBoolean(HOME_BOUNCE_SEEN, true).apply();
getStateManager().removeStateListener(this);
}
}
});
}
if (!getSharedPrefs().getBoolean(SHELF_BOUNCE_SEEN, false)) {
getStateManager().addStateListener(new LauncherStateManager.StateListener() {
@Override
public void onStateTransitionStart(LauncherState toState) { }
@Override
public void onStateTransitionComplete(LauncherState finalState) {
LauncherState prevState = getStateManager().getLastState();
if ((finalState == ALL_APPS && prevState == OVERVIEW) || BOUNCE_MAX_COUNT
<= getSharedPrefs().getInt(SHELF_BOUNCE_COUNT, 0)) {
getSharedPrefs().edit().putBoolean(SHELF_BOUNCE_SEEN, true).apply();
getStateManager().removeStateListener(this);
}
}
});
}
}
@Override
@@ -141,12 +95,6 @@ public abstract class BaseQuickstepLauncher extends Launcher
getDragLayer().recreateControllers();
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
mSystemActions.onActivityResult(requestCode);
}
@Override
public void onEnterAnimationComplete() {
super.onEnterAnimationComplete();
@@ -210,15 +158,6 @@ public abstract class BaseQuickstepLauncher extends Launcher
// removes the task itself.
startActivity(ProxyActivityStarter.getLaunchIntent(this, null));
}
// Register all system actions once they are available
mSystemActions.register();
}
@Override
protected void onPause() {
super.onPause();
mSystemActions.unregister();
}
@Override
@@ -261,6 +200,12 @@ public abstract class BaseQuickstepLauncher extends Launcher
return mDepthController;
}
@Override
protected OnboardingPrefs createOnboardingPrefs(SharedPreferences sharedPrefs,
LauncherStateManager stateManager) {
return new QuickstepOnboardingPrefs(this, sharedPrefs, stateManager);
}
@Override
protected ScaleAndTranslation getOverviewScaleAndTranslationForNormalState() {
if (SysUINavigationMode.getMode(this) == Mode.NO_BUTTON) {
@@ -1,90 +0,0 @@
/*
* Copyright (C) 2020 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.accessibility;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
import android.app.PendingIntent;
import android.app.RemoteAction;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.drawable.Icon;
import android.view.accessibility.AccessibilityManager;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.R;
/**
* Manages the launcher system actions presented to accessibility services.
*/
public class SystemActions {
/**
* System Action ID to show all apps. This ID should follow the ones in
* com.android.systemui.accessibility.SystemActions.
*/
private static final int SYSTEM_ACTION_ID_ALL_APPS = 100;
private Launcher mLauncher;
private AccessibilityManager mAccessibilityManager;
private RemoteAction mAllAppsAction;
private boolean mRegistered;
public SystemActions(Launcher launcher) {
mLauncher = launcher;
mAccessibilityManager = (AccessibilityManager) launcher.getSystemService(
Context.ACCESSIBILITY_SERVICE);
mAllAppsAction = new RemoteAction(
Icon.createWithResource(launcher, R.drawable.ic_apps),
launcher.getString(R.string.all_apps_label),
launcher.getString(R.string.all_apps_label),
launcher.createPendingResult(SYSTEM_ACTION_ID_ALL_APPS, new Intent(),
0 /* flags */));
}
public void register() {
if (mRegistered) {
return;
}
mAccessibilityManager.registerSystemAction(mAllAppsAction, SYSTEM_ACTION_ID_ALL_APPS);
mRegistered = true;
}
public void unregister() {
if (!mRegistered) {
return;
}
mAccessibilityManager.unregisterSystemAction(SYSTEM_ACTION_ID_ALL_APPS);
mRegistered = false;
}
public void onActivityResult(int requestCode) {
if (requestCode == SYSTEM_ACTION_ID_ALL_APPS) {
showAllApps();
}
}
private void showAllApps() {
LauncherStateManager stateManager = mLauncher.getStateManager();
stateManager.goToState(NORMAL);
stateManager.goToState(ALL_APPS);
}
}
@@ -45,8 +45,8 @@ import android.util.Log;
import androidx.annotation.MainThread;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.R;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.popup.RemoteActionShortcut;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.util.MainThreadInitializedObject;
@@ -62,10 +62,13 @@ public abstract class BaseRecentsViewStateController<T extends View>
@Override
public void setState(@NonNull LauncherState state) {
ScaleAndTranslation scaleAndTranslation = state
.getOverviewScaleAndTranslation(mLauncher);
ScaleAndTranslation scaleAndTranslation = state.getOverviewScaleAndTranslation(mLauncher);
float translationX = scaleAndTranslation.translationX;
if (mRecentsView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
translationX = -translationX;
}
SCALE_PROPERTY.set(mRecentsView, scaleAndTranslation.scale);
mRecentsView.setTranslationX(scaleAndTranslation.translationX);
mRecentsView.setTranslationX(translationX);
mRecentsView.setTranslationY(scaleAndTranslation.translationY);
getContentAlphaProperty().set(mRecentsView, state.overviewUi ? 1f : 0);
@@ -96,9 +99,13 @@ public abstract class BaseRecentsViewStateController<T extends View>
void setStateWithAnimationInternal(@NonNull final LauncherState toState,
@NonNull StateAnimationConfig config, @NonNull PendingAnimation setter) {
ScaleAndTranslation scaleAndTranslation = toState.getOverviewScaleAndTranslation(mLauncher);
float translationX = scaleAndTranslation.translationX;
if (mRecentsView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
translationX = -translationX;
}
setter.setFloat(mRecentsView, SCALE_PROPERTY, scaleAndTranslation.scale,
config.getInterpolator(ANIM_OVERVIEW_SCALE, LINEAR));
setter.setFloat(mRecentsView, VIEW_TRANSLATE_X, scaleAndTranslation.translationX,
setter.setFloat(mRecentsView, VIEW_TRANSLATE_X, translationX,
config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_X, LINEAR));
setter.setFloat(mRecentsView, VIEW_TRANSLATE_Y, scaleAndTranslation.translationY,
config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, LINEAR));
@@ -1,48 +0,0 @@
/*
* Copyright (C) 2020 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.uioverrides;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import android.content.Context;
import android.os.Bundle;
import android.util.Size;
import android.view.View;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.graphics.LauncherPreviewRenderer;
import com.android.systemui.shared.system.SurfaceViewRequestReceiver;
/** Render preview using surface view. */
public class PreviewSurfaceRenderer {
/** Handle a received surface view request. */
public static void render(Context context, Bundle bundle) {
String gridName = bundle.getString("name");
bundle.remove("name");
if (gridName == null) {
gridName = InvariantDeviceProfile.getCurrentGridName(context);
}
final InvariantDeviceProfile idp = new InvariantDeviceProfile(context, gridName);
MAIN_EXECUTOR.execute(() -> {
View view = new LauncherPreviewRenderer(context, idp).getRenderedView();
new SurfaceViewRequestReceiver().onReceive(context, bundle, view,
new Size(view.getMeasuredWidth(), view.getMeasuredHeight()));
});
}
}
@@ -70,10 +70,10 @@ public interface InputConsumer {
}
/**
* Returns true if the given input consumer is in the hierarchy of this input consumer.
* Returns the active input consumer is in the hierarchy of this input consumer.
*/
default boolean isInConsumerHierarchy(InputConsumer candidate) {
return this == candidate;
default InputConsumer getActiveConsumerInHierarchy() {
return this;
}
/**
@@ -96,15 +96,15 @@ public interface InputConsumer {
}
default String getName() {
String name = "";
StringBuilder name = new StringBuilder();
for (int i = 0; i < NAMES.length; i++) {
if ((getType() & (1 << i)) != 0) {
if (name.length() > 0) {
name += ":";
name.append(":");
}
name += NAMES[i];
name.append(NAMES[i]);
}
}
return name;
return name.toString();
}
}
@@ -21,7 +21,7 @@ import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import com.android.launcher3.AppInfo;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.util.InstantAppResolver;
/**
@@ -22,6 +22,8 @@ import static android.view.MotionEvent.ACTION_MOVE;
import static android.view.MotionEvent.ACTION_POINTER_DOWN;
import static android.view.MotionEvent.ACTION_UP;
import static com.android.quickstep.util.RecentsOrientedState.postDisplayRotation;
import android.content.res.Resources;
import android.graphics.Matrix;
import android.graphics.Point;
@@ -33,8 +35,8 @@ import android.view.Surface;
import com.android.launcher3.R;
import com.android.launcher3.ResourceUtils;
import com.android.launcher3.states.RotationHelper;
import com.android.launcher3.util.DefaultDisplay;
import com.android.quickstep.util.RecentsOrientedState.SurfaceRotation;
import java.io.PrintWriter;
@@ -52,15 +54,30 @@ class OrientationTouchTransformer {
private static final boolean DEBUG = false;
private static final int MAX_ORIENTATIONS = 4;
private final Matrix mTmpMatrix = new Matrix();
private final float[] mTmpPoint = new float[2];
private SparseArray<OrientationRectF> mSwipeTouchRegions = new SparseArray<>(MAX_ORIENTATIONS);
private final RectF mAssistantLeftRegion = new RectF();
private final RectF mAssistantRightRegion = new RectF();
private int mCurrentRotation;
private int mCurrentDisplayRotation;
private boolean mEnableMultipleRegions;
private Resources mResources;
private OrientationRectF mLastRectTouched;
private SysUINavigationMode.Mode mMode;
private QuickStepContractInfo mContractInfo;
/**
* Represents if we're currently in a swipe "session" of sorts. If value is -1, then user
* has not tapped on an active nav region. Otherwise it will be the rotation of the display
* when the user first interacted with the active nav bar region.
* The "session" ends when {@link #enableMultipleRegions(boolean, DefaultDisplay.Info)} is
* called - usually from a timeout or if user starts interacting w/ the foreground app.
*
* This is different than {@link #mLastRectTouched} as it can get reset by the system whereas
* the rect is purely used for tracking touch interactions and usually this "session" will
* outlast the touch interaction.
*/
private int mQuickStepStartingRotation = -1;
/** For testability */
@@ -68,6 +85,7 @@ class OrientationTouchTransformer {
float getWindowCornerRadius();
}
OrientationTouchTransformer(Resources resources, SysUINavigationMode.Mode mode,
QuickStepContractInfo contractInfo) {
mResources = resources;
@@ -92,20 +110,21 @@ class OrientationTouchTransformer {
* @see #enableMultipleRegions(boolean, DefaultDisplay.Info)
*/
void createOrAddTouchRegion(DefaultDisplay.Info info) {
mCurrentRotation = info.rotation;
if (mQuickStepStartingRotation > -1 && mCurrentRotation == mQuickStepStartingRotation) {
// Ignore nav bars in other rotations except for the one we started out in
mCurrentDisplayRotation = info.rotation;
if (mQuickStepStartingRotation > -1
&& mCurrentDisplayRotation == mQuickStepStartingRotation) {
// User already was swiping and the current screen is same rotation as the starting one
// Remove active nav bars in other rotations except for the one we started out in
resetSwipeRegions(info);
return;
}
OrientationRectF region = mSwipeTouchRegions.get(mCurrentRotation);
OrientationRectF region = mSwipeTouchRegions.get(mCurrentDisplayRotation);
if (region != null) {
return;
}
if (mEnableMultipleRegions) {
mSwipeTouchRegions.put(mCurrentRotation, createRegionForDisplay(info));
mSwipeTouchRegions.put(mCurrentDisplayRotation, createRegionForDisplay(info));
} else {
resetSwipeRegions(info);
}
@@ -123,12 +142,6 @@ class OrientationTouchTransformer {
if (!enableMultipleRegions) {
mQuickStepStartingRotation = -1;
resetSwipeRegions(info);
} else {
if (mLastRectTouched != null) {
// mLastRectTouched can be null if gesture type is changed (ex. from settings)
// but nav bar hasn't been interacted with yet.
mQuickStepStartingRotation = mLastRectTouched.mRotation;
}
}
}
@@ -140,17 +153,25 @@ class OrientationTouchTransformer {
*/
private void resetSwipeRegions(DefaultDisplay.Info region) {
if (DEBUG) {
Log.d(TAG, "clearing all regions except rotation: " + mCurrentRotation);
Log.d(TAG, "clearing all regions except rotation: " + mCurrentDisplayRotation);
}
mCurrentRotation = region.rotation;
mCurrentDisplayRotation = region.rotation;
OrientationRectF regionToKeep = mSwipeTouchRegions.get(mCurrentDisplayRotation);
mSwipeTouchRegions.clear();
mSwipeTouchRegions.put(mCurrentRotation, createRegionForDisplay(region));
mSwipeTouchRegions.put(mCurrentDisplayRotation,
regionToKeep != null ? regionToKeep : createRegionForDisplay(region));
}
private void resetSwipeRegions() {
OrientationRectF regionToKeep = mSwipeTouchRegions.get(mCurrentDisplayRotation);
mSwipeTouchRegions.clear();
mSwipeTouchRegions.put(mCurrentDisplayRotation, regionToKeep);
}
private OrientationRectF createRegionForDisplay(DefaultDisplay.Info display) {
if (DEBUG) {
Log.d(TAG, "creating rotation region for: " + mCurrentRotation);
Log.d(TAG, "creating rotation region for: " + mCurrentDisplayRotation);
}
Point size = display.realSize;
@@ -220,6 +241,10 @@ class OrientationTouchTransformer {
}
}
int getQuickStepStartingRotation() {
return mQuickStepStartingRotation;
}
public void transform(MotionEvent event) {
int eventAction = event.getActionMasked();
switch (eventAction) {
@@ -252,6 +277,11 @@ class OrientationTouchTransformer {
}
if (rect.applyTransform(event, false)) {
mLastRectTouched = rect;
if (mCurrentDisplayRotation == mLastRectTouched.mRotation) {
// Start a touch session for the default nav region for the display
mQuickStepStartingRotation = mLastRectTouched.mRotation;
resetSwipeRegions();
}
if (DEBUG) {
Log.d(TAG, "set active region: " + rect);
}
@@ -304,42 +334,46 @@ class OrientationTouchTransformer {
}
boolean applyTransform(MotionEvent event, boolean forceTransform) {
// TODO(b/149658423): See if we can use RotationHelper.getRotationMatrix here
MotionEvent tmp = MotionEvent.obtain(event);
Matrix outMatrix = new Matrix();
int delta = RotationHelper.deltaRotation(mCurrentRotation, mRotation);
switch (delta) {
case Surface.ROTATION_0:
outMatrix.reset();
break;
case Surface.ROTATION_90:
outMatrix.setRotate(270);
outMatrix.postTranslate(0, mHeight);
break;
case Surface.ROTATION_180:
outMatrix.setRotate(180);
outMatrix.postTranslate(mHeight, mWidth);
break;
case Surface.ROTATION_270:
outMatrix.setRotate(90);
outMatrix.postTranslate(mWidth, 0);
break;
mTmpMatrix.reset();
postDisplayRotation(deltaRotation(mCurrentDisplayRotation, mRotation),
mHeight, mWidth, mTmpMatrix);
if (forceTransform) {
if (DEBUG) {
Log.d(TAG, "Transforming rotation due to forceTransform, "
+ "mCurrentRotation: " + mCurrentDisplayRotation
+ "mRotation: " + mRotation);
}
event.transform(mTmpMatrix);
return true;
}
mTmpPoint[0] = event.getX();
mTmpPoint[1] = event.getY();
mTmpMatrix.mapPoints(mTmpPoint);
tmp.transform(outMatrix);
if (DEBUG) {
Log.d(TAG, "original: " + event.getX() + ", " + event.getY()
+ " new: " + tmp.getX() + ", " + tmp.getY()
+ " new: " + mTmpPoint[0] + ", " + mTmpPoint[1]
+ " rect: " + this + " forceTransform: " + forceTransform
+ " contains: " + contains(tmp.getX(), tmp.getY()));
+ " contains: " + contains(mTmpPoint[0], mTmpPoint[1]));
}
if (forceTransform || contains(tmp.getX(), tmp.getY())) {
event.transform(outMatrix);
tmp.recycle();
if (contains(mTmpPoint[0], mTmpPoint[1])) {
event.transform(mTmpMatrix);
return true;
}
return false;
}
}
/**
* @return how many factors {@param newRotation} is rotated 90 degrees clockwise.
* E.g. 1->Rotated by 90 degrees clockwise, 2->Rotated 180 clockwise...
* A value of 0 means no rotation has been applied
*/
@SurfaceRotation
private static int deltaRotation(int oldRotation, int newRotation) {
int delta = newRotation - oldRotation;
if (delta < 0) delta += 4;
return delta;
}
}
@@ -40,6 +40,7 @@ import com.android.systemui.shared.system.PackageManagerWrapper;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Objects;
import java.util.function.Consumer;
/**
* Class to keep track of the current overview component based off user preferences and app updates
@@ -57,6 +58,9 @@ public final class OverviewComponentObserver {
private final Intent mMyHomeIntent;
private final Intent mFallbackIntent;
private final SparseIntArray mConfigChangesMap = new SparseIntArray();
private Consumer<Boolean> mOverviewChangeListener = b -> { };
private String mUpdateRegisteredPackage;
private BaseActivityInterface mActivityInterface;
private Intent mOverviewIntent;
@@ -64,10 +68,10 @@ public final class OverviewComponentObserver {
private boolean mIsDefaultHome;
private boolean mIsHomeDisabled;
public OverviewComponentObserver(Context context, RecentsAnimationDeviceState deviceState) {
mContext = context;
mDeviceState = deviceState;
mCurrentHomeIntent = new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_HOME)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
@@ -95,6 +99,13 @@ public final class OverviewComponentObserver {
updateOverviewTargets();
}
/**
* Sets a listener for changes in {@link #isHomeAndOverviewSame()}
*/
public void setOverviewChangeListener(Consumer<Boolean> overviewChangeListener) {
mOverviewChangeListener = overviewChangeListener;
}
public void onSystemUiStateChanged() {
if (mDeviceState.isHomeDisabled() != mIsHomeDisabled) {
updateOverviewTargets();
@@ -159,6 +170,7 @@ public final class OverviewComponentObserver {
ACTION_PACKAGE_REMOVED));
}
}
mOverviewChangeListener.accept(mIsHomeAndOverviewSame);
}
/**
@@ -123,6 +123,17 @@ public class RecentTasksList extends TaskStackChangeListener {
mChangeId++;
}
@Override
public void onRecentTaskListUpdated() {
// In some cases immediately after booting, the tasks in the system recent task list may be
// loaded, but not in the active task hierarchy in the system. These tasks are displayed in
// overview, but removing them don't result in a onTaskStackChanged() nor a onTaskRemoved()
// callback (those are for changes to the active tasks), but the task list is still updated,
// so we should also invalidate the change id to ensure we load a new list instead of
// reusing a stale list.
mChangeId++;
}
@Override
public void onTaskRemoved(int taskId) {
mTasks = loadTasksInBackground(Integer.MAX_VALUE, false);
@@ -91,9 +91,6 @@ public class RecentsAnimationCallbacks implements
RemoteAnimationTargetCompat[] appTargets,
RemoteAnimationTargetCompat[] wallpaperTargets,
Rect homeContentInsets, Rect minimizedHomeBounds) {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NO_START_FROM_RECENTS, "onAnimationStart");
}
RecentsAnimationTargets targets = new RecentsAnimationTargets(appTargets,
wallpaperTargets, homeContentInsets, minimizedHomeBounds);
mController = new RecentsAnimationController(animationController,
@@ -22,13 +22,13 @@ import static android.view.MotionEvent.ACTION_UP;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import android.annotation.NonNull;
import android.os.SystemClock;
import android.util.Log;
import android.view.InputEvent;
import android.view.KeyEvent;
import android.view.MotionEvent;
import androidx.annotation.NonNull;
import androidx.annotation.UiThread;
import com.android.launcher3.util.Preconditions;
@@ -17,6 +17,7 @@ package com.android.quickstep;
import static android.content.Intent.ACTION_USER_UNLOCKED;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
import static com.android.quickstep.SysUINavigationMode.Mode.THREE_BUTTONS;
import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
@@ -73,6 +74,8 @@ public class RecentsAnimationDeviceState implements
NavigationModeChangeListener,
DefaultDisplay.DisplayInfoChangeListener {
private static final String TAG = "RecentsAnimationDeviceState";
private final Context mContext;
private final SysUINavigationMode mSysUiNavMode;
private final DefaultDisplay mDefaultDisplay;
@@ -95,6 +98,7 @@ public class RecentsAnimationDeviceState implements
@Override
public void onReceive(Context context, Intent intent) {
if (ACTION_USER_UNLOCKED.equals(intent.getAction())) {
Log.d(TAG, "User Unlocked Broadcast Received");
mIsUserUnlocked = true;
notifyUserUnlocked();
}
@@ -120,7 +124,6 @@ public class RecentsAnimationDeviceState implements
private Runnable mOnDestroyFrozenTaskRunnable;
public RecentsAnimationDeviceState(Context context) {
final ContentResolver resolver = context.getContentResolver();
mContext = context;
mSysUiNavMode = SysUINavigationMode.INSTANCE.get(context);
mDefaultDisplay = DefaultDisplay.INSTANCE.get(context);
@@ -511,8 +514,16 @@ public class RecentsAnimationDeviceState implements
mOrientationTouchTransformer.transform(event);
}
public void enableMultipleRegions(boolean enable) {
void enableMultipleRegions(boolean enable) {
mOrientationTouchTransformer.enableMultipleRegions(enable, mDefaultDisplay.getInfo());
if (enable) {
UI_HELPER_EXECUTOR.execute(() -> {
int quickStepStartingRotation =
mOrientationTouchTransformer.getQuickStepStartingRotation();
SystemUiProxy.INSTANCE.get(mContext)
.onQuickSwitchToNewTask(quickStepStartingRotation);
});
}
}
public int getCurrentActiveRotation() {
@@ -334,6 +334,7 @@ public class SystemUiProxy implements ISystemUiProxy {
}
}
@Override
public void onQuickSwitchToNewTask(int rotation) {
if (mSystemUiProxy != null) {
try {
@@ -53,9 +53,6 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
@UiThread
public RecentsAnimationCallbacks startRecentsAnimation(GestureState gestureState,
Intent intent, RecentsAnimationCallbacks.RecentsAnimationListener listener) {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NO_START_FROM_RECENTS, "startRecentsAnimation");
}
// Notify if recents animation is still running
if (mController != null) {
String msg = "New recents animation started before old animation completed";
@@ -35,7 +35,7 @@ final class BackGestureTutorialEngagedController extends BackGestureTutorialCont
@Override
void transitToController() {
super.transitToController();
mHandCoachingAnimation.maybeStartLoopedAnimation(mTutorialTypeInfo.get().getTutorialType());
mHandCoachingAnimation.startLoopedAnimation(mTutorialTypeInfo.get().getTutorialType());
}
@Override
@@ -38,8 +38,6 @@ final class BackGestureTutorialHandAnimation {
private final ImageView mHandCoachingView;
private final AnimatedVectorDrawable mGestureAnimation;
private boolean mIsAnimationPlayed = false;
BackGestureTutorialHandAnimation(Context context, View rootView) {
mHandCoachingView = rootView.findViewById(
R.id.back_gesture_tutorial_fragment_hand_coaching);
@@ -47,20 +45,15 @@ final class BackGestureTutorialHandAnimation {
R.drawable.back_gesture);
}
boolean isRunning() {
return mGestureAnimation.isRunning();
}
/**
* Starts animation if the playground is launched for the first time.
* [Re]starts animation for the given tutorial.
*/
void maybeStartLoopedAnimation(TutorialType tutorialType) {
if (isRunning() || mIsAnimationPlayed) {
return;
void startLoopedAnimation(TutorialType tutorialType) {
if (mGestureAnimation.isRunning()) {
stop();
}
mIsAnimationPlayed = true;
clearAnimationCallbacks();
mGestureAnimation.clearAnimationCallbacks();
mGestureAnimation.registerAnimationCallback(
new Animatable2.AnimationCallback() {
@Override
@@ -78,17 +71,11 @@ final class BackGestureTutorialHandAnimation {
float rotationY = tutorialType == TutorialType.LEFT_EDGE_BACK_NAVIGATION ? 180f : 0f;
mHandCoachingView.setRotationY(rotationY);
mHandCoachingView.setImageDrawable(mGestureAnimation);
mHandCoachingView.postDelayed(() -> mGestureAnimation.start(),
ANIMATION_START_DELAY.toMillis());
}
private void clearAnimationCallbacks() {
mGestureAnimation.clearAnimationCallbacks();
mHandCoachingView.postDelayed(mGestureAnimation::start, ANIMATION_START_DELAY.toMillis());
}
void stop() {
mIsAnimationPlayed = false;
clearAnimationCallbacks();
mGestureAnimation.clearAnimationCallbacks();
mGestureAnimation.stop();
}
}
@@ -23,16 +23,16 @@ import static android.stats.launcher.nano.Launcher.OVERVIEW;
import android.content.Context;
import com.android.launcher3.FolderInfo;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.logging.StatsLogUtils;
import com.android.launcher3.model.AllAppsList;
import com.android.launcher3.model.BaseModelUpdateTask;
import com.android.launcher3.model.BgDataModel;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.util.IntSparseArrayMap;
import java.util.ArrayList;
@@ -0,0 +1,96 @@
/*
* Copyright (C) 2020 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.quickstep.util;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import android.content.SharedPreferences;
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.LauncherStateManager.StateListener;
import com.android.launcher3.util.OnboardingPrefs;
import com.android.quickstep.SysUINavigationMode;
/**
* Extends {@link OnboardingPrefs} for quickstep-specific onboarding data.
*/
public class QuickstepOnboardingPrefs extends OnboardingPrefs<BaseQuickstepLauncher> {
public QuickstepOnboardingPrefs(BaseQuickstepLauncher launcher, SharedPreferences sharedPrefs,
LauncherStateManager stateManager) {
super(launcher, sharedPrefs, stateManager);
if (!getBoolean(HOME_BOUNCE_SEEN)) {
mStateManager.addStateListener(new StateListener() {
@Override
public void onStateTransitionStart(LauncherState toState) { }
@Override
public void onStateTransitionComplete(LauncherState finalState) {
boolean swipeUpEnabled = SysUINavigationMode.INSTANCE
.get(mLauncher).getMode().hasGestures;
LauncherState prevState = mStateManager.getLastState();
if (((swipeUpEnabled && finalState == OVERVIEW) || (!swipeUpEnabled
&& finalState == ALL_APPS && prevState == NORMAL) ||
hasReachedMaxCount(HOME_BOUNCE_COUNT))) {
mSharedPrefs.edit().putBoolean(HOME_BOUNCE_SEEN, true).apply();
mStateManager.removeStateListener(this);
}
}
});
}
if (!getBoolean(SHELF_BOUNCE_SEEN)) {
mStateManager.addStateListener(new StateListener() {
@Override
public void onStateTransitionStart(LauncherState toState) { }
@Override
public void onStateTransitionComplete(LauncherState finalState) {
LauncherState prevState = mStateManager.getLastState();
if ((finalState == ALL_APPS && prevState == OVERVIEW) ||
hasReachedMaxCount(SHELF_BOUNCE_COUNT)) {
mSharedPrefs.edit().putBoolean(SHELF_BOUNCE_SEEN, true).apply();
mStateManager.removeStateListener(this);
}
}
});
}
if (!hasReachedMaxCount(ALL_APPS_COUNT)) {
mStateManager.addStateListener(new StateListener() {
@Override
public void onStateTransitionStart(LauncherState toState) { }
@Override
public void onStateTransitionComplete(LauncherState finalState) {
if (finalState == ALL_APPS) {
if (incrementEventCount(ALL_APPS_COUNT)) {
mStateManager.removeStateListener(this);
mLauncher.getScrimView().updateDragHandleVisibility();
}
}
}
});
}
}
}
@@ -0,0 +1,217 @@
/*
* Copyright (C) 2020 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.quickstep.util;
import static android.hardware.camera2.params.OutputConfiguration.ROTATION_180;
import static android.view.Surface.ROTATION_0;
import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import android.graphics.Matrix;
import android.graphics.RectF;
import android.view.MotionEvent;
import android.view.Surface;
import androidx.annotation.IntDef;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.touch.PortraitPagedViewHandler;
import java.lang.annotation.Retention;
/**
* Container to hold orientation/rotation related information for Launcher.
* This is not meant to be an abstraction layer for applying different functionality between
* the different orientation/rotations. For that see {@link PagedOrientationHandler}
*
* This class has initial default state assuming the device and foreground app have
* no ({@link Surface#ROTATION_0} rotation.
*/
public final class RecentsOrientedState {
@Retention(SOURCE)
@IntDef({ROTATION_0, ROTATION_90, ROTATION_180, ROTATION_270})
public @interface SurfaceRotation {}
private PagedOrientationHandler mOrientationHandler = PagedOrientationHandler.PORTRAIT;
private @SurfaceRotation int mTouchRotation = ROTATION_0;
private @SurfaceRotation int mDisplayRotation = ROTATION_0;
/**
* If {@code true} we default to {@link PortraitPagedViewHandler} and don't support any fake
* launcher orientations.
*/
private boolean mDisableMultipleOrientations;
private final Matrix mTmpMatrix = new Matrix();
private final Matrix mTmpInverseMatrix = new Matrix();
/**
* Sets the appropriate {@link PagedOrientationHandler} for {@link #mOrientationHandler}
* @param touchRotation The rotation the nav bar region that is touched is in
* @param displayRotation Rotation of the display/device
*
* @return true if there was any change in the internal state as a result of this call,
* false otherwise
*/
public boolean update(
@SurfaceRotation int touchRotation, @SurfaceRotation int displayRotation) {
if (!FeatureFlags.ENABLE_FIXED_ROTATION_TRANSFORM.get()) {
return false;
}
if (mDisableMultipleOrientations) {
return false;
}
if (mDisplayRotation == displayRotation && mTouchRotation == touchRotation) {
return false;
}
mDisplayRotation = displayRotation;
mTouchRotation = touchRotation;
if (mTouchRotation == ROTATION_90) {
mOrientationHandler = PagedOrientationHandler.LANDSCAPE;
} else if (mTouchRotation == ROTATION_270) {
mOrientationHandler = PagedOrientationHandler.SEASCAPE;
} else {
mOrientationHandler = PagedOrientationHandler.PORTRAIT;
}
return true;
}
public boolean areMultipleLayoutOrientationsDisabled() {
return mDisableMultipleOrientations;
}
/**
* Setting this preference will render future calls to {@link #update(int, int)} as a no-op.
*/
public void disableMultipleOrientations(boolean disable) {
mDisableMultipleOrientations = disable;
if (disable) {
mDisplayRotation = mTouchRotation = ROTATION_0;
mOrientationHandler = PagedOrientationHandler.PORTRAIT;
}
}
@SurfaceRotation
public int getDisplayRotation() {
return mDisplayRotation;
}
@SurfaceRotation
public int getTouchRotation() {
return mTouchRotation;
}
public int getTouchRotationDegrees() {
switch (mTouchRotation) {
case ROTATION_90:
return 90;
case ROTATION_180:
return 180;
case ROTATION_270:
return 270;
case ROTATION_0:
default:
return 0;
}
}
public PagedOrientationHandler getOrientationHandler() {
return mOrientationHandler;
}
/**
* For landscape, since the navbar is already in a vertical position, we don't have to do any
* rotations as the change in Y coordinate is what is read. We only flip the sign of the
* y coordinate to make it match existing behavior of swipe to the top to go previous
*/
public void flipVertical(MotionEvent ev) {
mTmpMatrix.setScale(1, -1);
ev.transform(mTmpMatrix);
}
/**
* Creates a matrix to transform the given motion event specified by degrees.
* If inverse is {@code true}, the inverse of that matrix will be applied
*/
public void transformEvent(float degrees, MotionEvent ev, boolean inverse) {
mTmpMatrix.setRotate(inverse ? -degrees : degrees);
ev.transform(mTmpMatrix);
// TODO: Add scaling back in based on degrees
/*
if (getWidth() > 0 && getHeight() > 0) {
float scale = ((float) getWidth()) / getHeight();
transform.postScale(scale, 1 / scale);
}
*/
}
public void mapRectFromNormalOrientation(RectF src, int screenWidth, int screenHeight) {
mTmpMatrix.reset();
postDisplayRotation(mDisplayRotation, screenWidth, screenHeight, mTmpMatrix);
mTmpMatrix.mapRect(src);
}
public void mapInverseRectFromNormalOrientation(RectF src, int screenWidth, int screenHeight) {
mTmpMatrix.reset();
postDisplayRotation(mDisplayRotation, screenWidth, screenHeight, mTmpMatrix);
mTmpMatrix.invert(mTmpInverseMatrix);
mTmpInverseMatrix.mapRect(src);
}
@SurfaceRotation
public static int getRotationForUserDegreesRotated(float degrees) {
int threshold = 70;
if (degrees >= (360 - threshold) || degrees < (threshold)) {
return ROTATION_0;
} else if (degrees < (90 + threshold)) {
return ROTATION_270;
} else if (degrees < 180 + threshold) {
return ROTATION_180;
} else {
return ROTATION_90;
}
}
/**
* Posts the transformation on the matrix representing the provided display rotation
*/
public static void postDisplayRotation(@SurfaceRotation int displayRotation,
float screenWidth, float screenHeight, Matrix out) {
switch (displayRotation) {
case ROTATION_0:
return;
case ROTATION_90:
out.postRotate(270);
out.postTranslate(0, screenWidth);
break;
case ROTATION_180:
out.postRotate(180);
out.postTranslate(screenHeight, screenWidth);
break;
case ROTATION_270:
out.postRotate(90);
out.postTranslate(screenHeight, 0);
break;
}
}
}
@@ -43,6 +43,7 @@ import com.android.launcher3.Utilities;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.uioverrides.states.OverviewState;
import com.android.launcher3.util.OnboardingPrefs;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ScrimView;
import com.android.quickstep.SysUINavigationMode;
@@ -75,6 +76,7 @@ public class ShelfScrimView extends ScrimView<BaseQuickstepLauncher>
private final float mRadius;
private final int mMaxScrimAlpha;
private final Paint mPaint;
private final OnboardingPrefs mOnboardingPrefs;
// Mid point where the alpha changes
private int mMidAlpha;
@@ -100,6 +102,7 @@ public class ShelfScrimView extends ScrimView<BaseQuickstepLauncher>
private boolean mRemainingScreenPathValid = false;
private Mode mSysUINavigationMode;
private boolean mIsTwoZoneSwipeModel;
public ShelfScrimView(Context context, AttributeSet attrs) {
super(context, attrs);
@@ -108,6 +111,7 @@ public class ShelfScrimView extends ScrimView<BaseQuickstepLauncher>
mEndAlpha = Color.alpha(mEndScrim);
mRadius = BOTTOM_CORNER_RADIUS_RATIO * Themes.getDialogCornerRadius(context);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mOnboardingPrefs = mLauncher.getOnboardingPrefs();
// Just assume the easiest UI for now, until we have the proper layout information.
mDrawingFlatColor = true;
@@ -140,9 +144,11 @@ public class ShelfScrimView extends ScrimView<BaseQuickstepLauncher>
// Show the shelf more quickly before reaching overview progress.
mBeforeMidProgressColorInterpolator = ACCEL_2;
mAfterMidProgressColorInterpolator = ACCEL;
mIsTwoZoneSwipeModel = FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get();
} else {
mBeforeMidProgressColorInterpolator = ACCEL;
mAfterMidProgressColorInterpolator = Interpolators.clampToProgress(ACCEL, 0.5f, 1f);
mIsTwoZoneSwipeModel = false;
}
}
@@ -236,9 +242,9 @@ public class ShelfScrimView extends ScrimView<BaseQuickstepLauncher>
@Override
protected boolean shouldDragHandleBeVisible() {
boolean twoZoneSwipeModel = FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get()
&& SysUINavigationMode.removeShelfFromOverview(mLauncher);
return twoZoneSwipeModel || super.shouldDragHandleBeVisible();
boolean needsAllAppsEdu = mIsTwoZoneSwipeModel
&& !mOnboardingPrefs.hasReachedMaxCount(OnboardingPrefs.ALL_APPS_COUNT);
return needsAllAppsEdu || super.shouldDragHandleBeVisible();
}
@Override
@@ -48,9 +48,9 @@ import androidx.test.uiautomator.By;
import androidx.test.uiautomator.UiDevice;
import androidx.test.uiautomator.Until;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.tapl.Background;
import com.android.launcher3.testcomponent.ListViewService;
import com.android.launcher3.testcomponent.ListViewService.SimpleViewsFactory;
+1 -1
View File
@@ -20,7 +20,7 @@
android:gravity="center">
<TextView
style="@style/PrimaryMediumText"
style="@style/PrimaryHeadline"
android:textColor="?attr/workProfileOverlayTextColor"
android:id="@+id/work_apps_paused_title"
android:layout_width="wrap_content"
+8
View File
@@ -118,6 +118,8 @@
<!-- numHotseatIcons defaults to numColumns, if not specified -->
<attr name="numHotseatIcons" format="integer" />
<attr name="dbFile" format="string" />
<!-- numAllAppsColumns defaults to numColumns, if not specified -->
<attr name="numAllAppsColumns" format="integer" />
<attr name="defaultLayoutId" format="reference" />
<attr name="demoModeLayoutId" format="reference" />
</declare-styleable>
@@ -133,6 +135,12 @@
<attr name="iconTextSize" format="float" />
<!-- If true, this display option is used to determine the default grid -->
<attr name="canBeDefault" format="boolean" />
<!-- The following values are only enabled if grid is supported. -->
<!-- allAppsIconSize defaults to iconSize, if not specified -->
<attr name="allAppsIconSize" format="float" />
<!-- allAppsIconTextSize defaults to iconTextSize, if not specified -->
<attr name="allAppsIconTextSize" format="float" />
</declare-styleable>
<declare-styleable name="CellLayout">
+2 -2
View File
@@ -131,8 +131,8 @@
<item name="dismiss_task_trans_x_damping_ratio" type="dimen" format="float">0.5</item>
<item name="dismiss_task_trans_x_stiffness" type="dimen" format="float">1500</item>
<item name="horizontal_spring_damping_ratio" type="dimen" format="float">0.75</item>
<item name="horizontal_spring_stiffness" type="dimen" format="float">200</item>
<item name="horizontal_spring_damping_ratio" type="dimen" format="float">0.8</item>
<item name="horizontal_spring_stiffness" type="dimen" format="float">400</item>
<item name="swipe_up_rect_scale_damping_ratio" type="dimen" format="float">0.75</item>
<item name="swipe_up_rect_scale_stiffness" type="dimen" format="float">200</item>
+4
View File
@@ -343,6 +343,10 @@
<string name="work_apps_paused_title">Work profile is paused</string>
<!--- body shown when user opens work apps tab while work apps are paused -->
<string name="work_apps_paused_body">Work apps can\t send you notifications, use your battery, or access your location</string>
<!-- content description for paused work apps list -->
<string name="work_apps_paused_content_description">Work profile is paused. Work apps can\t send you notifications, use your battery, or access your location</string>
<!-- A tip shown pointing at work toggle -->
<string name="work_switch_tip">Pause work apps and notifications</string>
+3
View File
@@ -226,6 +226,9 @@
<style name="TextHeadline" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle" />
<style name="PrimaryMediumText" parent="@android:style/TextAppearance.DeviceDefault.Medium"/>
<style name="PrimaryHeadline" parent="@android:style/TextAppearance.DeviceDefault.DialogWindowTitle">
<item name="android:textStyle">bold</item>
</style>
<style name="TextTitle" parent="@android:style/TextAppearance.DeviceDefault" />
+2 -2
View File
@@ -21,7 +21,7 @@ include $(CLEAR_VARS)
LOCAL_MODULE := LauncherRoboTests
LOCAL_MODULE_CLASS := JAVA_LIBRARIES
LOCAL_SDK_VERSION := current
LOCAL_SDK_VERSION := system_current
LOCAL_SRC_FILES := $(call all-java-files-under, src)
LOCAL_STATIC_JAVA_LIBRARIES := \
androidx.test.runner \
@@ -47,7 +47,7 @@ include $(BUILD_STATIC_JAVA_LIBRARY)
include $(CLEAR_VARS)
LOCAL_MODULE := RunLauncherRoboTests
LOCAL_SDK_VERSION := current
LOCAL_SDK_VERSION := system_current
LOCAL_JAVA_LIBRARIES := LauncherRoboTests
LOCAL_RESOURCE_DIR := $(LOCAL_PATH)/res
@@ -1 +1,17 @@
sdk=29
shadows= \
com.android.launcher3.shadows.LShadowAppPredictionManager \
com.android.launcher3.shadows.LShadowAppWidgetManager \
com.android.launcher3.shadows.LShadowBackupManager \
com.android.launcher3.shadows.LShadowBitmap \
com.android.launcher3.shadows.LShadowLauncherApps \
com.android.launcher3.shadows.LShadowTypeface \
com.android.launcher3.shadows.LShadowUserManager \
com.android.launcher3.shadows.LShadowWallpaperManager \
com.android.launcher3.shadows.ShadowDeviceFlag \
com.android.launcher3.shadows.ShadowLooperExecutor \
com.android.launcher3.shadows.ShadowMainThreadInitializedObject \
com.android.launcher3.shadows.ShadowOverrides \
com.android.launcher3.shadows.ShadowSyncRtSurfaceTransactionApplierCompat \
application=com.android.launcher3.util.LauncherTestApplication
@@ -1,6 +1,6 @@
# Model data used by CacheDataUpdatedTaskTest
classMap s com.android.launcher3.WorkspaceItemInfo
classMap s com.android.launcher3.model.data.WorkspaceItemInfo
# Items for the BgDataModel
@@ -1,7 +1,7 @@
# Model data used by PackageInstallStateChangeTaskTest
classMap s com.android.launcher3.WorkspaceItemInfo
classMap w com.android.launcher3.LauncherAppWidgetInfo
classMap s com.android.launcher3.model.data.WorkspaceItemInfo
classMap w com.android.launcher3.model.data.LauncherAppWidgetInfo
# Items for the BgDataModel
@@ -20,20 +20,20 @@ import static org.junit.Assert.assertEquals;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.os.UserHandle;
import com.android.launcher3.AppInfo;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.shadows.LShadowUserManager;
import com.android.launcher3.util.LauncherRoboTestRunner;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
@RunWith(LauncherRoboTestRunner.class)
@RunWith(RobolectricTestRunner.class)
public final class FolderNameProviderTest {
private Context mContext;
private WorkspaceItemInfo mItem1;
@@ -45,13 +45,13 @@ public final class FolderNameProviderTest {
mItem1 = new WorkspaceItemInfo(new AppInfo(
new ComponentName("a.b.c", "a.b.c/a.b.c.d"),
"title1",
LShadowUserManager.newUserHandle(10),
UserHandle.of(10),
new Intent().setComponent(new ComponentName("a.b.c", "a.b.c/a.b.c.d"))
));
mItem2 = new WorkspaceItemInfo(new AppInfo(
new ComponentName("a.b.c", "a.b.c/a.b.c.d"),
"title2",
LShadowUserManager.newUserHandle(10),
UserHandle.of(10),
new Intent().setComponent(new ComponentName("a.b.c", "a.b.c/a.b.c.d"))
));
}
@@ -3,12 +3,11 @@ package com.android.launcher3.logging;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import com.android.launcher3.util.LauncherRoboTestRunner;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.Shadows;
import org.robolectric.util.Scheduler;
@@ -21,7 +20,7 @@ import java.util.Calendar;
/**
* Tests for {@link FileLog}
*/
@RunWith(LauncherRoboTestRunner.class)
@RunWith(RobolectricTestRunner.class)
public class FileLogTest {
private File mTempDir;
@@ -14,23 +14,23 @@ import android.graphics.Rect;
import android.util.Pair;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.util.ContentWriter;
import com.android.launcher3.util.GridOccupancy;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.IntSparseArrayMap;
import com.android.launcher3.util.LauncherModelHelper;
import com.android.launcher3.util.LauncherRoboTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.LooperMode;
import org.robolectric.annotation.LooperMode.Mode;
@@ -41,7 +41,7 @@ import java.util.List;
/**
* Tests for {@link AddWorkspaceItemsTask}
*/
@RunWith(LauncherRoboTestRunner.class)
@RunWith(RobolectricTestRunner.class)
@LooperMode(Mode.PAUSED)
public class AddWorkspaceItemsTaskTest {
@@ -43,11 +43,11 @@ import com.android.launcher3.provider.RestoreDbTask;
import com.android.launcher3.shadows.LShadowBackupManager;
import com.android.launcher3.shadows.LShadowUserManager;
import com.android.launcher3.util.LauncherModelHelper;
import com.android.launcher3.util.LauncherRoboTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.LooperMode;
import org.robolectric.shadow.api.Shadow;
@@ -55,7 +55,7 @@ import org.robolectric.shadow.api.Shadow;
/**
* Tests to verify backup and restore flow.
*/
@RunWith(LauncherRoboTestRunner.class)
@RunWith(RobolectricTestRunner.class)
@LooperMode(LooperMode.Mode.PAUSED)
public class BackupRestoreTest {
@@ -17,19 +17,19 @@ import android.os.UserManager;
import androidx.annotation.NonNull;
import com.android.launcher3.AppInfo;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.cache.CachingLogic;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.util.LauncherModelHelper;
import com.android.launcher3.util.LauncherRoboTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.LooperMode;
import org.robolectric.annotation.LooperMode.Mode;
@@ -40,7 +40,7 @@ import java.util.HashSet;
/**
* Tests for {@link CacheDataUpdatedTask}
*/
@RunWith(LauncherRoboTestRunner.class)
@RunWith(RobolectricTestRunner.class)
@LooperMode(Mode.PAUSED)
public class CacheDataUpdatedTaskTest {
@@ -36,11 +36,11 @@ import com.android.launcher3.LauncherProvider;
import com.android.launcher3.LauncherProvider.DatabaseHelper;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.R;
import com.android.launcher3.util.LauncherRoboTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.io.File;
@@ -48,7 +48,7 @@ import java.io.File;
/**
* Tests for {@link DbDowngradeHelper}
*/
@RunWith(LauncherRoboTestRunner.class)
@RunWith(RobolectricTestRunner.class)
public class DbDowngradeHelperTest {
private static final String SCHEMA_FILE = "test_schema.json";
@@ -28,17 +28,17 @@ import android.content.pm.PackageInstaller;
import android.content.pm.PackageInstaller.SessionInfo;
import android.content.pm.PackageInstaller.SessionParams;
import com.android.launcher3.FolderInfo;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.util.LauncherLayoutBuilder;
import com.android.launcher3.util.LauncherModelHelper;
import com.android.launcher3.util.LauncherRoboTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.LooperMode;
import org.robolectric.annotation.LooperMode.Mode;
@@ -46,7 +46,7 @@ import org.robolectric.annotation.LooperMode.Mode;
/**
* Tests for layout parser for remote layout
*/
@RunWith(LauncherRoboTestRunner.class)
@RunWith(RobolectricTestRunner.class)
@LooperMode(Mode.PAUSED)
public class DefaultLayoutProviderTest {
@@ -23,17 +23,17 @@ import android.graphics.Point;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.LauncherSettings.Settings;
import com.android.launcher3.util.LauncherModelHelper;
import com.android.launcher3.util.LauncherRoboTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
/**
* Unit tests for {@link GridBackupTable}
*/
@RunWith(LauncherRoboTestRunner.class)
@RunWith(RobolectricTestRunner.class)
public class GridBackupTableTest {
private static final int BACKUP_ITEM_COUNT = 12;
@@ -20,11 +20,11 @@ import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.GridSizeMigrationTask.MultiStepMigrationTask;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.LauncherModelHelper;
import com.android.launcher3.util.LauncherRoboTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.HashSet;
@@ -33,7 +33,7 @@ import java.util.LinkedList;
/**
* Unit tests for {@link GridSizeMigrationTask}
*/
@RunWith(LauncherRoboTestRunner.class)
@RunWith(RobolectricTestRunner.class)
public class GridSizeMigrationTaskTest {
private LauncherModelHelper mModelHelper;
@@ -39,17 +39,17 @@ import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.util.LauncherModelHelper;
import com.android.launcher3.util.LauncherRoboTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.HashSet;
/** Unit tests for {@link GridSizeMigrationTaskV2} */
@RunWith(LauncherRoboTestRunner.class)
@RunWith(RobolectricTestRunner.class)
public class GridSizeMigrationTaskV2Test {
private LauncherModelHelper mModelHelper;
@@ -50,17 +50,17 @@ import android.database.MatrixCursor;
import android.os.Process;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.LauncherRoboTestRunner;
import com.android.launcher3.util.PackageManagerHelper;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.LooperMode;
import org.robolectric.annotation.LooperMode.Mode;
@@ -68,7 +68,7 @@ import org.robolectric.annotation.LooperMode.Mode;
/**
* Tests for {@link LoaderCursor}
*/
@RunWith(LauncherRoboTestRunner.class)
@RunWith(RobolectricTestRunner.class)
@LooperMode(Mode.PAUSED)
public class LoaderCursorTest {
@@ -127,7 +127,7 @@ public class LoaderCursorTest {
new Intent().setComponent(cn), false /* allowMissingTarget */, true))
.get();
assertNotNull(info);
assertTrue(PackageManagerHelper.isLauncherAppTarget(info.intent));
assertTrue(PackageManagerHelper.isLauncherAppTarget(info.getIntent()));
}
@Test
@@ -141,7 +141,7 @@ public class LoaderCursorTest {
new Intent().setComponent(cn), true /* allowMissingTarget */, true))
.get();
assertNotNull(info);
assertTrue(PackageManagerHelper.isLauncherAppTarget(info.intent));
assertTrue(PackageManagerHelper.isLauncherAppTarget(info.getIntent()));
}
@Test
@@ -26,20 +26,20 @@ import static org.robolectric.Shadows.shadowOf;
import android.os.Process;
import com.android.launcher3.AppInfo;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.PagedView;
import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.LauncherLayoutBuilder;
import com.android.launcher3.util.LauncherModelHelper;
import com.android.launcher3.util.LauncherRoboTestRunner;
import com.android.launcher3.util.LooperExecutor;
import com.android.launcher3.util.ViewOnDrawExecutor;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.LooperMode;
import org.robolectric.annotation.LooperMode.Mode;
@@ -55,7 +55,7 @@ import java.util.stream.Collectors;
/**
* Tests to verify multiple callbacks in Loader
*/
@RunWith(LauncherRoboTestRunner.class)
@RunWith(RobolectricTestRunner.class)
@LooperMode(Mode.PAUSED)
public class ModelMultiCallbacksTest {
@@ -2,16 +2,16 @@ package com.android.launcher3.model;
import static org.junit.Assert.assertEquals;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.pm.PackageInstallInfo;
import com.android.launcher3.util.LauncherModelHelper;
import com.android.launcher3.util.LauncherRoboTestRunner;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.LooperMode;
import org.robolectric.annotation.LooperMode.Mode;
@@ -21,7 +21,7 @@ import java.util.HashSet;
/**
* Tests for {@link PackageInstallStateChangedTask}
*/
@RunWith(LauncherRoboTestRunner.class)
@RunWith(RobolectricTestRunner.class)
@LooperMode(Mode.PAUSED)
public class PackageInstallStateChangedTaskTest {
@@ -27,10 +27,9 @@ import static org.mockito.Mockito.spy;
import android.content.pm.ShortcutInfo;
import com.android.launcher3.util.LauncherRoboTestRunner;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import java.util.ArrayList;
@@ -40,7 +39,7 @@ import java.util.List;
/**
* Tests the sorting and filtering of shortcuts in {@link PopupPopulator}.
*/
@RunWith(LauncherRoboTestRunner.class)
@RunWith(RobolectricTestRunner.class)
public class PopupPopulatorTest {
@Test
@@ -23,16 +23,16 @@ import android.database.sqlite.SQLiteDatabase;
import com.android.launcher3.LauncherProvider.DatabaseHelper;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.util.LauncherRoboTestRunner;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
/**
* Tests for {@link RestoreDbTask}
*/
@RunWith(LauncherRoboTestRunner.class)
@RunWith(RobolectricTestRunner.class)
public class RestoreDbTaskTest {
@Test
@@ -0,0 +1,38 @@
/*
* Copyright (C) 2020 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.shadows;
import static org.mockito.Mockito.mock;
import android.app.prediction.AppPredictionContext;
import android.app.prediction.AppPredictionManager;
import android.app.prediction.AppPredictor;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
/**
* Shadow for {@link AppPredictionManager} which create mock predictors
*/
@Implements(value = AppPredictionManager.class)
public class LShadowAppPredictionManager {
@Implementation
public AppPredictor createAppPredictionSession(AppPredictionContext predictionContext) {
return mock(AppPredictor.class);
}
}
@@ -16,7 +16,6 @@
package com.android.launcher3.shadows;
import android.os.Parcel;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.SparseBooleanArray;
@@ -51,12 +50,4 @@ public class LShadowUserManager extends ShadowUserManager {
public void setUserLocked(UserHandle userHandle, boolean enabled) {
mLockedUsers.put(userHandle.hashCode(), enabled);
}
// Create user handle from parcel since UserHandle.of() was only added in later APIs.
public static UserHandle newUserHandle(int uid) {
Parcel userParcel = Parcel.obtain();
userParcel.writeInt(uid);
userParcel.setDataPosition(0);
return new UserHandle(userParcel);
}
}
@@ -0,0 +1,42 @@
/*
* Copyright (C) 2020 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.shadows;
import static org.robolectric.shadow.api.Shadow.invokeConstructor;
import static org.robolectric.util.ReflectionHelpers.ClassParameter.from;
import android.view.View;
import org.robolectric.annotation.Implementation;
import org.robolectric.annotation.Implements;
import org.robolectric.annotation.RealObject;
/**
* Shadow for SyncRtSurfaceTransactionApplierCompat to override default functionality
*/
@Implements(className = "com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat",
isInAndroidSdk = false)
public class ShadowSyncRtSurfaceTransactionApplierCompat {
@RealObject
private Object mRealObject;
@Implementation
protected void __constructor__(View view) {
invokeConstructor(mRealObject.getClass(), mRealObject, from(View.class, null));
}
}
@@ -14,23 +14,20 @@
* limitations under the License.
*/package com.android.launcher3.ui;
import static android.view.View.MeasureSpec.EXACTLY;
import static android.view.View.MeasureSpec.makeMeasureSpec;
import static com.android.launcher3.util.LauncherModelHelper.TEST_PACKAGE;
import static com.android.launcher3.util.LauncherUIHelper.buildAndBindLauncher;
import static com.android.launcher3.util.LauncherUIHelper.doLayout;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotEquals;
import static org.mockito.Mockito.mock;
import android.app.Activity;
import android.content.Context;
import android.os.SystemClock;
import android.provider.Settings;
import android.view.InputDevice;
import android.view.MotionEvent;
import android.view.MotionEvent.PointerProperties;
import android.view.View;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
@@ -44,24 +41,21 @@ import com.android.launcher3.shadows.ShadowOverrides;
import com.android.launcher3.util.LauncherLayoutBuilder;
import com.android.launcher3.util.LauncherLayoutBuilder.FolderBuilder;
import com.android.launcher3.util.LauncherModelHelper;
import com.android.launcher3.util.LauncherRoboTestRunner;
import com.android.launcher3.util.ViewOnDrawExecutor;
import com.android.launcher3.widget.WidgetsFullSheet;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.annotation.LooperMode;
import org.robolectric.annotation.LooperMode.Mode;
import org.robolectric.shadows.ShadowLooper;
import org.robolectric.util.ReflectionHelpers;
/**
* Tests scroll behavior at various Launcher UI components
*/
@RunWith(LauncherRoboTestRunner.class)
@RunWith(RobolectricTestRunner.class)
@LooperMode(Mode.PAUSED)
public class LauncherUIScrollTest {
@@ -166,23 +160,7 @@ public class LauncherUIScrollTest {
private Launcher loadLauncher() throws Exception {
mModelHelper.setupDefaultLayoutProvider(mLayoutBuilder).loadModelSync();
Launcher launcher = Robolectric.buildActivity(Launcher.class).setup().get();
doLayout(launcher);
ViewOnDrawExecutor executor = ReflectionHelpers.getField(launcher, "mPendingExecutor");
if (executor != null) {
executor.runAllTasks();
}
return launcher;
}
private static void doLayout(Activity activity) {
DeviceProfile dp = InvariantDeviceProfile.INSTANCE
.get(RuntimeEnvironment.application).portraitProfile;
View view = activity.getWindow().getDecorView();
view.measure(makeMeasureSpec(dp.widthPx, EXACTLY), makeMeasureSpec(dp.heightPx, EXACTLY));
view.layout(0, 0, dp.widthPx, dp.heightPx);
ShadowLooper.idleMainLooper();
return buildAndBindLauncher();
}
private static MotionEvent createScrollEvent(int scroll) {
@@ -1,16 +1,17 @@
package com.android.launcher3.util;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
/**
* Unit tests for {@link GridOccupancy}
*/
@RunWith(LauncherRoboTestRunner.class)
@RunWith(RobolectricTestRunner.class)
public class GridOccupancyTest {
@Test
@@ -19,11 +19,12 @@ import static com.google.common.truth.Truth.assertThat;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
/**
* Robolectric unit tests for {@link IntArray}
*/
@RunWith(LauncherRoboTestRunner.class)
@RunWith(RobolectricTestRunner.class)
public class IntArrayTest {
@Test
@@ -17,17 +17,18 @@ package com.android.launcher3.util;
import static com.google.common.truth.Truth.assertThat;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
/**
* Robolectric unit tests for {@link IntSet}
*/
@RunWith(LauncherRoboTestRunner.class)
@RunWith(RobolectricTestRunner.class)
public class IntSetTest {
@Test
@@ -36,9 +36,7 @@ import android.net.Uri;
import android.os.Process;
import android.provider.Settings;
import com.android.launcher3.AppInfo;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherModel.ModelUpdateTask;
@@ -47,6 +45,8 @@ import com.android.launcher3.LauncherSettings;
import com.android.launcher3.model.AllAppsList;
import com.android.launcher3.model.BgDataModel;
import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.pm.UserCache;
import org.mockito.ArgumentCaptor;
@@ -1,101 +0,0 @@
/*
* Copyright (C) 2019 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.util;
import static org.mockito.Mockito.mock;
import com.android.launcher3.shadows.LShadowAppWidgetManager;
import com.android.launcher3.shadows.LShadowBackupManager;
import com.android.launcher3.shadows.LShadowBitmap;
import com.android.launcher3.shadows.LShadowLauncherApps;
import com.android.launcher3.shadows.LShadowTypeface;
import com.android.launcher3.shadows.LShadowUserManager;
import com.android.launcher3.shadows.LShadowWallpaperManager;
import com.android.launcher3.shadows.ShadowDeviceFlag;
import com.android.launcher3.shadows.ShadowLooperExecutor;
import com.android.launcher3.shadows.ShadowMainThreadInitializedObject;
import com.android.launcher3.shadows.ShadowOverrides;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import org.junit.runners.model.InitializationError;
import org.robolectric.DefaultTestLifecycle;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.TestLifecycle;
import org.robolectric.annotation.Config;
import org.robolectric.shadows.ShadowLog;
import java.lang.reflect.Method;
import javax.annotation.Nonnull;
/**
* Test runner with Launcher specific configurations
*/
public class LauncherRoboTestRunner extends RobolectricTestRunner {
private static final Class<?>[] SHADOWS = new Class<?>[] {
LShadowAppWidgetManager.class,
LShadowUserManager.class,
LShadowLauncherApps.class,
LShadowBitmap.class,
LShadowBackupManager.class,
LShadowTypeface.class,
LShadowWallpaperManager.class,
ShadowLooperExecutor.class,
ShadowMainThreadInitializedObject.class,
ShadowDeviceFlag.class,
ShadowOverrides.class
};
public LauncherRoboTestRunner(Class<?> testClass) throws InitializationError {
super(testClass);
}
@Override
protected Config buildGlobalConfig() {
return new Config.Builder().setShadows(SHADOWS).build();
}
@Nonnull
@Override
protected Class<? extends TestLifecycle> getTestLifecycleClass() {
return LauncherTestLifecycle.class;
}
public static class LauncherTestLifecycle extends DefaultTestLifecycle {
@Override
public void beforeTest(Method method) {
super.beforeTest(method);
ShadowLog.stream = System.out;
// Disable plugins
PluginManagerWrapper.INSTANCE.initializeForTesting(mock(PluginManagerWrapper.class));
// Initialize mock wallpaper manager
LShadowWallpaperManager.initializeMock();
}
@Override
public void afterTest(Method method) {
super.afterTest(method);
ShadowLog.stream = null;
ShadowMainThreadInitializedObject.resetInitializedObjects();
ShadowOverrides.clearProvider();
}
}
}
@@ -0,0 +1,54 @@
/*
* Copyright (C) 2020 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.util;
import static org.mockito.Mockito.mock;
import android.app.Application;
import com.android.launcher3.shadows.LShadowWallpaperManager;
import com.android.launcher3.shadows.ShadowMainThreadInitializedObject;
import com.android.launcher3.shadows.ShadowOverrides;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import org.robolectric.TestLifecycleApplication;
import org.robolectric.shadows.ShadowLog;
import java.lang.reflect.Method;
public class LauncherTestApplication extends Application implements TestLifecycleApplication {
@Override
public void beforeTest(Method method) {
ShadowLog.stream = System.out;
// Disable plugins
PluginManagerWrapper.INSTANCE.initializeForTesting(mock(PluginManagerWrapper.class));
// Initialize mock wallpaper manager
LShadowWallpaperManager.initializeMock();
}
@Override
public void prepareTest(Object test) { }
@Override
public void afterTest(Method method) {
ShadowLog.stream = null;
ShadowMainThreadInitializedObject.resetInitializedObjects();
ShadowOverrides.clearProvider();
}
}
@@ -0,0 +1,102 @@
/*
* Copyright (C) 2020 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.util;
import static android.view.View.MeasureSpec.EXACTLY;
import static android.view.View.MeasureSpec.makeMeasureSpec;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ResolveInfo;
import android.graphics.Point;
import android.view.View;
import android.view.WindowManager;
import com.android.launcher3.Launcher;
import org.robolectric.Robolectric;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.android.controller.ActivityController;
import org.robolectric.shadows.ShadowLooper;
import org.robolectric.util.ReflectionHelpers;
import java.util.List;
/**
* Utility class to help manage Launcher UI and related objects for test.
*/
public class LauncherUIHelper {
/**
* Returns the class name for the Launcher activity as defined in the manifest
*/
public static String getLauncherClassName() {
Context context = RuntimeEnvironment.application;
Intent homeIntent = new Intent(Intent.ACTION_MAIN)
.addCategory(Intent.CATEGORY_HOME)
.setPackage(context.getPackageName())
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
List<ResolveInfo> launchers = context.getPackageManager()
.queryIntentActivities(homeIntent, 0);
if (launchers.size() != 1) {
return null;
}
return launchers.get(0).activityInfo.name;
}
/**
* Returns an activity controller for Launcher activity defined in the manifest
*/
public static <T extends Launcher> ActivityController<T> buildLauncher() {
try {
Class<T> tClass = (Class<T>) Class.forName(getLauncherClassName());
return Robolectric.buildActivity(tClass);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* Creates and binds a Launcher activity defined in the manifest.
* Note that the model must be bound before calling this
*/
public static <T extends Launcher> T buildAndBindLauncher() {
ActivityController<T> controller = buildLauncher();
T launcher = controller.setup().get();
doLayout(launcher);
ViewOnDrawExecutor executor = ReflectionHelpers.getField(launcher, "mPendingExecutor");
if (executor != null) {
executor.runAllTasks();
}
return launcher;
}
/**
* Performs a measure and layout pass for the given activity
*/
public static void doLayout(Activity activity) {
Point size = new Point();
RuntimeEnvironment.application.getSystemService(WindowManager.class)
.getDefaultDisplay().getSize(size);
View view = activity.getWindow().getDecorView();
view.measure(makeMeasureSpec(size.x, EXACTLY), makeMeasureSpec(size.y, EXACTLY));
view.layout(0, 0, size.x, size.y);
ShadowLooper.idleMainLooper();
}
}
@@ -34,15 +34,15 @@ import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.WidgetPreviewLoader;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.util.LauncherRoboTestRunner;
import com.android.launcher3.model.data.PackageItemInfo;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.RuntimeEnvironment;
import org.robolectric.shadows.ShadowPackageManager;
import org.robolectric.util.ReflectionHelpers;
@@ -50,7 +50,7 @@ import org.robolectric.util.ReflectionHelpers;
import java.util.ArrayList;
import java.util.Collections;
@RunWith(LauncherRoboTestRunner.class)
@RunWith(RobolectricTestRunner.class)
public class WidgetsListAdapterTest {
@Mock private LayoutInflater mMockLayoutInflater;
@@ -350,7 +350,7 @@ public class AppWidgetResizeFrame extends AbstractFloatingView implements View.O
mWidgetView.requestLayout();
}
static void updateWidgetSizeRanges(AppWidgetHostView widgetView, Launcher launcher,
public static void updateWidgetSizeRanges(AppWidgetHostView widgetView, Launcher launcher,
int spanX, int spanY) {
getWidgetSizeRanges(launcher, spanX, spanY, sTmpRect);
widgetView.updateAppWidgetSize(null, sTmpRect.left, sTmpRect.top,
@@ -17,6 +17,7 @@ import androidx.annotation.WorkerThread;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.model.LoaderTask;
import com.android.launcher3.model.WidgetsModel;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.provider.RestoreDbTask;
import com.android.launcher3.util.ContentWriter;
@@ -44,6 +44,8 @@ import com.android.launcher3.LauncherProvider.SqlArguments;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.icons.GraphicsUtils;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.qsb.QsbContainerView;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.PackageManagerHelper;
@@ -39,6 +39,8 @@ import androidx.annotation.Nullable;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.ItemClickHandler;
@@ -53,7 +53,12 @@ import com.android.launcher3.graphics.PreloadIconDrawable;
import com.android.launcher3.icons.DotRenderer;
import com.android.launcher3.icons.IconCache.IconLoadRequest;
import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.PackageItemInfo;
import com.android.launcher3.model.data.PromiseAppInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.IconLabelDotView;
@@ -46,6 +46,7 @@ import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.Thunk;
+8 -2
View File
@@ -59,6 +59,7 @@ import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.folder.PreviewBackground;
import com.android.launcher3.graphics.DragPreviewProvider;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.util.CellAndSpan;
import com.android.launcher3.util.GridOccupancy;
import com.android.launcher3.util.ParcelableSparseArray;
@@ -959,8 +960,13 @@ public class CellLayout extends ViewGroup {
height = r.height();
}
if (v != null && v.getViewType() == DraggableView.DRAGGABLE_ICON) {
left += ((mCellWidth * spanX) - dragOutline.getWidth()) / 2;
// Center horizontaly
left += ((mCellWidth * spanX) - dragOutline.getWidth()) / 2;
if (v != null && v.getViewType() == DraggableView.DRAGGABLE_WIDGET) {
// Center vertically
top += ((mCellHeight * spanY) - dragOutline.getHeight()) / 2;
} else if (v != null && v.getViewType() == DraggableView.DRAGGABLE_ICON) {
int cHeight = getShortcutsAndWidgets().getCellContentHeight();
int cellPaddingY = (int) Math.max(0, ((mCellHeight - cHeight) / 2f));
top += cellPaddingY;
@@ -28,6 +28,10 @@ import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.logging.LoggerUtils;
import com.android.launcher3.model.ModelWriter;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.views.Snackbar;
+18 -28
View File
@@ -25,8 +25,6 @@ import android.graphics.Rect;
import android.util.DisplayMetrics;
import android.view.Surface;
import androidx.annotation.Nullable;
import com.android.launcher3.CellLayout.ContainerType;
import com.android.launcher3.graphics.IconShape;
import com.android.launcher3.icons.DotRenderer;
@@ -36,8 +34,6 @@ import com.android.launcher3.util.DefaultDisplay;
public class DeviceProfile {
public final InvariantDeviceProfile inv;
// IDP with no grid override values.
@Nullable private final InvariantDeviceProfile originalIdp;
// Device properties
public final boolean isTablet;
@@ -138,11 +134,10 @@ public class DeviceProfile {
public DotRenderer mDotRendererAllApps;
public DeviceProfile(Context context, InvariantDeviceProfile inv,
InvariantDeviceProfile originalIDP, Point minSize, Point maxSize,
Point minSize, Point maxSize,
int width, int height, boolean isLandscape, boolean isMultiWindowMode) {
this.inv = inv;
this.originalIdp = inv;
this.isLandscape = isLandscape;
this.isMultiWindowMode = isMultiWindowMode;
@@ -234,19 +229,6 @@ public class DeviceProfile {
// Recalculate the available dimensions using the new hotseat size.
updateAvailableDimensions(dm, res);
}
if (originalIDP != null) {
// Grid size change should not affect All Apps UI, so we use the original profile
// measurements here.
DeviceProfile originalProfile = isLandscape
? originalIDP.landscapeProfile
: originalIDP.portraitProfile;
allAppsIconSizePx = originalProfile.iconSizePx;
allAppsIconTextSizePx = originalProfile.iconTextSizePx;
allAppsCellHeightPx = originalProfile.allAppsCellHeightPx;
allAppsIconDrawablePaddingPx = originalProfile.iconDrawablePaddingOriginalPx;
allAppsCellWidthPx = allAppsIconSizePx + 2 * allAppsIconDrawablePaddingPx;
}
updateWorkspacePadding();
// This is done last, after iconSizePx is calculated above.
@@ -259,8 +241,8 @@ public class DeviceProfile {
public DeviceProfile copy(Context context) {
Point size = new Point(availableWidthPx, availableHeightPx);
return new DeviceProfile(context, inv, originalIdp, size, size, widthPx, heightPx,
isLandscape, isMultiWindowMode);
return new DeviceProfile(context, inv, size, size, widthPx, heightPx, isLandscape,
isMultiWindowMode);
}
public DeviceProfile getMultiWindowProfile(Context context, Point mwSize) {
@@ -271,8 +253,8 @@ public class DeviceProfile {
// In multi-window mode, we can have widthPx = availableWidthPx
// and heightPx = availableHeightPx because Launcher uses the InvariantDeviceProfiles'
// widthPx and heightPx values where it's needed.
DeviceProfile profile = new DeviceProfile(context, inv, null, mwSize, mwSize,
mwSize.x, mwSize.y, isLandscape, true);
DeviceProfile profile = new DeviceProfile(context, inv, mwSize, mwSize, mwSize.x, mwSize.y,
isLandscape, true);
// If there isn't enough vertical cell padding with the labels displayed, hide the labels.
float workspaceCellPaddingY = profile.getCellSize().y - profile.iconSizePx
@@ -356,11 +338,19 @@ public class DeviceProfile {
}
cellWidthPx = iconSizePx + iconDrawablePaddingPx;
allAppsIconSizePx = iconSizePx;
allAppsIconTextSizePx = iconTextSizePx;
allAppsIconDrawablePaddingPx = iconDrawablePaddingPx;
allAppsCellHeightPx = getCellSize().y;
allAppsCellWidthPx = allAppsIconSizePx + 2 * allAppsIconDrawablePaddingPx;
// All apps
if (allAppsHasDifferentNumColumns()) {
allAppsIconSizePx = ResourceUtils.pxFromDp(inv.allAppsIconSize, dm);
allAppsIconTextSizePx = Utilities.pxFromSp(inv.allAppsIconTextSize, dm);
allAppsCellHeightPx = getCellSize(inv.numAllAppsColumns, inv.numAllAppsColumns).y;
allAppsIconDrawablePaddingPx = iconDrawablePaddingOriginalPx;
} else {
allAppsIconSizePx = iconSizePx;
allAppsIconTextSizePx = iconTextSizePx;
allAppsIconDrawablePaddingPx = iconDrawablePaddingPx;
allAppsCellHeightPx = getCellSize().y;
}
allAppsCellWidthPx = allAppsIconSizePx + allAppsIconDrawablePaddingPx;
if (isVerticalBarLayout()) {
// Always hide the Workspace text with vertical bar layout.
@@ -25,6 +25,7 @@ import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DragView;
import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.folder.FolderNameProvider;
import com.android.launcher3.model.data.ItemInfo;
/**
* Interface defining an object that can receive a drag.
@@ -35,6 +35,7 @@ import android.util.Property;
import com.android.launcher3.graphics.PlaceHolderIconDrawable;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
public class FastBitmapDrawable extends Drawable {
@@ -25,6 +25,7 @@ import android.view.ViewGroup;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderPagedView;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.util.FocusLogic;
import com.android.launcher3.util.Thunk;
+1
View File
@@ -28,6 +28,7 @@ import android.view.ViewGroup;
import android.widget.FrameLayout;
import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
@@ -46,6 +46,10 @@ import androidx.annotation.WorkerThread;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.GraphicsUtils;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.shortcuts.ShortcutRequest;
@@ -47,7 +47,6 @@ import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.graphics.IconShape;
import com.android.launcher3.graphics.LauncherPreviewRenderer;
import com.android.launcher3.util.ConfigMonitor;
import com.android.launcher3.util.DefaultDisplay;
import com.android.launcher3.util.DefaultDisplay.Info;
@@ -61,7 +60,6 @@ import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
public class InvariantDeviceProfile {
@@ -107,6 +105,8 @@ public class InvariantDeviceProfile {
public int iconBitmapSize;
public int fillResIconDpi;
public float iconTextSize;
public float allAppsIconSize;
public float allAppsIconTextSize;
private SparseArray<TypedValue> mExtraAttrs;
@@ -149,6 +149,8 @@ public class InvariantDeviceProfile {
numHotseatIcons = p.numHotseatIcons;
numAllAppsColumns = p.numAllAppsColumns;
dbFile = p.dbFile;
allAppsIconSize = p.allAppsIconSize;
allAppsIconTextSize = p.allAppsIconTextSize;
defaultLayoutId = p.defaultLayoutId;
demoModeLayoutId = p.demoModeLayoutId;
mExtraAttrs = p.mExtraAttrs;
@@ -157,12 +159,9 @@ public class InvariantDeviceProfile {
@TargetApi(23)
private InvariantDeviceProfile(Context context) {
if (context instanceof LauncherPreviewRenderer.PreviewContext) {
throw new IllegalArgumentException(
"PreviewContext is passed into this IDP constructor");
}
String gridName = getCurrentGridName(context);
String gridName = Utilities.getPrefs(context).getBoolean(GRID_OPTIONS_PREFERENCE_KEY, false)
? Utilities.getPrefs(context).getString(KEY_IDP_GRID_NAME, null)
: null;
initGrid(context, gridName);
mConfigMonitor = new ConfigMonitor(context,
APPLY_CONFIG_AT_RUNTIME.get() ? this::onConfigChanged : this::killProcess);
@@ -179,7 +178,7 @@ public class InvariantDeviceProfile {
}
}
/**
/**
* This constructor should NOT have any monitors by design.
*/
public InvariantDeviceProfile(Context context, Display display) {
@@ -211,11 +210,55 @@ public class InvariantDeviceProfile {
Point smallestSize = new Point(displayInfo.smallestSize);
Point largestSize = new Point(displayInfo.largestSize);
ArrayList<DisplayOption> allOptions = getPredefinedDeviceProfiles(context, gridName);
// This guarantees that width < height
float minWidthDps = Utilities.dpiFromPx(Math.min(smallestSize.x, smallestSize.y),
displayInfo.metrics);
float minHeightDps = Utilities.dpiFromPx(Math.min(largestSize.x, largestSize.y),
displayInfo.metrics);
// Sort the profiles based on the closeness to the device size
Collections.sort(allOptions, (a, b) ->
Float.compare(dist(minWidthDps, minHeightDps, a.minWidthDps, a.minHeightDps),
dist(minWidthDps, minHeightDps, b.minWidthDps, b.minHeightDps)));
DisplayOption interpolatedDisplayOption =
invDistWeightedInterpolate(minWidthDps, minHeightDps, allOptions);
GridOption closestProfile = allOptions.get(0).grid;
numRows = closestProfile.numRows;
numColumns = closestProfile.numColumns;
numHotseatIcons = closestProfile.numHotseatIcons;
dbFile = closestProfile.dbFile;
defaultLayoutId = closestProfile.defaultLayoutId;
demoModeLayoutId = closestProfile.demoModeLayoutId;
numFolderRows = closestProfile.numFolderRows;
numFolderColumns = closestProfile.numFolderColumns;
numAllAppsColumns = closestProfile.numAllAppsColumns;
mExtraAttrs = closestProfile.extraAttrs;
if (!closestProfile.name.equals(gridName)) {
Utilities.getPrefs(context).edit()
.putString(KEY_IDP_GRID_NAME, closestProfile.name).apply();
}
iconSize = interpolatedDisplayOption.iconSize;
iconShapePath = getIconShapePath(context);
landscapeIconSize = interpolatedDisplayOption.landscapeIconSize;
iconBitmapSize = ResourceUtils.pxFromDp(iconSize, displayInfo.metrics);
iconTextSize = interpolatedDisplayOption.iconTextSize;
fillResIconDpi = getLauncherIconDensity(iconBitmapSize);
if (Utilities.getPrefs(context).getBoolean(GRID_OPTIONS_PREFERENCE_KEY, false)) {
allAppsIconSize = interpolatedDisplayOption.allAppsIconSize;
allAppsIconTextSize = interpolatedDisplayOption.allAppsIconTextSize;
} else {
allAppsIconSize = iconSize;
allAppsIconTextSize = iconTextSize;
}
// If the partner customization apk contains any grid overrides, apply them
// Supported overrides: numRows, numColumns, iconSize
applyPartnerDeviceProfileOverrides(context, displayInfo.metrics);
Point realSize = new Point(displayInfo.realSize);
// The real size never changes. smallSide and largeSide will remain the
@@ -223,64 +266,10 @@ public class InvariantDeviceProfile {
int smallSide = Math.min(realSize.x, realSize.y);
int largeSide = Math.max(realSize.x, realSize.y);
// We want a list of all options as well as the list of filtered options. This allows us
// to have a consistent UI for areas that the grid size change should not affect
// ie. All Apps should be consistent between grid sizes.
ArrayList<DisplayOption> allOptions = new ArrayList<>();
ArrayList<DisplayOption> filteredOptions = new ArrayList<>();
getPredefinedDeviceProfiles(context, gridName, filteredOptions, allOptions);
if (allOptions.isEmpty() && filteredOptions.isEmpty()) {
throw new RuntimeException("No display option with canBeDefault=true");
}
// Sort the profiles based on the closeness to the device size
Comparator<DisplayOption> comparator = (a, b) -> Float.compare(dist(minWidthDps,
minHeightDps, a.minWidthDps, a.minHeightDps),
dist(minWidthDps, minHeightDps, b.minWidthDps, b.minHeightDps));
// Calculate the device profiles as if there is no grid override.
Collections.sort(allOptions, comparator);
DisplayOption interpolatedDisplayOption =
invDistWeightedInterpolate(minWidthDps, minHeightDps, allOptions);
initGridOption(context, allOptions, interpolatedDisplayOption, displayInfo.metrics);
// Create IDP with no grid override values.
InvariantDeviceProfile originalIDP = new InvariantDeviceProfile(this);
originalIDP.landscapeProfile = new DeviceProfile(context, this, null, smallestSize,
largestSize, largeSide, smallSide, true /* isLandscape */,
false /* isMultiWindowMode */);
originalIDP.portraitProfile = new DeviceProfile(context, this, null, smallestSize,
largestSize, smallSide, largeSide, false /* isLandscape */,
false /* isMultiWindowMode */);
if (filteredOptions.isEmpty()) {
filteredOptions = allOptions;
landscapeProfile = originalIDP.landscapeProfile;
portraitProfile = originalIDP.portraitProfile;
} else {
Collections.sort(filteredOptions, comparator);
interpolatedDisplayOption =
invDistWeightedInterpolate(minWidthDps, minHeightDps, filteredOptions);
initGridOption(context, filteredOptions, interpolatedDisplayOption,
displayInfo.metrics);
numAllAppsColumns = originalIDP.numAllAppsColumns;
landscapeProfile = new DeviceProfile(context, this, originalIDP, smallestSize,
largestSize, largeSide, smallSide, true /* isLandscape */,
false /* isMultiWindowMode */);
portraitProfile = new DeviceProfile(context, this, originalIDP, smallestSize,
largestSize, smallSide, largeSide, false /* isLandscape */,
false /* isMultiWindowMode */);
}
GridOption closestProfile = filteredOptions.get(0).grid;
if (!closestProfile.name.equals(gridName)) {
Utilities.getPrefs(context).edit()
.putString(KEY_IDP_GRID_NAME, closestProfile.name).apply();
}
landscapeProfile = new DeviceProfile(context, this, smallestSize, largestSize,
largeSide, smallSide, true /* isLandscape */, false /* isMultiWindowMode */);
portraitProfile = new DeviceProfile(context, this, smallestSize, largestSize,
smallSide, largeSide, false /* isLandscape */, false /* isMultiWindowMode */);
// We need to ensure that there is enough extra space in the wallpaper
// for the intended parallax effects
@@ -298,34 +287,6 @@ public class InvariantDeviceProfile {
return closestProfile.name;
}
private void initGridOption(Context context, ArrayList<DisplayOption> options,
DisplayOption displayOption, DisplayMetrics metrics) {
GridOption closestProfile = options.get(0).grid;
numRows = closestProfile.numRows;
numColumns = closestProfile.numColumns;
numHotseatIcons = closestProfile.numHotseatIcons;
dbFile = closestProfile.dbFile;
defaultLayoutId = closestProfile.defaultLayoutId;
demoModeLayoutId = closestProfile.demoModeLayoutId;
numFolderRows = closestProfile.numFolderRows;
numFolderColumns = closestProfile.numFolderColumns;
numAllAppsColumns = numColumns;
mExtraAttrs = closestProfile.extraAttrs;
iconSize = displayOption.iconSize;
iconShapePath = getIconShapePath(context);
landscapeIconSize = displayOption.landscapeIconSize;
iconBitmapSize = ResourceUtils.pxFromDp(iconSize, metrics);
iconTextSize = displayOption.iconTextSize;
fillResIconDpi = getLauncherIconDensity(iconBitmapSize);
// If the partner customization apk contains any grid overrides, apply them
// Supported overrides: numRows, numColumns, iconSize
applyPartnerDeviceProfileOverrides(context, metrics);
}
@Nullable
public TypedValue getAttrValue(int attr) {
return mExtraAttrs == null ? null : mExtraAttrs.get(attr);
@@ -403,13 +364,7 @@ public class InvariantDeviceProfile {
}
}
/**
* @param gridName The current grid name.
* @param filteredOptionsOut List filled with all the filtered options based on gridName.
* @param allOptionsOut List filled with all the options that can be the default option.
*/
static void getPredefinedDeviceProfiles(Context context, String gridName,
ArrayList<DisplayOption> filteredOptionsOut, ArrayList<DisplayOption> allOptionsOut) {
static ArrayList<DisplayOption> getPredefinedDeviceProfiles(Context context, String gridName) {
ArrayList<DisplayOption> profiles = new ArrayList<>();
try (XmlResourceParser parser = context.getResources().getXml(R.xml.device_profiles)) {
final int depth = parser.getDepth();
@@ -436,19 +391,26 @@ public class InvariantDeviceProfile {
throw new RuntimeException(e);
}
ArrayList<DisplayOption> filteredProfiles = new ArrayList<>();
if (!TextUtils.isEmpty(gridName)) {
for (DisplayOption option : profiles) {
if (gridName.equals(option.grid.name)) {
filteredOptionsOut.add(option);
filteredProfiles.add(option);
}
}
}
for (DisplayOption option : profiles) {
if (option.canBeDefault) {
allOptionsOut.add(option);
if (filteredProfiles.isEmpty()) {
// No grid found, use the default options
for (DisplayOption option : profiles) {
if (option.canBeDefault) {
filteredProfiles.add(option);
}
}
}
if (filteredProfiles.isEmpty()) {
throw new RuntimeException("No display option with canBeDefault=true");
}
return filteredProfiles;
}
private int getLauncherIconDensity(int requiredSize) {
@@ -493,7 +455,7 @@ public class InvariantDeviceProfile {
@VisibleForTesting
static DisplayOption invDistWeightedInterpolate(float width, float height,
ArrayList<DisplayOption> points) {
ArrayList<DisplayOption> points) {
float weights = 0;
DisplayOption p = points.get(0);
@@ -573,6 +535,8 @@ public class InvariantDeviceProfile {
private final int numHotseatIcons;
private final String dbFile;
private final int numAllAppsColumns;
private final int defaultLayoutId;
private final int demoModeLayoutId;
@@ -596,6 +560,8 @@ public class InvariantDeviceProfile {
R.styleable.GridDisplayOption_numFolderRows, numRows);
numFolderColumns = a.getInt(
R.styleable.GridDisplayOption_numFolderColumns, numColumns);
numAllAppsColumns = a.getInt(
R.styleable.GridDisplayOption_numAllAppsColumns, numColumns);
a.recycle();
@@ -615,6 +581,8 @@ public class InvariantDeviceProfile {
private float iconSize;
private float iconTextSize;
private float landscapeIconSize;
private float allAppsIconSize;
private float allAppsIconTextSize;
DisplayOption(GridOption grid, Context context, AttributeSet attrs) {
this.grid = grid;
@@ -633,6 +601,10 @@ public class InvariantDeviceProfile {
iconSize);
iconTextSize = a.getFloat(R.styleable.ProfileDisplayOption_iconTextSize, 0);
allAppsIconSize = a.getFloat(R.styleable.ProfileDisplayOption_allAppsIconSize,
iconSize);
allAppsIconTextSize = a.getFloat(R.styleable.ProfileDisplayOption_allAppsIconTextSize,
iconTextSize);
a.recycle();
}
@@ -647,14 +619,18 @@ public class InvariantDeviceProfile {
private DisplayOption multiply(float w) {
iconSize *= w;
landscapeIconSize *= w;
allAppsIconSize *= w;
iconTextSize *= w;
allAppsIconTextSize *= w;
return this;
}
private DisplayOption add(DisplayOption p) {
iconSize += p.iconSize;
landscapeIconSize += p.landscapeIconSize;
allAppsIconSize += p.allAppsIconSize;
iconTextSize += p.iconTextSize;
allAppsIconTextSize += p.allAppsIconTextSize;
return this;
}
}
@@ -672,4 +648,4 @@ public class InvariantDeviceProfile {
onConfigChanged(context);
}
}
}
}
+23 -1
View File
@@ -110,6 +110,12 @@ import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.model.ModelWriter;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
import com.android.launcher3.model.data.PromiseAppInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.notification.NotificationListener;
import com.android.launcher3.pm.PinRequestHelper;
import com.android.launcher3.pm.UserCache;
@@ -133,6 +139,7 @@ import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
import com.android.launcher3.util.OnboardingPrefs;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.PendingRequestArgs;
@@ -294,6 +301,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
// We only want to get the SharedPreferences once since it does an FS stat each time we get
// it from the context.
private SharedPreferences mSharedPrefs;
private OnboardingPrefs mOnboardingPrefs;
// Activity result which needs to be processed after workspace has loaded.
private ActivityResultInfo mPendingActivityResult;
@@ -361,6 +369,8 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
mAllAppsController = new AllAppsTransitionController(this);
mStateManager = new LauncherStateManager(this);
mOnboardingPrefs = createOnboardingPrefs(mSharedPrefs, mStateManager);
mAppWidgetManager = new WidgetManagerHelper(this);
mAppWidgetHost = new LauncherAppWidgetHost(this,
appWidgetId -> getWorkspace().removeWidget(appWidgetId));
@@ -452,6 +462,15 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
return new LauncherOverlayManager() { };
}
protected OnboardingPrefs createOnboardingPrefs(SharedPreferences sharedPrefs,
LauncherStateManager stateManager) {
return new OnboardingPrefs<>(this, sharedPrefs, stateManager);
}
public OnboardingPrefs getOnboardingPrefs() {
return mOnboardingPrefs;
}
@Override
public void onPluginConnected(OverlayPlugin overlayManager, Context context) {
switchOverlay(() -> overlayManager.createOverlayManager(this, this));
@@ -1443,6 +1462,8 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
mLauncherCallbacks.onHomeIntent(internalStateHandled);
}
mOverlayManager.hideOverlay(isStarted() && !isForceInvisible());
} else if (Intent.ACTION_ALL_APPS.equals(intent.getAction())) {
getStateManager().goToState(ALL_APPS, alreadyOnHome);
}
TraceHelper.INSTANCE.endSection(traceToken);
@@ -2557,9 +2578,10 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
writer.println(prefix + "\tmRotationHelper: " + mRotationHelper);
writer.println(prefix + "\tmAppWidgetHost.isListening: " + mAppWidgetHost.isListening());
// Extra logging for b/116853349
// Extra logging for general debugging
mDragLayer.dump(prefix, writer);
mStateManager.dump(prefix, writer);
mPopupDataProvider.dump(prefix, writer);
try {
FileLog.flushAll(writer);
@@ -13,6 +13,7 @@ import android.os.UserHandle;
import com.android.launcher3.icons.ComponentWithLabelAndIcon;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
/**
* This class is a thin wrapper around the framework AppWidgetProviderInfo class. This class affords

Some files were not shown because too many files have changed in this diff Show More