Snap for 11565217 from be65a75e82 to 24Q3-release
Change-Id: I5904f5fa53134d5bddafd79e71218f39cc19ea0c
This commit is contained in:
+1
-2
@@ -135,8 +135,7 @@ public class QuickstepAtomicAnimationFactory extends
|
||||
config.duration = Math.max(config.duration, scrollDuration);
|
||||
|
||||
// Sync scroll so that it ends before or at the same time as the taskbar animation.
|
||||
if (DisplayController.isTransientTaskbar(mActivity)
|
||||
&& mActivity.getDeviceProfile().isTaskbarPresent) {
|
||||
if (mActivity.getDeviceProfile().isTaskbarPresent) {
|
||||
config.duration = Math.min(config.duration, TASKBAR_TO_HOME_DURATION);
|
||||
}
|
||||
overview.snapToPage(DEFAULT_PAGE, Math.toIntExact(config.duration));
|
||||
|
||||
@@ -328,11 +328,15 @@ public interface TaskShortcutFactory {
|
||||
|
||||
// No "save app pair" menu item if:
|
||||
// - app pairs feature is not enabled
|
||||
// - we are in 3p launcher
|
||||
// - the task in question is a single task
|
||||
// - at least one app in app pair is unpinnable
|
||||
// - the Overview Actions Button should be visible
|
||||
if (!FeatureFlags.enableAppPairs() || !taskView.containsMultipleTasks()
|
||||
|| hasUnpinnableApp || shouldShowActionsButtonInstead) {
|
||||
if (!FeatureFlags.enableAppPairs()
|
||||
|| !recentsView.supportsAppPairs()
|
||||
|| !taskView.containsMultipleTasks()
|
||||
|| hasUnpinnableApp
|
||||
|| shouldShowActionsButtonInstead) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
@@ -40,7 +40,6 @@ import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent
|
||||
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_DOWN;
|
||||
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_MOVE;
|
||||
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.MOTION_UP;
|
||||
import static com.android.quickstep.util.ActiveGestureErrorDetector.GestureEvent.RECENTS_ANIMATION_START_PENDING;
|
||||
import static com.android.systemui.shared.system.ActivityManagerWrapper.CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
|
||||
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_UNFOLD_ANIMATION_FORWARDER;
|
||||
@@ -734,15 +733,17 @@ public class TouchInteractionService extends Service {
|
||||
// an ACTION_HOVER_ENTER will fire as well.
|
||||
boolean isHoverActionWithoutConsumer = enableCursorHoverStates()
|
||||
&& isHoverActionWithoutConsumer(event);
|
||||
if (mTaskAnimationManager.isRecentsAnimationStartPending()
|
||||
&& (action == ACTION_DOWN || isHoverActionWithoutConsumer)) {
|
||||
ActiveGestureLog.INSTANCE.addLog(
|
||||
new CompoundString("TIS.onInputEvent: ")
|
||||
.append("Cannot process input event: a recents animation has been ")
|
||||
.append("requested, but hasn't started."),
|
||||
RECENTS_ANIMATION_START_PENDING);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO(b/285636175): Uncomment this once WM can properly guarantee all animation callbacks
|
||||
// if (mTaskAnimationManager.isRecentsAnimationStartPending()
|
||||
// && (action == ACTION_DOWN || isHoverActionWithoutConsumer)) {
|
||||
// ActiveGestureLog.INSTANCE.addLog(
|
||||
// new CompoundString("TIS.onInputEvent: ")
|
||||
// .append("Cannot process input event: a recents animation has been ")
|
||||
// .append("requested, but hasn't started."),
|
||||
// RECENTS_ANIMATION_START_PENDING);
|
||||
// return;
|
||||
// }
|
||||
|
||||
SafeCloseable traceToken = TraceHelper.INSTANCE.allowIpcs("TIS.onInputEvent");
|
||||
|
||||
|
||||
@@ -45,8 +45,7 @@ public class FallbackNavBarTouchController implements TouchController,
|
||||
NavBarPosition navBarPosition = new NavBarPosition(sysUINavigationMode,
|
||||
DisplayController.INSTANCE.get(mActivity).getInfo());
|
||||
mTriggerSwipeUpTracker = new TriggerSwipeUpTouchTracker(mActivity,
|
||||
true /* disableHorizontalSwipe */, navBarPosition,
|
||||
null /* onInterceptTouch */, this);
|
||||
true /* disableHorizontalSwipe */, navBarPosition, this);
|
||||
} else {
|
||||
mTriggerSwipeUpTracker = null;
|
||||
}
|
||||
@@ -78,7 +77,4 @@ public class FallbackNavBarTouchController implements TouchController,
|
||||
public void onSwipeUp(boolean wasFling, PointF finalVelocity) {
|
||||
mActivity.<FallbackRecentsView>getOverviewPanel().startHome();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSwipeUpCancelled() {}
|
||||
}
|
||||
|
||||
@@ -302,4 +302,10 @@ public class FallbackRecentsView extends RecentsView<RecentsActivity, RecentsSta
|
||||
protected boolean canLaunchFullscreenTask() {
|
||||
return !mActivity.isInState(OVERVIEW_SPLIT_SELECT);
|
||||
}
|
||||
|
||||
/** Returns if app pairs are supported in this launcher. */
|
||||
@Override
|
||||
public boolean supportsAppPairs() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
+3
-5
@@ -51,7 +51,7 @@ public class OverviewWithoutFocusInputConsumer implements InputConsumer,
|
||||
mGestureState = gestureState;
|
||||
mInputMonitor = inputMonitor;
|
||||
mTriggerSwipeUpTracker = new TriggerSwipeUpTouchTracker(context, disableHorizontalSwipe,
|
||||
deviceState.getNavBarPosition(), this::onInterceptTouch, this);
|
||||
deviceState.getNavBarPosition(), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -69,7 +69,8 @@ public class OverviewWithoutFocusInputConsumer implements InputConsumer,
|
||||
mTriggerSwipeUpTracker.onMotionEvent(ev);
|
||||
}
|
||||
|
||||
private void onInterceptTouch() {
|
||||
@Override
|
||||
public void onSwipeUpTouchIntercepted() {
|
||||
if (mInputMonitor != null) {
|
||||
TestLogging.recordEvent(TestProtocol.SEQUENCE_PILFER, "pilferPointers");
|
||||
mInputMonitor.pilferPointers();
|
||||
@@ -93,7 +94,4 @@ public class OverviewWithoutFocusInputConsumer implements InputConsumer,
|
||||
.build())
|
||||
.log(LAUNCHER_HOME_GESTURE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSwipeUpCancelled() {}
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ public class SysUiOverlayInputConsumer implements InputConsumer,
|
||||
mContext = context;
|
||||
mInputMonitor = inputMonitor;
|
||||
mTriggerSwipeUpTracker = new TriggerSwipeUpTouchTracker(context, true,
|
||||
deviceState.getNavBarPosition(), this::onInterceptTouch, this);
|
||||
deviceState.getNavBarPosition(), this);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -72,7 +72,8 @@ public class SysUiOverlayInputConsumer implements InputConsumer,
|
||||
mTriggerSwipeUpTracker.onMotionEvent(ev);
|
||||
}
|
||||
|
||||
private void onInterceptTouch() {
|
||||
@Override
|
||||
public void onSwipeUpTouchIntercepted() {
|
||||
if (mInputMonitor != null) {
|
||||
TestLogging.recordEvent(TestProtocol.SEQUENCE_PILFER, "pilferPointers");
|
||||
mInputMonitor.pilferPointers();
|
||||
@@ -88,9 +89,4 @@ public class SysUiOverlayInputConsumer implements InputConsumer,
|
||||
Log.e(TAG, "Exception calling closeSystemDialogs " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSwipeUpCancelled() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ public class NavBarGestureHandler implements OnTouchListener,
|
||||
mSwipeUpTouchTracker =
|
||||
new TriggerSwipeUpTouchTracker(context, true /*disableHorizontalSwipe*/,
|
||||
new NavBarPosition(NavigationMode.NO_BUTTON, displayInfo),
|
||||
null /*onInterceptTouch*/, this);
|
||||
this);
|
||||
mMotionPauseDetector = new MotionPauseDetector(context);
|
||||
|
||||
final Resources resources = context.getResources();
|
||||
|
||||
@@ -28,6 +28,8 @@ import android.graphics.PointF;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.VelocityTracker;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Utilities;
|
||||
|
||||
@@ -41,21 +43,20 @@ public class TriggerSwipeUpTouchTracker {
|
||||
private final float mMinFlingVelocity;
|
||||
private final boolean mDisableHorizontalSwipe;
|
||||
private final NavBarPosition mNavBarPosition;
|
||||
private final Runnable mOnInterceptTouch;
|
||||
|
||||
@NonNull
|
||||
private final OnSwipeUpListener mOnSwipeUp;
|
||||
|
||||
private boolean mInterceptedTouch;
|
||||
private VelocityTracker mVelocityTracker;
|
||||
|
||||
public TriggerSwipeUpTouchTracker(Context context, boolean disableHorizontalSwipe,
|
||||
NavBarPosition navBarPosition, Runnable onInterceptTouch,
|
||||
OnSwipeUpListener onSwipeUp) {
|
||||
NavBarPosition navBarPosition, @NonNull OnSwipeUpListener onSwipeUp) {
|
||||
mSquaredTouchSlop = Utilities.squaredTouchSlop(context);
|
||||
mMinFlingVelocity = context.getResources().getDimension(
|
||||
R.dimen.quickstep_fling_threshold_speed);
|
||||
mNavBarPosition = navBarPosition;
|
||||
mDisableHorizontalSwipe = disableHorizontalSwipe;
|
||||
mOnInterceptTouch = onInterceptTouch;
|
||||
mOnSwipeUp = onSwipeUp;
|
||||
|
||||
init();
|
||||
@@ -103,10 +104,7 @@ public class TriggerSwipeUpTouchTracker {
|
||||
}
|
||||
|
||||
mInterceptedTouch = true;
|
||||
|
||||
if (mOnInterceptTouch != null) {
|
||||
mOnInterceptTouch.run();
|
||||
}
|
||||
mOnSwipeUp.onSwipeUpTouchIntercepted();
|
||||
}
|
||||
}
|
||||
break;
|
||||
@@ -124,7 +122,8 @@ public class TriggerSwipeUpTouchTracker {
|
||||
}
|
||||
}
|
||||
|
||||
private void endTouchTracking() {
|
||||
/** Finishes the tracking. All events after this call are ignored */
|
||||
public void endTouchTracking() {
|
||||
if (mVelocityTracker != null) {
|
||||
mVelocityTracker.recycle();
|
||||
mVelocityTracker = null;
|
||||
@@ -151,12 +150,10 @@ public class TriggerSwipeUpTouchTracker {
|
||||
isSwipeUp = squaredHypot(displacementX, displacementY) >= mSquaredTouchSlop;
|
||||
}
|
||||
|
||||
if (mOnSwipeUp != null) {
|
||||
if (isSwipeUp) {
|
||||
mOnSwipeUp.onSwipeUp(wasFling, new PointF(velocityX, velocityY));
|
||||
} else {
|
||||
mOnSwipeUp.onSwipeUpCancelled();
|
||||
}
|
||||
if (isSwipeUp) {
|
||||
mOnSwipeUp.onSwipeUp(wasFling, new PointF(velocityX, velocityY));
|
||||
} else {
|
||||
mOnSwipeUp.onSwipeUpCancelled();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,6 +169,9 @@ public class TriggerSwipeUpTouchTracker {
|
||||
void onSwipeUp(boolean wasFling, PointF finalVelocity);
|
||||
|
||||
/** Called on touch up if a swipe up was not detected. */
|
||||
void onSwipeUpCancelled();
|
||||
default void onSwipeUpCancelled() { }
|
||||
|
||||
/** Called when the touch for swipe up is intercepted. */
|
||||
default void onSwipeUpTouchIntercepted() { }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,6 +106,7 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo
|
||||
public @interface AppPairButtonHiddenFlags { }
|
||||
public static final int FLAG_SINGLE_TASK_HIDE_APP_PAIR = 1 << 0;
|
||||
public static final int FLAG_SMALL_SCREEN_HIDE_APP_PAIR = 1 << 1;
|
||||
public static final int FLAG_3P_LAUNCHER_HIDE_APP_PAIR = 1 << 2;
|
||||
|
||||
private MultiValueAlpha mMultiValueAlpha;
|
||||
|
||||
@@ -254,6 +255,13 @@ public class OverviewActionsView<T extends OverlayUICallbacks> extends FrameLayo
|
||||
updateAppPairButtonHiddenFlags(FLAG_SMALL_SCREEN_HIDE_APP_PAIR, isSmallScreen);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates flags to hide and show actions buttons for 1p/3p launchers.
|
||||
*/
|
||||
public void updateFor3pLauncher(boolean is3pLauncher) {
|
||||
updateAppPairButtonHiddenFlags(FLAG_3P_LAUNCHER_HIDE_APP_PAIR, is3pLauncher);
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the proper flags to indicate whether the "Screenshot" button should be hidden.
|
||||
*
|
||||
|
||||
@@ -4022,6 +4022,8 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
||||
mActionsView.updateForGroupedTask(isCurrentSplit);
|
||||
// Update flags to see if actions bar should show buttons for tablets or phones.
|
||||
mActionsView.updateForSmallScreen(!mActivity.getDeviceProfile().isTablet);
|
||||
// Update flags for 1p/3p launchers
|
||||
mActionsView.updateFor3pLauncher(!supportsAppPairs());
|
||||
|
||||
if (isDesktopModeSupported()) {
|
||||
boolean isCurrentDesktop = getCurrentPageTaskView() instanceof DesktopTaskView;
|
||||
@@ -4029,6 +4031,11 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
||||
}
|
||||
}
|
||||
|
||||
/** Returns if app pairs are supported in this launcher. Overridden in subclasses. */
|
||||
public boolean supportsAppPairs() {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all the tasks in the top row, without the focused task
|
||||
*/
|
||||
@@ -5162,9 +5169,13 @@ public abstract class RecentsView<ACTIVITY_TYPE extends StatefulActivity<STATE_T
|
||||
public float getMaxScaleForFullScreen() {
|
||||
if (enableGridOnlyOverview() && mActivity.getDeviceProfile().isTablet
|
||||
&& !mOverviewGridEnabled) {
|
||||
if (mLastComputedCarouselTaskSize.isEmpty()) {
|
||||
mSizeStrategy.calculateCarouselTaskSize(mActivity, mActivity.getDeviceProfile(),
|
||||
mLastComputedCarouselTaskSize, getPagedOrientationHandler());
|
||||
}
|
||||
mTempRect.set(mLastComputedCarouselTaskSize);
|
||||
} else {
|
||||
if (mLastComputedTaskSize.height() == 0 || mLastComputedTaskSize.width() == 0) {
|
||||
if (mLastComputedTaskSize.isEmpty()) {
|
||||
getTaskSize(mLastComputedTaskSize);
|
||||
}
|
||||
mTempRect.set(mLastComputedTaskSize);
|
||||
|
||||
@@ -30,6 +30,8 @@ import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncest
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_ICON_TAP_OR_LONGPRESS;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_TAP;
|
||||
import static com.android.launcher3.model.data.ItemInfoWithIcon.FLAG_NOT_PINNABLE;
|
||||
import static com.android.launcher3.testing.shared.TestProtocol.SUCCESSFUL_GESTURE_MISMATCH_EVENTS;
|
||||
import static com.android.launcher3.testing.shared.TestProtocol.testLogD;
|
||||
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
||||
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
|
||||
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
|
||||
@@ -861,6 +863,7 @@ public class TaskView extends FrameLayout implements Reusable {
|
||||
@Nullable
|
||||
public RunnableList launchTaskAnimated() {
|
||||
if (mTask != null) {
|
||||
testLogD(SUCCESSFUL_GESTURE_MISMATCH_EVENTS, "TaskView.launchTaskAnimated");
|
||||
TestLogging.recordEvent(
|
||||
TestProtocol.SEQUENCE_MAIN, "startActivityFromRecentsAsync", mTask);
|
||||
ActivityOptionsWrapper opts = mActivity.getActivityLaunchOptions(this, null);
|
||||
@@ -909,6 +912,7 @@ public class TaskView extends FrameLayout implements Reusable {
|
||||
*/
|
||||
public void launchTask(@NonNull Consumer<Boolean> callback, boolean isQuickswitch) {
|
||||
if (mTask != null) {
|
||||
testLogD(SUCCESSFUL_GESTURE_MISMATCH_EVENTS, "TaskView.launchTaskAnimated");
|
||||
TestLogging.recordEvent(
|
||||
TestProtocol.SEQUENCE_MAIN, "startActivityFromRecentsAsync", mTask);
|
||||
|
||||
|
||||
+16
-4
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package com.android.launcher3.model;
|
||||
|
||||
import static android.content.pm.ApplicationInfo.CATEGORY_PRODUCTIVITY;
|
||||
import static android.content.pm.ApplicationInfo.FLAG_INSTALLED;
|
||||
import static android.os.Process.myUserHandle;
|
||||
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION;
|
||||
@@ -37,6 +39,8 @@ import android.app.prediction.AppTargetId;
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.appwidget.AppWidgetProviderInfo;
|
||||
import android.content.ComponentName;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.LauncherApps;
|
||||
import android.os.UserHandle;
|
||||
import android.platform.test.flag.junit.SetFlagsRule;
|
||||
import android.text.TextUtils;
|
||||
@@ -81,6 +85,8 @@ public final class WidgetsPredicationUpdateTaskTest {
|
||||
private FakeBgDataModelCallback mCallback = new FakeBgDataModelCallback();
|
||||
private LauncherModelHelper mModelHelper;
|
||||
private UserHandle mUserHandle;
|
||||
private LauncherApps mLauncherApps;
|
||||
|
||||
|
||||
@Before
|
||||
public void setup() throws Exception {
|
||||
@@ -103,12 +109,18 @@ public final class WidgetsPredicationUpdateTaskTest {
|
||||
allWidgets = Arrays.asList(mApp1Provider1, mApp1Provider2, mApp2Provider1,
|
||||
mApp4Provider1, mApp4Provider2, mApp5Provider1);
|
||||
|
||||
mLauncherApps = mModelHelper.sandboxContext.spyService(LauncherApps.class);
|
||||
doAnswer(i -> {
|
||||
String pkg = i.getArgument(0);
|
||||
return ApplicationInfoBuilder.newBuilder().setPackageName(pkg).setName(
|
||||
"App " + pkg).build();
|
||||
}).when(mModelHelper.sandboxContext.getPackageManager())
|
||||
.getApplicationInfo(anyString(), anyInt());
|
||||
ApplicationInfo applicationInfo = ApplicationInfoBuilder.newBuilder()
|
||||
.setPackageName(pkg)
|
||||
.setName("App " + pkg)
|
||||
.build();
|
||||
applicationInfo.category = CATEGORY_PRODUCTIVITY;
|
||||
applicationInfo.flags = FLAG_INSTALLED;
|
||||
return applicationInfo;
|
||||
}).when(mLauncherApps).getApplicationInfo(anyString(), anyInt(), any());
|
||||
|
||||
AppWidgetManager manager = mModelHelper.sandboxContext.spyService(AppWidgetManager.class);
|
||||
doReturn(allWidgets).when(manager).getInstalledProviders();
|
||||
doReturn(allWidgets).when(manager).getInstalledProvidersForProfile(eq(myUserHandle()));
|
||||
|
||||
@@ -76,17 +76,21 @@ import org.junit.rules.TestRule;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.junit.runners.model.Statement;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
@LargeTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
public class FallbackRecentsTest {
|
||||
|
||||
private static final String FALLBACK_LAUNCHER_TITLE = "Test launcher";
|
||||
private static final Pattern COMPONENT_INFO_REGEX = Pattern.compile("ComponentInfo\\{(.*)\\}");
|
||||
|
||||
private final UiDevice mDevice;
|
||||
private final LauncherInstrumentation mLauncher;
|
||||
@@ -253,7 +257,7 @@ public class FallbackRecentsTest {
|
||||
//@NavigationModeSwitch
|
||||
@Test
|
||||
@ScreenRecordRule.ScreenRecord // b/321775748
|
||||
public void testOverview() {
|
||||
public void testOverview() throws IOException {
|
||||
startAppFast(getAppPackageName());
|
||||
startAppFast(resolveSystemApp(Intent.CATEGORY_APP_CALCULATOR));
|
||||
startTestActivity(2);
|
||||
@@ -261,7 +265,10 @@ public class FallbackRecentsTest {
|
||||
Wait.atMost("Expected three apps in the task list",
|
||||
() -> mLauncher.getRecentTasks().size() >= 3, DEFAULT_ACTIVITY_TIMEOUT, mLauncher);
|
||||
|
||||
checkTestLauncher();
|
||||
BaseOverview overview = mLauncher.getLaunchedAppState().switchToOverview();
|
||||
checkTestLauncher();
|
||||
|
||||
executeOnRecents(recents -> {
|
||||
assertTrue("Don't have at least 3 tasks", getTaskCount(recents) >= 3);
|
||||
});
|
||||
@@ -303,6 +310,17 @@ public class FallbackRecentsTest {
|
||||
mOtherLauncherActivity.packageName).text(FALLBACK_LAUNCHER_TITLE)), WAIT_TIME_MS));
|
||||
}
|
||||
|
||||
private void checkTestLauncher() throws IOException {
|
||||
final Matcher matcher = COMPONENT_INFO_REGEX.matcher(
|
||||
mDevice.executeShellCommand("cmd shortcut get-default-launcher"));
|
||||
assertTrue("Incorrect output from get-default-launcher", matcher.find());
|
||||
assertEquals("Current Launcher activity is incorrect",
|
||||
"com.google.android.apps.nexuslauncher.tests/com.android"
|
||||
+ ".launcher3.testcomponent.TestLauncherActivity",
|
||||
matcher.group(1)
|
||||
);
|
||||
}
|
||||
|
||||
private int getCurrentOverviewPage(RecentsActivity recents) {
|
||||
return recents.<RecentsView>getOverviewPanel().getCurrentPage();
|
||||
}
|
||||
|
||||
@@ -75,12 +75,14 @@
|
||||
<!-- Widget suggestions header title in the full widgets picker for large screen devices
|
||||
in landscape mode. [CHAR_LIMIT=50] -->
|
||||
<string name="suggested_widgets_header_title">Suggestions</string>
|
||||
<string name="productivity_widget_recommendation_category_label">Your Daily Essentials</string>
|
||||
<string name="news_widget_recommendation_category_label">News For You</string>
|
||||
<string name="productivity_widget_recommendation_category_label">Essentials</string>
|
||||
<string name="news_widget_recommendation_category_label">News & magazines</string>
|
||||
<string name="social_and_entertainment_widget_recommendation_category_label">Your Chill Zone</string>
|
||||
<string name="fitness_widget_recommendation_category_label">Reach Your Fitness Goals</string>
|
||||
<string name="weather_widget_recommendation_category_label">Stay Ahead of the Weather</string>
|
||||
<string name="others_widget_recommendation_category_label">You Might Also Like</string>
|
||||
<string name="entertainment_widget_recommendation_category_label">Entertainment</string>
|
||||
<string name="social_widget_recommendation_category_label">Social</string>
|
||||
<string name="fitness_widget_recommendation_category_label">Health & fitness</string>
|
||||
<string name="weather_widget_recommendation_category_label">Weather</string>
|
||||
<string name="others_widget_recommendation_category_label">Suggested for you</string>
|
||||
<!-- accessibilityPaneTitle for the right pane when showing suggested widgets. -->
|
||||
<string name="widget_picker_right_pane_accessibility_title"><xliff:g id="selected_header" example="Calendar">%1$s</xliff:g> widgets on right, search and options on left</string>
|
||||
<!-- Label for showing the number of widgets an app has in the full widgets picker.
|
||||
|
||||
@@ -72,7 +72,6 @@ import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class InvariantDeviceProfile {
|
||||
@@ -578,45 +577,6 @@ public class InvariantDeviceProfile {
|
||||
return filteredProfiles;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the GridOption associated to the given file name or null if the fileName is not
|
||||
* supported.
|
||||
* Ej, launcher.db -> "normal grid", launcher_4_by_4.db -> "practical grid"
|
||||
*/
|
||||
public GridOption getGridOptionFromFileName(Context context, String fileName) {
|
||||
return parseAllGridOptions(context).stream()
|
||||
.filter(gridOption -> Objects.equals(gridOption.dbFile, fileName))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the given size on the current device or empty string if the size is not
|
||||
* supported. Ej. 4x4 -> normal, 5x4 -> practical, etc.
|
||||
* (Note: the name of the grid can be different for the same grid size depending of
|
||||
* the values of the InvariantDeviceProfile)
|
||||
*
|
||||
*/
|
||||
public String getGridNameFromSize(Context context, Point size) {
|
||||
return parseAllGridOptions(context).stream()
|
||||
.filter(gridOption -> gridOption.numColumns == size.x
|
||||
&& gridOption.numRows == size.y)
|
||||
.map(gridOption -> gridOption.name)
|
||||
.findFirst()
|
||||
.orElse("");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the grid option for the given gridName on the current device (Note: the gridOption
|
||||
* be different for the same gridName depending on the values of the InvariantDeviceProfile).
|
||||
*/
|
||||
public GridOption getGridOptionFromName(Context context, String gridName) {
|
||||
return parseAllGridOptions(context).stream()
|
||||
.filter(gridOption -> Objects.equals(gridOption.name, gridName))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return all the grid options that can be shown on the device
|
||||
*/
|
||||
|
||||
@@ -123,6 +123,7 @@ public class WidgetItem extends ComponentKey {
|
||||
if (!Flags.enableGeneratedPreviews() || generatedPreviews == null) {
|
||||
return false;
|
||||
}
|
||||
return generatedPreviews.contains(widgetCategory);
|
||||
return generatedPreviews.contains(widgetCategory)
|
||||
&& generatedPreviews.get(widgetCategory) != null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,10 +50,8 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.annotation.WorkerThread;
|
||||
|
||||
import com.android.launcher3.Flags;
|
||||
import com.android.launcher3.InvariantDeviceProfile;
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.LauncherFiles;
|
||||
import com.android.launcher3.LauncherPrefs;
|
||||
import com.android.launcher3.LauncherSettings;
|
||||
import com.android.launcher3.LauncherSettings.Favorites;
|
||||
@@ -123,48 +121,7 @@ public class RestoreDbTask {
|
||||
// executed again.
|
||||
LauncherPrefs.get(context).removeSync(RESTORE_DEVICE);
|
||||
|
||||
if (Flags.narrowGridRestore()) {
|
||||
String oldPhoneFileName = idp.dbFile;
|
||||
removeOldDBs(context, oldPhoneFileName);
|
||||
trySettingPreviousGidAsCurrent(context, idp, oldPhoneFileName);
|
||||
} else {
|
||||
idp.reinitializeAfterRestore(context);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Try setting the gird used in the previous phone to the new one. If the current device doesn't
|
||||
* support the previous grid option it will not be set.
|
||||
*/
|
||||
private static void trySettingPreviousGidAsCurrent(Context context, InvariantDeviceProfile idp,
|
||||
String oldPhoneDbFileName) {
|
||||
InvariantDeviceProfile.GridOption gridOption = idp.getGridOptionFromFileName(context,
|
||||
oldPhoneDbFileName);
|
||||
if (gridOption != null) {
|
||||
/*
|
||||
* We do this because in some cases different devices have different names for grid
|
||||
* options, in one device the grid option "normal" can be 4x4 while in other it
|
||||
* could be "practical". Calling this changes the current device grid to the same
|
||||
* we had in the other phone, in the case the current phone doesn't support the grid
|
||||
* option we use the default and migrate the db to the default. Migration occurs on
|
||||
* {@code GridSizeMigrationUtil#migrateGridIfNeeded}
|
||||
*/
|
||||
idp.setCurrentGrid(context, gridOption.name);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Only keep the last database used on the previous device.
|
||||
*/
|
||||
private static void removeOldDBs(Context context, String oldPhoneDbFileName) {
|
||||
// At this point idp.dbFile contains the name of the dbFile from the previous phone
|
||||
LauncherFiles.GRID_DB_FILES.stream()
|
||||
.filter(dbName -> !dbName.equals(oldPhoneDbFileName))
|
||||
.forEach(dbName -> {
|
||||
if (context.getDatabasePath(dbName).delete()) {
|
||||
FileLog.d(TAG, "Removed old grid db file: " + dbName);
|
||||
}
|
||||
});
|
||||
idp.reinitializeAfterRestore(context);
|
||||
}
|
||||
|
||||
private static boolean performRestore(Context context, ModelDbController controller) {
|
||||
|
||||
@@ -18,13 +18,13 @@ package com.android.launcher3.widget.picker;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.WorkerThread;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.model.WidgetItem;
|
||||
import com.android.launcher3.util.PackageManagerHelper;
|
||||
import com.android.launcher3.util.Preconditions;
|
||||
import com.android.launcher3.util.ResourceBasedOverride;
|
||||
|
||||
@@ -54,6 +54,7 @@ public class WidgetRecommendationCategoryProvider implements ResourceBasedOverri
|
||||
* to display the recommendation grouped by categories.
|
||||
*/
|
||||
@WorkerThread
|
||||
@Nullable
|
||||
public WidgetRecommendationCategory getWidgetRecommendationCategory(Context context,
|
||||
WidgetItem item) {
|
||||
// This is a default implementation that uses application category to derive the category to
|
||||
@@ -61,17 +62,16 @@ public class WidgetRecommendationCategoryProvider implements ResourceBasedOverri
|
||||
// via the overridden WidgetRecommendationCategoryProvider resource.
|
||||
|
||||
Preconditions.assertWorkerThread();
|
||||
PackageManager pm = context.getPackageManager();
|
||||
PackageManagerHelper pmHelper = new PackageManagerHelper(context);
|
||||
if (item.widgetInfo != null && item.widgetInfo.getComponent() != null) {
|
||||
String widgetComponentName = item.widgetInfo.getComponent().getClassName();
|
||||
try {
|
||||
int predictionCategory = pm.getApplicationInfo(
|
||||
item.widgetInfo.getComponent().getPackageName(), 0 /* flags */).category;
|
||||
ApplicationInfo applicationInfo = pmHelper.getApplicationInfo(
|
||||
item.widgetInfo.getComponent().getPackageName(), item.widgetInfo.getUser(),
|
||||
0 /* flags */);
|
||||
if (applicationInfo != null) {
|
||||
int predictionCategory = applicationInfo.category;
|
||||
return getCategoryFromApplicationCategory(context, predictionCategory,
|
||||
widgetComponentName);
|
||||
} catch (PackageManager.NameNotFoundException e) {
|
||||
Log.e(TAG, "Failed to retrieve application category when determining the "
|
||||
+ "widget category for " + widgetComponentName, e);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
@@ -112,17 +112,22 @@ public class WidgetRecommendationCategoryProvider implements ResourceBasedOverri
|
||||
R.string.news_widget_recommendation_category_label, /*order=*/1);
|
||||
}
|
||||
|
||||
if (applicationCategory == ApplicationInfo.CATEGORY_SOCIAL
|
||||
|| applicationCategory == ApplicationInfo.CATEGORY_AUDIO
|
||||
if (applicationCategory == ApplicationInfo.CATEGORY_SOCIAL) {
|
||||
return new WidgetRecommendationCategory(
|
||||
R.string.social_widget_recommendation_category_label,
|
||||
/*order=*/5);
|
||||
}
|
||||
|
||||
if (applicationCategory == ApplicationInfo.CATEGORY_AUDIO
|
||||
|| applicationCategory == ApplicationInfo.CATEGORY_VIDEO
|
||||
|| applicationCategory == ApplicationInfo.CATEGORY_IMAGE) {
|
||||
return new WidgetRecommendationCategory(
|
||||
R.string.social_and_entertainment_widget_recommendation_category_label,
|
||||
/*order=*/4);
|
||||
R.string.entertainment_widget_recommendation_category_label,
|
||||
/*order=*/6);
|
||||
}
|
||||
|
||||
return new WidgetRecommendationCategory(
|
||||
R.string.others_widget_recommendation_category_label, /*order=*/5);
|
||||
R.string.others_widget_recommendation_category_label, /*order=*/4);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+5
-4
@@ -177,7 +177,7 @@ android_library {
|
||||
name: "launcher-testing-shared",
|
||||
srcs: [
|
||||
"multivalentTests/shared/com/android/launcher3/testing/shared/**/*.java",
|
||||
"multivalentTests/shared/com/android/launcher3/testing/shared/**/*.kt"
|
||||
"multivalentTests/shared/com/android/launcher3/testing/shared/**/*.kt",
|
||||
],
|
||||
resource_dirs: [],
|
||||
manifest: "multivalentTests/shared/AndroidManifest.xml",
|
||||
@@ -225,8 +225,8 @@ android_robolectric_test {
|
||||
// multivalentTests directory is a shared folder for not only robolectric converted test
|
||||
// classes but also shared helper classes.
|
||||
srcs: [
|
||||
"multivalentTests/src/com/android/launcher3/util/*.java",
|
||||
"multivalentTests/src/com/android/launcher3/util/*.kt",
|
||||
"multivalentTests/src/**/*.java",
|
||||
"multivalentTests/src/**/*.kt",
|
||||
|
||||
// Test util classes
|
||||
":launcher-testing-helpers",
|
||||
@@ -246,7 +246,8 @@ android_robolectric_test {
|
||||
"androidx.test.uiautomator_uiautomator",
|
||||
"androidx.core_core-animation-testing",
|
||||
"androidx.test.ext.junit",
|
||||
"inline-mockito-robolectric-prebuilt",
|
||||
"mockito-robolectric-prebuilt",
|
||||
"mockito-kotlin2",
|
||||
"platform-parametric-runner-lib",
|
||||
"testables",
|
||||
"Launcher3TestResources",
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,95 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2024 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.launcher3.util.rule
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
|
||||
import com.android.launcher3.InvariantDeviceProfile
|
||||
import com.android.launcher3.LauncherPrefs
|
||||
import java.io.File
|
||||
import java.nio.file.Paths
|
||||
import kotlin.io.path.pathString
|
||||
import org.junit.rules.TestRule
|
||||
import org.junit.runner.Description
|
||||
import org.junit.runners.model.Statement
|
||||
|
||||
/**
|
||||
* Removes all launcher's DBs from the device and copies the dbs in
|
||||
* assets/databases/BackupAndRestore to the device. It also set's the needed LauncherPrefs variables
|
||||
* needed to kickstart a backup and restore.
|
||||
*/
|
||||
class BackAndRestoreRule : TestRule {
|
||||
|
||||
private val phoneContext = getInstrumentation().targetContext
|
||||
|
||||
private fun isWorkspaceDatabase(rawFileName: String): Boolean {
|
||||
val fileName = Paths.get(rawFileName).fileName.pathString
|
||||
return fileName.startsWith("launcher") && fileName.endsWith(".db")
|
||||
}
|
||||
|
||||
fun getDatabaseFiles() =
|
||||
File(phoneContext.dataDir.path, "/databases").listFiles().filter {
|
||||
isWorkspaceDatabase(it.name)
|
||||
}
|
||||
|
||||
private fun deleteDBs() = getDatabaseFiles().forEach { it.delete() }
|
||||
|
||||
/**
|
||||
* Setting RESTORE_DEVICE would trigger a restore next time the Launcher starts, and we remove
|
||||
* the widgets and apps ids to prevent issues when loading the database.
|
||||
*/
|
||||
private fun setRestoreConstants() {
|
||||
LauncherPrefs.get(phoneContext)
|
||||
.put(LauncherPrefs.RESTORE_DEVICE.to(InvariantDeviceProfile.TYPE_MULTI_DISPLAY))
|
||||
LauncherPrefs.get(phoneContext)
|
||||
.remove(LauncherPrefs.OLD_APP_WIDGET_IDS, LauncherPrefs.APP_WIDGET_IDS)
|
||||
}
|
||||
|
||||
private fun uploadDatabase(dbName: String) {
|
||||
val file = File(File(getInstrumentation().targetContext.dataDir, "/databases"), dbName)
|
||||
file.writeBytes(
|
||||
InstrumentationRegistry.getInstrumentation()
|
||||
.context
|
||||
.assets
|
||||
.open("databases/BackupAndRestore/$dbName")
|
||||
.readBytes()
|
||||
)
|
||||
file.setWritable(true, false)
|
||||
}
|
||||
|
||||
fun before() {
|
||||
setRestoreConstants()
|
||||
deleteDBs()
|
||||
uploadDatabase("launcher.db")
|
||||
uploadDatabase("launcher_4_by_4.db")
|
||||
uploadDatabase("launcher_4_by_5.db")
|
||||
uploadDatabase("launcher_3_by_3.db")
|
||||
}
|
||||
|
||||
fun after() {
|
||||
deleteDBs()
|
||||
}
|
||||
|
||||
override fun apply(base: Statement?, description: Description?): Statement =
|
||||
object : Statement() {
|
||||
override fun evaluate() {
|
||||
before()
|
||||
base?.evaluate()
|
||||
after()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2024 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.backuprestore
|
||||
|
||||
import android.platform.test.flag.junit.SetFlagsRule
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.MediumTest
|
||||
import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation
|
||||
import com.android.launcher3.Flags
|
||||
import com.android.launcher3.model.ModelDbController
|
||||
import com.android.launcher3.util.Executors.MODEL_EXECUTOR
|
||||
import com.android.launcher3.util.TestUtil
|
||||
import com.android.launcher3.util.rule.BackAndRestoreRule
|
||||
import com.android.launcher3.util.rule.setFlags
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
/**
|
||||
* Makes sure to test {@code RestoreDbTask#removeOldDBs}, we need to remove all the dbs that are not
|
||||
* the last one used when we restore the device.
|
||||
*/
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@MediumTest
|
||||
class BackupAndRestoreDBSelectionTest {
|
||||
|
||||
@JvmField @Rule var backAndRestoreRule = BackAndRestoreRule()
|
||||
|
||||
@JvmField
|
||||
@Rule
|
||||
val setFlagsRule = SetFlagsRule(SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT)
|
||||
|
||||
@Before
|
||||
fun setUp() {
|
||||
setFlagsRule.setFlags(true, Flags.FLAG_NARROW_GRID_RESTORE)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun oldDatabasesNotPresentAfterRestore() {
|
||||
val dbController = ModelDbController(getInstrumentation().targetContext)
|
||||
dbController.tryMigrateDB(null)
|
||||
TestUtil.runOnExecutorSync(MODEL_EXECUTOR) {
|
||||
assert(backAndRestoreRule.getDatabaseFiles().size == 1) {
|
||||
"There should only be one database after restoring, the last one used. Actual databases ${backAndRestoreRule.getDatabaseFiles()}"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -110,6 +110,18 @@ class GeneratedPreviewTest {
|
||||
assertThat(widgetItem.hasGeneratedPreview(WIDGET_CATEGORY_SEARCHBOX)).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresFlagsEnabled(FLAG_ENABLE_GENERATED_PREVIEWS)
|
||||
fun widgetItem_hasGeneratedPreview_nullPreview() {
|
||||
appWidgetProviderInfo.generatedPreviewCategories =
|
||||
WIDGET_CATEGORY_HOME_SCREEN or WIDGET_CATEGORY_KEYGUARD
|
||||
createWidgetItem()
|
||||
assertThat(widgetItem.hasGeneratedPreview(WIDGET_CATEGORY_HOME_SCREEN)).isTrue()
|
||||
// loadGeneratedPreview returns null for KEYGUARD, so this should still be false.
|
||||
assertThat(widgetItem.hasGeneratedPreview(WIDGET_CATEGORY_KEYGUARD)).isFalse()
|
||||
assertThat(widgetItem.hasGeneratedPreview(WIDGET_CATEGORY_SEARCHBOX)).isFalse()
|
||||
}
|
||||
|
||||
@Test
|
||||
@RequiresFlagsDisabled(FLAG_ENABLE_GENERATED_PREVIEWS)
|
||||
fun widgetItem_hasGeneratedPreview_flagDisabled() {
|
||||
|
||||
+37
-23
@@ -23,6 +23,7 @@ import static android.content.pm.ApplicationInfo.CATEGORY_PRODUCTIVITY;
|
||||
import static android.content.pm.ApplicationInfo.CATEGORY_SOCIAL;
|
||||
import static android.content.pm.ApplicationInfo.CATEGORY_UNDEFINED;
|
||||
import static android.content.pm.ApplicationInfo.CATEGORY_VIDEO;
|
||||
import static android.content.pm.ApplicationInfo.FLAG_INSTALLED;
|
||||
|
||||
import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
|
||||
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
|
||||
@@ -30,8 +31,7 @@ import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentat
|
||||
import static com.google.common.truth.Truth.assertThat;
|
||||
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.anyInt;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
import static org.mockito.ArgumentMatchers.eq;
|
||||
import static org.mockito.Mockito.doAnswer;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@@ -41,7 +41,7 @@ import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.ContextWrapper;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.pm.LauncherApps;
|
||||
import android.os.Process;
|
||||
|
||||
import androidx.test.core.content.pm.ApplicationInfoBuilder;
|
||||
@@ -70,10 +70,25 @@ import java.util.Map;
|
||||
public class WidgetRecommendationCategoryProviderTest {
|
||||
private static final String TEST_PACKAGE = "com.foo.test";
|
||||
private static final String TEST_APP_NAME = "foo";
|
||||
public static final WidgetRecommendationCategory SOCIAL_AND_ENTERTAINMENT_CATEGORY =
|
||||
private static final WidgetRecommendationCategory PRODUCTIVITY =
|
||||
new WidgetRecommendationCategory(
|
||||
R.string.social_and_entertainment_widget_recommendation_category_label,
|
||||
/*order=*/4);
|
||||
R.string.productivity_widget_recommendation_category_label,
|
||||
/*order=*/0);
|
||||
private static final WidgetRecommendationCategory NEWS =
|
||||
new WidgetRecommendationCategory(
|
||||
R.string.news_widget_recommendation_category_label, /*order=*/1);
|
||||
private static final WidgetRecommendationCategory SUGGESTED_FOR_YOU =
|
||||
new WidgetRecommendationCategory(
|
||||
R.string.others_widget_recommendation_category_label, /*order=*/4);
|
||||
private static final WidgetRecommendationCategory SOCIAL =
|
||||
new WidgetRecommendationCategory(
|
||||
R.string.social_widget_recommendation_category_label,
|
||||
/*order=*/5);
|
||||
private static final WidgetRecommendationCategory ENTERTAINMENT =
|
||||
new WidgetRecommendationCategory(
|
||||
R.string.entertainment_widget_recommendation_category_label,
|
||||
/*order=*/6);
|
||||
|
||||
private final ApplicationInfo mTestAppInfo = ApplicationInfoBuilder.newBuilder().setPackageName(
|
||||
TEST_PACKAGE).setName(TEST_APP_NAME).build();
|
||||
private Context mContext;
|
||||
@@ -82,7 +97,7 @@ public class WidgetRecommendationCategoryProviderTest {
|
||||
|
||||
private WidgetItem mTestWidgetItem;
|
||||
@Mock
|
||||
private PackageManager mPackageManager;
|
||||
private LauncherApps mLauncherApps;
|
||||
private InvariantDeviceProfile mTestProfile;
|
||||
|
||||
@Before
|
||||
@@ -90,10 +105,12 @@ public class WidgetRecommendationCategoryProviderTest {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mContext = new ContextWrapper(getInstrumentation().getTargetContext()) {
|
||||
@Override
|
||||
public PackageManager getPackageManager() {
|
||||
return mPackageManager;
|
||||
public Object getSystemService(String name) {
|
||||
return LAUNCHER_APPS_SERVICE.equals(name) ? mLauncherApps : super.getSystemService(
|
||||
name);
|
||||
}
|
||||
};
|
||||
mTestAppInfo.flags = FLAG_INSTALLED;
|
||||
mTestProfile = new InvariantDeviceProfile();
|
||||
mTestProfile.numRows = 5;
|
||||
mTestProfile.numColumns = 5;
|
||||
@@ -103,25 +120,22 @@ public class WidgetRecommendationCategoryProviderTest {
|
||||
@Test
|
||||
public void getWidgetRecommendationCategory_returnsMappedCategory() throws Exception {
|
||||
ImmutableMap<Integer, WidgetRecommendationCategory> testCategories = ImmutableMap.of(
|
||||
CATEGORY_PRODUCTIVITY, new WidgetRecommendationCategory(
|
||||
R.string.productivity_widget_recommendation_category_label,
|
||||
/*order=*/
|
||||
0),
|
||||
CATEGORY_NEWS, new WidgetRecommendationCategory(
|
||||
R.string.news_widget_recommendation_category_label, /*order=*/1),
|
||||
CATEGORY_SOCIAL, SOCIAL_AND_ENTERTAINMENT_CATEGORY,
|
||||
CATEGORY_AUDIO, SOCIAL_AND_ENTERTAINMENT_CATEGORY,
|
||||
CATEGORY_IMAGE, SOCIAL_AND_ENTERTAINMENT_CATEGORY,
|
||||
CATEGORY_VIDEO, SOCIAL_AND_ENTERTAINMENT_CATEGORY,
|
||||
CATEGORY_UNDEFINED, new WidgetRecommendationCategory(
|
||||
R.string.others_widget_recommendation_category_label, /*order=*/5));
|
||||
CATEGORY_PRODUCTIVITY, PRODUCTIVITY,
|
||||
CATEGORY_NEWS, NEWS,
|
||||
CATEGORY_SOCIAL, SOCIAL,
|
||||
CATEGORY_AUDIO, ENTERTAINMENT,
|
||||
CATEGORY_IMAGE, ENTERTAINMENT,
|
||||
CATEGORY_VIDEO, ENTERTAINMENT,
|
||||
CATEGORY_UNDEFINED, SUGGESTED_FOR_YOU);
|
||||
|
||||
for (Map.Entry<Integer, WidgetRecommendationCategory> testCategory :
|
||||
testCategories.entrySet()) {
|
||||
|
||||
mTestAppInfo.category = testCategory.getKey();
|
||||
when(mPackageManager.getApplicationInfo(anyString(), anyInt())).thenReturn(
|
||||
mTestAppInfo);
|
||||
when(mLauncherApps.getApplicationInfo(/*packageName=*/ eq(TEST_PACKAGE),
|
||||
/*flags=*/ eq(0),
|
||||
/*user=*/ eq(Process.myUserHandle())))
|
||||
.thenReturn(mTestAppInfo);
|
||||
|
||||
WidgetRecommendationCategory category = Executors.MODEL_EXECUTOR.submit(() ->
|
||||
new WidgetRecommendationCategoryProvider().getWidgetRecommendationCategory(
|
||||
|
||||
@@ -26,11 +26,11 @@ import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_STASHED_
|
||||
import static com.android.launcher3.testing.shared.TestProtocol.REQUEST_TASKBAR_FROM_NAV_THRESHOLD;
|
||||
import static com.android.launcher3.testing.shared.TestProtocol.SUCCESSFUL_GESTURE_MISMATCH_EVENTS;
|
||||
import static com.android.launcher3.testing.shared.TestProtocol.TEST_INFO_RESPONSE_FIELD;
|
||||
import static com.android.launcher3.testing.shared.TestProtocol.testLogD;
|
||||
|
||||
import android.graphics.Point;
|
||||
import android.graphics.Rect;
|
||||
import android.os.SystemClock;
|
||||
import android.util.Log;
|
||||
import android.view.InputDevice;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.ViewConfiguration;
|
||||
@@ -141,7 +141,7 @@ public final class LaunchedAppState extends Background {
|
||||
|
||||
return new Taskbar(mLauncher);
|
||||
} finally {
|
||||
testLogD(SUCCESSFUL_GESTURE_MISMATCH_EVENTS,
|
||||
Log.d(SUCCESSFUL_GESTURE_MISMATCH_EVENTS,
|
||||
"swipeUpToUnstashTaskbar: completed gesture");
|
||||
mLauncher.getTestInfo(REQUEST_DISABLE_BLOCK_TIMEOUT);
|
||||
}
|
||||
|
||||
@@ -19,8 +19,10 @@ package com.android.launcher3.tapl;
|
||||
import static com.android.launcher3.tapl.OverviewTask.OverviewSplitTask.DEFAULT;
|
||||
import static com.android.launcher3.tapl.OverviewTask.OverviewSplitTask.SPLIT_BOTTOM_OR_RIGHT;
|
||||
import static com.android.launcher3.tapl.OverviewTask.OverviewSplitTask.SPLIT_TOP_OR_LEFT;
|
||||
import static com.android.launcher3.testing.shared.TestProtocol.SUCCESSFUL_GESTURE_MISMATCH_EVENTS;
|
||||
|
||||
import android.graphics.Rect;
|
||||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.test.uiautomator.By;
|
||||
@@ -219,6 +221,7 @@ public final class OverviewTask {
|
||||
return new LaunchedAppState(mLauncher);
|
||||
}
|
||||
} else {
|
||||
Log.d(SUCCESSFUL_GESTURE_MISMATCH_EVENTS, "TaskView.launchTaskAnimated");
|
||||
mLauncher.expectEvent(TestProtocol.SEQUENCE_MAIN, TASK_START_EVENT);
|
||||
return new LaunchedAppState(mLauncher);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user