Merging from ub-launcher3-master @ build 6356169

Bug:150504032
Test: manual, presubmit on the source branch
x20/teams/android-launcher/merge/ub-launcher3-master_6356169.html

Change-Id: Ife6c578d9a1e212eccdf3cf9a0cd838132eb90f5
This commit is contained in:
Adam Cohen
2020-04-01 17:52:00 -04:00
committed by Winson Chung
125 changed files with 1881 additions and 1478 deletions
-8
View File
@@ -137,14 +137,6 @@
</intent-filter>
</activity>
<!--
Should point to the content provider which can be used to dump Launcher3 compatible
worspace configuration to the dump's file descriptor by using launcher_dump.proto
-->
<meta-data
android:name="com.android.launcher3.launcher_dump_provider"
android:value="com.android.launcher3.LauncherProvider" />
<!--
The settings provider contains Home's data, like the workspace favorites. The permissions
should be changed to what is defined above. The authorities should also be changed to
+108
View File
@@ -0,0 +1,108 @@
/*
* 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.
*/
syntax = "proto2";
option java_package = "com.android.launcher3.logger";
option java_outer_classname = "LauncherAtom";
//
// ItemInfos
message ItemInfo {
oneof Item {
Application application = 1;
Task task= 2;
Shortcut shortcut = 3;
Widget widget = 4;
}
// When used for launch event, stores the global predictive rank
optional int32 rank = 5;
// Stores whether the Item belows to non primary user
optional bool is_work = 6;
// Item can be child node to parent container or parent containers (nested)
oneof Container {
WorkspaceContainer workspace = 7;
HotseatContainer hotseat = 8;
FolderContainer folder = 9;
}
// Stores the origin of the Item
optional Origin source = 10;
}
enum Origin {
UNKNOWN = 0;
DEFAULT_LAYOUT = 1; // icon automatically placed in workspace, folder, hotseat
BACKUP_RESTORE = 2; // icon layout restored from backup
PINITEM = 3; // from another app (e.g., Chrome's "Add to Home screen")
ALLAPPS_ATOZ = 4; // within launcher surface, all aps a-z
WIDGETS = 5; // within launcher, widgets tray
ADD_TO_HOMESCREEN = 6; // play install + launcher home setting
ALLAPPS_PREDICTION = 7; // from prediction bar in all apps container
HOTSEAT_PREDICTION = 8; // from prediction bar in hotseat container
}
// Main app icons
message Application {
optional string package_name = 1;
optional string component_name = 2;
}
// Legacy shortcuts and shortcuts handled by ShortcutManager
message Shortcut {
optional string shortcut_name = 1;
}
// AppWidgets handled by AppWidgetManager
message Widget {
optional int32 span_x = 1;
optional int32 span_y = 2;
optional int32 app_widget_id = 3;
optional string package_name = 4; // only populated during snapshot if from workspace
optional string component_name = 5; // only populated during snapshot if from workspace
}
// Tasks handled by PackageManager
message Task {
optional string package_name = 1;
optional string component_name = 2;
optional int32 index = 3;
}
//////////////////////////////////////////////
// Containers
message WorkspaceContainer {
optional int32 page_index = 1; // range [-1, l], 0 is the index of the main homescreen
optional int32 grid_x = 2; // [0, m], m varies based on the display density and resolution
optional int32 grid_y = 3; // [0, n], n varies based on the display density and resolution
}
message HotseatContainer {
optional int32 index = 1;
}
message FolderContainer {
optional int32 page_index = 1;
optional int32 grid_x = 2;
optional int32 grid_y = 3;
oneof Container {
WorkspaceContainer workspace = 4;
HotseatContainer hotseat = 5;
}
}
-75
View File
@@ -1,75 +0,0 @@
/*
* Copyright (C) 2017 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.
*/
syntax = "proto2";
option java_package = "com.android.launcher3.model";
option java_outer_classname = "LauncherDumpProto";
package model;
message DumpTarget {
enum Type {
NONE = 0;
ITEM = 1;
CONTAINER = 2;
}
optional Type type = 1;
optional int32 page_id = 2;
optional int32 grid_x = 3;
optional int32 grid_y = 4;
// For container types only
optional ContainerType container_type = 5;
// For item types only
optional ItemType item_type = 6;
optional string package_name = 7; // All ItemTypes except UNKNOWN type
optional string component = 8; // All ItemTypes except UNKNOWN type
optional string item_id = 9; // For Pinned Shortcuts and appWidgetId
optional int32 span_x = 10 [default = 1];// Used for ItemType.WIDGET
optional int32 span_y = 11 [default = 1];// Used for ItemType.WIDGET
optional UserType user_type = 12;
}
// Used to define what type of item a Target would represent.
enum ItemType {
UNKNOWN_ITEMTYPE = 0; // Launcher specific items
APP_ICON = 1; // Regular app icons
WIDGET = 2; // Elements from AppWidgetManager
SHORTCUT = 3; // ShortcutManager
}
// Used to define what type of container a Target would represent.
enum ContainerType {
UNKNOWN_CONTAINERTYPE = 0;
WORKSPACE = 1;
HOTSEAT = 2;
FOLDER = 3;
}
// Used to define what type of control a Target would represent.
enum UserType {
DEFAULT = 0;
WORK = 1;
}
// Main message;
message LauncherImpression {
repeated DumpTarget targets = 1;
}
+11
View File
@@ -73,6 +73,17 @@
</intent-filter>
</provider>
<!-- FileProvider used for sharing images. -->
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${packageName}.overview.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/overview_file_provider_paths" />
</provider>
<service
android:name="com.android.launcher3.uioverrides.dynamicui.WallpaperManagerCompatVL$ColorExtractionService"
tools:node="remove" />
@@ -31,9 +31,6 @@
<dimen name="all_apps_label_top_padding">16dp</dimen>
<dimen name="all_apps_label_bottom_padding">8dp</dimen>
<dimen name="all_apps_label_text_size">14sp</dimen>
<dimen name="all_apps_tip_bottom_margin">8dp</dimen>
<!-- The size of corner radius of the arrow in the arrow toast. -->
<dimen name="arrow_toast_corner_radius">2dp</dimen>
<!-- Minimum distance to swipe to trigger accessibility gesture -->
<dimen name="accessibility_gesture_min_swipe_distance">80dp</dimen>
@@ -24,13 +24,13 @@ import static com.android.quickstep.logging.UserEventDispatcherExtension.ALL_APP
import android.os.UserManager;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.ArrowTipView;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.FloatingHeaderView;
import com.android.launcher3.views.ArrowTipView;
import com.android.systemui.shared.system.LauncherEventUtil;
/**
@@ -26,7 +26,6 @@ import android.view.View;
import androidx.core.app.NotificationCompat;
import com.android.launcher3.ArrowTipView;
import com.android.launcher3.CellLayout;
import com.android.launcher3.FolderInfo;
import com.android.launcher3.Hotseat;
@@ -43,6 +42,7 @@ import com.android.launcher3.util.ActivityTracker;
import com.android.launcher3.util.GridOccupancy;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ArrowTipView;
import com.android.launcher3.views.Snackbar;
import java.util.ArrayDeque;
@@ -15,18 +15,22 @@
*/
package com.android.launcher3.uioverrides;
import static com.android.launcher3.LauncherState.RECENTS_CLEAR_ALL_BUTTON;
import static com.android.launcher3.LauncherState.OVERVIEW_BUTTONS;
import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE_IN_OUT;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_FADE;
import static com.android.quickstep.views.RecentsView.CONTENT_ALPHA;
import static com.android.quickstep.views.RecentsView.FULLSCREEN_PROGRESS;
import android.annotation.TargetApi;
import android.os.Build;
import android.util.FloatProperty;
import android.view.View;
import android.view.animation.Interpolator;
import androidx.annotation.NonNull;
import com.android.launcher3.Launcher;
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.PendingAnimation;
@@ -44,7 +48,7 @@ import com.android.quickstep.views.RecentsView;
public final class RecentsViewStateController extends
BaseRecentsViewStateController<LauncherRecentsView> {
public RecentsViewStateController(Launcher launcher) {
public RecentsViewStateController(BaseQuickstepLauncher launcher) {
super(launcher);
}
@@ -55,7 +59,7 @@ public final class RecentsViewStateController extends
mRecentsView.updateEmptyMessage();
mRecentsView.resetTaskVisuals();
}
setAlphas(PropertySetter.NO_ANIM_PROPERTY_SETTER, state.getVisibleElements(mLauncher));
setAlphas(PropertySetter.NO_ANIM_PROPERTY_SETTER, state, LINEAR);
mRecentsView.setFullscreenProgress(state.getOverviewFullscreenProgress());
}
@@ -73,15 +77,22 @@ public final class RecentsViewStateController extends
AnimationSuccessListener.forRunnable(mRecentsView::resetTaskVisuals));
}
setAlphas(builder, toState.getVisibleElements(mLauncher));
setAlphas(builder, toState,
config.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT));
builder.setFloat(mRecentsView, FULLSCREEN_PROGRESS,
toState.getOverviewFullscreenProgress(), LINEAR);
}
private void setAlphas(PropertySetter propertySetter, int visibleElements) {
boolean hasClearAllButton = (visibleElements & RECENTS_CLEAR_ALL_BUTTON) != 0;
private void setAlphas(PropertySetter propertySetter, LauncherState state,
Interpolator actionInterpolator) {
float buttonAlpha = (state.getVisibleElements(mLauncher) & OVERVIEW_BUTTONS) != 0 ? 1 : 0;
propertySetter.setFloat(mRecentsView.getClearAllButton(), ClearAllButton.VISIBILITY_ALPHA,
hasClearAllButton ? 1f : 0f, LINEAR);
buttonAlpha, LINEAR);
View actionsView = mLauncher.getActionsView();
if (actionsView != null) {
propertySetter.setViewAlpha(actionsView, buttonAlpha, actionInterpolator);
}
}
@Override
@@ -90,7 +90,7 @@ public class BackgroundAppState extends OverviewState {
@Override
public int getVisibleElements(Launcher launcher) {
return super.getVisibleElements(launcher)
& ~RECENTS_CLEAR_ALL_BUTTON & ~VERTICAL_SWIPE_INDICATOR;
& ~OVERVIEW_BUTTONS & ~VERTICAL_SWIPE_INDICATOR;
}
@Override
@@ -25,6 +25,7 @@ import static com.android.launcher3.states.StateAnimationConfig.ANIM_OVERVIEW_TR
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.states.StateAnimationConfig;
public class OverviewPeekState extends OverviewState {
@@ -37,6 +38,9 @@ public class OverviewPeekState extends OverviewState {
ScaleAndTranslation result = super.getOverviewScaleAndTranslation(launcher);
result.translationX = NORMAL.getOverviewScaleAndTranslation(launcher).translationX
- launcher.getResources().getDimension(R.dimen.overview_peek_distance);
if (Utilities.isRtl(launcher.getResources())) {
result.translationX = -result.translationX;
}
return result;
}
@@ -164,17 +164,15 @@ public class OverviewState extends LauncherState {
@Override
public int getVisibleElements(Launcher launcher) {
if (launcher.getDeviceProfile().isVerticalBarLayout()) {
return VERTICAL_SWIPE_INDICATOR | RECENTS_CLEAR_ALL_BUTTON;
if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(launcher)) {
return OVERVIEW_BUTTONS;
} else if (launcher.getDeviceProfile().isVerticalBarLayout()) {
return VERTICAL_SWIPE_INDICATOR | OVERVIEW_BUTTONS;
} else {
if (ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(launcher)) {
return VERTICAL_SWIPE_INDICATOR | RECENTS_CLEAR_ALL_BUTTON;
}
boolean hasAllAppsHeaderExtra = launcher.getAppsView() != null
&& launcher.getAppsView().getFloatingHeaderView().hasVisibleContent();
return HOTSEAT_SEARCH_BOX | VERTICAL_SWIPE_INDICATOR | RECENTS_CLEAR_ALL_BUTTON |
(hasAllAppsHeaderExtra ? ALL_APPS_HEADER_EXTRA : HOTSEAT_ICONS);
return HOTSEAT_SEARCH_BOX | VERTICAL_SWIPE_INDICATOR | OVERVIEW_BUTTONS
| (hasAllAppsHeaderExtra ? ALL_APPS_HEADER_EXTRA : HOTSEAT_ICONS);
}
}
@@ -73,7 +73,11 @@ public class FlingAndHoldTouchController extends PortraitStatesTouchController {
super(l, false /* allowDragToOverview */);
mMotionPauseDetector = new MotionPauseDetector(l);
mMotionPauseMinDisplacement = ViewConfiguration.get(l).getScaledTouchSlop();
mMotionPauseMaxDisplacement = getShiftRange() * MAX_DISPLACEMENT_PERCENT;
mMotionPauseMaxDisplacement = getMotionPauseMaxDisplacement();
}
protected float getMotionPauseMaxDisplacement() {
return getShiftRange() * MAX_DISPLACEMENT_PERCENT;
}
@Override
@@ -65,6 +65,13 @@ public class NoButtonNavbarToOverviewTouchController extends FlingAndHoldTouchCo
mRecentsView = l.getOverviewPanel();
}
@Override
protected float getMotionPauseMaxDisplacement() {
// No need to disallow pause when swiping up all the way up the screen (unlike
// FlingAndHoldTouchController where user is probably intending to go to all apps).
return Float.MAX_VALUE;
}
@Override
protected boolean canInterceptTouch(MotionEvent ev) {
mDidTouchStartInNavBar = (ev.getEdgeFlags() & EDGE_NAV_BAR) != 0;
@@ -19,7 +19,7 @@ import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
import static com.android.launcher3.uioverrides.DepthController.DEPTH;
import static com.android.launcher3.statehandlers.DepthController.DEPTH;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
@@ -34,7 +34,7 @@ import android.view.View;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.uioverrides.DepthController;
import com.android.launcher3.statehandlers.DepthController;
import com.android.quickstep.util.AppWindowAnimationHelper;
import com.android.quickstep.util.AppWindowAnimationHelper.TransformParams;
import com.android.quickstep.util.RemoteAnimationProvider;
@@ -105,12 +105,6 @@ final class AppToOverviewAnimationProvider<T extends BaseDraggingActivity> exten
mRecentsView.setRunningTaskIconScaledDown(true);
}
DepthController depthController = mActivityInterface.getDepthController();
if (depthController != null) {
// Update the surface to be the lowest closing app surface
depthController.setSurfaceToLauncher(mRecentsView);
}
AnimatorSet anim = new AnimatorSet();
anim.addListener(new AnimationSuccessListener() {
@Override
@@ -123,11 +117,17 @@ final class AppToOverviewAnimationProvider<T extends BaseDraggingActivity> exten
});
if (mActivity == null) {
Log.e(TAG, "Animation created, before activity");
anim.play(ValueAnimator.ofInt(0, 1).setDuration(RECENTS_LAUNCH_DURATION))
.with(createDepthAnimator(depthController));
return anim;
}
DepthController depthController = mActivityInterface.getDepthController();
if (depthController != null) {
anim.play(ObjectAnimator.ofFloat(depthController, DEPTH,
BACKGROUND_APP.getDepth(mActivity),
OVERVIEW.getDepth(mActivity))
.setDuration(RECENTS_LAUNCH_DURATION));
}
RemoteAnimationTargets targets = new RemoteAnimationTargets(appTargets,
wallpaperTargets, MODE_CLOSING);
@@ -135,8 +135,6 @@ final class AppToOverviewAnimationProvider<T extends BaseDraggingActivity> exten
RemoteAnimationTargetCompat runningTaskTarget = targets.findTask(mTargetTaskId);
if (runningTaskTarget == null) {
Log.e(TAG, "No closing app");
anim.play(ValueAnimator.ofInt(0, 1).setDuration(RECENTS_LAUNCH_DURATION))
.with(createDepthAnimator(depthController));
return anim;
}
@@ -183,8 +181,6 @@ final class AppToOverviewAnimationProvider<T extends BaseDraggingActivity> exten
transaction.apply();
});
}
anim.play(valueAnimator)
.with(createDepthAnimator(depthController));
return anim;
}
@@ -196,15 +192,4 @@ final class AppToOverviewAnimationProvider<T extends BaseDraggingActivity> exten
long getRecentsLaunchDuration() {
return RECENTS_LAUNCH_DURATION;
}
private Animator createDepthAnimator(DepthController depthController) {
if (depthController == null) {
// Dummy animation
return ValueAnimator.ofInt(0);
}
return ObjectAnimator.ofFloat(depthController, DEPTH,
BACKGROUND_APP.getDepth(mActivity),
OVERVIEW.getDepth(mActivity))
.setDuration(RECENTS_LAUNCH_DURATION);
}
}
@@ -31,6 +31,7 @@ import android.graphics.PointF;
import android.graphics.Rect;
import android.graphics.RectF;
import android.os.Build;
import android.util.Log;
import android.util.Pair;
import android.view.MotionEvent;
import android.view.View;
@@ -48,6 +49,7 @@ import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.model.PagedViewOrientedState;
import com.android.launcher3.states.RotationHelper;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.touch.PortraitPagedViewHandler;
import com.android.launcher3.util.VibratorWrapper;
@@ -198,18 +200,33 @@ public abstract class BaseSwipeUpHandler<T extends BaseDraggingActivity, Q exten
}
protected void startNewTask(int successStateFlag, Consumer<Boolean> resultCallback) {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NO_START_FROM_RECENTS, "startNewTask1");
}
// Launch the task user scrolled to (mRecentsView.getNextPage()).
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
// We finish recents animation inside launchTask() when live tile is enabled.
mRecentsView.getNextPageTaskView().launchTask(false /* animate */,
true /* freezeTaskList */);
} else {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NO_START_FROM_RECENTS, "startNewTask2");
}
int taskId = mRecentsView.getNextPageTaskView().getTask().key.id;
mFinishingRecentsAnimationForNewTaskId = taskId;
mRecentsAnimationController.finish(true /* toRecents */, () -> {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NO_START_FROM_RECENTS, "onFinishComplete1");
}
if (!mCanceled) {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NO_START_FROM_RECENTS, "onFinishComplete2");
}
TaskView nextTask = mRecentsView.getTaskView(taskId);
if (nextTask != null) {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NO_START_FROM_RECENTS, "onFinishComplete3");
}
nextTask.launchTask(false /* animate */, true /* freezeTaskList */,
success -> {
resultCallback.accept(success);
@@ -166,6 +166,7 @@ public class FallbackSwipeHandler extends BaseSwipeUpHandler<RecentsActivity, Fa
super.onActivityInit(alreadyOnHome);
mActivity = mActivityInterface.getCreatedActivity();
mRecentsView = mActivity.getOverviewPanel();
mRecentsView.setOnPageTransitionEndCallback(null);
linkRecentsViewScroll();
mRecentsView.setDisallowScrollToClearAll(true);
mRecentsView.getClearAllButton().setVisibilityAlpha(0);
@@ -434,7 +435,12 @@ public class FallbackSwipeHandler extends BaseSwipeUpHandler<RecentsActivity, Fa
@Override
public void onAnimationSuccess(Animator animator) {
finishAnimationTargetSetAnimationComplete();
if (mRecentsView != null) {
mRecentsView.setOnPageTransitionEndCallback(FallbackSwipeHandler.this
::finishAnimationTargetSetAnimationComplete);
} else {
finishAnimationTargetSetAnimationComplete();
}
mFinishAnimation = null;
}
};
@@ -0,0 +1,94 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.quickstep;
import static android.content.Intent.EXTRA_STREAM;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.quickstep.util.ImageActionUtils.persistBitmapAndStartActivity;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Insets;
import android.graphics.Rect;
import android.util.Log;
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
import com.android.launcher3.BuildConfig;
import com.android.quickstep.util.ImageActionUtils;
import java.util.function.Supplier;
/**
* Contains image selection functions necessary to complete overview action button functions.
*/
public class ImageActionsApi {
private static final String TAG = BuildConfig.APPLICATION_ID + "ImageActionsApi";
private final Context mContext;
private final Supplier<Bitmap> mBitmapSupplier;
private final SystemUiProxy mSystemUiProxy;
public ImageActionsApi(Context context, Supplier<Bitmap> bitmapSupplier) {
mContext = context;
mBitmapSupplier = bitmapSupplier;
mSystemUiProxy = SystemUiProxy.INSTANCE.get(context);
}
/**
* Share the image this api was constructed with using the provided intent. The implementation
* should add an {@link Intent#EXTRA_STREAM} with the URI pointing to the image to the intent.
*/
@UiThread
public void shareWithExplicitIntent(@Nullable Rect crop, Intent intent) {
if (mBitmapSupplier.get() == null) {
Log.e(TAG, "No snapshot available, not starting share.");
return;
}
UI_HELPER_EXECUTOR.execute(() -> persistBitmapAndStartActivity(mContext,
mBitmapSupplier.get(), crop, intent, (uri, intentForUri) -> {
intentForUri.putExtra(EXTRA_STREAM, uri);
return new Intent[]{intentForUri};
}, TAG));
}
/**
* Share the image this api was constructed with.
*/
@UiThread
public void startShareActivity() {
ImageActionUtils.startShareActivity(mContext, mBitmapSupplier, null, null, TAG);
}
/**
* @param screenshot to be saved to the media store.
* @param screenshotBounds the location of where the bitmap was laid out on the screen in
* screen coordinates.
* @param visibleInsets that are used to draw the screenshot within the bounds.
* @param taskId of the task that the screenshot was taken of.
*/
public void saveScreenshot(Bitmap screenshot, Rect screenshotBounds,
Insets visibleInsets, int taskId) {
ImageActionUtils.saveScreenshot(mSystemUiProxy, screenshot, screenshotBounds, visibleInsets,
taskId);
}
}
@@ -55,9 +55,9 @@ import com.android.launcher3.LauncherState;
import com.android.launcher3.allapps.DiscoveryBounce;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.appprediction.PredictionUiStateManager;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statehandlers.DepthController.ClampedDepthProperty;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.uioverrides.DepthController;
import com.android.launcher3.uioverrides.DepthController.ClampedDepthProperty;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.views.FloatingIconView;
import com.android.quickstep.SysUINavigationMode.Mode;
@@ -29,6 +29,7 @@ import static com.android.quickstep.GestureState.GestureEndTarget.LAST_TASK;
import static com.android.quickstep.GestureState.GestureEndTarget.NEW_TASK;
import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
import static com.android.quickstep.GestureState.STATE_END_TARGET_ANIMATION_FINISHED;
import static com.android.quickstep.GestureState.STATE_RECENTS_SCROLLING_FINISHED;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
import static com.android.quickstep.util.ShelfPeekAnim.ShelfAnimState.HIDE;
@@ -46,6 +47,7 @@ import android.graphics.PointF;
import android.graphics.RectF;
import android.os.Build;
import android.os.SystemClock;
import android.util.Log;
import android.view.View;
import android.view.View.OnApplyWindowInsetsListener;
import android.view.ViewTreeObserver.OnDrawListener;
@@ -64,6 +66,7 @@ import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.logging.UserEventDispatcher;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
@@ -244,7 +247,11 @@ public class LauncherSwipeHandler<T extends BaseDraggingActivity>
| STATE_GESTURE_STARTED,
this::setupLauncherUiAfterSwipeUpToRecentsAnimation);
mGestureState.runOnceAtState(STATE_END_TARGET_ANIMATION_FINISHED, this::onEndTargetSet);
mGestureState.runOnceAtState(STATE_END_TARGET_ANIMATION_FINISHED,
this::continueComputingRecentsScrollIfNecessary);
mGestureState.runOnceAtState(STATE_END_TARGET_ANIMATION_FINISHED
| STATE_RECENTS_SCROLLING_FINISHED,
this::onSettledOnEndTarget);
mStateCallback.runOnceAtState(STATE_HANDLER_INVALIDATED, this::invalidateHandler);
mStateCallback.runOnceAtState(STATE_LAUNCHER_PRESENT | STATE_HANDLER_INVALIDATED,
@@ -283,6 +290,7 @@ public class LauncherSwipeHandler<T extends BaseDraggingActivity>
}
mRecentsView = activity.getOverviewPanel();
mRecentsView.setOnPageTransitionEndCallback(null);
linkRecentsViewScroll();
addLiveTileOverlay();
@@ -294,7 +302,6 @@ public class LauncherSwipeHandler<T extends BaseDraggingActivity>
}
setupRecentsViewUi();
mActivityInterface.getDepthController().setSurfaceToLauncher(mRecentsView);
if (mDeviceState.getNavMode() == TWO_BUTTONS) {
// If the device is in two button mode, swiping up will show overview with predictions
@@ -506,16 +513,22 @@ public class LauncherSwipeHandler<T extends BaseDraggingActivity>
}
private void buildAnimationController() {
if (mGestureState.getEndTarget() == HOME || mHasLauncherTransitionControllerStarted) {
// We don't want a new mLauncherTransitionController if
// mGestureState.getEndTarget() == HOME (it has its own animation) or if we're already
// animating the current controller.
if (!canCreateNewOrUpdateExistingLauncherTransitionController()) {
return;
}
initTransitionEndpoints(mActivity.getDeviceProfile());
mAnimationFactory.createActivityInterface(mTransitionDragLength);
}
/**
* We don't want to change mLauncherTransitionController if mGestureState.getEndTarget() == HOME
* (it has its own animation) or if we're already animating the current controller.
* @return Whether we can create the launcher controller or update its progress.
*/
private boolean canCreateNewOrUpdateExistingLauncherTransitionController() {
return mGestureState.getEndTarget() != HOME && !mHasLauncherTransitionControllerStarted;
}
@Override
public WindowInsets onApplyWindowInsets(View view, WindowInsets windowInsets) {
WindowInsets result = view.onApplyWindowInsets(windowInsets);
@@ -559,15 +572,12 @@ public class LauncherSwipeHandler<T extends BaseDraggingActivity>
}
}
if (mLauncherTransitionController == null || mLauncherTransitionController
.getAnimationPlayer().isStarted()) {
return;
}
updateLauncherTransitionProgress();
}
private void updateLauncherTransitionProgress() {
if (mGestureState.getEndTarget() == HOME) {
if (mLauncherTransitionController == null
|| !canCreateNewOrUpdateExistingLauncherTransitionController()) {
return;
}
// Normalize the progress to 0 to 1, as the animation controller will clamp it to that
@@ -697,7 +707,7 @@ public class LauncherSwipeHandler<T extends BaseDraggingActivity>
}
}
private void onEndTargetSet() {
private void onSettledOnEndTarget() {
switch (mGestureState.getEndTarget()) {
case HOME:
mStateCallback.setState(STATE_SCALED_CONTROLLER_HOME | STATE_CAPTURE_SCREENSHOT);
@@ -860,13 +870,17 @@ public class LauncherSwipeHandler<T extends BaseDraggingActivity>
if (mDeviceState.isFullyGesturalNavMode()) {
setShelfState(ShelfAnimState.OVERVIEW, interpolator, duration);
}
} else if (endTarget == NEW_TASK || endTarget == LAST_TASK) {
// Let RecentsView handle the scrolling to the task, which we launch in startNewTask()
// or resumeLastTask().
if (mRecentsView != null) {
duration = Math.max(duration, mRecentsView.getScroller().getDuration());
}
}
// Let RecentsView handle the scrolling to the task, which we launch in startNewTask()
// or resumeLastTask().
if (mRecentsView != null) {
mRecentsView.setOnPageTransitionEndCallback(
() -> mGestureState.setState(STATE_RECENTS_SCROLLING_FINISHED));
} else {
mGestureState.setState(STATE_RECENTS_SCROLLING_FINISHED);
}
animateToProgress(startShift, endShift, duration, interpolator, endTarget, velocityPxPerMs);
}
@@ -950,15 +964,14 @@ public class LauncherSwipeHandler<T extends BaseDraggingActivity>
ValueAnimator windowAnim = mCurrentShift.animateToValue(start, end);
windowAnim.setDuration(duration).setInterpolator(interpolator);
windowAnim.addUpdateListener(valueAnimator -> {
if (mRecentsView != null && mRecentsView.getVisibility() != View.VISIBLE) {
// Views typically don't compute scroll when invisible as an optimization,
// but in our case we need to since the window offset depends on the scroll.
mRecentsView.computeScroll();
}
computeRecentsScrollIfInvisible();
});
windowAnim.addListener(new AnimationSuccessListener() {
@Override
public void onAnimationSuccess(Animator animator) {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NO_START_FROM_RECENTS, "onAnimationSuccess");
}
if (mRecentsAnimationController == null) {
// If the recents animation is interrupted, we still end the running
// animation (not canceled) so this is still called. In that case, we can
@@ -1006,6 +1019,21 @@ public class LauncherSwipeHandler<T extends BaseDraggingActivity>
mHasLauncherTransitionControllerStarted = true;
}
private void computeRecentsScrollIfInvisible() {
if (mRecentsView != null && mRecentsView.getVisibility() != View.VISIBLE) {
// Views typically don't compute scroll when invisible as an optimization,
// but in our case we need to since the window offset depends on the scroll.
mRecentsView.computeScroll();
}
}
private void continueComputingRecentsScrollIfNecessary() {
if (!mGestureState.hasState(STATE_RECENTS_SCROLLING_FINISHED)) {
computeRecentsScrollIfInvisible();
mRecentsView.post(this::continueComputingRecentsScrollIfNecessary);
}
}
/**
* Creates an animation that transforms the current app window into the home app.
* @param startProgress The progress of {@link #mCurrentShift} to start the window from.
@@ -1167,6 +1195,9 @@ public class LauncherSwipeHandler<T extends BaseDraggingActivity>
}
private void switchToScreenshot() {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NO_START_FROM_RECENTS, "switchToScreenshot");
}
final int runningTaskId = mGestureState.getRunningTaskId();
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
if (mRecentsAnimationController != null) {
@@ -16,12 +16,13 @@
package com.android.quickstep;
import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
import static com.android.launcher3.util.MainThreadInitializedObject.forOverride;
import android.content.Context;
import android.graphics.Insets;
import android.graphics.Matrix;
import android.view.View;
import androidx.annotation.Nullable;
import android.graphics.Rect;
import com.android.launcher3.BaseActivity;
import com.android.launcher3.BaseDraggingActivity;
@@ -29,6 +30,7 @@ import com.android.launcher3.R;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.ResourceBasedOverride;
import com.android.quickstep.views.OverviewActionsView;
import com.android.quickstep.views.TaskThumbnailView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.plugins.OverscrollPlugin;
@@ -43,16 +45,6 @@ import java.util.List;
*/
public class TaskOverlayFactory implements ResourceBasedOverride {
/** Note that these will be shown in order from top to bottom, if available for the task. */
private static final TaskShortcutFactory[] MENU_OPTIONS = new TaskShortcutFactory[]{
TaskShortcutFactory.APP_INFO,
TaskShortcutFactory.SPLIT_SCREEN,
TaskShortcutFactory.PIN,
TaskShortcutFactory.INSTALL,
TaskShortcutFactory.FREE_FORM,
TaskShortcutFactory.WELLBEING
};
public static List<SystemShortcut> getEnabledShortcuts(TaskView taskView) {
final ArrayList<SystemShortcut> shortcuts = new ArrayList<>();
final BaseDraggingActivity activity = BaseActivity.fromContext(taskView.getContext());
@@ -76,25 +68,68 @@ public class TaskOverlayFactory implements ResourceBasedOverride {
}
public TaskOverlay createOverlay(TaskThumbnailView thumbnailView) {
return new TaskOverlay();
return new TaskOverlay(thumbnailView);
}
/** Note that these will be shown in order from top to bottom, if available for the task. */
private static final TaskShortcutFactory[] MENU_OPTIONS = new TaskShortcutFactory[]{
TaskShortcutFactory.APP_INFO,
TaskShortcutFactory.SPLIT_SCREEN,
TaskShortcutFactory.PIN,
TaskShortcutFactory.INSTALL,
TaskShortcutFactory.FREE_FORM,
TaskShortcutFactory.WELLBEING
};
/**
* Overlay on each task handling Overview Action Buttons.
*/
public static class TaskOverlay {
private final Context mApplicationContext;
private OverviewActionsView mActionsView;
private final TaskThumbnailView mThumbnailView;
protected TaskOverlay(TaskThumbnailView taskThumbnailView) {
mApplicationContext = taskThumbnailView.getContext().getApplicationContext();
mThumbnailView = taskThumbnailView;
}
/**
* Called when the current task is interactive for the user
*/
public void initOverlay(Task task, ThumbnailData thumbnail, Matrix matrix) { }
public void initOverlay(Task task, ThumbnailData thumbnail, Matrix matrix) {
ImageActionsApi imageApi = new ImageActionsApi(
mApplicationContext, mThumbnailView::getThumbnail);
if (mActionsView == null && ENABLE_OVERVIEW_ACTIONS.get()
&& SysUINavigationMode.removeShelfFromOverview(mApplicationContext)) {
mActionsView = BaseActivity.fromContext(mThumbnailView.getContext()).findViewById(
R.id.overview_actions_view);
}
if (mActionsView != null) {
mActionsView.setListener(new OverviewActionsView.Listener() {
@Override
public void onShare() {
imageApi.startShareActivity();
}
@Override
public void onScreenshot() {
imageApi.saveScreenshot(mThumbnailView.getThumbnail(),
getTaskSnapshotBounds(), getTaskSnapshotInsets(), task.key.id);
}
});
}
@Nullable
public View getActionsView() {
return null;
}
/**
* Called when the overlay is no longer used.
*/
public void reset() { }
public void reset() {
}
/**
* Whether the overlay is modal, which means only tapping is enabled, but no swiping.
@@ -102,5 +137,28 @@ public class TaskOverlayFactory implements ResourceBasedOverride {
public boolean isOverlayModal() {
return false;
}
/**
* Gets the task snapshot as it is displayed on the screen.
*
* @return the bounds of the snapshot in screen coordinates.
*/
public Rect getTaskSnapshotBounds() {
int[] location = new int[2];
mThumbnailView.getLocationOnScreen(location);
return new Rect(location[0], location[1], mThumbnailView.getWidth() + location[0],
mThumbnailView.getHeight() + location[1]);
}
/**
* Gets the insets that the snapshot is drawn with.
*
* @return the insets in screen coordinates.
*/
public Insets getTaskSnapshotInsets() {
// TODO: return the real insets
return Insets.of(0, 0, 0, 0);
}
}
}
@@ -19,7 +19,7 @@ import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.uioverrides.DepthController.DEPTH;
import static com.android.launcher3.statehandlers.DepthController.DEPTH;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
import android.animation.Animator;
@@ -35,7 +35,7 @@ import com.android.launcher3.BaseActivity;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.Utilities;
import com.android.launcher3.uioverrides.DepthController;
import com.android.launcher3.statehandlers.DepthController;
import com.android.quickstep.util.AppWindowAnimationHelper;
import com.android.quickstep.util.MultiValueUpdateListener;
import com.android.quickstep.views.RecentsView;
@@ -22,6 +22,7 @@ import static android.view.MotionEvent.ACTION_UP;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.quickstep.GestureState.DEFAULT_STATE;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_INPUT_MONITOR;
import static com.android.systemui.shared.system.QuickStepContract.KEY_EXTRA_SYSUI_PROXY;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_TRACING_ENABLED;
@@ -124,6 +125,7 @@ public class TouchInteractionService extends Service implements PluginListener<O
private static final String NOTIFY_ACTION_BACK = "com.android.quickstep.action.BACK_GESTURE";
private static final String HAS_ENABLED_QUICKSTEP_ONCE = "launcher.has_enabled_quickstep_once";
private static final int MAX_BACK_NOTIFICATION_COUNT = 3;
private int mBackGestureNotificationCounter = -1;
@Nullable
private OverscrollPlugin mOverscrollPlugin;
@@ -263,7 +265,7 @@ public class TouchInteractionService extends Service implements PluginListener<O
private InputConsumer mConsumer = InputConsumer.NO_OP;
private Choreographer mMainChoreographer;
private InputConsumer mResetGestureInputConsumer;
private GestureState mGestureState = new GestureState();
private GestureState mGestureState = DEFAULT_STATE;
private InputMonitorCompat mInputMonitorCompat;
private InputEventReceiver mInputEventReceiver;
@@ -435,16 +437,14 @@ public class TouchInteractionService extends Service implements PluginListener<O
Object traceToken = TraceHelper.INSTANCE.beginFlagsOverride(
TraceHelper.FLAG_ALLOW_BINDER_TRACKING);
mDeviceState.setOrientationTransformIfNeeded(event);
final int action = event.getAction();
if (action == ACTION_DOWN) {
GestureState newGestureState = new GestureState(mOverviewComponentObserver,
ActiveGestureLog.INSTANCE.generateAndSetLogId());
newGestureState.updateRunningTask(TraceHelper.whitelistIpcs("getRunningTask.0",
() -> mAM.getRunningTask(true /* filterOnlyVisibleRecents */)));
mDeviceState.setOrientationTransformIfNeeded(event);
GestureState newGestureState;
if (mDeviceState.isInSwipeUpTouchRegion(event)) {
newGestureState = createGestureState();
mConsumer.onConsumerAboutToBeSwitched();
mConsumer = newConsumer(mGestureState, newGestureState, event);
@@ -453,6 +453,7 @@ public class TouchInteractionService extends Service implements PluginListener<O
} else if (mDeviceState.isUserUnlocked()
&& mDeviceState.isFullyGesturalNavMode()
&& mDeviceState.canTriggerAssistantAction(event)) {
newGestureState = createGestureState();
// Do not change mConsumer as if there is an ongoing QuickSwitch gesture, we should
// not interrupt it. QuickSwitch assumes that interruption can only happen if the
// next gesture is also quick switch.
@@ -462,11 +463,18 @@ public class TouchInteractionService extends Service implements PluginListener<O
InputConsumer.NO_OP, mInputMonitorCompat,
mOverviewComponentObserver.assistantGestureIsConstrained());
} else {
newGestureState = DEFAULT_STATE;
mUncheckedConsumer = InputConsumer.NO_OP;
}
// Save the current gesture state
mGestureState = newGestureState;
} else {
// Other events
if (mUncheckedConsumer != InputConsumer.NO_OP) {
// Only transform the event if we are handling it in a proper consumer
mDeviceState.setOrientationTransformIfNeeded(event);
}
}
ActiveGestureLog.INSTANCE.addLog("onMotionEvent", event.getActionMasked());
@@ -481,6 +489,14 @@ public class TouchInteractionService extends Service implements PluginListener<O
TraceHelper.INSTANCE.endFlagsOverride(traceToken);
}
private GestureState createGestureState() {
GestureState gestureState = new GestureState(mOverviewComponentObserver,
ActiveGestureLog.INSTANCE.generateAndSetLogId());
gestureState.updateRunningTask(TraceHelper.whitelistIpcs("getRunningTask.0",
() -> mAM.getRunningTask(true /* filterOnlyVisibleRecents */)));
return gestureState;
}
private InputConsumer newConsumer(GestureState previousGestureState,
GestureState newGestureState, MotionEvent event) {
boolean canStartSystemGesture = mDeviceState.canStartSystemGesture();
@@ -41,7 +41,7 @@ public abstract class DelegateInputConsumer implements InputConsumer {
protected void setActive(MotionEvent ev) {
mState = STATE_ACTIVE;
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "pilferPointers");
TestLogging.recordEvent(TestProtocol.SEQUENCE_PILFER, "pilferPointers");
mInputMonitor.pilferPointers();
// Send cancel event
@@ -204,7 +204,7 @@ public class DeviceLockedInputConsumer implements InputConsumer,
private void startRecentsTransition() {
mThresholdCrossed = true;
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "pilferPointers");
TestLogging.recordEvent(TestProtocol.SEQUENCE_PILFER, "pilferPointers");
mInputMonitorCompat.pilferPointers();
Intent intent = new Intent(Intent.ACTION_MAIN)
@@ -314,7 +314,7 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
if (mInteractionHandler == null) {
return;
}
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "pilferPointers");
TestLogging.recordEvent(TestProtocol.SEQUENCE_PILFER, "pilferPointers");
mInputMonitorCompat.pilferPointers();
mActivityInterface.closeOverlay();
@@ -108,7 +108,7 @@ public class OverviewInputConsumer<T extends BaseDraggingActivity>
ActiveGestureLog.INSTANCE.addLog("startQuickstep");
}
if (mInputMonitor != null) {
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "pilferPointers");
TestLogging.recordEvent(TestProtocol.SEQUENCE_PILFER, "pilferPointers");
mInputMonitor.pilferPointers();
}
}
@@ -65,7 +65,7 @@ public class OverviewWithoutFocusInputConsumer implements InputConsumer {
private void onInterceptTouch() {
if (mInputMonitor != null) {
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "pilferPointers");
TestLogging.recordEvent(TestProtocol.SEQUENCE_PILFER, "pilferPointers");
mInputMonitor.pilferPointers();
}
}
@@ -19,7 +19,7 @@ import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.ALL_APPS_HEADER_EXTRA;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.LauncherState.RECENTS_CLEAR_ALL_BUTTON;
import static com.android.launcher3.LauncherState.OVERVIEW_BUTTONS;
import static com.android.launcher3.LauncherState.SPRING_LOADED;
import static com.android.launcher3.QuickstepAppTransitionManagerImpl.ALL_APPS_PROGRESS_OFF_SCREEN;
import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
@@ -37,9 +37,9 @@ import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Hotseat;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager.StateListener;
import com.android.launcher3.R;
@@ -47,8 +47,8 @@ import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.appprediction.PredictionUiStateManager;
import com.android.launcher3.appprediction.PredictionUiStateManager.Client;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.states.RotationHelper;
import com.android.launcher3.uioverrides.DepthController;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.views.ScrimView;
@@ -63,7 +63,8 @@ import com.android.systemui.plugins.RecentsExtraCard;
* {@link RecentsView} used in Launcher activity
*/
@TargetApi(Build.VERSION_CODES.O)
public class LauncherRecentsView extends RecentsView<Launcher> implements StateListener {
public class LauncherRecentsView extends RecentsView<BaseQuickstepLauncher>
implements StateListener {
private static final Rect sTempRect = new Rect();
@@ -322,7 +323,7 @@ public class LauncherRecentsView extends RecentsView<Launcher> implements StateL
if (enabled) {
LauncherState state = mActivity.getStateManager().getState();
boolean hasClearAllButton = (state.getVisibleElements(mActivity)
& RECENTS_CLEAR_ALL_BUTTON) != 0;
& OVERVIEW_BUTTONS) != 0;
setDisallowScrollToClearAll(!hasClearAllButton);
}
}
@@ -0,0 +1,79 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.quickstep.views;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.FrameLayout;
import androidx.annotation.Nullable;
import com.android.launcher3.R;
/**
* View for showing action buttons in Overview
*/
public class OverviewActionsView extends FrameLayout {
private final View mScreenshotButton;
private final View mShareButton;
/**
* Listener for taps on the various actions.
*/
public interface Listener {
/** User has initiated the share actions. */
void onShare();
/** User has initiated the screenshot action. */
void onScreenshot();
}
public OverviewActionsView(Context context) {
this(context, null);
}
public OverviewActionsView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public OverviewActionsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public OverviewActionsView(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
LayoutInflater.from(context).inflate(R.layout.overview_actions, this, true);
mShareButton = findViewById(R.id.action_share);
mScreenshotButton = findViewById(R.id.action_screenshot);
}
/**
* Set listener for callbacks on action button taps.
*
* @param listener for callbacks, or {@code null} to clear the listener.
*/
public void setListener(@Nullable OverviewActionsView.Listener listener) {
mShareButton.setOnClickListener(
listener == null ? null : view -> listener.onShare());
mScreenshotButton.setOnClickListener(
listener == null ? null : view -> listener.onScreenshot());
}
}
@@ -30,7 +30,9 @@ import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.config.FeatureFlags.ENABLE_OVERVIEW_ACTIONS;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.uioverrides.DepthController.DEPTH;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.TASK_DISMISS_SWIPE_UP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.TASK_LAUNCH_SWIPE_DOWN;
import static com.android.launcher3.statehandlers.DepthController.DEPTH;
import static com.android.launcher3.uioverrides.touchcontrollers.TaskViewTouchController.SUCCESS_TRANSITION_PROGRESS;
import static com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch.TAP;
import static com.android.launcher3.userevent.nano.LauncherLogProto.ControlType.CLEAR_ALL_BUTTON;
@@ -96,9 +98,9 @@ import com.android.launcher3.anim.SpringProperty;
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.graphics.RotationMode;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.states.RotationHelper;
import com.android.launcher3.touch.PagedOrientationHandler.CurveProperties;
import com.android.launcher3.uioverrides.DepthController;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
@@ -1183,13 +1185,13 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
verticalFactor * secondaryTaskDimension).setDuration(duration), LINEAR, sp);
}
private void removeTask(Task task, int index, EndState endState) {
if (task != null) {
ActivityManagerWrapper.getInstance().removeTask(task.key.id);
ComponentKey componentKey = TaskUtils.getLaunchComponentKeyForTask(task.key);
private void removeTask(TaskView taskView, int index, EndState endState) {
if (taskView.getTask() != null) {
ActivityManagerWrapper.getInstance().removeTask(taskView.getTask().key.id);
ComponentKey compKey = TaskUtils.getLaunchComponentKeyForTask(taskView.getTask().key);
mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
endState.logAction, Direction.UP, index, componentKey);
mActivity.getStatsLogManager().logTaskDismiss(this, componentKey);
endState.logAction, Direction.UP, index, compKey);
mActivity.getStatsLogManager().log(TASK_DISMISS_SWIPE_UP, taskView.buildProto());
}
}
@@ -1284,7 +1286,7 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
private void onEnd(EndState endState) {
if (endState.isSuccess) {
if (shouldRemoveTask) {
removeTask(taskView.getTask(), draggedIndex, endState);
removeTask(taskView, draggedIndex, endState);
}
int pageToSnapTo = mCurrentPage;
@@ -1733,6 +1735,8 @@ public abstract class RecentsView<T extends BaseActivity> extends PagedView impl
mActivity.getUserEventDispatcher().logTaskLaunchOrDismiss(
endState.logAction, Direction.DOWN, indexOfChild(tv),
TaskUtils.getLaunchComponentKeyForTask(task.key));
mActivity.getStatsLogManager().log(TASK_LAUNCH_SWIPE_DOWN, tv.buildProto()
);
}
} else {
onTaskLaunched(false);
@@ -29,6 +29,7 @@ import static com.android.launcher3.anim.Interpolators.FAST_OUT_SLOW_IN;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.TASK_LAUNCH_TAP;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
@@ -43,6 +44,7 @@ import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Process;
import android.util.AttributeSet;
import android.util.FloatProperty;
import android.util.Log;
@@ -59,6 +61,7 @@ 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.popup.SystemShortcut;
import com.android.launcher3.states.RotationHelper;
@@ -68,6 +71,7 @@ import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.ViewPool.Reusable;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.TaskIconCache;
@@ -217,8 +221,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().logTaskLaunch(getRecentsView(),
TaskUtils.getLaunchComponentKeyForTask(getTask().key));
mActivity.getStatsLogManager().log(TASK_LAUNCH_TAP, buildProto());
});
mCornerRadius = TaskCornerRadius.get(context);
mWindowCornerRadius = QuickStepContract.getWindowCornerRadius(context.getResources());
@@ -229,6 +232,17 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
setOutlineProvider(mOutlineProvider);
}
/* Builds proto for logging */
protected LauncherAtom.ItemInfo buildProto() {
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();
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
@@ -812,8 +826,8 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
super.onInitializeAccessibilityNodeInfo(info);
info.addAction(
new AccessibilityNodeInfo.AccessibilityAction(R.string.accessibility_close_task,
getContext().getText(R.string.accessibility_close_task)));
new AccessibilityNodeInfo.AccessibilityAction(R.string.accessibility_close,
getContext().getText(R.string.accessibility_close)));
final Context context = getContext();
for (SystemShortcut s : TaskOverlayFactory.getEnabledShortcuts(this)) {
@@ -837,7 +851,7 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
@Override
public boolean performAccessibilityAction(int action, Bundle arguments) {
if (action == R.string.accessibility_close_task) {
if (action == R.string.accessibility_close) {
getRecentsView().dismissTask(this, true /*animateTaskView*/,
true /*removeTask*/);
return true;
+23
View File
@@ -0,0 +1,23 @@
<!-- 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M17,1.01L7,1c-1.1,0 -2,0.9 -2,2v18c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2L19,3c0,-1.1 -0.9,-1.99 -2,-1.99zM17,21L7,21v-1h10v1zM17,18L7,18L7,6h10v12zM17,4L7,4L7,3h10v1zM9.5,8.5L12,8.5L12,7L8,7v4h1.5zM12,17h4v-4h-1.5v2.5L12,15.5z"/>
</vector>
+23
View File
@@ -0,0 +1,23 @@
<!-- 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.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="#FF000000"
android:pathData="M18,16c-0.79,0 -1.5,0.31 -2.03,0.81L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.53,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.48 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.05,4.12c-0.05,0.22 -0.09,0.45 -0.09,0.69 0,1.66 1.34,3 3,3s3,-1.34 3,-3 -1.34,-3 -3,-3zM18,4c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM6,13c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1zM18,20c-0.55,0 -1,-0.45 -1,-1s0.45,-1 1,-1 1,0.45 1,1 -0.45,1 -1,1z"/>
</vector>
+41
View File
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:id="@+id/action_buttons"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:orientation="horizontal">
<Space
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="1" >
</Space>
<Button
android:id="@+id/action_screenshot"
style="@style/OverviewActionButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableTop="@drawable/ic_screenshot"
android:text="@string/action_screenshot" />
<Space
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="1" >
</Space>
<Button
android:id="@+id/action_share"
style="@style/OverviewActionButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableTop="@drawable/ic_share"
android:text="@string/action_share" />
<Space
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="1" >
</Space>
</LinearLayout>
</merge>
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
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.
-->
<com.android.quickstep.views.OverviewActionsView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="gone">
</com.android.quickstep.views.OverviewActionsView>
+1 -1
View File
@@ -23,7 +23,7 @@
<dimen name="task_corner_radius_small">2dp</dimen>
<!-- Overrideable in overlay that provides the Overview Actions. -->
<dimen name="overview_actions_height">0dp</dimen>
<dimen name="overview_actions_height">110dp</dimen>
<dimen name="recents_page_spacing">10dp</dimen>
<dimen name="recents_clear_all_deadzone_vertical_margin">70dp</dimen>
+11 -9
View File
@@ -32,9 +32,6 @@
<!-- Recents: The empty recents string. [CHAR LIMIT=NONE] -->
<string name="recents_empty_message">No recent items</string>
<!-- Content description for the recent apps's accessibility option that closes it. [CHAR LIMIT=NONE] -->
<string name="accessibility_close_task">Close</string>
<!-- Content description for the recent apps's accessibility option that opens its usage settings. [CHAR LIMIT=NONE] -->
<string name="accessibility_app_usage_settings">App usage settings</string>
@@ -83,16 +80,15 @@
<!-- Button text to dismiss opt in for fully predicted hotseat -->
<string name="hotseat_edu_dismiss">No thanks</string>
<!-- action shown to turn of predictions after onboarding -->
<!-- action shown to turn off predictions after onboarding -->
<string name="hotseat_turn_off">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>
<!-- tip shown if user declines migration and has some open spots for prediction -->
<string name="hotseat_tip_no_empty_slots">Drag apps off the bottom row to get app suggestions</string>
<!-- tip shown if user declines migration and has no open spots for prediction -->
<string name="hotseat_tip_gaps_filled">App suggestions added to empty space.</string>
<string name="hotseat_tip_no_empty_slots">Drag apps off the bottom row to get app suggestions</string>
<!-- 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>
<!-- Title shown during interactive part of Back gesture tutorial for right edge. [CHAR LIMIT=30] -->
<string name="back_gesture_tutorial_playground_title_swipe_inward_right_edge" translatable="false">Try the back gesture</string>
@@ -113,4 +109,10 @@
<string name="back_gesture_tutorial_action_button_label" translatable="false">Done</string>
<!-- Button text shown on a text button on the confirm screen. [CHAR LIMIT=14] -->
<string name="back_gesture_tutorial_action_text_button_label" translatable="false">Settings</string>
</resources>
<!-- ******* Overview ******* -->
<!-- Label for a button that causes the current overview app to be shared. [CHAR_LIMIT=40] -->
<string name="action_share">Share</string>
<!-- Label for a button that causes a screen shot of the current app to be taken. [CHAR_LIMIT=40] -->
<string name="action_screenshot">Screenshot</string>
</resources>
+9
View File
@@ -60,4 +60,13 @@
parent="TextAppearance.BackGestureTutorial.ButtonLabel">
<item name="android:textColor">@color/back_gesture_tutorial_primary_color</item>
</style>
<style name="OverviewActionButton"
parent="@android:style/Widget.DeviceDefault.Button.Borderless">
<item name="android:textColor">?attr/workspaceTextColor</item>
<item name="android:drawableTint">?attr/workspaceTextColor</item>
<item name="android:tint">?attr/workspaceTextColor</item>
<item name="android:drawablePadding">4dp</item>
<item name="android:textAllCaps">false</item>
</style>
</resources>
@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<cache-path name="shared_images" path="/" />
<files-path name="log_files" path="/" />
</paths>
@@ -34,6 +34,7 @@ import android.content.Intent;
import android.content.IntentSender;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.view.View;
import com.android.launcher3.LauncherState.ScaleAndTranslation;
import com.android.launcher3.LauncherStateManager.StateHandler;
@@ -43,8 +44,9 @@ import com.android.launcher3.model.WellbeingModel;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.proxy.ProxyActivityStarter;
import com.android.launcher3.proxy.StartActivityParams;
import com.android.launcher3.statehandlers.BackButtonAlphaHandler;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.uioverrides.BackButtonAlphaHandler;
import com.android.launcher3.uioverrides.RecentsViewStateController;
import com.android.launcher3.util.UiThreadHelper;
import com.android.quickstep.RecentsModel;
@@ -67,6 +69,7 @@ import java.util.stream.Stream;
public abstract class BaseQuickstepLauncher extends Launcher
implements NavigationModeChangeListener {
private DepthController mDepthController = new DepthController(this);
protected SystemActions mSystemActions;
/**
@@ -78,6 +81,8 @@ public abstract class BaseQuickstepLauncher extends Launcher
private final ShelfPeekAnim mShelfPeekAnim = new ShelfPeekAnim(this);
private View mActionsView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
@@ -222,16 +227,22 @@ public abstract class BaseQuickstepLauncher extends Launcher
@Override
protected void setupViews() {
super.setupViews();
mActionsView = findViewById(R.id.overview_actions_view);
if (FeatureFlags.ENABLE_OVERVIEW_ACTIONS.get() && removeShelfFromOverview(this)) {
// Overview is above all other launcher elements, including qsb, so move it to the top.
getOverviewPanel().bringToFront();
if (getActionsView() != null) {
getActionsView().bringToFront();
if (mActionsView != null) {
mActionsView.bringToFront();
}
}
}
public View getActionsView() {
return mActionsView;
}
@Override
protected void closeOpenViews(boolean animate) {
super.closeOpenViews(animate);
@@ -249,6 +260,10 @@ public abstract class BaseQuickstepLauncher extends Launcher
new BackButtonAlphaHandler(this)};
}
public DepthController getDepthController() {
return mDepthController;
}
@Override
protected ScaleAndTranslation getOverviewScaleAndTranslationForNormalState() {
if (SysUINavigationMode.getMode(this) == Mode.NO_BUTTON) {
@@ -294,6 +309,10 @@ public abstract class BaseQuickstepLauncher extends Launcher
onLauncherStateOrFocusChanged();
}
if ((changeBits & ACTIVITY_STATE_STARTED) != 0) {
mDepthController.setActivityStarted(isStarted());
}
super.onActivityFlagsChanged(changeBits);
}
@@ -33,7 +33,7 @@ import static com.android.launcher3.anim.Interpolators.EXAGGERATED_EASE;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.config.FeatureFlags.KEYGUARD_ANIMATION;
import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_TRANSITIONS;
import static com.android.launcher3.uioverrides.DepthController.DEPTH;
import static com.android.launcher3.statehandlers.DepthController.DEPTH;
import static com.android.launcher3.views.FloatingIconView.SHAPE_PROGRESS_DURATION;
import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
import static com.android.systemui.shared.system.QuickStepContract.getWindowCornerRadius;
@@ -72,7 +72,7 @@ import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.launcher3.uioverrides.DepthController;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.util.DynamicResource;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
@@ -622,7 +622,7 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
backgroundRadiusAnim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
depthController.setSurfaceToLauncher(mLauncher.getDragLayer());
depthController.setSurfaceToApp(null);
}
});
}
@@ -14,7 +14,7 @@
* limitations under the License.
*/
package com.android.launcher3.uioverrides;
package com.android.launcher3.statehandlers;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.quickstep.AnimatedFloat.VALUE;
@@ -29,6 +29,9 @@ import com.android.quickstep.AnimatedFloat;
import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.SystemUiProxy;
/**
* State handler for animating back button alpha
*/
public class BackButtonAlphaHandler implements LauncherStateManager.StateHandler {
private final BaseQuickstepLauncher mLauncher;
@@ -14,13 +14,14 @@
* limitations under the License.
*/
package com.android.launcher3.uioverrides;
package com.android.launcher3.statehandlers;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import android.os.IBinder;
import android.util.FloatProperty;
import android.view.View;
import android.view.ViewTreeObserver;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
@@ -77,6 +78,16 @@ public class DepthController implements LauncherStateManager.StateHandler {
}
}
private final ViewTreeObserver.OnDrawListener mOnDrawListener =
new ViewTreeObserver.OnDrawListener() {
@Override
public void onDraw() {
View view = mLauncher.getDragLayer();
setSurface(new SurfaceControlCompat(view));
view.post(() -> view.getViewTreeObserver().removeOnDrawListener(this));
}
};
private final Launcher mLauncher;
/**
* Blur radius when completely zoomed out, in pixels.
@@ -102,22 +113,29 @@ public class DepthController implements LauncherStateManager.StateHandler {
mWallpaperManager = new WallpaperManagerCompat(mLauncher);
}
/**
* Sets if the underlying activity is started or not
*/
public void setActivityStarted(boolean isStarted) {
if (isStarted) {
mLauncher.getDragLayer().getViewTreeObserver().addOnDrawListener(mOnDrawListener);
} else {
mLauncher.getDragLayer().getViewTreeObserver().removeOnDrawListener(mOnDrawListener);
setSurface(null);
}
}
/**
* Sets the specified app target surface to apply the blur to.
*/
public void setSurfaceToApp(RemoteAnimationTargetCompat target) {
if (target != null) {
setSurface(target.leash);
} else {
setActivityStarted(mLauncher.isStarted());
}
}
/**
* Sets the surface to apply the blur to as the launcher surface.
*/
public void setSurfaceToLauncher(View v) {
setSurface(v != null ? new SurfaceControlCompat(v) : null);
}
private void setSurface(SurfaceControlCompat surface) {
if (mSurface != surface) {
mSurface = surface;
@@ -17,7 +17,6 @@
package com.android.launcher3.uioverrides;
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_X;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE_IN_OUT;
@@ -34,11 +33,10 @@ import static com.android.launcher3.states.StateAnimationConfig.SKIP_OVERVIEW;
import android.util.FloatProperty;
import android.view.View;
import android.view.animation.Interpolator;
import androidx.annotation.NonNull;
import com.android.launcher3.Launcher;
import com.android.launcher3.BaseQuickstepLauncher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherState.ScaleAndTranslation;
import com.android.launcher3.LauncherStateManager.StateHandler;
@@ -55,13 +53,11 @@ import com.android.launcher3.states.StateAnimationConfig;
public abstract class BaseRecentsViewStateController<T extends View>
implements StateHandler {
protected final T mRecentsView;
protected final Launcher mLauncher;
protected final View mActionsView;
protected final BaseQuickstepLauncher mLauncher;
public BaseRecentsViewStateController(@NonNull Launcher launcher) {
public BaseRecentsViewStateController(@NonNull BaseQuickstepLauncher launcher) {
mLauncher = launcher;
mRecentsView = launcher.getOverviewPanel();
mActionsView = launcher.getActionsView();
}
@Override
@@ -69,19 +65,12 @@ public abstract class BaseRecentsViewStateController<T extends View>
ScaleAndTranslation scaleAndTranslation = state
.getOverviewScaleAndTranslation(mLauncher);
SCALE_PROPERTY.set(mRecentsView, scaleAndTranslation.scale);
float translationX = scaleAndTranslation.translationX;
if (mRecentsView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
translationX = -translationX;
}
mRecentsView.setTranslationX(translationX);
mRecentsView.setTranslationX(scaleAndTranslation.translationX);
mRecentsView.setTranslationY(scaleAndTranslation.translationY);
getContentAlphaProperty().set(mRecentsView, state.overviewUi ? 1f : 0);
OverviewScrim scrim = mLauncher.getDragLayer().getOverviewScrim();
SCRIM_PROGRESS.set(scrim, state.getOverviewScrimAlpha(mLauncher));
if (mActionsView != null) {
mActionsView.setTranslationX(translationX);
mActionsView.setAlpha(state.overviewUi ? 1f : 0);
}
}
@Override
@@ -107,29 +96,18 @@ public abstract class BaseRecentsViewStateController<T extends View>
void setStateWithAnimationInternal(@NonNull final LauncherState toState,
@NonNull StateAnimationConfig config, @NonNull PendingAnimation setter) {
ScaleAndTranslation scaleAndTranslation = toState.getOverviewScaleAndTranslation(mLauncher);
Interpolator scaleInterpolator = config.getInterpolator(ANIM_OVERVIEW_SCALE, LINEAR);
setter.setFloat(mRecentsView, SCALE_PROPERTY, scaleAndTranslation.scale, scaleInterpolator);
Interpolator translateXInterpolator = config.getInterpolator(
ANIM_OVERVIEW_TRANSLATE_X, LINEAR);
Interpolator translateYInterpolator = config.getInterpolator(
ANIM_OVERVIEW_TRANSLATE_Y, LINEAR);
float translationX = scaleAndTranslation.translationX;
if (mRecentsView.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
translationX = -translationX;
}
setter.setFloat(mRecentsView, VIEW_TRANSLATE_X, translationX, translateXInterpolator);
setter.setFloat(mRecentsView, SCALE_PROPERTY, scaleAndTranslation.scale,
config.getInterpolator(ANIM_OVERVIEW_SCALE, LINEAR));
setter.setFloat(mRecentsView, VIEW_TRANSLATE_X, scaleAndTranslation.translationX,
config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_X, LINEAR));
setter.setFloat(mRecentsView, VIEW_TRANSLATE_Y, scaleAndTranslation.translationY,
translateYInterpolator);
config.getInterpolator(ANIM_OVERVIEW_TRANSLATE_Y, LINEAR));
setter.setFloat(mRecentsView, getContentAlphaProperty(), toState.overviewUi ? 1 : 0,
config.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT));
OverviewScrim scrim = mLauncher.getDragLayer().getOverviewScrim();
setter.setFloat(scrim, SCRIM_PROGRESS, toState.getOverviewScrimAlpha(mLauncher),
config.getInterpolator(ANIM_OVERVIEW_SCRIM_FADE, LINEAR));
if (mActionsView != null) {
setter.setFloat(mActionsView, VIEW_TRANSLATE_X, translationX, translateXInterpolator);
setter.setFloat(mActionsView, VIEW_ALPHA, toState.overviewUi ? 1 : 0,
config.getInterpolator(ANIM_OVERVIEW_FADE, AGGRESSIVE_EASE_IN_OUT));
}
}
/**
@@ -54,6 +54,9 @@ public class DeviceFlag extends DebugFlag {
@Override
public void addChangeListener(Context context, Runnable r) {
if (mListeners == null) {
initialize(context);
}
mListeners.add(r);
}
@@ -32,8 +32,8 @@ import androidx.annotation.UiThread;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.touch.PagedOrientationHandler;
import com.android.launcher3.uioverrides.DepthController;
import com.android.quickstep.util.ActivityInitListener;
import com.android.quickstep.util.ShelfPeekAnim;
import com.android.systemui.shared.recents.model.ThumbnailData;
@@ -64,6 +64,8 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
private static final String TAG = "GestureState";
private static final ArrayList<String> STATE_NAMES = new ArrayList<>();
public static final GestureState DEFAULT_STATE = new GestureState();
private static int FLAG_COUNT = 0;
private static int getFlagForIndex(String name) {
if (DEBUG_STATES) {
@@ -103,6 +105,10 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
public static final int STATE_RECENTS_ANIMATION_ENDED =
getFlagForIndex("STATE_RECENTS_ANIMATION_ENDED");
// Called when RecentsView stops scrolling and settles on a TaskView.
public static final int STATE_RECENTS_SCROLLING_FINISHED =
getFlagForIndex("STATE_RECENTS_SCROLLING_FINISHED");
// Needed to interact with the current activity
private final Intent mHomeIntent;
@@ -19,11 +19,13 @@ import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import android.graphics.Rect;
import android.util.ArraySet;
import android.util.Log;
import androidx.annotation.BinderThread;
import androidx.annotation.UiThread;
import com.android.launcher3.Utilities;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.util.Preconditions;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
@@ -89,6 +91,9 @@ public class RecentsAnimationCallbacks implements
RemoteAnimationTargetCompat[] appTargets,
RemoteAnimationTargetCompat[] wallpaperTargets,
Rect homeContentInsets, Rect minimizedHomeBounds) {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NO_START_FROM_RECENTS, "onAnimationStart");
}
RecentsAnimationTargets targets = new RecentsAnimationTargets(appTargets,
wallpaperTargets, homeContentInsets, minimizedHomeBounds);
mController = new RecentsAnimationController(animationController,
@@ -22,6 +22,7 @@ import static com.android.quickstep.SysUINavigationMode.Mode.THREE_BUTTONS;
import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_CLICKABLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_A11Y_BUTTON_LONG_CLICKABLE;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_BUBBLES_EXPANDED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_HOME_DISABLED;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NAV_BAR_HIDDEN;
import static com.android.systemui.shared.system.QuickStepContract.SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED;
@@ -356,6 +357,7 @@ public class RecentsAnimationDeviceState implements
return (mSystemUiStateFlags & SYSUI_STATE_NAV_BAR_HIDDEN) == 0
&& (mSystemUiStateFlags & SYSUI_STATE_NOTIFICATION_PANEL_EXPANDED) == 0
&& (mSystemUiStateFlags & SYSUI_STATE_QUICK_SETTINGS_EXPANDED) == 0
&& (mSystemUiStateFlags & SYSUI_STATE_BUBBLES_EXPANDED) == 0
&& ((mSystemUiStateFlags & SYSUI_STATE_HOME_DISABLED) == 0
|| (mSystemUiStateFlags & SYSUI_STATE_OVERVIEW_DISABLED) == 0);
}
@@ -26,6 +26,7 @@ import androidx.annotation.UiThread;
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.testing.TestProtocol;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
@@ -52,6 +53,9 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
@UiThread
public RecentsAnimationCallbacks startRecentsAnimation(GestureState gestureState,
Intent intent, RecentsAnimationCallbacks.RecentsAnimationListener listener) {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NO_START_FROM_RECENTS, "startRecentsAnimation");
}
// Notify if recents animation is still running
if (mController != null) {
String msg = "New recents animation started before old animation completed";
@@ -18,36 +18,22 @@ package com.android.quickstep.logging;
import static android.stats.launcher.nano.Launcher.ALLAPPS;
import static android.stats.launcher.nano.Launcher.BACKGROUND;
import static android.stats.launcher.nano.Launcher.DISMISS_TASK;
import static android.stats.launcher.nano.Launcher.HOME;
import static android.stats.launcher.nano.Launcher.LAUNCH_APP;
import static android.stats.launcher.nano.Launcher.LAUNCH_TASK;
import static android.stats.launcher.nano.Launcher.OVERVIEW;
import static com.android.launcher3.logging.UserEventDispatcher.makeTargetsList;
import android.content.Context;
import android.content.Intent;
import android.os.UserHandle;
import android.stats.launcher.nano.Launcher;
import android.stats.launcher.nano.LauncherExtension;
import android.stats.launcher.nano.LauncherTarget;
import android.util.Log;
import android.view.View;
import androidx.annotation.Nullable;
import com.android.launcher3.FolderInfo;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.logging.StatsLogUtils;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.userevent.nano.LauncherLogProto.ControlType;
import com.android.launcher3.userevent.nano.LauncherLogProto.ItemType;
import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
import com.android.launcher3.util.ComponentKey;
import com.android.systemui.shared.system.SysUiStatsLog;
import com.google.protobuf.nano.MessageNano;
import com.android.launcher3.model.AllAppsList;
import com.android.launcher3.model.BaseModelUpdateTask;
import com.android.launcher3.model.BgDataModel;
import com.android.launcher3.util.IntSparseArrayMap;
import java.util.ArrayList;
@@ -62,186 +48,17 @@ import java.util.ArrayList;
public class StatsLogCompatManager extends StatsLogManager {
private static final int SUPPORTED_TARGET_DEPTH = 2;
private static final String TAG = "StatsLogCompatManager";
private static final String TAG = "StatsLog";
private static final boolean DEBUG = false;
private static Context sContext;
public StatsLogCompatManager(Context context) {
sContext = context;
}
@Override
public void logAppLaunch(View v, Intent intent, @Nullable UserHandle userHandle) {
LauncherExtension ext = new LauncherExtension();
ext.srcTarget = new LauncherTarget[SUPPORTED_TARGET_DEPTH];
int srcState = mStateProvider.getCurrentState();
fillInLauncherExtension(v, ext);
if (ext.srcTarget[0] != null) {
ext.srcTarget[0].item = LauncherTarget.APP_ICON;
}
SysUiStatsLog.write(SysUiStatsLog.LAUNCHER_EVENT, LAUNCH_APP, srcState,
BACKGROUND /* dstState */, MessageNano.toByteArray(ext), true);
}
@Override
public void logTaskLaunch(View v, ComponentKey componentKey) {
LauncherExtension ext = new LauncherExtension();
ext.srcTarget = new LauncherTarget[SUPPORTED_TARGET_DEPTH];
int srcState = OVERVIEW;
fillInLauncherExtension(v, ext);
SysUiStatsLog.write(SysUiStatsLog.LAUNCHER_EVENT, LAUNCH_TASK, srcState,
BACKGROUND /* dstState */, MessageNano.toByteArray(ext), true);
}
@Override
public void logTaskDismiss(View v, ComponentKey componentKey) {
LauncherExtension ext = new LauncherExtension();
ext.srcTarget = new LauncherTarget[SUPPORTED_TARGET_DEPTH];
int srcState = OVERVIEW;
fillInLauncherExtension(v, ext);
SysUiStatsLog.write(SysUiStatsLog.LAUNCHER_EVENT, DISMISS_TASK, srcState,
BACKGROUND /* dstState */, MessageNano.toByteArray(ext), true);
}
@Override
public void logSwipeOnContainer(boolean isSwipingToLeft, int pageId) {
LauncherExtension ext = new LauncherExtension();
ext.srcTarget = new LauncherTarget[1];
int srcState = mStateProvider.getCurrentState();
fillInLauncherExtensionWithPageId(ext, pageId);
int launcherAction = isSwipingToLeft ? Launcher.SWIPE_LEFT : Launcher.SWIPE_RIGHT;
SysUiStatsLog.write(SysUiStatsLog.LAUNCHER_EVENT, launcherAction, srcState, srcState,
MessageNano.toByteArray(ext), true);
}
public static boolean fillInLauncherExtension(View v, LauncherExtension extension) {
if (DEBUG) {
Log.d(TAG, "fillInLauncherExtension");
}
StatsLogUtils.LogContainerProvider provider = StatsLogUtils.getLaunchProviderRecursive(v);
if (v == null || !(v.getTag() instanceof ItemInfo) || provider == null) {
if (DEBUG) {
Log.d(TAG, "View or provider is null, or view doesn't have an ItemInfo tag.");
}
return false;
}
Target child = new Target();
ArrayList<Target> targets = makeTargetsList(child);
targets.add(child);
provider.fillInLogContainerData((ItemInfo) v.getTag(), child, targets);
int maxDepth = Math.min(SUPPORTED_TARGET_DEPTH, targets.size());
extension.srcTarget = new LauncherTarget[maxDepth];
for (int i = 0; i < maxDepth; i++) {
extension.srcTarget[i] = new LauncherTarget();
copy(targets.get(i), extension.srcTarget[i]);
}
return true;
}
public static boolean fillInLauncherExtensionWithPageId(LauncherExtension ext, int pageId) {
if (DEBUG) {
Log.d(TAG, "fillInLauncherExtensionWithPageId, pageId = " + pageId);
}
Target target = new Target();
target.pageIndex = pageId;
ext.srcTarget[0] = new LauncherTarget();
copy(target, ext.srcTarget[0]);
return true;
}
private static void copy(Target src, LauncherTarget dst) {
if (DEBUG) {
Log.d(TAG, "copy target information from clearcut Target to LauncherTarget.");
}
// Fill in type
switch (src.type) {
case Target.Type.ITEM:
dst.type = LauncherTarget.ITEM_TYPE;
break;
case Target.Type.CONTROL:
dst.type = LauncherTarget.CONTROL_TYPE;
break;
case Target.Type.CONTAINER:
dst.type = LauncherTarget.CONTAINER_TYPE;
break;
default:
dst.type = LauncherTarget.NONE;
break;
}
// Fill in item
switch (src.itemType) {
case ItemType.APP_ICON:
dst.item = LauncherTarget.APP_ICON;
break;
case ItemType.SHORTCUT:
dst.item = LauncherTarget.SHORTCUT;
break;
case ItemType.WIDGET:
dst.item = LauncherTarget.WIDGET;
break;
case ItemType.FOLDER_ICON:
dst.item = LauncherTarget.FOLDER_ICON;
break;
case ItemType.DEEPSHORTCUT:
dst.item = LauncherTarget.DEEPSHORTCUT;
break;
case ItemType.SEARCHBOX:
dst.item = LauncherTarget.SEARCHBOX;
break;
case ItemType.EDITTEXT:
dst.item = LauncherTarget.EDITTEXT;
break;
case ItemType.NOTIFICATION:
dst.item = LauncherTarget.NOTIFICATION;
break;
case ItemType.TASK:
dst.item = LauncherTarget.TASK;
break;
default:
dst.item = LauncherTarget.DEFAULT_ITEM;
break;
}
// Fill in container
switch (src.containerType) {
case ContainerType.HOTSEAT:
dst.container = LauncherTarget.HOTSEAT;
break;
case ContainerType.FOLDER:
dst.container = LauncherTarget.FOLDER;
break;
case ContainerType.PREDICTION:
dst.container = LauncherTarget.PREDICTION;
break;
case ContainerType.SEARCHRESULT:
dst.container = LauncherTarget.SEARCHRESULT;
break;
default:
dst.container = LauncherTarget.DEFAULT_CONTAINER;
break;
}
// Fill in control
switch (src.controlType) {
case ControlType.UNINSTALL_TARGET:
dst.control = LauncherTarget.UNINSTALL;
break;
case ControlType.REMOVE_TARGET:
dst.control = LauncherTarget.REMOVE;
break;
default:
dst.control = LauncherTarget.DEFAULT_CONTROL;
break;
}
// Fill in other fields
dst.pageId = src.pageIndex;
dst.gridX = src.gridX;
dst.gridY = src.gridY;
public void log(LauncherEvent eventId, LauncherAtom.ItemInfo item) {
// Call StatsLog method
}
@Override
@@ -254,4 +71,36 @@ public class StatsLogCompatManager extends StatsLogManager {
"StatsLogUtil constants doesn't match enums in launcher.proto");
}
}
/**
* Logs the workspace layout information on the model thread.
*/
public void logSnapshot() {
LauncherAppState.getInstance(sContext).getModel().enqueueModelUpdateTask(
new SnapshotWorker());
}
private class SnapshotWorker extends BaseModelUpdateTask {
@Override
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
IntSparseArrayMap<FolderInfo> folders = dataModel.folders.clone();
ArrayList<ItemInfo> workspaceItems = (ArrayList) dataModel.workspaceItems.clone();
ArrayList<LauncherAppWidgetInfo> appWidgets = (ArrayList) dataModel.appWidgets.clone();
for (ItemInfo info : workspaceItems) {
LauncherAtom.ItemInfo atomInfo = info.buildProto(null, null);
// call StatsLog method
}
for (FolderInfo fInfo : folders) {
for (ItemInfo info : fInfo.contents) {
LauncherAtom.ItemInfo atomInfo = info.buildProto(null, fInfo);
// call StatsLog method
}
}
for (ItemInfo info : appWidgets) {
LauncherAtom.ItemInfo atomInfo = info.buildProto(null, null);
// call StatsLog method
}
}
}
}
@@ -0,0 +1,169 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.quickstep.util;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.content.Intent.FLAG_GRANT_READ_URI_PERMISSION;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Insets;
import android.graphics.Picture;
import android.graphics.Rect;
import android.net.Uri;
import android.util.Log;
import androidx.annotation.UiThread;
import androidx.annotation.WorkerThread;
import androidx.core.content.FileProvider;
import com.android.launcher3.BuildConfig;
import com.android.quickstep.SystemUiProxy;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.function.BiFunction;
import java.util.function.Supplier;
/**
* Utility class containing methods to help manage image actions such as sharing, cropping, and
* saving image.
*/
public class ImageActionUtils {
private static final String AUTHORITY = BuildConfig.APPLICATION_ID + ".overview.fileprovider";
/**
* Saves screenshot to location determine by SystemUiProxy
*/
public static void saveScreenshot(SystemUiProxy systemUiProxy, Bitmap screenshot,
Rect screenshotBounds,
Insets visibleInsets, int taskId) {
systemUiProxy.handleImageAsScreenshot(screenshot, screenshotBounds, visibleInsets, taskId);
}
/**
* Launch the activity to share image.
*/
@UiThread
public static void startShareActivity(Context context, Supplier<Bitmap> bitmapSupplier,
Rect crop, Intent intent, String tag) {
if (bitmapSupplier.get() == null) {
Log.e(tag, "No snapshot available, not starting share.");
return;
}
UI_HELPER_EXECUTOR.execute(() -> persistBitmapAndStartActivity(context,
bitmapSupplier.get(), crop, intent, ImageActionUtils::getShareIntentForImageUri,
tag));
}
/**
* Starts activity based on given intent created from image uri.
*/
@WorkerThread
public static void persistBitmapAndStartActivity(Context context, Bitmap bitmap, Rect crop,
Intent intent, BiFunction<Uri, Intent, Intent[]> uriToIntentMap, String tag) {
context.startActivities(
uriToIntentMap.apply(getImageUri(bitmap, crop, context, tag), intent));
}
/**
* Converts image bitmap to Uri by temporarily saving bitmap to cache, and creating Uri pointing
* to that location. Used to be able to share an image with another app.
*
* @param bitmap The whole bitmap to be shared.
* @param crop The section of the bitmap to be shared.
* @param context The application context, used to interact with file system.
* @param tag Tag used to log errors.
* @return Uri that points to the cropped version of desired bitmap to share.
*/
@WorkerThread
public static Uri getImageUri(Bitmap bitmap, Rect crop, Context context, String tag) {
Bitmap croppedBitmap = cropBitmap(bitmap, crop);
int cropHash = crop == null ? 0 : crop.hashCode();
String baseName = "image_" + bitmap.hashCode() + "_" + cropHash + ".png";
File file = new File(context.getCacheDir(), baseName);
try (FileOutputStream fos = new FileOutputStream(file)) {
croppedBitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
} catch (IOException e) {
Log.e(tag, "Error saving image", e);
}
return FileProvider.getUriForFile(context, AUTHORITY, file);
}
/**
* Crops the bitmap to the provided size and returns a software backed bitmap whenever possible.
*
* @param bitmap The bitmap to be cropped.
* @param crop The section of the bitmap in the crop.
* @return The cropped bitmap.
*/
@WorkerThread
public static Bitmap cropBitmap(Bitmap bitmap, Rect crop) {
Rect src = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
if (crop == null) {
crop = new Rect(src);
}
if (crop.equals(src)) {
return bitmap;
} else {
if (bitmap.getConfig() != Bitmap.Config.HARDWARE) {
return Bitmap.createBitmap(bitmap, crop.left, crop.top, crop.width(),
crop.height());
}
// For hardware bitmaps, use the Picture API to directly create a software bitmap
Picture picture = new Picture();
Canvas canvas = picture.beginRecording(crop.width(), crop.height());
canvas.drawBitmap(bitmap, -crop.left, -crop.top, null);
picture.endRecording();
return Bitmap.createBitmap(picture, crop.width(), crop.height(),
Bitmap.Config.ARGB_8888);
}
}
/**
* Gets the intent used to share image.
*/
@WorkerThread
private static Intent[] getShareIntentForImageUri(Uri uri, Intent intent) {
if (intent == null) {
intent = new Intent();
}
ClipData clipdata = new ClipData(new ClipDescription("content",
new String[]{ClipDescription.MIMETYPE_TEXT_PLAIN}),
new ClipData.Item(uri));
intent.setAction(Intent.ACTION_SEND)
.setComponent(null)
.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
.setType("image/png")
.setFlags(FLAG_GRANT_READ_URI_PERMISSION)
.putExtra(Intent.EXTRA_STREAM, uri)
.setClipData(clipdata);
return new Intent[]{Intent.createChooser(intent, null).addFlags(FLAG_ACTIVITY_NEW_TASK)};
}
}
@@ -88,7 +88,6 @@ public class AppPredictionsUITests extends AbstractQuickStepTest {
*/
@Test
public void testPredictionExistsInAllApps() {
mDevice.pressHome();
mLauncher.pressHome().switchToAllApps();
// Dispatch an update
+19
View File
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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.
-->
<com.android.launcher3.graphics.ShadowDrawable
xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@drawable/ic_block_no_shadow"
android:elevation="@dimen/drop_target_shadow_elevation" />
@@ -50,7 +50,7 @@
android:src="@drawable/ic_remove_no_shadow"
android:tint="@android:color/white"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/accessibility_close_task"/>
android:contentDescription="@string/accessibility_close"/>
</LinearLayout>
<View
+1 -2
View File
@@ -34,5 +34,4 @@
android:singleLine="true"
android:textColor="?android:attr/textColorSecondary"
android:textColorHint="@drawable/all_apps_search_hint"
android:textSize="16sp"
android:translationY="24dp" />
android:textSize="16sp" />
+1 -1
View File
@@ -20,7 +20,7 @@
android:gravity="center">
<TextView
style="@style/TextHeadline"
style="@style/PrimaryMediumText"
android:textColor="?attr/workProfileOverlayTextColor"
android:id="@+id/work_apps_paused_title"
android:layout_width="wrap_content"
+4
View File
@@ -85,6 +85,10 @@
<dimen name="all_apps_tabs_side_padding">12dp</dimen>
<dimen name="all_apps_divider_height">1dp</dimen>
<dimen name="all_apps_tip_bottom_margin">8dp</dimen>
<!-- The size of corner radius of the arrow in the arrow toast. -->
<dimen name="arrow_toast_corner_radius">2dp</dimen>
<dimen name="all_apps_work_profile_tab_footer_padding">20dp</dimen>
<!-- Search bar in All Apps -->
+1
View File
@@ -17,4 +17,5 @@
<drawable name="ic_setup_shadow">@drawable/ic_setting</drawable>
<drawable name="ic_remove_shadow">@drawable/ic_remove_no_shadow</drawable>
<drawable name="ic_uninstall_shadow">@drawable/ic_uninstall_no_shadow</drawable>
<drawable name="ic_block_shadow">@drawable/ic_block_no_shadow</drawable>
</resources>
+3
View File
@@ -316,6 +316,9 @@
<!-- Accessibility action to dismiss a notification in the shortcuts menu for an icon. [CHAR_LIMIT=30] -->
<string name="action_dismiss_notification">Dismiss</string>
<!-- Content description for arrow tip close button. [CHAR LIMIT=NONE] -->
<string name="accessibility_close">Close</string>
<!-- Accessibility confirmation for notification being dismissed. -->
<string name="notification_dismissed">Notification dismissed</string>
@@ -16,6 +16,7 @@
package com.android.launcher3;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.APP_LAUNCH_TAP;
import static com.android.launcher3.util.DefaultDisplay.CHANGE_ROTATION;
import android.app.ActivityOptions;
@@ -181,7 +182,7 @@ public abstract class BaseDraggingActivity extends BaseActivity
sourceContainer);
}
getUserEventDispatcher().logAppLaunch(v, intent, user);
getStatsLogManager().logAppLaunch(v, intent, user);
getStatsLogManager().log(APP_LAUNCH_TAP, item.buildProto(null, null));
return true;
} catch (NullPointerException|ActivityNotFoundException|SecurityException e) {
Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
@@ -18,6 +18,7 @@ package com.android.launcher3;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
@@ -25,6 +26,7 @@ import android.view.ViewGroup;
import androidx.recyclerview.widget.RecyclerView;
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.views.RecyclerViewFastScroller;
@@ -177,6 +179,10 @@ public abstract class BaseRecyclerView extends RecyclerView {
public void onScrollStateChanged(int state) {
super.onScrollStateChanged(state);
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "onScrollStateChanged: " + state);
}
if (state == SCROLL_STATE_IDLE) {
AccessibilityManagerCompat.sendScrollFinishedEventToTest(getContext());
}
+8 -42
View File
@@ -39,7 +39,6 @@ import android.util.TypedValue;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewDebug;
import android.widget.TextView;
@@ -109,8 +108,6 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
private final int mDisplay;
private final CheckLongPressHelper mLongPressHelper;
private final StylusEventHelper mStylusEventHelper;
private final float mSlop;
private final boolean mLayoutHorizontal;
private final int mIconSize;
@@ -137,9 +134,6 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
@ViewDebug.ExportedProperty(category = "launcher")
private boolean mDisableRelayout = false;
@ViewDebug.ExportedProperty(category = "launcher")
private final boolean mIgnorePaddingTouch;
private IconLoadRequest mIconLoadRequest;
public BubbleTextView(Context context) {
@@ -153,7 +147,6 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
public BubbleTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mActivity = ActivityContext.lookupContext(context);
mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.BubbleTextView, defStyle, 0);
@@ -166,23 +159,19 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.iconTextSizePx);
setCompoundDrawablePadding(grid.iconDrawablePaddingPx);
defaultIconSize = grid.iconSizePx;
mIgnorePaddingTouch = true;
} else if (mDisplay == DISPLAY_ALL_APPS) {
DeviceProfile grid = mActivity.getDeviceProfile();
setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.allAppsIconTextSizePx);
setCompoundDrawablePadding(grid.allAppsIconDrawablePaddingPx);
defaultIconSize = grid.allAppsIconSizePx;
mIgnorePaddingTouch = true;
} else if (mDisplay == DISPLAY_FOLDER) {
DeviceProfile grid = mActivity.getDeviceProfile();
setTextSize(TypedValue.COMPLEX_UNIT_PX, grid.folderChildTextSizePx);
setCompoundDrawablePadding(grid.folderChildDrawablePaddingPx);
defaultIconSize = grid.folderChildIconSizePx;
mIgnorePaddingTouch = true;
} else {
// widget_selection or shortcut_popup
defaultIconSize = mActivity.getDeviceProfile().iconSizePx;
mIgnorePaddingTouch = false;
}
mCenterVertically = a.getBoolean(R.styleable.BubbleTextView_centerVertically, false);
@@ -192,7 +181,6 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
a.recycle();
mLongPressHelper = new CheckLongPressHelper(this);
mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
mDotParams = new DotRenderer.DrawParams();
@@ -333,42 +321,21 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
@Override
public boolean onTouchEvent(MotionEvent event) {
// ignore events if they happen in padding area
if (event.getAction() == MotionEvent.ACTION_DOWN && mIgnorePaddingTouch
if (event.getAction() == MotionEvent.ACTION_DOWN
&& (event.getY() < getPaddingTop()
|| event.getX() < getPaddingLeft()
|| event.getY() > getHeight() - getPaddingBottom()
|| event.getX() > getWidth() - getPaddingRight())) {
return false;
}
// Call the superclass onTouchEvent first, because sometimes it changes the state to
// isPressed() on an ACTION_UP
boolean result = super.onTouchEvent(event);
// Check for a stylus button press, if it occurs cancel any long press checks.
if (mStylusEventHelper.onMotionEvent(event)) {
mLongPressHelper.cancelLongPress();
result = true;
if (isLongClickable()) {
super.onTouchEvent(event);
mLongPressHelper.onTouchEvent(event);
// Keep receiving the rest of the events
return true;
} else {
return super.onTouchEvent(event);
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// If we're in a stylus button press, don't check for long press.
if (!mStylusEventHelper.inStylusButtonPressed()) {
mLongPressHelper.postCheckForLongPress();
}
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
mLongPressHelper.cancelLongPress();
break;
case MotionEvent.ACTION_MOVE:
if (!Utilities.pointInView(this, event.getX(), event.getY(), mSlop)) {
mLongPressHelper.cancelLongPress();
}
break;
}
return result;
}
void setStayPressed(boolean stayPressed) {
@@ -531,7 +498,6 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
@Override
public void cancelLongPress() {
super.cancelLongPress();
mLongPressHelper.cancelLongPress();
}
@@ -16,46 +16,68 @@
package com.android.launcher3;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import com.android.launcher3.util.Thunk;
/**
* Utility class to handle tripper long press on a view with custom timeout and stylus event
*/
public class CheckLongPressHelper {
public static final float DEFAULT_LONG_PRESS_TIMEOUT_FACTOR = 0.75f;
@Thunk View mView;
@Thunk View.OnLongClickListener mListener;
@Thunk boolean mHasPerformedLongPress;
private float mLongPressTimeoutFactor = DEFAULT_LONG_PRESS_TIMEOUT_FACTOR;
private CheckForLongPress mPendingCheckForLongPress;
private final View mView;
private final View.OnLongClickListener mListener;
private final float mSlop;
class CheckForLongPress implements Runnable {
public void run() {
if ((mView.getParent() != null) && mView.hasWindowFocus()
&& !mHasPerformedLongPress) {
boolean handled;
if (mListener != null) {
handled = mListener.onLongClick(mView);
} else {
handled = mView.performLongClick();
}
if (handled) {
mView.setPressed(false);
mHasPerformedLongPress = true;
}
}
}
}
private float mLongPressTimeoutFactor = DEFAULT_LONG_PRESS_TIMEOUT_FACTOR;
private boolean mHasPerformedLongPress;
private Runnable mPendingCheckForLongPress;
public CheckLongPressHelper(View v) {
mView = v;
this(v, null);
}
public CheckLongPressHelper(View v, View.OnLongClickListener listener) {
mView = v;
mListener = listener;
mSlop = ViewConfiguration.get(mView.getContext()).getScaledTouchSlop();
}
/**
* Handles the touch event on a view
*
* @see View#onTouchEvent(MotionEvent)
*/
public void onTouchEvent(MotionEvent ev) {
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN: {
// Just in case the previous long press hasn't been cleared, we make sure to
// start fresh on touch down.
cancelLongPress();
postCheckForLongPress();
if (isStylusButtonPressed(ev)) {
triggerLongPress();
}
break;
}
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
cancelLongPress();
break;
case MotionEvent.ACTION_MOVE:
if (!Utilities.pointInView(mView, ev.getX(), ev.getY(), mSlop)) {
cancelLongPress();
} else if (mPendingCheckForLongPress != null && isStylusButtonPressed(ev)) {
// Only trigger long press if it has not been cancelled before
triggerLongPress();
}
break;
}
}
/**
@@ -65,25 +87,64 @@ public class CheckLongPressHelper {
mLongPressTimeoutFactor = longPressTimeoutFactor;
}
public void postCheckForLongPress() {
private void postCheckForLongPress() {
mHasPerformedLongPress = false;
if (mPendingCheckForLongPress == null) {
mPendingCheckForLongPress = new CheckForLongPress();
mPendingCheckForLongPress = this::triggerLongPress;
}
mView.postDelayed(mPendingCheckForLongPress,
(long) (ViewConfiguration.getLongPressTimeout() * mLongPressTimeoutFactor));
}
/**
* Cancels any pending long press
*/
public void cancelLongPress() {
mHasPerformedLongPress = false;
clearCallbacks();
}
/**
* Returns true if long press has been performed in the current touch gesture
*/
public boolean hasPerformedLongPress() {
return mHasPerformedLongPress;
}
private void triggerLongPress() {
if ((mView.getParent() != null) && mView.hasWindowFocus() && !mHasPerformedLongPress) {
boolean handled;
if (mListener != null) {
handled = mListener.onLongClick(mView);
} else {
handled = mView.performLongClick();
}
if (handled) {
mView.setPressed(false);
mHasPerformedLongPress = true;
}
clearCallbacks();
}
}
private void clearCallbacks() {
if (mPendingCheckForLongPress != null) {
mView.removeCallbacks(mPendingCheckForLongPress);
mPendingCheckForLongPress = null;
}
}
public boolean hasPerformedLongPress() {
return mHasPerformedLongPress;
/**
* Identifies if the provided {@link MotionEvent} is a stylus with the primary stylus button
* pressed.
*
* @param event The event to check.
* @return Whether a stylus button press occurred.
*/
private static boolean isStylusButtonPressed(MotionEvent event) {
return event.getToolType(0) == MotionEvent.TOOL_TYPE_STYLUS
&& event.isButtonPressed(MotionEvent.BUTTON_SECONDARY);
}
}
+78
View File
@@ -16,6 +16,13 @@
package com.android.launcher3;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_HOTSEAT;
import static com.android.launcher3.LauncherSettings.Favorites.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 android.content.ComponentName;
import android.content.ContentValues;
import android.content.Intent;
@@ -24,13 +31,17 @@ import android.os.UserHandle;
import androidx.annotation.Nullable;
import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.util.ContentWriter;
/**
* Represents an item in the launcher.
*/
public class ItemInfo {
public static final boolean DEBUG = true;
public static final int NO_ID = -1;
/**
@@ -190,6 +201,7 @@ public class ItemInfo {
return "id=" + id
+ " type=" + LauncherSettings.Favorites.itemTypeToString(itemType)
+ " container=" + LauncherSettings.Favorites.containerToString((int)container)
+ " targetComponent=" + getTargetComponent()
+ " screen=" + screenId
+ " cell(" + cellX + "," + cellY + ")"
+ " span(" + spanX + "," + spanY + ")"
@@ -221,4 +233,70 @@ public class ItemInfo {
return container == LauncherSettings.Favorites.CONTAINER_HOTSEAT_PREDICTION
|| container == LauncherSettings.Favorites.CONTAINER_PREDICTION;
}
/**
* Can be overridden by inherited classes to fill in {@link LauncherAtom.ItemInfo}
*/
public void setItemBuilder(LauncherAtom.ItemInfo.Builder builder) {
}
/**
* Creates {@link LauncherAtom.ItemInfo} with important fields and parent container info.
*/
public LauncherAtom.ItemInfo buildProto(Intent intent, FolderInfo fInfo) {
LauncherAtom.ItemInfo.Builder itemBuilder = LauncherAtom.ItemInfo.newBuilder();
itemBuilder.setIsWork(user != Process.myUserHandle());
ComponentName cn = getTargetComponent();
switch (itemType) {
case ITEM_TYPE_APPLICATION:
itemBuilder.setApplication(LauncherAtom.Application.newBuilder()
.setComponentName(cn.flattenToShortString())
.setPackageName(cn.getPackageName()));
break;
case ITEM_TYPE_DEEP_SHORTCUT:
case ITEM_TYPE_SHORTCUT:
itemBuilder.setShortcut(LauncherAtom.Shortcut.newBuilder()
.setShortcutName(cn.flattenToShortString()));
break;
case ITEM_TYPE_APPWIDGET:
setItemBuilder(itemBuilder);
break;
default:
break;
}
if (fInfo != null) {
LauncherAtom.FolderContainer.Builder folderBuilder =
LauncherAtom.FolderContainer.newBuilder();
folderBuilder.setGridX(cellX).setGridY(cellY).setPageIndex(screenId);
switch (fInfo.container) {
case CONTAINER_HOTSEAT:
folderBuilder.setHotseat(LauncherAtom.HotseatContainer.newBuilder()
.setIndex(fInfo.screenId));
break;
case CONTAINER_DESKTOP:
folderBuilder.setWorkspace(LauncherAtom.WorkspaceContainer.newBuilder()
.setPageIndex(fInfo.screenId)
.setGridX(fInfo.cellX).setGridY(fInfo.cellY));
break;
}
itemBuilder.setFolder(folderBuilder);
} else {
switch (container) {
case CONTAINER_HOTSEAT:
itemBuilder.setHotseat(LauncherAtom.HotseatContainer.newBuilder()
.setIndex(screenId));
break;
case CONTAINER_DESKTOP:
itemBuilder.setWorkspace(LauncherAtom.WorkspaceContainer.newBuilder()
.setGridX(cellX)
.setGridY(cellY)
.setPageIndex(screenId));
break;
}
}
return itemBuilder.build();
}
}
+54 -58
View File
@@ -77,7 +77,6 @@ import android.view.Menu;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.OvershootInterpolator;
import android.widget.Toast;
@@ -115,6 +114,7 @@ import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.model.ModelWriter;
import com.android.launcher3.notification.NotificationListener;
import com.android.launcher3.pm.PinRequestHelper;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.popup.PopupContainerWithArrow;
import com.android.launcher3.popup.PopupDataProvider;
import com.android.launcher3.popup.SystemShortcut;
@@ -124,7 +124,6 @@ import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.AllAppsSwipeController;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.uioverrides.DepthController;
import com.android.launcher3.uioverrides.plugins.PluginManagerWrapper;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
@@ -139,6 +138,7 @@ import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.util.PendingRequestArgs;
import com.android.launcher3.util.SafeCloseable;
import com.android.launcher3.util.ShortcutUtil;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.Themes;
@@ -271,7 +271,6 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
// UI and state for the overview panel
private View mOverviewPanel;
private View mActionsView;
@Thunk
boolean mWorkspaceLoading = true;
@@ -326,20 +325,10 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
private boolean mDeferOverlayCallbacks;
private final Runnable mDeferredOverlayCallbacks = this::checkIfOverlayStillDeferred;
private DepthController mDepthController =
new DepthController(this);
private final ViewTreeObserver.OnDrawListener mOnDrawListener =
new ViewTreeObserver.OnDrawListener() {
@Override
public void onDraw() {
getDepthController().setSurfaceToLauncher(mDragLayer);
mDragLayer.post(() -> mDragLayer.getViewTreeObserver().removeOnDrawListener(
this));
}
};
private long mLastTouchUpTime = -1;
private boolean mTouchInProgress;
private SafeCloseable mUserChangedCallbackCloseable;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -459,6 +448,9 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
});
TraceHelper.INSTANCE.endSection(traceToken);
mUserChangedCallbackCloseable = UserCache.INSTANCE.get(this).addUserChangeListener(
() -> getStateManager().goToState(NORMAL));
}
protected LauncherOverlayManager getDefaultOverlay() {
@@ -760,8 +752,8 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1) : -1;
if (resultCode == RESULT_CANCELED) {
completeTwoStageWidgetDrop(RESULT_CANCELED, appWidgetId, requestArgs);
mWorkspace.removeExtraEmptyScreenDelayed(true, exitSpringLoaded,
ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
mWorkspace.removeExtraEmptyScreenDelayed(
ON_ACTIVITY_RESULT_ANIMATION_DELAY, false, exitSpringLoaded);
} else if (resultCode == RESULT_OK) {
addAppWidgetImpl(
appWidgetId, requestArgs, null,
@@ -791,15 +783,9 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
"returned from the widget configuration activity.");
result = RESULT_CANCELED;
completeTwoStageWidgetDrop(result, appWidgetId, requestArgs);
final Runnable onComplete = new Runnable() {
@Override
public void run() {
getStateManager().goToState(NORMAL);
}
};
mWorkspace.removeExtraEmptyScreenDelayed(true, onComplete,
ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
mWorkspace.removeExtraEmptyScreenDelayed(
ON_ACTIVITY_RESULT_ANIMATION_DELAY, false,
() -> getStateManager().goToState(NORMAL));
} else {
if (requestArgs.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
// When the screen id represents an actual screen (as opposed to a rank)
@@ -818,8 +804,8 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
dropLayout.setDropPending(false);
}
};
mWorkspace.removeExtraEmptyScreenDelayed(true, onComplete,
ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
mWorkspace.removeExtraEmptyScreenDelayed(
ON_ACTIVITY_RESULT_ANIMATION_DELAY, false, onComplete);
}
return;
}
@@ -838,12 +824,12 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
// Handle custom shortcuts created using ACTION_CREATE_SHORTCUT.
if (resultCode == RESULT_OK && requestArgs.container != ItemInfo.NO_ID) {
completeAdd(requestCode, data, -1, requestArgs);
mWorkspace.removeExtraEmptyScreenDelayed(true, exitSpringLoaded,
ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
mWorkspace.removeExtraEmptyScreenDelayed(
ON_ACTIVITY_RESULT_ANIMATION_DELAY, false, exitSpringLoaded);
} else if (resultCode == RESULT_CANCELED) {
mWorkspace.removeExtraEmptyScreenDelayed(true, exitSpringLoaded,
ON_ACTIVITY_RESULT_ANIMATION_DELAY, false);
mWorkspace.removeExtraEmptyScreenDelayed(
ON_ACTIVITY_RESULT_ANIMATION_DELAY, false, exitSpringLoaded);
}
}
@@ -942,8 +928,6 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
final int origDragLayerChildCount = mDragLayer.getChildCount();
super.onStop();
mDragLayer.getViewTreeObserver().removeOnDrawListener(mOnDrawListener);
if (mDeferOverlayCallbacks) {
checkIfOverlayStillDeferred();
} else {
@@ -956,7 +940,6 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
NotificationListener.removeNotificationsChangedListener();
getStateManager().moveToRestState();
getDepthController().setSurfaceToLauncher(null);
// Workaround for b/78520668, explicitly trim memory once UI is hidden
onTrimMemory(TRIM_MEMORY_UI_HIDDEN);
@@ -974,6 +957,8 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
}
});
}
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "Activity.onStop");
}
@Override
@@ -984,10 +969,10 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
if (!mDeferOverlayCallbacks) {
mOverlayManager.onActivityStarted(this);
}
mDragLayer.getViewTreeObserver().addOnDrawListener(mOnDrawListener);
mAppWidgetHost.setListenIfResumed(true);
TraceHelper.INSTANCE.endSection(traceToken);
TestLogging.recordEvent(TestProtocol.SEQUENCE_MAIN, "Activity.onStart");
}
private void handleDeferredResume() {
@@ -1180,7 +1165,6 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
mWorkspace = mDragLayer.findViewById(R.id.workspace);
mWorkspace.initParentViews(mDragLayer);
mOverviewPanel = findViewById(R.id.overview_panel);
mActionsView = findViewById(R.id.overview_actions_view);
mHotseat = findViewById(R.id.hotseat);
mLauncherView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
@@ -1207,7 +1191,6 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
mScrimView = findViewById(R.id.scrim_view);
// Setup the drag controller (drop targets have to be added in reverse order in priority)
mDragController.setMoveTarget(mWorkspace);
mDropTargetBar.setup(mDragController);
mAllAppsController.setupViews(mAppsView);
@@ -1428,10 +1411,6 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
return (T) mOverviewPanel;
}
public View getActionsView() {
return mActionsView;
}
public DropTargetBar getDropTargetBar() {
return mDropTargetBar;
}
@@ -1507,11 +1486,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
target.pageIndex = mWorkspace.getCurrentPage();
ued.logActionCommand(Action.Command.HOME_INTENT, target,
newContainerTarget(ContainerType.WORKSPACE));
final View v = getWindow().peekDecorView();
if (v != null && v.getWindowToken() != null) {
UiThreadHelper.hideKeyboardAsync(this, v.getWindowToken());
}
hideKeyboard();
if (mLauncherCallbacks != null) {
mLauncherCallbacks.onHomeIntent(internalStateHandled);
@@ -1522,6 +1497,16 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
TraceHelper.INSTANCE.endSection(traceToken);
}
/**
* Hides the keyboard if visible
*/
public void hideKeyboard() {
final View v = getWindow().peekDecorView();
if (v != null && v.getWindowToken() != null) {
UiThreadHelper.hideKeyboardAsync(this, v.getWindowToken());
}
}
@Override
public void onRestoreInstanceState(Bundle state) {
super.onRestoreInstanceState(state);
@@ -1589,6 +1574,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
mOverlayManager.onActivityDestroyed(this);
mAppTransitionManager.unregisterRemoteAnimations();
mUserChangedCallbackCloseable.close();
}
public LauncherAccessibilityDelegate getAccessibilityDelegate() {
@@ -1682,7 +1668,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
};
completeAddAppWidget(appWidgetId, info, boundWidget,
addFlowHandler.getProviderInfo(this));
mWorkspace.removeExtraEmptyScreenDelayed(true, onComplete, delay, false);
mWorkspace.removeExtraEmptyScreenDelayed(delay, false, onComplete);
}
}
@@ -1840,13 +1826,28 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_UP) {
mLastTouchUpTime = System.currentTimeMillis();
switch (ev.getAction()) {
case MotionEvent.ACTION_DOWN:
mTouchInProgress = true;
break;
case MotionEvent.ACTION_UP:
mLastTouchUpTime = System.currentTimeMillis();
// Follow through
case MotionEvent.ACTION_CANCEL:
mTouchInProgress = false;
break;
}
TestLogging.recordMotionEvent(TestProtocol.SEQUENCE_MAIN, "Touch event", ev);
return super.dispatchTouchEvent(ev);
}
/**
* Returns true if a touch interaction is in progress
*/
public boolean isTouchInProgress() {
return mTouchInProgress;
}
@Override
public void onBackPressed() {
if (finishAutoCancelActionMode()) {
@@ -2112,7 +2113,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
}
// Remove the extra empty screen
mWorkspace.removeExtraEmptyScreen(false, false);
mWorkspace.removeExtraEmptyScreen(false);
}
/**
@@ -2716,8 +2717,7 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
}
protected StateHandler[] createStateHandlers() {
return new StateHandler[] { getAllAppsController(), getWorkspace(),
getDepthController() };
return new StateHandler[] { getAllAppsController(), getWorkspace() };
}
public TouchController[] createTouchControllers() {
@@ -2754,10 +2754,6 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
return Stream.of(APP_INFO, WIDGETS, INSTALL);
}
public DepthController getDepthController() {
return mDepthController;
}
public static Launcher getLauncher(Context context) {
return fromContext(context);
}
@@ -21,6 +21,7 @@ import android.content.ComponentName;
import android.content.Intent;
import android.os.Process;
import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.model.PackageItemInfo;
import com.android.launcher3.util.ContentWriter;
@@ -162,7 +163,9 @@ public class LauncherAppWidgetInfo extends ItemInfo {
@Override
protected String dumpProperties() {
return super.dumpProperties() + " appWidgetId=" + appWidgetId;
return super.dumpProperties()
+ " providerName=" + providerName
+ " appWidgetId=" + appWidgetId;
}
public final boolean isWidgetIdAllocated() {
@@ -182,4 +185,13 @@ public class LauncherAppWidgetInfo extends ItemInfo {
public final boolean hasOptionFlag(int option) {
return (options & option) != 0;
}
@Override
public void setItemBuilder(LauncherAtom.ItemInfo.Builder builder) {
builder.setWidget(LauncherAtom.Widget.newBuilder()
.setSpanX(spanX)
.setSpanY(spanY)
.setComponentName(providerName.toString())
.setPackageName(providerName.getPackageName()));
}
}
+43 -18
View File
@@ -85,6 +85,7 @@ import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Locale;
import java.util.function.Supplier;
public class LauncherProvider extends ContentProvider {
private static final String TAG = "LauncherProvider";
@@ -145,7 +146,7 @@ public class LauncherProvider extends ContentProvider {
*/
protected synchronized void createDbIfNotExists() {
if (mOpenHelper == null) {
mOpenHelper = new DatabaseHelper(getContext());
mOpenHelper = DatabaseHelper.createDatabaseHelper(getContext());
if (RestoreDbTask.isPending(getContext())) {
if (!RestoreDbTask.performRestore(getContext(), mOpenHelper,
@@ -159,17 +160,17 @@ public class LauncherProvider extends ContentProvider {
}
}
private synchronized boolean updateCurrentOpenHelper() {
final InvariantDeviceProfile idp = InvariantDeviceProfile.INSTANCE.get(getContext());
if (TextUtils.equals(idp.dbFile, mOpenHelper.getDatabaseName())) {
private synchronized boolean prepForMigration(String dbFile, String targetTableName,
Supplier<DatabaseHelper> src, Supplier<DatabaseHelper> dst) {
if (TextUtils.equals(dbFile, mOpenHelper.getDatabaseName())) {
return false;
}
DatabaseHelper oldHelper = mOpenHelper;
mOpenHelper = new DatabaseHelper(getContext());
copyTable(oldHelper.getReadableDatabase(), Favorites.TABLE_NAME,
mOpenHelper.getWritableDatabase(), Favorites.TMP_TABLE, getContext());
oldHelper.close();
final DatabaseHelper helper = src.get();
mOpenHelper = dst.get();
copyTable(helper.getReadableDatabase(), Favorites.TABLE_NAME,
mOpenHelper.getWritableDatabase(), targetTableName, getContext());
helper.close();
return true;
}
@@ -425,7 +426,23 @@ public class LauncherProvider extends ContentProvider {
if (MULTI_DB_GRID_MIRATION_ALGO.get()) {
Bundle result = new Bundle();
result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE,
updateCurrentOpenHelper());
prepForMigration(
InvariantDeviceProfile.INSTANCE.get(getContext()).dbFile,
Favorites.TMP_TABLE,
() -> mOpenHelper,
() -> DatabaseHelper.createDatabaseHelper(getContext())));
return result;
}
}
case LauncherSettings.Settings.METHOD_PREP_FOR_PREVIEW: {
if (MULTI_DB_GRID_MIRATION_ALGO.get()) {
Bundle result = new Bundle();
result.putBoolean(LauncherSettings.Settings.EXTRA_VALUE,
prepForMigration(
arg /* dbFile */,
Favorites.PREVIEW_TABLE_NAME,
() -> DatabaseHelper.createDatabaseHelper(getContext(), arg),
() -> mOpenHelper));
return result;
}
}
@@ -596,23 +613,31 @@ public class LauncherProvider extends ContentProvider {
private int mMaxScreenId = -1;
private boolean mBackupTableExists;
DatabaseHelper(Context context) {
this(context, MULTI_DB_GRID_MIRATION_ALGO.get() ? InvariantDeviceProfile.INSTANCE.get(
context).dbFile : LauncherFiles.LAUNCHER_DB);
static DatabaseHelper createDatabaseHelper(Context context) {
return createDatabaseHelper(context, null);
}
static DatabaseHelper createDatabaseHelper(Context context, String dbName) {
if (dbName == null) {
dbName = MULTI_DB_GRID_MIRATION_ALGO.get() ? InvariantDeviceProfile.INSTANCE.get(
context).dbFile : LauncherFiles.LAUNCHER_DB;
}
DatabaseHelper databaseHelper = new DatabaseHelper(context, dbName);
// Table creation sometimes fails silently, which leads to a crash loop.
// This way, we will try to create a table every time after crash, so the device
// would eventually be able to recover.
if (!tableExists(getReadableDatabase(), Favorites.TABLE_NAME)) {
if (!tableExists(databaseHelper.getReadableDatabase(), Favorites.TABLE_NAME)) {
Log.e(TAG, "Tables are missing after onCreate has been called. Trying to recreate");
// This operation is a no-op if the table already exists.
addFavoritesTable(getWritableDatabase(), true);
databaseHelper.addFavoritesTable(databaseHelper.getWritableDatabase(), true);
}
if (!MULTI_DB_GRID_MIRATION_ALGO.get()) {
mBackupTableExists = tableExists(getReadableDatabase(),
Favorites.BACKUP_TABLE_NAME);
databaseHelper.mBackupTableExists = tableExists(
databaseHelper.getReadableDatabase(), Favorites.BACKUP_TABLE_NAME);
}
initIds();
databaseHelper.initIds();
return databaseHelper;
}
/**
@@ -326,10 +326,16 @@ public class LauncherSettings {
public static final String METHOD_UPDATE_CURRENT_OPEN_HELPER = "update_current_open_helper";
public static final String METHOD_PREP_FOR_PREVIEW = "prep_for_preview";
public static final String EXTRA_VALUE = "value";
public static Bundle call(ContentResolver cr, String method) {
return cr.call(CONTENT_URI, method, null, null);
return call(cr, method, null);
}
public static Bundle call(ContentResolver cr, String method, String arg) {
return cr.call(CONTENT_URI, method, arg, null);
}
}
}
+1 -1
View File
@@ -73,7 +73,7 @@ public abstract class LauncherState {
public static final int ALL_APPS_HEADER_EXTRA = 1 << 3; // e.g. app predictions
public static final int ALL_APPS_CONTENT = 1 << 4;
public static final int VERTICAL_SWIPE_INDICATOR = 1 << 5;
public static final int RECENTS_CLEAR_ALL_BUTTON = 1 << 6;
public static final int OVERVIEW_BUTTONS = 1 << 6;
/** Mask of all the items that are contained in the apps view. */
public static final int APPS_VIEW_ITEM_MASK =
@@ -324,6 +324,7 @@ public class LauncherStateManager {
public AnimatorPlaybackController createAnimationToNewWorkspace(LauncherState state,
StateAnimationConfig config) {
config.userControlled = true;
mConfig.reset();
config.copyTo(mConfig);
mConfig.playbackController = createAnimationToNewWorkspaceInternal(state)
+19
View File
@@ -50,6 +50,8 @@ import static com.android.launcher3.touch.PagedOrientationHandler.CANVAS_TRANSLA
import static com.android.launcher3.touch.PagedOrientationHandler.VIEW_SCROLL_BY;
import static com.android.launcher3.touch.PagedOrientationHandler.VIEW_SCROLL_TO;
import androidx.annotation.Nullable;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.config.FeatureFlags;
@@ -133,6 +135,7 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
protected int mActivePointerId = INVALID_POINTER;
protected boolean mIsPageInTransition = false;
private Runnable mOnPageTransitionEndCallback;
protected float mSpringOverScroll;
@@ -391,6 +394,22 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
AccessibilityManagerCompat.sendScrollFinishedEventToTest(getContext());
AccessibilityManagerCompat.sendCustomAccessibilityEvent(getPageAt(mCurrentPage),
AccessibilityEvent.TYPE_VIEW_FOCUSED, null);
if (mOnPageTransitionEndCallback != null) {
mOnPageTransitionEndCallback.run();
mOnPageTransitionEndCallback = null;
}
}
/**
* Sets a callback to run once when the scrolling finishes. If there is currently
* no page in transition, then the callback is called immediately.
*/
public void setOnPageTransitionEndCallback(@Nullable Runnable callback) {
if (mIsPageInTransition || callback == null) {
mOnPageTransitionEndCallback = callback;
} else {
callback.run();
}
}
protected int getUnboundedScroll() {
@@ -108,7 +108,7 @@ public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmList
updateText(R.string.uninstall_drop_target_label);
} else if (action == DISMISS_PREDICTION) {
mHoverColor = Themes.getColorAccent(getContext());
setDrawable(R.drawable.ic_block);
setDrawable(R.drawable.ic_block_shadow);
updateText(R.string.dismiss_prediction_label);
} else if (action == RECONFIGURE) {
mHoverColor = Themes.getColorAccent(getContext());
@@ -1,25 +0,0 @@
package com.android.launcher3;
import android.view.MotionEvent;
import android.view.View;
import com.android.launcher3.StylusEventHelper.StylusButtonListener;
/**
* Simple listener that performs a long click on the view after a stylus button press.
*/
public class SimpleOnStylusPressListener implements StylusButtonListener {
private View mView;
public SimpleOnStylusPressListener(View view) {
mView = view;
}
public boolean onPressed(MotionEvent event) {
return mView.isLongClickable() && mView.performLongClick();
}
public boolean onReleased(MotionEvent event) {
return false;
}
}
@@ -1,109 +0,0 @@
package com.android.launcher3;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
/**
* Helper for identifying when a stylus touches a view while the primary stylus button is pressed.
* This can occur in {@value MotionEvent#ACTION_DOWN} or {@value MotionEvent#ACTION_MOVE}.
*/
public class StylusEventHelper {
/**
* Implement this interface to receive callbacks for a stylus button press and release.
*/
public interface StylusButtonListener {
/**
* Called when the stylus button is pressed.
*
* @param event The MotionEvent that the button press occurred for.
* @return Whether the event was handled.
*/
public boolean onPressed(MotionEvent event);
/**
* Called when the stylus button is released after a button press. This is also called if
* the event is canceled or the stylus is lifted off the screen.
*
* @param event The MotionEvent the button release occurred for.
* @return Whether the event was handled.
*/
public boolean onReleased(MotionEvent event);
}
private boolean mIsButtonPressed;
private View mView;
private StylusButtonListener mListener;
private final float mSlop;
/**
* Constructs a helper for listening to stylus button presses and releases. Ensure that {
* {@link #onMotionEvent(MotionEvent)} and {@link #onGenericMotionEvent(MotionEvent)} are called on
* the helper to correctly identify stylus events.
*
* @param listener The listener to call for stylus events.
* @param view Optional view associated with the touch events.
*/
public StylusEventHelper(StylusButtonListener listener, View view) {
mListener = listener;
mView = view;
if (mView != null) {
mSlop = ViewConfiguration.get(mView.getContext()).getScaledTouchSlop();
} else {
mSlop = ViewConfiguration.getTouchSlop();
}
}
public boolean onMotionEvent(MotionEvent event) {
final boolean stylusButtonPressed = isStylusButtonPressed(event);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mIsButtonPressed = stylusButtonPressed;
if (mIsButtonPressed) {
return mListener.onPressed(event);
}
break;
case MotionEvent.ACTION_MOVE:
if (!Utilities.pointInView(mView, event.getX(), event.getY(), mSlop)) {
return false;
}
if (!mIsButtonPressed && stylusButtonPressed) {
mIsButtonPressed = true;
return mListener.onPressed(event);
} else if (mIsButtonPressed && !stylusButtonPressed) {
mIsButtonPressed = false;
return mListener.onReleased(event);
}
break;
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL:
if (mIsButtonPressed) {
mIsButtonPressed = false;
return mListener.onReleased(event);
}
break;
}
return false;
}
/**
* Whether a stylus button press is occurring.
*/
public boolean inStylusButtonPressed() {
return mIsButtonPressed;
}
/**
* Identifies if the provided {@link MotionEvent} is a stylus with the primary stylus button
* pressed.
*
* @param event The event to check.
* @return Whether a stylus button press occurred.
*/
private static boolean isStylusButtonPressed(MotionEvent event) {
return event.getToolType(0) == MotionEvent.TOOL_TYPE_STYLUS
&& ((event.getButtonState() & MotionEvent.BUTTON_SECONDARY)
== MotionEvent.BUTTON_SECONDARY);
}
}
+25 -80
View File
@@ -29,7 +29,6 @@ import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.LayoutTransition;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.annotation.SuppressLint;
@@ -43,7 +42,6 @@ import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Parcelable;
import android.os.UserHandle;
@@ -125,9 +123,6 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
private static final boolean ENFORCE_DRAG_EVENT_ORDER = false;
private static final int SNAP_OFF_EMPTY_SCREEN_DURATION = 400;
private static final int FADE_EMPTY_SCREEN_DURATION = 150;
private static final int ADJACENT_SCREEN_DROP_DURATION = 300;
private static final int DEFAULT_PAGE = 0;
@@ -140,7 +135,6 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
@Thunk final IntSparseArrayMap<CellLayout> mWorkspaceScreens = new IntSparseArrayMap<>();
@Thunk final IntArray mScreenOrder = new IntArray();
@Thunk Runnable mRemoveEmptyScreenRunnable;
@Thunk boolean mDeferRemoveExtraEmptyScreen = false;
/**
@@ -428,7 +422,7 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
}
if (!mDeferRemoveExtraEmptyScreen) {
removeExtraEmptyScreen(true, mDragSourceInternal != null);
removeExtraEmptyScreen(mDragSourceInternal != null);
}
updateChildrenLayersEnabled();
@@ -453,8 +447,16 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
private void setupLayoutTransition() {
// We want to show layout transitions when pages are deleted, to close the gap.
mLayoutTransition = new LayoutTransition();
mLayoutTransition.enableTransitionType(LayoutTransition.DISAPPEARING);
mLayoutTransition.enableTransitionType(LayoutTransition.CHANGE_DISAPPEARING);
// Change the interpolators such that the fade animation plays before the move animation.
// This prevents empty adjacent pages to overlay during animation
mLayoutTransition.setInterpolator(LayoutTransition.DISAPPEARING,
Interpolators.clampToProgress(Interpolators.ACCEL_DEACCEL, 0, 0.5f));
mLayoutTransition.setInterpolator(LayoutTransition.CHANGE_DISAPPEARING,
Interpolators.clampToProgress(Interpolators.ACCEL_DEACCEL, 0.5f, 1));
mLayoutTransition.disableTransitionType(LayoutTransition.APPEARING);
mLayoutTransition.disableTransitionType(LayoutTransition.CHANGE_APPEARING);
setLayoutTransition(mLayoutTransition);
@@ -571,9 +573,6 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
boolean lastChildOnScreen = false;
boolean childOnFinalScreen = false;
// Cancel any pending removal of empty screen
mRemoveEmptyScreenRunnable = null;
if (mDragSourceInternal != null) {
if (mDragSourceInternal.getChildCount() == 1) {
lastChildOnScreen = true;
@@ -624,43 +623,34 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
}
}
public void removeExtraEmptyScreen(final boolean animate, boolean stripEmptyScreens) {
removeExtraEmptyScreenDelayed(animate, null, 0, stripEmptyScreens);
public void removeExtraEmptyScreen(boolean stripEmptyScreens) {
removeExtraEmptyScreenDelayed(0, stripEmptyScreens, null);
}
public void removeExtraEmptyScreenDelayed(final boolean animate, final Runnable onComplete,
final int delay, final boolean stripEmptyScreens) {
public void removeExtraEmptyScreenDelayed(
int delay, boolean stripEmptyScreens, Runnable onComplete) {
if (mLauncher.isWorkspaceLoading()) {
// Don't strip empty screens if the workspace is still loading
return;
}
if (delay > 0) {
postDelayed(new Runnable() {
@Override
public void run() {
removeExtraEmptyScreenDelayed(animate, onComplete, 0, stripEmptyScreens);
}
}, delay);
postDelayed(
() -> removeExtraEmptyScreenDelayed(0, stripEmptyScreens, onComplete), delay);
return;
}
convertFinalScreenToEmptyScreenIfNecessary();
if (hasExtraEmptyScreen()) {
int emptyIndex = mScreenOrder.indexOf(EXTRA_EMPTY_SCREEN_ID);
if (getNextPage() == emptyIndex) {
snapToPage(getNextPage() - 1, SNAP_OFF_EMPTY_SCREEN_DURATION);
fadeAndRemoveEmptyScreen(SNAP_OFF_EMPTY_SCREEN_DURATION, FADE_EMPTY_SCREEN_DURATION,
onComplete, stripEmptyScreens);
} else {
snapToPage(getNextPage(), 0);
fadeAndRemoveEmptyScreen(0, FADE_EMPTY_SCREEN_DURATION,
onComplete, stripEmptyScreens);
}
return;
} else if (stripEmptyScreens) {
// If we're not going to strip the empty screens after removing
// the extra empty screen, do it right away.
removeView(mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID));
mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID);
mScreenOrder.removeValue(EXTRA_EMPTY_SCREEN_ID);
// Update the page indicator to reflect the removed page.
showPageIndicatorAtCurrentScroll();
}
if (stripEmptyScreens) {
stripEmptyScreens();
}
@@ -669,44 +659,6 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
}
}
private void fadeAndRemoveEmptyScreen(int delay, int duration, final Runnable onComplete,
final boolean stripEmptyScreens) {
// XXX: Do we need to update LM workspace screens below?
final CellLayout cl = mWorkspaceScreens.get(EXTRA_EMPTY_SCREEN_ID);
mRemoveEmptyScreenRunnable = new Runnable() {
@Override
public void run() {
if (hasExtraEmptyScreen()) {
mWorkspaceScreens.remove(EXTRA_EMPTY_SCREEN_ID);
mScreenOrder.removeValue(EXTRA_EMPTY_SCREEN_ID);
removeView(cl);
if (stripEmptyScreens) {
stripEmptyScreens();
}
// Update the page indicator to reflect the removed page.
showPageIndicatorAtCurrentScroll();
}
}
};
ObjectAnimator oa = ObjectAnimator.ofFloat(cl, ALPHA, 0f);
oa.setDuration(duration);
oa.setStartDelay(delay);
oa.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
if (mRemoveEmptyScreenRunnable != null) {
mRemoveEmptyScreenRunnable.run();
}
if (onComplete != null) {
onComplete.run();
}
}
});
oa.start();
}
public boolean hasExtraEmptyScreen() {
return mWorkspaceScreens.containsKey(EXTRA_EMPTY_SCREEN_ID) && getChildCount() > 1;
}
@@ -793,8 +745,6 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
}
}
boolean isInAccessibleDrag = mLauncher.getAccessibilityDelegate().isInAccessibleDrag();
// We enforce at least one page to add new items to. In the case that we remove the last
// such screen, we convert the last screen to the empty screen
int minScreens = 1;
@@ -813,7 +763,6 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
removeView(cl);
} else {
// if this is the last screen, convert it to the empty screen
mRemoveEmptyScreenRunnable = null;
mWorkspaceScreens.put(EXTRA_EMPTY_SCREEN_ID, cl);
mScreenOrder.add(EXTRA_EMPTY_SCREEN_ID);
}
@@ -1042,7 +991,6 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
if (!mOverlayShown) {
mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
Action.Direction.LEFT, ContainerType.WORKSPACE, 0);
mLauncher.getStatsLogManager().logSwipeOnContainer(true, 0);
}
mOverlayShown = true;
// Not announcing the overlay page for accessibility since it announces itself.
@@ -1052,7 +1000,6 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
if (!ued.isPreviousHomeGesture()) {
mLauncher.getUserEventDispatcher().logActionOnContainer(Action.Touch.SWIPE,
Action.Direction.RIGHT, ContainerType.WORKSPACE, -1);
mLauncher.getStatsLogManager().logSwipeOnContainer(false, -1);
}
} else if (Float.compare(mOverlayTranslation, 0f) != 0) {
// When arriving to 0 overscroll from non-zero overscroll, announce page for
@@ -1224,10 +1171,8 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
protected void onAttachedToWindow() {
super.onAttachedToWindow();
IBinder windowToken = getWindowToken();
mWallpaperOffset.setWindowToken(windowToken);
mWallpaperOffset.setWindowToken(getWindowToken());
computeScroll();
mDragController.setWindowToken(windowToken);
}
protected void onDetachedFromWindow() {
@@ -146,11 +146,21 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme
}
}
if (!fromKeyboard && !itemSupportsLongClick(host, item)) {
info.setLongClickable(false);
info.removeAction(AccessibilityAction.ACTION_LONG_CLICK);
}
if ((item instanceof AppInfo) || (item instanceof PendingAddItemInfo)) {
info.addAction(mActions.get(ADD_TO_WORKSPACE));
}
}
private boolean itemSupportsLongClick(View host, ItemInfo info) {
return new CustomActionsPopup(mLauncher, host).canShow()
|| ShortcutUtil.supportsShortcuts(info);
}
private boolean itemSupportsAccessibleDrag(ItemInfo item) {
if (item instanceof WorkspaceItemInfo) {
// Support the action unless the item is in a context menu.
@@ -171,18 +181,18 @@ public class LauncherAccessibilityDelegate extends AccessibilityDelegate impleme
public boolean performAction(final View host, final ItemInfo item, int action) {
if (action == ACTION_LONG_CLICK) {
if (ShortcutUtil.isDeepShortcut(item)) {
CustomActionsPopup popup = new CustomActionsPopup(mLauncher, host);
if (popup.canShow()) {
popup.show();
return true;
}
} else if (host instanceof BubbleTextView) {
if (ShortcutUtil.supportsShortcuts(item)) {
// Long press should be consumed for workspace items, and it should invoke the
// Shortcuts / Notifications / Actions pop-up menu, and not start a drag as the
// standard long press path does.
PopupContainerWithArrow.showForIcon((BubbleTextView) host);
return true;
} else {
CustomActionsPopup popup = new CustomActionsPopup(mLauncher, host);
if (popup.canShow()) {
popup.show();
return true;
}
}
}
@@ -406,6 +406,11 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
setupWorkToggle();
mAH[AdapterHolder.MAIN].setup(mViewPager.getChildAt(0), mPersonalMatcher);
mAH[AdapterHolder.WORK].setup(mViewPager.getChildAt(1), mWorkMatcher);
mViewPager.getPageIndicator().setActiveMarker(AdapterHolder.MAIN);
findViewById(R.id.tab_personal)
.setOnClickListener((View view) -> mViewPager.snapToPage(AdapterHolder.MAIN));
findViewById(R.id.tab_work)
.setOnClickListener((View view) -> mViewPager.snapToPage(AdapterHolder.WORK));
onTabChanged(mViewPager.getNextPage());
} else {
mAH[AdapterHolder.MAIN].setup(findViewById(R.id.apps_list_view), null);
@@ -456,16 +461,10 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
public void onTabChanged(int pos) {
mHeader.setMainActive(pos == 0);
reset(true /* animate */);
mViewPager.getPageIndicator().updateTabTextColor(pos);
if (mAH[pos].recyclerView != null) {
mAH[pos].recyclerView.bindFastScrollbar();
findViewById(R.id.tab_personal)
.setOnClickListener((View view) -> mViewPager.snapToPage(AdapterHolder.MAIN));
findViewById(R.id.tab_work)
.setOnClickListener((View view) -> mViewPager.snapToPage(AdapterHolder.WORK));
}
reset(true /* animate */);
if (mWorkModeSwitch != null) {
mWorkModeSwitch.setWorkTabVisible(pos == AdapterHolder.WORK);
}
@@ -135,6 +135,7 @@ public class PersonalWorkSlidingTabStrip extends LinearLayout implements PageInd
@Override
public void setActiveMarker(int activePage) {
updateTabTextColor(activePage);
updateIndicatorPosition(activePage);
if (mContainerView != null && mLastActivePage != activePage) {
mContainerView.onTabChanged(activePage);
}
@@ -17,8 +17,6 @@ package com.android.launcher3.allapps;
import static com.android.launcher3.util.PackageManagerHelper.hasShortcutsPermission;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.content.Context;
import android.content.pm.PackageManager;
import android.graphics.Rect;
@@ -27,7 +25,6 @@ import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.Switch;
import com.android.launcher3.Insettable;
@@ -35,6 +32,7 @@ import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.views.ArrowTipView;
import java.lang.ref.WeakReference;
@@ -43,27 +41,21 @@ import java.lang.ref.WeakReference;
*/
public class WorkModeSwitch extends Switch implements Insettable {
private Rect mInsets = new Rect();
protected ObjectAnimator mOpenCloseAnimator;
private static final int WORK_TIP_THRESHOLD = 2;
public static final String KEY_WORK_TIP_COUNTER = "worked_tip_counter";
private Rect mInsets = new Rect();
public WorkModeSwitch(Context context) {
super(context);
init();
}
public WorkModeSwitch(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public WorkModeSwitch(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
mOpenCloseAnimator = ObjectAnimator.ofPropertyValuesHolder(this);
}
@Override
@@ -73,6 +65,9 @@ public class WorkModeSwitch extends Switch implements Insettable {
@Override
public void toggle() {
Launcher launcher = Launcher.getLauncher(getContext());
// don't show tip if user uses toggle
launcher.getSharedPrefs().edit().putInt(KEY_WORK_TIP_COUNTER, -1).apply();
trySetQuietModeEnabledToAllProfilesAsync(isChecked());
}
@@ -95,11 +90,6 @@ public class WorkModeSwitch extends Switch implements Insettable {
this.setVisibility(shouldShowWorkSwitch() ? VISIBLE : GONE);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
return ev.getActionMasked() == MotionEvent.ACTION_MOVE || super.onTouchEvent(ev);
}
private void trySetQuietModeEnabledToAllProfilesAsync(boolean enabled) {
new SetQuietModeEnabledAsyncTask(enabled, new WeakReference<>(this)).execute();
}
@@ -117,9 +107,15 @@ public class WorkModeSwitch extends Switch implements Insettable {
*/
public void setWorkTabVisible(boolean workTabVisible) {
if (!shouldShowWorkSwitch()) return;
mOpenCloseAnimator.setValues(PropertyValuesHolder.ofFloat(ALPHA, workTabVisible ? 1 : 0));
mOpenCloseAnimator.start();
clearAnimation();
if (workTabVisible) {
setVisibility(VISIBLE);
setAlpha(0);
animate().alpha(1).start();
showTipifNeeded();
} else {
animate().alpha(0).withEndAction(() -> this.setVisibility(GONE)).start();
}
}
private static final class SetQuietModeEnabledAsyncTask
@@ -179,4 +175,17 @@ public class WorkModeSwitch extends Switch implements Insettable {
|| launcher.checkSelfPermission("android.permission.MODIFY_QUIET_MODE")
== PackageManager.PERMISSION_GRANTED);
}
/**
* Shows a work tip on the Nth work tab open
*/
public void showTipifNeeded() {
Launcher launcher = Launcher.getLauncher(getContext());
int tipCounter = launcher.getSharedPrefs().getInt(KEY_WORK_TIP_COUNTER, WORK_TIP_THRESHOLD);
if (tipCounter < 0) return;
if (tipCounter == 0) {
new ArrowTipView(launcher).show(launcher.getString(R.string.work_switch_tip), getTop());
}
launcher.getSharedPrefs().edit().putInt(KEY_WORK_TIP_COUNTER, tipCounter - 1).apply();
}
}
@@ -62,9 +62,8 @@ public class AppsSearchContainerLayout extends ExtendedEditText
private AlphabeticalAppsList mApps;
private AllAppsContainerView mAppsView;
// This value was used to position the QSB. We store it here for translationY animations.
private final float mFixedTranslationY;
private final float mMarginTopAdjusting;
// The amount of pixels to shift down and overlap with the rest of the content.
private final int mContentOverlap;
public AppsSearchContainerLayout(Context context) {
this(context, null);
@@ -82,11 +81,10 @@ public class AppsSearchContainerLayout extends ExtendedEditText
mSearchQueryBuilder = new SpannableStringBuilder();
Selection.setSelection(mSearchQueryBuilder, 0);
mFixedTranslationY = getTranslationY();
mMarginTopAdjusting = mFixedTranslationY - getPaddingTop();
setHint(prefixTextWithIcon(getContext(), R.drawable.ic_allapps_search, getHint()));
mContentOverlap =
getResources().getDimensionPixelSize(R.dimen.all_apps_search_bar_field_height) / 2;
}
@Override
@@ -128,6 +126,8 @@ public class AppsSearchContainerLayout extends ExtendedEditText
int expectedLeft = parent.getPaddingLeft() + (availableWidth - myWidth) / 2;
int shift = expectedLeft - left;
setTranslationX(shift);
offsetTopAndBottom(mContentOverlap);
}
@Override
@@ -196,7 +196,7 @@ public class AppsSearchContainerLayout extends ExtendedEditText
@Override
public void setInsets(Rect insets) {
MarginLayoutParams mlp = (MarginLayoutParams) getLayoutParams();
mlp.topMargin = Math.round(Math.max(-mFixedTranslationY, insets.top - mMarginTopAdjusting));
mlp.topMargin = insets.top;
requestLayout();
}
@@ -205,9 +205,7 @@ public class AppsSearchContainerLayout extends ExtendedEditText
if (mLauncher.getDeviceProfile().isVerticalBarLayout()) {
return 0;
} else {
int topMargin = Math.round(Math.max(
-mFixedTranslationY, insets.top - mMarginTopAdjusting));
return insets.bottom + topMargin + mFixedTranslationY;
return insets.bottom + insets.top;
}
}
@@ -75,6 +75,9 @@ public class AccessibilityManagerCompat {
}
public static void sendScrollFinishedEventToTest(Context context) {
if (TestProtocol.sDebugTracing) {
Log.d(TestProtocol.NO_SCROLL_END_WIDGETS, "sendScrollFinishedEventToTest");
}
final AccessibilityManager accessibilityManager = getAccessibilityManagerForTest(context);
if (accessibilityManager == null) return;
@@ -154,7 +154,7 @@ public final class FeatureFlags {
"Replace Smartspace with a version rendered by System UI.");
public static final BooleanFlag ENABLE_LSQ_VELOCITY_PROVIDER = getDebugFlag(
"ENABLE_LSQ_VELOCITY_PROVIDER", false,
"ENABLE_LSQ_VELOCITY_PROVIDER", true,
"Use Least Square algorithm for motion pause detection.");
public static final BooleanFlag ALWAYS_USE_HARDWARE_OPTIMIZATION_FOR_FOLDER_ANIMATIONS =
@@ -20,6 +20,7 @@ import static com.android.launcher3.AbstractFloatingView.TYPE_DISCOVERY_BOUNCE;
import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.Utilities.ATLEAST_Q;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import android.animation.ValueAnimator;
import android.content.ComponentName;
@@ -27,7 +28,6 @@ import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.Rect;
import android.os.IBinder;
import android.view.DragEvent;
import android.view.HapticFeedbackConstants;
import android.view.KeyEvent;
@@ -44,7 +44,6 @@ import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.accessibility.DragViewStateAnnouncer;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.util.UiThreadHelper;
import java.util.ArrayList;
@@ -64,7 +63,7 @@ public class DragController implements DragDriver.EventListener, TouchController
private final FlingToDeleteHelper mFlingToDeleteHelper;
// temporaries to avoid gc thrash
private Rect mRectTemp = new Rect();
private final Rect mRectTemp = new Rect();
private final int[] mCoordinatesTemp = new int[2];
/**
@@ -86,21 +85,14 @@ public class DragController implements DragDriver.EventListener, TouchController
private DropTarget.DragObject mDragObject;
/** Who can receive drop events */
private ArrayList<DropTarget> mDropTargets = new ArrayList<>();
private ArrayList<DragListener> mListeners = new ArrayList<>();
/** The window token used as the parent for the DragView. */
private IBinder mWindowToken;
private View mMoveTarget;
private final ArrayList<DropTarget> mDropTargets = new ArrayList<>();
private final ArrayList<DragListener> mListeners = new ArrayList<>();
private DropTarget mLastDropTarget;
private int mLastTouchClassification;
private int mDistanceSinceScroll = 0;
private Rect mDragLayerRect = new Rect();
private boolean mIsInPreDrag;
/**
@@ -153,8 +145,7 @@ public class DragController implements DragDriver.EventListener, TouchController
android.os.Debug.startMethodTracing("Launcher");
}
// Hide soft keyboard, if visible
UiThreadHelper.hideKeyboardAsync(mLauncher, mWindowToken);
mLauncher.hideKeyboard();
AbstractFloatingView.closeOpenViews(mLauncher, false, TYPE_DISCOVERY_BOUNCE);
mOptions = options;
@@ -217,6 +208,11 @@ public class DragController implements DragDriver.EventListener, TouchController
handleMoveEvent(mLastTouch.x, mLastTouch.y);
mLauncher.getUserEventDispatcher().resetActionDurationMillis();
if (!mLauncher.isTouchInProgress() && options.simulatedDndStartPoint == null) {
// If it is an internal drag and the touch is already complete, cancel immediately
MAIN_EXECUTOR.submit(this::cancelDrag);
}
return dragView;
}
@@ -362,9 +358,9 @@ public class DragController implements DragDriver.EventListener, TouchController
* Clamps the position to the drag layer bounds.
*/
private Point getClampedDragLayerPos(float x, float y) {
mLauncher.getDragLayer().getLocalVisibleRect(mDragLayerRect);
mTmpPoint.x = (int) Math.max(mDragLayerRect.left, Math.min(x, mDragLayerRect.right - 1));
mTmpPoint.y = (int) Math.max(mDragLayerRect.top, Math.min(y, mDragLayerRect.bottom - 1));
mLauncher.getDragLayer().getLocalVisibleRect(mRectTemp);
mTmpPoint.x = (int) Math.max(mRectTemp.left, Math.min(x, mRectTemp.right - 1));
mTmpPoint.y = (int) Math.max(mRectTemp.top, Math.min(y, mRectTemp.bottom - 1));
return mTmpPoint;
}
@@ -439,17 +435,6 @@ public class DragController implements DragDriver.EventListener, TouchController
return mDragDriver != null && mDragDriver.onDragEvent(event);
}
/**
* Sets the view that should handle move events.
*/
public void setMoveTarget(View view) {
mMoveTarget = view;
}
public boolean dispatchUnhandledMove(View focused, int direction) {
return mMoveTarget != null && mMoveTarget.dispatchUnhandledMove(focused, direction);
}
private void handleMoveEvent(int x, int y) {
mDragObject.dragView.move(x, y);
@@ -593,10 +578,6 @@ public class DragController implements DragDriver.EventListener, TouchController
return mLauncher.getWorkspace();
}
public void setWindowToken(IBinder token) {
mWindowToken = token;
}
/**
* Sets the drag listener which will be notified when a drag starts or ends.
*/
@@ -21,6 +21,7 @@ import static android.view.View.MeasureSpec.EXACTLY;
import static android.view.View.MeasureSpec.getMode;
import static android.view.View.MeasureSpec.getSize;
import static com.android.launcher3.anim.Interpolators.DEACCEL_1_5;
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
import android.animation.Animator;
@@ -49,7 +50,6 @@ import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.Workspace;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.graphics.OverviewScrim;
import com.android.launcher3.graphics.RotationMode;
@@ -74,11 +74,11 @@ public class DragLayer extends BaseDragLayer<Launcher> {
public static final int ANIMATION_END_DISAPPEAR = 0;
public static final int ANIMATION_END_REMAIN_VISIBLE = 2;
@Thunk DragController mDragController;
private DragController mDragController;
// Variables relating to animation of views after drop
private ValueAnimator mDropAnim = null;
private final TimeInterpolator mCubicEaseOutInterpolator = Interpolators.DEACCEL_1_5;
@Thunk DragView mDropView = null;
@Thunk int mAnchorViewInitialScrollX = 0;
@Thunk View mAnchorView = null;
@@ -88,13 +88,14 @@ public class DragLayer extends BaseDragLayer<Launcher> {
private int mTopViewIndex;
private int mChildCountOnLastUpdate = -1;
private Rect mTmpRect = new Rect();
// Related to adjacent page hints
private final ViewGroupFocusHelper mFocusIndicatorHelper;
private final WorkspaceAndHotseatScrim mWorkspaceScrim;
private final OverviewScrim mOverviewScrim;
// View that should handle move events
private View mMoveTarget;
/**
* Used to create a new DragLayer from XML.
*
@@ -116,6 +117,7 @@ public class DragLayer extends BaseDragLayer<Launcher> {
public void setup(DragController dragController, Workspace workspace) {
mDragController = dragController;
mWorkspaceScrim.setWorkspace(workspace);
mMoveTarget = workspace;
recreateControllers();
}
@@ -223,7 +225,7 @@ public class DragLayer extends BaseDragLayer<Launcher> {
@Override
public boolean dispatchUnhandledMove(View focused, int direction) {
return super.dispatchUnhandledMove(focused, direction)
|| mDragController.dispatchUnhandledMove(focused, direction);
|| mMoveTarget.dispatchUnhandledMove(focused, direction);
}
@Override
@@ -260,9 +262,10 @@ public class DragLayer extends BaseDragLayer<Launcher> {
parentChildren.measureChild(child);
parentChildren.layoutChild(child);
getViewRectRelativeToSelf(dragView, mTmpRect);
final int fromX = mTmpRect.left;
final int fromY = mTmpRect.top;
Rect dragViewBounds = new Rect();
getViewRectRelativeToSelf(dragView, dragViewBounds);
final int fromX = dragViewBounds.left;
final int fromY = dragViewBounds.top;
float coord[] = new float[2];
float childScale = child.getScaleX();
@@ -283,15 +286,15 @@ public class DragLayer extends BaseDragLayer<Launcher> {
if (child instanceof DraggableView) {
DraggableView d = (DraggableView) child;
d.getVisualDragBounds(mTmpRect);
d.getVisualDragBounds(dragViewBounds);
// This accounts for the offset of the DragView created by scaling it about its
// center as it animates into place.
float scaleShiftX = dragView.getMeasuredWidth() * (1 - scale) / 2;
float scaleShiftY = dragView.getMeasuredHeight() * (1 - scale) / 2;
toX += scale * (mTmpRect.left - dragView.getBlurSizeOutline() / 2) - scaleShiftX;
toY += scale * (mTmpRect.top - dragView.getBlurSizeOutline() / 2) - scaleShiftY;
toX += scale * (dragViewBounds.left - dragView.getBlurSizeOutline() / 2) - scaleShiftX;
toY += scale * (dragViewBounds.top - dragView.getBlurSizeOutline() / 2) - scaleShiftY;
}
child.setVisibility(INVISIBLE);
@@ -348,7 +351,7 @@ public class DragLayer extends BaseDragLayer<Launcher> {
if (duration < 0) {
duration = res.getInteger(R.integer.config_dropAnimMaxDuration);
if (dist < maxDist) {
duration *= mCubicEaseOutInterpolator.getInterpolation(dist / maxDist);
duration *= DEACCEL_1_5.getInterpolation(dist / maxDist);
}
duration = Math.max(duration, res.getInteger(R.integer.config_dropAnimMinDuration));
}
@@ -356,7 +359,7 @@ public class DragLayer extends BaseDragLayer<Launcher> {
// Fall back to cubic ease out interpolator for the animation if none is specified
TimeInterpolator interpolator = null;
if (alphaInterpolator == null || motionInterpolator == null) {
interpolator = mCubicEaseOutInterpolator;
interpolator = DEACCEL_1_5;
}
// Animate the view
@@ -31,7 +31,6 @@ import android.util.Property;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewDebug;
import android.view.ViewGroup;
import android.widget.FrameLayout;
@@ -52,8 +51,6 @@ import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.OnAlarmListener;
import com.android.launcher3.R;
import com.android.launcher3.SimpleOnStylusPressListener;
import com.android.launcher3.StylusEventHelper;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.WorkspaceItemInfo;
@@ -87,7 +84,6 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel
private FolderInfo mInfo;
private CheckLongPressHelper mLongPressHelper;
private StylusEventHelper mStylusEventHelper;
static final int DROP_IN_ANIMATION_DURATION = 400;
@@ -110,8 +106,6 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel
boolean mAnimating = false;
private float mSlop;
private Alarm mOpenAlarm = new Alarm();
private boolean mForceHideDot;
@@ -149,9 +143,7 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel
private void init() {
mLongPressHelper = new CheckLongPressHelper(this);
mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this);
mPreviewLayoutRule = new ClippedFolderIconLayoutRule();
mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop();
mPreviewItemManager = new PreviewItemManager(this);
mDotParams = new DotRenderer.DrawParams();
}
@@ -663,29 +655,10 @@ public class FolderIcon extends FrameLayout implements FolderListener, IconLabel
public boolean onTouchEvent(MotionEvent event) {
// Call the superclass onTouchEvent first, because sometimes it changes the state to
// isPressed() on an ACTION_UP
boolean result = super.onTouchEvent(event);
// Check for a stylus button press, if it occurs cancel any long press checks.
if (mStylusEventHelper.onMotionEvent(event)) {
mLongPressHelper.cancelLongPress();
return true;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mLongPressHelper.postCheckForLongPress();
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
mLongPressHelper.cancelLongPress();
break;
case MotionEvent.ACTION_MOVE:
if (!Utilities.pointInView(this, event.getX(), event.getY(), mSlop)) {
mLongPressHelper.cancelLongPress();
}
break;
}
return result;
super.onTouchEvent(event);
mLongPressHelper.onTouchEvent(event);
// Keep receiving the rest of the events
return true;
}
@Override
@@ -21,7 +21,6 @@ import static android.view.View.VISIBLE;
import static com.android.launcher3.config.FeatureFlags.ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER;
import static com.android.launcher3.config.FeatureFlags.MULTI_DB_GRID_MIRATION_ALGO;
import static com.android.launcher3.model.GridSizeMigrationTask.needsToMigrate;
import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems;
import static com.android.launcher3.model.ModelUtils.sortWorkspaceItemsSpatially;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
@@ -397,7 +396,10 @@ public class LauncherPreviewRenderer implements Callable<Bitmap> {
private void populate() {
if (ENABLE_LAUNCHER_PREVIEW_IN_GRID_PICKER.get()) {
boolean needsToMigrate = needsToMigrate(mContext, mIdp);
boolean needsToMigrate =
MULTI_DB_GRID_MIRATION_ALGO.get()
? GridSizeMigrationTaskV2.needsToMigrate(mContext, mIdp)
: GridSizeMigrationTask.needsToMigrate(mContext, mIdp);
boolean success = false;
if (needsToMigrate) {
success = MULTI_DB_GRID_MIRATION_ALGO.get()
@@ -1,169 +0,0 @@
/*
* Copyright (C) 2017 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.logging;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT;
import android.content.ComponentName;
import android.os.Process;
import android.text.TextUtils;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppWidgetInfo;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.model.nano.LauncherDumpProto;
import com.android.launcher3.model.nano.LauncherDumpProto.ContainerType;
import com.android.launcher3.model.nano.LauncherDumpProto.DumpTarget;
import com.android.launcher3.model.nano.LauncherDumpProto.ItemType;
import com.android.launcher3.model.nano.LauncherDumpProto.UserType;
import com.android.launcher3.util.ShortcutUtil;
import java.util.ArrayList;
import java.util.List;
/**
* This class can be used when proto definition doesn't support nesting.
*/
public class DumpTargetWrapper {
DumpTarget node;
ArrayList<DumpTargetWrapper> children;
public DumpTargetWrapper() {
children = new ArrayList<>();
}
public DumpTargetWrapper(int containerType, int id) {
this();
node = newContainerTarget(containerType, id);
}
public DumpTargetWrapper(ItemInfo info) {
this();
node = newItemTarget(info);
}
public DumpTarget getDumpTarget() {
return node;
}
public void add(DumpTargetWrapper child) {
children.add(child);
}
public List<DumpTarget> getFlattenedList() {
ArrayList<DumpTarget> list = new ArrayList<>();
list.add(node);
if (!children.isEmpty()) {
for(DumpTargetWrapper t: children) {
list.addAll(t.getFlattenedList());
}
list.add(node); // add a delimiter empty object
}
return list;
}
public DumpTarget newItemTarget(ItemInfo info) {
DumpTarget dt = new DumpTarget();
dt.type = DumpTarget.Type.ITEM;
if (info == null) {
return dt;
}
switch (info.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_APPLICATION:
dt.itemType = ItemType.APP_ICON;
break;
case LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET:
dt.itemType = ItemType.WIDGET;
break;
case ITEM_TYPE_DEEP_SHORTCUT:
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
dt.itemType = ItemType.SHORTCUT;
break;
default:
dt.itemType = ItemType.UNKNOWN_ITEMTYPE;
break;
}
return dt;
}
public DumpTarget newContainerTarget(int type, int id) {
DumpTarget dt = new DumpTarget();
dt.type = DumpTarget.Type.CONTAINER;
dt.containerType = type;
dt.pageId = id;
return dt;
}
public static String getDumpTargetStr(DumpTarget t) {
if (t == null){
return "";
}
switch (t.type) {
case LauncherDumpProto.DumpTarget.Type.ITEM:
return getItemStr(t);
case LauncherDumpProto.DumpTarget.Type.CONTAINER:
String str = LoggerUtils.getFieldName(t.containerType, ContainerType.class);
if (t.containerType == ContainerType.WORKSPACE) {
str += " id=" + t.pageId;
} else if (t.containerType == ContainerType.FOLDER) {
str += " grid(" + t.gridX + "," + t.gridY+ ")";
}
return str;
default:
return "UNKNOWN TARGET TYPE";
}
}
private static String getItemStr(DumpTarget t) {
if (t == null) {
return "";
}
String typeStr = LoggerUtils.getFieldName(t.itemType, ItemType.class);
if (!TextUtils.isEmpty(t.packageName)) {
typeStr += ", package=" + t.packageName;
}
if (!TextUtils.isEmpty(t.component)) {
typeStr += ", component=" + t.component;
}
return typeStr + ", grid(" + t.gridX + "," + t.gridY + "), span(" + t.spanX + "," + t.spanY
+ "), pageIdx=" + t.pageId + " user=" + t.userType;
}
public DumpTarget writeToDumpTarget(ItemInfo info) {
if (info == null) {
return node;
}
if (ShortcutUtil.isDeepShortcut(info)) {
node.component = ((WorkspaceItemInfo) info).getDeepShortcutId();
} else {
ComponentName cmp = info.getTargetComponent();
node.component = cmp == null ? "" : cmp.flattenToString();
}
node.packageName = info.getTargetComponent() == null? "":
info.getTargetComponent().getPackageName();
if (info instanceof LauncherAppWidgetInfo) {
node.component = ((LauncherAppWidgetInfo) info).providerName.flattenToString();
node.packageName = ((LauncherAppWidgetInfo) info).providerName.getPackageName();
}
node.gridX = info.cellX;
node.gridY = info.cellY;
node.spanX = info.spanX;
node.spanY = info.spanY;
node.userType = (info.user.equals(Process.myUserHandle()))? UserType.DEFAULT : UserType.WORK;
return node;
}
}
@@ -0,0 +1,30 @@
/*
* 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.logging;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.RetentionPolicy.SOURCE;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
@Retention(SOURCE)
@Target(FIELD)
public @interface LauncherUiEvent {
/** An explanation, suitable for Android analysts, of the UI event that this log represents. */
String doc();
}
@@ -16,23 +16,44 @@
package com.android.launcher3.logging;
import android.content.Context;
import android.content.Intent;
import android.os.UserHandle;
import android.view.View;
import androidx.annotation.Nullable;
import com.android.launcher3.R;
import com.android.launcher3.logger.LauncherAtom;
import com.android.launcher3.logger.LauncherAtom.ItemInfo;
import com.android.launcher3.logging.StatsLogUtils.LogStateProvider;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.ResourceBasedOverride;
/**
* Handles the user event logging in Q.
* Handles the user event logging in R+.
*/
public class StatsLogManager implements ResourceBasedOverride {
interface EventEnum {
int getId();
}
public enum LauncherEvent implements EventEnum {
@LauncherUiEvent(doc = "App launched from workspace, hotseat or folder in launcher")
APP_LAUNCH_TAP(1),
@LauncherUiEvent(doc = "Task launched from overview using TAP")
TASK_LAUNCH_TAP(2),
@LauncherUiEvent(doc = "Task launched from overview using SWIPE DOWN")
TASK_LAUNCH_SWIPE_DOWN(2),
@LauncherUiEvent(doc = "TASK dismissed from overview using SWIPE UP")
TASK_DISMISS_SWIPE_UP(3);
// ADD MORE
private final int mId;
LauncherEvent(int id) {
mId = id;
}
public int getId() {
return mId;
}
}
protected LogStateProvider mStateProvider;
public static StatsLogManager newInstance(Context context, LogStateProvider stateProvider) {
StatsLogManager mgr = Overrides.getObject(StatsLogManager.class,
context.getApplicationContext(), R.string.stats_log_manager_class);
@@ -42,11 +63,14 @@ public class StatsLogManager implements ResourceBasedOverride {
}
/**
* Logs app launches
* Logs an event and accompanying {@link ItemInfo}
*/
public void logAppLaunch(View v, Intent intent, @Nullable UserHandle userHandle) { }
public void logTaskLaunch(View v, ComponentKey key) { }
public void logTaskDismiss(View v, ComponentKey key) { }
public void logSwipeOnContainer(boolean isSwipingToLeft, int pageId) { }
public void log(LauncherEvent eventId, LauncherAtom.ItemInfo itemInfo) { }
/**
* Logs snapshot, or impression of the current workspace.
*/
public void logSnapshot() { }
public void verify() {} // TODO: should move into robo tests
}
@@ -36,10 +36,6 @@ import com.android.launcher3.PromiseAppInfo;
import com.android.launcher3.Workspace;
import com.android.launcher3.WorkspaceItemInfo;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.logging.DumpTargetWrapper;
import com.android.launcher3.model.nano.LauncherDumpProto;
import com.android.launcher3.model.nano.LauncherDumpProto.ContainerType;
import com.android.launcher3.model.nano.LauncherDumpProto.DumpTarget;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.shortcuts.ShortcutRequest;
import com.android.launcher3.util.ComponentKey;
@@ -50,11 +46,7 @@ import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.util.ViewOnDrawExecutor;
import com.android.launcher3.widget.WidgetListRowEntry;
import com.google.protobuf.nano.MessageNano;
import java.io.FileDescriptor;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Arrays;
@@ -150,10 +142,6 @@ public class BgDataModel {
public synchronized void dump(String prefix, FileDescriptor fd, PrintWriter writer,
String[] args) {
if (Arrays.asList(args).contains("--proto")) {
dumpProto(prefix, fd, writer, args);
return;
}
writer.println(prefix + "Data Model:");
writer.println(prefix + " ---- workspace items ");
for (int i = 0; i < workspaceItems.size(); i++) {
@@ -181,89 +169,6 @@ public class BgDataModel {
}
}
private synchronized void dumpProto(String prefix, FileDescriptor fd, PrintWriter writer,
String[] args) {
// Add top parent nodes. (L1)
DumpTargetWrapper hotseat = new DumpTargetWrapper(ContainerType.HOTSEAT, 0);
IntSparseArrayMap<DumpTargetWrapper> workspaces = new IntSparseArrayMap<>();
IntArray workspaceScreens = collectWorkspaceScreens();
for (int i = 0; i < workspaceScreens.size(); i++) {
workspaces.put(workspaceScreens.get(i),
new DumpTargetWrapper(ContainerType.WORKSPACE, i));
}
DumpTargetWrapper dtw;
// Add non leaf / non top nodes (L2)
for (int i = 0; i < folders.size(); i++) {
FolderInfo fInfo = folders.valueAt(i);
dtw = new DumpTargetWrapper(ContainerType.FOLDER, folders.size());
dtw.writeToDumpTarget(fInfo);
for(WorkspaceItemInfo sInfo: fInfo.contents) {
DumpTargetWrapper child = new DumpTargetWrapper(sInfo);
child.writeToDumpTarget(sInfo);
dtw.add(child);
}
if (fInfo.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
hotseat.add(dtw);
} else if (fInfo.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
workspaces.get(fInfo.screenId).add(dtw);
}
}
// Add leaf nodes (L3): *Info
for (int i = 0; i < workspaceItems.size(); i++) {
ItemInfo info = workspaceItems.get(i);
if (info instanceof FolderInfo) {
continue;
}
dtw = new DumpTargetWrapper(info);
dtw.writeToDumpTarget(info);
if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
hotseat.add(dtw);
} else if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
workspaces.get(info.screenId).add(dtw);
}
}
for (int i = 0; i < appWidgets.size(); i++) {
ItemInfo info = appWidgets.get(i);
dtw = new DumpTargetWrapper(info);
dtw.writeToDumpTarget(info);
if (info.container == LauncherSettings.Favorites.CONTAINER_HOTSEAT) {
hotseat.add(dtw);
} else if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
workspaces.get(info.screenId).add(dtw);
}
}
// Traverse target wrapper
ArrayList<DumpTarget> targetList = new ArrayList<>();
targetList.addAll(hotseat.getFlattenedList());
for (int i = 0; i < workspaces.size(); i++) {
targetList.addAll(workspaces.valueAt(i).getFlattenedList());
}
if (Arrays.asList(args).contains("--debug")) {
for (int i = 0; i < targetList.size(); i++) {
writer.println(prefix + DumpTargetWrapper.getDumpTargetStr(targetList.get(i)));
}
return;
} else {
LauncherDumpProto.LauncherImpression proto = new LauncherDumpProto.LauncherImpression();
proto.targets = new DumpTarget[targetList.size()];
for (int i = 0; i < targetList.size(); i++) {
proto.targets[i] = targetList.get(i);
}
FileOutputStream fos = new FileOutputStream(fd);
try {
fos.write(MessageNano.toByteArray(proto));
Log.d(TAG, MessageNano.toByteArray(proto).length + "Bytes");
} catch (IOException e) {
Log.e(TAG, "Exception writing dumpsys --proto", e);
}
}
}
public synchronized void removeItem(Context context, ItemInfo... items) {
removeItem(context, Arrays.asList(items));
}
@@ -123,8 +123,16 @@ public class GridSizeMigrationTaskV2 {
}
/**
* Run the migration algorithm if needed. For preview, we provide the intended idp because it
* has not been changed. If idp is null, we read it from the context, for actual grid migration.
* When migrating the grid for preview, we copy the table
* {@link LauncherSettings.Favorites.TABLE_NAME} into
* {@link LauncherSettings.Favorites.PREVIEW_TABLE_NAME}, run grid size migration from the
* former to the later, then use the later table for preview.
*
* Similarly when doing the actual grid migration, the former grid option's table
* {@link LauncherSettings.Favorites.TABLE_NAME} is copied into the new grid option's
* {@link LauncherSettings.Favorites.TMP_TABLE}, we then run the grid size migration algorithm
* to migrate the later to the former, and load the workspace from the default
* {@link LauncherSettings.Favorites.TABLE_NAME}.
*
* @return false if the migration failed.
*/
@@ -151,7 +159,14 @@ public class GridSizeMigrationTaskV2 {
HashSet<String> validPackages = getValidPackages(context);
int srcHotseatCount = prefs.getInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numHotseatIcons);
if (!LauncherSettings.Settings.call(
if (migrateForPreview) {
if (!LauncherSettings.Settings.call(
context.getContentResolver(),
LauncherSettings.Settings.METHOD_PREP_FOR_PREVIEW, idp.dbFile).getBoolean(
LauncherSettings.Settings.EXTRA_VALUE)) {
return false;
}
} else if (!LauncherSettings.Settings.call(
context.getContentResolver(),
LauncherSettings.Settings.METHOD_UPDATE_CURRENT_OPEN_HELPER).getBoolean(
LauncherSettings.Settings.EXTRA_VALUE)) {
@@ -164,9 +179,13 @@ public class GridSizeMigrationTaskV2 {
LauncherSettings.Settings.METHOD_NEW_TRANSACTION).getBinder(
LauncherSettings.Settings.EXTRA_VALUE)) {
DbReader srcReader = new DbReader(t.getDb(), LauncherSettings.Favorites.TMP_TABLE,
DbReader srcReader = new DbReader(t.getDb(),
migrateForPreview ? LauncherSettings.Favorites.TABLE_NAME
: LauncherSettings.Favorites.TMP_TABLE,
context, validPackages, srcHotseatCount);
DbReader destReader = new DbReader(t.getDb(), LauncherSettings.Favorites.TABLE_NAME,
DbReader destReader = new DbReader(t.getDb(),
migrateForPreview ? LauncherSettings.Favorites.PREVIEW_TABLE_NAME
: LauncherSettings.Favorites.TABLE_NAME,
context, validPackages, idp.numHotseatIcons);
Point targetSize = new Point(idp.numColumns, idp.numRows);
@@ -174,7 +193,9 @@ public class GridSizeMigrationTaskV2 {
srcReader, destReader, idp.numHotseatIcons, targetSize);
task.migrate();
dropTable(t.getDb(), LauncherSettings.Favorites.TMP_TABLE);
if (!migrateForPreview) {
dropTable(t.getDb(), LauncherSettings.Favorites.TMP_TABLE);
}
t.commit();
return true;
@@ -186,11 +207,13 @@ public class GridSizeMigrationTaskV2 {
Log.v(TAG, "Workspace migration completed in "
+ (System.currentTimeMillis() - migrationStartTime));
// Save current configuration, so that the migration does not run again.
prefs.edit()
.putString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, gridSizeString)
.putInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numHotseatIcons)
.apply();
if (!migrateForPreview) {
// Save current configuration, so that the migration does not run again.
prefs.edit()
.putString(KEY_MIGRATION_SRC_WORKSPACE_SIZE, gridSizeString)
.putInt(KEY_MIGRATION_SRC_HOTSEAT_COUNT, idp.numHotseatIcons)
.apply();
}
}
}
@@ -202,7 +225,7 @@ public class GridSizeMigrationTaskV2 {
// Migrate hotseat
HotseatPlacementSolution hotseatSolution = new HotseatPlacementSolution(mDb, mSrcReader,
mContext, mDestHotseatSize, mHotseatItems, mHotseatDiff);
mDestReader, mContext, mDestHotseatSize, mHotseatItems, mHotseatDiff);
hotseatSolution.find();
// Sort the items by the reading order.
@@ -215,7 +238,7 @@ public class GridSizeMigrationTaskV2 {
}
List<DbEntry> entries = mDestReader.loadWorkspaceEntries(screenId);
GridPlacementSolution workspaceSolution = new GridPlacementSolution(mDb, mSrcReader,
mContext, entries, screenId, mTrgX, mTrgY, mWorkspaceDiff);
mDestReader, mContext, entries, screenId, mTrgX, mTrgY, mWorkspaceDiff);
workspaceSolution.find();
if (mWorkspaceDiff.isEmpty()) {
break;
@@ -225,7 +248,8 @@ public class GridSizeMigrationTaskV2 {
int screenId = mDestReader.mLastScreenId + 1;
while (!mWorkspaceDiff.isEmpty()) {
GridPlacementSolution workspaceSolution = new GridPlacementSolution(mDb, mSrcReader,
mContext, new ArrayList<>(), screenId, mTrgX, mTrgY, mWorkspaceDiff);
mDestReader, mContext, new ArrayList<>(), screenId, mTrgX, mTrgY,
mWorkspaceDiff);
workspaceSolution.find();
screenId++;
}
@@ -246,7 +270,8 @@ public class GridSizeMigrationTaskV2 {
}
private static void insertEntryInDb(SQLiteDatabase db, Context context,
ArrayList<DbEntry> entriesFromSrcDb, DbEntry entry) {
ArrayList<DbEntry> entriesFromSrcDb, DbEntry entry, String srcTableName,
String destTableName) {
int id = -1;
switch (entry.itemType) {
case LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT:
@@ -283,8 +308,8 @@ public class GridSizeMigrationTaskV2 {
return;
}
Cursor c = db.query(LauncherSettings.Favorites.TMP_TABLE, null,
LauncherSettings.Favorites._ID + " = '" + id + "'", null, null, null, null);
Cursor c = db.query(srcTableName, null, LauncherSettings.Favorites._ID + " = '" + id + "'",
null, null, null, null);
while (c.moveToNext()) {
ContentValues values = new ContentValues();
@@ -294,14 +319,14 @@ public class GridSizeMigrationTaskV2 {
LauncherSettings.Settings.call(context.getContentResolver(),
LauncherSettings.Settings.METHOD_NEW_ITEM_ID).getInt(
LauncherSettings.Settings.EXTRA_VALUE));
db.insert(LauncherSettings.Favorites.TABLE_NAME, null, values);
db.insert(destTableName, null, values);
}
c.close();
}
private static void removeEntryFromDb(SQLiteDatabase db, IntArray entryId) {
db.delete(LauncherSettings.Favorites.TABLE_NAME, Utilities.createDbSelectionQuery(
LauncherSettings.Favorites._ID, entryId), null);
private static void removeEntryFromDb(SQLiteDatabase db, String tableName, IntArray entryId) {
db.delete(tableName,
Utilities.createDbSelectionQuery(LauncherSettings.Favorites._ID, entryId), null);
}
private static HashSet<String> getValidPackages(Context context) {
@@ -325,6 +350,7 @@ public class GridSizeMigrationTaskV2 {
private final SQLiteDatabase mDb;
private final DbReader mSrcReader;
private final DbReader mDestReader;
private final Context mContext;
private final GridOccupancy mOccupied;
private final int mScreenId;
@@ -335,11 +361,12 @@ public class GridSizeMigrationTaskV2 {
private int mNextStartX;
private int mNextStartY;
GridPlacementSolution(SQLiteDatabase db, DbReader srcReader, Context context,
List<DbEntry> placedWorkspaceItems, int screenId, int trgX,
GridPlacementSolution(SQLiteDatabase db, DbReader srcReader, DbReader destReader,
Context context, List<DbEntry> placedWorkspaceItems, int screenId, int trgX,
int trgY, List<DbEntry> itemsToPlace) {
mDb = db;
mSrcReader = srcReader;
mDestReader = destReader;
mContext = context;
mOccupied = new GridOccupancy(trgX, trgY);
mScreenId = screenId;
@@ -362,7 +389,8 @@ public class GridSizeMigrationTaskV2 {
continue;
}
if (findPlacement(entry)) {
insertEntryInDb(mDb, mContext, mSrcReader.mWorkspaceEntries, entry);
insertEntryInDb(mDb, mContext, mSrcReader.mWorkspaceEntries, entry,
mSrcReader.mTableName, mDestReader.mTableName);
iterator.remove();
}
}
@@ -397,14 +425,17 @@ public class GridSizeMigrationTaskV2 {
private final SQLiteDatabase mDb;
private final DbReader mSrcReader;
private final DbReader mDestReader;
private final Context mContext;
private final HotseatOccupancy mOccupied;
private final List<DbEntry> mItemsToPlace;
HotseatPlacementSolution(SQLiteDatabase db, DbReader srcReader, Context context,
int hotseatSize, List<DbEntry> placedHotseatItems, List<DbEntry> itemsToPlace) {
HotseatPlacementSolution(SQLiteDatabase db, DbReader srcReader, DbReader destReader,
Context context, int hotseatSize, List<DbEntry> placedHotseatItems,
List<DbEntry> itemsToPlace) {
mDb = db;
mSrcReader = srcReader;
mDestReader = destReader;
mContext = context;
mOccupied = new HotseatOccupancy(hotseatSize);
for (DbEntry entry : placedHotseatItems) {
@@ -422,7 +453,8 @@ public class GridSizeMigrationTaskV2 {
// to something other than -1.
entry.cellX = i;
entry.cellY = 0;
insertEntryInDb(mDb, mContext, mSrcReader.mHotseatEntries, entry);
insertEntryInDb(mDb, mContext, mSrcReader.mHotseatEntries, entry,
mSrcReader.mTableName, mDestReader.mTableName);
mOccupied.markCells(entry, true);
}
}
@@ -519,7 +551,7 @@ public class GridSizeMigrationTaskV2 {
}
mHotseatEntries.add(entry);
}
removeEntryFromDb(mDb, entriesToRemove);
removeEntryFromDb(mDb, mTableName, entriesToRemove);
c.close();
return mHotseatEntries;
}
@@ -639,7 +671,7 @@ public class GridSizeMigrationTaskV2 {
}
mWorkspaceEntries.add(entry);
}
removeEntryFromDb(mDb, entriesToRemove);
removeEntryFromDb(mDb, mTableName, entriesToRemove);
c.close();
return mWorkspaceEntries;
}
@@ -657,7 +689,7 @@ public class GridSizeMigrationTaskV2 {
total++;
entry.mFolderItems.add(intent);
} catch (Exception e) {
removeEntryFromDb(mDb, IntArray.wrap(c.getInt(0)));
removeEntryFromDb(mDb, mTableName, IntArray.wrap(c.getInt(0)));
}
}
c.close();
@@ -35,6 +35,8 @@ import android.text.TextUtils;
import android.util.Log;
import android.util.LongSparseArray;
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.AppInfo;
import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.ItemInfo;
@@ -150,8 +152,10 @@ public class LoaderCursor extends CursorWrapper {
}
}
@VisibleForTesting
public WorkspaceItemInfo loadSimpleWorkspaceItem() {
final WorkspaceItemInfo info = new WorkspaceItemInfo();
info.intent = new Intent();
// Non-app shortcuts are only supported for current user.
info.user = user;
info.itemType = itemType;
+1 -1
View File
@@ -95,7 +95,7 @@ public class UserCache {
private void removeUserChangeListener(Runnable command) {
synchronized (this) {
mUserChangeListeners.add(command);
mUserChangeListeners.remove(command);
if (mUserChangeListeners.isEmpty()) {
// Disable cache and stop listening
mContext.unregisterReceiver(mUserChangeReceiver);
@@ -129,6 +129,7 @@ public class LauncherDbUtils {
toDb.execSQL("ATTACH DATABASE '" + fromDb.getPath() + "' AS from_db");
toDb.execSQL(
"INSERT INTO " + toTable + " SELECT * FROM from_db." + fromTable);
toDb.execSQL("DETACH DATABASE 'from_db'");
} else {
toDb.execSQL("INSERT INTO " + toTable + " SELECT * FROM " + fromTable);
}
@@ -15,10 +15,12 @@
*/
package com.android.launcher3.states;
import static android.Manifest.permission.WRITE_SECURE_SETTINGS;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_LOCKED;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE;
import static com.android.launcher3.config.FeatureFlags.FLAG_ENABLE_FIXED_ROTATION_TRANSFORM;
@@ -37,7 +39,6 @@ import android.view.Surface;
import android.view.WindowManager;
import com.android.launcher3.Launcher;
import com.android.launcher3.PagedView;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
@@ -142,9 +143,12 @@ public class RotationHelper implements OnSharedPreferenceChangeListener {
if (setValueFromPrefs) {
mForcedRotation = isForcedRotation;
}
UI_HELPER_EXECUTOR.execute(
() -> Settings.Global.putInt(mContentResolver, FIXED_ROTATION_TRANSFORM_SETTING_NAME,
mForcedRotation ? 1 : 0));
UI_HELPER_EXECUTOR.execute(() -> {
if (mLauncher.checkSelfPermission(WRITE_SECURE_SETTINGS) == PERMISSION_GRANTED) {
Settings.Global.putInt(mContentResolver, FIXED_ROTATION_TRANSFORM_SETTING_NAME,
mForcedRotation ? 1 : 0);
}
});
for (ForcedRotationChangedListener listener : mForcedRotationChangedListeners) {
listener.onForcedRotationChanged(mForcedRotation);
}
@@ -17,6 +17,7 @@ package com.android.launcher3.states;
import static com.android.launcher3.states.RotationHelper.REQUEST_LOCK;
import android.content.Context;
import android.graphics.Rect;
import com.android.launcher3.DeviceProfile;
@@ -76,6 +77,11 @@ public class SpringLoadedState extends LauncherState {
return new ScaleAndTranslation(scale, 0, (desiredCellTop - actualCellTop) / scale);
}
@Override
public float getDepth(Context context) {
return 0.5f;
}
@Override
public ScaleAndTranslation getHotseatScaleAndTranslation(Launcher launcher) {
return new ScaleAndTranslation(1, 0, 0);
@@ -35,6 +35,7 @@ public final class TestProtocol {
public static final String TAPL_EVENTS_TAG = "TaplEvents";
public static final String SEQUENCE_MAIN = "Main";
public static final String SEQUENCE_TIS = "TIS";
public static final String SEQUENCE_PILFER = "Pilfer";
public static String stateOrdinalToString(int ordinal) {
switch (ordinal) {
@@ -95,4 +96,6 @@ public final class TestProtocol {
public static final String NO_BACKGROUND_TO_OVERVIEW_TAG = "b/138251824";
public static final String APP_NOT_DISABLED = "b/139891609";
public static final String NO_SCROLL_END_WIDGETS = "b/152354290";
public static final String NO_START_FROM_RECENTS = "b/152658211";
}

Some files were not shown because too many files have changed in this diff Show More