Merging from ub-launcher3-rvc-dev @ build 6511067
Bug:150504032 Test: manual, presubmit on the source branch x20/teams/android-launcher/merge/ub-launcher3-rvc-dev_rvc-dev_6511067.html Change-Id: Ifc8c251ef0f03e66de3b1c9fa94bfcb980704769
This commit is contained in:
commit
41dc164d11
|
@ -48,12 +48,16 @@ message ContainerInfo {
|
|||
HotseatContainer hotseat = 2;
|
||||
FolderContainer folder = 3;
|
||||
AllAppsContainer all_apps_container = 4;
|
||||
WidgetsContainer widgets_container = 5;
|
||||
}
|
||||
}
|
||||
|
||||
message AllAppsContainer {
|
||||
}
|
||||
|
||||
message WidgetsContainer {
|
||||
}
|
||||
|
||||
enum Origin {
|
||||
UNKNOWN = 0;
|
||||
DEFAULT_LAYOUT = 1; // icon automatically placed in workspace, folder, hotseat
|
||||
|
@ -95,7 +99,18 @@ message Task {
|
|||
|
||||
// Represents folder in a closed state.
|
||||
message FolderIcon {
|
||||
// Number of items inside folder.
|
||||
optional int32 cardinality = 1;
|
||||
|
||||
// State of the folder label before the event.
|
||||
optional FromState from_label_state = 2;
|
||||
|
||||
// State of the folder label after the event.
|
||||
optional ToState to_label_state = 3;
|
||||
|
||||
// Details about actual folder label.
|
||||
// Populated when folder label is not a PII.
|
||||
optional string label_info = 4;
|
||||
}
|
||||
|
||||
//////////////////////////////////////////////
|
||||
|
@ -120,3 +135,78 @@ message FolderContainer {
|
|||
HotseatContainer hotseat = 5;
|
||||
}
|
||||
}
|
||||
|
||||
// Represents state of EditText field before update.
|
||||
enum FromState {
|
||||
// Default value.
|
||||
// Used when a FromState is not applicable, for example, during folder creation.
|
||||
FROM_STATE_UNSPECIFIED = 0;
|
||||
|
||||
// EditText was empty.
|
||||
// Eg: When a folder label is updated from empty string.
|
||||
FROM_EMPTY = 1;
|
||||
|
||||
// EditText was non-empty and manually entered by the user.
|
||||
// Eg: When a folder label is updated from a user-entered value.
|
||||
FROM_CUSTOM = 2;
|
||||
|
||||
// EditText was non-empty and one of the suggestions.
|
||||
// Eg: When a folder label is updated from a suggested value.
|
||||
FROM_SUGGESTED = 3;
|
||||
}
|
||||
|
||||
// Represents state of EditText field after update.
|
||||
enum ToState {
|
||||
// Default value.
|
||||
// Used when ToState is not applicable, for example, when folder label is updated to a different
|
||||
// value when folder label suggestion feature is disabled.
|
||||
TO_STATE_UNSPECIFIED = 0;
|
||||
|
||||
// User attempted to change the EditText, but was not changed.
|
||||
UNCHANGED = 1;
|
||||
|
||||
// New label matches with primary(aka top) suggestion.
|
||||
TO_SUGGESTION0 = 2;
|
||||
|
||||
// New value matches with second top suggestion even though the top suggestion was non-empty.
|
||||
TO_SUGGESTION1_WITH_VALID_PRIMARY = 3;
|
||||
|
||||
// New value matches with second top suggestion given that top suggestion was empty.
|
||||
TO_SUGGESTION1_WITH_EMPTY_PRIMARY = 4;
|
||||
|
||||
// New value matches with third top suggestion even though the top suggestion was non-empty.
|
||||
TO_SUGGESTION2_WITH_VALID_PRIMARY = 5;
|
||||
|
||||
// New value matches with third top suggestion given that top suggestion was empty.
|
||||
TO_SUGGESTION2_WITH_EMPTY_PRIMARY = 6;
|
||||
|
||||
// New value matches with 4th top suggestion even though the top suggestion was non-empty.
|
||||
TO_SUGGESTION3_WITH_VALID_PRIMARY = 7;
|
||||
|
||||
// New value matches with 4th top suggestion given that top suggestion was empty.
|
||||
TO_SUGGESTION3_WITH_EMPTY_PRIMARY = 8;
|
||||
|
||||
// New value is empty even though the top suggestion was non-empty.
|
||||
TO_EMPTY_WITH_VALID_PRIMARY = 9;
|
||||
|
||||
// New value is empty given that top suggestion was empty.
|
||||
TO_EMPTY_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY = 10;
|
||||
|
||||
// New value is empty given that no suggestions were provided.
|
||||
TO_EMPTY_WITH_EMPTY_SUGGESTIONS = 11;
|
||||
|
||||
// New value is empty given that suggestions feature was disabled.
|
||||
TO_EMPTY_WITH_SUGGESTIONS_DISABLED = 12;
|
||||
|
||||
// New value is non-empty and does not match with any of the suggestions even though the top suggestion was non-empty.
|
||||
TO_CUSTOM_WITH_VALID_PRIMARY = 13;
|
||||
|
||||
// New value is non-empty and not match with any suggestions given that top suggestion was empty.
|
||||
TO_CUSTOM_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY = 14;
|
||||
|
||||
// New value is non-empty and also no suggestions were provided.
|
||||
TO_CUSTOM_WITH_EMPTY_SUGGESTIONS = 15;
|
||||
|
||||
// New value is non-empty and also suggestions feature was disable.
|
||||
TO_CUSTOM_WITH_SUGGESTIONS_DISABLED = 16;
|
||||
}
|
||||
|
|
|
@ -95,12 +95,6 @@
|
|||
android:clearTaskOnLaunch="true"
|
||||
android:exported="false" />
|
||||
|
||||
<activity android:name="com.android.quickstep.LockScreenRecentsActivity"
|
||||
android:theme="@android:style/Theme.NoDisplay"
|
||||
android:showOnLockScreen="true"
|
||||
android:taskAffinity="${packageName}.locktask"
|
||||
android:directBootAware="true" />
|
||||
|
||||
<activity
|
||||
android:name="com.android.quickstep.interaction.GestureSandboxActivity"
|
||||
android:autoRemoveFromRecents="true"
|
||||
|
|
|
@ -26,5 +26,7 @@
|
|||
<string name="main_process_initializer_class" translatable="false">com.android.quickstep.QuickstepProcessInitializer</string>
|
||||
|
||||
<string name="user_event_dispatcher_class" translatable="false">com.android.quickstep.logging.UserEventDispatcherAppPredictionExtension</string>
|
||||
|
||||
<string name="prediction_model_class" translatable="false">com.android.launcher3.hybridhotseat.HotseatPredictionModel</string>
|
||||
</resources>
|
||||
|
||||
|
|
|
@ -211,8 +211,11 @@ public class PredictionUiStateManager implements StateListener<LauncherState>,
|
|||
}
|
||||
|
||||
private void dispatchOnChange(boolean changed) {
|
||||
PredictionState newState = changed ? parseLastState() :
|
||||
(mPendingState == null ? mCurrentState : mPendingState);
|
||||
PredictionState newState = changed
|
||||
? parseLastState()
|
||||
: mPendingState != null && canApplyPredictions(mPendingState)
|
||||
? mPendingState
|
||||
: mCurrentState;
|
||||
if (changed && mAppsView != null && !canApplyPredictions(newState)) {
|
||||
scheduleApplyPredictedApps(newState);
|
||||
} else {
|
||||
|
|
|
@ -121,7 +121,7 @@ public class HotseatEduController {
|
|||
if (!putIntoFolder.isEmpty()) {
|
||||
ItemInfo firstItem = putIntoFolder.get(0);
|
||||
FolderInfo folderInfo = new FolderInfo();
|
||||
folderInfo.title = "";
|
||||
folderInfo.setTitle("");
|
||||
mLauncher.getModelWriter().addItemToDatabase(folderInfo, firstItem.container,
|
||||
firstItem.screenId, firstItem.cellX, firstItem.cellY);
|
||||
folderInfo.contents.addAll(putIntoFolder);
|
||||
|
|
|
@ -25,10 +25,7 @@ import android.app.prediction.AppPredictionManager;
|
|||
import android.app.prediction.AppPredictor;
|
||||
import android.app.prediction.AppTarget;
|
||||
import android.app.prediction.AppTargetEvent;
|
||||
import android.app.prediction.AppTargetId;
|
||||
import android.content.ComponentName;
|
||||
import android.os.Bundle;
|
||||
import android.os.Process;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
@ -36,8 +33,6 @@ import android.view.ViewGroup;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.BubbleTextView;
|
||||
import com.android.launcher3.CellLayout;
|
||||
import com.android.launcher3.DragSource;
|
||||
import com.android.launcher3.DropTarget;
|
||||
import com.android.launcher3.Hotseat;
|
||||
|
@ -48,7 +43,6 @@ 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.allapps.AllAppsStore;
|
||||
import com.android.launcher3.anim.AnimationSuccessListener;
|
||||
import com.android.launcher3.appprediction.ComponentKeyMapper;
|
||||
|
@ -57,12 +51,10 @@ import com.android.launcher3.dragndrop.DragController;
|
|||
import com.android.launcher3.dragndrop.DragOptions;
|
||||
import com.android.launcher3.icons.IconCache;
|
||||
import com.android.launcher3.logging.FileLog;
|
||||
import com.android.launcher3.model.PredictionModel;
|
||||
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;
|
||||
|
@ -77,7 +69,6 @@ import java.lang.ref.WeakReference;
|
|||
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;
|
||||
|
||||
|
@ -93,17 +84,6 @@ public class HotseatPredictionController implements DragController.DragListener,
|
|||
private static final String TAG = "PredictiveHotseat";
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
//TODO: replace this with AppTargetEvent.ACTION_UNPIN (b/144119543)
|
||||
private static final int APPTARGET_ACTION_UNPIN = 4;
|
||||
|
||||
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;
|
||||
|
@ -116,13 +96,14 @@ public class HotseatPredictionController implements DragController.DragListener,
|
|||
|
||||
private DynamicItemCache mDynamicItemCache;
|
||||
|
||||
private final PredictionModel mPredictionModel;
|
||||
private final HotseatPredictionModel mPredictionModel;
|
||||
private AppPredictor mAppPredictor;
|
||||
private AllAppsStore mAllAppsStore;
|
||||
private AnimatorSet mIconRemoveAnimators;
|
||||
private boolean mUIUpdatePaused = false;
|
||||
private boolean mRequiresCacheUpdate = true;
|
||||
private boolean mIsCacheEmpty;
|
||||
private boolean mIsDestroyed = false;
|
||||
|
||||
private HotseatEduController mHotseatEduController;
|
||||
|
||||
|
@ -141,13 +122,13 @@ public class HotseatPredictionController implements DragController.DragListener,
|
|||
mLauncher = launcher;
|
||||
mHotseat = launcher.getHotseat();
|
||||
mAllAppsStore = mLauncher.getAppsView().getAppsStore();
|
||||
mPredictionModel = LauncherAppState.INSTANCE.get(launcher).getPredictionModel();
|
||||
LauncherAppState appState = LauncherAppState.getInstance(launcher);
|
||||
mPredictionModel = (HotseatPredictionModel) appState.getPredictionModel();
|
||||
mAllAppsStore.addUpdateListener(this);
|
||||
mDynamicItemCache = new DynamicItemCache(mLauncher, this::fillGapsWithPrediction);
|
||||
mHotSeatItemsCount = mLauncher.getDeviceProfile().inv.numHotseatIcons;
|
||||
launcher.getDeviceProfile().inv.addOnChangeListener(this);
|
||||
mHotseat.addOnAttachStateChangeListener(this);
|
||||
mIsCacheEmpty = mPredictionModel.getPredictionComponentKeys().isEmpty();
|
||||
if (mHotseat.isAttachedToWindow()) {
|
||||
onViewAttachedToWindow(mHotseat);
|
||||
}
|
||||
|
@ -260,6 +241,7 @@ public class HotseatPredictionController implements DragController.DragListener,
|
|||
* Unregisters callbacks and frees resources
|
||||
*/
|
||||
public void destroy() {
|
||||
mIsDestroyed = true;
|
||||
mAllAppsStore.removeUpdateListener(this);
|
||||
mLauncher.getDeviceProfile().inv.removeOnChangeListener(this);
|
||||
mHotseat.removeOnAttachStateChangeListener(this);
|
||||
|
@ -292,100 +274,54 @@ public class HotseatPredictionController implements DragController.DragListener,
|
|||
}
|
||||
if (mAppPredictor != null) {
|
||||
mAppPredictor.destroy();
|
||||
mAppPredictor = null;
|
||||
}
|
||||
mAppPredictor = apm.createAppPredictionSession(
|
||||
new AppPredictionContext.Builder(mLauncher)
|
||||
.setUiSurface(PREDICTION_CLIENT)
|
||||
.setPredictedTargetCount(mHotSeatItemsCount)
|
||||
.setExtras(getAppPredictionContextExtra())
|
||||
.build());
|
||||
WeakReference<HotseatPredictionController> controllerRef = new WeakReference<>(this);
|
||||
mAppPredictor.registerPredictionUpdates(mLauncher.getApplicationContext().getMainExecutor(),
|
||||
list -> {
|
||||
if (controllerRef.get() != null) {
|
||||
controllerRef.get().setPredictedApps(list);
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
mPredictionModel.createBundle(bundle -> {
|
||||
if (mIsDestroyed) return;
|
||||
mAppPredictor = apm.createAppPredictionSession(
|
||||
new AppPredictionContext.Builder(mLauncher)
|
||||
.setUiSurface(PREDICTION_CLIENT)
|
||||
.setPredictedTargetCount(mHotSeatItemsCount)
|
||||
.setExtras(bundle)
|
||||
.build());
|
||||
mAppPredictor.registerPredictionUpdates(
|
||||
mLauncher.getApplicationContext().getMainExecutor(),
|
||||
list -> {
|
||||
if (controllerRef.get() != null) {
|
||||
controllerRef.get().setPredictedApps(list);
|
||||
}
|
||||
});
|
||||
mAppPredictor.requestPredictionUpdate();
|
||||
});
|
||||
setPauseUIUpdate(false);
|
||||
if (!isEduSeen()) {
|
||||
mHotseatEduController = new HotseatEduController(mLauncher, this::createPredictor);
|
||||
}
|
||||
mAppPredictor.requestPredictionUpdate();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create WorkspaceItemInfo objects and binds PredictedAppIcon views for cached predicted items.
|
||||
*/
|
||||
public void showCachedItems(List<AppInfo> apps, IntArray ranks) {
|
||||
public void showCachedItems(List<AppInfo> apps, IntArray ranks) {
|
||||
mIsCacheEmpty = apps.isEmpty();
|
||||
int count = Math.min(ranks.size(), apps.size());
|
||||
List<WorkspaceItemInfo> items = new ArrayList<>(count);
|
||||
mComponentKeyMappers.clear();
|
||||
for (int i = 0; i < count; i++) {
|
||||
WorkspaceItemInfo item = new WorkspaceItemInfo(apps.get(i));
|
||||
ComponentKey componentKey = new ComponentKey(item.getTargetComponent(), item.user);
|
||||
preparePredictionInfo(item, ranks.get(i));
|
||||
items.add(item);
|
||||
}
|
||||
mComponentKeyMappers.clear();
|
||||
for (ComponentKey key : mPredictionModel.getPredictionComponentKeys()) {
|
||||
mComponentKeyMappers.add(new ComponentKeyMapper(key, mDynamicItemCache));
|
||||
|
||||
mComponentKeyMappers.add(new ComponentKeyMapper(componentKey, mDynamicItemCache));
|
||||
}
|
||||
updateDependencies();
|
||||
bindItems(items, false, null);
|
||||
}
|
||||
|
||||
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++) {
|
||||
View child = viewGroup.getChildAt(i);
|
||||
if (isPinnedIcon(child)) {
|
||||
WorkspaceItemInfo itemInfo = (WorkspaceItemInfo) child.getTag();
|
||||
pinnedApps.add(getAppTargetFromItemInfo(itemInfo));
|
||||
}
|
||||
}
|
||||
return pinnedApps;
|
||||
}
|
||||
|
||||
private void setPredictedApps(List<AppTarget> appTargets) {
|
||||
mComponentKeyMappers.clear();
|
||||
StringBuilder predictionLog = new StringBuilder("predictedApps: [\n");
|
||||
|
@ -443,8 +379,11 @@ public class HotseatPredictionController implements DragController.DragListener,
|
|||
workspaceItemInfo.cellX, workspaceItemInfo.cellY);
|
||||
ObjectAnimator.ofFloat(icon, SCALE_PROPERTY, 1, 0.8f, 1).start();
|
||||
icon.pin(workspaceItemInfo);
|
||||
AppTarget appTarget = getAppTargetFromItemInfo(workspaceItemInfo);
|
||||
notifyItemAction(appTarget, APP_LOCATION_HOTSEAT, AppTargetEvent.ACTION_PIN);
|
||||
AppTarget appTarget = mPredictionModel.getAppTargetFromInfo(workspaceItemInfo);
|
||||
if (appTarget != null) {
|
||||
notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(appTarget,
|
||||
AppTargetEvent.ACTION_PIN, workspaceItemInfo));
|
||||
}
|
||||
mRequiresCacheUpdate = true;
|
||||
}
|
||||
|
||||
|
@ -524,10 +463,9 @@ public class HotseatPredictionController implements DragController.DragListener,
|
|||
mIconRemoveAnimators.start();
|
||||
}
|
||||
|
||||
private void notifyItemAction(AppTarget target, String location, int action) {
|
||||
private void notifyItemAction(AppTargetEvent event) {
|
||||
if (mAppPredictor != null) {
|
||||
mAppPredictor.notifyAppTargetEvent(new AppTargetEvent.Builder(target,
|
||||
action).setLaunchLocation(location).build());
|
||||
mAppPredictor.notifyAppTargetEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -545,36 +483,35 @@ public class HotseatPredictionController implements DragController.DragListener,
|
|||
/**
|
||||
* Unpins pinned app when it's converted into a folder
|
||||
*/
|
||||
public void folderCreatedFromWorkspaceItem(ItemInfo info, FolderInfo folderInfo) {
|
||||
if (info.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
|
||||
return;
|
||||
public void folderCreatedFromWorkspaceItem(ItemInfo itemInfo, FolderInfo folderInfo) {
|
||||
AppTarget folderTarget = mPredictionModel.getAppTargetFromInfo(folderInfo);
|
||||
AppTarget itemTarget = mPredictionModel.getAppTargetFromInfo(itemInfo);
|
||||
if (folderTarget != null && HotseatPredictionModel.isTrackedForPrediction(folderInfo)) {
|
||||
notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(folderTarget,
|
||||
AppTargetEvent.ACTION_PIN, folderInfo));
|
||||
}
|
||||
AppTarget target = getAppTargetFromItemInfo(info);
|
||||
ViewGroup hotseatVG = mHotseat.getShortcutsAndWidgets();
|
||||
ViewGroup firstScreenVG = mLauncher.getWorkspace().getScreenWithId(
|
||||
Workspace.FIRST_SCREEN_ID).getShortcutsAndWidgets();
|
||||
|
||||
if (isInHotseat(folderInfo) && !getPinnedAppTargetsInViewGroup(hotseatVG).contains(
|
||||
target)) {
|
||||
notifyItemAction(target, APP_LOCATION_HOTSEAT, APPTARGET_ACTION_UNPIN);
|
||||
} else if (isInFirstPage(folderInfo) && !getPinnedAppTargetsInViewGroup(
|
||||
firstScreenVG).contains(target)) {
|
||||
notifyItemAction(target, APP_LOCATION_WORKSPACE, APPTARGET_ACTION_UNPIN);
|
||||
// using folder info with isTrackedForPrediction as itemInfo.container is already changed
|
||||
// to folder by this point
|
||||
if (itemTarget != null && HotseatPredictionModel.isTrackedForPrediction(folderInfo)) {
|
||||
notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(itemTarget,
|
||||
AppTargetEvent.ACTION_UNPIN, folderInfo
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Pins workspace item created when all folder items are removed but one
|
||||
*/
|
||||
public void folderConvertedToWorkspaceItem(ItemInfo info, FolderInfo folderInfo) {
|
||||
if (info.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION) {
|
||||
return;
|
||||
public void folderConvertedToWorkspaceItem(ItemInfo itemInfo, FolderInfo folderInfo) {
|
||||
AppTarget folderTarget = mPredictionModel.getAppTargetFromInfo(folderInfo);
|
||||
AppTarget itemTarget = mPredictionModel.getAppTargetFromInfo(itemInfo);
|
||||
if (folderTarget != null && HotseatPredictionModel.isTrackedForPrediction(folderInfo)) {
|
||||
notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(folderTarget,
|
||||
AppTargetEvent.ACTION_UNPIN, folderInfo));
|
||||
}
|
||||
AppTarget target = getAppTargetFromItemInfo(info);
|
||||
if (isInHotseat(info)) {
|
||||
notifyItemAction(target, APP_LOCATION_HOTSEAT, AppTargetEvent.ACTION_PIN);
|
||||
} else if (isInFirstPage(info)) {
|
||||
notifyItemAction(target, APP_LOCATION_WORKSPACE, AppTargetEvent.ACTION_PIN);
|
||||
if (itemTarget != null && HotseatPredictionModel.isTrackedForPrediction(itemInfo)) {
|
||||
notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(itemTarget,
|
||||
AppTargetEvent.ACTION_PIN, itemInfo));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -585,29 +522,18 @@ public class HotseatPredictionController implements DragController.DragListener,
|
|||
}
|
||||
|
||||
ItemInfo dragInfo = mDragObject.dragInfo;
|
||||
ViewGroup hotseatVG = mHotseat.getShortcutsAndWidgets();
|
||||
ViewGroup firstScreenVG = mLauncher.getWorkspace().getScreenWithId(
|
||||
Workspace.FIRST_SCREEN_ID).getShortcutsAndWidgets();
|
||||
|
||||
if (dragInfo instanceof WorkspaceItemInfo
|
||||
&& dragInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
|
||||
&& dragInfo.getTargetComponent() != null) {
|
||||
AppTarget appTarget = getAppTargetFromItemInfo(dragInfo);
|
||||
if (!isInHotseat(dragInfo) && isInHotseat(mDragObject.originalDragInfo)) {
|
||||
if (!getPinnedAppTargetsInViewGroup(hotseatVG).contains(appTarget)) {
|
||||
notifyItemAction(appTarget, APP_LOCATION_HOTSEAT, APPTARGET_ACTION_UNPIN);
|
||||
}
|
||||
if (mDragObject.isMoved()) {
|
||||
AppTarget appTarget = mPredictionModel.getAppTargetFromInfo(dragInfo);
|
||||
//always send pin event first to prevent AiAi from predicting an item moved within
|
||||
// the same page
|
||||
if (appTarget != null && HotseatPredictionModel.isTrackedForPrediction(dragInfo)) {
|
||||
notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(appTarget,
|
||||
AppTargetEvent.ACTION_PIN, dragInfo));
|
||||
}
|
||||
if (!isInFirstPage(dragInfo) && isInFirstPage(mDragObject.originalDragInfo)) {
|
||||
if (!getPinnedAppTargetsInViewGroup(firstScreenVG).contains(appTarget)) {
|
||||
notifyItemAction(appTarget, APP_LOCATION_WORKSPACE, APPTARGET_ACTION_UNPIN);
|
||||
}
|
||||
}
|
||||
if (isInHotseat(dragInfo) && !isInHotseat(mDragObject.originalDragInfo)) {
|
||||
notifyItemAction(appTarget, APP_LOCATION_HOTSEAT, AppTargetEvent.ACTION_PIN);
|
||||
}
|
||||
if (isInFirstPage(dragInfo) && !isInFirstPage(mDragObject.originalDragInfo)) {
|
||||
notifyItemAction(appTarget, APP_LOCATION_WORKSPACE, AppTargetEvent.ACTION_PIN);
|
||||
if (appTarget != null && HotseatPredictionModel.isTrackedForPrediction(
|
||||
mDragObject.originalDragInfo)) {
|
||||
notifyItemAction(mPredictionModel.wrapAppTargetWithLocation(appTarget,
|
||||
AppTargetEvent.ACTION_UNPIN, mDragObject.originalDragInfo));
|
||||
}
|
||||
}
|
||||
mDragObject = null;
|
||||
|
@ -615,6 +541,7 @@ public class HotseatPredictionController implements DragController.DragListener,
|
|||
mRequiresCacheUpdate = true;
|
||||
}
|
||||
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public SystemShortcut<QuickstepLauncher> getShortcut(QuickstepLauncher activity,
|
||||
|
@ -711,77 +638,4 @@ public class HotseatPredictionController implements DragController.DragListener,
|
|||
&& ((WorkspaceItemInfo) view.getTag()).container
|
||||
== LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
|
||||
}
|
||||
|
||||
private static boolean isPinnedIcon(View view) {
|
||||
if (!(view instanceof BubbleTextView && view.getTag() instanceof WorkspaceItemInfo)) {
|
||||
return false;
|
||||
}
|
||||
ItemInfo info = (ItemInfo) view.getTag();
|
||||
return info.container != LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION && (
|
||||
info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION);
|
||||
}
|
||||
|
||||
private static boolean isInHotseat(ItemInfo itemInfo) {
|
||||
return itemInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT;
|
||||
}
|
||||
|
||||
private static boolean isInFirstPage(ItemInfo itemInfo) {
|
||||
return itemInfo.container == LauncherSettings.Favorites.CONTAINER_DESKTOP
|
||||
&& itemInfo.screenId == Workspace.FIRST_SCREEN_ID;
|
||||
}
|
||||
|
||||
private static AppTarget getAppTargetFromItemInfo(ItemInfo info) {
|
||||
if (info.getTargetComponent() == null) return null;
|
||||
ComponentName cn = info.getTargetComponent();
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,136 @@
|
|||
/*
|
||||
* 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.hybridhotseat;
|
||||
|
||||
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
||||
|
||||
import android.app.prediction.AppTarget;
|
||||
import android.app.prediction.AppTargetEvent;
|
||||
import android.app.prediction.AppTargetId;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.LauncherSettings;
|
||||
import com.android.launcher3.Workspace;
|
||||
import com.android.launcher3.model.AllAppsList;
|
||||
import com.android.launcher3.model.BaseModelUpdateTask;
|
||||
import com.android.launcher3.model.BgDataModel;
|
||||
import com.android.launcher3.model.PredictionModel;
|
||||
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.shortcuts.ShortcutKey;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Model helper for app predictions in workspace
|
||||
*/
|
||||
public class HotseatPredictionModel extends PredictionModel {
|
||||
private static final String APP_LOCATION_HOTSEAT = "hotseat";
|
||||
private static final String APP_LOCATION_WORKSPACE = "workspace";
|
||||
|
||||
private static final String BUNDLE_KEY_PIN_EVENTS = "pin_events";
|
||||
private static final String BUNDLE_KEY_CURRENT_ITEMS = "current_items";
|
||||
|
||||
|
||||
public HotseatPredictionModel(Context context) { }
|
||||
|
||||
/**
|
||||
* Creates and returns bundle using workspace items and cached items
|
||||
*/
|
||||
public void createBundle(Consumer<Bundle> cb) {
|
||||
LauncherAppState appState = LauncherAppState.getInstance(mContext);
|
||||
appState.getModel().enqueueModelUpdateTask(new BaseModelUpdateTask() {
|
||||
@Override
|
||||
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
|
||||
Bundle bundle = new Bundle();
|
||||
ArrayList<AppTargetEvent> events = new ArrayList<>();
|
||||
ArrayList<ItemInfo> workspaceItems = new ArrayList<>(dataModel.workspaceItems);
|
||||
workspaceItems.addAll(dataModel.appWidgets);
|
||||
for (ItemInfo item : workspaceItems) {
|
||||
AppTarget target = getAppTargetFromInfo(item);
|
||||
if (target != null && !isTrackedForPrediction(item)) continue;
|
||||
events.add(wrapAppTargetWithLocation(target, AppTargetEvent.ACTION_PIN, item));
|
||||
}
|
||||
ArrayList<AppTarget> currentTargets = new ArrayList<>();
|
||||
for (ItemInfo itemInfo : dataModel.cachedPredictedItems) {
|
||||
AppTarget target = getAppTargetFromInfo(itemInfo);
|
||||
if (target != null) currentTargets.add(target);
|
||||
}
|
||||
bundle.putParcelableArrayList(BUNDLE_KEY_PIN_EVENTS, events);
|
||||
bundle.putParcelableArrayList(BUNDLE_KEY_CURRENT_ITEMS, currentTargets);
|
||||
MAIN_EXECUTOR.execute(() -> cb.accept(bundle));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns for {@link AppTarget} object given an {@link ItemInfo}. Returns null
|
||||
* if item is not supported prediction
|
||||
*/
|
||||
public 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),
|
||||
mContext.getPackageName(), info.user).build();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Creates and returns {@link AppTargetEvent} from an {@link AppTarget}, action, and item
|
||||
* location using {@link ItemInfo}
|
||||
*/
|
||||
public AppTargetEvent wrapAppTargetWithLocation(AppTarget target, int action, ItemInfo info) {
|
||||
String location = String.format(Locale.ENGLISH, "%s/%d/[%d,%d]/[%d,%d]",
|
||||
info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT
|
||||
? APP_LOCATION_HOTSEAT : APP_LOCATION_WORKSPACE,
|
||||
info.screenId, info.cellX, info.cellY, info.spanX, info.spanY);
|
||||
return new AppTargetEvent.Builder(target, action).setLaunchLocation(location).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to determine if {@link ItemInfo} should be tracked and reported to predictors
|
||||
*/
|
||||
public static boolean isTrackedForPrediction(ItemInfo info) {
|
||||
return info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT || (
|
||||
info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP
|
||||
&& info.screenId == Workspace.FIRST_SCREEN_ID);
|
||||
}
|
||||
}
|
|
@ -101,7 +101,7 @@ public class QuickstepLauncher extends BaseQuickstepLauncher {
|
|||
@Override
|
||||
public void onConfigurationChanged(Configuration newConfig) {
|
||||
super.onConfigurationChanged(newConfig);
|
||||
onStateOrResumeChanged();
|
||||
onStateOrResumeChanging(false /* inTransition */);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -116,11 +116,9 @@ public class QuickstepLauncher extends BaseQuickstepLauncher {
|
|||
@Override
|
||||
protected void onActivityFlagsChanged(int changeBits) {
|
||||
super.onActivityFlagsChanged(changeBits);
|
||||
|
||||
if ((changeBits & (ACTIVITY_STATE_DEFERRED_RESUMED | ACTIVITY_STATE_STARTED
|
||||
| ACTIVITY_STATE_USER_ACTIVE | ACTIVITY_STATE_TRANSITION_ACTIVE)) != 0
|
||||
&& (getActivityFlags() & ACTIVITY_STATE_TRANSITION_ACTIVE) == 0) {
|
||||
onStateOrResumeChanged();
|
||||
| ACTIVITY_STATE_USER_ACTIVE | ACTIVITY_STATE_TRANSITION_ACTIVE)) != 0) {
|
||||
onStateOrResumeChanging((getActivityFlags() & ACTIVITY_STATE_TRANSITION_ACTIVE) == 0);
|
||||
}
|
||||
|
||||
if (mHotseatPredictionController != null && ((changeBits & ACTIVITY_STATE_STARTED) != 0
|
||||
|
@ -165,14 +163,16 @@ public class QuickstepLauncher extends BaseQuickstepLauncher {
|
|||
/**
|
||||
* Recents logic that triggers when launcher state changes or launcher activity stops/resumes.
|
||||
*/
|
||||
private void onStateOrResumeChanged() {
|
||||
private void onStateOrResumeChanging(boolean inTransition) {
|
||||
LauncherState state = getStateManager().getState();
|
||||
DeviceProfile profile = getDeviceProfile();
|
||||
boolean visible = (state == NORMAL || state == OVERVIEW) && isUserActive()
|
||||
boolean willUserBeActive = (getActivityFlags() & ACTIVITY_STATE_USER_WILL_BE_ACTIVE) != 0;
|
||||
boolean visible = (state == NORMAL || state == OVERVIEW)
|
||||
&& (willUserBeActive || isUserActive())
|
||||
&& !profile.isVerticalBarLayout();
|
||||
UiThreadHelper.runAsyncCommand(this, SET_SHELF_HEIGHT, visible ? 1 : 0,
|
||||
profile.hotseatBarSizePx);
|
||||
if (state == NORMAL) {
|
||||
if (state == NORMAL && !inTransition) {
|
||||
((RecentsView) getOverviewPanel()).setSwipeDownShouldLaunchApp(false);
|
||||
}
|
||||
}
|
||||
|
@ -218,7 +218,9 @@ public class QuickstepLauncher extends BaseQuickstepLauncher {
|
|||
break;
|
||||
}
|
||||
case OVERVIEW_STATE_ORDINAL: {
|
||||
DiscoveryBounce.showForOverviewIfNeeded(this);
|
||||
RecentsView recentsView = getOverviewPanel();
|
||||
DiscoveryBounce.showForOverviewIfNeeded(this,
|
||||
recentsView.getPagedOrientationHandler());
|
||||
RecentsView rv = getOverviewPanel();
|
||||
sendCustomAccessibilityEvent(
|
||||
rv.getPageAt(rv.getCurrentPage()), TYPE_VIEW_FOCUSED, null);
|
||||
|
|
|
@ -71,7 +71,7 @@ public final class RecentsViewStateController extends
|
|||
builder.addOnFrameCallback(mRecentsView::loadVisibleTaskData);
|
||||
mRecentsView.updateEmptyMessage();
|
||||
} else {
|
||||
builder.getAnim().addListener(
|
||||
builder.addListener(
|
||||
AnimationSuccessListener.forRunnable(mRecentsView::resetTaskVisuals));
|
||||
}
|
||||
|
||||
|
|
|
@ -16,7 +16,6 @@
|
|||
package com.android.launcher3.uioverrides.states;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Rect;
|
||||
|
||||
import com.android.launcher3.BaseDraggingActivity;
|
||||
|
@ -58,8 +57,6 @@ public class OverviewModalTaskState extends OverviewState {
|
|||
}
|
||||
|
||||
public static float[] getOverviewScaleAndOffsetForModalState(BaseDraggingActivity activity) {
|
||||
Resources res = activity.getResources();
|
||||
|
||||
Rect out = new Rect();
|
||||
activity.<RecentsView>getOverviewPanel().getTaskSize(out);
|
||||
int taskHeight = out.height();
|
||||
|
|
|
@ -19,6 +19,8 @@ import static com.android.launcher3.anim.Interpolators.DEACCEL_2;
|
|||
import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
|
||||
import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
|
||||
import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
|
||||
import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
|
||||
import static com.android.quickstep.SysUINavigationMode.getMode;
|
||||
import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
|
||||
|
||||
import android.content.Context;
|
||||
|
@ -127,7 +129,11 @@ public class OverviewState extends LauncherState {
|
|||
|
||||
@Override
|
||||
public int getVisibleElements(Launcher launcher) {
|
||||
if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(launcher)) {
|
||||
RecentsView recentsView = launcher.getOverviewPanel();
|
||||
boolean hideShelfTwoButtonLandscape = getMode(launcher) == TWO_BUTTONS &&
|
||||
!recentsView.getPagedOrientationHandler().isLayoutNaturalToLauncher();
|
||||
if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(launcher) ||
|
||||
hideShelfTwoButtonLandscape) {
|
||||
return OVERVIEW_BUTTONS;
|
||||
} else if (launcher.getDeviceProfile().isVerticalBarLayout()) {
|
||||
return VERTICAL_SWIPE_INDICATOR | OVERVIEW_BUTTONS;
|
||||
|
|
|
@ -18,49 +18,47 @@ package com.android.quickstep;
|
|||
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
|
||||
import static com.android.launcher3.LauncherState.OVERVIEW;
|
||||
import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
|
||||
import static com.android.launcher3.anim.Interpolators.clampToProgress;
|
||||
import static com.android.launcher3.statehandlers.DepthController.DEPTH;
|
||||
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
|
||||
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.graphics.Rect;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import com.android.launcher3.AbstractFloatingView;
|
||||
import com.android.launcher3.BaseDraggingActivity;
|
||||
import com.android.launcher3.anim.AnimationSuccessListener;
|
||||
import com.android.launcher3.anim.PendingAnimation;
|
||||
import com.android.launcher3.statehandlers.DepthController;
|
||||
import com.android.quickstep.util.AppWindowAnimationHelper;
|
||||
import com.android.launcher3.statemanager.StatefulActivity;
|
||||
import com.android.quickstep.util.RemoteAnimationProvider;
|
||||
import com.android.quickstep.util.TaskViewSimulator;
|
||||
import com.android.quickstep.util.TransformParams;
|
||||
import com.android.quickstep.views.RecentsView;
|
||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
|
||||
import com.android.systemui.shared.system.TransactionCompat;
|
||||
|
||||
/**
|
||||
* Provider for the atomic (for 3-button mode) remote window animation from the app to the overview.
|
||||
*
|
||||
* @param <T> activity that contains the overview
|
||||
*/
|
||||
final class AppToOverviewAnimationProvider<T extends BaseDraggingActivity> extends
|
||||
final class AppToOverviewAnimationProvider<T extends StatefulActivity<?>> extends
|
||||
RemoteAnimationProvider {
|
||||
|
||||
private static final long RECENTS_LAUNCH_DURATION = 250;
|
||||
private static final String TAG = "AppToOverviewAnimationProvider";
|
||||
|
||||
private final BaseActivityInterface<T> mActivityInterface;
|
||||
private final BaseActivityInterface<?, T> mActivityInterface;
|
||||
// The id of the currently running task that is transitioning to overview.
|
||||
private final int mTargetTaskId;
|
||||
|
||||
private T mActivity;
|
||||
private RecentsView mRecentsView;
|
||||
|
||||
AppToOverviewAnimationProvider(BaseActivityInterface<T> activityInterface, int targetTaskId) {
|
||||
AppToOverviewAnimationProvider(
|
||||
BaseActivityInterface<?, T> activityInterface, int targetTaskId) {
|
||||
mActivityInterface = activityInterface;
|
||||
mTargetTaskId = targetTaskId;
|
||||
}
|
||||
|
@ -96,32 +94,25 @@ final class AppToOverviewAnimationProvider<T extends BaseDraggingActivity> exten
|
|||
@Override
|
||||
public AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] appTargets,
|
||||
RemoteAnimationTargetCompat[] wallpaperTargets) {
|
||||
if (mRecentsView != null) {
|
||||
mRecentsView.setRunningTaskIconScaledDown(true);
|
||||
PendingAnimation pa = new PendingAnimation(RECENTS_LAUNCH_DURATION);
|
||||
if (mActivity == null) {
|
||||
Log.e(TAG, "Animation created, before activity");
|
||||
return pa.buildAnim();
|
||||
}
|
||||
|
||||
AnimatorSet anim = new AnimatorSet();
|
||||
anim.addListener(new AnimationSuccessListener() {
|
||||
mRecentsView.setRunningTaskIconScaledDown(true);
|
||||
pa.addListener(new AnimationSuccessListener() {
|
||||
@Override
|
||||
public void onAnimationSuccess(Animator animator) {
|
||||
mActivityInterface.onSwipeUpToRecentsComplete();
|
||||
if (mRecentsView != null) {
|
||||
mRecentsView.animateUpRunningTaskIconScale();
|
||||
}
|
||||
mRecentsView.animateUpRunningTaskIconScale();
|
||||
}
|
||||
});
|
||||
if (mActivity == null) {
|
||||
Log.e(TAG, "Animation created, before activity");
|
||||
anim.play(ValueAnimator.ofInt(0, 1).setDuration(RECENTS_LAUNCH_DURATION));
|
||||
return anim;
|
||||
}
|
||||
|
||||
DepthController depthController = mActivityInterface.getDepthController();
|
||||
if (depthController != null) {
|
||||
anim.play(ObjectAnimator.ofFloat(depthController, DEPTH,
|
||||
BACKGROUND_APP.getDepth(mActivity),
|
||||
OVERVIEW.getDepth(mActivity))
|
||||
.setDuration(RECENTS_LAUNCH_DURATION));
|
||||
pa.addFloat(depthController, DEPTH, BACKGROUND_APP.getDepth(mActivity),
|
||||
OVERVIEW.getDepth(mActivity), TOUCH_RESPONSE_INTERPOLATOR);
|
||||
}
|
||||
|
||||
RemoteAnimationTargets targets = new RemoteAnimationTargets(appTargets,
|
||||
|
@ -131,53 +122,39 @@ final class AppToOverviewAnimationProvider<T extends BaseDraggingActivity> exten
|
|||
RemoteAnimationTargetCompat runningTaskTarget = targets.findTask(mTargetTaskId);
|
||||
if (runningTaskTarget == null) {
|
||||
Log.e(TAG, "No closing app");
|
||||
anim.play(ValueAnimator.ofInt(0, 1).setDuration(RECENTS_LAUNCH_DURATION));
|
||||
return anim;
|
||||
return pa.buildAnim();
|
||||
}
|
||||
|
||||
final AppWindowAnimationHelper clipHelper = new AppWindowAnimationHelper(
|
||||
mRecentsView.getPagedViewOrientedState(), mActivity);
|
||||
|
||||
// At this point, the activity is already started and laid-out. Get the home-bounds
|
||||
// relative to the screen using the rootView of the activity.
|
||||
int loc[] = new int[2];
|
||||
View rootView = mActivity.getRootView();
|
||||
rootView.getLocationOnScreen(loc);
|
||||
Rect homeBounds = new Rect(loc[0], loc[1],
|
||||
loc[0] + rootView.getWidth(), loc[1] + rootView.getHeight());
|
||||
clipHelper.updateSource(homeBounds, runningTaskTarget);
|
||||
|
||||
Rect targetRect = new Rect();
|
||||
mActivityInterface.getSwipeUpDestinationAndLength(mActivity.getDeviceProfile(), mActivity,
|
||||
targetRect);
|
||||
clipHelper.updateTargetRect(targetRect);
|
||||
clipHelper.prepareAnimation(mActivity.getDeviceProfile());
|
||||
TaskViewSimulator tsv = new TaskViewSimulator(mActivity, mRecentsView.getSizeStrategy());
|
||||
tsv.setDp(mActivity.getDeviceProfile());
|
||||
tsv.setPreview(runningTaskTarget);
|
||||
tsv.setLayoutRotation(mRecentsView.getPagedViewOrientedState().getTouchRotation(),
|
||||
mRecentsView.getPagedViewOrientedState().getDisplayRotation());
|
||||
|
||||
TransformParams params = new TransformParams()
|
||||
.setSyncTransactionApplier(new SyncRtSurfaceTransactionApplierCompat(rootView));
|
||||
ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, 1);
|
||||
valueAnimator.setDuration(RECENTS_LAUNCH_DURATION);
|
||||
valueAnimator.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
|
||||
valueAnimator.addUpdateListener((v) -> {
|
||||
params.setProgress((float) v.getAnimatedValue()).setTargetSet(targets);
|
||||
clipHelper.applyTransform(params);
|
||||
});
|
||||
.setTargetSet(targets)
|
||||
.setSyncTransactionApplier(
|
||||
new SyncRtSurfaceTransactionApplierCompat(mActivity.getRootView()));
|
||||
|
||||
AnimatedFloat recentsAlpha = new AnimatedFloat(() -> { });
|
||||
params.setBaseAlphaCallback((t, a) -> recentsAlpha.value);
|
||||
|
||||
Interpolator taskInterpolator;
|
||||
if (targets.isAnimatingHome()) {
|
||||
// If we are animating home, fade in the opening targets
|
||||
RemoteAnimationTargets openingSet = new RemoteAnimationTargets(appTargets,
|
||||
wallpaperTargets, MODE_OPENING);
|
||||
|
||||
TransactionCompat transaction = new TransactionCompat();
|
||||
valueAnimator.addUpdateListener((v) -> {
|
||||
for (RemoteAnimationTargetCompat app : openingSet.apps) {
|
||||
transaction.setAlpha(app.leash, (float) v.getAnimatedValue());
|
||||
}
|
||||
transaction.apply();
|
||||
});
|
||||
taskInterpolator = TOUCH_RESPONSE_INTERPOLATOR;
|
||||
pa.addFloat(recentsAlpha, AnimatedFloat.VALUE, 0, 1, TOUCH_RESPONSE_INTERPOLATOR);
|
||||
} else {
|
||||
// When animation from app to recents, the recents layer is drawn on top of the app. To
|
||||
// prevent the overlap, we animate the task first and then quickly fade in the recents.
|
||||
taskInterpolator = clampToProgress(TOUCH_RESPONSE_INTERPOLATOR, 0, 0.8f);
|
||||
pa.addFloat(recentsAlpha, AnimatedFloat.VALUE, 0, 1,
|
||||
clampToProgress(TOUCH_RESPONSE_INTERPOLATOR, 0.8f, 1));
|
||||
}
|
||||
anim.play(valueAnimator);
|
||||
return anim;
|
||||
|
||||
pa.addFloat(params, TransformParams.PROGRESS, 0, 1, taskInterpolator);
|
||||
tsv.addAppToOverviewAnim(pa, taskInterpolator);
|
||||
pa.addOnFrameCallback(() -> tsv.apply(params));
|
||||
return pa.buildAnim();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -15,8 +15,6 @@
|
|||
*/
|
||||
package com.android.quickstep;
|
||||
|
||||
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
|
||||
import static com.android.launcher3.LauncherState.OVERVIEW;
|
||||
import static com.android.launcher3.anim.Interpolators.ACCEL_1_5;
|
||||
import static com.android.launcher3.anim.Interpolators.DEACCEL;
|
||||
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
|
||||
|
@ -25,8 +23,6 @@ import static com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC;
|
|||
import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
|
@ -36,6 +32,7 @@ import android.graphics.PointF;
|
|||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.os.Build;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
|
@ -44,23 +41,25 @@ import androidx.annotation.NonNull;
|
|||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.UiThread;
|
||||
|
||||
import com.android.launcher3.BaseDraggingActivity;
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.InvariantDeviceProfile;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.anim.AnimationSuccessListener;
|
||||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
import com.android.launcher3.anim.PendingAnimation;
|
||||
import com.android.launcher3.statemanager.StatefulActivity;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
import com.android.launcher3.touch.PagedOrientationHandler;
|
||||
import com.android.launcher3.util.VibratorWrapper;
|
||||
import com.android.launcher3.views.FloatingIconView;
|
||||
import com.android.quickstep.RecentsAnimationCallbacks.RecentsAnimationListener;
|
||||
import com.android.quickstep.util.ActiveGestureLog;
|
||||
import com.android.quickstep.util.ActivityInitListener;
|
||||
import com.android.quickstep.util.AppWindowAnimationHelper;
|
||||
import com.android.quickstep.util.RectFSpringAnim;
|
||||
import com.android.quickstep.util.TaskViewSimulator;
|
||||
import com.android.quickstep.util.TransformParams;
|
||||
import com.android.quickstep.util.TransformParams.BuilderProxy;
|
||||
import com.android.quickstep.util.WindowSizeStrategy;
|
||||
import com.android.quickstep.views.RecentsView;
|
||||
import com.android.quickstep.views.TaskView;
|
||||
import com.android.systemui.shared.recents.model.ThumbnailData;
|
||||
|
@ -76,7 +75,7 @@ import java.util.function.Consumer;
|
|||
* Base class for swipe up handler with some utility methods
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.Q)
|
||||
public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q extends RecentsView>
|
||||
public abstract class BaseSwipeUpHandler<T extends StatefulActivity<?>, Q extends RecentsView>
|
||||
implements RecentsAnimationListener {
|
||||
|
||||
private static final String TAG = "BaseSwipeUpHandler";
|
||||
|
@ -97,7 +96,7 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
|
|||
protected final Context mContext;
|
||||
protected final RecentsAnimationDeviceState mDeviceState;
|
||||
protected final GestureState mGestureState;
|
||||
protected final BaseActivityInterface<T> mActivityInterface;
|
||||
protected final BaseActivityInterface<?, T> mActivityInterface;
|
||||
protected final InputConsumerController mInputConsumer;
|
||||
|
||||
protected final TaskViewSimulator mTaskViewSimulator;
|
||||
|
@ -132,15 +131,14 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
|
|||
private boolean mRecentsViewScrollLinked = false;
|
||||
|
||||
protected BaseSwipeUpHandler(Context context, RecentsAnimationDeviceState deviceState,
|
||||
GestureState gestureState, InputConsumerController inputConsumer,
|
||||
WindowSizeStrategy windowSizeStrategy) {
|
||||
GestureState gestureState, InputConsumerController inputConsumer) {
|
||||
mContext = context;
|
||||
mDeviceState = deviceState;
|
||||
mGestureState = gestureState;
|
||||
mActivityInterface = gestureState.getActivityInterface();
|
||||
mActivityInitListener = mActivityInterface.createActivityInitListener(this::onActivityInit);
|
||||
mInputConsumer = inputConsumer;
|
||||
mTaskViewSimulator = new TaskViewSimulator(context, windowSizeStrategy);
|
||||
mTaskViewSimulator = new TaskViewSimulator(context, gestureState.getActivityInterface());
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -379,18 +377,9 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
|
|||
mDragLengthFactorStartPullback = mDragLengthFactorMaxPullback = 1;
|
||||
}
|
||||
|
||||
AnimatorSet anim = new AnimatorSet();
|
||||
anim.setDuration(mTransitionDragLength * 2);
|
||||
anim.setInterpolator(t -> t * mDragLengthFactor);
|
||||
anim.play(ObjectAnimator.ofFloat(mTaskViewSimulator.recentsViewScale,
|
||||
AnimatedFloat.VALUE,
|
||||
mTaskViewSimulator.getFullScreenScale(), 1));
|
||||
anim.play(ObjectAnimator.ofFloat(mTaskViewSimulator.fullScreenProgress,
|
||||
AnimatedFloat.VALUE,
|
||||
BACKGROUND_APP.getOverviewFullscreenProgress(),
|
||||
OVERVIEW.getOverviewFullscreenProgress()));
|
||||
mWindowTransitionController =
|
||||
AnimatorPlaybackController.wrap(anim, mTransitionDragLength * 2);
|
||||
PendingAnimation pa = new PendingAnimation(mTransitionDragLength * 2);
|
||||
mTaskViewSimulator.addAppToOverviewAnim(pa, t -> t * mDragLengthFactor);
|
||||
mWindowTransitionController = pa.createPlaybackController();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -400,7 +389,16 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
|
|||
|
||||
protected boolean onActivityInit(Boolean alreadyOnHome) {
|
||||
T createdActivity = mActivityInterface.getCreatedActivity();
|
||||
if (TestProtocol.sDebugTracing) {
|
||||
Log.d(TestProtocol.PAUSE_NOT_DETECTED, "BaseSwipeUpHandler.1");
|
||||
}
|
||||
if (createdActivity != null) {
|
||||
if (TestProtocol.sDebugTracing) {
|
||||
Log.d(TestProtocol.PAUSE_NOT_DETECTED, "BaseSwipeUpHandler.2");
|
||||
}
|
||||
((RecentsView) createdActivity.getOverviewPanel())
|
||||
.setLayoutRotation(mDeviceState.getCurrentActiveRotation(),
|
||||
mDeviceState.getDisplayRotation());
|
||||
initTransitionEndpoints(InvariantDeviceProfile.INSTANCE.get(mContext)
|
||||
.getDeviceProfile(mContext));
|
||||
}
|
||||
|
@ -653,14 +651,11 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onBuildParams(Builder builder, RemoteAnimationTargetCompat app, int targetMode,
|
||||
TransformParams params) {
|
||||
if (app.mode == targetMode
|
||||
&& app.activityType != RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
|
||||
builder.withMatrix(mMatrix)
|
||||
.withWindowCrop(mCropRect)
|
||||
.withCornerRadius(params.getCornerRadius());
|
||||
}
|
||||
public void onBuildTargetParams(
|
||||
Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
|
||||
builder.withMatrix(mMatrix)
|
||||
.withWindowCrop(mCropRect)
|
||||
.withCornerRadius(params.getCornerRadius());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -20,20 +20,22 @@ import static com.android.launcher3.anim.Interpolators.LINEAR;
|
|||
import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
|
||||
import static com.android.quickstep.fallback.RecentsState.BACKGROUND_APP;
|
||||
import static com.android.quickstep.fallback.RecentsState.DEFAULT;
|
||||
import static com.android.quickstep.util.WindowSizeStrategy.FALLBACK_RECENTS_SIZE_STRATEGY;
|
||||
import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
|
||||
import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
import com.android.launcher3.anim.PendingAnimation;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto;
|
||||
import com.android.quickstep.fallback.FallbackRecentsView;
|
||||
import com.android.quickstep.fallback.RecentsState;
|
||||
import com.android.quickstep.util.ActivityInitListener;
|
||||
import com.android.quickstep.views.RecentsView;
|
||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||
|
@ -46,19 +48,18 @@ import java.util.function.Predicate;
|
|||
* currently running one and apps should interact with the {@link RecentsActivity} as opposed
|
||||
* to the in-launcher one.
|
||||
*/
|
||||
public final class FallbackActivityInterface implements
|
||||
BaseActivityInterface<RecentsActivity> {
|
||||
public final class FallbackActivityInterface extends
|
||||
BaseActivityInterface<RecentsState, RecentsActivity> {
|
||||
|
||||
public FallbackActivityInterface() { }
|
||||
public static final FallbackActivityInterface INSTANCE = new FallbackActivityInterface();
|
||||
|
||||
@Override
|
||||
public void onTransitionCancelled(boolean activityVisible) {
|
||||
// TODO:
|
||||
private FallbackActivityInterface() {
|
||||
super(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect) {
|
||||
FALLBACK_RECENTS_SIZE_STRATEGY.calculateTaskSize(context, dp, outRect);
|
||||
calculateTaskSize(context, dp, outRect);
|
||||
if (dp.isVerticalBarLayout()
|
||||
&& SysUINavigationMode.INSTANCE.get(context).getMode() != NO_BUTTON) {
|
||||
Rect targetInsets = dp.getInsets();
|
||||
|
@ -69,17 +70,6 @@ public final class FallbackActivityInterface implements
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSwipeUpToRecentsComplete() {
|
||||
RecentsActivity activity = getCreatedActivity();
|
||||
if (activity == null) {
|
||||
return;
|
||||
}
|
||||
RecentsView recentsView = activity.getOverviewPanel();
|
||||
recentsView.getClearAllButton().setVisibilityAlpha(1);
|
||||
recentsView.setDisallowScrollToClearAll(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAssistantVisibilityChanged(float visibility) {
|
||||
// This class becomes active when the screen is locked.
|
||||
|
@ -198,11 +188,14 @@ public final class FallbackActivityInterface implements
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onLaunchTaskSuccess() {
|
||||
RecentsActivity activity = getCreatedActivity();
|
||||
if (activity == null) {
|
||||
return;
|
||||
}
|
||||
activity.onTaskLaunched();
|
||||
public void getMultiWindowSize(Context context, DeviceProfile dp, PointF out) {
|
||||
out.set(dp.widthPx, dp.heightPx);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float getExtraSpace(Context context, DeviceProfile dp) {
|
||||
return showOverviewActions(context)
|
||||
? context.getResources().getDimensionPixelSize(R.dimen.overview_actions_height)
|
||||
: 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,7 +24,6 @@ import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
|
|||
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
|
||||
import static com.android.quickstep.RecentsActivity.EXTRA_TASK_ID;
|
||||
import static com.android.quickstep.RecentsActivity.EXTRA_THUMBNAIL;
|
||||
import static com.android.quickstep.util.WindowSizeStrategy.FALLBACK_RECENTS_SIZE_STRATEGY;
|
||||
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
|
||||
|
||||
import android.animation.Animator;
|
||||
|
@ -114,7 +113,7 @@ public class FallbackSwipeHandler extends BaseSwipeUpHandler<RecentsActivity, Fa
|
|||
public FallbackSwipeHandler(Context context, RecentsAnimationDeviceState deviceState,
|
||||
GestureState gestureState, InputConsumerController inputConsumer,
|
||||
boolean isLikelyToStartNewTask, boolean continuingLastGesture) {
|
||||
super(context, deviceState, gestureState, inputConsumer, FALLBACK_RECENTS_SIZE_STRATEGY);
|
||||
super(context, deviceState, gestureState, inputConsumer);
|
||||
|
||||
mInQuickSwitchMode = isLikelyToStartNewTask || continuingLastGesture;
|
||||
mContinuingLastGesture = continuingLastGesture;
|
||||
|
|
|
@ -21,16 +21,21 @@ import static com.android.launcher3.LauncherState.OVERVIEW;
|
|||
import static com.android.launcher3.anim.Interpolators.ACCEL_2;
|
||||
import static com.android.launcher3.anim.Interpolators.INSTANT;
|
||||
import static com.android.launcher3.anim.Interpolators.LINEAR;
|
||||
import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
|
||||
import static com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory.INDEX_RECENTS_FADE_ANIM;
|
||||
import static com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory.INDEX_RECENTS_TRANSLATE_X_ANIM;
|
||||
import static com.android.launcher3.uioverrides.states.QuickstepAtomicAnimationFactory.INDEX_SHELF_ANIM;
|
||||
import static com.android.quickstep.LauncherSwipeHandler.RECENTS_ATTACH_DURATION;
|
||||
import static com.android.quickstep.util.WindowSizeStrategy.LAUNCHER_ACTIVITY_SIZE_STRATEGY;
|
||||
import static com.android.quickstep.SysUINavigationMode.getMode;
|
||||
import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
|
||||
import static com.android.quickstep.util.LayoutUtils.getDefaultSwipeHeight;
|
||||
import static com.android.quickstep.views.RecentsView.ADJACENT_PAGE_OFFSET;
|
||||
import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
|
@ -44,6 +49,7 @@ import com.android.launcher3.DeviceProfile;
|
|||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherInitListener;
|
||||
import com.android.launcher3.LauncherState;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.allapps.DiscoveryBounce;
|
||||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
import com.android.launcher3.anim.PendingAnimation;
|
||||
|
@ -60,7 +66,6 @@ import com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState;
|
|||
import com.android.quickstep.views.LauncherRecentsView;
|
||||
import com.android.quickstep.views.RecentsView;
|
||||
import com.android.systemui.plugins.shared.LauncherOverlayManager;
|
||||
import com.android.systemui.shared.recents.model.ThumbnailData;
|
||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
@ -69,11 +74,18 @@ import java.util.function.Predicate;
|
|||
/**
|
||||
* {@link BaseActivityInterface} for the in-launcher recents.
|
||||
*/
|
||||
public final class LauncherActivityInterface implements BaseActivityInterface<Launcher> {
|
||||
public final class LauncherActivityInterface extends
|
||||
BaseActivityInterface<LauncherState, Launcher> {
|
||||
|
||||
public static final LauncherActivityInterface INSTANCE = new LauncherActivityInterface();
|
||||
|
||||
private LauncherActivityInterface() {
|
||||
super(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect) {
|
||||
LAUNCHER_ACTIVITY_SIZE_STRATEGY.calculateTaskSize(context, dp, outRect);
|
||||
calculateTaskSize(context, dp, outRect);
|
||||
if (dp.isVerticalBarLayout() && SysUINavigationMode.getMode(context) != Mode.NO_BUTTON) {
|
||||
Rect targetInsets = dp.getInsets();
|
||||
int hotseatInset = dp.isSeascape() ? targetInsets.left : targetInsets.right;
|
||||
|
@ -83,25 +95,15 @@ public final class LauncherActivityInterface implements BaseActivityInterface<La
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTransitionCancelled(boolean activityVisible) {
|
||||
Launcher launcher = getCreatedActivity();
|
||||
if (launcher == null) {
|
||||
return;
|
||||
}
|
||||
LauncherState startState = launcher.getStateManager().getRestState();
|
||||
launcher.getStateManager().goToState(startState, activityVisible);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSwipeUpToRecentsComplete() {
|
||||
// Re apply state in case we did something funky during the transition.
|
||||
super.onSwipeUpToRecentsComplete();
|
||||
Launcher launcher = getCreatedActivity();
|
||||
if (launcher == null) {
|
||||
return;
|
||||
if (launcher != null) {
|
||||
RecentsView recentsView = launcher.getOverviewPanel();
|
||||
DiscoveryBounce.showForOverviewIfNeeded(launcher,
|
||||
recentsView.getPagedOrientationHandler());
|
||||
}
|
||||
launcher.getStateManager().reapplyState();
|
||||
DiscoveryBounce.showForOverviewIfNeeded(launcher);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -113,15 +115,7 @@ public final class LauncherActivityInterface implements BaseActivityInterface<La
|
|||
// Ensure recents is at the correct position for NORMAL state. For example, when we detach
|
||||
// recents, we assume the first task is invisible, making translation off by one task.
|
||||
launcher.getStateManager().reapplyState();
|
||||
setLauncherHideBackArrow(false);
|
||||
}
|
||||
|
||||
private void setLauncherHideBackArrow(boolean hideBackArrow) {
|
||||
Launcher launcher = getCreatedActivity();
|
||||
if (launcher == null) {
|
||||
return;
|
||||
}
|
||||
launcher.getRootView().setForceHideBackArrow(hideBackArrow);
|
||||
launcher.getRootView().setForceHideBackArrow(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -298,6 +292,11 @@ public final class LauncherActivityInterface implements BaseActivityInterface<La
|
|||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHintUserWillBeActive() {
|
||||
getCreatedActivity().setHintUserWillBeActive();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deferStartingActivity(RecentsAnimationDeviceState deviceState, MotionEvent ev) {
|
||||
return deviceState.isInDeferredGestureRegion(ev);
|
||||
|
@ -336,15 +335,6 @@ public final class LauncherActivityInterface implements BaseActivityInterface<La
|
|||
launcher.getStateManager().goToState(OVERVIEW);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLaunchTaskSuccess() {
|
||||
Launcher launcher = getCreatedActivity();
|
||||
if (launcher == null) {
|
||||
return;
|
||||
}
|
||||
launcher.getStateManager().moveToRestState();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void closeOverlay() {
|
||||
Launcher launcher = getCreatedActivity();
|
||||
|
@ -359,23 +349,6 @@ public final class LauncherActivityInterface implements BaseActivityInterface<La
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void switchRunningTaskViewToScreenshot(ThumbnailData thumbnailData,
|
||||
Runnable onFinishRunnable) {
|
||||
Launcher launcher = getCreatedActivity();
|
||||
if (launcher == null) {
|
||||
return;
|
||||
}
|
||||
RecentsView recentsView = launcher.getOverviewPanel();
|
||||
if (recentsView == null) {
|
||||
if (onFinishRunnable != null) {
|
||||
onFinishRunnable.run();
|
||||
}
|
||||
return;
|
||||
}
|
||||
recentsView.switchToScreenshot(thumbnailData, onFinishRunnable);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setOnDeferredActivityLaunchCallback(Runnable r) {
|
||||
Launcher launcher = getCreatedActivity();
|
||||
|
@ -404,4 +377,50 @@ public final class LauncherActivityInterface implements BaseActivityInterface<La
|
|||
}
|
||||
return launcher.getDepthController();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getMultiWindowSize(Context context, DeviceProfile dp, PointF out) {
|
||||
DeviceProfile fullDp = dp.getFullScreenProfile();
|
||||
// Use availableWidthPx and availableHeightPx instead of widthPx and heightPx to
|
||||
// account for system insets
|
||||
out.set(fullDp.availableWidthPx, fullDp.availableHeightPx);
|
||||
float halfDividerSize = context.getResources()
|
||||
.getDimension(R.dimen.multi_window_task_divider_size) / 2;
|
||||
|
||||
if (fullDp.isLandscape) {
|
||||
out.x = out.x / 2 - halfDividerSize;
|
||||
} else {
|
||||
out.y = out.y / 2 - halfDividerSize;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected float getExtraSpace(Context context, DeviceProfile dp) {
|
||||
if (dp.isVerticalBarLayout()) {
|
||||
return 0;
|
||||
} else {
|
||||
Resources res = context.getResources();
|
||||
if (showOverviewActions(context)) {
|
||||
//TODO: this needs to account for the swipe gesture height and accessibility
|
||||
// UI when shown.
|
||||
float actionsBottomMargin = 0;
|
||||
if (getMode(context) == Mode.THREE_BUTTONS) {
|
||||
actionsBottomMargin = res.getDimensionPixelSize(
|
||||
R.dimen.overview_actions_bottom_margin_three_button);
|
||||
} else {
|
||||
actionsBottomMargin = res.getDimensionPixelSize(
|
||||
R.dimen.overview_actions_bottom_margin_gesture);
|
||||
}
|
||||
float actionsHeight = actionsBottomMargin
|
||||
+ res.getDimensionPixelSize(R.dimen.overview_actions_height);
|
||||
return actionsHeight;
|
||||
} else {
|
||||
return getDefaultSwipeHeight(context, dp) + dp.workspacePageIndicatorHeight
|
||||
+ res.getDimensionPixelSize(
|
||||
R.dimen.dynamic_grid_hotseat_extra_vertical_size)
|
||||
+ res.getDimensionPixelSize(
|
||||
R.dimen.dynamic_grid_hotseat_bottom_padding);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -35,7 +35,6 @@ import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
|
|||
import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
|
||||
import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.HIDE;
|
||||
import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.PEEK;
|
||||
import static com.android.quickstep.util.WindowSizeStrategy.LAUNCHER_ACTIVITY_SIZE_STRATEGY;
|
||||
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
|
||||
|
||||
import android.animation.Animator;
|
||||
|
@ -60,7 +59,6 @@ import androidx.annotation.NonNull;
|
|||
import androidx.annotation.UiThread;
|
||||
|
||||
import com.android.launcher3.AbstractFloatingView;
|
||||
import com.android.launcher3.BaseDraggingActivity;
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.R;
|
||||
|
@ -69,6 +67,7 @@ import com.android.launcher3.anim.AnimationSuccessListener;
|
|||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
import com.android.launcher3.anim.Interpolators;
|
||||
import com.android.launcher3.logging.UserEventDispatcher;
|
||||
import com.android.launcher3.statemanager.StatefulActivity;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
|
||||
|
@ -198,7 +197,7 @@ public class LauncherSwipeHandler extends BaseSwipeUpHandler<Launcher, RecentsVi
|
|||
TaskAnimationManager taskAnimationManager, GestureState gestureState,
|
||||
long touchTimeMs, boolean continuingLastGesture,
|
||||
InputConsumerController inputConsumer) {
|
||||
super(context, deviceState, gestureState, inputConsumer, LAUNCHER_ACTIVITY_SIZE_STRATEGY);
|
||||
super(context, deviceState, gestureState, inputConsumer);
|
||||
mTaskAnimationManager = taskAnimationManager;
|
||||
mTouchTimeMs = touchTimeMs;
|
||||
mContinuingLastGesture = continuingLastGesture;
|
||||
|
@ -277,6 +276,8 @@ public class LauncherSwipeHandler extends BaseSwipeUpHandler<Launcher, RecentsVi
|
|||
if (mActivity == activity) {
|
||||
return true;
|
||||
}
|
||||
mTaskViewSimulator.setLayoutRotation(mDeviceState.getCurrentActiveRotation(),
|
||||
mDeviceState.getDisplayRotation());
|
||||
if (mActivity != null) {
|
||||
// The launcher may have been recreated as a result of device rotation.
|
||||
int oldState = mStateCallback.getState() & ~LAUNCHER_UI_STATES;
|
||||
|
@ -587,9 +588,8 @@ public class LauncherSwipeHandler extends BaseSwipeUpHandler<Launcher, RecentsVi
|
|||
boolean quickswitchThresholdPassed = centermostTask != runningTask;
|
||||
|
||||
// We will handle the sysui flags based on the centermost task view.
|
||||
mRecentsAnimationController.setUseLauncherSystemBarFlags(
|
||||
(swipeUpThresholdPassed || quickswitchThresholdPassed)
|
||||
&& centermostTaskFlags != 0);
|
||||
mRecentsAnimationController.setUseLauncherSystemBarFlags(swipeUpThresholdPassed
|
||||
|| (quickswitchThresholdPassed && centermostTaskFlags != 0));
|
||||
mRecentsAnimationController.setSplitScreenMinimized(swipeUpThresholdPassed);
|
||||
|
||||
int sysuiFlags = swipeUpThresholdPassed ? 0 : centermostTaskFlags;
|
||||
|
@ -684,7 +684,7 @@ public class LauncherSwipeHandler extends BaseSwipeUpHandler<Launcher, RecentsVi
|
|||
setTargetAlphaProvider(LauncherSwipeHandler::getHiddenTargetAlpha);
|
||||
}
|
||||
|
||||
BaseDraggingActivity activity = mActivityInterface.getCreatedActivity();
|
||||
StatefulActivity activity = mActivityInterface.getCreatedActivity();
|
||||
return activity == null ? InputConsumer.NO_OP
|
||||
: new OverviewInputConsumer(mGestureState, activity, null, true);
|
||||
}
|
||||
|
@ -940,6 +940,7 @@ public class LauncherSwipeHandler extends BaseSwipeUpHandler<Launcher, RecentsVi
|
|||
: null;
|
||||
|
||||
mActivity.getRootView().setForceHideBackArrow(true);
|
||||
mActivityInterface.setHintUserWillBeActive();
|
||||
|
||||
homeAnimFactory = new HomeAnimationFactory(floatingIconView) {
|
||||
|
||||
|
|
|
@ -1,31 +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.quickstep;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
|
||||
/**
|
||||
* Empty activity to start a recents transition
|
||||
*/
|
||||
public class LockScreenRecentsActivity extends Activity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
finish();
|
||||
}
|
||||
}
|
|
@ -28,9 +28,10 @@ import android.os.SystemClock;
|
|||
import android.view.ViewConfiguration;
|
||||
|
||||
import androidx.annotation.BinderThread;
|
||||
import com.android.launcher3.BaseDraggingActivity;
|
||||
|
||||
import com.android.launcher3.appprediction.PredictionUiStateManager;
|
||||
import com.android.launcher3.logging.UserEventDispatcher;
|
||||
import com.android.launcher3.statemanager.StatefulActivity;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto;
|
||||
import com.android.quickstep.util.ActivityInitListener;
|
||||
import com.android.quickstep.util.RemoteAnimationProvider;
|
||||
|
@ -111,7 +112,7 @@ public class OverviewCommandHelper {
|
|||
protected void onTransitionComplete() {
|
||||
// TODO(b/138729100) This doesn't execute first time launcher is run
|
||||
if (mTriggeredFromAltTab) {
|
||||
RecentsView rv = (RecentsView) mActivityInterface.getVisibleRecentsView();
|
||||
RecentsView rv = mActivityInterface.getVisibleRecentsView();
|
||||
if (rv == null) {
|
||||
return;
|
||||
}
|
||||
|
@ -136,7 +137,7 @@ public class OverviewCommandHelper {
|
|||
|
||||
@Override
|
||||
protected boolean handleCommand(long elapsedTime) {
|
||||
RecentsView recents = (RecentsView) mActivityInterface.getVisibleRecentsView();
|
||||
RecentsView recents = mActivityInterface.getVisibleRecentsView();
|
||||
if (recents == null) {
|
||||
return false;
|
||||
}
|
||||
|
@ -150,9 +151,9 @@ public class OverviewCommandHelper {
|
|||
}
|
||||
}
|
||||
|
||||
private class RecentsActivityCommand<T extends BaseDraggingActivity> implements Runnable {
|
||||
private class RecentsActivityCommand<T extends StatefulActivity<?>> implements Runnable {
|
||||
|
||||
protected final BaseActivityInterface<T> mActivityInterface;
|
||||
protected final BaseActivityInterface<?, T> mActivityInterface;
|
||||
private final long mCreateTime;
|
||||
private final AppToOverviewAnimationProvider<T> mAnimationProvider;
|
||||
|
||||
|
|
|
@ -261,10 +261,6 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> {
|
|||
AccessibilityManagerCompat.sendStateEventToTest(getBaseContext(), OVERVIEW_STATE_ORDINAL);
|
||||
}
|
||||
|
||||
public void onTaskLaunched() {
|
||||
mFallbackRecentsView.resetTaskVisuals();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
|
|
@ -63,6 +63,7 @@ import com.android.launcher3.config.FeatureFlags;
|
|||
import com.android.launcher3.logging.UserEventDispatcher;
|
||||
import com.android.launcher3.model.AppLaunchTracker;
|
||||
import com.android.launcher3.provider.RestoreDbTask;
|
||||
import com.android.launcher3.statemanager.StatefulActivity;
|
||||
import com.android.launcher3.testing.TestLogging;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
import com.android.launcher3.tracing.nano.LauncherTraceProto;
|
||||
|
@ -539,8 +540,6 @@ 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).
|
||||
|
@ -617,21 +616,6 @@ public class TouchInteractionService extends Service implements PluginListener<O
|
|||
return;
|
||||
}
|
||||
mDeviceState.enableMultipleRegions(baseInputConsumer instanceof OtherActivityInputConsumer);
|
||||
BaseDraggingActivity activity =
|
||||
mOverviewComponentObserver.getActivityInterface().getCreatedActivity();
|
||||
if (TestProtocol.sDebugTracing) {
|
||||
Log.d(TestProtocol.PAUSE_NOT_DETECTED, "handleOrientationSetup.2");
|
||||
}
|
||||
if (activity == null || !(activity.getOverviewPanel() instanceof RecentsView)) {
|
||||
return;
|
||||
}
|
||||
if (TestProtocol.sDebugTracing) {
|
||||
Log.d(TestProtocol.PAUSE_NOT_DETECTED, "handleOrientationSetup.3");
|
||||
}
|
||||
((RecentsView) activity.getOverviewPanel())
|
||||
.setLayoutRotation(mDeviceState.getCurrentActiveRotation(),
|
||||
mDeviceState.getDisplayRotation());
|
||||
activity.getDragLayer().recreateControllers();
|
||||
}
|
||||
|
||||
private InputConsumer newBaseConsumer(GestureState previousGestureState,
|
||||
|
@ -705,7 +689,7 @@ public class TouchInteractionService extends Service implements PluginListener<O
|
|||
public InputConsumer createOverviewInputConsumer(GestureState previousGestureState,
|
||||
GestureState gestureState, MotionEvent event,
|
||||
boolean forceOverviewInputConsumer) {
|
||||
BaseDraggingActivity activity = gestureState.getActivityInterface().getCreatedActivity();
|
||||
StatefulActivity activity = gestureState.getActivityInterface().getCreatedActivity();
|
||||
if (activity == null) {
|
||||
return mResetGestureInputConsumer;
|
||||
}
|
||||
|
@ -754,7 +738,7 @@ public class TouchInteractionService extends Service implements PluginListener<O
|
|||
return;
|
||||
}
|
||||
|
||||
final BaseActivityInterface<BaseDraggingActivity> activityInterface =
|
||||
final BaseActivityInterface activityInterface =
|
||||
mOverviewComponentObserver.getActivityInterface();
|
||||
final Intent overviewIntent = new Intent(
|
||||
mOverviewComponentObserver.getOverviewIntentIgnoreSysUiState());
|
||||
|
|
|
@ -17,7 +17,6 @@ package com.android.quickstep.fallback;
|
|||
|
||||
import static com.android.quickstep.fallback.RecentsState.DEFAULT;
|
||||
import static com.android.quickstep.fallback.RecentsState.MODAL_TASK;
|
||||
import static com.android.quickstep.util.WindowSizeStrategy.FALLBACK_RECENTS_SIZE_STRATEGY;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.ActivityManager.RunningTaskInfo;
|
||||
|
@ -26,6 +25,7 @@ import android.os.Build;
|
|||
import android.util.AttributeSet;
|
||||
|
||||
import com.android.launcher3.statemanager.StateManager.StateListener;
|
||||
import com.android.quickstep.FallbackActivityInterface;
|
||||
import com.android.quickstep.RecentsActivity;
|
||||
import com.android.quickstep.views.OverviewActionsView;
|
||||
import com.android.quickstep.views.RecentsView;
|
||||
|
@ -45,7 +45,7 @@ public class FallbackRecentsView extends RecentsView<RecentsActivity>
|
|||
}
|
||||
|
||||
public FallbackRecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr, FALLBACK_RECENTS_SIZE_STRATEGY);
|
||||
super(context, attrs, defStyleAttr, FallbackActivityInterface.INSTANCE);
|
||||
mActivity.getStateManager().addStateListener(this);
|
||||
}
|
||||
|
||||
|
|
|
@ -25,43 +25,45 @@ import static com.android.quickstep.LauncherSwipeHandler.MIN_PROGRESS_FOR_OVERVI
|
|||
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
|
||||
import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.Point;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.VelocityTracker;
|
||||
import android.view.ViewConfiguration;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.anim.Interpolators;
|
||||
import com.android.launcher3.testing.TestLogging;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
import com.android.launcher3.util.DefaultDisplay;
|
||||
import com.android.quickstep.AnimatedFloat;
|
||||
import com.android.quickstep.GestureState;
|
||||
import com.android.quickstep.InputConsumer;
|
||||
import com.android.quickstep.LockScreenRecentsActivity;
|
||||
import com.android.quickstep.MultiStateCallback;
|
||||
import com.android.quickstep.RecentsAnimationCallbacks;
|
||||
import com.android.quickstep.RecentsAnimationController;
|
||||
import com.android.quickstep.RecentsAnimationDeviceState;
|
||||
import com.android.quickstep.RecentsAnimationTargets;
|
||||
import com.android.quickstep.TaskAnimationManager;
|
||||
import com.android.quickstep.util.AppWindowAnimationHelper;
|
||||
import com.android.quickstep.util.TransformParams;
|
||||
import com.android.quickstep.util.TransformParams.BuilderProxy;
|
||||
import com.android.systemui.shared.recents.model.ThumbnailData;
|
||||
import com.android.systemui.shared.system.ActivityManagerWrapper;
|
||||
import com.android.systemui.shared.system.InputMonitorCompat;
|
||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder;
|
||||
|
||||
/**
|
||||
* A dummy input consumer used when the device is still locked, e.g. from secure camera.
|
||||
*/
|
||||
public class DeviceLockedInputConsumer implements InputConsumer,
|
||||
RecentsAnimationCallbacks.RecentsAnimationListener {
|
||||
|
||||
private static final float SCALE_DOWN = 0.75f;
|
||||
RecentsAnimationCallbacks.RecentsAnimationListener, BuilderProxy {
|
||||
|
||||
private static final String[] STATE_NAMES = DEBUG_STATES ? new String[2] : null;
|
||||
private static int getFlagForIndex(int index, String name) {
|
||||
|
@ -84,18 +86,20 @@ public class DeviceLockedInputConsumer implements InputConsumer,
|
|||
private final InputMonitorCompat mInputMonitorCompat;
|
||||
|
||||
private final PointF mTouchDown = new PointF();
|
||||
private final AppWindowAnimationHelper mAppWindowAnimationHelper;
|
||||
private final TransformParams mTransformParams;
|
||||
private final Point mDisplaySize;
|
||||
private final MultiStateCallback mStateCallback;
|
||||
|
||||
private final Point mDisplaySize;
|
||||
private final Matrix mMatrix = new Matrix();
|
||||
private final float mMaxTranslationY;
|
||||
|
||||
private VelocityTracker mVelocityTracker;
|
||||
private float mProgress;
|
||||
private final AnimatedFloat mProgress = new AnimatedFloat(this::applyTransform);
|
||||
|
||||
private boolean mThresholdCrossed = false;
|
||||
private boolean mHomeLaunched = false;
|
||||
|
||||
private RecentsAnimationController mRecentsAnimationController;
|
||||
private RecentsAnimationTargets mRecentsAnimationTargets;
|
||||
|
||||
public DeviceLockedInputConsumer(Context context, RecentsAnimationDeviceState deviceState,
|
||||
TaskAnimationManager taskAnimationManager, GestureState gestureState,
|
||||
|
@ -105,9 +109,10 @@ public class DeviceLockedInputConsumer implements InputConsumer,
|
|||
mTaskAnimationManager = taskAnimationManager;
|
||||
mGestureState = gestureState;
|
||||
mTouchSlopSquared = squaredTouchSlop(context);
|
||||
mAppWindowAnimationHelper = new AppWindowAnimationHelper(context);
|
||||
mTransformParams = new TransformParams();
|
||||
mInputMonitorCompat = inputMonitorCompat;
|
||||
mMaxTranslationY = context.getResources().getDimensionPixelSize(
|
||||
R.dimen.device_locked_y_offset);
|
||||
|
||||
// Do not use DeviceProfile as the user data might be locked
|
||||
mDisplaySize = DefaultDisplay.INSTANCE.get(context).getInfo().realSize;
|
||||
|
@ -158,9 +163,7 @@ public class DeviceLockedInputConsumer implements InputConsumer,
|
|||
}
|
||||
} else {
|
||||
float dy = Math.max(mTouchDown.y - y, 0);
|
||||
mProgress = dy / mDisplaySize.y;
|
||||
mTransformParams.setProgress(mProgress);
|
||||
mAppWindowAnimationHelper.applyTransform(mTransformParams);
|
||||
mProgress.updateValue(dy / mDisplaySize.y);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
@ -176,7 +179,6 @@ public class DeviceLockedInputConsumer implements InputConsumer,
|
|||
* the animation can still be running.
|
||||
*/
|
||||
private void finishTouchTracking(MotionEvent ev) {
|
||||
mStateCallback.setState(STATE_HANDLER_INVALIDATED);
|
||||
if (mThresholdCrossed && ev.getAction() == ACTION_UP) {
|
||||
mVelocityTracker.computeCurrentVelocity(1000,
|
||||
ViewConfiguration.get(mContext).getScaledMaximumFlingVelocity());
|
||||
|
@ -190,14 +192,29 @@ public class DeviceLockedInputConsumer implements InputConsumer,
|
|||
// Is fling
|
||||
dismissTask = velocityY < 0;
|
||||
} else {
|
||||
dismissTask = mProgress >= (1 - MIN_PROGRESS_FOR_OVERVIEW);
|
||||
}
|
||||
if (dismissTask) {
|
||||
// For now, just start the home intent so user is prompted to unlock the device.
|
||||
mContext.startActivity(new Intent(Intent.ACTION_MAIN)
|
||||
.addCategory(Intent.CATEGORY_HOME)
|
||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
|
||||
dismissTask = mProgress.value >= (1 - MIN_PROGRESS_FOR_OVERVIEW);
|
||||
}
|
||||
|
||||
// Animate back to fullscreen before finishing
|
||||
ObjectAnimator animator = mProgress.animateToValue(mProgress.value, 0);
|
||||
animator.setDuration(100);
|
||||
animator.setInterpolator(Interpolators.ACCEL);
|
||||
animator.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
if (dismissTask) {
|
||||
// For now, just start the home intent so user is prompted to unlock the device.
|
||||
mContext.startActivity(new Intent(Intent.ACTION_MAIN)
|
||||
.addCategory(Intent.CATEGORY_HOME)
|
||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK));
|
||||
mHomeLaunched = true;
|
||||
}
|
||||
mStateCallback.setState(STATE_HANDLER_INVALIDATED);
|
||||
}
|
||||
});
|
||||
animator.start();
|
||||
} else {
|
||||
mStateCallback.setState(STATE_HANDLER_INVALIDATED);
|
||||
}
|
||||
mVelocityTracker.recycle();
|
||||
mVelocityTracker = null;
|
||||
|
@ -205,13 +222,11 @@ public class DeviceLockedInputConsumer implements InputConsumer,
|
|||
|
||||
private void startRecentsTransition() {
|
||||
mThresholdCrossed = true;
|
||||
mHomeLaunched = false;
|
||||
TestLogging.recordEvent(TestProtocol.SEQUENCE_PILFER, "pilferPointers");
|
||||
mInputMonitorCompat.pilferPointers();
|
||||
|
||||
Intent intent = new Intent(Intent.ACTION_MAIN)
|
||||
.addCategory(Intent.CATEGORY_DEFAULT)
|
||||
.setComponent(new ComponentName(mContext, LockScreenRecentsActivity.class))
|
||||
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK)
|
||||
Intent intent = mGestureState.getHomeIntent()
|
||||
.putExtra(INTENT_EXTRA_LOG_TRACE_ID, mGestureState.getGestureId());
|
||||
mTaskAnimationManager.startRecentsAnimation(mGestureState, intent, this);
|
||||
}
|
||||
|
@ -220,37 +235,40 @@ public class DeviceLockedInputConsumer implements InputConsumer,
|
|||
public void onRecentsAnimationStart(RecentsAnimationController controller,
|
||||
RecentsAnimationTargets targets) {
|
||||
mRecentsAnimationController = controller;
|
||||
mRecentsAnimationTargets = targets;
|
||||
|
||||
Rect displaySize = new Rect(0, 0, mDisplaySize.x, mDisplaySize.y);
|
||||
RemoteAnimationTargetCompat targetCompat = targets.findTask(
|
||||
mGestureState.getRunningTaskId());
|
||||
if (targetCompat != null) {
|
||||
mAppWindowAnimationHelper.updateSource(displaySize, targetCompat);
|
||||
}
|
||||
|
||||
Utilities.scaleRectAboutCenter(displaySize, SCALE_DOWN);
|
||||
displaySize.offsetTo(displaySize.left, 0);
|
||||
mTransformParams.setTargetSet(mRecentsAnimationTargets);
|
||||
mAppWindowAnimationHelper.updateTargetRect(displaySize);
|
||||
mAppWindowAnimationHelper.applyTransform(mTransformParams);
|
||||
|
||||
mTransformParams.setTargetSet(targets);
|
||||
applyTransform();
|
||||
mStateCallback.setState(STATE_TARGET_RECEIVED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
|
||||
mRecentsAnimationController = null;
|
||||
mRecentsAnimationTargets = null;
|
||||
mTransformParams.setTargetSet(null);
|
||||
}
|
||||
|
||||
private void endRemoteAnimation() {
|
||||
if (mRecentsAnimationController != null) {
|
||||
if (mHomeLaunched) {
|
||||
ActivityManagerWrapper.getInstance().cancelRecentsAnimation(false);
|
||||
} else if (mRecentsAnimationController != null) {
|
||||
mRecentsAnimationController.finishController(
|
||||
false /* toRecents */, null /* callback */, false /* sendUserLeaveHint */);
|
||||
}
|
||||
}
|
||||
|
||||
private void applyTransform() {
|
||||
mTransformParams.setProgress(mProgress.value);
|
||||
if (mTransformParams.getTargetSet() != null) {
|
||||
mTransformParams.applySurfaceParams(mTransformParams.createSurfaceParams(this));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBuildTargetParams(
|
||||
Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
|
||||
mMatrix.setTranslate(0, mProgress.value * mMaxTranslationY);
|
||||
builder.withMatrix(mMatrix);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onConsumerAboutToBeSwitched() {
|
||||
mStateCallback.setState(STATE_HANDLER_INVALIDATED);
|
||||
|
|
|
@ -26,6 +26,7 @@ import static android.view.MotionEvent.INVALID_POINTER_ID;
|
|||
import static com.android.launcher3.Utilities.EDGE_NAV_BAR;
|
||||
import static com.android.launcher3.Utilities.squaredHypot;
|
||||
import static com.android.launcher3.util.TraceHelper.FLAG_CHECK_FOR_RACE_CONDITIONS;
|
||||
import static com.android.quickstep.GestureState.STATE_OVERSCROLL_WINDOW_CREATED;
|
||||
import static com.android.quickstep.util.ActiveGestureLog.INTENT_EXTRA_LOG_TRACE_ID;
|
||||
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
|
||||
|
||||
|
@ -430,6 +431,6 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
|
|||
|
||||
@Override
|
||||
public boolean allowInterceptByParent() {
|
||||
return !mPassedPilferInputSlop;
|
||||
return !mPassedPilferInputSlop || mGestureState.hasState(STATE_OVERSCROLL_WINDOW_CREATED);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,12 +24,15 @@ import static android.view.MotionEvent.ACTION_UP;
|
|||
|
||||
import static com.android.launcher3.Utilities.squaredHypot;
|
||||
|
||||
import static java.lang.Math.abs;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.PointF;
|
||||
import android.view.GestureDetector;
|
||||
import android.util.Log;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.ViewConfiguration;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.BaseDraggingActivity;
|
||||
|
@ -44,24 +47,36 @@ import com.android.systemui.shared.system.InputMonitorCompat;
|
|||
* Input consumer for handling events to pass to an {@code OverscrollPlugin}.
|
||||
*/
|
||||
public class OverscrollInputConsumer extends DelegateInputConsumer {
|
||||
|
||||
private static final String TAG = "OverscrollInputConsumer";
|
||||
private static final boolean DEBUG_LOGS_ENABLED = false;
|
||||
private static void debugPrint(String log) {
|
||||
if (DEBUG_LOGS_ENABLED) {
|
||||
Log.v(TAG, log);
|
||||
}
|
||||
}
|
||||
|
||||
private final PointF mDownPos = new PointF();
|
||||
private final PointF mLastPos = new PointF();
|
||||
private final PointF mStartDragPos = new PointF();
|
||||
private final int mAngleThreshold;
|
||||
|
||||
private final float mFlingThresholdPx;
|
||||
private final int mFlingDistanceThresholdPx;
|
||||
private final int mFlingVelocityThresholdPx;
|
||||
private int mActivePointerId = -1;
|
||||
private boolean mPassedSlop = false;
|
||||
|
||||
// True if we set ourselves as active, meaning we no longer pass events to the delegate.
|
||||
private boolean mPassedActiveThreshold = false;
|
||||
// When a gesture crosses this length, this recognizer will attempt to interpret touch events.
|
||||
private final float mSquaredSlop;
|
||||
// When a gesture crosses this length, this recognizer will become the sole active recognizer.
|
||||
private final float mSquaredActiveThreshold;
|
||||
// When a gesture crosses this length, the overscroll view should be shown.
|
||||
private final float mSquaredFinishThreshold;
|
||||
private boolean mThisDownIsIgnored = false;
|
||||
|
||||
private final GestureState mGestureState;
|
||||
@Nullable
|
||||
private final OverscrollPlugin mPlugin;
|
||||
private final GestureDetector mGestureDetector;
|
||||
|
||||
@Nullable
|
||||
private RecentsView mRecentsView;
|
||||
|
@ -72,15 +87,24 @@ public class OverscrollInputConsumer extends DelegateInputConsumer {
|
|||
|
||||
mAngleThreshold = context.getResources()
|
||||
.getInteger(R.integer.assistant_gesture_corner_deg_threshold);
|
||||
mFlingThresholdPx = context.getResources()
|
||||
.getDimension(R.dimen.gestures_overscroll_fling_threshold);
|
||||
mFlingDistanceThresholdPx = (int) context.getResources()
|
||||
.getDimension(R.dimen.gestures_overscroll_fling_threshold);
|
||||
mFlingVelocityThresholdPx = ViewConfiguration.get(context).getScaledMinimumFlingVelocity();
|
||||
mGestureState = gestureState;
|
||||
mPlugin = plugin;
|
||||
|
||||
float slop = ViewConfiguration.get(context).getScaledTouchSlop();
|
||||
|
||||
mSquaredSlop = slop * slop;
|
||||
mGestureDetector = new GestureDetector(context, new FlingGestureListener());
|
||||
|
||||
|
||||
float finishGestureThreshold = (int) context.getResources()
|
||||
.getDimension(R.dimen.gestures_overscroll_finish_threshold);
|
||||
mSquaredFinishThreshold = finishGestureThreshold * finishGestureThreshold;
|
||||
|
||||
float activeThreshold = (int) context.getResources()
|
||||
.getDimension(R.dimen.gestures_overscroll_active_threshold);
|
||||
mSquaredActiveThreshold = activeThreshold * activeThreshold;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -90,12 +114,26 @@ public class OverscrollInputConsumer extends DelegateInputConsumer {
|
|||
|
||||
@Override
|
||||
public void onMotionEvent(MotionEvent ev) {
|
||||
if (mPlugin == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
debugPrint("got event, underlying activity is " + getUnderlyingActivity());
|
||||
switch (ev.getActionMasked()) {
|
||||
case ACTION_DOWN: {
|
||||
debugPrint("ACTION_DOWN");
|
||||
mActivePointerId = ev.getPointerId(0);
|
||||
mDownPos.set(ev.getX(), ev.getY());
|
||||
mLastPos.set(mDownPos);
|
||||
|
||||
if (mPlugin.blockOtherGestures()) {
|
||||
debugPrint("mPlugin.blockOtherGestures(), becoming active on ACTION_DOWN");
|
||||
// Otherwise, if an appear gesture is performed when the Activity is visible,
|
||||
// the Activity will dismiss its keyboard.
|
||||
mPassedActiveThreshold = true;
|
||||
mPassedSlop = true;
|
||||
mStartDragPos.set(mLastPos.x, mLastPos.y);
|
||||
setActive(ev);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ACTION_POINTER_DOWN: {
|
||||
|
@ -121,57 +159,61 @@ public class OverscrollInputConsumer extends DelegateInputConsumer {
|
|||
if (mState == STATE_DELEGATE_ACTIVE) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!mDelegate.allowInterceptByParent()) {
|
||||
mState = STATE_DELEGATE_ACTIVE;
|
||||
break;
|
||||
}
|
||||
|
||||
// Update last touch position.
|
||||
int pointerIndex = ev.findPointerIndex(mActivePointerId);
|
||||
if (pointerIndex == -1) {
|
||||
break;
|
||||
}
|
||||
mLastPos.set(ev.getX(pointerIndex), ev.getY(pointerIndex));
|
||||
|
||||
if (!mPassedSlop) {
|
||||
// Normal gesture, ensure we pass the slop before we start tracking the gesture
|
||||
if (squaredHypot(mLastPos.x - mDownPos.x, mLastPos.y - mDownPos.y)
|
||||
> mSquaredSlop) {
|
||||
|
||||
mPassedSlop = true;
|
||||
mStartDragPos.set(mLastPos.x, mLastPos.y);
|
||||
if (isOverscrolled()) {
|
||||
setActive(ev);
|
||||
|
||||
if (mPlugin != null) {
|
||||
mPlugin.onTouchStart(getDeviceState(), getUnderlyingActivity());
|
||||
}
|
||||
} else {
|
||||
mState = STATE_DELEGATE_ACTIVE;
|
||||
}
|
||||
}
|
||||
float squaredDist = squaredHypot(mLastPos.x - mDownPos.x, mLastPos.y - mDownPos.y);
|
||||
if ((!mPassedSlop) && (squaredDist > mSquaredSlop)) {
|
||||
mPassedSlop = true;
|
||||
mStartDragPos.set(mLastPos.x, mLastPos.y);
|
||||
mGestureState.setState(GestureState.STATE_OVERSCROLL_WINDOW_CREATED);
|
||||
}
|
||||
|
||||
if (mPassedSlop && mState != STATE_DELEGATE_ACTIVE && isOverscrolled()
|
||||
&& mPlugin != null) {
|
||||
mPlugin.onTouchTraveled(getDistancePx());
|
||||
boolean becomeActive = mPassedSlop && !mPassedActiveThreshold && isOverscrolled()
|
||||
&& (squaredDist > mSquaredActiveThreshold);
|
||||
if (becomeActive) {
|
||||
debugPrint("Past slop and past threshold, set active");
|
||||
mPassedActiveThreshold = true;
|
||||
setActive(ev);
|
||||
}
|
||||
|
||||
if (mPassedActiveThreshold) {
|
||||
debugPrint("ACTION_MOVE Relaying touch event");
|
||||
mPlugin.onTouchEvent(ev, getHorizontalDistancePx(), getVerticalDistancePx(),
|
||||
(int) Math.sqrt(mSquaredFinishThreshold), mFlingDistanceThresholdPx,
|
||||
mFlingVelocityThresholdPx, getDeviceState(), getUnderlyingActivity());
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case ACTION_CANCEL:
|
||||
case ACTION_UP:
|
||||
if (mState != STATE_DELEGATE_ACTIVE && mPassedSlop && mPlugin != null) {
|
||||
mPlugin.onTouchEnd(getDistancePx());
|
||||
debugPrint("ACTION_UP");
|
||||
if (mPassedActiveThreshold) {
|
||||
debugPrint("ACTION_UP Relaying touch event");
|
||||
|
||||
mPlugin.onTouchEvent(ev, getHorizontalDistancePx(), getVerticalDistancePx(),
|
||||
(int) Math.sqrt(mSquaredFinishThreshold), mFlingDistanceThresholdPx,
|
||||
mFlingVelocityThresholdPx, getDeviceState(), getUnderlyingActivity());
|
||||
}
|
||||
|
||||
|
||||
mPassedSlop = false;
|
||||
mPassedActiveThreshold = false;
|
||||
mState = STATE_INACTIVE;
|
||||
break;
|
||||
}
|
||||
|
||||
if (mState != STATE_DELEGATE_ACTIVE) {
|
||||
mGestureDetector.onTouchEvent(ev);
|
||||
}
|
||||
|
||||
if (mState != STATE_ACTIVE) {
|
||||
mDelegate.onMotionEvent(ev);
|
||||
}
|
||||
|
@ -192,15 +234,20 @@ public class OverscrollInputConsumer extends DelegateInputConsumer {
|
|||
maxIndex = 1;
|
||||
}
|
||||
|
||||
boolean atRightMostApp = (mRecentsView == null
|
||||
|| mRecentsView.getRunningTaskIndex() <= maxIndex);
|
||||
boolean atRightMostApp = mRecentsView == null
|
||||
|| (mRecentsView.getRunningTaskIndex() <= maxIndex);
|
||||
|
||||
// Check if the gesture is within our angle threshold of horizontal
|
||||
float deltaY = Math.abs(mLastPos.y - mDownPos.y);
|
||||
float deltaX = mDownPos.x - mLastPos.x; // Positive if this is a gesture to the left
|
||||
boolean angleInBounds = Math.toDegrees(Math.atan2(deltaY, deltaX)) < mAngleThreshold;
|
||||
float deltaY = abs(mLastPos.y - mDownPos.y);
|
||||
float deltaX = mLastPos.x - mDownPos.x;
|
||||
|
||||
return atRightMostApp && angleInBounds;
|
||||
boolean angleInBounds = (Math.toDegrees(Math.atan2(deltaY, abs(deltaX))) < mAngleThreshold);
|
||||
|
||||
boolean overscrollVisible = mPlugin.blockOtherGestures();
|
||||
boolean overscrollInvisibleAndLeftSwipe = !overscrollVisible && deltaX < 0;
|
||||
boolean gestureDirectionMatchesVisibility = overscrollVisible
|
||||
|| overscrollInvisibleAndLeftSwipe;
|
||||
return atRightMostApp && angleInBounds && gestureDirectionMatchesVisibility;
|
||||
}
|
||||
|
||||
private String getDeviceState() {
|
||||
|
@ -219,35 +266,22 @@ public class OverscrollInputConsumer extends DelegateInputConsumer {
|
|||
return deviceState;
|
||||
}
|
||||
|
||||
private int getDistancePx() {
|
||||
return (int) Math.hypot(mLastPos.x - mDownPos.x, mLastPos.y - mDownPos.y);
|
||||
private int getHorizontalDistancePx() {
|
||||
return (int) (mLastPos.x - mDownPos.x);
|
||||
}
|
||||
|
||||
private String getUnderlyingActivity() {
|
||||
private int getVerticalDistancePx() {
|
||||
return (int) (mLastPos.y - mDownPos.y);
|
||||
}
|
||||
|
||||
private @NonNull String getUnderlyingActivity() {
|
||||
// Overly defensive, got guidance on code review that something in the chain of
|
||||
// `mGestureState.getRunningTask().topActivity` can be null and thus cause a null pointer
|
||||
// exception to be thrown, but we aren't sure which part can be null.
|
||||
if ((mGestureState == null) || (mGestureState.getRunningTask() == null)
|
||||
|| (mGestureState.getRunningTask().topActivity == null)) {
|
||||
return "";
|
||||
}
|
||||
return mGestureState.getRunningTask().topActivity.flattenToString();
|
||||
}
|
||||
|
||||
private class FlingGestureListener extends GestureDetector.SimpleOnGestureListener {
|
||||
@Override
|
||||
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
|
||||
if (isValidAngle(velocityX, -velocityY)
|
||||
&& getDistancePx() >= mFlingThresholdPx
|
||||
&& mState != STATE_DELEGATE_ACTIVE) {
|
||||
|
||||
if (mPlugin != null) {
|
||||
mPlugin.onFling(-velocityX);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private boolean isValidAngle(float deltaX, float deltaY) {
|
||||
float angle = (float) Math.toDegrees(Math.atan2(deltaY, deltaX));
|
||||
// normalize so that angle is measured clockwise from horizontal in the bottom right
|
||||
// corner and counterclockwise from horizontal in the bottom left corner
|
||||
|
||||
angle = angle > 90 ? 180 - angle : angle;
|
||||
return (angle < mAngleThreshold);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -24,8 +24,8 @@ import android.view.MotionEvent;
|
|||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.BaseDraggingActivity;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.statemanager.StatefulActivity;
|
||||
import com.android.launcher3.testing.TestLogging;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
import com.android.launcher3.views.BaseDragLayer;
|
||||
|
@ -41,11 +41,11 @@ import java.util.function.Predicate;
|
|||
/**
|
||||
* Input consumer for handling touch on the recents/Launcher activity.
|
||||
*/
|
||||
public class OverviewInputConsumer<T extends BaseDraggingActivity>
|
||||
public class OverviewInputConsumer<T extends StatefulActivity<?>>
|
||||
implements InputConsumer {
|
||||
|
||||
private final T mActivity;
|
||||
private final BaseActivityInterface<T> mActivityInterface;
|
||||
private final BaseActivityInterface<?, T> mActivityInterface;
|
||||
private final BaseDragLayer mTarget;
|
||||
private final InputMonitorCompat mInputMonitor;
|
||||
|
||||
|
|
|
@ -77,7 +77,6 @@ public class AppWindowAnimationHelper implements TransformParams.BuilderProxy {
|
|||
private final Matrix mTmpMatrix = new Matrix();
|
||||
private final Rect mTmpRect = new Rect();
|
||||
private final RectF mTmpRectF = new RectF();
|
||||
private final RectF mCurrentRectWithInsets = new RectF();
|
||||
private RecentsOrientedState mOrientedState;
|
||||
// Corner radius of windows, in pixels
|
||||
private final float mWindowCornerRadius;
|
||||
|
@ -88,9 +87,6 @@ public class AppWindowAnimationHelper implements TransformParams.BuilderProxy {
|
|||
// Whether or not to actually use the rounded cornders on windows
|
||||
private boolean mUseRoundedCornersOnWindows;
|
||||
|
||||
// Corner radius currently applied to transformed window.
|
||||
private float mCurrentCornerRadius;
|
||||
|
||||
public AppWindowAnimationHelper(RecentsOrientedState orientedState, Context context) {
|
||||
Resources res = context.getResources();
|
||||
mOrientedState = orientedState;
|
||||
|
@ -100,24 +96,11 @@ public class AppWindowAnimationHelper implements TransformParams.BuilderProxy {
|
|||
mUseRoundedCornersOnWindows = mSupportsRoundedCornersOnWindows;
|
||||
}
|
||||
|
||||
public AppWindowAnimationHelper(Context context) {
|
||||
this(null, context);
|
||||
}
|
||||
|
||||
private void updateSourceStack(RemoteAnimationTargetCompat target) {
|
||||
mSourceInsets.set(target.contentInsets);
|
||||
mSourceStackBounds.set(target.screenSpaceBounds);
|
||||
}
|
||||
|
||||
public void updateSource(Rect homeStackBounds, RemoteAnimationTargetCompat target) {
|
||||
updateSourceStack(target);
|
||||
updateHomeBounds(homeStackBounds);
|
||||
}
|
||||
|
||||
public void updateHomeBounds(Rect homeStackBounds) {
|
||||
mHomeStackBounds.set(homeStackBounds);
|
||||
}
|
||||
|
||||
public void updateTargetRect(Rect targetRect) {
|
||||
mSourceRect.set(mSourceInsets.left, mSourceInsets.top,
|
||||
mSourceStackBounds.width() - mSourceInsets.right,
|
||||
|
@ -202,7 +185,6 @@ public class AppWindowAnimationHelper implements TransformParams.BuilderProxy {
|
|||
cornerRadius = mapRange(boundToRange(params.getProgress(), 0, 1),
|
||||
windowCornerRadius, mTaskCornerRadius);
|
||||
}
|
||||
mCurrentCornerRadius = cornerRadius;
|
||||
}
|
||||
|
||||
builder.withMatrix(mTmpMatrix)
|
||||
|
@ -238,11 +220,6 @@ public class AppWindowAnimationHelper implements TransformParams.BuilderProxy {
|
|||
mSourceStackBounds.height() - (mSourceWindowClipInsets.bottom * progress);
|
||||
}
|
||||
|
||||
public RectF getCurrentRectWithInsets() {
|
||||
mTmpMatrix.mapRect(mCurrentRectWithInsets, mCurrentClipRectF);
|
||||
return mCurrentRectWithInsets;
|
||||
}
|
||||
|
||||
public void fromTaskThumbnailView(TaskThumbnailView ttv, RecentsView rv,
|
||||
@Nullable RemoteAnimationTargetCompat target) {
|
||||
BaseDraggingActivity activity = BaseDraggingActivity.fromContext(ttv.getContext());
|
||||
|
@ -314,8 +291,4 @@ public class AppWindowAnimationHelper implements TransformParams.BuilderProxy {
|
|||
return mTargetRect;
|
||||
}
|
||||
|
||||
public float getCurrentCornerRadius() {
|
||||
return mCurrentCornerRadius;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -212,12 +212,13 @@ public class StaggeredWorkspaceAnim {
|
|||
}
|
||||
|
||||
private void addScrimAnimationForState(Launcher launcher, LauncherState state, long duration) {
|
||||
PendingAnimation builder = new PendingAnimation(duration, mAnimators);
|
||||
PendingAnimation builder = new PendingAnimation(duration);
|
||||
launcher.getWorkspace().getStateTransitionAnimation().setScrim(builder, state);
|
||||
builder.setFloat(
|
||||
launcher.getDragLayer().getOverviewScrim(),
|
||||
OverviewScrim.SCRIM_PROGRESS,
|
||||
state.getOverviewScrimAlpha(launcher),
|
||||
ACCEL_DEACCEL);
|
||||
mAnimators.play(builder.buildAnim());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import static com.android.launcher3.touch.PagedOrientationHandler.MATRIX_POST_TR
|
|||
import static com.android.quickstep.util.RecentsOrientedState.postDisplayRotation;
|
||||
import static com.android.systemui.shared.system.WindowManagerWrapper.WINDOWING_MODE_FULLSCREEN;
|
||||
|
||||
import android.animation.TimeInterpolator;
|
||||
import android.content.Context;
|
||||
import android.graphics.Matrix;
|
||||
import android.graphics.PointF;
|
||||
|
@ -29,8 +30,10 @@ import android.graphics.RectF;
|
|||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.anim.PendingAnimation;
|
||||
import com.android.launcher3.touch.PagedOrientationHandler;
|
||||
import com.android.quickstep.AnimatedFloat;
|
||||
import com.android.quickstep.BaseActivityInterface;
|
||||
import com.android.quickstep.views.RecentsView.ScrollState;
|
||||
import com.android.quickstep.views.TaskThumbnailView.PreviewPositionHelper;
|
||||
import com.android.quickstep.views.TaskView;
|
||||
|
@ -50,7 +53,7 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
|
|||
|
||||
private final RecentsOrientedState mOrientationState;
|
||||
private final Context mContext;
|
||||
private final WindowSizeStrategy mSizeStrategy;
|
||||
private final BaseActivityInterface mSizeStrategy;
|
||||
|
||||
private final Rect mTaskRect = new Rect();
|
||||
private final PointF mPivot = new PointF();
|
||||
|
@ -79,7 +82,7 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
|
|||
private boolean mLayoutValid = false;
|
||||
private boolean mScrollValid = false;
|
||||
|
||||
public TaskViewSimulator(Context context, WindowSizeStrategy sizeStrategy) {
|
||||
public TaskViewSimulator(Context context, BaseActivityInterface sizeStrategy) {
|
||||
mContext = context;
|
||||
mSizeStrategy = sizeStrategy;
|
||||
|
||||
|
@ -141,6 +144,14 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds animation for all the components corresponding to transition from an app to overview
|
||||
*/
|
||||
public void addAppToOverviewAnim(PendingAnimation pa, TimeInterpolator interpolator) {
|
||||
pa.addFloat(fullScreenProgress, AnimatedFloat.VALUE, 1, 0, interpolator);
|
||||
pa.addFloat(recentsViewScale, AnimatedFloat.VALUE, getFullScreenScale(), 1, interpolator);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the current clipped/visible window bounds in the window coordinate space
|
||||
*/
|
||||
|
@ -247,14 +258,11 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onBuildParams(Builder builder, RemoteAnimationTargetCompat app,
|
||||
int targetMode, TransformParams params) {
|
||||
if (app.mode == targetMode
|
||||
&& app.activityType != RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
|
||||
builder.withMatrix(mMatrix)
|
||||
.withWindowCrop(mTmpCropRect)
|
||||
.withCornerRadius(getCurrentCornerRadius());
|
||||
}
|
||||
public void onBuildTargetParams(
|
||||
Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
|
||||
builder.withMatrix(mMatrix)
|
||||
.withWindowCrop(mTmpCropRect)
|
||||
.withCornerRadius(getCurrentCornerRadius());
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package com.android.quickstep.util;
|
||||
|
||||
import android.graphics.RectF;
|
||||
import android.util.FloatProperty;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
|
@ -29,6 +30,19 @@ import com.android.systemui.shared.system.TransactionCompat;
|
|||
|
||||
public class TransformParams {
|
||||
|
||||
public static FloatProperty<TransformParams> PROGRESS =
|
||||
new FloatProperty<TransformParams>("progress") {
|
||||
@Override
|
||||
public void setValue(TransformParams params, float v) {
|
||||
params.setProgress(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Float get(TransformParams params) {
|
||||
return params.getProgress();
|
||||
}
|
||||
};
|
||||
|
||||
private float mProgress;
|
||||
private @Nullable RectF mCurrentRect;
|
||||
private float mTargetAlpha;
|
||||
|
@ -176,10 +190,6 @@ public class TransformParams {
|
|||
return mTargetSet;
|
||||
}
|
||||
|
||||
public SyncRtSurfaceTransactionApplierCompat getSyncTransactionApplier() {
|
||||
return mSyncTransactionApplier;
|
||||
}
|
||||
|
||||
public void applySurfaceParams(SurfaceParams[] params) {
|
||||
if (mSyncTransactionApplier != null) {
|
||||
mSyncTransactionApplier.scheduleApply(params);
|
||||
|
@ -199,7 +209,15 @@ public class TransformParams {
|
|||
|
||||
public interface BuilderProxy {
|
||||
|
||||
void onBuildParams(SurfaceParams.Builder builder,
|
||||
RemoteAnimationTargetCompat app, int targetMode, TransformParams params);
|
||||
default void onBuildParams(SurfaceParams.Builder builder,
|
||||
RemoteAnimationTargetCompat app, int targetMode, TransformParams params) {
|
||||
if (app.mode == targetMode
|
||||
&& app.activityType != RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
|
||||
onBuildTargetParams(builder, app, params);
|
||||
}
|
||||
}
|
||||
|
||||
default void onBuildTargetParams(SurfaceParams.Builder builder,
|
||||
RemoteAnimationTargetCompat app, TransformParams params) { }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,6 @@ import static com.android.launcher3.LauncherState.SPRING_LOADED;
|
|||
import static com.android.launcher3.QuickstepAppTransitionManagerImpl.ALL_APPS_PROGRESS_OFF_SCREEN;
|
||||
import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
|
||||
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
|
||||
import static com.android.quickstep.util.WindowSizeStrategy.LAUNCHER_ACTIVITY_SIZE_STRATEGY;
|
||||
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
|
@ -47,6 +46,7 @@ import com.android.launcher3.statemanager.StateManager.StateListener;
|
|||
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
|
||||
import com.android.launcher3.util.TraceHelper;
|
||||
import com.android.launcher3.views.ScrimView;
|
||||
import com.android.quickstep.LauncherActivityInterface;
|
||||
import com.android.quickstep.SysUINavigationMode;
|
||||
import com.android.quickstep.util.TransformParams;
|
||||
import com.android.systemui.plugins.PluginListener;
|
||||
|
@ -89,7 +89,7 @@ public class LauncherRecentsView extends RecentsView<BaseQuickstepLauncher>
|
|||
}
|
||||
|
||||
public LauncherRecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr, LAUNCHER_ACTIVITY_SIZE_STRATEGY);
|
||||
super(context, attrs, defStyleAttr, LauncherActivityInterface.INSTANCE);
|
||||
mActivity.getStateManager().addStateListener(this);
|
||||
}
|
||||
|
||||
|
|
|
@ -115,6 +115,7 @@ import com.android.launcher3.util.MultiValueAlpha;
|
|||
import com.android.launcher3.util.OverScroller;
|
||||
import com.android.launcher3.util.Themes;
|
||||
import com.android.launcher3.util.ViewPool;
|
||||
import com.android.quickstep.BaseActivityInterface;
|
||||
import com.android.quickstep.RecentsAnimationController;
|
||||
import com.android.quickstep.RecentsAnimationTargets;
|
||||
import com.android.quickstep.RecentsModel;
|
||||
|
@ -128,7 +129,6 @@ import com.android.quickstep.util.LayoutUtils;
|
|||
import com.android.quickstep.util.RecentsOrientedState;
|
||||
import com.android.quickstep.util.SplitScreenBounds;
|
||||
import com.android.quickstep.util.TransformParams;
|
||||
import com.android.quickstep.util.WindowSizeStrategy;
|
||||
import com.android.systemui.plugins.ResourceProvider;
|
||||
import com.android.systemui.shared.recents.IPinnedStackAnimationListener;
|
||||
import com.android.systemui.shared.recents.model.Task;
|
||||
|
@ -209,7 +209,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
|||
};
|
||||
|
||||
protected final RecentsOrientedState mOrientationState;
|
||||
protected final WindowSizeStrategy mSizeStrategy;
|
||||
protected final BaseActivityInterface mSizeStrategy;
|
||||
protected RecentsAnimationController mRecentsAnimationController;
|
||||
protected RecentsAnimationTargets mRecentsAnimationTargets;
|
||||
protected AppWindowAnimationHelper mAppWindowAnimationHelper;
|
||||
|
@ -381,7 +381,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
|||
};
|
||||
|
||||
public RecentsView(Context context, AttributeSet attrs, int defStyleAttr,
|
||||
WindowSizeStrategy sizeStrategy) {
|
||||
BaseActivityInterface sizeStrategy) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
setPageSpacing(getResources().getDimensionPixelSize(R.dimen.recents_page_spacing));
|
||||
setEnableFreeScroll(true);
|
||||
|
@ -2190,6 +2190,10 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
|
|||
*/
|
||||
public void setModalStateEnabled(boolean isModalState) { }
|
||||
|
||||
public BaseActivityInterface getSizeStrategy() {
|
||||
return mSizeStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used to register callbacks for when our empty message state changes.
|
||||
*
|
||||
|
|
|
@ -79,8 +79,13 @@
|
|||
<!-- Distance to move elements when swiping up to go home from launcher -->
|
||||
<dimen name="home_pullback_distance">28dp</dimen>
|
||||
|
||||
<!-- Distance to move the tasks when swiping up while the device is locked -->
|
||||
<dimen name="device_locked_y_offset">-80dp</dimen>
|
||||
|
||||
<!-- Overscroll Gesture -->
|
||||
<dimen name="gestures_overscroll_fling_threshold">40dp</dimen>
|
||||
<dimen name="gestures_overscroll_active_threshold">80dp</dimen>
|
||||
<dimen name="gestures_overscroll_finish_threshold">136dp</dimen>
|
||||
|
||||
<!-- Tips Gesture Tutorial -->
|
||||
<dimen name="gesture_tutorial_title_margin_start_end">40dp</dimen>
|
||||
|
|
|
@ -304,4 +304,8 @@ public abstract class BaseQuickstepLauncher extends Launcher
|
|||
public ShelfPeekAnim getShelfPeekAnim() {
|
||||
return mShelfPeekAnim;
|
||||
}
|
||||
|
||||
public void setHintUserWillBeActive() {
|
||||
addActivityFlags(ACTIVITY_STATE_USER_WILL_BE_ACTIVE);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,23 +15,34 @@
|
|||
*/
|
||||
package com.android.quickstep;
|
||||
|
||||
import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
|
||||
import static com.android.quickstep.SysUINavigationMode.getMode;
|
||||
import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Build;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.animation.Interpolator;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.UiThread;
|
||||
|
||||
import com.android.launcher3.BaseDraggingActivity;
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
import com.android.launcher3.statehandlers.DepthController;
|
||||
import com.android.launcher3.statemanager.BaseState;
|
||||
import com.android.launcher3.statemanager.StatefulActivity;
|
||||
import com.android.launcher3.util.WindowBounds;
|
||||
import com.android.quickstep.SysUINavigationMode.Mode;
|
||||
import com.android.quickstep.util.ActivityInitListener;
|
||||
import com.android.quickstep.util.ShelfPeekAnim;
|
||||
import com.android.quickstep.util.SplitScreenBounds;
|
||||
import com.android.quickstep.views.RecentsView;
|
||||
import com.android.systemui.shared.recents.model.ThumbnailData;
|
||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||
|
||||
|
@ -42,84 +53,234 @@ import java.util.function.Predicate;
|
|||
* Utility class which abstracts out the logical differences between Launcher and RecentsActivity.
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.P)
|
||||
public interface BaseActivityInterface<T extends BaseDraggingActivity> {
|
||||
public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_TYPE>,
|
||||
ACTIVITY_TYPE extends StatefulActivity<STATE_TYPE>> {
|
||||
|
||||
void onTransitionCancelled(boolean activityVisible);
|
||||
private final PointF mTempPoint = new PointF();
|
||||
public final boolean rotationSupportedByActivity;
|
||||
|
||||
int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect);
|
||||
protected BaseActivityInterface(boolean rotationSupportedByActivity) {
|
||||
this.rotationSupportedByActivity = rotationSupportedByActivity;
|
||||
}
|
||||
|
||||
void onSwipeUpToRecentsComplete();
|
||||
public void onTransitionCancelled(boolean activityVisible) {
|
||||
ACTIVITY_TYPE activity = getCreatedActivity();
|
||||
if (activity == null) {
|
||||
return;
|
||||
}
|
||||
STATE_TYPE startState = activity.getStateManager().getRestState();
|
||||
activity.getStateManager().goToState(startState, activityVisible);
|
||||
}
|
||||
|
||||
default void onSwipeUpToHomeComplete() { }
|
||||
void onAssistantVisibilityChanged(float visibility);
|
||||
public abstract int getSwipeUpDestinationAndLength(
|
||||
DeviceProfile dp, Context context, Rect outRect);
|
||||
|
||||
AnimationFactory prepareRecentsUI(
|
||||
public void onSwipeUpToRecentsComplete() {
|
||||
// Re apply state in case we did something funky during the transition.
|
||||
ACTIVITY_TYPE activity = getCreatedActivity();
|
||||
if (activity == null) {
|
||||
return;
|
||||
}
|
||||
activity.getStateManager().reapplyState();
|
||||
}
|
||||
|
||||
public void onSwipeUpToHomeComplete() { }
|
||||
|
||||
public abstract void onAssistantVisibilityChanged(float visibility);
|
||||
|
||||
public abstract AnimationFactory prepareRecentsUI(
|
||||
boolean activityVisible, Consumer<AnimatorPlaybackController> callback);
|
||||
|
||||
ActivityInitListener createActivityInitListener(Predicate<Boolean> onInitListener);
|
||||
public abstract ActivityInitListener createActivityInitListener(
|
||||
Predicate<Boolean> onInitListener);
|
||||
|
||||
/**
|
||||
* Sets a callback to be run when an activity launch happens while launcher is not yet resumed.
|
||||
*/
|
||||
default void setOnDeferredActivityLaunchCallback(Runnable r) {}
|
||||
public void setOnDeferredActivityLaunchCallback(Runnable r) {}
|
||||
|
||||
@Nullable
|
||||
T getCreatedActivity();
|
||||
public abstract ACTIVITY_TYPE getCreatedActivity();
|
||||
|
||||
@Nullable
|
||||
default DepthController getDepthController() {
|
||||
public DepthController getDepthController() {
|
||||
return null;
|
||||
}
|
||||
|
||||
default boolean isResumed() {
|
||||
BaseDraggingActivity activity = getCreatedActivity();
|
||||
public final boolean isResumed() {
|
||||
ACTIVITY_TYPE activity = getCreatedActivity();
|
||||
return activity != null && activity.hasBeenResumed();
|
||||
}
|
||||
|
||||
default boolean isStarted() {
|
||||
BaseDraggingActivity activity = getCreatedActivity();
|
||||
public final boolean isStarted() {
|
||||
ACTIVITY_TYPE activity = getCreatedActivity();
|
||||
return activity != null && activity.isStarted();
|
||||
}
|
||||
|
||||
@UiThread
|
||||
@Nullable
|
||||
<T extends View> T getVisibleRecentsView();
|
||||
public abstract <T extends RecentsView> T getVisibleRecentsView();
|
||||
|
||||
@UiThread
|
||||
boolean switchToRecentsIfVisible(Runnable onCompleteCallback);
|
||||
public abstract boolean switchToRecentsIfVisible(Runnable onCompleteCallback);
|
||||
|
||||
Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTargetCompat target);
|
||||
public abstract Rect getOverviewWindowBounds(
|
||||
Rect homeBounds, RemoteAnimationTargetCompat target);
|
||||
|
||||
boolean allowMinimizeSplitScreen();
|
||||
public abstract boolean allowMinimizeSplitScreen();
|
||||
|
||||
default boolean deferStartingActivity(RecentsAnimationDeviceState deviceState, MotionEvent ev) {
|
||||
public boolean deferStartingActivity(RecentsAnimationDeviceState deviceState, MotionEvent ev) {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the prediction state to the overview state.
|
||||
*/
|
||||
default void updateOverviewPredictionState() {
|
||||
// By default overview predictions are not supported
|
||||
public void updateOverviewPredictionState() {
|
||||
// By public overview predictions are not supported
|
||||
}
|
||||
|
||||
/**
|
||||
* Used for containerType in {@link com.android.launcher3.logging.UserEventDispatcher}
|
||||
*/
|
||||
int getContainerType();
|
||||
public abstract int getContainerType();
|
||||
|
||||
boolean isInLiveTileMode();
|
||||
public abstract boolean isInLiveTileMode();
|
||||
|
||||
void onLaunchTaskFailed();
|
||||
public abstract void onLaunchTaskFailed();
|
||||
|
||||
void onLaunchTaskSuccess();
|
||||
public void onLaunchTaskSuccess() {
|
||||
ACTIVITY_TYPE activity = getCreatedActivity();
|
||||
if (activity == null) {
|
||||
return;
|
||||
}
|
||||
activity.getStateManager().moveToRestState();
|
||||
}
|
||||
|
||||
default void closeOverlay() { }
|
||||
public void closeOverlay() { }
|
||||
|
||||
default void switchRunningTaskViewToScreenshot(ThumbnailData thumbnailData,
|
||||
Runnable runnable) {}
|
||||
public void switchRunningTaskViewToScreenshot(ThumbnailData thumbnailData, Runnable runnable) {
|
||||
ACTIVITY_TYPE activity = getCreatedActivity();
|
||||
if (activity == null) {
|
||||
return;
|
||||
}
|
||||
RecentsView recentsView = activity.getOverviewPanel();
|
||||
if (recentsView == null) {
|
||||
if (runnable != null) {
|
||||
runnable.run();
|
||||
}
|
||||
return;
|
||||
}
|
||||
recentsView.switchToScreenshot(thumbnailData, runnable);
|
||||
}
|
||||
|
||||
interface AnimationFactory {
|
||||
public void setHintUserWillBeActive() {}
|
||||
|
||||
/**
|
||||
* Sets the expected window size in multi-window mode
|
||||
*/
|
||||
public abstract void getMultiWindowSize(Context context, DeviceProfile dp, PointF out);
|
||||
|
||||
/**
|
||||
* Calculates the taskView size for the provided device configuration
|
||||
*/
|
||||
public final void calculateTaskSize(Context context, DeviceProfile dp, Rect outRect) {
|
||||
calculateTaskSize(context, dp, getExtraSpace(context, dp), outRect);
|
||||
}
|
||||
|
||||
protected abstract float getExtraSpace(Context context, DeviceProfile dp);
|
||||
|
||||
private void calculateTaskSize(
|
||||
Context context, DeviceProfile dp, float extraVerticalSpace, Rect outRect) {
|
||||
Resources res = context.getResources();
|
||||
final boolean showLargeTaskSize = showOverviewActions(context);
|
||||
|
||||
final int paddingResId;
|
||||
if (dp.isMultiWindowMode) {
|
||||
paddingResId = R.dimen.multi_window_task_card_horz_space;
|
||||
} else if (dp.isVerticalBarLayout()) {
|
||||
paddingResId = R.dimen.landscape_task_card_horz_space;
|
||||
} else if (showLargeTaskSize) {
|
||||
paddingResId = R.dimen.portrait_task_card_horz_space_big_overview;
|
||||
} else {
|
||||
paddingResId = R.dimen.portrait_task_card_horz_space;
|
||||
}
|
||||
float paddingHorz = res.getDimension(paddingResId);
|
||||
float paddingVert = showLargeTaskSize
|
||||
? 0 : res.getDimension(R.dimen.task_card_vert_space);
|
||||
|
||||
calculateTaskSizeInternal(context, dp, extraVerticalSpace, paddingHorz, paddingVert,
|
||||
res.getDimension(R.dimen.task_thumbnail_top_margin), outRect);
|
||||
}
|
||||
|
||||
private void calculateTaskSizeInternal(Context context, DeviceProfile dp,
|
||||
float extraVerticalSpace, float paddingHorz, float paddingVert, float topIconMargin,
|
||||
Rect outRect) {
|
||||
float taskWidth, taskHeight;
|
||||
Rect insets = dp.getInsets();
|
||||
if (dp.isMultiWindowMode) {
|
||||
WindowBounds bounds = SplitScreenBounds.INSTANCE.getSecondaryWindowBounds(context);
|
||||
taskWidth = bounds.availableSize.x;
|
||||
taskHeight = bounds.availableSize.y;
|
||||
} else {
|
||||
taskWidth = dp.availableWidthPx;
|
||||
taskHeight = dp.availableHeightPx;
|
||||
}
|
||||
|
||||
// Note this should be same as dp.availableWidthPx and dp.availableHeightPx unless
|
||||
// we override the insets ourselves.
|
||||
int launcherVisibleWidth = dp.widthPx - insets.left - insets.right;
|
||||
int launcherVisibleHeight = dp.heightPx - insets.top - insets.bottom;
|
||||
|
||||
float availableHeight = launcherVisibleHeight
|
||||
- topIconMargin - extraVerticalSpace - paddingVert;
|
||||
float availableWidth = launcherVisibleWidth - paddingHorz;
|
||||
|
||||
float scale = Math.min(availableWidth / taskWidth, availableHeight / taskHeight);
|
||||
float outWidth = scale * taskWidth;
|
||||
float outHeight = scale * taskHeight;
|
||||
|
||||
// Center in the visible space
|
||||
float x = insets.left + (launcherVisibleWidth - outWidth) / 2;
|
||||
float y = insets.top + Math.max(topIconMargin,
|
||||
(launcherVisibleHeight - extraVerticalSpace - outHeight) / 2);
|
||||
outRect.set(Math.round(x), Math.round(y),
|
||||
Math.round(x) + Math.round(outWidth), Math.round(y) + Math.round(outHeight));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the modal taskView size for the provided device configuration
|
||||
*/
|
||||
public void calculateModalTaskSize(Context context, DeviceProfile dp, Rect outRect) {
|
||||
float paddingHorz = context.getResources().getDimension(dp.isMultiWindowMode
|
||||
? R.dimen.multi_window_task_card_horz_space
|
||||
: dp.isVerticalBarLayout()
|
||||
? R.dimen.landscape_task_card_horz_space
|
||||
: R.dimen.portrait_modal_task_card_horz_space);
|
||||
float extraVerticalSpace = getOverviewActionsHeight(context);
|
||||
float paddingVert = 0;
|
||||
float topIconMargin = 0;
|
||||
calculateTaskSizeInternal(context, dp, extraVerticalSpace, paddingHorz, paddingVert,
|
||||
topIconMargin, outRect);
|
||||
}
|
||||
|
||||
/** Gets the space that the overview actions will take, including margins. */
|
||||
public float getOverviewActionsHeight(Context context) {
|
||||
Resources res = context.getResources();
|
||||
float actionsBottomMargin = 0;
|
||||
if (getMode(context) == Mode.THREE_BUTTONS) {
|
||||
actionsBottomMargin = res.getDimensionPixelSize(
|
||||
R.dimen.overview_actions_bottom_margin_three_button);
|
||||
} else {
|
||||
actionsBottomMargin = res.getDimensionPixelSize(
|
||||
R.dimen.overview_actions_bottom_margin_gesture);
|
||||
}
|
||||
float overviewActionsHeight = actionsBottomMargin
|
||||
+ res.getDimensionPixelSize(R.dimen.overview_actions_height);
|
||||
return overviewActionsHeight;
|
||||
}
|
||||
|
||||
public interface AnimationFactory {
|
||||
|
||||
default void onRemoteAnimationReceived(RemoteAnimationTargets targets) { }
|
||||
|
||||
|
@ -137,4 +298,8 @@ public interface BaseActivityInterface<T extends BaseDraggingActivity> {
|
|||
*/
|
||||
default void setRecentsAttachedToAppWindow(boolean attached, boolean animate) { }
|
||||
}
|
||||
|
||||
protected static boolean showOverviewActions(Context context) {
|
||||
return ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(context);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,10 +17,12 @@ package com.android.quickstep;
|
|||
|
||||
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.ActivityManager;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
|
||||
import com.android.launcher3.BaseDraggingActivity;
|
||||
import com.android.launcher3.statemanager.StatefulActivity;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
|
||||
import com.android.systemui.shared.recents.model.ThumbnailData;
|
||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||
|
@ -32,6 +34,7 @@ import java.util.ArrayList;
|
|||
* Manages the state for an active system gesture, listens for events from the system and Launcher,
|
||||
* and fires events when the states change.
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.R)
|
||||
public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationListener {
|
||||
|
||||
/**
|
||||
|
@ -106,6 +109,10 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
|
|||
public static final int STATE_RECENTS_ANIMATION_ENDED =
|
||||
getFlagForIndex("STATE_RECENTS_ANIMATION_ENDED");
|
||||
|
||||
// Called when we create an overscroll window when swiping right to left on the most recent app
|
||||
public static final int STATE_OVERSCROLL_WINDOW_CREATED =
|
||||
getFlagForIndex("STATE_OVERSCROLL_WINDOW_CREATED");
|
||||
|
||||
// Called when RecentsView stops scrolling and settles on a TaskView.
|
||||
public static final int STATE_RECENTS_SCROLLING_FINISHED =
|
||||
getFlagForIndex("STATE_RECENTS_SCROLLING_FINISHED");
|
||||
|
@ -189,7 +196,7 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
|
|||
/**
|
||||
* @return the interface to the activity handing the UI updates for this gesture.
|
||||
*/
|
||||
public <T extends BaseDraggingActivity> BaseActivityInterface<T> getActivityInterface() {
|
||||
public <T extends StatefulActivity<?>> BaseActivityInterface<?, T> getActivityInterface() {
|
||||
return mActivityInterface;
|
||||
}
|
||||
|
||||
|
@ -315,7 +322,7 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
|
|||
pw.println(" gestureID=" + mGestureId);
|
||||
pw.println(" runningTask=" + mRunningTask);
|
||||
pw.println(" endTarget=" + mEndTarget);
|
||||
pw.println(" lastAppearedTaskTarget=" + mLastAppearedTaskTarget);
|
||||
pw.println(" lastAppearedTaskTargetId=" + getLastAppearedTaskId());
|
||||
pw.println(" lastStartedTaskId=" + mLastStartedTaskId);
|
||||
pw.println(" isRecentsAnimationRunning=" + isRecentsAnimationRunning());
|
||||
}
|
||||
|
|
|
@ -317,13 +317,6 @@ class OrientationTouchTransformer {
|
|||
|
||||
private class OrientationRectF extends RectF {
|
||||
|
||||
/**
|
||||
* Delta to subtract width and height by because if we report the translated touch
|
||||
* bounds as the width and height, calling {@link RectF#contains(float, float)} will
|
||||
* be false
|
||||
*/
|
||||
private float maxDelta = 0.001f;
|
||||
|
||||
private int mRotation;
|
||||
private float mHeight;
|
||||
private float mWidth;
|
||||
|
@ -331,8 +324,8 @@ class OrientationTouchTransformer {
|
|||
OrientationRectF(float left, float top, float right, float bottom, int rotation) {
|
||||
super(left, top, right, bottom);
|
||||
this.mRotation = rotation;
|
||||
mHeight = bottom - maxDelta;
|
||||
mWidth = right - maxDelta;
|
||||
mHeight = bottom;
|
||||
mWidth = right;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -342,6 +335,13 @@ class OrientationTouchTransformer {
|
|||
return s;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean contains(float x, float y) {
|
||||
// Mark bottom right as included in the Rect (copied from Rect src, added "=" in "<=")
|
||||
return left < right && top < bottom // check for empty first
|
||||
&& x >= left && x <= right && y >= top && y <= bottom;
|
||||
}
|
||||
|
||||
boolean applyTransform(MotionEvent event, boolean forceTransform) {
|
||||
mTmpMatrix.reset();
|
||||
postDisplayRotation(deltaRotation(mCurrentDisplayRotation, mRotation),
|
||||
|
|
|
@ -140,7 +140,7 @@ public final class OverviewComponentObserver {
|
|||
|
||||
if (!mDeviceState.isHomeDisabled() && (defaultHome == null || mIsDefaultHome)) {
|
||||
// User default home is same as out home app. Use Overview integrated in Launcher.
|
||||
mActivityInterface = new LauncherActivityInterface();
|
||||
mActivityInterface = LauncherActivityInterface.INSTANCE;
|
||||
mIsHomeAndOverviewSame = true;
|
||||
mOverviewIntent = mMyHomeIntent;
|
||||
mCurrentHomeIntent.setComponent(mMyHomeIntent.getComponent());
|
||||
|
@ -150,7 +150,7 @@ public final class OverviewComponentObserver {
|
|||
} else {
|
||||
// The default home app is a different launcher. Use the fallback Overview instead.
|
||||
|
||||
mActivityInterface = new FallbackActivityInterface();
|
||||
mActivityInterface = FallbackActivityInterface.INSTANCE;
|
||||
mIsHomeAndOverviewSame = false;
|
||||
mOverviewIntent = mFallbackIntent;
|
||||
mCurrentHomeIntent.setComponent(defaultHome);
|
||||
|
|
|
@ -73,8 +73,6 @@ 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;
|
||||
|
@ -97,7 +95,6 @@ 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();
|
||||
}
|
||||
|
|
|
@ -68,7 +68,7 @@ public class RemoteAnimationTargets {
|
|||
}
|
||||
|
||||
public boolean isAnimatingHome() {
|
||||
for (RemoteAnimationTargetCompat target : apps) {
|
||||
for (RemoteAnimationTargetCompat target : unfilteredApps) {
|
||||
if (target.activityType == RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME) {
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -64,24 +64,46 @@ public class StatsLogCompatManager extends StatsLogManager {
|
|||
}
|
||||
|
||||
/**
|
||||
* Logs an event and accompanying {@link ItemInfo}
|
||||
* Logs a {@link LauncherEvent}.
|
||||
*/
|
||||
@Override
|
||||
public void log(LauncherEvent event) {
|
||||
log(event, DEFAULT_INSTANCE_ID, LauncherAtom.ItemInfo.getDefaultInstance());
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an event and accompanying {@link InstanceId}.
|
||||
*/
|
||||
@Override
|
||||
public void log(LauncherEvent event, InstanceId instanceId) {
|
||||
log(event, instanceId, LauncherAtom.ItemInfo.getDefaultInstance());
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an event and accompanying {@link ItemInfo}.
|
||||
*/
|
||||
@Override
|
||||
public void log(LauncherEvent event, LauncherAtom.ItemInfo itemInfo) {
|
||||
log(event, DEFAULT_INSTANCE_ID, itemInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an event and accompanying {@link LauncherAtom.ItemInfo}
|
||||
* Logs an event and accompanying {@link InstanceId} and {@link LauncherAtom.ItemInfo}.
|
||||
*/
|
||||
@Override
|
||||
public void log(LauncherEvent event, InstanceId instanceId, LauncherAtom.ItemInfo itemInfo) {
|
||||
if (IS_VERBOSE) {
|
||||
Log.d(TAG, String.format("\n%s\n%s", event.name(), itemInfo));
|
||||
Log.d(TAG, instanceId == DEFAULT_INSTANCE_ID
|
||||
? String.format("\n%s\n%s", event.name(), itemInfo)
|
||||
: String.format("%s(InstanceId:%s)\n%s", event.name(), instanceId, itemInfo));
|
||||
}
|
||||
|
||||
if (!Utilities.ATLEAST_R) {
|
||||
return;
|
||||
}
|
||||
SysUiStatsLog.write(SysUiStatsLog.LAUNCHER_EVENT,
|
||||
|
||||
SysUiStatsLog.write(
|
||||
SysUiStatsLog.LAUNCHER_EVENT,
|
||||
SysUiStatsLog.LAUNCHER_UICHANGED__ACTION__DEFAULT_ACTION /* deprecated */,
|
||||
SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__HOME /* TODO */,
|
||||
SysUiStatsLog.LAUNCHER_UICHANGED__DST_STATE__BACKGROUND /* TODO */,
|
||||
|
@ -118,6 +140,7 @@ public class StatsLogCompatManager extends StatsLogManager {
|
|||
}
|
||||
|
||||
private class SnapshotWorker extends BaseModelUpdateTask {
|
||||
|
||||
@Override
|
||||
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
|
||||
IntSparseArrayMap<FolderInfo> folders = dataModel.folders.clone();
|
||||
|
@ -140,6 +163,7 @@ public class StatsLogCompatManager extends StatsLogManager {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static void writeSnapshot(LauncherAtom.ItemInfo itemInfo) {
|
||||
if (IS_VERBOSE) {
|
||||
Log.d(TAG, "\nwriteSnapshot:" + itemInfo);
|
||||
|
|
|
@ -17,7 +17,6 @@ package com.android.quickstep.util;
|
|||
|
||||
import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
|
||||
import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
|
||||
import static com.android.quickstep.util.WindowSizeStrategy.LAUNCHER_ACTIVITY_SIZE_STRATEGY;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
|
@ -26,6 +25,7 @@ import android.view.ViewGroup;
|
|||
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.quickstep.LauncherActivityInterface;
|
||||
import com.android.quickstep.SysUINavigationMode;
|
||||
|
||||
public class LayoutUtils {
|
||||
|
@ -45,7 +45,7 @@ public class LayoutUtils {
|
|||
// Track the bottom of the window.
|
||||
if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(context)) {
|
||||
Rect taskSize = new Rect();
|
||||
LAUNCHER_ACTIVITY_SIZE_STRATEGY.calculateTaskSize(context, dp, taskSize);
|
||||
LauncherActivityInterface.INSTANCE.calculateTaskSize(context, dp, taskSize);
|
||||
return (dp.heightPx - taskSize.height()) / 2;
|
||||
}
|
||||
int shelfHeight = dp.hotseatBarSizePx + dp.getInsets().bottom;
|
||||
|
|
|
@ -27,6 +27,7 @@ import static com.android.launcher3.logging.LoggerUtils.extractObjectNameAndAddr
|
|||
import static com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY;
|
||||
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
|
||||
|
||||
import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
|
||||
import static java.lang.annotation.RetentionPolicy.SOURCE;
|
||||
|
||||
import android.content.ContentResolver;
|
||||
|
@ -52,6 +53,8 @@ import com.android.launcher3.Utilities;
|
|||
import com.android.launcher3.testing.TestProtocol;
|
||||
import com.android.launcher3.touch.PagedOrientationHandler;
|
||||
import com.android.launcher3.util.WindowBounds;
|
||||
import com.android.quickstep.BaseActivityInterface;
|
||||
import com.android.quickstep.SysUINavigationMode;
|
||||
|
||||
import java.lang.annotation.Retention;
|
||||
import java.util.function.IntConsumer;
|
||||
|
@ -117,11 +120,14 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre
|
|||
MASK_MULTIPLE_ORIENTATION_SUPPORTED_BY_DEVICE | FLAG_SYSTEM_ROTATION_ALLOWED
|
||||
| FLAG_ROTATION_WATCHER_SUPPORTED | FLAG_ROTATION_WATCHER_ENABLED;
|
||||
|
||||
private SysUINavigationMode.NavigationModeChangeListener mNavModeChangeListener =
|
||||
newMode -> setFlag(FLAG_ROTATION_WATCHER_SUPPORTED, newMode != TWO_BUTTONS);
|
||||
|
||||
private final Context mContext;
|
||||
private final ContentResolver mContentResolver;
|
||||
private final SharedPreferences mSharedPrefs;
|
||||
private final OrientationEventListener mOrientationListener;
|
||||
private final WindowSizeStrategy mSizeStrategy;
|
||||
private final BaseActivityInterface mSizeStrategy;
|
||||
|
||||
private final Matrix mTmpMatrix = new Matrix();
|
||||
|
||||
|
@ -133,7 +139,7 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre
|
|||
* is enabled
|
||||
* @see #setRotationWatcherEnabled(boolean)
|
||||
*/
|
||||
public RecentsOrientedState(Context context, WindowSizeStrategy sizeStrategy,
|
||||
public RecentsOrientedState(Context context, BaseActivityInterface sizeStrategy,
|
||||
IntConsumer rotationChangeListener) {
|
||||
mContext = context;
|
||||
mContentResolver = context.getContentResolver();
|
||||
|
@ -162,13 +168,7 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre
|
|||
if (isFixedRotationTransformEnabled(context)) {
|
||||
mFlags |= FLAG_MULTIPLE_ORIENTATION_SUPPORTED_BY_FLAG;
|
||||
}
|
||||
if (mOrientationListener.canDetectOrientation()) {
|
||||
mFlags |= FLAG_ROTATION_WATCHER_SUPPORTED;
|
||||
}
|
||||
|
||||
// initialize external flags
|
||||
updateAutoRotateSetting();
|
||||
updateHomeRotationSetting();
|
||||
initFlags();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -272,6 +272,18 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre
|
|||
mSharedPrefs.getBoolean(ALLOW_ROTATION_PREFERENCE_KEY, false));
|
||||
}
|
||||
|
||||
private void initFlags() {
|
||||
SysUINavigationMode.Mode currentMode = SysUINavigationMode.getMode(mContext);
|
||||
if (mOrientationListener.canDetectOrientation() &&
|
||||
currentMode != TWO_BUTTONS) {
|
||||
mFlags |= FLAG_ROTATION_WATCHER_SUPPORTED;
|
||||
}
|
||||
|
||||
// initialize external flags
|
||||
updateAutoRotateSetting();
|
||||
updateHomeRotationSetting();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes any system values and registers corresponding change listeners. It must be
|
||||
* paired with {@link #destroyListeners()} call
|
||||
|
@ -282,9 +294,11 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre
|
|||
mContentResolver.registerContentObserver(
|
||||
Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION),
|
||||
false, mSystemAutoRotateObserver);
|
||||
SysUINavigationMode.Mode currentMode =
|
||||
SysUINavigationMode.INSTANCE.get(mContext)
|
||||
.addModeChangeListener(mNavModeChangeListener);
|
||||
}
|
||||
updateAutoRotateSetting();
|
||||
updateHomeRotationSetting();
|
||||
initFlags();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -294,6 +308,8 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre
|
|||
if (isMultipleOrientationSupportedByDevice()) {
|
||||
mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this);
|
||||
mContentResolver.unregisterContentObserver(mSystemAutoRotateObserver);
|
||||
SysUINavigationMode.INSTANCE.get(mContext)
|
||||
.removeModeChangeListener(mNavModeChangeListener);
|
||||
}
|
||||
setRotationWatcherEnabled(false);
|
||||
}
|
||||
|
|
|
@ -1,185 +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.quickstep.util;
|
||||
|
||||
import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
|
||||
import static com.android.quickstep.SysUINavigationMode.getMode;
|
||||
import static com.android.quickstep.SysUINavigationMode.removeShelfFromOverview;
|
||||
import static com.android.quickstep.util.LayoutUtils.getDefaultSwipeHeight;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Build;
|
||||
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.util.WindowBounds;
|
||||
import com.android.quickstep.SysUINavigationMode.Mode;
|
||||
|
||||
/**
|
||||
* Utility class to wrap different layout behavior for Launcher and RecentsView
|
||||
* TODO: Merge is with {@link com.android.quickstep.BaseActivityInterface} once we remove the
|
||||
* state dependent members from {@link com.android.quickstep.LauncherActivityInterface}
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.R)
|
||||
public abstract class WindowSizeStrategy {
|
||||
|
||||
public final boolean rotationSupportedByActivity;
|
||||
|
||||
private WindowSizeStrategy(boolean rotationSupportedByActivity) {
|
||||
this.rotationSupportedByActivity = rotationSupportedByActivity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the taskView size for the provided device configuration
|
||||
*/
|
||||
public final void calculateTaskSize(Context context, DeviceProfile dp, Rect outRect) {
|
||||
calculateTaskSize(context, dp, getExtraSpace(context, dp), outRect);
|
||||
}
|
||||
|
||||
abstract float getExtraSpace(Context context, DeviceProfile dp);
|
||||
|
||||
private void calculateTaskSize(
|
||||
Context context, DeviceProfile dp, float extraVerticalSpace, Rect outRect) {
|
||||
Resources res = context.getResources();
|
||||
final boolean showLargeTaskSize = showOverviewActions(context);
|
||||
|
||||
final int paddingResId;
|
||||
if (dp.isMultiWindowMode) {
|
||||
paddingResId = R.dimen.multi_window_task_card_horz_space;
|
||||
} else if (dp.isVerticalBarLayout()) {
|
||||
paddingResId = R.dimen.landscape_task_card_horz_space;
|
||||
} else if (showLargeTaskSize) {
|
||||
paddingResId = R.dimen.portrait_task_card_horz_space_big_overview;
|
||||
} else {
|
||||
paddingResId = R.dimen.portrait_task_card_horz_space;
|
||||
}
|
||||
float paddingHorz = res.getDimension(paddingResId);
|
||||
float paddingVert = showLargeTaskSize
|
||||
? 0 : res.getDimension(R.dimen.task_card_vert_space);
|
||||
|
||||
calculateTaskSizeInternal(context, dp, extraVerticalSpace, paddingHorz, paddingVert,
|
||||
res.getDimension(R.dimen.task_thumbnail_top_margin), outRect);
|
||||
}
|
||||
|
||||
private void calculateTaskSizeInternal(Context context, DeviceProfile dp,
|
||||
float extraVerticalSpace, float paddingHorz, float paddingVert, float topIconMargin,
|
||||
Rect outRect) {
|
||||
float taskWidth, taskHeight;
|
||||
Rect insets = dp.getInsets();
|
||||
if (dp.isMultiWindowMode) {
|
||||
WindowBounds bounds = SplitScreenBounds.INSTANCE.getSecondaryWindowBounds(context);
|
||||
taskWidth = bounds.availableSize.x;
|
||||
taskHeight = bounds.availableSize.y;
|
||||
} else {
|
||||
taskWidth = dp.availableWidthPx;
|
||||
taskHeight = dp.availableHeightPx;
|
||||
}
|
||||
|
||||
// Note this should be same as dp.availableWidthPx and dp.availableHeightPx unless
|
||||
// we override the insets ourselves.
|
||||
int launcherVisibleWidth = dp.widthPx - insets.left - insets.right;
|
||||
int launcherVisibleHeight = dp.heightPx - insets.top - insets.bottom;
|
||||
|
||||
float availableHeight = launcherVisibleHeight
|
||||
- topIconMargin - extraVerticalSpace - paddingVert;
|
||||
float availableWidth = launcherVisibleWidth - paddingHorz;
|
||||
|
||||
float scale = Math.min(availableWidth / taskWidth, availableHeight / taskHeight);
|
||||
float outWidth = scale * taskWidth;
|
||||
float outHeight = scale * taskHeight;
|
||||
|
||||
// Center in the visible space
|
||||
float x = insets.left + (launcherVisibleWidth - outWidth) / 2;
|
||||
float y = insets.top + Math.max(topIconMargin,
|
||||
(launcherVisibleHeight - extraVerticalSpace - outHeight) / 2);
|
||||
outRect.set(Math.round(x), Math.round(y),
|
||||
Math.round(x) + Math.round(outWidth), Math.round(y) + Math.round(outHeight));
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the modal taskView size for the provided device configuration
|
||||
*/
|
||||
public void calculateModalTaskSize(Context context, DeviceProfile dp, Rect outRect) {
|
||||
float paddingHorz = context.getResources().getDimension(dp.isMultiWindowMode
|
||||
? R.dimen.multi_window_task_card_horz_space
|
||||
: dp.isVerticalBarLayout()
|
||||
? R.dimen.landscape_task_card_horz_space
|
||||
: R.dimen.portrait_modal_task_card_horz_space);
|
||||
float extraVerticalSpace = getOverviewActionsHeight(context);
|
||||
float paddingVert = 0;
|
||||
float topIconMargin = 0;
|
||||
calculateTaskSizeInternal(context, dp, extraVerticalSpace, paddingHorz, paddingVert,
|
||||
topIconMargin, outRect);
|
||||
}
|
||||
|
||||
/** Gets the space that the overview actions will take, including margins. */
|
||||
public float getOverviewActionsHeight(Context context) {
|
||||
Resources res = context.getResources();
|
||||
float actionsBottomMargin = 0;
|
||||
if (getMode(context) == Mode.THREE_BUTTONS) {
|
||||
actionsBottomMargin = res.getDimensionPixelSize(
|
||||
R.dimen.overview_actions_bottom_margin_three_button);
|
||||
} else {
|
||||
actionsBottomMargin = res.getDimensionPixelSize(
|
||||
R.dimen.overview_actions_bottom_margin_gesture);
|
||||
}
|
||||
float overviewActionsHeight = actionsBottomMargin
|
||||
+ res.getDimensionPixelSize(R.dimen.overview_actions_height);
|
||||
return overviewActionsHeight;
|
||||
}
|
||||
|
||||
public static final WindowSizeStrategy LAUNCHER_ACTIVITY_SIZE_STRATEGY =
|
||||
new WindowSizeStrategy(true) {
|
||||
|
||||
@Override
|
||||
float getExtraSpace(Context context, DeviceProfile dp) {
|
||||
if (dp.isVerticalBarLayout()) {
|
||||
return 0;
|
||||
} else {
|
||||
Resources res = context.getResources();
|
||||
if (showOverviewActions(context)) {
|
||||
//TODO: this needs to account for the swipe gesture height and accessibility
|
||||
// UI when shown.
|
||||
return getOverviewActionsHeight(context);
|
||||
} else {
|
||||
return getDefaultSwipeHeight(context, dp) + dp.workspacePageIndicatorHeight
|
||||
+ res.getDimensionPixelSize(
|
||||
R.dimen.dynamic_grid_hotseat_extra_vertical_size)
|
||||
+ res.getDimensionPixelSize(
|
||||
R.dimen.dynamic_grid_hotseat_bottom_padding);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
public static final WindowSizeStrategy FALLBACK_RECENTS_SIZE_STRATEGY =
|
||||
new WindowSizeStrategy(false) {
|
||||
|
||||
@Override
|
||||
float getExtraSpace(Context context, DeviceProfile dp) {
|
||||
return showOverviewActions(context)
|
||||
? context.getResources().getDimensionPixelSize(R.dimen.overview_actions_height)
|
||||
: 0;
|
||||
}
|
||||
};
|
||||
|
||||
static boolean showOverviewActions(Context context) {
|
||||
return ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(context);
|
||||
}
|
||||
}
|
|
@ -69,6 +69,7 @@
|
|||
<string name="app_launch_tracker_class" translatable="false"></string>
|
||||
<string name="test_information_handler_class" translatable="false"></string>
|
||||
<string name="launcher_activity_logic_class" translatable="false"></string>
|
||||
<string name="prediction_model_class" translatable="false"></string>
|
||||
|
||||
<!-- View ID to use for QSB widget -->
|
||||
<item type="id" name="qsb_widget" />
|
||||
|
|
|
@ -107,10 +107,15 @@ public abstract class BaseActivity extends Activity implements LogStateProvider,
|
|||
*/
|
||||
public static final int ACTIVITY_STATE_USER_ACTIVE = 1 << 4;
|
||||
|
||||
/**
|
||||
* State flag indicating if the user will be active shortly.
|
||||
*/
|
||||
public static final int ACTIVITY_STATE_USER_WILL_BE_ACTIVE = 1 << 5;
|
||||
|
||||
/**
|
||||
* State flag indicating that a state transition is in progress
|
||||
*/
|
||||
public static final int ACTIVITY_STATE_TRANSITION_ACTIVE = 1 << 5;
|
||||
public static final int ACTIVITY_STATE_TRANSITION_ACTIVE = 1 << 6;
|
||||
|
||||
@Retention(SOURCE)
|
||||
@IntDef(
|
||||
|
@ -180,6 +185,7 @@ public abstract class BaseActivity extends Activity implements LogStateProvider,
|
|||
@Override
|
||||
protected void onResume() {
|
||||
addActivityFlags(ACTIVITY_STATE_RESUMED | ACTIVITY_STATE_USER_ACTIVE);
|
||||
removeActivityFlags(ACTIVITY_STATE_USER_WILL_BE_ACTIVE);
|
||||
super.onResume();
|
||||
}
|
||||
|
||||
|
|
|
@ -110,6 +110,18 @@ public interface DropTarget {
|
|||
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This is used to determine if an object is dropped at a different location than it was
|
||||
* dragged from
|
||||
*/
|
||||
public boolean isMoved() {
|
||||
return dragInfo.cellX != originalDragInfo.cellX
|
||||
|| dragInfo.cellY != originalDragInfo.cellY
|
||||
|| dragInfo.screenId != originalDragInfo.screenId
|
||||
|| dragInfo.container != originalDragInfo.container;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -91,6 +91,9 @@ public class InsettableFrameLayout extends FrameLayout implements Insettable {
|
|||
@Override
|
||||
public void onViewAdded(View child) {
|
||||
super.onViewAdded(child);
|
||||
if (!isAttachedToWindow()) {
|
||||
return;
|
||||
}
|
||||
setFrameLayoutChildInsets(child, mInsets, new Rect());
|
||||
}
|
||||
|
||||
|
|
|
@ -129,7 +129,7 @@ public class LauncherAppState {
|
|||
mIconCache = new IconCache(mContext, mInvariantDeviceProfile, iconCacheFileName);
|
||||
mWidgetCache = new WidgetPreviewLoader(mContext, mIconCache);
|
||||
mModel = new LauncherModel(this, mIconCache, AppFilter.newInstance(mContext));
|
||||
mPredictionModel = new PredictionModel(mContext);
|
||||
mPredictionModel = PredictionModel.newInstance(mContext);
|
||||
}
|
||||
|
||||
protected void onNotificationSettingsChanged(boolean areNotificationDotsEnabled) {
|
||||
|
|
|
@ -153,12 +153,16 @@ public class LauncherSettings {
|
|||
public static final int CONTAINER_PREDICTION = -102;
|
||||
public static final int CONTAINER_HOTSEAT_PREDICTION = -103;
|
||||
public static final int CONTAINER_ALL_APPS = -104;
|
||||
public static final int CONTAINER_WIDGETS_TRAY = -105;
|
||||
|
||||
|
||||
public static final String containerToString(int container) {
|
||||
switch (container) {
|
||||
case CONTAINER_DESKTOP: return "desktop";
|
||||
case CONTAINER_HOTSEAT: return "hotseat";
|
||||
case CONTAINER_PREDICTION: return "prediction";
|
||||
case CONTAINER_ALL_APPS: return "all_apps";
|
||||
case CONTAINER_WIDGETS_TRAY: return "widgets_tray";
|
||||
default: return String.valueOf(container);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,12 +18,15 @@ package com.android.launcher3;
|
|||
|
||||
import android.content.ComponentName;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
/**
|
||||
* Meta data that is used for deferred binding.
|
||||
* e.g., this object is used to pass information on draggable targets when they are dropped onto
|
||||
* the workspace from another container.
|
||||
* Meta data that is used for deferred binding. e.g., this object is used to pass information on
|
||||
* draggable targets when they are dropped onto the workspace from another container.
|
||||
*/
|
||||
public class PendingAddItemInfo extends ItemInfo {
|
||||
|
||||
|
@ -36,4 +39,22 @@ public class PendingAddItemInfo extends ItemInfo {
|
|||
protected String dumpProperties() {
|
||||
return super.dumpProperties() + " componentName=" + componentName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns shallow copy of the object.
|
||||
*/
|
||||
@Override
|
||||
public ItemInfo makeShallowCopy() {
|
||||
PendingAddItemInfo itemInfo = new PendingAddItemInfo();
|
||||
itemInfo.copyFrom(this);
|
||||
itemInfo.componentName = this.componentName;
|
||||
return itemInfo;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public ComponentName getTargetComponent() {
|
||||
return Optional.ofNullable(super.getTargetComponent()).orElse(componentName);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2438,6 +2438,10 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
|||
// widgets/shortcuts/folders in a slightly different way
|
||||
mLauncher.addPendingItem(pendingInfo, container, screenId, mTargetCell,
|
||||
item.spanX, item.spanY);
|
||||
mStatsLogManager.log(
|
||||
LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED,
|
||||
d.logInstanceId,
|
||||
d.dragInfo.buildProto(null));
|
||||
}
|
||||
};
|
||||
boolean isWidget = pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
|
||||
|
@ -2526,11 +2530,12 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
|||
mLauncher.getDragLayer().animateViewIntoPosition(d.dragView, view, this);
|
||||
resetTransitionTransform();
|
||||
}
|
||||
mStatsLogManager.log(
|
||||
LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED,
|
||||
d.logInstanceId,
|
||||
d.dragInfo.buildProto(null));
|
||||
}
|
||||
mStatsLogManager.log(
|
||||
LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED,
|
||||
d.logInstanceId,
|
||||
d.dragInfo.buildProto(null));
|
||||
|
||||
}
|
||||
|
||||
public Bitmap createWidgetBitmap(ItemInfo widgetInfo, View layout) {
|
||||
|
|
|
@ -34,6 +34,7 @@ import com.android.launcher3.LauncherState;
|
|||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.statemanager.StateManager.StateListener;
|
||||
import com.android.launcher3.touch.PagedOrientationHandler;
|
||||
import com.android.launcher3.util.OnboardingPrefs;
|
||||
|
||||
/**
|
||||
|
@ -153,16 +154,19 @@ public class DiscoveryBounce extends AbstractFloatingView {
|
|||
new DiscoveryBounce(launcher, 0).show(HOTSEAT);
|
||||
}
|
||||
|
||||
public static void showForOverviewIfNeeded(Launcher launcher) {
|
||||
showForOverviewIfNeeded(launcher, true);
|
||||
public static void showForOverviewIfNeeded(Launcher launcher,
|
||||
PagedOrientationHandler orientationHandler) {
|
||||
showForOverviewIfNeeded(launcher, true, orientationHandler);
|
||||
}
|
||||
|
||||
private static void showForOverviewIfNeeded(Launcher launcher, boolean withDelay) {
|
||||
private static void showForOverviewIfNeeded(Launcher launcher, boolean withDelay,
|
||||
PagedOrientationHandler orientationHandler) {
|
||||
OnboardingPrefs onboardingPrefs = launcher.getOnboardingPrefs();
|
||||
if (!launcher.isInState(OVERVIEW)
|
||||
|| !launcher.hasBeenResumed()
|
||||
|| launcher.isForceInvisible()
|
||||
|| launcher.getDeviceProfile().isVerticalBarLayout()
|
||||
|| !orientationHandler.isLayoutNaturalToLauncher()
|
||||
|| onboardingPrefs.getBoolean(OnboardingPrefs.SHELF_BOUNCE_SEEN)
|
||||
|| launcher.getSystemService(UserManager.class).isDemoUser()
|
||||
|| Utilities.IS_RUNNING_IN_TEST_HARNESS) {
|
||||
|
@ -170,7 +174,8 @@ public class DiscoveryBounce extends AbstractFloatingView {
|
|||
}
|
||||
|
||||
if (withDelay) {
|
||||
new Handler().postDelayed(() -> showForOverviewIfNeeded(launcher, false), DELAY_MS);
|
||||
new Handler().postDelayed(() -> showForOverviewIfNeeded(launcher, false,
|
||||
orientationHandler), DELAY_MS);
|
||||
return;
|
||||
} else if (AbstractFloatingView.getTopOpenView(launcher) != null) {
|
||||
// TODO: Move these checks to the top and call this method after invalidate handler.
|
||||
|
|
|
@ -59,6 +59,14 @@ public class LauncherAllAppsContainerView extends AllAppsContainerView {
|
|||
return super.onInterceptTouchEvent(ev);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onTouchEvent(MotionEvent ev) {
|
||||
if (!mLauncher.isInState(LauncherState.ALL_APPS)) {
|
||||
return false;
|
||||
}
|
||||
return super.onTouchEvent(ev);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setInsets(Rect insets) {
|
||||
super.setInsets(insets);
|
||||
|
|
|
@ -18,6 +18,7 @@ package com.android.launcher3.anim;
|
|||
import static com.android.launcher3.anim.AnimatorPlaybackController.addAnimationHoldersRecur;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.Animator.AnimatorListener;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.TimeInterpolator;
|
||||
|
@ -51,12 +52,8 @@ public class PendingAnimation implements PropertySetter {
|
|||
private ValueAnimator mProgressAnimator;
|
||||
|
||||
public PendingAnimation(long duration) {
|
||||
this(duration, new AnimatorSet());
|
||||
}
|
||||
|
||||
public PendingAnimation(long duration, AnimatorSet targetSet) {
|
||||
mDuration = duration;
|
||||
mAnim = targetSet;
|
||||
mAnim = new AnimatorSet();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -129,13 +126,32 @@ public class PendingAnimation implements PropertySetter {
|
|||
public void addOnFrameCallback(Runnable runnable) {
|
||||
if (mProgressAnimator == null) {
|
||||
mProgressAnimator = ValueAnimator.ofFloat(0, 1).setDuration(mDuration);
|
||||
add(mProgressAnimator);
|
||||
}
|
||||
|
||||
mProgressAnimator.addUpdateListener(anim -> runnable.run());
|
||||
}
|
||||
|
||||
public AnimatorSet getAnim() {
|
||||
/**
|
||||
* @see AnimatorSet#addListener(AnimatorListener)
|
||||
*/
|
||||
public void addListener(Animator.AnimatorListener listener) {
|
||||
mAnim.addListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns the underlying AnimatorSet
|
||||
*/
|
||||
public AnimatorSet buildAnim() {
|
||||
// Add progress animation to the end, so that frame callback is called after all the other
|
||||
// animation update.
|
||||
if (mProgressAnimator != null) {
|
||||
add(mProgressAnimator);
|
||||
mProgressAnimator = null;
|
||||
}
|
||||
if (mAnimHolders.isEmpty()) {
|
||||
// Add a dummy animation to that the duration is respected
|
||||
add(ValueAnimator.ofFloat(0, 1));
|
||||
}
|
||||
return mAnim;
|
||||
}
|
||||
|
||||
|
@ -143,7 +159,7 @@ public class PendingAnimation implements PropertySetter {
|
|||
* Creates a controller for this animation
|
||||
*/
|
||||
public AnimatorPlaybackController createPlaybackController() {
|
||||
return new AnimatorPlaybackController(mAnim, mDuration, mAnimHolders);
|
||||
return new AnimatorPlaybackController(buildAnim(), mDuration, mAnimHolders);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -18,7 +18,6 @@ package com.android.launcher3.anim;
|
|||
import static com.android.launcher3.anim.Interpolators.LINEAR;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.util.FloatProperty;
|
||||
|
@ -197,9 +196,9 @@ public class SpringAnimationBuilder {
|
|||
animator.setDuration(getDuration()).setInterpolator(LINEAR);
|
||||
animator.addUpdateListener(anim ->
|
||||
property.set(target, getInterpolatedValue(anim.getAnimatedFraction())));
|
||||
animator.addListener(new AnimatorListenerAdapter() {
|
||||
animator.addListener(new AnimationSuccessListener() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
public void onAnimationSuccess(Animator animation) {
|
||||
property.set(target, mEndValue);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -110,6 +110,9 @@ public final class FeatureFlags {
|
|||
public static final BooleanFlag ENABLE_QUICK_CAPTURE_GESTURE = getDebugFlag(
|
||||
"ENABLE_QUICK_CAPTURE_GESTURE", true, "Swipe from right to left to quick capture");
|
||||
|
||||
public static final BooleanFlag ENABLE_QUICK_CAPTURE_WINDOW = getDebugFlag(
|
||||
"ENABLE_QUICK_CAPTURE_WINDOW", false, "Use window to host quick capture");
|
||||
|
||||
public static final BooleanFlag FORCE_LOCAL_OVERSCROLL_PLUGIN = getDebugFlag(
|
||||
"FORCE_LOCAL_OVERSCROLL_PLUGIN", false,
|
||||
"Use a launcher-provided OverscrollPlugin if available");
|
||||
|
|
|
@ -18,23 +18,17 @@ package com.android.launcher3.folder;
|
|||
|
||||
import static android.text.TextUtils.isEmpty;
|
||||
|
||||
import static androidx.core.util.Preconditions.checkNotNull;
|
||||
|
||||
import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
|
||||
import static com.android.launcher3.LauncherState.NORMAL;
|
||||
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
|
||||
import static com.android.launcher3.config.FeatureFlags.ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS;
|
||||
import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_LABEL_UPDATED;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED;
|
||||
import static com.android.launcher3.model.data.FolderInfo.FLAG_MANUAL_FOLDER_NAME;
|
||||
import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_CUSTOM;
|
||||
import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_EMPTY;
|
||||
import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_FOLDER_LABEL_STATE_UNSPECIFIED;
|
||||
import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_SUGGESTED;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Arrays.stream;
|
||||
import static java.util.Optional.ofNullable;
|
||||
|
||||
import android.animation.Animator;
|
||||
|
@ -94,12 +88,6 @@ import com.android.launcher3.model.data.FolderInfo.FolderListener;
|
|||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
||||
import com.android.launcher3.pageindicators.PageIndicatorDots;
|
||||
import com.android.launcher3.userevent.LauncherLogProto.Action;
|
||||
import com.android.launcher3.userevent.LauncherLogProto.ContainerType;
|
||||
import com.android.launcher3.userevent.LauncherLogProto.ItemType;
|
||||
import com.android.launcher3.userevent.LauncherLogProto.LauncherEvent;
|
||||
import com.android.launcher3.userevent.LauncherLogProto.Target;
|
||||
import com.android.launcher3.userevent.LauncherLogProto.Target.ToFolderLabelState;
|
||||
import com.android.launcher3.userevent.nano.LauncherLogProto;
|
||||
import com.android.launcher3.util.Executors;
|
||||
import com.android.launcher3.util.Thunk;
|
||||
|
@ -111,10 +99,7 @@ import java.util.Collections;
|
|||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.OptionalInt;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
/**
|
||||
* Represents a set of icons chosen by the user or generated by the system.
|
||||
|
@ -213,8 +198,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
|||
@Thunk int mScrollHintDir = SCROLL_NONE;
|
||||
@Thunk int mCurrentScrollDir = SCROLL_NONE;
|
||||
|
||||
private String mPreviousLabel;
|
||||
private boolean mIsPreviousLabelSuggested;
|
||||
private StatsLogManager mStatsLogManager;
|
||||
|
||||
/**
|
||||
* Used to inflate the Workspace from XML.
|
||||
|
@ -227,10 +211,12 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
|||
setAlwaysDrawnWithCacheEnabled(false);
|
||||
|
||||
mLauncher = Launcher.getLauncher(context);
|
||||
mStatsLogManager = StatsLogManager.newInstance(context);
|
||||
// We need this view to be focusable in touch mode so that when text editing of the folder
|
||||
// name is complete, we have something to focus on, thus hiding the cursor and giving
|
||||
// reliable behavior when clicking the text field (since it will always gain focus on click).
|
||||
setFocusableInTouchMode(true);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -348,9 +334,9 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
|||
if (DEBUG) {
|
||||
Log.d(TAG, "onBackKey newTitle=" + newTitle);
|
||||
}
|
||||
|
||||
mInfo.title = newTitle;
|
||||
mInfo.setOption(FLAG_MANUAL_FOLDER_NAME, !getAcceptedSuggestionIndex().isPresent(),
|
||||
mInfo.setTitle(newTitle);
|
||||
mInfo.fromCustom = mInfo.hasOption(FLAG_MANUAL_FOLDER_NAME);
|
||||
mInfo.setOption(FLAG_MANUAL_FOLDER_NAME, !mInfo.getAcceptedSuggestionIndex().isPresent(),
|
||||
mLauncher.getModelWriter());
|
||||
mFolderIcon.onTitleChanged(newTitle);
|
||||
mLauncher.getModelWriter().updateItemInDatabase(mInfo);
|
||||
|
@ -441,8 +427,6 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
|||
}
|
||||
mItemsInvalidated = true;
|
||||
mInfo.addListener(this);
|
||||
Optional.ofNullable(mInfo.title).ifPresent(title -> mPreviousLabel = title.toString());
|
||||
mIsPreviousLabelSuggested = !mInfo.hasOption(FLAG_MANUAL_FOLDER_NAME);
|
||||
|
||||
if (!isEmpty(mInfo.title)) {
|
||||
mFolderName.setText(mInfo.title);
|
||||
|
@ -1347,10 +1331,8 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
|||
if (d.stateAnnouncer != null) {
|
||||
d.stateAnnouncer.completeAction(R.string.item_moved);
|
||||
}
|
||||
StatsLogManager.newInstance(getContext())
|
||||
.log(StatsLogManager.LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED,
|
||||
d.logInstanceId,
|
||||
d.dragInfo.buildProto(mInfo));
|
||||
mStatsLogManager
|
||||
.log(LAUNCHER_ITEM_DROP_COMPLETED, d.logInstanceId, d.dragInfo.buildProto(mInfo));
|
||||
}
|
||||
|
||||
// This is used so the item doesn't immediately appear in the folder when added. In one case
|
||||
|
@ -1455,7 +1437,8 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
|||
if (hasFocus) {
|
||||
startEditingFolderName();
|
||||
} else {
|
||||
logCurrentFolderLabelState();
|
||||
mStatsLogManager.log(LAUNCHER_FOLDER_LABEL_UPDATED, mInfo.buildProto());
|
||||
logFolderLabelState();
|
||||
mFolderName.dispatchBackKey();
|
||||
}
|
||||
}
|
||||
|
@ -1654,147 +1637,14 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
|
|||
return mContent;
|
||||
}
|
||||
|
||||
protected void logCurrentFolderLabelState() {
|
||||
LauncherEvent launcherEvent = LauncherEvent.newBuilder()
|
||||
.setAction(Action.newBuilder().setType(Action.Type.SOFT_KEYBOARD))
|
||||
.addSrcTarget(newEditTextTargetBuilder()
|
||||
.setFromFolderLabelState(getFromFolderLabelState())
|
||||
.setToFolderLabelState(getToFolderLabelState()))
|
||||
.addSrcTarget(newFolderTargetBuilder())
|
||||
.addSrcTarget(newParentContainerTarget())
|
||||
.build();
|
||||
mLauncher.getUserEventDispatcher().logLauncherEvent(launcherEvent);
|
||||
mPreviousLabel = mFolderName.getText().toString();
|
||||
mIsPreviousLabelSuggested = !mInfo.hasOption(FLAG_MANUAL_FOLDER_NAME);
|
||||
}
|
||||
|
||||
private Target.FromFolderLabelState getFromFolderLabelState() {
|
||||
return mPreviousLabel == null
|
||||
? FROM_FOLDER_LABEL_STATE_UNSPECIFIED
|
||||
: mPreviousLabel.isEmpty()
|
||||
? FROM_EMPTY
|
||||
: mIsPreviousLabelSuggested
|
||||
? FROM_SUGGESTED
|
||||
: FROM_CUSTOM;
|
||||
}
|
||||
|
||||
private Target.ToFolderLabelState getToFolderLabelState() {
|
||||
String newLabel =
|
||||
checkNotNull(mFolderName.getText().toString(),
|
||||
"Expected valid folder label, but found null");
|
||||
if (newLabel.equals(mPreviousLabel)) {
|
||||
return Target.ToFolderLabelState.UNCHANGED;
|
||||
}
|
||||
|
||||
if (!FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
|
||||
return newLabel.isEmpty()
|
||||
? ToFolderLabelState.TO_EMPTY_WITH_SUGGESTIONS_DISABLED
|
||||
: ToFolderLabelState.TO_CUSTOM_WITH_SUGGESTIONS_DISABLED;
|
||||
}
|
||||
|
||||
Optional<String[]> suggestedLabels = getSuggestedLabels();
|
||||
boolean isEmptySuggestions = suggestedLabels
|
||||
.map(labels -> stream(labels).allMatch(TextUtils::isEmpty))
|
||||
.orElse(true);
|
||||
if (isEmptySuggestions) {
|
||||
return newLabel.isEmpty()
|
||||
? ToFolderLabelState.TO_EMPTY_WITH_EMPTY_SUGGESTIONS
|
||||
: ToFolderLabelState.TO_CUSTOM_WITH_EMPTY_SUGGESTIONS;
|
||||
}
|
||||
|
||||
boolean hasValidPrimary = suggestedLabels
|
||||
.map(labels -> !isEmpty(labels[0]))
|
||||
.orElse(false);
|
||||
if (newLabel.isEmpty()) {
|
||||
return hasValidPrimary ? ToFolderLabelState.TO_EMPTY_WITH_VALID_PRIMARY
|
||||
: ToFolderLabelState.TO_EMPTY_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY;
|
||||
}
|
||||
|
||||
OptionalInt accepted_suggestion_index = getAcceptedSuggestionIndex();
|
||||
if (!accepted_suggestion_index.isPresent()) {
|
||||
return hasValidPrimary ? ToFolderLabelState.TO_CUSTOM_WITH_VALID_PRIMARY
|
||||
: ToFolderLabelState.TO_CUSTOM_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY;
|
||||
}
|
||||
|
||||
switch (accepted_suggestion_index.getAsInt()) {
|
||||
case 0:
|
||||
return ToFolderLabelState.TO_SUGGESTION0_WITH_VALID_PRIMARY;
|
||||
case 1:
|
||||
return hasValidPrimary ? ToFolderLabelState.TO_SUGGESTION1_WITH_VALID_PRIMARY
|
||||
: ToFolderLabelState.TO_SUGGESTION1_WITH_EMPTY_PRIMARY;
|
||||
case 2:
|
||||
return hasValidPrimary ? ToFolderLabelState.TO_SUGGESTION2_WITH_VALID_PRIMARY
|
||||
: ToFolderLabelState.TO_SUGGESTION2_WITH_EMPTY_PRIMARY;
|
||||
case 3:
|
||||
return hasValidPrimary ? ToFolderLabelState.TO_SUGGESTION3_WITH_VALID_PRIMARY
|
||||
: ToFolderLabelState.TO_SUGGESTION3_WITH_EMPTY_PRIMARY;
|
||||
default:
|
||||
// fall through
|
||||
}
|
||||
return ToFolderLabelState.TO_FOLDER_LABEL_STATE_UNSPECIFIED;
|
||||
|
||||
}
|
||||
|
||||
private Optional<String[]> getSuggestedLabels() {
|
||||
return ofNullable(mInfo)
|
||||
.map(info -> info.suggestedFolderNames)
|
||||
.map(
|
||||
folderNames ->
|
||||
(FolderNameInfo[])
|
||||
folderNames.getParcelableArrayExtra(FolderInfo.EXTRA_FOLDER_SUGGESTIONS))
|
||||
.map(
|
||||
folderNameInfoArray ->
|
||||
stream(folderNameInfoArray)
|
||||
.filter(Objects::nonNull)
|
||||
.map(FolderNameInfo::getLabel)
|
||||
.filter(Objects::nonNull)
|
||||
.map(CharSequence::toString)
|
||||
.toArray(String[]::new));
|
||||
}
|
||||
|
||||
private OptionalInt getAcceptedSuggestionIndex() {
|
||||
String newLabel = checkNotNull(mFolderName.getText().toString(),
|
||||
"Expected valid folder label, but found null");
|
||||
return getSuggestedLabels()
|
||||
.map(suggestionsArray ->
|
||||
IntStream.range(0, suggestionsArray.length)
|
||||
.filter(
|
||||
index -> !isEmpty(suggestionsArray[index])
|
||||
&& newLabel.equalsIgnoreCase(suggestionsArray[index]))
|
||||
.sequential()
|
||||
.findFirst()
|
||||
).orElse(OptionalInt.empty());
|
||||
|
||||
}
|
||||
|
||||
|
||||
private Target.Builder newEditTextTargetBuilder() {
|
||||
return Target.newBuilder().setType(Target.Type.ITEM).setItemType(ItemType.EDITTEXT);
|
||||
}
|
||||
|
||||
private Target.Builder newFolderTargetBuilder() {
|
||||
return Target.newBuilder()
|
||||
.setType(Target.Type.CONTAINER)
|
||||
.setContainerType(ContainerType.FOLDER)
|
||||
.setPageIndex(mInfo.screenId)
|
||||
.setGridX(mInfo.cellX)
|
||||
.setGridY(mInfo.cellY)
|
||||
.setCardinality(mInfo.contents.size());
|
||||
}
|
||||
|
||||
private Target.Builder newParentContainerTarget() {
|
||||
Target.Builder builder = Target.newBuilder().setType(Target.Type.CONTAINER);
|
||||
switch (mInfo.container) {
|
||||
case CONTAINER_HOTSEAT:
|
||||
return builder.setContainerType(ContainerType.HOTSEAT);
|
||||
case CONTAINER_DESKTOP:
|
||||
return builder.setContainerType(ContainerType.WORKSPACE);
|
||||
default:
|
||||
throw new AssertionError(String
|
||||
.format("Expected container to be either %s or %s but found %s.",
|
||||
CONTAINER_HOTSEAT,
|
||||
CONTAINER_DESKTOP,
|
||||
mInfo.container));
|
||||
}
|
||||
/**
|
||||
* Logs current folder label info.
|
||||
*
|
||||
* @deprecated This method is only used for log validation and soon will be removed.
|
||||
*/
|
||||
@Deprecated
|
||||
public void logFolderLabelState() {
|
||||
mLauncher.getUserEventDispatcher()
|
||||
.logLauncherEvent(mInfo.getFolderLabelStateLauncherEvent());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import static android.text.TextUtils.isEmpty;
|
|||
|
||||
import static com.android.launcher3.folder.ClippedFolderIconLayoutRule.MAX_NUM_ITEMS_IN_PREVIEW;
|
||||
import static com.android.launcher3.folder.PreviewItemManager.INITIAL_ITEM_ANIMATION_DURATION;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_LABEL_UPDATED;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
|
@ -62,6 +63,8 @@ import com.android.launcher3.dragndrop.DragLayer;
|
|||
import com.android.launcher3.dragndrop.DragView;
|
||||
import com.android.launcher3.dragndrop.DraggableView;
|
||||
import com.android.launcher3.icons.DotRenderer;
|
||||
import com.android.launcher3.logging.InstanceId;
|
||||
import com.android.launcher3.logging.StatsLogManager;
|
||||
import com.android.launcher3.model.data.AppInfo;
|
||||
import com.android.launcher3.model.data.FolderInfo;
|
||||
import com.android.launcher3.model.data.FolderInfo.FolderListener;
|
||||
|
@ -410,10 +413,10 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel
|
|||
Executors.UI_HELPER_EXECUTOR.post(() -> {
|
||||
d.folderNameProvider.getSuggestedFolderName(
|
||||
getContext(), mInfo.contents, nameInfos);
|
||||
showFinalView(finalIndex, item, nameInfos);
|
||||
showFinalView(finalIndex, item, nameInfos, d.logInstanceId);
|
||||
});
|
||||
} else {
|
||||
showFinalView(finalIndex, item, nameInfos);
|
||||
showFinalView(finalIndex, item, nameInfos, d.logInstanceId);
|
||||
}
|
||||
} else {
|
||||
addItem(item);
|
||||
|
@ -421,12 +424,12 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel
|
|||
}
|
||||
|
||||
private void showFinalView(int finalIndex, final WorkspaceItemInfo item,
|
||||
FolderNameInfo[] nameInfos) {
|
||||
FolderNameInfo[] nameInfos, InstanceId instanceId) {
|
||||
postDelayed(() -> {
|
||||
mPreviewItemManager.hidePreviewItem(finalIndex, false);
|
||||
mFolder.showItem(item);
|
||||
setLabelSuggestion(nameInfos);
|
||||
mFolder.logCurrentFolderLabelState();
|
||||
setLabelSuggestion(nameInfos, instanceId);
|
||||
mFolder.logFolderLabelState();
|
||||
invalidate();
|
||||
}, DROP_IN_ANIMATION_DURATION);
|
||||
}
|
||||
|
@ -434,7 +437,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel
|
|||
/**
|
||||
* Set the suggested folder name.
|
||||
*/
|
||||
public void setLabelSuggestion(FolderNameInfo[] nameInfos) {
|
||||
public void setLabelSuggestion(FolderNameInfo[] nameInfos, InstanceId instanceId) {
|
||||
if (!FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
|
||||
return;
|
||||
}
|
||||
|
@ -445,7 +448,9 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel
|
|||
if (nameInfos == null || nameInfos[0] == null || isEmpty(nameInfos[0].getLabel())) {
|
||||
return;
|
||||
}
|
||||
mInfo.title = nameInfos[0].getLabel();
|
||||
mInfo.setTitle(nameInfos[0].getLabel());
|
||||
StatsLogManager.newInstance(getContext())
|
||||
.log(LAUNCHER_FOLDER_LABEL_UPDATED, instanceId, mInfo.buildProto());
|
||||
onTitleChanged(mInfo.title);
|
||||
mFolder.mFolderName.setText(mInfo.title);
|
||||
mFolder.mLauncher.getModelWriter().updateItemInDatabase(mInfo);
|
||||
|
|
|
@ -81,10 +81,12 @@ public class PreviewSurfaceRenderer implements IBinder.DeathRecipient {
|
|||
binderDied();
|
||||
}
|
||||
|
||||
SurfaceControlViewHost.SurfacePackage surfacePackage;
|
||||
try {
|
||||
mSurfaceControlViewHost = MAIN_EXECUTOR
|
||||
.submit(() -> new SurfaceControlViewHost(mContext, mDisplay, mHostToken))
|
||||
.get(5, TimeUnit.SECONDS);
|
||||
surfacePackage = mSurfaceControlViewHost.getSurfacePackage();
|
||||
mHostToken.linkToDeath(this, 0);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
@ -92,6 +94,14 @@ public class PreviewSurfaceRenderer implements IBinder.DeathRecipient {
|
|||
}
|
||||
|
||||
MAIN_EXECUTOR.execute(() -> {
|
||||
// If mSurfaceControlViewHost is null due to any reason (e.g. binder died,
|
||||
// happening when user leaves the preview screen before preview rendering finishes),
|
||||
// we should return here.
|
||||
SurfaceControlViewHost host = mSurfaceControlViewHost;
|
||||
if (host == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
View view = new LauncherPreviewRenderer(mContext, mIdp).getRenderedView();
|
||||
// This aspect scales the view to fit in the surface and centers it
|
||||
final float scale = Math.min(mWidth / (float) view.getMeasuredWidth(),
|
||||
|
@ -107,14 +117,14 @@ public class PreviewSurfaceRenderer implements IBinder.DeathRecipient {
|
|||
.setInterpolator(new AccelerateDecelerateInterpolator())
|
||||
.setDuration(FADE_IN_ANIMATION_DURATION)
|
||||
.start();
|
||||
mSurfaceControlViewHost.setView(view, view.getMeasuredWidth(),
|
||||
host.setView(view, view.getMeasuredWidth(),
|
||||
view.getMeasuredHeight());
|
||||
});
|
||||
|
||||
Bundle result = new Bundle();
|
||||
result.putParcelable(KEY_SURFACE_PACKAGE, mSurfaceControlViewHost.getSurfacePackage());
|
||||
result.putParcelable(KEY_SURFACE_PACKAGE, surfacePackage);
|
||||
|
||||
Handler handler = new Handler(Looper.getMainLooper(), Loopermessage -> {
|
||||
Handler handler = new Handler(Looper.getMainLooper(), message -> {
|
||||
binderDied();
|
||||
return true;
|
||||
});
|
||||
|
@ -128,8 +138,10 @@ public class PreviewSurfaceRenderer implements IBinder.DeathRecipient {
|
|||
@Override
|
||||
public void binderDied() {
|
||||
if (mSurfaceControlViewHost != null) {
|
||||
mSurfaceControlViewHost.release();
|
||||
mSurfaceControlViewHost = null;
|
||||
MAIN_EXECUTOR.execute(() -> {
|
||||
mSurfaceControlViewHost.release();
|
||||
mSurfaceControlViewHost = null;
|
||||
});
|
||||
}
|
||||
mHostToken.unlinkToDeath(this, 0);
|
||||
}
|
||||
|
|
|
@ -16,10 +16,8 @@
|
|||
package com.android.launcher3.logging;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.logger.LauncherAtom;
|
||||
import com.android.launcher3.logger.LauncherAtom.ItemInfo;
|
||||
import com.android.launcher3.logging.StatsLogUtils.LogStateProvider;
|
||||
import com.android.launcher3.util.ResourceBasedOverride;
|
||||
|
@ -32,61 +30,65 @@ import com.android.launcher3.util.ResourceBasedOverride;
|
|||
*/
|
||||
public class StatsLogManager implements ResourceBasedOverride {
|
||||
|
||||
private static final String TAG = "StatsLogManager";
|
||||
|
||||
interface EventEnum {
|
||||
int getId();
|
||||
}
|
||||
|
||||
public enum LauncherEvent implements EventEnum {
|
||||
@LauncherUiEvent(doc = "App launched from workspace, hotseat or folder in launcher")
|
||||
@UiEvent(doc = "App launched from workspace, hotseat or folder in launcher")
|
||||
LAUNCHER_APP_LAUNCH_TAP(338),
|
||||
|
||||
@LauncherUiEvent(doc = "Task launched from overview using TAP")
|
||||
@UiEvent(doc = "Task launched from overview using TAP")
|
||||
LAUNCHER_TASK_LAUNCH_TAP(339),
|
||||
|
||||
@LauncherUiEvent(doc = "Task launched from overview using SWIPE DOWN")
|
||||
@UiEvent(doc = "Task launched from overview using SWIPE DOWN")
|
||||
LAUNCHER_TASK_LAUNCH_SWIPE_DOWN(340),
|
||||
|
||||
@LauncherUiEvent(doc = "TASK dismissed from overview using SWIPE UP")
|
||||
@UiEvent(doc = "TASK dismissed from overview using SWIPE UP")
|
||||
LAUNCHER_TASK_DISMISS_SWIPE_UP(341),
|
||||
|
||||
@LauncherUiEvent(doc = "User dragged a launcher item")
|
||||
@UiEvent(doc = "User dragged a launcher item")
|
||||
LAUNCHER_ITEM_DRAG_STARTED(383),
|
||||
|
||||
@LauncherUiEvent(doc = "A dragged launcher item is successfully dropped")
|
||||
@UiEvent(doc = "A dragged launcher item is successfully dropped")
|
||||
LAUNCHER_ITEM_DROP_COMPLETED(385),
|
||||
|
||||
@LauncherUiEvent(doc = "A dragged launcher item is successfully dropped on another item "
|
||||
@UiEvent(doc = "A dragged launcher item is successfully dropped on another item "
|
||||
+ "resulting in a new folder creation")
|
||||
LAUNCHER_ITEM_DROP_FOLDER_CREATED(386),
|
||||
|
||||
@LauncherUiEvent(doc = "A dragged item is dropped on 'Remove' button in the target bar")
|
||||
@UiEvent(doc = "User action resulted in or manually updated the folder label to "
|
||||
+ "new/same value.")
|
||||
LAUNCHER_FOLDER_LABEL_UPDATED(460),
|
||||
|
||||
@UiEvent(doc = "A dragged item is dropped on 'Remove' button in the target bar")
|
||||
LAUNCHER_ITEM_DROPPED_ON_REMOVE(465),
|
||||
|
||||
@LauncherUiEvent(doc = "A dragged item is dropped on 'Cancel' button in the target bar")
|
||||
@UiEvent(doc = "A dragged item is dropped on 'Cancel' button in the target bar")
|
||||
LAUNCHER_ITEM_DROPPED_ON_CANCEL(466),
|
||||
|
||||
@LauncherUiEvent(doc = "A predicted item is dragged and dropped on 'Don't suggest app'"
|
||||
@UiEvent(doc = "A predicted item is dragged and dropped on 'Don't suggest app'"
|
||||
+ " button in the target bar")
|
||||
LAUNCHER_ITEM_DROPPED_ON_DONT_SUGGEST(467),
|
||||
|
||||
@LauncherUiEvent(doc = "A dragged item is dropped on 'Uninstall' button in target bar")
|
||||
@UiEvent(doc = "A dragged item is dropped on 'Uninstall' button in target bar")
|
||||
LAUNCHER_ITEM_DROPPED_ON_UNINSTALL(468),
|
||||
|
||||
@LauncherUiEvent(doc = "User completed uninstalling the package after dropping on "
|
||||
@UiEvent(doc = "User completed uninstalling the package after dropping on "
|
||||
+ "the icon onto 'Uninstall' button in the target bar")
|
||||
LAUNCHER_ITEM_UNINSTALL_COMPLETED(469),
|
||||
|
||||
@LauncherUiEvent(doc = "User cancelled uninstalling the package after dropping on "
|
||||
@UiEvent(doc = "User cancelled uninstalling the package after dropping on "
|
||||
+ "the icon onto 'Uninstall' button in the target bar")
|
||||
LAUNCHER_ITEM_UNINSTALL_CANCELLED(470);
|
||||
// ADD MORE
|
||||
|
||||
private final int mId;
|
||||
|
||||
LauncherEvent(int id) {
|
||||
mId = id;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return mId;
|
||||
}
|
||||
|
@ -109,22 +111,32 @@ public class StatsLogManager implements ResourceBasedOverride {
|
|||
}
|
||||
|
||||
/**
|
||||
* Logs an event and accompanying {@link ItemInfo}
|
||||
* Logs a {@link LauncherEvent}.
|
||||
*/
|
||||
public void log(LauncherEvent event, InstanceId instanceId) {
|
||||
Log.d(TAG, String.format("%s(InstanceId:%s)", event.name(), instanceId));
|
||||
// Call StatsLog method
|
||||
public void log(LauncherEvent event) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an event and accompanying {@link LauncherAtom.ItemInfo}
|
||||
* Logs an event and accompanying {@link InstanceId}.
|
||||
*/
|
||||
public void log(LauncherEvent event, InstanceId instanceId, LauncherAtom.ItemInfo itemInfo) { }
|
||||
public void log(LauncherEvent event, LauncherAtom.ItemInfo itemInfo) { }
|
||||
public void log(LauncherEvent event, InstanceId instanceId) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an event and accompanying {@link ItemInfo}.
|
||||
*/
|
||||
public void log(LauncherEvent event, ItemInfo itemInfo) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs an event and accompanying {@link InstanceId} and {@link ItemInfo}.
|
||||
*/
|
||||
public void log(LauncherEvent event, InstanceId instanceId, ItemInfo itemInfo) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Logs snapshot, or impression of the current workspace.
|
||||
*/
|
||||
public void logSnapshot() { }
|
||||
public void logSnapshot() {
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.launcher3.logging;
|
||||
|
||||
import static java.lang.annotation.ElementType.FIELD;
|
||||
|
@ -23,8 +24,11 @@ import java.lang.annotation.Target;
|
|||
|
||||
@Retention(SOURCE)
|
||||
@Target(FIELD)
|
||||
public @interface LauncherUiEvent {
|
||||
/** An explanation, suitable for Android analysts, of the UI event that this log represents. */
|
||||
// Copy of frameworks/base/core/java/com/android/internal/logging/UiEvent.java
|
||||
public @interface UiEvent {
|
||||
|
||||
/**
|
||||
* An explanation, suitable for Android analysts, of the UI event that this log represents.
|
||||
*/
|
||||
String doc();
|
||||
}
|
||||
|
|
@ -253,8 +253,8 @@ public abstract class BaseLoaderResults {
|
|||
}
|
||||
|
||||
private void bindPredictedItems(IntArray ranks, final Executor executor) {
|
||||
executeCallbacksTask(
|
||||
c -> c.bindPredictedItems(mBgDataModel.cachedPredictedItems, ranks), executor);
|
||||
ArrayList<AppInfo> items = new ArrayList<>(mBgDataModel.cachedPredictedItems);
|
||||
executeCallbacksTask(c -> c.bindPredictedItems(items, ranks), executor);
|
||||
}
|
||||
|
||||
protected void executeCallbacksTask(CallbackTask task, Executor executor) {
|
||||
|
|
|
@ -45,6 +45,8 @@ import android.util.LongSparseArray;
|
|||
import android.util.MutableInt;
|
||||
import android.util.TimingLogger;
|
||||
|
||||
import androidx.annotation.WorkerThread;
|
||||
|
||||
import com.android.launcher3.InstallShortcutReceiver;
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.LauncherModel;
|
||||
|
@ -850,12 +852,11 @@ public class LoaderTask implements Runnable {
|
|||
}
|
||||
}
|
||||
|
||||
private List<AppInfo> loadCachedPredictions() {
|
||||
@WorkerThread
|
||||
private void loadCachedPredictions() {
|
||||
synchronized (mBgDataModel) {
|
||||
List<ComponentKey> componentKeys =
|
||||
mApp.getPredictionModel().getPredictionComponentKeys();
|
||||
List<AppInfo> results = new ArrayList<>();
|
||||
if (componentKeys == null) return results;
|
||||
List<LauncherActivityInfo> l;
|
||||
mBgDataModel.cachedPredictedItems.clear();
|
||||
for (ComponentKey key : componentKeys) {
|
||||
|
@ -866,7 +867,6 @@ public class LoaderTask implements Runnable {
|
|||
mBgDataModel.cachedPredictedItems.add(info);
|
||||
mIconCache.getTitleAndIcon(info, false);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -14,80 +14,111 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
package com.android.launcher3.model;
|
||||
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
|
||||
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.UserHandle;
|
||||
|
||||
import androidx.annotation.AnyThread;
|
||||
import androidx.annotation.WorkerThread;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.model.data.AppInfo;
|
||||
import com.android.launcher3.pm.UserCache;
|
||||
import com.android.launcher3.util.ComponentKey;
|
||||
import com.android.launcher3.util.Preconditions;
|
||||
import com.android.launcher3.util.ResourceBasedOverride;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Model helper for app predictions in workspace
|
||||
* Model Helper for app predictions
|
||||
*/
|
||||
public class PredictionModel {
|
||||
public class PredictionModel implements ResourceBasedOverride {
|
||||
|
||||
private static final String CACHED_ITEMS_KEY = "predicted_item_keys";
|
||||
private static final int MAX_CACHE_ITEMS = 5;
|
||||
|
||||
private final Context mContext;
|
||||
private final SharedPreferences mDevicePrefs;
|
||||
private ArrayList<ComponentKey> mCachedComponentKeys;
|
||||
protected Context mContext;
|
||||
private SharedPreferences mDevicePrefs;
|
||||
private UserCache mUserCache;
|
||||
|
||||
public PredictionModel(Context context) {
|
||||
mContext = context;
|
||||
mDevicePrefs = Utilities.getDevicePrefs(mContext);
|
||||
|
||||
/**
|
||||
* Retrieve instance of this object that can be overridden in runtime based on the build
|
||||
* variant of the application.
|
||||
*/
|
||||
public static PredictionModel newInstance(Context context) {
|
||||
PredictionModel model = Overrides.getObject(PredictionModel.class, context,
|
||||
R.string.prediction_model_class);
|
||||
model.init(context);
|
||||
return model;
|
||||
}
|
||||
|
||||
protected void init(Context context) {
|
||||
mContext = context;
|
||||
mDevicePrefs = Utilities.getDevicePrefs(mContext);
|
||||
mUserCache = UserCache.INSTANCE.get(mContext);
|
||||
|
||||
}
|
||||
/**
|
||||
* Formats and stores a list of component key in device preferences.
|
||||
*/
|
||||
@AnyThread
|
||||
public void cachePredictionComponentKeys(List<ComponentKey> componentKeys) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
int count = Math.min(componentKeys.size(), MAX_CACHE_ITEMS);
|
||||
for (int i = 0; i < count; i++) {
|
||||
builder.append(componentKeys.get(i));
|
||||
builder.append("\n");
|
||||
}
|
||||
mDevicePrefs.edit().putString(CACHED_ITEMS_KEY, builder.toString()).apply();
|
||||
mCachedComponentKeys = null;
|
||||
MODEL_EXECUTOR.execute(() -> {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
int count = Math.min(componentKeys.size(), MAX_CACHE_ITEMS);
|
||||
for (int i = 0; i < count; i++) {
|
||||
builder.append(serializeComponentKeyToString(componentKeys.get(i)));
|
||||
builder.append("\n");
|
||||
}
|
||||
mDevicePrefs.edit().putString(CACHED_ITEMS_KEY, builder.toString()).apply();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* parses and returns ComponentKeys saved by
|
||||
* {@link PredictionModel#cachePredictionComponentKeys(List)}
|
||||
*/
|
||||
@WorkerThread
|
||||
public List<ComponentKey> getPredictionComponentKeys() {
|
||||
if (mCachedComponentKeys == null) {
|
||||
mCachedComponentKeys = new ArrayList<>();
|
||||
|
||||
String cachedBlob = mDevicePrefs.getString(CACHED_ITEMS_KEY, "");
|
||||
for (String line : cachedBlob.split("\n")) {
|
||||
ComponentKey key = ComponentKey.fromString(line);
|
||||
if (key != null) {
|
||||
mCachedComponentKeys.add(key);
|
||||
}
|
||||
Preconditions.assertWorkerThread();
|
||||
ArrayList<ComponentKey> items = new ArrayList<>();
|
||||
String cachedBlob = mDevicePrefs.getString(CACHED_ITEMS_KEY, "");
|
||||
for (String line : cachedBlob.split("\n")) {
|
||||
ComponentKey key = getComponentKeyFromSerializedString(line);
|
||||
if (key != null) {
|
||||
items.add(key);
|
||||
}
|
||||
|
||||
}
|
||||
return mCachedComponentKeys;
|
||||
return items;
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove uninstalled applications from model
|
||||
*/
|
||||
public void removePackage(String pkgName, UserHandle user, ArrayList<AppInfo> ids) {
|
||||
for (int i = ids.size() - 1; i >= 0; i--) {
|
||||
AppInfo info = ids.get(i);
|
||||
if (info.user.equals(user) && pkgName.equals(info.componentName.getPackageName())) {
|
||||
ids.remove(i);
|
||||
}
|
||||
private String serializeComponentKeyToString(ComponentKey componentKey) {
|
||||
long userSerialNumber = mUserCache.getSerialNumberForUser(componentKey.user);
|
||||
return componentKey.componentName.flattenToString() + "#" + userSerialNumber;
|
||||
}
|
||||
|
||||
private ComponentKey getComponentKeyFromSerializedString(String str) {
|
||||
int sep = str.indexOf('#');
|
||||
if (sep < 0 || (sep + 1) >= str.length()) {
|
||||
return null;
|
||||
}
|
||||
ComponentName componentName = ComponentName.unflattenFromString(str.substring(0, sep));
|
||||
if (componentName == null) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
long serialNumber = Long.parseLong(str.substring(sep + 1));
|
||||
UserHandle userHandle = mUserCache.getUserForSerialNumber(serialNumber);
|
||||
return userHandle != null ? new ComponentKey(componentName, userHandle) : null;
|
||||
} catch (NumberFormatException ex) {
|
||||
return null;
|
||||
}
|
||||
cachePredictionComponentKeys(getPredictionComponentKeys().stream()
|
||||
.filter(cn -> !(cn.user.equals(user) && cn.componentName.getPackageName().equals(
|
||||
pkgName))).collect(Collectors.toList()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,16 +16,45 @@
|
|||
|
||||
package com.android.launcher3.model.data;
|
||||
|
||||
import static android.text.TextUtils.isEmpty;
|
||||
|
||||
import static androidx.core.util.Preconditions.checkNotNull;
|
||||
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
|
||||
import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_CUSTOM;
|
||||
import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_EMPTY;
|
||||
import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_FOLDER_LABEL_STATE_UNSPECIFIED;
|
||||
import static com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState.FROM_SUGGESTED;
|
||||
|
||||
import static java.util.Arrays.stream;
|
||||
import static java.util.Optional.ofNullable;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Process;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.android.launcher3.LauncherSettings;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.folder.FolderNameInfo;
|
||||
import com.android.launcher3.logger.LauncherAtom;
|
||||
import com.android.launcher3.logger.LauncherAtom.FromState;
|
||||
import com.android.launcher3.logger.LauncherAtom.ToState;
|
||||
import com.android.launcher3.model.ModelWriter;
|
||||
import com.android.launcher3.userevent.LauncherLogProto;
|
||||
import com.android.launcher3.userevent.LauncherLogProto.Target;
|
||||
import com.android.launcher3.userevent.LauncherLogProto.Target.FromFolderLabelState;
|
||||
import com.android.launcher3.userevent.LauncherLogProto.Target.ToFolderLabelState;
|
||||
import com.android.launcher3.util.ContentWriter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.OptionalInt;
|
||||
import java.util.StringJoiner;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
|
||||
/**
|
||||
* Represents a folder containing shortcuts or apps.
|
||||
|
@ -57,6 +86,20 @@ public class FolderInfo extends ItemInfo {
|
|||
|
||||
public Intent suggestedFolderNames;
|
||||
|
||||
// Represents the title before current.
|
||||
// Primarily used for logging purpose.
|
||||
private CharSequence mPreviousTitle;
|
||||
|
||||
// True if the title before was manually entered, suggested otherwise.
|
||||
// Primarily used for logging purpose.
|
||||
public boolean fromCustom;
|
||||
|
||||
/**
|
||||
* Used for separating {@link #mPreviousTitle} and {@link #title} when concatenating them
|
||||
* for logging.
|
||||
*/
|
||||
private static final CharSequence FOLDER_LABEL_DELIMITER = "=>";
|
||||
|
||||
/**
|
||||
* The apps and shortcuts
|
||||
*/
|
||||
|
@ -160,9 +203,20 @@ public class FolderInfo extends ItemInfo {
|
|||
@Override
|
||||
public LauncherAtom.ItemInfo buildProto(FolderInfo fInfo) {
|
||||
return getDefaultItemInfoBuilder()
|
||||
.setFolderIcon(LauncherAtom.FolderIcon.newBuilder().setCardinality(contents.size()))
|
||||
.setContainerInfo(getContainerInfo())
|
||||
.build();
|
||||
.setFolderIcon(LauncherAtom.FolderIcon.newBuilder().setCardinality(contents.size()))
|
||||
.setRank(rank)
|
||||
.setContainerInfo(getContainerInfo())
|
||||
.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setTitle(CharSequence title) {
|
||||
mPreviousTitle = this.title;
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public CharSequence getPreviousTitle() {
|
||||
return mPreviousTitle;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -172,4 +226,244 @@ public class FolderInfo extends ItemInfo {
|
|||
folderInfo.contents = this.contents;
|
||||
return folderInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link LauncherAtom.FolderIcon} wrapped as {@link LauncherAtom.ItemInfo} for logging.
|
||||
*/
|
||||
@Override
|
||||
public LauncherAtom.ItemInfo buildProto() {
|
||||
FromState fromFolderLabelState = getFromFolderLabelState();
|
||||
ToState toFolderLabelState = getToFolderLabelState();
|
||||
LauncherAtom.FolderIcon.Builder folderIconBuilder = LauncherAtom.FolderIcon.newBuilder()
|
||||
.setCardinality(contents.size())
|
||||
.setFromLabelState(fromFolderLabelState)
|
||||
.setToLabelState(toFolderLabelState);
|
||||
|
||||
// If the folder label is suggested, it is logged to improve prediction model.
|
||||
// When both old and new labels are logged together delimiter is used.
|
||||
StringJoiner labelInfoBuilder = new StringJoiner(FOLDER_LABEL_DELIMITER);
|
||||
if (fromFolderLabelState.equals(FromState.FROM_SUGGESTED)) {
|
||||
labelInfoBuilder.add(mPreviousTitle);
|
||||
}
|
||||
if (toFolderLabelState.toString().startsWith("TO_SUGGESTION")) {
|
||||
labelInfoBuilder.add(title);
|
||||
}
|
||||
if (labelInfoBuilder.length() > 0) {
|
||||
folderIconBuilder.setLabelInfo(labelInfoBuilder.toString());
|
||||
}
|
||||
|
||||
return getDefaultItemInfoBuilder()
|
||||
.setFolderIcon(folderIconBuilder)
|
||||
.setContainerInfo(getContainerInfo())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns index of the accepted suggestion.
|
||||
*/
|
||||
public OptionalInt getAcceptedSuggestionIndex() {
|
||||
String newLabel = checkNotNull(title,
|
||||
"Expected valid folder label, but found null").toString();
|
||||
return getSuggestedLabels()
|
||||
.map(suggestionsArray ->
|
||||
IntStream.range(0, suggestionsArray.length)
|
||||
.filter(
|
||||
index -> !isEmpty(suggestionsArray[index])
|
||||
&& newLabel.equalsIgnoreCase(
|
||||
suggestionsArray[index]))
|
||||
.sequential()
|
||||
.findFirst()
|
||||
).orElse(OptionalInt.empty());
|
||||
|
||||
}
|
||||
|
||||
private LauncherAtom.ToState getToFolderLabelState() {
|
||||
if (title == null) {
|
||||
return LauncherAtom.ToState.TO_STATE_UNSPECIFIED;
|
||||
}
|
||||
|
||||
if (title.equals(mPreviousTitle)) {
|
||||
return LauncherAtom.ToState.UNCHANGED;
|
||||
}
|
||||
|
||||
if (!FeatureFlags.FOLDER_NAME_SUGGEST.get()) {
|
||||
return title.length() > 0
|
||||
? LauncherAtom.ToState.TO_CUSTOM_WITH_SUGGESTIONS_DISABLED
|
||||
: LauncherAtom.ToState.TO_EMPTY_WITH_SUGGESTIONS_DISABLED;
|
||||
}
|
||||
|
||||
Optional<String[]> suggestedLabels = getSuggestedLabels();
|
||||
boolean isEmptySuggestions = suggestedLabels
|
||||
.map(labels -> stream(labels).allMatch(TextUtils::isEmpty))
|
||||
.orElse(true);
|
||||
if (isEmptySuggestions) {
|
||||
return title.length() > 0
|
||||
? LauncherAtom.ToState.TO_CUSTOM_WITH_EMPTY_SUGGESTIONS
|
||||
: LauncherAtom.ToState.TO_EMPTY_WITH_EMPTY_SUGGESTIONS;
|
||||
}
|
||||
|
||||
boolean hasValidPrimary = suggestedLabels
|
||||
.map(labels -> !isEmpty(labels[0]))
|
||||
.orElse(false);
|
||||
if (title.length() == 0) {
|
||||
return hasValidPrimary ? LauncherAtom.ToState.TO_EMPTY_WITH_VALID_PRIMARY
|
||||
: LauncherAtom.ToState.TO_EMPTY_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY;
|
||||
}
|
||||
|
||||
OptionalInt accepted_suggestion_index = getAcceptedSuggestionIndex();
|
||||
if (!accepted_suggestion_index.isPresent()) {
|
||||
return hasValidPrimary ? LauncherAtom.ToState.TO_CUSTOM_WITH_VALID_PRIMARY
|
||||
: LauncherAtom.ToState.TO_CUSTOM_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY;
|
||||
}
|
||||
|
||||
switch (accepted_suggestion_index.getAsInt()) {
|
||||
case 0:
|
||||
return LauncherAtom.ToState.TO_SUGGESTION0;
|
||||
case 1:
|
||||
return hasValidPrimary ? LauncherAtom.ToState.TO_SUGGESTION1_WITH_VALID_PRIMARY
|
||||
: LauncherAtom.ToState.TO_SUGGESTION1_WITH_EMPTY_PRIMARY;
|
||||
case 2:
|
||||
return hasValidPrimary ? LauncherAtom.ToState.TO_SUGGESTION2_WITH_VALID_PRIMARY
|
||||
: LauncherAtom.ToState.TO_SUGGESTION2_WITH_EMPTY_PRIMARY;
|
||||
case 3:
|
||||
return hasValidPrimary ? LauncherAtom.ToState.TO_SUGGESTION3_WITH_VALID_PRIMARY
|
||||
: LauncherAtom.ToState.TO_SUGGESTION3_WITH_EMPTY_PRIMARY;
|
||||
default:
|
||||
// fall through
|
||||
}
|
||||
return LauncherAtom.ToState.TO_STATE_UNSPECIFIED;
|
||||
|
||||
}
|
||||
|
||||
private LauncherAtom.FromState getFromFolderLabelState() {
|
||||
return mPreviousTitle == null
|
||||
? LauncherAtom.FromState.FROM_STATE_UNSPECIFIED
|
||||
: mPreviousTitle.length() == 0
|
||||
? LauncherAtom.FromState.FROM_EMPTY
|
||||
: fromCustom
|
||||
? LauncherAtom.FromState.FROM_CUSTOM
|
||||
: LauncherAtom.FromState.FROM_SUGGESTED;
|
||||
}
|
||||
|
||||
private Optional<String[]> getSuggestedLabels() {
|
||||
return ofNullable(suggestedFolderNames)
|
||||
.map(folderNames ->
|
||||
(FolderNameInfo[])
|
||||
folderNames.getParcelableArrayExtra(EXTRA_FOLDER_SUGGESTIONS))
|
||||
.map(folderNameInfoArray ->
|
||||
stream(folderNameInfoArray)
|
||||
.filter(Objects::nonNull)
|
||||
.map(FolderNameInfo::getLabel)
|
||||
.filter(Objects::nonNull)
|
||||
.map(CharSequence::toString)
|
||||
.toArray(String[]::new));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns {@link LauncherLogProto.LauncherEvent} to log current folder label info.
|
||||
*
|
||||
* @deprecated This method is used only for validation purpose and soon will be removed.
|
||||
*/
|
||||
@Deprecated
|
||||
public LauncherLogProto.LauncherEvent getFolderLabelStateLauncherEvent() {
|
||||
return LauncherLogProto.LauncherEvent.newBuilder()
|
||||
.setAction(LauncherLogProto.Action
|
||||
.newBuilder()
|
||||
.setType(LauncherLogProto.Action.Type.SOFT_KEYBOARD))
|
||||
.addSrcTarget(Target
|
||||
.newBuilder()
|
||||
.setType(Target.Type.ITEM)
|
||||
.setItemType(LauncherLogProto.ItemType.EDITTEXT)
|
||||
.setFromFolderLabelState(convertFolderLabelState(getFromFolderLabelState()))
|
||||
.setToFolderLabelState(convertFolderLabelState(getToFolderLabelState())))
|
||||
.addSrcTarget(Target.newBuilder()
|
||||
.setType(Target.Type.CONTAINER)
|
||||
.setContainerType(LauncherLogProto.ContainerType.FOLDER)
|
||||
.setPageIndex(screenId)
|
||||
.setGridX(cellX)
|
||||
.setGridY(cellY)
|
||||
.setCardinality(contents.size()))
|
||||
.addSrcTarget(newParentContainerTarget())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated This method is used only for validation purpose and soon will be removed.
|
||||
*/
|
||||
@Deprecated
|
||||
private Target.Builder newParentContainerTarget() {
|
||||
Target.Builder builder = Target.newBuilder().setType(Target.Type.CONTAINER);
|
||||
switch (container) {
|
||||
case CONTAINER_HOTSEAT:
|
||||
return builder.setContainerType(LauncherLogProto.ContainerType.HOTSEAT);
|
||||
case CONTAINER_DESKTOP:
|
||||
return builder.setContainerType(LauncherLogProto.ContainerType.WORKSPACE);
|
||||
default:
|
||||
throw new AssertionError(String
|
||||
.format("Expected container to be either %s or %s but found %s.",
|
||||
CONTAINER_HOTSEAT,
|
||||
CONTAINER_DESKTOP,
|
||||
container));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated This method is used only for validation purpose and soon will be removed.
|
||||
*/
|
||||
@Deprecated
|
||||
private static FromFolderLabelState convertFolderLabelState(FromState fromState) {
|
||||
switch (fromState) {
|
||||
case FROM_EMPTY:
|
||||
return FROM_EMPTY;
|
||||
case FROM_SUGGESTED:
|
||||
return FROM_SUGGESTED;
|
||||
case FROM_CUSTOM:
|
||||
return FROM_CUSTOM;
|
||||
default:
|
||||
return FROM_FOLDER_LABEL_STATE_UNSPECIFIED;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated This method is used only for validation purpose and soon will be removed.
|
||||
*/
|
||||
@Deprecated
|
||||
private static ToFolderLabelState convertFolderLabelState(ToState toState) {
|
||||
switch (toState) {
|
||||
case UNCHANGED:
|
||||
return ToFolderLabelState.UNCHANGED;
|
||||
case TO_SUGGESTION0:
|
||||
return ToFolderLabelState.TO_SUGGESTION0_WITH_VALID_PRIMARY;
|
||||
case TO_SUGGESTION1_WITH_VALID_PRIMARY:
|
||||
return ToFolderLabelState.TO_SUGGESTION1_WITH_VALID_PRIMARY;
|
||||
case TO_SUGGESTION1_WITH_EMPTY_PRIMARY:
|
||||
return ToFolderLabelState.TO_SUGGESTION1_WITH_EMPTY_PRIMARY;
|
||||
case TO_SUGGESTION2_WITH_VALID_PRIMARY:
|
||||
return ToFolderLabelState.TO_SUGGESTION2_WITH_VALID_PRIMARY;
|
||||
case TO_SUGGESTION2_WITH_EMPTY_PRIMARY:
|
||||
return ToFolderLabelState.TO_SUGGESTION2_WITH_EMPTY_PRIMARY;
|
||||
case TO_SUGGESTION3_WITH_VALID_PRIMARY:
|
||||
return ToFolderLabelState.TO_SUGGESTION3_WITH_VALID_PRIMARY;
|
||||
case TO_SUGGESTION3_WITH_EMPTY_PRIMARY:
|
||||
return ToFolderLabelState.TO_SUGGESTION3_WITH_EMPTY_PRIMARY;
|
||||
case TO_EMPTY_WITH_VALID_PRIMARY:
|
||||
return ToFolderLabelState.TO_EMPTY_WITH_VALID_PRIMARY;
|
||||
case TO_EMPTY_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY:
|
||||
return ToFolderLabelState.TO_EMPTY_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY;
|
||||
case TO_EMPTY_WITH_EMPTY_SUGGESTIONS:
|
||||
return ToFolderLabelState.TO_EMPTY_WITH_EMPTY_SUGGESTIONS;
|
||||
case TO_EMPTY_WITH_SUGGESTIONS_DISABLED:
|
||||
return ToFolderLabelState.TO_EMPTY_WITH_SUGGESTIONS_DISABLED;
|
||||
case TO_CUSTOM_WITH_VALID_PRIMARY:
|
||||
return ToFolderLabelState.TO_CUSTOM_WITH_VALID_PRIMARY;
|
||||
case TO_CUSTOM_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY:
|
||||
return ToFolderLabelState.TO_CUSTOM_WITH_VALID_SUGGESTIONS_AND_EMPTY_PRIMARY;
|
||||
case TO_CUSTOM_WITH_EMPTY_SUGGESTIONS:
|
||||
return ToFolderLabelState.TO_CUSTOM_WITH_EMPTY_SUGGESTIONS;
|
||||
case TO_CUSTOM_WITH_SUGGESTIONS_DISABLED:
|
||||
return ToFolderLabelState.TO_CUSTOM_WITH_SUGGESTIONS_DISABLED;
|
||||
default:
|
||||
return ToFolderLabelState.TO_FOLDER_LABEL_STATE_UNSPECIFIED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -20,6 +20,7 @@ import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_ALL_APP
|
|||
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_TRAY;
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION;
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET;
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
|
||||
|
@ -249,6 +250,13 @@ public class ItemInfo {
|
|||
public void setItemBuilder(LauncherAtom.ItemInfo.Builder builder) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates {@link LauncherAtom.ItemInfo} with important fields and parent container info.
|
||||
*/
|
||||
public LauncherAtom.ItemInfo buildProto() {
|
||||
return buildProto(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates {@link LauncherAtom.ItemInfo} with important fields and parent container info.
|
||||
*/
|
||||
|
@ -335,6 +343,11 @@ public class ItemInfo {
|
|||
.setAllAppsContainer(
|
||||
AllAppsContainer.getDefaultInstance())
|
||||
.build();
|
||||
case CONTAINER_WIDGETS_TRAY:
|
||||
return ContainerInfo.newBuilder()
|
||||
.setWidgetsContainer(
|
||||
LauncherAtom.WidgetsContainer.getDefaultInstance())
|
||||
.build();
|
||||
}
|
||||
return ContainerInfo.getDefaultInstance();
|
||||
}
|
||||
|
@ -345,4 +358,8 @@ public class ItemInfo {
|
|||
itemInfo.copyFrom(this);
|
||||
return itemInfo;
|
||||
}
|
||||
|
||||
public void setTitle(CharSequence title) {
|
||||
this.title = title;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -239,7 +239,7 @@ public class StateManager<STATE_TYPE extends BaseState<STATE_TYPE>> {
|
|||
? fromState.getTransitionDuration(mActivity)
|
||||
: state.getTransitionDuration(mActivity);
|
||||
prepareForAtomicAnimation(fromState, state, mConfig);
|
||||
AnimatorSet animation = createAnimationToNewWorkspaceInternal(state).getAnim();
|
||||
AnimatorSet animation = createAnimationToNewWorkspaceInternal(state).buildAnim();
|
||||
if (onCompleteRunnable != null) {
|
||||
animation.addListener(AnimationSuccessListener.forRunnable(onCompleteRunnable));
|
||||
}
|
||||
|
@ -267,7 +267,7 @@ public class StateManager<STATE_TYPE extends BaseState<STATE_TYPE>> {
|
|||
for (StateHandler handler : mActivity.getStateManager().getStateHandlers()) {
|
||||
handler.setStateWithAnimation(toState, config, builder);
|
||||
}
|
||||
return builder.getAnim();
|
||||
return builder.buildAnim();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -309,7 +309,7 @@ public class StateManager<STATE_TYPE extends BaseState<STATE_TYPE>> {
|
|||
for (StateHandler handler : getStateHandlers()) {
|
||||
handler.setStateWithAnimation(state, mConfig, builder);
|
||||
}
|
||||
builder.getAnim().addListener(new AnimationSuccessListener() {
|
||||
builder.addListener(new AnimationSuccessListener() {
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
|
@ -325,7 +325,7 @@ public class StateManager<STATE_TYPE extends BaseState<STATE_TYPE>> {
|
|||
onStateTransitionEnd(state);
|
||||
}
|
||||
});
|
||||
mConfig.setAnimation(builder.getAnim(), state);
|
||||
mConfig.setAnimation(builder.buildAnim(), state);
|
||||
return builder;
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
package com.android.launcher3.widget;
|
||||
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_TRAY;
|
||||
|
||||
import com.android.launcher3.PendingAddItemInfo;
|
||||
import com.android.launcher3.pm.ShortcutConfigActivityInfo;
|
||||
|
||||
|
@ -32,5 +34,6 @@ public class PendingAddShortcutInfo extends PendingAddItemInfo {
|
|||
componentName = activityInfo.getComponent();
|
||||
user = activityInfo.getUser();
|
||||
itemType = activityInfo.getItemType();
|
||||
this.container = CONTAINER_WIDGETS_TRAY;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
package com.android.launcher3.widget;
|
||||
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_TRAY;
|
||||
|
||||
import android.appwidget.AppWidgetHostView;
|
||||
import android.os.Bundle;
|
||||
|
||||
|
@ -50,6 +52,7 @@ public class PendingAddWidgetInfo extends PendingAddItemInfo {
|
|||
spanY = i.spanY;
|
||||
minSpanX = i.minSpanX;
|
||||
minSpanY = i.minSpanY;
|
||||
this.container = CONTAINER_WIDGETS_TRAY;
|
||||
}
|
||||
|
||||
public WidgetAddFlowHandler getHandler() {
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
package com.android.systemui.plugins;
|
||||
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import com.android.systemui.plugins.annotations.ProvidesInterface;
|
||||
|
||||
/**
|
||||
|
@ -28,7 +30,7 @@ import com.android.systemui.plugins.annotations.ProvidesInterface;
|
|||
public interface OverscrollPlugin extends Plugin {
|
||||
|
||||
String ACTION = "com.android.systemui.action.PLUGIN_LAUNCHER_OVERSCROLL";
|
||||
int VERSION = 3;
|
||||
int VERSION = 4;
|
||||
|
||||
String DEVICE_STATE_LOCKED = "Locked";
|
||||
String DEVICE_STATE_LAUNCHER = "Launcher";
|
||||
|
@ -41,33 +43,33 @@ public interface OverscrollPlugin extends Plugin {
|
|||
boolean isActive();
|
||||
|
||||
/**
|
||||
* Called when a touch is down and has been recognized as an overscroll gesture.
|
||||
* A call of this method will always result in `onTouchUp` being called, and possibly
|
||||
* `onFling` as well.
|
||||
*
|
||||
* Called when a touch has been recognized as an overscroll gesture.
|
||||
* @param horizontalDistancePx Horizontal distance from the last finger location to the finger
|
||||
* location when it first touched the screen.
|
||||
* @param verticalDistancePx Horizontal distance from the last finger location to the finger
|
||||
* location when it first touched the screen.
|
||||
* @param thresholdPx Minimum distance for gesture.
|
||||
* @param flingDistanceThresholdPx Minimum distance for gesture by fling.
|
||||
* @param flingVelocityThresholdPx Minimum velocity for gesture by fling.
|
||||
* @param deviceState String representing the current device state
|
||||
* @param underlyingActivity String representing the currently active Activity
|
||||
*/
|
||||
void onTouchStart(String deviceState, String underlyingActivity);
|
||||
void onTouchEvent(MotionEvent event,
|
||||
int horizontalDistancePx,
|
||||
int verticalDistancePx,
|
||||
int thresholdPx,
|
||||
int flingDistanceThresholdPx,
|
||||
int flingVelocityThresholdPx,
|
||||
String deviceState,
|
||||
String underlyingActivity);
|
||||
|
||||
/**
|
||||
* Called when a touch that was previously recognized has moved.
|
||||
*
|
||||
* @param px distance between the position of touch on this update and the position of the
|
||||
* touch when it was initially recognized.
|
||||
* @return `true` if overscroll gesture handling should override all other gestures.
|
||||
*/
|
||||
void onTouchTraveled(int px);
|
||||
boolean blockOtherGestures();
|
||||
|
||||
/**
|
||||
* Called when a touch that was previously recognized has ended.
|
||||
*
|
||||
* @param px distance between the position of touch on this update and the position of the
|
||||
* touch when it was initially recognized.
|
||||
* @return `true` if the overscroll gesture can pan the underlying app.
|
||||
*/
|
||||
void onTouchEnd(int px);
|
||||
|
||||
/**
|
||||
* Called when the user starts Compose with a fling. `onTouchUp` will also be called.
|
||||
*/
|
||||
void onFling(float velocity);
|
||||
boolean allowsUnderlyingActivityOverscroll();
|
||||
}
|
||||
|
|
|
@ -362,7 +362,7 @@ public final class LauncherInstrumentation {
|
|||
public void checkForAnomaly() {
|
||||
final String systemAnomalyMessage = getSystemAnomalyMessage();
|
||||
if (systemAnomalyMessage != null) {
|
||||
Assert.fail(formatSystemHealthMessage(closeEvents(
|
||||
Assert.fail(formatSystemHealthMessage(formatErrorWithEvents(
|
||||
"http://go/tapl : Tests are broken by a non-Launcher system error: "
|
||||
+ systemAnomalyMessage, false)));
|
||||
}
|
||||
|
@ -424,7 +424,7 @@ public final class LauncherInstrumentation {
|
|||
return message;
|
||||
}
|
||||
|
||||
private String closeEvents(String message, boolean checkEvents) {
|
||||
private String formatErrorWithEvents(String message, boolean checkEvents) {
|
||||
if (sCheckingEvents) {
|
||||
sCheckingEvents = false;
|
||||
if (checkEvents) {
|
||||
|
@ -436,9 +436,12 @@ public final class LauncherInstrumentation {
|
|||
sEventChecker.finishNoWait();
|
||||
}
|
||||
}
|
||||
// b/156287114
|
||||
|
||||
try {
|
||||
log("Input: " + mDevice.executeShellCommand("dumpsys input"));
|
||||
Log.e("b/156287114", "Input:");
|
||||
for (String line : mDevice.executeShellCommand("dumpsys input").split("\\n")) {
|
||||
Log.d("b/156287114", line);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
@ -451,7 +454,7 @@ public final class LauncherInstrumentation {
|
|||
|
||||
private void fail(String message) {
|
||||
checkForAnomaly();
|
||||
Assert.fail(formatSystemHealthMessage(closeEvents(
|
||||
Assert.fail(formatSystemHealthMessage(formatErrorWithEvents(
|
||||
"http://go/tapl : " + getContextDescription() + message
|
||||
+ " (visible state: " + getVisibleStateMessage() + ")", true)));
|
||||
}
|
||||
|
@ -629,8 +632,6 @@ public final class LauncherInstrumentation {
|
|||
* @return the Workspace object.
|
||||
*/
|
||||
public Workspace pressHome() {
|
||||
mInstrumentation.getUiAutomation().setOnAccessibilityEventListener(
|
||||
e -> Log.d("b/155926212", e.toString()));
|
||||
try (LauncherInstrumentation.Closable e = eventsCheck()) {
|
||||
waitForLauncherInitialized();
|
||||
// Click home, then wait for any accessibility event, then wait until accessibility
|
||||
|
@ -639,9 +640,7 @@ public final class LauncherInstrumentation {
|
|||
// otherwise waitForIdle may return immediately in case when there was a big enough
|
||||
// pause in accessibility events prior to pressing Home.
|
||||
final String action;
|
||||
Log.d("b/155926212", "Before isLauncherVisible()");
|
||||
final boolean launcherWasVisible = isLauncherVisible();
|
||||
Log.d("b/155926212", "After isLauncherVisible(): " + launcherWasVisible);
|
||||
if (getNavigationModel() == NavigationModel.ZERO_BUTTON) {
|
||||
checkForAnomaly();
|
||||
|
||||
|
@ -697,8 +696,6 @@ public final class LauncherInstrumentation {
|
|||
"performed action to switch to Home - " + action)) {
|
||||
return getWorkspace();
|
||||
}
|
||||
} finally {
|
||||
mInstrumentation.getUiAutomation().setOnAccessibilityEventListener(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1312,7 +1309,8 @@ public final class LauncherInstrumentation {
|
|||
if (mOnLauncherCrashed != null) mOnLauncherCrashed.run();
|
||||
checkForAnomaly();
|
||||
Assert.fail(
|
||||
formatSystemHealthMessage(closeEvents("Launcher crashed", false)));
|
||||
formatSystemHealthMessage(
|
||||
formatErrorWithEvents("Launcher crashed", false)));
|
||||
}
|
||||
|
||||
if (sCheckingEvents) {
|
||||
|
@ -1320,6 +1318,16 @@ public final class LauncherInstrumentation {
|
|||
if (mCheckEventsForSuccessfulGestures) {
|
||||
final String message = sEventChecker.verify(WAIT_TIME_MS, true);
|
||||
if (message != null) {
|
||||
try {
|
||||
Log.e("b/156287114", "Input:");
|
||||
for (String line : mDevice.executeShellCommand("dumpsys input").split(
|
||||
"\\n")) {
|
||||
Log.d("b/156287114", line);
|
||||
}
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
checkForAnomaly();
|
||||
Assert.fail(formatSystemHealthMessage(
|
||||
"http://go/tapl : successful gesture produced " + message));
|
||||
|
|
Loading…
Reference in New Issue