Snap for 7863959 from 92a6bccb4c to tm-release
Change-Id: I1a2b1fc9a95f7029512f450525ff8c7e9c3cec79
This commit is contained in:
@@ -73,6 +73,7 @@
|
||||
launcher:containerType="hotseat" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/button_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="@dimen/bottom_sheet_edu_padding"
|
||||
|
||||
@@ -193,6 +193,8 @@
|
||||
<string name="action_split">Split</string>
|
||||
<!-- Label for toast with instructions for split screen selection mode. [CHAR_LIMIT=50] -->
|
||||
<string name="toast_split_select_app">Tap another app to use splitscreen</string>
|
||||
<!-- Label for toast when app selected for split isn't supported. [CHAR_LIMIT=50] -->
|
||||
<string name="toast_split_app_unsupported">App does not support split-screen.</string>
|
||||
<!-- Message shown when an action is blocked by a policy enforced by the app or the organization managing the device. [CHAR_LIMIT=NONE] -->
|
||||
<string name="blocked_by_policy">This action isn\'t allowed by the app or your organization</string>
|
||||
|
||||
|
||||
@@ -28,17 +28,20 @@ import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
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.InvariantDeviceProfile;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.anim.Interpolators;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
||||
import com.android.launcher3.uioverrides.ApiWrapper;
|
||||
import com.android.launcher3.uioverrides.PredictedAppIcon;
|
||||
import com.android.launcher3.views.AbstractSlideInView;
|
||||
|
||||
@@ -89,8 +92,9 @@ public class HotseatEduDialog extends AbstractSlideInView<Launcher> implements I
|
||||
mHotseatWrapper = findViewById(R.id.hotseat_wrapper);
|
||||
mSampleHotseat = findViewById(R.id.sample_prediction);
|
||||
|
||||
Context context = getContext();
|
||||
DeviceProfile grid = mActivityContext.getDeviceProfile();
|
||||
Rect padding = grid.getHotseatLayoutPadding(getContext());
|
||||
Rect padding = grid.getHotseatLayoutPadding(context);
|
||||
|
||||
mSampleHotseat.getLayoutParams().height = grid.cellHeightPx;
|
||||
mSampleHotseat.setGridSize(grid.numShownHotseatIcons, 1);
|
||||
@@ -102,6 +106,15 @@ public class HotseatEduDialog extends AbstractSlideInView<Launcher> implements I
|
||||
mDismissBtn = findViewById(R.id.no_thanks);
|
||||
mDismissBtn.setOnClickListener(this::onDismiss);
|
||||
|
||||
LinearLayout buttonContainer = findViewById(R.id.button_container);
|
||||
int adjustedMarginEnd = ApiWrapper.getHotseatEndOffset(context)
|
||||
- buttonContainer.getPaddingEnd();
|
||||
if (InvariantDeviceProfile.INSTANCE.get(context)
|
||||
.getDeviceProfile(context).isTaskbarPresent && adjustedMarginEnd > 0) {
|
||||
((LinearLayout.LayoutParams) buttonContainer.getLayoutParams()).setMarginEnd(
|
||||
adjustedMarginEnd);
|
||||
}
|
||||
|
||||
// update ui to reflect which migration method is going to be used
|
||||
if (FeatureFlags.HOTSEAT_MIGRATE_TO_FOLDER.get()) {
|
||||
((TextView) findViewById(R.id.hotseat_edu_content)).setText(
|
||||
|
||||
@@ -16,33 +16,25 @@
|
||||
package com.android.launcher3.hybridhotseat;
|
||||
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
|
||||
import static com.android.launcher3.model.PredictionHelper.getAppTargetFromItemInfo;
|
||||
import static com.android.launcher3.model.PredictionHelper.isTrackedForHotseatPrediction;
|
||||
import static com.android.launcher3.model.PredictionHelper.wrapAppTargetWithItemLocation;
|
||||
|
||||
import android.app.prediction.AppTarget;
|
||||
import android.app.prediction.AppTargetEvent;
|
||||
import android.app.prediction.AppTargetId;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.android.launcher3.LauncherSettings;
|
||||
import com.android.launcher3.Workspace;
|
||||
import com.android.launcher3.model.BgDataModel;
|
||||
import com.android.launcher3.model.BgDataModel.FixedContainerItems;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
|
||||
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
||||
import com.android.launcher3.shortcuts.ShortcutKey;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* Model helper for app predictions in workspace
|
||||
*/
|
||||
public class HotseatPredictionModel {
|
||||
private static final String APP_LOCATION_HOTSEAT = "hotseat";
|
||||
private static final String APP_LOCATION_WORKSPACE = "workspace";
|
||||
|
||||
private static final String BUNDLE_KEY_PIN_EVENTS = "pin_events";
|
||||
private static final String BUNDLE_KEY_CURRENT_ITEMS = "current_items";
|
||||
|
||||
@@ -54,15 +46,15 @@ public class HotseatPredictionModel {
|
||||
ArrayList<AppTargetEvent> events = new ArrayList<>();
|
||||
ArrayList<ItemInfo> workspaceItems = dataModel.getAllWorkspaceItems();
|
||||
for (ItemInfo item : workspaceItems) {
|
||||
AppTarget target = getAppTargetFromInfo(context, item);
|
||||
if (target != null && !isTrackedForPrediction(item)) continue;
|
||||
events.add(wrapAppTargetWithLocation(target, AppTargetEvent.ACTION_PIN, item));
|
||||
AppTarget target = getAppTargetFromItemInfo(context, item);
|
||||
if (target != null && !isTrackedForHotseatPrediction(item)) continue;
|
||||
events.add(wrapAppTargetWithItemLocation(target, AppTargetEvent.ACTION_PIN, item));
|
||||
}
|
||||
ArrayList<AppTarget> currentTargets = new ArrayList<>();
|
||||
FixedContainerItems hotseatItems = dataModel.extraItems.get(CONTAINER_HOTSEAT_PREDICTION);
|
||||
if (hotseatItems != null) {
|
||||
for (ItemInfo itemInfo : hotseatItems.items) {
|
||||
AppTarget target = getAppTargetFromInfo(context, itemInfo);
|
||||
AppTarget target = getAppTargetFromItemInfo(context, itemInfo);
|
||||
if (target != null) currentTargets.add(target);
|
||||
}
|
||||
}
|
||||
@@ -70,56 +62,4 @@ public class HotseatPredictionModel {
|
||||
bundle.putParcelableArrayList(BUNDLE_KEY_CURRENT_ITEMS, currentTargets);
|
||||
return bundle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns for {@link AppTarget} object given an {@link ItemInfo}. Returns null
|
||||
* if item is not supported prediction
|
||||
*/
|
||||
public static AppTarget getAppTargetFromInfo(Context context, ItemInfo info) {
|
||||
if (info == null) return null;
|
||||
if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
|
||||
&& info instanceof LauncherAppWidgetInfo
|
||||
&& ((LauncherAppWidgetInfo) info).providerName != null) {
|
||||
ComponentName cn = ((LauncherAppWidgetInfo) info).providerName;
|
||||
return new AppTarget.Builder(new AppTargetId("widget:" + cn.getPackageName()),
|
||||
cn.getPackageName(), info.user).setClassName(cn.getClassName()).build();
|
||||
} else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
|
||||
&& info.getTargetComponent() != null) {
|
||||
ComponentName cn = info.getTargetComponent();
|
||||
return new AppTarget.Builder(new AppTargetId("app:" + cn.getPackageName()),
|
||||
cn.getPackageName(), info.user).setClassName(cn.getClassName()).build();
|
||||
} else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
|
||||
&& info instanceof WorkspaceItemInfo) {
|
||||
ShortcutKey shortcutKey = ShortcutKey.fromItemInfo(info);
|
||||
//TODO: switch to using full shortcut info
|
||||
return new AppTarget.Builder(new AppTargetId("shortcut:" + shortcutKey.getId()),
|
||||
shortcutKey.componentName.getPackageName(), shortcutKey.user).build();
|
||||
} else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
|
||||
return new AppTarget.Builder(new AppTargetId("folder:" + info.id),
|
||||
context.getPackageName(), info.user).build();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns {@link AppTargetEvent} from an {@link AppTarget}, action, and item
|
||||
* location using {@link ItemInfo}
|
||||
*/
|
||||
public static AppTargetEvent wrapAppTargetWithLocation(
|
||||
AppTarget target, int action, ItemInfo info) {
|
||||
String location = String.format(Locale.ENGLISH, "%s/%d/[%d,%d]/[%d,%d]",
|
||||
info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT
|
||||
? APP_LOCATION_HOTSEAT : APP_LOCATION_WORKSPACE,
|
||||
info.screenId, info.cellX, info.cellY, info.spanX, info.spanY);
|
||||
return new AppTargetEvent.Builder(target, action).setLaunchLocation(location).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to determine if {@link ItemInfo} should be tracked and reported to predictors
|
||||
*/
|
||||
public static boolean isTrackedForPrediction(ItemInfo info) {
|
||||
return info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT || (
|
||||
info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP
|
||||
&& info.screenId == Workspace.FIRST_SCREEN_ID);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import static android.app.prediction.AppTargetEvent.ACTION_UNPIN;
|
||||
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION;
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_PREDICTION;
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_FOLDER_CONVERTED_TO_ICON;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_HOTSEAT_PREDICTION_PINNED;
|
||||
@@ -35,6 +36,8 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_QUICKSWITCH_RIGHT;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_SWIPE_DOWN;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_TAP;
|
||||
import static com.android.launcher3.model.PredictionHelper.isTrackedForHotseatPrediction;
|
||||
import static com.android.launcher3.model.PredictionHelper.isTrackedForWidgetPrediction;
|
||||
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
@@ -62,7 +65,6 @@ import com.android.launcher3.logger.LauncherAtom.FolderContainer;
|
||||
import com.android.launcher3.logger.LauncherAtom.HotseatContainer;
|
||||
import com.android.launcher3.logger.LauncherAtom.WorkspaceContainer;
|
||||
import com.android.launcher3.logging.StatsLogManager.EventEnum;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.pm.UserCache;
|
||||
import com.android.launcher3.shortcuts.ShortcutRequest;
|
||||
import com.android.quickstep.logging.StatsLogCompatManager.StatsLogConsumer;
|
||||
@@ -141,6 +143,9 @@ public class AppEventProducer implements StatsLogConsumer {
|
||||
if (isTrackedForHotseatPrediction(mLastDragItem)) {
|
||||
sendEvent(mLastDragItem, ACTION_UNPIN, CONTAINER_HOTSEAT_PREDICTION);
|
||||
}
|
||||
if (isTrackedForWidgetPrediction(atomInfo)) {
|
||||
sendEvent(atomInfo, ACTION_PIN, CONTAINER_WIDGETS_PREDICTION);
|
||||
}
|
||||
mLastDragItem = null;
|
||||
} else if (event == LAUNCHER_ITEM_DROP_FOLDER_CREATED) {
|
||||
if (isTrackedForHotseatPrediction(atomInfo)) {
|
||||
@@ -158,12 +163,15 @@ public class AppEventProducer implements StatsLogConsumer {
|
||||
if (mLastDragItem != null && isTrackedForHotseatPrediction(mLastDragItem)) {
|
||||
sendEvent(mLastDragItem, ACTION_UNPIN, CONTAINER_HOTSEAT_PREDICTION);
|
||||
}
|
||||
if (mLastDragItem != null && isTrackedForWidgetPrediction(mLastDragItem)) {
|
||||
sendEvent(mLastDragItem, ACTION_UNPIN, CONTAINER_WIDGETS_PREDICTION);
|
||||
}
|
||||
} else if (event == LAUNCHER_HOTSEAT_PREDICTION_PINNED) {
|
||||
if (isTrackedForHotseatPrediction(atomInfo)) {
|
||||
sendEvent(atomInfo, ACTION_PIN, CONTAINER_HOTSEAT_PREDICTION);
|
||||
}
|
||||
} else if (event == LAUNCHER_ONRESUME) {
|
||||
AppTarget target = new AppTarget.Builder(new AppTargetId("id:launcher"),
|
||||
AppTarget target = new AppTarget.Builder(new AppTargetId("launcher:launcher"),
|
||||
mContext.getPackageName(), Process.myUserHandle())
|
||||
.build();
|
||||
sendEvent(target, atomInfo, ACTION_LAUNCH, CONTAINER_PREDICTION);
|
||||
@@ -302,19 +310,4 @@ public class AppEventProducer implements StatsLogConsumer {
|
||||
return TextUtils.isEmpty(componentNameString)
|
||||
? null : ComponentName.unflattenFromString(componentNameString);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to determine if {@link ItemInfo} should be tracked and reported to predictors
|
||||
*/
|
||||
private static boolean isTrackedForHotseatPrediction(LauncherAtom.ItemInfo info) {
|
||||
ContainerInfo ci = info.getContainerInfo();
|
||||
switch (ci.getContainerCase()) {
|
||||
case HOTSEAT:
|
||||
return true;
|
||||
case WORKSPACE:
|
||||
return ci.getWorkspace().getPageIndex() == 0;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
* Copyright (C) 2021 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.model;
|
||||
|
||||
import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.WORKSPACE;
|
||||
|
||||
import android.app.prediction.AppTarget;
|
||||
import android.app.prediction.AppTargetEvent;
|
||||
import android.app.prediction.AppTargetId;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.LauncherSettings;
|
||||
import com.android.launcher3.Workspace;
|
||||
import com.android.launcher3.logger.LauncherAtom;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.model.data.LauncherAppWidgetInfo;
|
||||
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
||||
import com.android.launcher3.shortcuts.ShortcutKey;
|
||||
|
||||
import java.util.Locale;
|
||||
|
||||
/** Helper class with methods for converting launcher items to form usable by predictors */
|
||||
public final class PredictionHelper {
|
||||
private static final String APP_LOCATION_HOTSEAT = "hotseat";
|
||||
private static final String APP_LOCATION_WORKSPACE = "workspace";
|
||||
|
||||
/**
|
||||
* Creates and returns an {@link AppTarget} object for an {@link ItemInfo}. Returns null
|
||||
* if item type is not supported in predictions
|
||||
*/
|
||||
@Nullable
|
||||
public static AppTarget getAppTargetFromItemInfo(Context context, ItemInfo info) {
|
||||
if (info == null) return null;
|
||||
if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
|
||||
&& info instanceof LauncherAppWidgetInfo
|
||||
&& ((LauncherAppWidgetInfo) info).providerName != null) {
|
||||
ComponentName cn = ((LauncherAppWidgetInfo) info).providerName;
|
||||
return new AppTarget.Builder(new AppTargetId("widget:" + cn.getPackageName()),
|
||||
cn.getPackageName(), info.user).setClassName(cn.getClassName()).build();
|
||||
} else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
|
||||
&& info.getTargetComponent() != null) {
|
||||
ComponentName cn = info.getTargetComponent();
|
||||
return new AppTarget.Builder(new AppTargetId("app:" + cn.getPackageName()),
|
||||
cn.getPackageName(), info.user).setClassName(cn.getClassName()).build();
|
||||
} else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
|
||||
&& info instanceof WorkspaceItemInfo) {
|
||||
ShortcutKey shortcutKey = ShortcutKey.fromItemInfo(info);
|
||||
//TODO: switch to using full shortcut info
|
||||
return new AppTarget.Builder(new AppTargetId("shortcut:" + shortcutKey.getId()),
|
||||
shortcutKey.componentName.getPackageName(), shortcutKey.user).build();
|
||||
} else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
|
||||
return new AppTarget.Builder(new AppTargetId("folder:" + info.id),
|
||||
context.getPackageName(), info.user).build();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates and returns {@link AppTargetEvent} from an {@link AppTarget}, action, and item
|
||||
* location using {@link ItemInfo}
|
||||
*/
|
||||
public static AppTargetEvent wrapAppTargetWithItemLocation(
|
||||
AppTarget target, int action, ItemInfo info) {
|
||||
String location = String.format(Locale.ENGLISH, "%s/%d/[%d,%d]/[%d,%d]",
|
||||
info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT
|
||||
? APP_LOCATION_HOTSEAT : APP_LOCATION_WORKSPACE,
|
||||
info.screenId, info.cellX, info.cellY, info.spanX, info.spanY);
|
||||
return new AppTargetEvent.Builder(target, action).setLaunchLocation(location).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to determine if {@link ItemInfo} should be tracked and reported to hotseat
|
||||
* predictors
|
||||
*/
|
||||
public static boolean isTrackedForHotseatPrediction(ItemInfo info) {
|
||||
return info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT || (
|
||||
info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP
|
||||
&& info.screenId == Workspace.FIRST_SCREEN_ID);
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to determine if {@link LauncherAtom.ItemInfo} should be tracked and reported to
|
||||
* hotseat predictors
|
||||
*/
|
||||
public static boolean isTrackedForHotseatPrediction(LauncherAtom.ItemInfo info) {
|
||||
LauncherAtom.ContainerInfo ci = info.getContainerInfo();
|
||||
switch (ci.getContainerCase()) {
|
||||
case HOTSEAT:
|
||||
return true;
|
||||
case WORKSPACE:
|
||||
return ci.getWorkspace().getPageIndex() == 0;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to determine if {@link ItemInfo} should be tracked and reported to widget
|
||||
* predictors
|
||||
*/
|
||||
public static boolean isTrackedForWidgetPrediction(ItemInfo info) {
|
||||
return info.itemType == LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
|
||||
&& info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP;
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method to determine if {@link LauncherAtom.ItemInfo} should be tracked and reported
|
||||
* to widget predictors
|
||||
*/
|
||||
public static boolean isTrackedForWidgetPrediction(LauncherAtom.ItemInfo info) {
|
||||
return info.getItemCase() == LauncherAtom.ItemInfo.ItemCase.WIDGET
|
||||
&& info.getContainerInfo().getContainerCase() == WORKSPACE;
|
||||
}
|
||||
}
|
||||
@@ -25,8 +25,12 @@ import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICA
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
|
||||
import static com.android.launcher3.Utilities.getDevicePrefs;
|
||||
import static com.android.launcher3.hybridhotseat.HotseatPredictionModel.convertDataModelToAppTargetBundle;
|
||||
import static com.android.launcher3.model.PredictionHelper.getAppTargetFromItemInfo;
|
||||
import static com.android.launcher3.model.PredictionHelper.wrapAppTargetWithItemLocation;
|
||||
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
|
||||
|
||||
import static java.util.stream.Collectors.toCollection;
|
||||
|
||||
import android.app.StatsManager;
|
||||
import android.app.prediction.AppPredictionContext;
|
||||
import android.app.prediction.AppPredictionManager;
|
||||
@@ -39,6 +43,7 @@ import android.content.SharedPreferences;
|
||||
import android.content.pm.LauncherActivityInfo;
|
||||
import android.content.pm.LauncherApps;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserHandle;
|
||||
import android.util.Log;
|
||||
import android.util.StatsEvent;
|
||||
@@ -62,6 +67,7 @@ import com.android.launcher3.util.PersistedItemArray;
|
||||
import com.android.quickstep.logging.StatsLogCompatManager;
|
||||
import com.android.systemui.shared.system.SysUiStatsLog;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@@ -75,6 +81,7 @@ public class QuickstepModelDelegate extends ModelDelegate {
|
||||
|
||||
public static final String LAST_PREDICTION_ENABLED_STATE = "last_prediction_enabled_state";
|
||||
private static final String LAST_SNAPSHOT_TIME_MILLIS = "LAST_SNAPSHOT_TIME_MILLIS";
|
||||
private static final String BUNDLE_KEY_ADDED_APP_WIDGETS = "added_app_widgets";
|
||||
private static final int NUM_OF_RECOMMENDED_WIDGETS_PREDICATION = 20;
|
||||
|
||||
private static final boolean IS_DEBUG = false;
|
||||
@@ -272,6 +279,7 @@ public class QuickstepModelDelegate extends ModelDelegate {
|
||||
registerWidgetsPredictor(apm.createAppPredictionSession(
|
||||
new AppPredictionContext.Builder(context)
|
||||
.setUiSurface("widgets")
|
||||
.setExtras(getBundleForWidgetsOnWorkspace(context, mDataModel))
|
||||
.setPredictedTargetCount(NUM_OF_RECOMMENDED_WIDGETS_PREDICATION)
|
||||
.build()));
|
||||
}
|
||||
@@ -306,12 +314,41 @@ public class QuickstepModelDelegate extends ModelDelegate {
|
||||
}
|
||||
|
||||
private void onAppTargetEvent(AppTargetEvent event, int client) {
|
||||
PredictorState state = client == CONTAINER_PREDICTION ? mAllAppsState : mHotseatState;
|
||||
PredictorState state;
|
||||
switch(client) {
|
||||
case CONTAINER_PREDICTION:
|
||||
state = mAllAppsState;
|
||||
break;
|
||||
case CONTAINER_WIDGETS_PREDICTION:
|
||||
state = mWidgetsRecommendationState;
|
||||
break;
|
||||
case CONTAINER_HOTSEAT_PREDICTION:
|
||||
default:
|
||||
state = mHotseatState;
|
||||
break;
|
||||
}
|
||||
if (state.predictor != null) {
|
||||
state.predictor.notifyAppTargetEvent(event);
|
||||
}
|
||||
}
|
||||
|
||||
private Bundle getBundleForWidgetsOnWorkspace(Context context, BgDataModel dataModel) {
|
||||
Bundle bundle = new Bundle();
|
||||
ArrayList<AppTargetEvent> widgetEvents =
|
||||
dataModel.getAllWorkspaceItems().stream()
|
||||
.filter(PredictionHelper::isTrackedForWidgetPrediction)
|
||||
.map(item -> {
|
||||
AppTarget target = getAppTargetFromItemInfo(context, item);
|
||||
if (target == null) return null;
|
||||
return wrapAppTargetWithItemLocation(
|
||||
target, AppTargetEvent.ACTION_PIN, item);
|
||||
})
|
||||
.filter(Objects::nonNull)
|
||||
.collect(toCollection(ArrayList::new));
|
||||
bundle.putParcelableArrayList(BUNDLE_KEY_ADDED_APP_WIDGETS, widgetEvents);
|
||||
return bundle;
|
||||
}
|
||||
|
||||
static class PredictorState {
|
||||
|
||||
public final FixedContainerItems items;
|
||||
|
||||
@@ -444,6 +444,10 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<S>,
|
||||
mAnimationFactory = mActivityInterface.prepareRecentsUI(mDeviceState,
|
||||
mWasLauncherAlreadyVisible, this::onAnimatorPlaybackControllerCreated);
|
||||
maybeUpdateRecentsAttachedState(false /* animate */);
|
||||
if (mGestureState.getEndTarget() != null) {
|
||||
// Update the end target in case the gesture ended before we init.
|
||||
mAnimationFactory.setEndTarget(mGestureState.getEndTarget());
|
||||
}
|
||||
};
|
||||
if (mWasLauncherAlreadyVisible) {
|
||||
// Launcher is visible, but might be about to stop. Thus, if we prepare recents
|
||||
|
||||
@@ -59,7 +59,6 @@ import android.os.SystemProperties;
|
||||
import android.os.UserManager;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
import android.util.DisplayMetrics;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.Surface;
|
||||
|
||||
@@ -581,8 +580,7 @@ public class RecentsAnimationDeviceState implements
|
||||
final Info displayInfo = mDisplayController.getInfo();
|
||||
return (mRotationTouchHelper.touchInOneHandedModeRegion(ev)
|
||||
&& displayInfo.rotation != Surface.ROTATION_90
|
||||
&& displayInfo.rotation != Surface.ROTATION_270
|
||||
&& displayInfo.densityDpi < DisplayMetrics.DENSITY_600);
|
||||
&& displayInfo.rotation != Surface.ROTATION_270);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -274,7 +274,6 @@ abstract class TutorialController implements BackGestureAttemptCallback,
|
||||
mFeedbackView.findViewById(R.id.gesture_tutorial_fragment_feedback_subtitle);
|
||||
subtitle.setText(subtitleResId);
|
||||
if (isGestureSuccessful) {
|
||||
hideCloseButton();
|
||||
if (mTutorialFragment.isAtFinalStep()) {
|
||||
showActionButton();
|
||||
}
|
||||
@@ -402,6 +401,7 @@ abstract class TutorialController implements BackGestureAttemptCallback,
|
||||
void transitToController() {
|
||||
hideFeedback();
|
||||
hideActionButton();
|
||||
updateCloseButton();
|
||||
updateSubtext();
|
||||
updateDrawables();
|
||||
updateLayout();
|
||||
@@ -412,26 +412,21 @@ abstract class TutorialController implements BackGestureAttemptCallback,
|
||||
}
|
||||
}
|
||||
|
||||
void hideCloseButton() {
|
||||
mCloseButton.setVisibility(GONE);
|
||||
}
|
||||
|
||||
void showCloseButton() {
|
||||
mCloseButton.setVisibility(View.VISIBLE);
|
||||
void updateCloseButton() {
|
||||
mCloseButton.setTextAppearance(Utilities.isDarkTheme(mContext)
|
||||
? R.style.TextAppearance_GestureTutorial_Feedback_Subtext
|
||||
: R.style.TextAppearance_GestureTutorial_Feedback_Subtext_Dark);
|
||||
}
|
||||
|
||||
void hideActionButton() {
|
||||
showCloseButton();
|
||||
mCloseButton.setVisibility(View.VISIBLE);
|
||||
// Invisible to maintain the layout.
|
||||
mActionButton.setVisibility(View.INVISIBLE);
|
||||
mActionButton.setOnClickListener(null);
|
||||
}
|
||||
|
||||
void showActionButton() {
|
||||
hideCloseButton();
|
||||
mCloseButton.setVisibility(GONE);
|
||||
mActionButton.setVisibility(View.VISIBLE);
|
||||
mActionButton.setOnClickListener(this::onActionButtonClicked);
|
||||
}
|
||||
|
||||
@@ -599,6 +599,8 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
||||
private SplitConfigurationOptions.StagedSplitBounds mSplitBoundsConfig;
|
||||
private final Toast mSplitToast = Toast.makeText(getContext(),
|
||||
R.string.toast_split_select_app, Toast.LENGTH_SHORT);
|
||||
private final Toast mSplitUnsupportedToast = Toast.makeText(getContext(),
|
||||
R.string.toast_split_app_unsupported, Toast.LENGTH_SHORT);
|
||||
|
||||
/**
|
||||
* Keeps track of the index of the TaskView that split screen was initialized with so we know
|
||||
@@ -3878,6 +3880,11 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
||||
|
||||
public void confirmSplitSelect(TaskView taskView) {
|
||||
mSplitToast.cancel();
|
||||
if (!taskView.getTask().isDockable) {
|
||||
// Task not split screen supported
|
||||
mSplitUnsupportedToast.show();
|
||||
return;
|
||||
}
|
||||
RectF secondTaskStartingBounds = new RectF();
|
||||
Rect secondTaskEndingBounds = new Rect();
|
||||
// TODO(194414938) starting bounds seem slightly off, investigate
|
||||
@@ -3920,6 +3927,7 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
||||
int duration = mActivity.getStateManager().getState().getTransitionDuration(getContext());
|
||||
PendingAnimation pendingAnim = new PendingAnimation(duration);
|
||||
mSplitToast.cancel();
|
||||
mSplitUnsupportedToast.cancel();
|
||||
if (!animate) {
|
||||
resetFromSplitSelectionState();
|
||||
return pendingAnim;
|
||||
|
||||
@@ -44,7 +44,7 @@ public abstract class AbstractQuickStepTest extends AbstractLauncherUiTest {
|
||||
protected void onLauncherActivityClose(Launcher launcher) {
|
||||
RecentsView recentsView = launcher.getOverviewPanel();
|
||||
if (recentsView != null) {
|
||||
recentsView.finishRecentsAnimation(true, null);
|
||||
recentsView.finishRecentsAnimation(false /* toRecents */, null);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,7 +43,6 @@ import com.android.quickstep.views.RecentsView;
|
||||
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
|
||||
@@ -289,7 +288,6 @@ public class TaplTestsQuickstep extends AbstractQuickStepTest {
|
||||
|
||||
@Test
|
||||
@PortraitLandscape
|
||||
@Ignore("b/203781041")
|
||||
public void testOverviewForTablet() throws Exception {
|
||||
if (!mLauncher.isTablet()) {
|
||||
return;
|
||||
|
||||
@@ -275,8 +275,12 @@ public class DropTargetBar extends FrameLayout
|
||||
@Override
|
||||
protected void onVisibilityChanged(@NonNull View changedView, int visibility) {
|
||||
super.onVisibilityChanged(changedView, visibility);
|
||||
if (TestProtocol.sDebugTracing && visibility == VISIBLE) {
|
||||
Log.d(TestProtocol.NO_DROP_TARGET, "9");
|
||||
if (TestProtocol.sDebugTracing) {
|
||||
if (visibility == VISIBLE) {
|
||||
Log.d(TestProtocol.NO_DROP_TARGET, "9");
|
||||
} else {
|
||||
Log.d(TestProtocol.NO_DROP_TARGET, "Hiding drop target", new Exception());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,6 +40,7 @@ import androidx.annotation.Nullable;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.android.launcher3.AbstractFloatingView;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherSettings;
|
||||
import com.android.launcher3.R;
|
||||
@@ -220,6 +221,11 @@ public class OptionsPopupView extends ArrowPopup<Launcher>
|
||||
Toast.makeText(launcher, R.string.safemode_widget_error, Toast.LENGTH_SHORT).show();
|
||||
return null;
|
||||
} else {
|
||||
AbstractFloatingView floatingView = AbstractFloatingView.getTopOpenViewWithType(
|
||||
launcher, TYPE_WIDGETS_FULL_SHEET);
|
||||
if (floatingView != null) {
|
||||
return (WidgetsFullSheet) floatingView;
|
||||
}
|
||||
return WidgetsFullSheet.show(launcher, true /* animated */);
|
||||
}
|
||||
}
|
||||
@@ -279,7 +285,7 @@ public class OptionsPopupView extends ArrowPopup<Launcher>
|
||||
public final OnLongClickListener clickListener;
|
||||
|
||||
public OptionItem(Context context, int labelRes, int iconRes, EventEnum eventId,
|
||||
OnLongClickListener clickListener) {
|
||||
OnLongClickListener clickListener) {
|
||||
this.labelRes = labelRes;
|
||||
this.label = context.getText(labelRes);
|
||||
this.icon = ContextCompat.getDrawable(context, iconRes);
|
||||
@@ -288,7 +294,7 @@ public class OptionsPopupView extends ArrowPopup<Launcher>
|
||||
}
|
||||
|
||||
public OptionItem(CharSequence label, Drawable icon, EventEnum eventId,
|
||||
OnLongClickListener clickListener) {
|
||||
OnLongClickListener clickListener) {
|
||||
this.labelRes = 0;
|
||||
this.label = label;
|
||||
this.icon = icon;
|
||||
|
||||
@@ -54,18 +54,14 @@ public class BaseOverview extends LauncherInstrumentation.VisibleContainer {
|
||||
}
|
||||
|
||||
private void flingForwardImpl() {
|
||||
flingForwardImpl(0);
|
||||
}
|
||||
|
||||
private void flingForwardImpl(int rightMargin) {
|
||||
try (LauncherInstrumentation.Closable c =
|
||||
mLauncher.addContextLayer("want to fling forward in overview")) {
|
||||
LauncherInstrumentation.log("Overview.flingForward before fling");
|
||||
final UiObject2 overview = verifyActiveContainer();
|
||||
final int leftMargin =
|
||||
mLauncher.getTargetInsets().left + mLauncher.getEdgeSensitivityWidth();
|
||||
mLauncher.scroll(overview, Direction.LEFT, new Rect(leftMargin + 1, 0, rightMargin, 0),
|
||||
20, false);
|
||||
mLauncher.scroll(overview, Direction.LEFT, new Rect(leftMargin + 1, 0, 0, 0), 20,
|
||||
false);
|
||||
try (LauncherInstrumentation.Closable c2 =
|
||||
mLauncher.addContextLayer("flung forwards")) {
|
||||
verifyActiveContainer();
|
||||
@@ -131,7 +127,7 @@ public class BaseOverview extends LauncherInstrumentation.VisibleContainer {
|
||||
|
||||
OverviewTask task = getCurrentTask();
|
||||
mLauncher.assertNotNull("current task is null", task);
|
||||
flingForwardImpl(task.getTaskCenterX());
|
||||
mLauncher.scrollLeftByDistance(verifyActiveContainer(), task.getVisibleWidth());
|
||||
|
||||
try (LauncherInstrumentation.Closable c2 =
|
||||
mLauncher.addContextLayer("scrolled task off screen")) {
|
||||
|
||||
@@ -1190,10 +1190,19 @@ public final class LauncherInstrumentation {
|
||||
return getVisibleBounds(container).bottom - bottomGestureStartOnScreen;
|
||||
}
|
||||
|
||||
int getRightGestureMarginInContainer(UiObject2 container) {
|
||||
final int rightGestureStartOnScreen = getRightGestureStartOnScreen();
|
||||
return getVisibleBounds(container).right - rightGestureStartOnScreen;
|
||||
}
|
||||
|
||||
int getBottomGestureStartOnScreen() {
|
||||
return getRealDisplaySize().y - getBottomGestureSize();
|
||||
}
|
||||
|
||||
int getRightGestureStartOnScreen() {
|
||||
return getRealDisplaySize().x - getWindowInsets().right;
|
||||
}
|
||||
|
||||
void clickLauncherObject(UiObject2 object) {
|
||||
waitForObjectEnabled(object, "clickLauncherObject");
|
||||
expectEvent(TestProtocol.SEQUENCE_MAIN, LauncherInstrumentation.EVENT_TOUCH_DOWN);
|
||||
@@ -1235,6 +1244,21 @@ public final class LauncherInstrumentation {
|
||||
true);
|
||||
}
|
||||
|
||||
void scrollLeftByDistance(UiObject2 container, int distance) {
|
||||
final Rect containerRect = getVisibleBounds(container);
|
||||
final int rightGestureMarginInContainer = getRightGestureMarginInContainer(container);
|
||||
scroll(
|
||||
container,
|
||||
Direction.LEFT,
|
||||
new Rect(
|
||||
0,
|
||||
containerRect.width() - distance - rightGestureMarginInContainer,
|
||||
0,
|
||||
rightGestureMarginInContainer),
|
||||
10,
|
||||
true);
|
||||
}
|
||||
|
||||
void scroll(
|
||||
UiObject2 container, Direction direction, Rect margins, int steps, boolean slowDown) {
|
||||
final Rect rect = getVisibleBounds(container);
|
||||
|
||||
@@ -53,6 +53,10 @@ public final class OverviewTask {
|
||||
return mTask.getVisibleBounds().height();
|
||||
}
|
||||
|
||||
int getVisibleWidth() {
|
||||
return mTask.getVisibleBounds().width();
|
||||
}
|
||||
|
||||
int getTaskCenterX() {
|
||||
return mTask.getVisibleCenter().x;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user