Merging from ub-launcher3-rvc-dev @ build 6595814 am: efdc4e2318 am: 9cb1795a71 am: ad4bd43b8f am: 9269333de9

Original change: https://googleplex-android-review.googlesource.com/c/platform/packages/apps/Launcher3/+/11885879

Change-Id: Ie7d2e841b2d41504e07633f116d80b4e3adde59f
This commit is contained in:
Miranda Kephart 2020-06-17 20:49:31 +00:00 committed by Automerger Merge Worker
commit 1bb17f2658
68 changed files with 1113 additions and 497 deletions

View File

@ -54,6 +54,7 @@ message ContainerInfo {
ShortcutsContainer shortcuts_container = 8;
SettingsContainer settings_container = 9;
PredictedHotseatContainer predicted_hotseat_container = 10;
TaskSwitcherContainer task_switcher_container = 11;
}
}
@ -82,6 +83,9 @@ message ShortcutsContainer {
message SettingsContainer {
}
message TaskSwitcherContainer {
}
enum Attribute {
UNKNOWN = 0;
DEFAULT_LAYOUT = 1; // icon automatically placed in workspace, folder, hotseat

View File

@ -18,6 +18,7 @@ package com.android.launcher3.appprediction;
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALL_APPS_RANKED;
import android.app.prediction.AppPredictor;
import android.app.prediction.AppTarget;
@ -37,6 +38,8 @@ import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.allapps.AllAppsStore.OnUpdateListener;
import com.android.launcher3.hybridhotseat.HotseatPredictionController;
import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.shortcuts.ShortcutKey;
@ -48,6 +51,7 @@ import com.android.launcher3.util.MainThreadInitializedObject;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.OptionalInt;
import java.util.stream.IntStream;
/**
@ -301,6 +305,41 @@ public class PredictionUiStateManager implements StateListener<LauncherState>,
return mCurrentState;
}
/**
* Logs ranking info for launched app within all apps prediction.
* Only applicable when {@link ItemInfo#itemType} is one of the followings:
* {@link LauncherSettings.Favorites#ITEM_TYPE_APPLICATION},
* {@link LauncherSettings.Favorites#ITEM_TYPE_SHORTCUT},
* {@link LauncherSettings.Favorites#ITEM_TYPE_DEEP_SHORTCUT}
*/
public void logLaunchedAppRankingInfo(@NonNull ItemInfo itemInfo, InstanceId instanceId) {
if (itemInfo.getTargetComponent() == null || itemInfo.user == null
|| (itemInfo.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
&& itemInfo.itemType != LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT
&& itemInfo.itemType != LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT)) {
return;
}
Launcher launcher = Launcher.getLauncher(mAppsView.getContext());
final ComponentKey k = new ComponentKey(itemInfo.getTargetComponent(), itemInfo.user);
final List<ComponentKeyMapper> predictedApps = getCurrentState().apps;
OptionalInt rank = IntStream.range(0, predictedApps.size())
.filter((i) -> k.equals(predictedApps.get(i).getComponentKey()))
.findFirst();
if (!rank.isPresent()) {
return;
}
LauncherAtom.ItemInfo.Builder atomBuilder = LauncherAtom.ItemInfo.newBuilder();
atomBuilder.setRank(rank.getAsInt());
atomBuilder.setContainerInfo(
LauncherAtom.ContainerInfo.newBuilder().setPredictionContainer(
LauncherAtom.PredictionContainer.newBuilder().build()).build());
launcher.getStatsLogManager().log(LAUNCHER_ALL_APPS_RANKED, instanceId,
atomBuilder.build());
}
/**
* Fill in predicted_rank field based on app prediction.
* Only applicable when {@link ItemInfo#itemType} is one of the followings:
@ -310,6 +349,7 @@ public class PredictionUiStateManager implements StateListener<LauncherState>,
*/
public static void fillInPredictedRank(
@NonNull ItemInfo itemInfo, @NonNull LauncherLogProto.Target target) {
final PredictionUiStateManager manager = PredictionUiStateManager.INSTANCE.getNoCreate();
if (manager == null || itemInfo.getTargetComponent() == null || itemInfo.user == null
|| (itemInfo.itemType != LauncherSettings.Favorites.ITEM_TYPE_APPLICATION

View File

@ -15,6 +15,9 @@
*/
package com.android.launcher3.hybridhotseat;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent
.LAUNCHER_HOTSEAT_EDU_ONLY_TIP;
import android.content.Intent;
import android.view.View;
@ -47,12 +50,12 @@ public class HotseatEduController {
public static final String KEY_HOTSEAT_EDU_SEEN = "hotseat_edu_seen";
public static final String HOTSEAT_EDU_ACTION =
"com.android.launcher3.action.SHOW_HYBRID_HOTSEAT_EDU";
private static final String SETTINGS_ACTION =
public static final String SETTINGS_ACTION =
"android.settings.ACTION_CONTENT_SUGGESTIONS_SETTINGS";
private final Launcher mLauncher;
private final Hotseat mHotseat;
private final HotseatRestoreHelper mRestoreHelper;
private HotseatRestoreHelper mRestoreHelper;
private List<WorkspaceItemInfo> mPredictedApps;
private HotseatEduDialog mActiveDialog;
@ -71,14 +74,17 @@ public class HotseatEduController {
* Checks what type of migration should be used and migrates hotseat
*/
void migrate() {
mRestoreHelper.createBackup();
if (mRestoreHelper != null) {
mRestoreHelper.createBackup();
}
if (FeatureFlags.HOTSEAT_MIGRATE_TO_FOLDER.get()) {
migrateToFolder();
} else {
migrateHotseatWhole();
}
Snackbar.show(mLauncher, R.string.hotsaet_tip_prediction_enabled, R.string.hotseat_turn_off,
null, () -> mLauncher.startActivity(new Intent(SETTINGS_ACTION)));
Snackbar.show(mLauncher, R.string.hotsaet_tip_prediction_enabled,
R.string.hotseat_prediction_settings, null,
() -> mLauncher.startActivity(new Intent(SETTINGS_ACTION)));
}
/**
@ -223,15 +229,15 @@ public class HotseatEduController {
void finishOnboarding() {
mOnOnboardingComplete.run();
destroy();
mLauncher.getSharedPrefs().edit().putBoolean(KEY_HOTSEAT_EDU_SEEN, true).apply();
}
void showDimissTip() {
if (mHotseat.getShortcutsAndWidgets().getChildCount()
< mLauncher.getDeviceProfile().inv.numHotseatIcons) {
Snackbar.show(mLauncher, R.string.hotseat_tip_gaps_filled, R.string.hotseat_turn_off,
null, () -> mLauncher.startActivity(new Intent(SETTINGS_ACTION)));
Snackbar.show(mLauncher, R.string.hotseat_tip_gaps_filled,
R.string.hotseat_prediction_settings, null,
() -> mLauncher.startActivity(new Intent(SETTINGS_ACTION)));
} else {
new ArrowTipView(mLauncher).show(
mLauncher.getString(R.string.hotseat_tip_no_empty_slots), mHotseat.getTop());
@ -242,12 +248,6 @@ public class HotseatEduController {
mPredictedApps = predictedApps;
}
void destroy() {
if (mActiveDialog != null) {
mActiveDialog.setHotseatEduController(null);
}
}
void showEdu() {
int childCount = mHotseat.getShortcutsAndWidgets().getChildCount();
CellLayout cellLayout = mLauncher.getWorkspace().getScreenWithId(Workspace.FIRST_SCREEN_ID);
@ -265,6 +265,7 @@ public class HotseatEduController {
requiresMigration ? R.string.hotseat_tip_no_empty_slots
: R.string.hotseat_auto_enrolled),
mHotseat.getTop());
mLauncher.getStatsLogManager().log(LAUNCHER_HOTSEAT_EDU_ONLY_TIP);
finishOnboarding();
}
}

View File

@ -15,9 +15,10 @@
*/
package com.android.launcher3.hybridhotseat;
import static com.android.launcher3.logging.LoggerUtils.newLauncherEvent;
import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType
.HYBRID_HOTSEAT_CANCELED;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent
.LAUNCHER_HOTSEAT_EDU_ACCEPT;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_EDU_DENY;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_EDU_SEEN;
import android.animation.PropertyValuesHolder;
import android.content.Context;
@ -29,15 +30,14 @@ import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.CellLayout;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Insettable;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.Workspace;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.uioverrides.PredictedAppIcon;
import com.android.launcher3.userevent.nano.LauncherLogProto;
@ -111,15 +111,13 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
mHotseatEduController.moveHotseatItems();
mHotseatEduController.finishOnboarding();
//TODO: pass actual page index here.
// Temporarily we're passing 1 for folder migration and 2 for page migration
logUserAction(true, FeatureFlags.HOTSEAT_MIGRATE_TO_FOLDER.get() ? 1 : 2);
mLauncher.getStatsLogManager().log(LAUNCHER_HOTSEAT_EDU_ACCEPT);
}
private void onDismiss(View v) {
mHotseatEduController.showDimissTip();
mHotseatEduController.finishOnboarding();
logUserAction(false, -1);
mLauncher.getStatsLogManager().log(LAUNCHER_HOTSEAT_EDU_DENY);
handleClose(true);
}
@ -163,39 +161,6 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
}
}
private void logUserAction(boolean migrated, int pageIndex) {
LauncherLogProto.Action action = new LauncherLogProto.Action();
LauncherLogProto.Target target = new LauncherLogProto.Target();
int hotseatItemsCount = mLauncher.getHotseat().getShortcutsAndWidgets().getChildCount();
// -1 to exclude smart space
int workspaceItemCount = mLauncher.getWorkspace().getScreenWithId(
Workspace.FIRST_SCREEN_ID).getShortcutsAndWidgets().getChildCount() - 1;
action.type = LauncherLogProto.Action.Type.TOUCH;
action.touch = LauncherLogProto.Action.Touch.TAP;
target.containerType = LauncherLogProto.ContainerType.TIP;
target.tipType = LauncherLogProto.TipType.HYBRID_HOTSEAT;
target.controlType = migrated ? LauncherLogProto.ControlType.HYBRID_HOTSEAT_ACCEPTED
: HYBRID_HOTSEAT_CANCELED;
target.rank = MIGRATION_EXPERIMENT_IDENTIFIER;
// encoding migration type on pageIndex
target.pageIndex = pageIndex;
target.cardinality = (workspaceItemCount * 1000) + hotseatItemsCount;
LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target);
UserEventDispatcher.newInstance(getContext()).dispatchUserEvent(event, null);
}
private void logOnBoardingSeen() {
LauncherLogProto.Action action = new LauncherLogProto.Action();
LauncherLogProto.Target target = new LauncherLogProto.Target();
action.type = LauncherLogProto.Action.Type.TIP;
target.containerType = LauncherLogProto.ContainerType.TIP;
target.tipType = LauncherLogProto.TipType.HYBRID_HOTSEAT;
LauncherLogProto.LauncherEvent event = newLauncherEvent(action, target);
UserEventDispatcher.newInstance(getContext()).dispatchUserEvent(event, null);
}
private void animateOpen() {
if (mIsOpen || mOpenCloseAnimator.isRunning()) {
return;
@ -244,8 +209,9 @@ public class HotseatEduDialog extends AbstractSlideInView implements Insettable
|| mHotseatEduController == null) {
return;
}
AbstractFloatingView.closeAllOpenViews(mLauncher);
attachToContainer();
logOnBoardingSeen();
mLauncher.getStatsLogManager().log(LAUNCHER_HOTSEAT_EDU_SEEN);
animateOpen();
populatePreview(predictions);
}

View File

@ -0,0 +1,129 @@
/*
* 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 android.content.Context;
import android.os.Handler;
import android.util.Log;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.MainThreadInitializedObject;
import java.io.File;
import java.io.FileWriter;
import java.io.PrintWriter;
import java.text.DateFormat;
import java.util.Calendar;
import java.util.Date;
/**
* Helper class to allow hot seat file logging
*/
public class HotseatFileLog {
public static final int LOG_DAYS = 10;
private static final String FILE_NAME_PREFIX = "hotseat-log-";
private static final DateFormat DATE_FORMAT =
DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
public static final MainThreadInitializedObject<HotseatFileLog> INSTANCE =
new MainThreadInitializedObject<>(HotseatFileLog::new);
private final Handler mHandler = new Handler(
Executors.createAndStartNewLooper("hotseat-logger"));
private final File mLogsDir;
private PrintWriter mCurrentWriter;
private String mFileName;
private HotseatFileLog(Context context) {
mLogsDir = context.getFilesDir();
}
/**
* Prints log values to disk
*/
public void log(String tag, String msg) {
String out = String.format("%s %s %s", DATE_FORMAT.format(new Date()), tag, msg);
mHandler.post(() -> {
synchronized (this) {
PrintWriter writer = getWriter();
if (writer != null) {
writer.println(out);
}
}
});
}
private PrintWriter getWriter() {
String fName = FILE_NAME_PREFIX + (LOG_DAYS % 10);
if (fName.equals(mFileName)) return mCurrentWriter;
Calendar cal = Calendar.getInstance();
boolean append = false;
File logFile = new File(mLogsDir, fName);
if (logFile.exists()) {
Calendar modifiedTime = Calendar.getInstance();
modifiedTime.setTimeInMillis(logFile.lastModified());
// If the file was modified more that 36 hours ago, purge the file.
// We use instead of 24 to account for day-365 followed by day-1
modifiedTime.add(Calendar.HOUR, 36);
append = cal.before(modifiedTime);
}
if (mCurrentWriter != null) {
mCurrentWriter.close();
}
try {
mCurrentWriter = new PrintWriter(new FileWriter(logFile, append));
mFileName = fName;
} catch (Exception ex) {
Log.e("HotseatLogs", "Error writing logs to file", ex);
closeWriter();
}
return mCurrentWriter;
}
private synchronized void closeWriter() {
mFileName = null;
if (mCurrentWriter != null) {
mCurrentWriter.close();
}
mCurrentWriter = null;
}
/**
* Returns a list of all log files
*/
public synchronized File[] getLogFiles() {
File[] files = new File[LOG_DAYS + FileLog.LOG_DAYS];
//include file log files here
System.arraycopy(FileLog.getLogFiles(), 0, files, 0, FileLog.LOG_DAYS);
closeWriter();
for (int i = 0; i < LOG_DAYS; i++) {
files[FileLog.LOG_DAYS + i] = new File(mLogsDir, FILE_NAME_PREFIX + i);
}
return files;
}
}

View File

@ -17,6 +17,8 @@ package com.android.launcher3.hybridhotseat;
import static com.android.launcher3.InvariantDeviceProfile.CHANGE_FLAG_GRID;
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.hybridhotseat.HotseatEduController.SETTINGS_ACTION;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_RANKED;
import android.animation.Animator;
import android.animation.AnimatorSet;
@ -27,6 +29,8 @@ import android.app.prediction.AppPredictor;
import android.app.prediction.AppTarget;
import android.app.prediction.AppTargetEvent;
import android.content.ComponentName;
import android.content.Intent;
import android.os.Process;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
@ -50,7 +54,8 @@ import com.android.launcher3.appprediction.DynamicItemCache;
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.logger.LauncherAtom;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.FolderInfo;
import com.android.launcher3.model.data.ItemInfo;
@ -64,6 +69,8 @@ import com.android.launcher3.uioverrides.QuickstepLauncher;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.views.ArrowTipView;
import com.android.launcher3.views.Snackbar;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
@ -107,8 +114,6 @@ public class HotseatPredictionController implements DragController.DragListener,
private boolean mIsCacheEmpty;
private boolean mIsDestroyed = false;
private HotseatEduController mHotseatEduController;
private List<PredictedAppIcon.PredictedIconOutlineDrawing> mOutlineDrawings = new ArrayList<>();
@ -146,11 +151,47 @@ public class HotseatPredictionController implements DragController.DragListener,
}
/**
* Transitions to NORMAL workspace mode and shows edu
* Shows appropriate hotseat education based on prediction enabled and migration states.
*/
public void showEdu() {
if (mHotseatEduController == null) return;
mHotseatEduController.showEdu();
if (mComponentKeyMappers.isEmpty()) {
// launcher has empty predictions set
Snackbar.show(mLauncher, R.string.hotsaet_tip_prediction_disabled,
R.string.hotseat_prediction_settings, null,
() -> mLauncher.startActivity(
new Intent(SETTINGS_ACTION)));
} else if (isEduSeen()) {
// user has already went through education
new ArrowTipView(mLauncher).show(
mLauncher.getString(R.string.hotsaet_tip_prediction_enabled),
mHotseat.getTop());
} else {
HotseatEduController eduController = new HotseatEduController(mLauncher, mRestoreHelper,
this::createPredictor);
eduController.setPredictedApps(mapToWorkspaceItemInfo(mComponentKeyMappers));
eduController.showEdu();
}
}
/**
* Shows educational tip for hotseat if user does not go through Tips app.
*/
public void showDiscoveryTip() {
if (getPredictedIcons().size() == mHotSeatItemsCount) {
new ArrowTipView(mLauncher).show(
mLauncher.getString(R.string.hotseat_tip_no_empty_slots), mHotseat.getTop());
} else {
Snackbar.show(mLauncher, R.string.hotseat_tip_gaps_filled,
R.string.hotseat_prediction_settings, null,
() -> mLauncher.startActivity(new Intent(SETTINGS_ACTION)));
}
}
/**
* Returns if hotseat client has predictions
*/
public boolean hasPredictions() {
return !mComponentKeyMappers.isEmpty();
}
@Override
@ -250,10 +291,6 @@ public class HotseatPredictionController implements DragController.DragListener,
if (mAppPredictor != null) {
mAppPredictor.destroy();
}
if (mHotseatEduController != null) {
mHotseatEduController.destroy();
mHotseatEduController = null;
}
}
/**
@ -299,20 +336,20 @@ public class HotseatPredictionController implements DragController.DragListener,
mAppPredictor.requestPredictionUpdate();
});
setPauseUIUpdate(false);
if (!isEduSeen()) {
mHotseatEduController = new HotseatEduController(mLauncher, mRestoreHelper,
this::createPredictor);
}
}
/**
* 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) {
if (hasPredictions() && mAppPredictor != null) {
mAppPredictor.requestPredictionUpdate();
fillGapsWithPrediction();
return;
}
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);
@ -324,6 +361,7 @@ public class HotseatPredictionController implements DragController.DragListener,
updateDependencies();
bindItems(items, false, null);
}
private void setPredictedApps(List<AppTarget> appTargets) {
mComponentKeyMappers.clear();
if (appTargets.isEmpty()) {
@ -347,12 +385,11 @@ public class HotseatPredictionController implements DragController.DragListener,
mComponentKeyMappers.add(new ComponentKeyMapper(key, mDynamicItemCache));
}
predictionLog.append("]");
if (Utilities.IS_DEBUG_DEVICE) FileLog.d(TAG, predictionLog.toString());
if (Utilities.IS_DEBUG_DEVICE) {
HotseatFileLog.INSTANCE.get(mLauncher).log(TAG, predictionLog.toString());
}
updateDependencies();
fillGapsWithPrediction();
if (!isEduSeen() && mHotseatEduController != null) {
mHotseatEduController.setPredictedApps(mapToWorkspaceItemInfo(mComponentKeyMappers));
}
cachePredictionComponentKeysIfNecessary(componentKeys);
}
@ -602,6 +639,48 @@ public class HotseatPredictionController implements DragController.DragListener,
mHotseat.fillInLogContainerData(childInfo, child, parents);
}
/**
* Logs rank info based on current list of predicted items
*/
public void logLaunchedAppRankingInfo(@NonNull ItemInfo itemInfo, InstanceId instanceId) {
if (Utilities.IS_DEBUG_DEVICE) {
final String pkg = itemInfo.getTargetComponent() != null
? itemInfo.getTargetComponent().getPackageName() : "unknown";
HotseatFileLog.INSTANCE.get(mLauncher).log("UserEvent",
"appLaunch: packageName:" + pkg + ",isWorkApp:" + (itemInfo.user != null
&& !Process.myUserHandle().equals(itemInfo.user))
+ ",launchLocation:" + itemInfo.container);
}
final ComponentKey k = new ComponentKey(itemInfo.getTargetComponent(), itemInfo.user);
final List<ComponentKeyMapper> predictedApps = new ArrayList<>(mComponentKeyMappers);
OptionalInt rank = IntStream.range(0, predictedApps.size())
.filter((i) -> k.equals(predictedApps.get(i).getComponentKey()))
.findFirst();
if (!rank.isPresent()) {
return;
}
LauncherAtom.PredictedHotseatContainer.Builder containerBuilder =
LauncherAtom.PredictedHotseatContainer.newBuilder();
LauncherAtom.ItemInfo.Builder atomBuilder = LauncherAtom.ItemInfo.newBuilder();
int cardinality = 0;
for (PredictedAppIcon icon : getPredictedIcons()) {
ItemInfo info = (ItemInfo) icon.getTag();
cardinality |= 1 << info.screenId;
}
containerBuilder.setCardinality(cardinality);
atomBuilder.setRank(rank.getAsInt());
if (itemInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION) {
containerBuilder.setIndex(rank.getAsInt());
}
atomBuilder.setContainerInfo(
LauncherAtom.ContainerInfo.newBuilder().setPredictedHotseatContainer(
containerBuilder).build());
mLauncher.getStatsLogManager().log(LAUNCHER_HOTSEAT_RANKED, instanceId,
atomBuilder.build());
}
private class PinPrediction extends SystemShortcut<QuickstepLauncher> {
private PinPrediction(QuickstepLauncher target, ItemInfo itemInfo) {

View File

@ -29,7 +29,6 @@ import static com.android.quickstep.SysUINavigationMode.Mode.NO_BUTTON;
import android.content.Intent;
import android.content.res.Configuration;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
@ -42,10 +41,12 @@ import com.android.launcher3.LauncherState;
import com.android.launcher3.Workspace;
import com.android.launcher3.allapps.DiscoveryBounce;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.appprediction.PredictionUiStateManager;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.hybridhotseat.HotseatEduController;
import com.android.launcher3.hybridhotseat.HotseatPredictionController;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
@ -88,11 +89,10 @@ public class QuickstepLauncher extends BaseQuickstepLauncher {
*/
public static final AsyncCommand SET_SHELF_HEIGHT = (context, arg1, arg2) ->
SystemUiProxy.INSTANCE.get(context).setShelfHeight(arg1 != 0, arg2);
private HotseatPredictionController mHotseatPredictionController;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
protected void setupViews() {
super.setupViews();
if (FeatureFlags.ENABLE_HYBRID_HOTSEAT.get()) {
mHotseatPredictionController = new HotseatPredictionController(this);
mHotseatPredictionController.createPredictor();
@ -113,6 +113,15 @@ public class QuickstepLauncher extends BaseQuickstepLauncher {
}
}
@Override
protected void logAppLaunch(ItemInfo info, InstanceId instanceId) {
super.logAppLaunch(info, instanceId);
if (mHotseatPredictionController != null) {
mHotseatPredictionController.logLaunchedAppRankingInfo(info, instanceId);
}
PredictionUiStateManager.INSTANCE.get(this).logLaunchedAppRankingInfo(info, instanceId);
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
@ -168,13 +177,6 @@ public class QuickstepLauncher extends BaseQuickstepLauncher {
}
}
/**
* Returns Prediction controller for hybrid hotseat
*/
public HotseatPredictionController getHotseatPredictionController() {
return mHotseatPredictionController;
}
/**
* Recents logic that triggers when launcher state changes or launcher activity stops/resumes.
*/

View File

@ -20,6 +20,7 @@ import android.graphics.Rect;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.quickstep.views.RecentsView;
@ -56,6 +57,17 @@ public class OverviewModalTaskState extends OverviewState {
return 1.0f;
}
@Override
public void onBackPressed(Launcher launcher) {
launcher.getStateManager().goToState(LauncherState.OVERVIEW);
RecentsView recentsView = launcher.<RecentsView>getOverviewPanel();
if (recentsView != null) {
recentsView.resetModalVisuals();
} else {
super.onBackPressed(launcher);
}
}
public static float[] getOverviewScaleAndOffsetForModalState(BaseDraggingActivity activity) {
Rect out = new Rect();
activity.<RecentsView>getOverviewPanel().getTaskSize(out);

View File

@ -156,6 +156,7 @@ public class QuickstepAtomicAnimationFactory extends
if (toState == NORMAL && fromState == OVERVIEW) {
config.setInterpolator(ANIM_WORKSPACE_SCALE, DEACCEL);
config.setInterpolator(ANIM_WORKSPACE_FADE, ACCEL);
config.setInterpolator(ANIM_ALL_APPS_FADE, ACCEL);
config.setInterpolator(ANIM_OVERVIEW_SCALE, clampToProgress(ACCEL, 0, 0.9f));
config.setInterpolator(ANIM_OVERVIEW_TRANSLATE_X, ACCEL);
config.setInterpolator(ANIM_OVERVIEW_FADE, DEACCEL_1_7);
@ -210,6 +211,7 @@ public class QuickstepAtomicAnimationFactory extends
}
}
config.setInterpolator(ANIM_WORKSPACE_FADE, OVERSHOOT_1_2);
config.setInterpolator(ANIM_ALL_APPS_FADE, OVERSHOOT_1_2);
config.setInterpolator(ANIM_OVERVIEW_SCALE, OVERSHOOT_1_2);
config.setInterpolator(ANIM_DEPTH, OVERSHOOT_1_2);
Interpolator translationInterpolator = ENABLE_OVERVIEW_ACTIONS.get()

View File

@ -32,7 +32,6 @@ import androidx.annotation.CallSuper;
import androidx.annotation.UiThread;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.VibratorWrapper;
@ -96,8 +95,8 @@ public abstract class BaseSwipeUpHandler<T extends StatefulActivity<?>, Q extend
* depend on proper class initialization.
*/
protected void initAfterSubclassConstructor() {
initTransitionEndpoints(InvariantDeviceProfile.INSTANCE.get(mContext)
.getDeviceProfile(mContext));
initTransitionEndpoints(
mTaskViewSimulator.getOrientationState().getLauncherDeviceProfile());
}
protected void performHapticFeedback() {
@ -144,13 +143,14 @@ public abstract class BaseSwipeUpHandler<T extends StatefulActivity<?>, Q extend
TaskView nextTask = mRecentsView.getTaskView(taskId);
if (nextTask != null) {
mGestureState.updateLastStartedTaskId(taskId);
boolean hasTaskPreviouslyAppeared = mGestureState.getPreviouslyAppearedTaskIds()
.contains(taskId);
nextTask.launchTask(false /* animate */, true /* freezeTaskList */,
success -> {
resultCallback.accept(success);
if (success) {
if (mRecentsView.indexOfChild(nextTask)
== getLastAppearedTaskIndex()) {
onRestartLastAppearedTask();
if (hasTaskPreviouslyAppeared) {
onRestartPreviouslyAppearedTask();
}
} else {
mActivityInterface.onLaunchTaskFailed();
@ -171,7 +171,7 @@ public abstract class BaseSwipeUpHandler<T extends StatefulActivity<?>, Q extend
* start A again to ensure it stays on top.
*/
@CallSuper
protected void onRestartLastAppearedTask() {
protected void onRestartPreviouslyAppearedTask() {
// Finish the controller here, since we won't get onTaskAppeared() for a task that already
// appeared.
if (mRecentsAnimationController != null) {
@ -205,7 +205,7 @@ public abstract class BaseSwipeUpHandler<T extends StatefulActivity<?>, Q extend
mRecentsAnimationController = recentsAnimationController;
mRecentsAnimationTargets = targets;
mTransformParams.setTargetSet(mRecentsAnimationTargets);
DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(mContext).getDeviceProfile(mContext);
DeviceProfile dp = mTaskViewSimulator.getOrientationState().getLauncherDeviceProfile();
RemoteAnimationTargetCompat runningTaskTarget = targets.findTask(
mGestureState.getRunningTaskId());
@ -300,8 +300,7 @@ public abstract class BaseSwipeUpHandler<T extends StatefulActivity<?>, Q extend
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.PAUSE_NOT_DETECTED, "BaseSwipeUpHandler.2");
}
initTransitionEndpoints(InvariantDeviceProfile.INSTANCE.get(mContext)
.getDeviceProfile(mContext));
initTransitionEndpoints(createdActivity.getDeviceProfile());
}
return true;
}

View File

@ -1122,8 +1122,8 @@ public abstract class BaseSwipeUpHandlerV2<T extends StatefulActivity<?>, Q exte
}
@Override
protected void onRestartLastAppearedTask() {
super.onRestartLastAppearedTask();
protected void onRestartPreviouslyAppearedTask() {
super.onRestartPreviouslyAppearedTask();
reset();
}

View File

@ -37,7 +37,7 @@ public class QuickstepTestInformationHandler extends TestInformationHandler {
case TestProtocol.REQUEST_BACKGROUND_TO_OVERVIEW_SWIPE_HEIGHT: {
final float swipeHeight =
LayoutUtils.getShelfTrackingDistance(mContext, mDeviceProfile,
PagedOrientationHandler.HOME_ROTATED);
PagedOrientationHandler.PORTRAIT);
response.putInt(TestProtocol.TEST_INFO_RESPONSE_FIELD, (int) swipeHeight);
return response;
}

View File

@ -139,13 +139,12 @@ public final class RecentsActivity extends StatefulActivity<RecentsState> {
*/
protected DeviceProfile createDeviceProfile() {
DeviceProfile dp = InvariantDeviceProfile.INSTANCE.get(this).getDeviceProfile(this);
DeviceProfile dp1 = InvariantDeviceProfile.INSTANCE.get(this).getDeviceProfile(this);
// In case we are reusing IDP, create a copy so that we don't conflict with Launcher
// activity.
return (mRecentsRootView != null) && isInMultiWindowMode()
? dp.getMultiWindowProfile(this, getMultiWindowDisplaySize())
: dp1.copy(this);
: dp.copy(this);
}
@Override

View File

@ -83,15 +83,15 @@ public abstract class SwipeUpAnimationLogic {
mGestureState = gestureState;
mTaskViewSimulator = new TaskViewSimulator(context, gestureState.getActivityInterface());
mTransformParams = transformParams;
mTaskViewSimulator.setLayoutRotation(
mDeviceState.getCurrentActiveRotation(), mDeviceState.getDisplayRotation());
}
protected void initTransitionEndpoints(DeviceProfile dp) {
mDp = dp;
mTaskViewSimulator.setDp(dp);
mTaskViewSimulator.setLayoutRotation(
mDeviceState.getCurrentActiveRotation(),
mDeviceState.getDisplayRotation());
mTransitionDragLength = mGestureState.getActivityInterface().getSwipeUpDestinationAndLength(
dp, mContext, TEMP_RECT,
mTaskViewSimulator.getOrientationState().getOrientationHandler());

View File

@ -16,6 +16,8 @@
package com.android.quickstep;
import static android.view.Surface.ROTATION_0;
import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
import android.annotation.SuppressLint;
@ -24,6 +26,7 @@ import android.graphics.Insets;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.os.Build;
import android.view.View;
import android.widget.Toast;
import androidx.annotation.RequiresApi;
@ -31,9 +34,12 @@ import androidx.annotation.RequiresApi;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.R;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.ResourceBasedOverride;
import com.android.quickstep.util.RecentsOrientedState;
import com.android.quickstep.views.OverviewActionsView;
import com.android.quickstep.views.TaskThumbnailView;
import com.android.quickstep.views.TaskView;
@ -58,6 +64,19 @@ public class TaskOverlayFactory implements ResourceBasedOverride {
shortcuts.add(shortcut);
}
}
RecentsOrientedState orientedState = taskView.getRecentsView().getPagedViewOrientedState();
boolean canLauncherRotate = orientedState.canLauncherRotate();
boolean isInLandscape = orientedState.getTouchRotation() != ROTATION_0;
// Add overview actions to the menu when in in-place rotate landscape mode.
if (!canLauncherRotate && isInLandscape) {
for (TaskShortcutFactory actionMenuOption : ACTION_MENU_OPTIONS) {
SystemShortcut shortcut = actionMenuOption.getShortcut(activity, taskView);
if (shortcut != null) {
shortcuts.add(shortcut);
}
}
}
return shortcuts;
}
@ -85,6 +104,11 @@ public class TaskOverlayFactory implements ResourceBasedOverride {
TaskShortcutFactory.WELLBEING
};
private static final TaskShortcutFactory[] ACTION_MENU_OPTIONS = new TaskShortcutFactory[]{
TaskShortcutFactory.SCREENSHOT,
TaskShortcutFactory.MODAL
};
/**
* Overlay on each task handling Overview Action Buttons.
*/
@ -94,10 +118,14 @@ public class TaskOverlayFactory implements ResourceBasedOverride {
protected final TaskThumbnailView mThumbnailView;
private T mActionsView;
private ImageActionsApi mImageApi;
private boolean mIsAllowedByPolicy;
protected TaskOverlay(TaskThumbnailView taskThumbnailView) {
mApplicationContext = taskThumbnailView.getContext().getApplicationContext();
mThumbnailView = taskThumbnailView;
mImageApi = new ImageActionsApi(
mApplicationContext, mThumbnailView::getThumbnail);
}
protected T getActionsView() {
@ -112,15 +140,12 @@ public class TaskOverlayFactory implements ResourceBasedOverride {
* Called when the current task is interactive for the user
*/
public void initOverlay(Task task, ThumbnailData thumbnail, Matrix matrix) {
ImageActionsApi imageApi = new ImageActionsApi(
mApplicationContext, mThumbnailView::getThumbnail);
final boolean isAllowedByPolicy = thumbnail.isRealSnapshot;
getActionsView().setCallbacks(new OverlayUICallbacks() {
@Override
public void onShare() {
if (isAllowedByPolicy) {
imageApi.startShareActivity();
mImageApi.startShareActivity();
} else {
showBlockedByPolicyMessage();
}
@ -129,16 +154,23 @@ public class TaskOverlayFactory implements ResourceBasedOverride {
@SuppressLint("NewApi")
@Override
public void onScreenshot() {
if (isAllowedByPolicy) {
imageApi.saveScreenshot(mThumbnailView.getThumbnail(),
getTaskSnapshotBounds(), getTaskSnapshotInsets(), task.key);
} else {
showBlockedByPolicyMessage();
}
saveScreenshot(task);
}
});
}
/**
* Called to save screenshot of the task thumbnail.
*/
@SuppressLint("NewApi")
private void saveScreenshot(Task task) {
if (mThumbnailView.isRealSnapshot()) {
mImageApi.saveScreenshot(mThumbnailView.getThumbnail(),
getTaskSnapshotBounds(), getTaskSnapshotInsets(), task.key);
} else {
showBlockedByPolicyMessage();
}
}
/**
* Called when the overlay is no longer used.
@ -146,6 +178,26 @@ public class TaskOverlayFactory implements ResourceBasedOverride {
public void reset() {
}
/**
* Called when the system wants to reset the modal visuals.
*/
public void resetModalVisuals() {
}
/**
* Gets the modal state system shortcut.
*/
public SystemShortcut getModalStateSystemShortcut(WorkspaceItemInfo itemInfo) {
return null;
}
/**
* Gets the system shortcut for the screenshot that will be added to the task menu.
*/
public SystemShortcut getScreenshotShortcut(BaseDraggingActivity activity,
ItemInfo iteminfo) {
return new ScreenshotSystemShortcut(activity, iteminfo);
}
/**
* Gets the task snapshot as it is displayed on the screen.
*
@ -175,6 +227,22 @@ public class TaskOverlayFactory implements ResourceBasedOverride {
R.string.blocked_by_policy,
Toast.LENGTH_LONG).show();
}
private class ScreenshotSystemShortcut extends SystemShortcut {
private final BaseDraggingActivity mActivity;
ScreenshotSystemShortcut(BaseDraggingActivity activity, ItemInfo itemInfo) {
super(R.drawable.ic_screenshot, R.string.action_screenshot, activity, itemInfo);
mActivity = activity;
}
@Override
public void onClick(View view) {
saveScreenshot(mThumbnailView.getTaskView().getTask());
dismissTaskMenuView(mActivity);
}
}
}
/**

View File

@ -18,29 +18,26 @@ package com.android.quickstep;
import static android.view.Display.DEFAULT_DISPLAY;
import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_SELECTIONS;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_FREE_FORM_TAP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_SPLIT_SCREEN_TAP;
import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP;
import android.app.Activity;
import android.app.ActivityOptions;
import android.content.ComponentName;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
import android.os.UserHandle;
import android.view.View;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.logging.StatsLogManager.LauncherEvent;
import com.android.launcher3.model.WellbeingModel;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.popup.SystemShortcut.AppInfo;
import com.android.launcher3.userevent.nano.LauncherLogProto;
@ -69,28 +66,7 @@ public interface TaskShortcutFactory {
SystemShortcut getShortcut(BaseDraggingActivity activity, TaskView view);
static WorkspaceItemInfo dummyInfo(TaskView view) {
Task task = view.getTask();
WorkspaceItemInfo dummyInfo = new WorkspaceItemInfo(){
/**
* Helps to log events as {@link LauncherAtom.Task}
* instead of {@link LauncherAtom.ItemInfo}.
*/
@Override
public LauncherAtom.ItemInfo buildProto() {
return view.buildProto();
}
};
dummyInfo.intent = new Intent();
ComponentName component = task.getTopComponent();
dummyInfo.getIntent().setComponent(component);
dummyInfo.user = UserHandle.of(task.key.userId);
dummyInfo.title = TaskUtils.getTitle(view.getContext(), task);
return dummyInfo;
}
TaskShortcutFactory APP_INFO = (activity, view) -> new AppInfo(activity, dummyInfo(view));
TaskShortcutFactory APP_INFO = (activity, view) -> new AppInfo(activity, view.getItemInfo());
abstract class MultiWindowFactory implements TaskShortcutFactory {
@ -134,7 +110,7 @@ public interface TaskShortcutFactory {
public MultiWindowSystemShortcut(int iconRes, int textRes, BaseDraggingActivity activity,
TaskView taskView, MultiWindowFactory factory, LauncherEvent launcherEvent) {
super(iconRes, textRes, activity, dummyInfo(taskView));
super(iconRes, textRes, activity, taskView.getItemInfo());
mLauncherEvent = launcherEvent;
mHandler = new Handler(Looper.getMainLooper());
mTaskView = taskView;
@ -220,7 +196,7 @@ public interface TaskShortcutFactory {
WindowManagerWrapper.getInstance().overridePendingAppTransitionMultiThumbFuture(
future, animStartedListener, mHandler, true /* scaleUp */,
taskKey.displayId);
mTarget.getStatsLogManager().log(mLauncherEvent, mTaskView.buildProto());
mTarget.getStatsLogManager().log(mLauncherEvent, mTaskView.getItemInfo());
}
}
}
@ -304,7 +280,7 @@ public interface TaskShortcutFactory {
private final TaskView mTaskView;
public PinSystemShortcut(BaseDraggingActivity target, TaskView tv) {
super(R.drawable.ic_pin, R.string.recent_task_option_pin, target, dummyInfo(tv));
super(R.drawable.ic_pin, R.string.recent_task_option_pin, target, tv.getItemInfo());
mTaskView = tv;
}
@ -321,15 +297,30 @@ public interface TaskShortcutFactory {
mTaskView.launchTask(true, resultCallback, Executors.MAIN_EXECUTOR.getHandler());
dismissTaskMenuView(mTarget);
mTarget.getStatsLogManager().log(LauncherEvent.LAUNCHER_SYSTEM_SHORTCUT_PIN_TAP,
mTaskView.buildProto());
mTaskView.getItemInfo());
}
}
TaskShortcutFactory INSTALL = (activity, view) ->
InstantAppResolver.newInstance(activity).isInstantApp(activity,
view.getTask().getTopComponent().getPackageName())
? new SystemShortcut.Install(activity, dummyInfo(view)) : null;
? new SystemShortcut.Install(activity, view.getItemInfo()) : null;
TaskShortcutFactory WELLBEING = (activity, view) ->
WellbeingModel.SHORTCUT_FACTORY.getShortcut(activity, dummyInfo(view));
WellbeingModel.SHORTCUT_FACTORY.getShortcut(activity, view.getItemInfo());
TaskShortcutFactory SCREENSHOT = (activity, tv) -> {
if (ENABLE_OVERVIEW_ACTIONS.get()) {
return tv.getThumbnail().getTaskOverlay()
.getScreenshotShortcut(activity, tv.getItemInfo());
}
return null;
};
TaskShortcutFactory MODAL = (activity, tv) -> {
if (ENABLE_OVERVIEW_ACTIONS.get() && ENABLE_OVERVIEW_SELECTIONS.get()) {
return tv.getThumbnail().getTaskOverlay().getModalStateSystemShortcut(tv.getItemInfo());
}
return null;
};
}

View File

@ -546,6 +546,8 @@ public class TouchInteractionService extends Service implements PluginListener<O
if (mTaskAnimationManager.isRecentsAnimationRunning()) {
gestureState.updateRunningTask(mGestureState.getRunningTask());
gestureState.updateLastStartedTaskId(mGestureState.getLastStartedTaskId());
gestureState.updatePreviouslyAppearedTaskIds(
mGestureState.getPreviouslyAppearedTaskIds());
} else {
gestureState.updateRunningTask(TraceHelper.whitelistIpcs("getRunningTask.0",
() -> mAM.getRunningTask(false /* filterOnlyVisibleRecents */)));

View File

@ -267,10 +267,6 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
if (!mPassedSlopOnThisGesture && passedSlop) {
mPassedSlopOnThisGesture = true;
}
// Until passing slop, we don't know what direction we're going, so assume we might
// be quick switching to avoid translating recents away when continuing the gesture.
boolean isLikelyToStartNewTask = !mPassedSlopOnThisGesture
|| horizontalDist > upDist;
if (!mPassedPilferInputSlop) {
if (passedSlop) {
@ -304,6 +300,13 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
}
if (mDeviceState.isFullyGesturalNavMode()) {
// Until passing slop, we don't know what direction we're going, so assume
// we're quick switching to avoid translating recents away when continuing
// the gesture.
boolean haveNotPassedSlopOnContinuedGesture =
!mPassedSlopOnThisGesture && mPassedPilferInputSlop;
boolean isLikelyToStartNewTask = haveNotPassedSlopOnContinuedGesture
|| horizontalDist > upDist;
mMotionPauseDetector.setDisallowPause(upDist < mMotionPauseMinDisplacement
|| isLikelyToStartNewTask);
mMotionPauseDetector.addPosition(ev);

View File

@ -36,8 +36,6 @@ import com.android.quickstep.util.ActiveGestureLog;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InputMonitorCompat;
import java.util.function.Predicate;
/**
* Input consumer for handling touch on the recents/Launcher activity.
*/
@ -50,8 +48,6 @@ public class OverviewInputConsumer<T extends StatefulActivity<?>>
private final InputMonitorCompat mInputMonitor;
private final int[] mLocationOnScreen = new int[2];
private final boolean mProxyTouch;
private final Predicate<MotionEvent> mEventReceiver;
private final boolean mStartingInActivityBounds;
private boolean mTargetHandledTouch;
@ -64,15 +60,7 @@ public class OverviewInputConsumer<T extends StatefulActivity<?>>
mActivityInterface = gestureState.getActivityInterface();
mTarget = activity.getDragLayer();
if (startingInActivityBounds) {
mEventReceiver = mTarget::dispatchTouchEvent;
mProxyTouch = true;
} else {
// Only proxy touches to controllers if we are starting touch from nav bar.
mEventReceiver = mTarget::proxyTouchEvent;
mTarget.getLocationOnScreen(mLocationOnScreen);
mProxyTouch = mTarget.prepareProxyEventStarting();
}
mTarget.getLocationOnScreen(mLocationOnScreen);
}
@Override
@ -87,10 +75,6 @@ public class OverviewInputConsumer<T extends StatefulActivity<?>>
@Override
public void onMotionEvent(MotionEvent ev) {
if (!mProxyTouch) {
return;
}
int flags = ev.getEdgeFlags();
if (!mStartingInActivityBounds) {
ev.setEdgeFlags(flags | Utilities.EDGE_NAV_BAR);
@ -99,7 +83,7 @@ public class OverviewInputConsumer<T extends StatefulActivity<?>>
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.PAUSE_NOT_DETECTED, "OverviewInputConsumer");
}
boolean handled = mEventReceiver.test(ev);
boolean handled = mTarget.proxyTouchEvent(ev, mStartingInActivityBounds);
ev.offsetLocation(mLocationOnScreen[0], mLocationOnScreen[1]);
ev.setEdgeFlags(flags);

View File

@ -30,6 +30,7 @@ import static com.android.launcher3.Utilities.mapToRange;
import static com.android.launcher3.Utilities.squaredHypot;
import static com.android.launcher3.Utilities.squaredTouchSlop;
import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.anim.Interpolators.ACCEL_0_75;
import static com.android.launcher3.anim.Interpolators.ACCEL_2;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR;
@ -607,6 +608,17 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
}
}
/**
* Whether the Clear All button is hidden or fully visible. Used to determine if center
* displayed page is a task or the Clear All button.
*
* @return True = Clear All button not fully visible, center page is a task. False = Clear All
* button fully visible, center page is Clear All button.
*/
public boolean isClearAllHidden() {
return mClearAllButton.getAlpha() != 1f;
}
@Override
protected void onPageBeginTransition() {
super.onPageBeginTransition();
@ -616,7 +628,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
@Override
protected void onPageEndTransition() {
super.onPageEndTransition();
if (getScrollX() == getScrollForPage(getPageNearestToCenterOfScreen())) {
if (isClearAllHidden()) {
LayoutUtils.setViewEnabled(mActionsView, true);
}
if (getNextPage() > 0) {
@ -1336,7 +1348,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
endState.logAction, Direction.UP, index, compKey);
mActivity.getStatsLogManager().log(
LAUNCHER_TASK_DISMISS_SWIPE_UP, taskView.buildProto());
LAUNCHER_TASK_DISMISS_SWIPE_UP, taskView.getItemInfo());
}
}
@ -1720,7 +1732,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
private void updatePageOffsets() {
float offset = mAdjacentPageOffset * getWidth();
float modalOffset = mTaskModalness * getWidth();
float modalOffset = ACCEL_0_75.getInterpolation(mTaskModalness) * getWidth();
if (mIsRtl) {
offset = -offset;
modalOffset = -modalOffset;
@ -1748,6 +1760,16 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
return Math.max(getWidth(), 1);
}
/**
* Resets the visuals when exit modal state.
*/
public void resetModalVisuals() {
TaskView taskView = getCurrentPageTaskView();
if (taskView != null) {
taskView.getThumbnail().getTaskOverlay().resetModalVisuals();
}
}
private void updateDeadZoneRects() {
// Get the deadzone rect surrounding the clear all button to not dismiss overview to home
mClearAllButtonDeadZoneRect.setEmpty();
@ -1919,7 +1941,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
endState.logAction, Direction.DOWN, indexOfChild(tv),
TaskUtils.getLaunchComponentKeyForTask(task.key));
mActivity.getStatsLogManager().log(
LAUNCHER_TASK_LAUNCH_SWIPE_DOWN, tv.buildProto());
LAUNCHER_TASK_LAUNCH_SWIPE_DOWN, tv.getItemInfo());
}
} else {
onTaskLaunched(false);
@ -2089,6 +2111,12 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
return mClearAllButton;
}
@Override
protected boolean onOverscroll(int amount) {
// overscroll should only be accepted on -1 direction (for clear all button)
if ((amount > 0 && !mIsRtl) || (amount < 0 && mIsRtl)) return false;
return super.onOverscroll(amount);
}
/**
* @return How many pixels the running task is offset on the currently laid out dominant axis.
@ -2182,6 +2210,11 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
if (getCurrentPageTaskView() != null) {
getCurrentPageTaskView().setModalness(modalness);
}
// Only show actions view when it's modal for in-place landscape mode.
boolean inPlaceLandscape = !mOrientationState.canLauncherRotate()
&& mOrientationState.getTouchRotation() != ROTATION_0;
mActionsView.updateHiddenFlags(HIDDEN_NON_ZERO_ROTATION, modalness < 1 && inPlaceLandscape);
LayoutUtils.setViewEnabled(mActionsView, true);
}
@Nullable

View File

@ -447,6 +447,16 @@ public class TaskThumbnailView extends View implements PluginListener<OverviewSc
return mThumbnailData.thumbnail;
}
/**
* Returns whether the snapshot is real.
*/
public boolean isRealSnapshot() {
if (mThumbnailData == null) {
return false;
}
return mThumbnailData.isRealSnapshot;
}
/**
* Utility class to position the thumbnail in the TaskView
*/

View File

@ -40,6 +40,7 @@ import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.app.ActivityOptions;
import android.content.Context;
import android.content.Intent;
import android.graphics.Outline;
import android.graphics.Rect;
import android.graphics.RectF;
@ -48,7 +49,6 @@ import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.InsetDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Process;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.util.Log;
@ -61,13 +61,14 @@ import android.widget.Toast;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol;
@ -213,7 +214,7 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
Touch.TAP, Direction.NONE, getRecentsView().indexOfChild(this),
TaskUtils.getLaunchComponentKeyForTask(getTask().key));
mActivity.getStatsLogManager().log(LAUNCHER_TASK_LAUNCH_TAP, buildProto());
mActivity.getStatsLogManager().log(LAUNCHER_TASK_LAUNCH_TAP, getItemInfo());
});
mCurrentFullscreenParams = new FullscreenDrawParams(context);
@ -226,14 +227,16 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
/**
* Builds proto for logging
*/
public LauncherAtom.ItemInfo buildProto() {
public WorkspaceItemInfo getItemInfo() {
ComponentKey componentKey = TaskUtils.getLaunchComponentKeyForTask(getTask().key);
LauncherAtom.ItemInfo.Builder itemBuilder = LauncherAtom.ItemInfo.newBuilder();
itemBuilder.setIsWork(componentKey.user != Process.myUserHandle());
itemBuilder.setTask(LauncherAtom.Task.newBuilder()
.setComponentName(componentKey.componentName.flattenToShortString())
.setIndex(getRecentsView().indexOfChild(this)));
return itemBuilder.build();
WorkspaceItemInfo dummyInfo = new WorkspaceItemInfo();
dummyInfo.itemType = LauncherSettings.Favorites.ITEM_TYPE_TASK;
dummyInfo.container = LauncherSettings.Favorites.CONTAINER_TASKSWITCHER;
dummyInfo.user = componentKey.user;
dummyInfo.intent = new Intent().setComponent(componentKey.componentName);
dummyInfo.title = TaskUtils.getTitle(getContext(), getTask());
dummyInfo.screenId = getRecentsView().indexOfChild(this);
return dummyInfo;
}
@Override
@ -425,13 +428,16 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
}
private boolean showTaskMenu(int action) {
getRecentsView().snapToPage(getRecentsView().indexOfChild(this));
mMenuView = TaskMenuView.showForTask(this);
mActivity.getStatsLogManager().log(LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS, buildProto());
UserEventDispatcher.newInstance(getContext()).logActionOnItem(action, Direction.NONE,
LauncherLogProto.ItemType.TASK_ICON);
if (mMenuView != null) {
mMenuView.addOnAttachStateChangeListener(mTaskMenuStateListener);
if (!getRecentsView().isClearAllHidden()) {
getRecentsView().snapToPage(getRecentsView().indexOfChild(this));
} else {
mMenuView = TaskMenuView.showForTask(this);
mActivity.getStatsLogManager().log(LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS, getItemInfo());
UserEventDispatcher.newInstance(getContext()).logActionOnItem(action, Direction.NONE,
LauncherLogProto.ItemType.TASK_ICON);
if (mMenuView != null) {
mMenuView.addOnAttachStateChangeListener(mTaskMenuStateListener);
}
}
return mMenuView != null;
}
@ -969,6 +975,9 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
}
void updateCurrentFullscreenParams(PreviewPositionHelper previewPositionHelper) {
if (getRecentsView() == null) {
return;
}
mCurrentFullscreenParams.setProgress(
mFullscreenProgress,
getRecentsView().getScaleX(),

View File

@ -76,8 +76,8 @@
<!-- Button text to dismiss opt in for fully predicted hotseat -->
<string name="hotseat_edu_dismiss">No thanks</string>
<!-- action shown to turn off predictions after onboarding -->
<string name="hotseat_turn_off">Settings</string>
<!-- action shown to toggle predictions after onboarding -->
<string name="hotseat_prediction_settings">Settings</string>
<!-- tip shown if user has no items in hotseat to migrate -->
<string name="hotseat_auto_enrolled">Most-used apps appear here, and change based on routines</string>
@ -86,7 +86,9 @@
<!-- tip shown if user declines migration and has some open spots for prediction -->
<string name="hotseat_tip_gaps_filled">App suggestions added to empty space</string>
<!-- tip shown when user migrates and predictions are enabled in hotseat -->
<string name="hotsaet_tip_prediction_enabled">App suggestions Enabled</string>
<string name="hotsaet_tip_prediction_enabled">App suggestions enabled</string>
<!-- tip shown when hotseat edu is requested while predicions are disabled -->
<string name="hotsaet_tip_prediction_disabled">App suggestions are disabled</string>
<!-- content description for hotseat items -->
<string name="hotseat_prediction_content_description">Predicted app: <xliff:g id="title" example="Chrome">%1$s</xliff:g></string>

View File

@ -31,6 +31,7 @@ import android.os.Bundle;
import android.os.CancellationSignal;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.hybridhotseat.HotseatPredictionController;
import com.android.launcher3.model.WellbeingModel;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.proxy.ProxyActivityStarter;
@ -75,6 +76,7 @@ public abstract class BaseQuickstepLauncher extends Launcher
private final ShelfPeekAnim mShelfPeekAnim = new ShelfPeekAnim(this);
private OverviewActionsView mActionsView;
protected HotseatPredictionController mHotseatPredictionController;
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -305,6 +307,13 @@ public abstract class BaseQuickstepLauncher extends Launcher
return mShelfPeekAnim;
}
/**
* Returns Prediction controller for hybrid hotseat
*/
public HotseatPredictionController getHotseatPredictionController() {
return mHotseatPredictionController;
}
public void setHintUserWillBeActive() {
addActivityFlags(ACTIVITY_STATE_USER_WILL_BE_ACTIVE);
}

View File

@ -32,6 +32,7 @@ import com.android.launcher3.Utilities;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.statemanager.StateManager.StateHandler;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.systemui.shared.system.BlurUtils;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import com.android.systemui.shared.system.SurfaceControlCompat;
import com.android.systemui.shared.system.TransactionCompat;
@ -110,6 +111,10 @@ public class DepthController implements StateHandler<LauncherState> {
}
private void ensureDependencies() {
if (mWallpaperManager == null) {
mMaxBlurRadius = mLauncher.getResources().getInteger(R.integer.max_depth_blur_radius);
mWallpaperManager = new WallpaperManagerCompat(mLauncher);
}
if (mLauncher.getRootView() != null && mOnAttachListener == null) {
mOnAttachListener = new View.OnAttachStateChangeListener() {
@Override
@ -127,11 +132,6 @@ public class DepthController implements StateHandler<LauncherState> {
};
mLauncher.getRootView().addOnAttachStateChangeListener(mOnAttachListener);
}
if (mWallpaperManager != null) {
return;
}
mMaxBlurRadius = mLauncher.getResources().getInteger(R.integer.max_depth_blur_radius);
mWallpaperManager = new WallpaperManagerCompat(mLauncher);
}
/**
@ -205,7 +205,8 @@ public class DepthController implements StateHandler<LauncherState> {
return;
}
if (mSurface == null || !mSurface.isValid()) {
boolean supportsBlur = BlurUtils.supportsBlursOnWindows();
if (supportsBlur && (mSurface == null || !mSurface.isValid())) {
return;
}
mDepth = depthF;
@ -214,17 +215,20 @@ public class DepthController implements StateHandler<LauncherState> {
if (windowToken != null) {
mWallpaperManager.setWallpaperZoomOut(windowToken, mDepth);
}
final int blur;
if (mLauncher.isInState(LauncherState.ALL_APPS) && mDepth == 1) {
// All apps has a solid background. We don't need to draw blurs after it's fully
// visible. This will take us out of GPU composition, saving battery and increasing
// performance.
blur = 0;
} else {
blur = (int) (mDepth * mMaxBlurRadius);
if (supportsBlur) {
final int blur;
if (mLauncher.isInState(LauncherState.ALL_APPS) && mDepth == 1) {
// All apps has a solid background. We don't need to draw blurs after it's fully
// visible. This will take us out of GPU composition, saving battery and increasing
// performance.
blur = 0;
} else {
blur = (int) (mDepth * mMaxBlurRadius);
}
new TransactionCompat()
.setBackgroundBlurRadius(mSurface, blur)
.apply();
}
new TransactionCompat()
.setBackgroundBlurRadius(mSurface, blur)
.apply();
}
}

View File

@ -30,6 +30,8 @@ import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Set;
/**
* Manages the state for an active system gesture, listens for events from the system and Launcher,
@ -128,6 +130,7 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
private ActivityManager.RunningTaskInfo mRunningTask;
private GestureEndTarget mEndTarget;
private RemoteAnimationTargetCompat mLastAppearedTaskTarget;
private Set<Integer> mPreviouslyAppearedTaskIds = new HashSet<>();
private int mLastStartedTaskId = -1;
public GestureState(OverviewComponentObserver componentObserver, int gestureId) {
@ -147,6 +150,7 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
mRunningTask = other.mRunningTask;
mEndTarget = other.mEndTarget;
mLastAppearedTaskTarget = other.mLastAppearedTaskTarget;
mPreviouslyAppearedTaskIds = other.mPreviouslyAppearedTaskIds;
mLastStartedTaskId = other.mLastStartedTaskId;
}
@ -234,6 +238,9 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
*/
public void updateLastAppearedTaskTarget(RemoteAnimationTargetCompat lastAppearedTaskTarget) {
mLastAppearedTaskTarget = lastAppearedTaskTarget;
if (lastAppearedTaskTarget != null) {
mPreviouslyAppearedTaskIds.add(lastAppearedTaskTarget.taskId);
}
}
/**
@ -243,6 +250,14 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
return mLastAppearedTaskTarget != null ? mLastAppearedTaskTarget.taskId : -1;
}
public void updatePreviouslyAppearedTaskIds(Set<Integer> previouslyAppearedTaskIds) {
mPreviouslyAppearedTaskIds = previouslyAppearedTaskIds;
}
public Set<Integer> getPreviouslyAppearedTaskIds() {
return mPreviouslyAppearedTaskIds;
}
/**
* Updates the last task that we started via startActivityFromRecents() during this gesture.
*/

View File

@ -64,6 +64,9 @@ public class StatsLogCompatManager extends StatsLogManager {
private static Context sContext;
private static final InstanceId DEFAULT_INSTANCE_ID = InstanceId.fakeInstanceId(0);
// LauncherAtom.ItemInfo.getDefaultInstance() should be used but until launcher proto migrates
// from nano to lite, bake constant to prevent robo test failure.
private static final int DEFAULT_PAGE_INDEX = -2;
private static final int FOLDER_HIERARCHY_OFFSET = 100;
public StatsLogCompatManager(Context context) {
@ -75,7 +78,7 @@ public class StatsLogCompatManager extends StatsLogManager {
*/
@Override
public void log(EventEnum event) {
log(event, DEFAULT_INSTANCE_ID, LauncherAtom.ItemInfo.getDefaultInstance());
log(event, DEFAULT_INSTANCE_ID, (ItemInfo) null);
}
/**
@ -83,26 +86,62 @@ public class StatsLogCompatManager extends StatsLogManager {
*/
@Override
public void log(EventEnum event, InstanceId instanceId) {
log(event, instanceId, LauncherAtom.ItemInfo.getDefaultInstance());
log(event, instanceId, (ItemInfo) null);
}
/**
* Logs an event and accompanying {@link ItemInfo}.
*/
@Override
public void log(EventEnum event, @Nullable LauncherAtom.ItemInfo info) {
public void log(EventEnum event, @Nullable ItemInfo info) {
log(event, DEFAULT_INSTANCE_ID, info);
}
/**
* Logs an event.
*
* @param event an enum implementing EventEnum interface.
* @param atomInfo item typically containing app or task launch related information.
*/
public void log(EventEnum event, InstanceId instanceId, LauncherAtom.ItemInfo atomInfo) {
LauncherAppState.getInstance(sContext).getModel().enqueueModelUpdateTask(
new BaseModelUpdateTask() {
@Override
public void execute(LauncherAppState app, BgDataModel dataModel,
AllAppsList apps) {
write(event, instanceId, atomInfo, null,
LAUNCHER_UICHANGED__DST_STATE__HOME,
LAUNCHER_UICHANGED__DST_STATE__BACKGROUND);
}
});
}
/**
* Logs an event.
*
* @param event an enum implementing EventEnum interface.
* @param atomItemInfo item typically containing app or task launch related information.
*/
@Override
public void log(EventEnum event, @Nullable LauncherAtom.ItemInfo atomItemInfo, int srcState,
int dstState) {
write(event, DEFAULT_INSTANCE_ID,
atomItemInfo == null ? LauncherAtom.ItemInfo.getDefaultInstance() : atomItemInfo,
null,
srcState,
dstState);
}
/**
* Logs an event and accompanying {@link InstanceId} and {@link LauncherAtom.ItemInfo}.
*/
@Override
public void log(EventEnum event, InstanceId instanceId,
@Nullable LauncherAtom.ItemInfo info) {
@Nullable ItemInfo info) {
logInternal(event, instanceId, info,
LAUNCHER_UICHANGED__DST_STATE__HOME,
LAUNCHER_UICHANGED__DST_STATE__BACKGROUND);
LAUNCHER_UICHANGED__DST_STATE__BACKGROUND,
DEFAULT_PAGE_INDEX);
}
/**
@ -124,38 +163,67 @@ public class StatsLogCompatManager extends StatsLogManager {
*/
@Override
public void log(EventEnum event, int srcState, int dstState, int pageIndex) {
LauncherAtom.ItemInfo info = LauncherAtom.ItemInfo.getDefaultInstance();
if (srcState == LAUNCHER_UICHANGED__DST_STATE__HOME
|| dstState == LAUNCHER_UICHANGED__SRC_STATE__HOME) {
info = LauncherAtom.ItemInfo.newBuilder().setContainerInfo(
LauncherAtom.ContainerInfo.newBuilder().setWorkspace(
LauncherAtom.WorkspaceContainer.newBuilder().setPageIndex(pageIndex)
)).build();
}
logInternal(event, DEFAULT_INSTANCE_ID, info, srcState, dstState);
logInternal(event, DEFAULT_INSTANCE_ID, null, srcState, dstState, pageIndex);
}
/**
* Logs an event and accompanying {@link InstanceId} and {@link LauncherAtom.ItemInfo}.
* Logs an event and accompanying {@link InstanceId} and {@link ItemInfo}.
*/
private void logInternal(EventEnum event, InstanceId instanceId,
@Nullable LauncherAtom.ItemInfo info, int srcState, int dstState) {
info = info == null ? LauncherAtom.ItemInfo.getDefaultInstance() : info;
@Nullable ItemInfo info, int srcState, int dstState, int pageIndex) {
if (IS_VERBOSE) {
String name = (event instanceof LauncherEvent) ? ((LauncherEvent) event).name() :
event.getId() + "";
LauncherAppState.getInstance(sContext).getModel().enqueueModelUpdateTask(
new BaseModelUpdateTask() {
@Override
public void execute(LauncherAppState app, BgDataModel dataModel,
AllAppsList apps) {
writeEvent(event, instanceId, info, srcState, dstState, pageIndex,
dataModel.folders);
}
});
}
Log.d(TAG, instanceId == DEFAULT_INSTANCE_ID
? String.format("\n%s (State:%s->%s) \n%s", name, getStateString(srcState),
getStateString(dstState), info)
: String.format("\n%s (State:%s->%s) (InstanceId:%s)\n%s", name, instanceId,
getStateString(srcState), getStateString(dstState), info));
}
private static void writeEvent(EventEnum event, InstanceId instanceId,
@Nullable ItemInfo info, int srcState, int dstState, int pageIndex,
IntSparseArrayMap<FolderInfo> folders) {
if (!Utilities.ATLEAST_R) {
return;
}
LauncherAtom.ItemInfo atomInfo = LauncherAtom.ItemInfo.getDefaultInstance();
if (info != null) {
if (info.container >= 0) {
atomInfo = info.buildProto(folders.get(info.container));
} else {
atomInfo = info.buildProto();
}
} else {
if (srcState == LAUNCHER_UICHANGED__DST_STATE__HOME
|| dstState == LAUNCHER_UICHANGED__SRC_STATE__HOME) {
atomInfo = LauncherAtom.ItemInfo.newBuilder().setContainerInfo(
LauncherAtom.ContainerInfo.newBuilder().setWorkspace(
LauncherAtom.WorkspaceContainer.newBuilder().setPageIndex(pageIndex)
)).build();
}
}
write(event, instanceId, atomInfo, info, srcState, dstState);
}
private static void write(EventEnum event, InstanceId instanceId,
LauncherAtom.ItemInfo atomInfo,
@Nullable ItemInfo info,
int srcState, int dstState) {
if (IS_VERBOSE) {
String name = (event instanceof Enum) ? ((Enum) event).name() :
event.getId() + "";
Log.d(TAG, instanceId == DEFAULT_INSTANCE_ID
? String.format("\n%s (State:%s->%s) \n%s\n%s", name, getStateString(srcState),
getStateString(dstState), info, atomInfo)
: String.format("\n%s (State:%s->%s) (InstanceId:%s)\n%s\n%s", name,
getStateString(srcState), getStateString(dstState), instanceId, info,
atomInfo));
}
SysUiStatsLog.write(
SysUiStatsLog.LAUNCHER_EVENT,
@ -165,24 +233,24 @@ public class StatsLogCompatManager extends StatsLogManager {
null /* launcher extensions, deprecated */,
false /* quickstep_enabled, deprecated */,
event.getId() /* event_id */,
info.getItemCase().getNumber() /* target_id */,
atomInfo.getItemCase().getNumber() /* target_id */,
instanceId.getId() /* instance_id TODO */,
0 /* uid TODO */,
getPackageName(info) /* package_name */,
getComponentName(info) /* component_name */,
getGridX(info, false) /* grid_x */,
getGridY(info, false) /* grid_y */,
getPageId(info, false) /* page_id */,
getGridX(info, true) /* grid_x_parent */,
getGridY(info, true) /* grid_y_parent */,
getPageId(info, true) /* page_id_parent */,
getHierarchy(info) /* hierarchy */,
info.getIsWork() /* is_work_profile */,
info.getRank() /* rank */,
info.getFolderIcon().getFromLabelState().getNumber() /* fromState */,
info.getFolderIcon().getToLabelState().getNumber() /* toState */,
info.getFolderIcon().getLabelInfo() /* edittext */,
getCardinality(info) /* cardinality */);
getPackageName(atomInfo) /* package_name */,
getComponentName(atomInfo) /* component_name */,
getGridX(atomInfo, false) /* grid_x */,
getGridY(atomInfo, false) /* grid_y */,
getPageId(atomInfo, false) /* page_id */,
getGridX(atomInfo, true) /* grid_x_parent */,
getGridY(atomInfo, true) /* grid_y_parent */,
getPageId(atomInfo, true) /* page_id_parent */,
getHierarchy(atomInfo) /* hierarchy */,
atomInfo.getIsWork() /* is_work_profile */,
atomInfo.getRank() /* rank */,
atomInfo.getFolderIcon().getFromLabelState().getNumber() /* fromState */,
atomInfo.getFolderIcon().getToLabelState().getNumber() /* toState */,
atomInfo.getFolderIcon().getLabelInfo() /* edittext */,
getCardinality(atomInfo) /* cardinality */);
}
/**
@ -333,7 +401,7 @@ public class StatsLogCompatManager extends StatsLogManager {
}
private static String getStateString(int state) {
switch(state) {
switch (state) {
case LAUNCHER_UICHANGED__DST_STATE__BACKGROUND:
return "BACKGROUND";
case LAUNCHER_UICHANGED__DST_STATE__HOME:

View File

@ -31,6 +31,7 @@ import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.Workspace;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.hybridhotseat.HotseatPredictionController;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.statemanager.StateManager.StateListener;
import com.android.launcher3.util.OnboardingPrefs;
@ -100,6 +101,28 @@ public class QuickstepOnboardingPrefs extends OnboardingPrefs<BaseQuickstepLaunc
});
}
if (!hasReachedMaxCount(HOTSEAT_DISCOVERY_TIP_COUNT)) {
stateManager.addStateListener(new StateListener<LauncherState>() {
boolean mFromAllApps = false;
@Override
public void onStateTransitionStart(LauncherState toState) {
mFromAllApps = mLauncher.getStateManager().getCurrentStableState() == ALL_APPS;
}
@Override
public void onStateTransitionComplete(LauncherState finalState) {
HotseatPredictionController client = mLauncher.getHotseatPredictionController();
if (mFromAllApps && finalState == NORMAL && client.hasPredictions()) {
if (incrementEventCount(HOTSEAT_DISCOVERY_TIP_COUNT)) {
client.showDiscoveryTip();
stateManager.removeStateListener(this);
}
}
}
});
}
if (SysUINavigationMode.getMode(launcher) == NO_BUTTON
&& FeatureFlags.ENABLE_ALL_APPS_EDU.get()) {
stateManager.addStateListener(new StateListener<LauncherState>() {

View File

@ -26,8 +26,8 @@ import static android.view.Surface.ROTATION_90;
import static com.android.launcher3.logging.LoggerUtils.extractObjectNameAndAddress;
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;
@ -49,6 +49,7 @@ import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.Utilities;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.PagedOrientationHandler;
@ -197,7 +198,7 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre
mPreviousRotation = touchRotation;
if (mLauncherRotation == mTouchRotation || canLauncherRotate()) {
mOrientationHandler = PagedOrientationHandler.HOME_ROTATED;
mOrientationHandler = PagedOrientationHandler.PORTRAIT;
if (DEBUG) {
Log.d(TAG, "current RecentsOrientedState: " + this);
}
@ -526,4 +527,15 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre
+ " mFlags=" + mFlags
+ "]";
}
/**
* Returns the device profile based on expected launcher rotation
*/
public DeviceProfile getLauncherDeviceProfile() {
InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(mContext);
// TODO also check the natural orientation is landscape or portrait
return (mLauncherRotation == ROTATION_90 || mLauncherRotation == ROTATION_270)
? idp.landscapeProfile
: idp.portraitProfile;
}
}

View File

@ -23,6 +23,7 @@ import static com.android.launcher3.anim.Interpolators.ACCEL;
import static com.android.launcher3.anim.Interpolators.ACCEL_2;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
import static com.android.launcher3.util.SystemUiController.UI_STATE_SCRIM_VIEW;
import android.content.Context;
import android.graphics.Canvas;
@ -187,6 +188,7 @@ public class ShelfScrimView extends ScrimView<BaseQuickstepLauncher>
mShelfTopAtThreshold = mShiftRange * SCRIM_CATCHUP_THRESHOLD + mTopOffset;
}
updateColors();
updateSysUiColors();
updateDragHandleAlpha();
invalidate();
}
@ -240,6 +242,18 @@ public class ShelfScrimView extends ScrimView<BaseQuickstepLauncher>
}
}
@Override
protected void updateSysUiColors() {
// Use a light system UI (dark icons) if all apps is behind at least half of the
// status bar.
boolean forceChange = mShelfTop <= mLauncher.getDeviceProfile().getInsets().top / 2f;
if (forceChange) {
mLauncher.getSystemUiController().updateUiState(UI_STATE_SCRIM_VIEW, !mIsScrimDark);
} else {
mLauncher.getSystemUiController().updateUiState(UI_STATE_SCRIM_VIEW, 0);
}
}
@Override
protected boolean shouldDragHandleBeVisible() {
boolean needsAllAppsEdu = mIsTwoZoneSwipeModel

View File

@ -37,7 +37,6 @@ import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import android.app.Instrumentation;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.pm.ActivityInfo;
@ -58,10 +57,8 @@ import com.android.launcher3.testcomponent.TestCommandReceiver;
import com.android.launcher3.util.Wait;
import com.android.launcher3.util.rule.FailureRewriterRule;
import com.android.launcher3.util.rule.FailureWatcher;
import com.android.quickstep.NavigationModeSwitchRule.NavigationModeSwitch;
import com.android.quickstep.views.RecentsView;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.RuleChain;
@ -95,7 +92,8 @@ public class FallbackRecentsTest {
mDevice = UiDevice.getInstance(instrumentation);
mDevice.setOrientationNatural();
mLauncher = new LauncherInstrumentation();
mLauncher.enableCheckEventsForSuccessfulGestures();
// b/143488140
//mLauncher.enableCheckEventsForSuccessfulGestures();
if (TestHelpers.isInLauncherProcess()) {
Utilities.enableRunningInTestHarnessForTests();
@ -132,9 +130,9 @@ public class FallbackRecentsTest {
}
}
@NavigationModeSwitch
// b/143488140
//@NavigationModeSwitch
@Test
@Ignore // b/143488140
public void goToOverviewFromHome() {
mDevice.pressHome();
assertTrue("Fallback Launcher not visible", mDevice.wait(Until.hasObject(By.pkg(
@ -143,9 +141,9 @@ public class FallbackRecentsTest {
mLauncher.getBackground().switchToOverview();
}
@NavigationModeSwitch
// b/143488140
//@NavigationModeSwitch
@Test
@Ignore // b/143488140
public void goToOverviewFromApp() {
startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
@ -178,9 +176,9 @@ public class FallbackRecentsTest {
return mLauncher.getBackground().switchToOverview();
}
@NavigationModeSwitch
// b/143488140
//@NavigationModeSwitch
@Test
@Ignore // b/143488140
public void testOverview() {
startAppFast(getAppPackageName());
startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));

View File

@ -45,6 +45,8 @@ public class StartLauncherViaGestureTests extends AbstractQuickStepTest {
@Before
public void setUp() throws Exception {
super.setUp();
// b/143488140
mLauncher.pressHome();
// Start an activity where the gestures start.
startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
}
@ -98,7 +100,6 @@ public class StartLauncherViaGestureTests extends AbstractQuickStepTest {
// The test action.
mLauncher.getBackground().switchToOverview();
}
// Workaround for b/157099707
mLauncher.pressHome();
}
}

View File

@ -34,6 +34,8 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:gravity="center"
android:layout_gravity="center_vertical"
android:textColor="@android:color/white"
@ -58,6 +60,5 @@
android:elevation="2dp"
android:layout_width="10dp"
android:layout_height="8dp"
android:layout_marginTop="-2dp"
android:layout_gravity="center_horizontal"/>
android:layout_marginTop="-2dp"/>
</merge>

View File

@ -172,8 +172,10 @@
<string name="folder_closed">Folder closed</string>
<!-- Folder renamed format -->
<string name="folder_renamed">Folder renamed to <xliff:g id="name" example="Games">%1$s</xliff:g></string>
<!-- Folder name format -->
<string name="folder_name_format">Folder: <xliff:g id="name" example="Games">%1$s</xliff:g></string>
<!-- Folder name format when folder has less than 4 items -->
<string name="folder_name_format_exact">Folder: <xliff:g id="name" example="Games">%1$s</xliff:g>, <xliff:g id="size" example="2">%2$d</xliff:g> items</string>
<!-- Folder name format when folder has 4 or more items shown in preview-->
<string name="folder_name_format_overflow">Folder: <xliff:g id="name" example="Games">%1$s</xliff:g>, <xliff:g id="size" example="2">%2$d</xliff:g> or more items</string>
<!-- Strings for the customization mode -->
<!-- Text for widget add button -->

View File

@ -89,6 +89,22 @@ public class DefaultLayoutProviderTest {
assertEquals(3, ((FolderInfo) info).contents.size());
}
@Test
public void testCustomProfileLoaded_with_folder_custom_title() throws Exception {
writeLayoutAndLoad(new LauncherLayoutBuilder().atHotseat(0).putFolder("CustomFolder")
.addApp(TEST_PACKAGE, TEST_PACKAGE)
.addApp(TEST_PACKAGE, TEST_PACKAGE)
.addApp(TEST_PACKAGE, TEST_PACKAGE)
.build());
// Verify folder
assertEquals(1, mModelHelper.getBgDataModel().workspaceItems.size());
ItemInfo info = mModelHelper.getBgDataModel().workspaceItems.get(0);
assertEquals(LauncherSettings.Favorites.ITEM_TYPE_FOLDER, info.itemType);
assertEquals(3, ((FolderInfo) info).contents.size());
assertEquals("CustomFolder", info.title.toString());
}
@Test
public void testCustomProfileLoaded_with_widget() throws Exception {
String pendingAppPkg = "com.test.pending";

View File

@ -47,6 +47,7 @@ public class LauncherLayoutBuilder {
private static final String ATTR_PACKAGE_NAME = "packageName";
private static final String ATTR_CLASS_NAME = "className";
private static final String ATTR_TITLE = "title";
private static final String ATTR_TITLE_TEXT = "titleText";
private static final String ATTR_SCREEN = "screen";
// x and y can be specified as negative integers, in which case -1 represents the
@ -145,8 +146,17 @@ public class LauncherLayoutBuilder {
}
public FolderBuilder putFolder(int titleResId) {
FolderBuilder folderBuilder = new FolderBuilder();
items.put(ATTR_TITLE, Integer.toString(titleResId));
return putFolder();
}
public FolderBuilder putFolder(String title) {
items.put(ATTR_TITLE_TEXT, title);
return putFolder();
}
private FolderBuilder putFolder() {
FolderBuilder folderBuilder = new FolderBuilder();
items.put(ATTR_CHILDREN, folderBuilder.mChildren);
mNodes.add(Pair.create(TAG_FOLDER, items));
return folderBuilder;

View File

@ -174,7 +174,8 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch
targetInfo.first, TYPE_WINDOW_STATE_CHANGED, targetInfo.second);
if (mIsOpen) {
performAccessibilityAction(AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
getAccessibilityInitialFocusView().performAccessibilityAction(
AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
}
ActivityContext.lookupContext(getContext()).getDragLayer()
.sendAccessibilityEvent(TYPE_WINDOW_CONTENT_CHANGED);
@ -184,6 +185,11 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch
return null;
}
/** Returns the View that Accessibility services should focus on first. */
protected View getAccessibilityInitialFocusView() {
return this;
}
/**
* Returns a view matching FloatingViewType
*/

View File

@ -132,6 +132,7 @@ public class AutoInstallsLayout {
private static final String ATTR_PACKAGE_NAME = "packageName";
private static final String ATTR_CLASS_NAME = "className";
private static final String ATTR_TITLE = "title";
private static final String ATTR_TITLE_TEXT = "titleText";
private static final String ATTR_SCREEN = "screen";
// x and y can be specified as negative integers, in which case -1 represents the
@ -585,7 +586,8 @@ public class AutoInstallsLayout {
if (titleResId != 0) {
title = mSourceRes.getString(titleResId);
} else {
title = "";
String titleText = getAttributeValue(parser, ATTR_TITLE_TEXT);
title = TextUtils.isEmpty(titleText) ? "" : titleText;
}
mValues.put(Favorites.TITLE, title);

View File

@ -43,6 +43,8 @@ import android.widget.Toast;
import androidx.annotation.Nullable;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.logging.InstanceId;
import com.android.launcher3.logging.InstanceIdSequence;
import com.android.launcher3.model.AppLaunchTracker;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
@ -188,7 +190,8 @@ public abstract class BaseDraggingActivity extends BaseActivity
}
getUserEventDispatcher().logAppLaunch(v, intent, user);
if (item != null) {
getStatsLogManager().log(LAUNCHER_APP_LAUNCH_TAP, item.buildProto());
InstanceId instanceId = new InstanceIdSequence().newInstanceId();
logAppLaunch(item, instanceId);
}
return true;
} catch (NullPointerException|ActivityNotFoundException|SecurityException e) {
@ -198,6 +201,10 @@ public abstract class BaseDraggingActivity extends BaseActivity
return false;
}
protected void logAppLaunch(ItemInfo info, InstanceId instanceId) {
getStatsLogManager().log(LAUNCHER_APP_LAUNCH_TAP, instanceId, info);
}
private void startShortcutIntentSafely(Intent intent, Bundle optsBundle, ItemInfo info,
@Nullable String sourceContainer) {
try {

View File

@ -163,6 +163,7 @@ public class LauncherSettings {
public static final int CONTAINER_SEARCH_RESULTS = -106;
public static final int CONTAINER_SHORTCUTS = -107;
public static final int CONTAINER_SETTINGS = -108;
public static final int CONTAINER_TASKSWITCHER = -109;
public static final String containerToString(int container) {
switch (container) {
@ -249,6 +250,12 @@ public class LauncherSettings {
*/
public static final int ITEM_TYPE_DEEP_SHORTCUT = 6;
/**
* Type of the item is recents task.
* TODO(hyunyoungs): move constants not related to Favorites DB to a better location.
*/
public static final int ITEM_TYPE_TASK = 7;
/**
* The appWidgetId of the widget
*

View File

@ -606,6 +606,7 @@ public final class Utilities {
outObj[0] = activityInfo;
return activityInfo.getFullResIcon(appState.getIconCache());
}
if (info.getIntent() == null || info.getIntent().getPackage() == null) return null;
List<ShortcutInfo> si = ShortcutKey.fromItemInfo(info)
.buildRequest(launcher)
.query(ShortcutRequest.ALL);

View File

@ -418,10 +418,7 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
mStatsLogManager.log(
LauncherEvent.LAUNCHER_ITEM_DRAG_STARTED,
dragObject.logInstanceId,
dragObject.dragSource instanceof Folder
? dragObject.originalDragInfo
.buildProto(((Folder) dragObject.dragSource).mInfo)
: dragObject.originalDragInfo.buildProto()
dragObject.originalDragInfo
);
}
@ -1652,7 +1649,7 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
mStatsLogManager.log(
LauncherEvent.LAUNCHER_ITEM_DROP_FOLDER_CREATED,
d.logInstanceId,
destInfo.buildProto(null));
destInfo);
FolderIcon fi = mLauncher.addFolder(target, container, screenId, targetCell[0],
targetCell[1]);
destInfo.cellX = -1;
@ -1693,7 +1690,7 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
mStatsLogManager.log(
LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED,
d.logInstanceId,
fi.mInfo.buildProto(null));
fi.mInfo);
fi.onDrop(d, false /* itemReturnedOnFailedDrop */);
// if the drag started here, we need to remove it from the workspace
@ -1899,7 +1896,7 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
mStatsLogManager.log(
LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED,
d.logInstanceId,
d.dragInfo.buildProto(null));
d.dragInfo);
}
if (d.stateAnnouncer != null && !droppedOnOriginalCell) {
@ -2440,7 +2437,7 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
mStatsLogManager.log(
LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED,
d.logInstanceId,
d.dragInfo.buildProto(null));
d.dragInfo);
}
};
boolean isWidget = pendingInfo.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
@ -2532,7 +2529,7 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
mStatsLogManager.log(
LauncherEvent.LAUNCHER_ITEM_DROP_COMPLETED,
d.logInstanceId,
d.dragInfo.buildProto(null));
d.dragInfo);
}
}

View File

@ -14,7 +14,6 @@ import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_FA
import static com.android.launcher3.states.StateAnimationConfig.ANIM_ALL_APPS_HEADER_FADE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_SCALE;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_VERTICAL_PROGRESS;
import static com.android.launcher3.util.SystemUiController.UI_STATE_ALL_APPS;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@ -37,7 +36,6 @@ import com.android.launcher3.anim.PropertySetter;
import com.android.launcher3.statemanager.StateManager.StateHandler;
import com.android.launcher3.states.StateAnimationConfig;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ScrimView;
import com.android.systemui.plugins.AllAppsSearchPlugin;
import com.android.systemui.plugins.PluginListener;
@ -75,7 +73,6 @@ public class AllAppsTransitionController implements StateHandler<LauncherState>,
private ScrimView mScrimView;
private final Launcher mLauncher;
private final boolean mIsDarkTheme;
private boolean mIsVerticalLayout;
// Animation in this class is controlled by a single variable {@link mProgress}.
@ -98,7 +95,6 @@ public class AllAppsTransitionController implements StateHandler<LauncherState>,
mShiftRange = mLauncher.getDeviceProfile().heightPx;
mProgress = 1f;
mIsDarkTheme = Themes.getAttrBoolean(mLauncher, R.attr.isMainColorDark);
mIsVerticalLayout = mLauncher.getDeviceProfile().isVerticalBarLayout();
mLauncher.addOnDeviceProfileChangeListener(this);
}
@ -137,16 +133,6 @@ public class AllAppsTransitionController implements StateHandler<LauncherState>,
if (mPlugin != null) {
mPlugin.setProgress(progress);
}
// Use a light system UI (dark icons) if all apps is behind at least half of the
// status bar.
boolean forceChange = Math.min(shiftCurrent, mScrimView.getVisualTop())
<= mLauncher.getDeviceProfile().getInsets().top / 2f;
if (forceChange) {
mLauncher.getSystemUiController().updateUiState(UI_STATE_ALL_APPS, !mIsDarkTheme);
} else {
mLauncher.getSystemUiController().updateUiState(UI_STATE_ALL_APPS, 0);
}
}
public float getProgress() {

View File

@ -35,6 +35,7 @@ public class FlingSpringAnim {
private final FlingAnimation mFlingAnim;
private SpringAnimation mSpringAnim;
private final boolean mSkipFlingAnim;
private float mTargetPosition;
@ -57,6 +58,10 @@ public class FlingSpringAnim {
.setMaxValue(maxValue);
mTargetPosition = targetPosition;
// We are already past the fling target, so skip it to avoid losing a frame of the spring.
mSkipFlingAnim = startPosition <= minValue && startVelocity < 0
|| startPosition >= maxValue && startVelocity > 0;
mFlingAnim.addEndListener(((animation, canceled, value, velocity) -> {
mSpringAnim = new SpringAnimation(object, property)
.setStartValue(value)
@ -84,6 +89,9 @@ public class FlingSpringAnim {
public void start() {
mFlingAnim.start();
if (mSkipFlingAnim) {
mFlingAnim.cancel();
}
}
public void end() {

View File

@ -745,6 +745,11 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
: getContext().getString(R.string.folder_closed));
}
@Override
protected View getAccessibilityInitialFocusView() {
return mContent.getFirstItem();
}
private void closeComplete(boolean wasAnimated) {
// TODO: Clear all active animations.
DragLayer parent = (DragLayer) getParent();
@ -1333,7 +1338,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
d.stateAnnouncer.completeAction(R.string.item_moved);
}
mStatsLogManager
.log(LAUNCHER_ITEM_DROP_COMPLETED, d.logInstanceId, d.dragInfo.buildProto(mInfo));
.log(LAUNCHER_ITEM_DROP_COMPLETED, d.logInstanceId, d.dragInfo);
}
// This is used so the item doesn't immediately appear in the folder when added. In one case
@ -1438,7 +1443,7 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
if (hasFocus) {
startEditingFolderName();
} else {
mStatsLogManager.log(LAUNCHER_FOLDER_LABEL_UPDATED, mInfo.buildProto());
mStatsLogManager.log(LAUNCHER_FOLDER_LABEL_UPDATED, mInfo);
logFolderLabelState();
mFolderName.dispatchBackKey();
}

View File

@ -201,8 +201,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel
icon.mActivity = activity;
icon.mDotRenderer = grid.mDotRendererWorkSpace;
icon.setContentDescription(
group.getContext().getString(R.string.folder_name_format, folderInfo.title));
icon.setContentDescription(icon.getAccessiblityTitle(folderInfo.title));
// Keep the notification dot up to date with the sum of all the content's dots.
FolderDotInfo folderDotInfo = new FolderDotInfo();
@ -450,7 +449,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel
}
mInfo.setTitle(nameInfos[0].getLabel());
StatsLogManager.newInstance(getContext())
.log(LAUNCHER_FOLDER_LABEL_UPDATED, instanceId, mInfo.buildProto());
.log(LAUNCHER_FOLDER_LABEL_UPDATED, instanceId, mInfo);
onTitleChanged(mInfo.title);
mFolder.mFolderName.setText(mInfo.title);
mFolder.mLauncher.getModelWriter().updateItemInDatabase(mInfo);
@ -665,6 +664,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel
mDotInfo.addDotInfo(mActivity.getDotInfoForItem(item));
boolean isDotted = mDotInfo.hasDot();
updateDotScale(wasDotted, isDotted);
setContentDescription(getAccessiblityTitle(mInfo.title));
invalidate();
requestLayout();
}
@ -675,13 +675,14 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel
mDotInfo.subtractDotInfo(mActivity.getDotInfoForItem(item));
boolean isDotted = mDotInfo.hasDot();
updateDotScale(wasDotted, isDotted);
setContentDescription(getAccessiblityTitle(mInfo.title));
invalidate();
requestLayout();
}
public void onTitleChanged(CharSequence title) {
mFolderName.setText(title);
setContentDescription(getContext().getString(R.string.folder_name_format, title));
setContentDescription(getAccessiblityTitle(title));
}
@Override
@ -775,4 +776,17 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel
public void getWorkspaceVisualDragBounds(Rect bounds) {
getPreviewBounds(bounds);
}
/**
* Returns a formatted accessibility title for folder
*/
public String getAccessiblityTitle(CharSequence title) {
int size = mInfo.contents.size();
if (size < MAX_NUM_ITEMS_IN_PREVIEW) {
return getContext().getString(R.string.folder_name_format_exact, title, size);
} else {
return getContext().getString(R.string.folder_name_format_overflow, title,
MAX_NUM_ITEMS_IN_PREVIEW);
}
}
}

View File

@ -70,8 +70,11 @@ public class FolderNameEditText extends ExtendedEditText {
for (int i = 0; i < cnt; i++) {
cInfo[i] = new CompletionInfo(i, i, suggestList.get(i));
}
post(() -> getContext().getSystemService(InputMethodManager.class)
.displayCompletions(this, cInfo));
// post it to future frame so that onSelectionChanged, onFocusChanged, all other
// TextView flag change and IME animation has settled. Ideally, there should be IMM
// callback to notify when the IME animation and state handling is finished.
postDelayed(() -> getContext().getSystemService(InputMethodManager.class)
.displayCompletions(this, cInfo), 40 /* 2~3 frame delay */);
}
/**

View File

@ -44,6 +44,13 @@ public class InstanceIdSequence {
mInstanceIdMax = min(max(1, instanceIdMax), InstanceId.INSTANCE_ID_MAX);
}
/**
* Constructs a sequence with identifiers [1, InstanceId.INSTANCE_ID_MAX].
*/
public InstanceIdSequence() {
this(InstanceId.INSTANCE_ID_MAX);
}
/**
* Gets the next instance from the sequence. Safe for concurrent use.
* @return new InstanceId

View File

@ -21,8 +21,9 @@ import androidx.annotation.Nullable;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.logger.LauncherAtom.ItemInfo;
import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.logging.StatsLogUtils.LogStateProvider;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.util.ResourceBasedOverride;
/**
@ -127,9 +128,29 @@ public class StatsLogManager implements ResourceBasedOverride {
LAUNCHER_SYSTEM_SHORTCUT_PIN_TAP(522),
@UiEvent(doc = "User is shown All Apps education view.")
LAUNCHER_ALL_APPS_EDU_SHOWN(523);
// ADD MORE
LAUNCHER_ALL_APPS_EDU_SHOWN(523),
@UiEvent(doc = "User opened a folder.")
LAUNCHER_FOLDER_OPEN(551),
@UiEvent(doc = "Hotseat education half sheet seen")
LAUNCHER_HOTSEAT_EDU_SEEN(479),
@UiEvent(doc = "Hotseat migration accepted")
LAUNCHER_HOTSEAT_EDU_ACCEPT(480),
@UiEvent(doc = "Hotseat migration denied")
LAUNCHER_HOTSEAT_EDU_DENY(481),
@UiEvent(doc = "Hotseat education tip shown")
LAUNCHER_HOTSEAT_EDU_ONLY_TIP(482),
@UiEvent(doc = "App launch ranking logged for all apps predictions")
LAUNCHER_ALL_APPS_RANKED(552),
@UiEvent(doc = "App launch ranking logged for hotseat predictions)")
LAUNCHER_HOTSEAT_RANKED(553);
// ADD MORE
private final int mId;
LauncherEvent(int id) {
@ -177,25 +198,56 @@ public class StatsLogManager implements ResourceBasedOverride {
}
/**
* Logs a {@link EventEnum}.
* Logs an event.
*
* @param event an enum implementing EventEnum interface.
*/
public void log(EventEnum event) {
}
/**
* Logs an event and accompanying {@link InstanceId}.
* Logs an event.
*
* @param event an enum implementing EventEnum interface.
* @param instanceId an identifier obtained from an InstanceIdSequence.
*/
public void log(EventEnum event, InstanceId instanceId) {
}
/**
* Logs an event and accompanying {@link ItemInfo}.
* Logs an event.
*
* @param event an enum implementing EventEnum interface.
* @param itemInfo item typically containing app or task launch related information.
*/
public void log(EventEnum event, @Nullable ItemInfo itemInfo) {
}
/**
* Logs an event and accompanying {@link InstanceId} and {@link ItemInfo}.
* Logs an event.
*
* @param event an enum implementing EventEnum interface.
* @param atomInfo item typically containing app or task launch related information.
*/
public void log(EventEnum event, InstanceId instanceId, LauncherAtom.ItemInfo atomInfo) {
}
/**
* Logs an event and accompanying {@link LauncherState}s.
*
* @param event an enum implementing EventEnum interface.
* @param launcherAtomItemInfo item typically containing app or task launch related information.
*/
public void log(EventEnum event, @Nullable LauncherAtom.ItemInfo launcherAtomItemInfo,
int srcState, int dstState) {
}
/**
* Logs an event.
*
* @param event an enum implementing EventEnum interface.
* @param instanceId an identifier obtained from an InstanceIdSequence.
* @param itemInfo item typically containing app or task launch related information.
*/
public void log(EventEnum event, InstanceId instanceId, @Nullable ItemInfo itemInfo) {
}
@ -204,7 +256,7 @@ public class StatsLogManager implements ResourceBasedOverride {
* Log an event with ranked-choice information along with package. Does nothing if event.getId()
* <= 0.
*
* @param rankingEvent an enum implementing UiEventEnum interface.
* @param rankingEvent an enum implementing EventEnum interface.
* @param instanceId An identifier obtained from an InstanceIdSequence.
* @param packageName the package name of the relevant app, if known (null otherwise).
* @param position the position picked.

View File

@ -48,7 +48,6 @@ import androidx.annotation.Nullable;
import com.android.launcher3.DropTarget;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.StatsLogUtils.LogContainerProvider;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.userevent.LauncherLogProto;
@ -143,14 +142,6 @@ public class UserEventDispatcher implements ResourceBasedOverride {
fillIntentInfo(itemTarget, intent, userHandle);
}
LauncherEvent event = newLauncherEvent(action, targets);
ItemInfo info = v == null ? null : (ItemInfo) v.getTag();
if (info != null && Utilities.IS_DEBUG_DEVICE && FeatureFlags.ENABLE_HYBRID_HOTSEAT.get()) {
final String pkg = info.getTargetComponent() != null
? info.getTargetComponent().getPackageName() : "unknown";
FileLog.d(TAG, "appLaunch: packageName:" + pkg
+ ",isWorkApp:" + (info.user != null && !Process.myUserHandle().equals(
userHandle)) + ",launchLocation:" + info.container);
}
dispatchUserEvent(event, intent);
mAppOrTaskLaunch = true;
}

View File

@ -59,7 +59,6 @@ import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
/**
* This class takes care of shrinking the workspace (by maximum of one row and one column), as a
@ -248,12 +247,25 @@ public class GridSizeMigrationTaskV2 {
/** Return what's in the src but not in the dest */
private static List<DbEntry> calcDiff(List<DbEntry> src, List<DbEntry> dest) {
Set<String> destSet = dest.parallelStream().map(DbEntry::getIntentStr).collect(
Collectors.toSet());
Set<String> destIntentSet = new HashSet<>();
Set<Set<String>> destFolderIntentSet = new HashSet<>();
for (DbEntry entry : dest) {
if (entry.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
destFolderIntentSet.add(entry.mFolderItems.keySet());
} else {
destIntentSet.add(entry.mIntent);
}
}
List<DbEntry> diff = new ArrayList<>();
for (DbEntry entry : src) {
if (!destSet.contains(entry.mIntent)) {
diff.add(entry);
if (entry.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
if (!destFolderIntentSet.contains(entry.mFolderItems.keySet())) {
diff.add(entry);
}
} else {
if (!destIntentSet.contains(entry.mIntent)) {
diff.add(entry);
}
}
}
return diff;

View File

@ -24,11 +24,13 @@ import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PREDICT
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_SEARCH_RESULTS;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_SETTINGS;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_SHORTCUTS;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_TASKSWITCHER;
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;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_TASK;
import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.CONTAINER_NOT_SET;
import android.content.ComponentName;
@ -49,6 +51,7 @@ import com.android.launcher3.logger.LauncherAtom.PredictionContainer;
import com.android.launcher3.logger.LauncherAtom.SearchResultContainer;
import com.android.launcher3.logger.LauncherAtom.SettingsContainer;
import com.android.launcher3.logger.LauncherAtom.ShortcutsContainer;
import com.android.launcher3.logger.LauncherAtom.TaskSwitcherContainer;
import com.android.launcher3.util.ContentWriter;
import java.util.Optional;
@ -298,6 +301,12 @@ public class ItemInfo {
.setSpanX(spanX)
.setSpanY(spanY));
break;
case ITEM_TYPE_TASK:
itemBuilder
.setTask(LauncherAtom.Task.newBuilder()
.setComponentName(getTargetComponent().flattenToShortString())
.setIndex(screenId));
break;
default:
break;
}
@ -378,6 +387,11 @@ public class ItemInfo {
return ContainerInfo.newBuilder()
.setSettingsContainer(SettingsContainer.getDefaultInstance())
.build();
case CONTAINER_TASKSWITCHER:
return ContainerInfo.newBuilder()
.setTaskSwitcherContainer(TaskSwitcherContainer.getDefaultInstance())
.build();
}
return ContainerInfo.getDefaultInstance();
}

View File

@ -108,7 +108,7 @@ public class NotificationInfo implements View.OnClickListener {
intent.send(null, 0, null, null, null, null, activityOptions);
launcher.getUserEventDispatcher().logNotificationLaunch(view, intent);
launcher.getStatsLogManager()
.log(LAUNCHER_NOTIFICATION_LAUNCH_TAP, mItemInfo.buildProto());
.log(LAUNCHER_NOTIFICATION_LAUNCH_TAP, mItemInfo);
} catch (PendingIntent.CanceledException e) {
e.printStackTrace();
}

View File

@ -389,6 +389,11 @@ public abstract class ArrowPopup<T extends BaseDraggingActivity> extends Abstrac
return Pair.create(this, "");
}
@Override
protected View getAccessibilityInitialFocusView() {
return getChildCount() > 0 ? getChildAt(0) : this;
}
private void animateOpen() {
setVisibility(View.VISIBLE);

View File

@ -78,7 +78,7 @@ public class RemoteActionShortcut extends SystemShortcut<BaseDraggingActivity> {
public void onClick(View view) {
AbstractFloatingView.closeAllOpenViews(mTarget);
mTarget.getStatsLogManager()
.log(LAUNCHER_SYSTEM_SHORTCUT_PAUSE_TAP, mItemInfo.buildProto());
.log(LAUNCHER_SYSTEM_SHORTCUT_PAUSE_TAP, mItemInfo);
final String actionIdentity = mAction.getTitle() + ", "
+ mItemInfo.getTargetComponent().getPackageName();

View File

@ -119,9 +119,7 @@ public abstract class SystemShortcut<T extends BaseDraggingActivity> extends Ite
widgetsBottomSheet.populateAndShow(mItemInfo);
mTarget.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
ControlType.WIDGETS_BUTTON, view);
// TODO(thiruram): Fix missing container info when item is inside folder.
mTarget.getStatsLogManager().log(LAUNCHER_SYSTEM_SHORTCUT_WIDGETS_TAP,
mItemInfo.buildProto());
mTarget.getStatsLogManager().log(LAUNCHER_SYSTEM_SHORTCUT_WIDGETS_TAP, mItemInfo);
}
}
@ -142,9 +140,8 @@ public abstract class SystemShortcut<T extends BaseDraggingActivity> extends Ite
mItemInfo, sourceBounds, ActivityOptions.makeBasic().toBundle());
mTarget.getUserEventDispatcher().logActionOnControl(Action.Touch.TAP,
ControlType.APPINFO_TARGET, view);
// TODO(thiruram): Fix missing container info when item is inside folder.
mTarget.getStatsLogManager()
.log(LAUNCHER_SYSTEM_SHORTCUT_APP_INFO_TAP, mItemInfo.buildProto());
.log(LAUNCHER_SYSTEM_SHORTCUT_APP_INFO_TAP, mItemInfo);
}
}

View File

@ -310,6 +310,9 @@ public abstract class AbstractStateChangeTouchController
}
protected void updateProgress(float fraction) {
if (mCurrentAnimation == null) {
return;
}
mCurrentAnimation.setPlayFraction(fraction);
if (mAtomicComponentsController != null) {
// Make sure we don't divide by 0, and have at least a small runway.

View File

@ -1,55 +0,0 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3.touch;
import android.graphics.RectF;
import android.view.Surface;
import android.widget.LinearLayout;
public class HomeRotatedPageHandler extends PortraitPagedViewHandler {
@Override
public void offsetTaskRect(RectF rect, float value, int displayRotation, int launcherRotation) {
if (launcherRotation == Surface.ROTATION_0) {
super.offsetTaskRect(rect, value, displayRotation, launcherRotation);
} else if (launcherRotation == Surface.ROTATION_90) {
if (displayRotation == Surface.ROTATION_0) {
rect.offset(0, value);
} else if (displayRotation == Surface.ROTATION_90) {
rect.offset(value, 0);
} else if (displayRotation == Surface.ROTATION_180) {
rect.offset(-value, 0);
} else {
rect.offset(-value, 0);
}
} else if (launcherRotation == Surface.ROTATION_270) {
if (displayRotation == Surface.ROTATION_0) {
rect.offset(0, -value);
} else if (displayRotation == Surface.ROTATION_90) {
rect.offset(value, 0);
} else if (displayRotation == Surface.ROTATION_180) {
rect.offset(0, -value);
} else {
rect.offset(value, 0);
}
} // TODO (b/149609488) handle 180 case as well
}
@Override
public int getTaskMenuLayoutOrientation(LinearLayout taskMenuLayout) {
return taskMenuLayout.getOrientation();
}
}

View File

@ -17,6 +17,7 @@ package com.android.launcher3.touch;
import static com.android.launcher3.Launcher.REQUEST_BIND_PENDING_APPWIDGET;
import static com.android.launcher3.Launcher.REQUEST_RECONFIGURE_APPWIDGET;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_OPEN;
import static com.android.launcher3.model.AppLaunchTracker.CONTAINER_ALL_APPS;
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_BY_PUBLISHER;
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_DISABLED_LOCKED_USER;
@ -45,6 +46,7 @@ import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderIcon;
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.ItemInfo;
@ -111,6 +113,7 @@ public class ItemClickHandler {
if (!folder.isOpen() && !folder.isDestroyed()) {
// Open the requested folder
folder.animateOpen();
StatsLogManager.newInstance(v.getContext()).log(LAUNCHER_FOLDER_OPEN, folder.mInfo);
}
}

View File

@ -180,19 +180,6 @@ public class LandscapePagedViewHandler implements PagedOrientationHandler {
return Surface.ROTATION_90;
}
@Override
public void offsetTaskRect(RectF rect, float value, int displayRotation, int launcherRotation) {
if (displayRotation == Surface.ROTATION_0) {
rect.offset(0, value);
} else if (displayRotation == Surface.ROTATION_90) {
rect.offset(value, 0);
} else if (displayRotation == Surface.ROTATION_180) {
rect.offset(0, -value);
} else {
rect.offset(-value, 0);
}
}
@Override
public int getChildStart(View view) {
return view.getTop();

View File

@ -43,7 +43,6 @@ public interface PagedOrientationHandler {
PagedOrientationHandler PORTRAIT = new PortraitPagedViewHandler();
PagedOrientationHandler LANDSCAPE = new LandscapePagedViewHandler();
PagedOrientationHandler SEASCAPE = new SeascapePagedViewHandler();
PagedOrientationHandler HOME_ROTATED = new HomeRotatedPageHandler();
interface Int2DAction<T> {
void call(T target, int x, int y);
@ -82,7 +81,6 @@ public interface PagedOrientationHandler {
boolean getRecentsRtlSetting(Resources resources);
float getDegreesRotated();
int getRotation();
void offsetTaskRect(RectF rect, float value, int delta, int launcherRotation);
int getPrimaryValue(int x, int y);
int getSecondaryValue(int x, int y);
void delegateScrollTo(PagedView pagedView, int secondaryScroll, int primaryScroll);

View File

@ -178,19 +178,6 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler {
return Surface.ROTATION_0;
}
@Override
public void offsetTaskRect(RectF rect, float value, int displayRotation, int launcherRotation) {
if (displayRotation == Surface.ROTATION_0) {
rect.offset(value, 0);
} else if (displayRotation == Surface.ROTATION_90) {
rect.offset(0, -value);
} else if (displayRotation == Surface.ROTATION_180) {
rect.offset(-value, 0);
} else {
rect.offset(0, value);
}
}
@Override
public int getChildStart(View view) {
return view.getLeft();
@ -250,7 +237,7 @@ public class PortraitPagedViewHandler implements PagedOrientationHandler {
@Override
public int getTaskMenuLayoutOrientation(LinearLayout taskMenuLayout) {
return LinearLayout.VERTICAL;
return taskMenuLayout.getOrientation();
}
@Override

View File

@ -18,7 +18,6 @@ package com.android.launcher3.touch;
import android.content.res.Resources;
import android.graphics.PointF;
import android.graphics.RectF;
import android.view.Surface;
import android.view.View;
@ -41,19 +40,6 @@ public class SeascapePagedViewHandler extends LandscapePagedViewHandler {
return Utilities.isRtl(resources);
}
@Override
public void offsetTaskRect(RectF rect, float value, int displayRotation, int launcherRotation) {
if (displayRotation == Surface.ROTATION_0) {
rect.offset(0, value);
} else if (displayRotation == Surface.ROTATION_90) {
rect.offset(value, 0);
} else if (displayRotation == Surface.ROTATION_180) {
rect.offset(0, -value);
} else {
rect.offset(-value, 0);
}
}
@Override
public float getDegreesRotated() {
return 270;

View File

@ -37,13 +37,14 @@ public class OnboardingPrefs<T extends Launcher> {
public static final String HOME_BOUNCE_COUNT = "launcher.home_bounce_count";
public static final String SHELF_BOUNCE_COUNT = "launcher.shelf_bounce_count";
public static final String ALL_APPS_COUNT = "launcher.all_apps_count";
public static final String HOTSEAT_DISCOVERY_TIP_COUNT = "launcher.hotseat_discovery_tip_count";
/**
* Events that either have happened or have not (booleans).
*/
@StringDef(value = {
HOME_BOUNCE_SEEN,
SHELF_BOUNCE_SEEN,
SHELF_BOUNCE_SEEN
})
@Retention(RetentionPolicy.SOURCE)
public @interface EventBoolKey {}
@ -55,6 +56,7 @@ public class OnboardingPrefs<T extends Launcher> {
HOME_BOUNCE_COUNT,
SHELF_BOUNCE_COUNT,
ALL_APPS_COUNT,
HOTSEAT_DISCOVERY_TIP_COUNT
})
@Retention(RetentionPolicy.SOURCE)
public @interface EventCountKey {}
@ -65,6 +67,7 @@ public class OnboardingPrefs<T extends Launcher> {
maxCounts.put(HOME_BOUNCE_COUNT, 3);
maxCounts.put(SHELF_BOUNCE_COUNT, 3);
maxCounts.put(ALL_APPS_COUNT, 5);
maxCounts.put(HOTSEAT_DISCOVERY_TIP_COUNT, 5);
MAX_COUNTS = Collections.unmodifiableMap(maxCounts);
}

View File

@ -30,7 +30,7 @@ public class SystemUiController {
// Various UI states in increasing order of priority
public static final int UI_STATE_BASE_WINDOW = 0;
public static final int UI_STATE_ALL_APPS = 1;
public static final int UI_STATE_SCRIM_VIEW = 1;
public static final int UI_STATE_WIDGET_BOTTOM_SHEET = 2;
public static final int UI_STATE_OVERVIEW = 3;
@ -61,25 +61,38 @@ public class SystemUiController {
// Apply the state flags in priority order
int newFlags = oldFlags;
for (int stateFlag : mStates) {
if (Utilities.ATLEAST_OREO) {
if ((stateFlag & FLAG_LIGHT_NAV) != 0) {
newFlags |= View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
} else if ((stateFlag & FLAG_DARK_NAV) != 0) {
newFlags &= ~(View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
}
}
if ((stateFlag & FLAG_LIGHT_STATUS) != 0) {
newFlags |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
} else if ((stateFlag & FLAG_DARK_STATUS) != 0) {
newFlags &= ~(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}
newFlags = getSysUiVisibilityFlags(stateFlag, newFlags);
}
if (newFlags != oldFlags) {
mWindow.getDecorView().setSystemUiVisibility(newFlags);
}
}
/**
* Returns the sysui visibility for the base layer
*/
public int getBaseSysuiVisibility() {
return getSysUiVisibilityFlags(
mStates[UI_STATE_BASE_WINDOW], mWindow.getDecorView().getSystemUiVisibility());
}
private int getSysUiVisibilityFlags(int stateFlag, int currentVisibility) {
if (Utilities.ATLEAST_OREO) {
if ((stateFlag & FLAG_LIGHT_NAV) != 0) {
currentVisibility |= View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR;
} else if ((stateFlag & FLAG_DARK_NAV) != 0) {
currentVisibility &= ~(View.SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR);
}
}
if ((stateFlag & FLAG_LIGHT_STATUS) != 0) {
currentVisibility |= View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR;
} else if ((stateFlag & FLAG_DARK_STATUS) != 0) {
currentVisibility &= ~(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}
return currentVisibility;
}
@Override
public String toString() {
return "mStates=" + Arrays.toString(mStates);

View File

@ -125,11 +125,35 @@ public class ArrowTipView extends AbstractFloatingView {
* Show Tip with specified string and Y location
*/
public ArrowTipView show(String text, int top) {
return show(text, Gravity.CENTER_HORIZONTAL, 0, top);
}
/**
* Show the ArrowTipView (tooltip) center, start, or end aligned.
*
* @param text The text to be shown in the tooltip.
* @param gravity The gravity aligns the tooltip center, start, or end.
* @param arrowMarginStart The margin from start to place arrow (ignored if center)
* @param top The Y coordinate of the bottom of tooltip.
* @return The tooltip.
*/
public ArrowTipView show(String text, int gravity, int arrowMarginStart, int top) {
((TextView) findViewById(R.id.text)).setText(text);
mActivity.getDragLayer().addView(this);
ViewGroup parent = mActivity.getDragLayer();
parent.addView(this);
DragLayer.LayoutParams params = (DragLayer.LayoutParams) getLayoutParams();
params.gravity = Gravity.CENTER_HORIZONTAL;
params.gravity = gravity;
LinearLayout.LayoutParams lp = (LinearLayout.LayoutParams) findViewById(
R.id.arrow).getLayoutParams();
lp.gravity = gravity;
if (gravity == Gravity.END) {
lp.setMarginEnd(parent.getMeasuredWidth() - arrowMarginStart);
} else if (gravity == Gravity.START) {
lp.setMarginStart(arrowMarginStart);
}
requestLayout();
params.leftMargin = mActivity.getDeviceProfile().workspacePadding.left;
params.rightMargin = mActivity.getDeviceProfile().workspacePadding.right;
post(() -> setY(top - getHeight()));

View File

@ -90,13 +90,23 @@ public abstract class BaseDragLayer<T extends Context & ActivityContext>
}
};
// Touch is being dispatched through the normal view dispatch system
private static final int TOUCH_DISPATCHING_VIEW = 1 << 0;
// Touch coming from normal view system is being dispatched.
private static final int TOUCH_DISPATCHING_FROM_VIEW = 1 << 0;
// Touch is being dispatched through the normal view dispatch system, and started at the
// system gesture region
private static final int TOUCH_DISPATCHING_GESTURE = 1 << 1;
// Touch is being dispatched through a proxy from InputMonitor
private static final int TOUCH_DISPATCHING_PROXY = 1 << 2;
// system gesture region. In this case we prevent internal gesture handling and only allow
// normal view event handling.
private static final int TOUCH_DISPATCHING_FROM_VIEW_GESTURE_REGION = 1 << 1;
// Touch coming from InputMonitor proxy is being dispatched 'only to gestures'. Note that both
// this and view-system can be active at the same time where view-system would go to the views,
// and this would go to the gestures.
// Note that this is not set when events are coming from proxy, but going through full dispatch
// process (both views and gestures) to allow view-system to easily take over in case it
// comes later.
private static final int TOUCH_DISPATCHING_FROM_PROXY = 1 << 2;
// ACTION_DOWN has been dispatched to child views and ACTION_UP or ACTION_CANCEL is pending.
// Note that the event source can either be view-dispatching or proxy-dispatching based on if
// TOUCH_DISPATCHING_VIEW is present or not.
private static final int TOUCH_DISPATCHING_TO_VIEW_IN_PROGRESS = 1 << 3;
protected final float[] mTmpXY = new float[2];
protected final float[] mTmpRectPoints = new float[4];
@ -204,7 +214,8 @@ public abstract class BaseDragLayer<T extends Context & ActivityContext>
protected boolean findActiveController(MotionEvent ev) {
mActiveController = null;
if ((mTouchDispatchState & (TOUCH_DISPATCHING_GESTURE | TOUCH_DISPATCHING_PROXY)) == 0) {
if ((mTouchDispatchState & (TOUCH_DISPATCHING_FROM_VIEW_GESTURE_REGION
| TOUCH_DISPATCHING_FROM_PROXY)) == 0) {
// Only look for controllers if we are not dispatching from gesture area and proxy is
// not active
mActiveController = findControllerToHandleTouch(ev);
@ -283,19 +294,28 @@ public abstract class BaseDragLayer<T extends Context & ActivityContext>
public boolean dispatchTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case ACTION_DOWN: {
mTouchDispatchState |= TOUCH_DISPATCHING_VIEW;
if ((mTouchDispatchState & TOUCH_DISPATCHING_TO_VIEW_IN_PROGRESS) != 0) {
// Cancel the previous touch
int action = ev.getAction();
ev.setAction(ACTION_CANCEL);
super.dispatchTouchEvent(ev);
ev.setAction(action);
}
mTouchDispatchState |= TOUCH_DISPATCHING_FROM_VIEW
| TOUCH_DISPATCHING_TO_VIEW_IN_PROGRESS;
if (isEventInLauncher(ev)) {
mTouchDispatchState &= ~TOUCH_DISPATCHING_GESTURE;
mTouchDispatchState &= ~TOUCH_DISPATCHING_FROM_VIEW_GESTURE_REGION;
} else {
mTouchDispatchState |= TOUCH_DISPATCHING_GESTURE;
mTouchDispatchState |= TOUCH_DISPATCHING_FROM_VIEW_GESTURE_REGION;
}
break;
}
case ACTION_CANCEL:
case ACTION_UP:
mTouchDispatchState &= ~TOUCH_DISPATCHING_GESTURE;
mTouchDispatchState &= ~TOUCH_DISPATCHING_VIEW;
mTouchDispatchState &= ~TOUCH_DISPATCHING_FROM_VIEW_GESTURE_REGION;
mTouchDispatchState &= ~TOUCH_DISPATCHING_FROM_VIEW;
mTouchDispatchState &= ~TOUCH_DISPATCHING_TO_VIEW_IN_PROGRESS;
break;
}
super.dispatchTouchEvent(ev);
@ -304,44 +324,54 @@ public abstract class BaseDragLayer<T extends Context & ActivityContext>
return true;
}
/**
* Called before we are about to receive proxy events.
*
* @return false if we can't handle proxy at this time
*/
public boolean prepareProxyEventStarting() {
mProxyTouchController = null;
if ((mTouchDispatchState & TOUCH_DISPATCHING_VIEW) != 0 && mActiveController != null) {
// We are already dispatching using view system and have an active controller, we can't
// handle another controller.
// This flag was already cleared in proxy ACTION_UP or ACTION_CANCEL. Added here just
// to be safe
mTouchDispatchState &= ~TOUCH_DISPATCHING_PROXY;
return false;
}
mTouchDispatchState |= TOUCH_DISPATCHING_PROXY;
return true;
}
/**
* Proxies the touch events to the gesture handlers
*/
public boolean proxyTouchEvent(MotionEvent ev) {
boolean handled;
if (mProxyTouchController != null) {
handled = mProxyTouchController.onControllerTouchEvent(ev);
public boolean proxyTouchEvent(MotionEvent ev, boolean allowViewDispatch) {
int actionMasked = ev.getActionMasked();
boolean isViewDispatching = (mTouchDispatchState & TOUCH_DISPATCHING_FROM_VIEW) != 0;
// Only do view dispatch if another view-dispatching is not running, or we already started
// proxy-dispatching before. Note that view-dispatching can always take over the proxy
// dispatching at anytime, but not vice-versa.
allowViewDispatch = allowViewDispatch && !isViewDispatching
&& (actionMasked == ACTION_DOWN
|| ((mTouchDispatchState & TOUCH_DISPATCHING_TO_VIEW_IN_PROGRESS) != 0));
if (allowViewDispatch) {
mTouchDispatchState |= TOUCH_DISPATCHING_TO_VIEW_IN_PROGRESS;
super.dispatchTouchEvent(ev);
if (actionMasked == ACTION_UP || actionMasked == ACTION_CANCEL) {
mTouchDispatchState &= ~TOUCH_DISPATCHING_TO_VIEW_IN_PROGRESS;
mTouchDispatchState &= ~TOUCH_DISPATCHING_FROM_PROXY;
}
return true;
} else {
mProxyTouchController = findControllerToHandleTouch(ev);
handled = mProxyTouchController != null;
boolean handled;
if (mProxyTouchController != null) {
handled = mProxyTouchController.onControllerTouchEvent(ev);
} else {
if (actionMasked == ACTION_DOWN) {
if (isViewDispatching && mActiveController != null) {
// A controller is already active, we can't initiate our own controller
mTouchDispatchState &= ~TOUCH_DISPATCHING_FROM_PROXY;
} else {
// We will control the handler via proxy
mTouchDispatchState |= TOUCH_DISPATCHING_FROM_PROXY;
}
}
if ((mTouchDispatchState & TOUCH_DISPATCHING_FROM_PROXY) != 0) {
mProxyTouchController = findControllerToHandleTouch(ev);
}
handled = mProxyTouchController != null;
}
if (actionMasked == ACTION_UP || actionMasked == ACTION_CANCEL) {
mProxyTouchController = null;
mTouchDispatchState &= ~TOUCH_DISPATCHING_FROM_PROXY;
}
return handled;
}
int action = ev.getAction();
if (action == ACTION_UP || action == ACTION_CANCEL) {
mProxyTouchController = null;
mTouchDispatchState &= ~TOUCH_DISPATCHING_PROXY;
}
return handled;
}
/**

View File

@ -27,6 +27,7 @@ import static com.android.launcher3.anim.Interpolators.DEACCEL;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.clampToProgress;
import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
import static com.android.launcher3.util.SystemUiController.UI_STATE_SCRIM_VIEW;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@ -53,6 +54,7 @@ import android.view.accessibility.AccessibilityManager.AccessibilityStateChangeL
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.graphics.ColorUtils;
import androidx.core.view.ViewCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat;
@ -77,7 +79,6 @@ import com.android.launcher3.widget.WidgetsFullSheet;
import java.util.List;
/**
* Simple scrim which draws a flat color
*/
@ -115,6 +116,7 @@ public class ScrimView<T extends Launcher> extends View implements Insettable, O
private final WallpaperColorInfo mWallpaperColorInfo;
private final AccessibilityManager mAM;
protected final int mEndScrim;
protected final boolean mIsScrimDark;
private final StateListener<LauncherState> mAccessibilityLauncherStateListener =
new StateListener<LauncherState>() {
@ -156,6 +158,7 @@ public class ScrimView<T extends Launcher> extends View implements Insettable, O
mLauncher = Launcher.cast(Launcher.getLauncher(context));
mWallpaperColorInfo = WallpaperColorInfo.INSTANCE.get(context);
mEndScrim = Themes.getAttrColor(context, R.attr.allAppsScrimColor);
mIsScrimDark = ColorUtils.calculateLuminance(mEndScrim) < 0.5f;
mMaxScrimAlpha = 0.7f;
@ -233,6 +236,7 @@ public class ScrimView<T extends Launcher> extends View implements Insettable, O
mProgress = progress;
stopDragHandleEducationAnim();
updateColors();
updateSysUiColors();
updateDragHandleAlpha();
invalidate();
}
@ -245,6 +249,17 @@ public class ScrimView<T extends Launcher> extends View implements Insettable, O
mEndFlatColor, Math.round((1 - mProgress) * mEndFlatColorAlpha));
}
protected void updateSysUiColors() {
// Use a light system UI (dark icons) if all apps is behind at least half of the
// status bar.
boolean forceChange = mProgress <= 0.1f;
if (forceChange) {
mLauncher.getSystemUiController().updateUiState(UI_STATE_SCRIM_VIEW, !mIsScrimDark);
} else {
mLauncher.getSystemUiController().updateUiState(UI_STATE_SCRIM_VIEW, 0);
}
}
protected void updateDragHandleAlpha() {
if (mDragHandle != null) {
mDragHandle.setAlpha(mDragHandleAlpha);