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:
commit
1bb17f2658
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -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 */)));
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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(),
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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>() {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 -->
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
*
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 */);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()));
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue