Merging ub-launcher3-qt-dev, build 5456758

Test: Manual

Bug:111142970 Icon display blur in all apps page
Bug:114136250 Have a more spartan RecentsActivity on android go
Bug:122609330 Update PIP animation for swiping up to home
Bug:123985787 Respect dialogCornerRadius for all apps / widgets / settings / shortcut menu / dwb / task / overview (everything)
Bug:126587956 Create an app to use with Launcher testing
Bug:126606858 In multiwindow mode, not able to switch between recent apps.
Bug:127766994 Gray app icons disappeared after completing cable transfer for a while
Bug:127987071 Create a proper contract for specifying default Launcher Layout
Bug:129033091 [Q-Preview] I would like the ability to swipe up anywhere on my screen to open up the app li.
Bug:129297464 [Gesture Nav] Exclude edges from most Launcher / Overview states.
Bug:129434166 Lab-only flake: drag to workspace doesn't happen
Bug:129746879 [Q-Preview] Quick search bar overlaying other app
Bug:129874298 Show different string (Wallpaper vs Style & Wallpaper) on Settings depending on device type
Bug:129947426 nexus launcher crash observed randomly during device boot up(NPE:Attempt to invoke virtual method 'android.app.ActivityManager$RunningTaskInfo com.android.systemui.shared.system.ActivityManagerWrapper.getRunningTask(int)' on a null object reference)
Bug:129976669 Implement returning to home from Widgets in 0-button mode Bug:130027168 Can't tap in nav region on home screen
Bug:130151609 Some tests in MultiDisplaySystemDecorationTests.java failing in pre-submit
Bug:130182878 Pixel launcher crashes on secondary display
Bug:130225926 Cannot unpin app while in gesture nav
Bug:130245920 Icons disappear from launcher when selected
Bug:130272454 [C1/B1] Message app crash when opening a video MMS.
Change-Id: I18aa35d2c75deaf5149358d96d4e1d7f26de2f02
This commit is contained in:
Hyunyoung Song
2019-04-10 16:39:25 -07:00
72 changed files with 1048 additions and 297 deletions
@@ -42,6 +42,9 @@ public final class TaskActionController {
* @param viewHolder the task view holder to launch
*/
public void launchTask(TaskHolder viewHolder) {
if (viewHolder.getTask() == null) {
return;
}
TaskItemView itemView = (TaskItemView) (viewHolder.itemView);
View v = itemView.getThumbnailView();
int left = 0;
@@ -60,6 +63,9 @@ public final class TaskActionController {
* @param viewHolder the task view holder to remove
*/
public void removeTask(TaskHolder viewHolder) {
if (viewHolder.getTask() == null) {
return;
}
int position = viewHolder.getAdapterPosition();
Task task = viewHolder.getTask();
ActivityManagerWrapper.getInstance().removeTask(task.key.id);
@@ -28,6 +28,7 @@ import com.android.quickstep.views.TaskItemView;
import com.android.systemui.shared.recents.model.Task;
import java.util.List;
import java.util.Objects;
/**
* Recycler view adapter that dynamically inflates and binds {@link TaskHolder} instances with the
@@ -40,6 +41,7 @@ public final class TaskAdapter extends Adapter<TaskHolder> {
private final TaskListLoader mLoader;
private final ArrayMap<Integer, TaskItemView> mTaskIdToViewMap = new ArrayMap<>();
private TaskActionController mTaskActionController;
private boolean mIsShowingLoadingUi;
public TaskAdapter(@NonNull TaskListLoader loader) {
mLoader = loader;
@@ -49,6 +51,18 @@ public final class TaskAdapter extends Adapter<TaskHolder> {
mTaskActionController = taskActionController;
}
/**
* Sets all positions in the task adapter to loading views, binding new views if necessary.
* This changes the task adapter's view of the data, so the appropriate notify events should be
* called in addition to this method to reflect the changes.
*
* @param isShowingLoadingUi true to bind loading task views to all positions, false to return
* to the real data
*/
public void setIsShowingLoadingUi(boolean isShowingLoadingUi) {
mIsShowingLoadingUi = isShowingLoadingUi;
}
/**
* Get task item view for a given task id if it's attached to the view.
*
@@ -70,6 +84,10 @@ public final class TaskAdapter extends Adapter<TaskHolder> {
@Override
public void onBindViewHolder(TaskHolder holder, int position) {
if (mIsShowingLoadingUi) {
holder.bindEmptyUi();
return;
}
List<Task> tasks = mLoader.getCurrentTaskList();
if (position >= tasks.size()) {
// Task list has updated.
@@ -79,13 +97,13 @@ public final class TaskAdapter extends Adapter<TaskHolder> {
holder.bindTask(task);
mLoader.loadTaskIconAndLabel(task, () -> {
// Ensure holder still has the same task.
if (task.equals(holder.getTask())) {
if (Objects.equals(task, holder.getTask())) {
holder.getTaskItemView().setIcon(task.icon);
holder.getTaskItemView().setLabel(task.titleDescription);
}
});
mLoader.loadTaskThumbnail(task, () -> {
if (task.equals(holder.getTask())) {
if (Objects.equals(task, holder.getTask())) {
holder.getTaskItemView().setThumbnail(task.thumbnail.thumbnail);
}
});
@@ -93,16 +111,27 @@ public final class TaskAdapter extends Adapter<TaskHolder> {
@Override
public void onViewAttachedToWindow(@NonNull TaskHolder holder) {
if (holder.getTask() == null) {
return;
}
mTaskIdToViewMap.put(holder.getTask().key.id, (TaskItemView) holder.itemView);
}
@Override
public void onViewDetachedFromWindow(@NonNull TaskHolder holder) {
if (holder.getTask() == null) {
return;
}
mTaskIdToViewMap.remove(holder.getTask().key.id);
}
@Override
public int getItemCount() {
return Math.min(mLoader.getCurrentTaskList().size(), MAX_TASKS_TO_DISPLAY);
if (mIsShowingLoadingUi) {
// Show loading version of all items.
return MAX_TASKS_TO_DISPLAY;
} else {
return Math.min(mLoader.getCurrentTaskList().size(), MAX_TASKS_TO_DISPLAY);
}
}
}
@@ -15,7 +15,7 @@
*/
package com.android.quickstep;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
import com.android.quickstep.views.TaskItemView;
@@ -50,11 +50,23 @@ public final class TaskHolder extends ViewHolder {
}
/**
* Gets the task currently bound to this view
* Bind a generic empty UI to the holder to make it clear that the item is loading/unbound and
* should not be expected to react to user input.
*/
public void bindEmptyUi() {
mTask = null;
// TODO: Set the task view to a loading, empty UI.
// Temporarily using the one below for visual confirmation but should be swapped out to new
// UI later.
mTaskItemView.resetTaskItemView();
}
/**
* Gets the task currently bound to this view. May be null if task holder is in a loading state.
*
* @return the current task
*/
public @NonNull Task getTask() {
public @Nullable Task getTask() {
return mTask;
}
}
@@ -67,17 +67,27 @@ public final class TaskListLoader {
return Collections.unmodifiableList(mTaskList);
}
/**
* Whether or not the loader needs to load data to be up to date. This can return true if the
* task list is already up to date OR there is already a load in progress for the task list to
* become up to date.
*
* @return true if already up to date or load in progress, false otherwise
*/
public boolean needsToLoad() {
return !mRecentsModel.isTaskListValid(mTaskListChangeId);
}
/**
* Fetches the most recent tasks and updates the task list asynchronously. This call does not
* provide guarantees the task content (icon, thumbnail, label) are loaded but will fill in
* what it has. May run the callback immediately if there have been no changes in the task
* list.
* list since the start of the last load.
*
* @param onLoadedCallback callback to run when task list is loaded
*/
public void loadTaskList(@Nullable Consumer<ArrayList<Task>> onLoadedCallback) {
if (mRecentsModel.isTaskListValid(mTaskListChangeId)) {
// Current task list is already up to date. No need to update.
if (!needsToLoad()) {
if (onLoadedCallback != null) {
onLoadedCallback.accept(mTaskList);
}
@@ -49,7 +49,6 @@ public class TouchInteractionService extends Service {
ISystemUiProxy iSystemUiProxy = ISystemUiProxy.Stub
.asInterface(bundle.getBinder(KEY_EXTRA_SYSUI_PROXY));
mRecentsModel.setSystemUiProxy(iSystemUiProxy);
mRecentsModel.onInitializeSystemUI(bundle);
}
@Override
@@ -28,6 +28,10 @@ import android.util.AttributeSet;
import android.util.FloatProperty;
import android.view.View;
import android.view.ViewDebug;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.AnimationSet;
import android.view.animation.LayoutAnimationController;
import android.widget.FrameLayout;
import androidx.annotation.NonNull;
@@ -69,6 +73,8 @@ public final class IconRecentsView extends FrameLayout {
}
};
private static final long CROSSFADE_DURATION = 300;
private static final long LAYOUT_ITEM_ANIMATE_IN_DURATION = 150;
private static final long LAYOUT_ITEM_ANIMATE_IN_DELAY_BETWEEN = 40;
private static final long ITEM_ANIMATE_OUT_DURATION = 150;
private static final long ITEM_ANIMATE_OUT_DELAY_BETWEEN = 40;
private static final float ITEM_ANIMATE_OUT_TRANSLATION_X_RATIO = .25f;
@@ -84,6 +90,7 @@ public final class IconRecentsView extends FrameLayout {
private final TaskListLoader mTaskLoader;
private final TaskAdapter mTaskAdapter;
private final TaskActionController mTaskActionController;
private final LayoutAnimationController mLayoutAnimation;
private RecentsToActivityHelper mActivityHelper;
private RecyclerView mTaskRecyclerView;
@@ -99,6 +106,7 @@ public final class IconRecentsView extends FrameLayout {
mTaskAdapter = new TaskAdapter(mTaskLoader);
mTaskActionController = new TaskActionController(mTaskLoader, mTaskAdapter);
mTaskAdapter.setActionController(mTaskActionController);
mLayoutAnimation = createLayoutAnimation();
}
@Override
@@ -112,6 +120,7 @@ public final class IconRecentsView extends FrameLayout {
ItemTouchHelper helper = new ItemTouchHelper(
new TaskSwipeCallback(mTaskActionController));
helper.attachToRecyclerView(mTaskRecyclerView);
mTaskRecyclerView.setLayoutAnimation(mLayoutAnimation);
mEmptyView = findViewById(R.id.recent_task_empty_view);
mContentView = findViewById(R.id.recent_task_content_view);
@@ -131,7 +140,6 @@ public final class IconRecentsView extends FrameLayout {
}
}
@Override
public void setEnabled(boolean enabled) {
super.setEnabled(enabled);
@@ -157,10 +165,17 @@ public final class IconRecentsView extends FrameLayout {
* becomes visible.
*/
public void onBeginTransitionToOverview() {
mTaskRecyclerView.scheduleLayoutAnimation();
// Load any task changes
if (!mTaskLoader.needsToLoad()) {
return;
}
mTaskAdapter.setIsShowingLoadingUi(true);
mTaskAdapter.notifyDataSetChanged();
mTaskLoader.loadTaskList(tasks -> {
// TODO: Put up some loading UI while task content is loading. May have to do something
// smarter when animating from app to overview.
mTaskAdapter.setIsShowingLoadingUi(false);
// TODO: Animate the loading UI out and the loaded data in.
mTaskAdapter.notifyDataSetChanged();
});
}
@@ -322,4 +337,18 @@ public final class IconRecentsView extends FrameLayout {
}
});
}
private static LayoutAnimationController createLayoutAnimation() {
AnimationSet anim = new AnimationSet(false /* shareInterpolator */);
Animation alphaAnim = new AlphaAnimation(0, 1);
alphaAnim.setDuration(LAYOUT_ITEM_ANIMATE_IN_DURATION);
anim.addAnimation(alphaAnim);
LayoutAnimationController layoutAnim = new LayoutAnimationController(anim);
layoutAnim.setDelay(
(float) LAYOUT_ITEM_ANIMATE_IN_DELAY_BETWEEN / LAYOUT_ITEM_ANIMATE_IN_DURATION);
return layoutAnim;
}
}
@@ -75,12 +75,4 @@ public class BackgroundAppState extends OverviewState {
return new ScaleAndTranslation(scale, 0f, 0f);
}
@Override
public int getVisibleElements(Launcher launcher) {
if (SysUINavigationMode.getMode(launcher) == Mode.NO_BUTTON) {
return super.getVisibleElements(launcher);
}
// Hide shelf content (e.g. QSB) because we fade it in when swiping up.
return ALL_APPS_HEADER_EXTRA;
}
}
@@ -15,74 +15,131 @@
*/
package com.android.launcher3.uioverrides.touchcontrollers;
import static android.view.View.TRANSLATION_X;
import static com.android.launcher3.AbstractFloatingView.TYPE_ALL;
import static com.android.launcher3.AbstractFloatingView.TYPE_HIDE_BACK_BUTTON;
import static com.android.launcher3.LauncherState.ALL_APPS;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.allapps.AllAppsTransitionController.ALL_APPS_PROGRESS;
import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
import static com.android.launcher3.touch.AbstractStateChangeTouchController.SUCCESS_TRANSITION_PROGRESS;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.ValueAnimator;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.Interpolator;
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager.AnimationConfig;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.touch.AbstractStateChangeTouchController;
import com.android.launcher3.touch.SwipeDetector;
import com.android.launcher3.userevent.nano.LauncherLogProto;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Command;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.util.TouchController;
import com.android.quickstep.views.RecentsView;
/**
* Handles swiping up on the nav bar to go home from overview or all apps.
* Handles swiping up on the nav bar to go home from launcher, e.g. overview or all apps.
*/
public class NavBarToHomeTouchController extends AbstractStateChangeTouchController {
public class NavBarToHomeTouchController implements TouchController, SwipeDetector.Listener {
private static final Interpolator PULLBACK_INTERPOLATOR = DEACCEL_3;
private final Launcher mLauncher;
private final SwipeDetector mSwipeDetector;
private final float mPullbackDistance;
private boolean mNoIntercept;
private LauncherState mStartState;
private LauncherState mEndState = NORMAL;
private AnimatorPlaybackController mCurrentAnimation;
public NavBarToHomeTouchController(Launcher launcher) {
super(launcher, SwipeDetector.VERTICAL);
mLauncher = launcher;
mSwipeDetector = new SwipeDetector(mLauncher, this, SwipeDetector.VERTICAL);
mPullbackDistance = mLauncher.getResources().getDimension(R.dimen.home_pullback_distance);
}
@Override
protected boolean canInterceptTouch(MotionEvent ev) {
boolean cameFromNavBar = (ev.getEdgeFlags() & Utilities.EDGE_NAV_BAR) != 0;
return cameFromNavBar && (mLauncher.isInState(OVERVIEW) || mLauncher.isInState(ALL_APPS));
}
@Override
protected LauncherState getTargetState(LauncherState fromState, boolean isDragTowardPositive) {
return isDragTowardPositive ? NORMAL : fromState;
}
@Override
protected float initCurrentAnimation(int animComponents) {
long accuracy = (long) (getShiftRange() * 2);
final AnimatorSet anim;
if (mFromState == OVERVIEW) {
anim = new AnimatorSet();
RecentsView recentsView = mLauncher.getOverviewPanel();
float pullbackDistance = recentsView.getPaddingStart() / 2;
if (!recentsView.isRtl()) {
pullbackDistance = -pullbackDistance;
public final boolean onControllerInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
mStartState = mLauncher.getStateManager().getState();
mNoIntercept = !canInterceptTouch(ev);
if (mNoIntercept) {
return false;
}
anim.play(ObjectAnimator.ofFloat(recentsView, View.TRANSLATION_X, pullbackDistance));
anim.setInterpolator(PULLBACK_INTERPOLATOR);
} else { // if (mFromState == ALL_APPS)
mSwipeDetector.setDetectableScrollConditions(SwipeDetector.DIRECTION_POSITIVE, false);
}
if (mNoIntercept) {
return false;
}
onControllerTouchEvent(ev);
return mSwipeDetector.isDraggingOrSettling();
}
private boolean canInterceptTouch(MotionEvent ev) {
boolean cameFromNavBar = (ev.getEdgeFlags() & Utilities.EDGE_NAV_BAR) != 0;
if (!cameFromNavBar) {
return false;
}
if (mStartState == OVERVIEW || mStartState == ALL_APPS) {
return true;
}
if (!mLauncher.hasWindowFocus()) {
return true;
}
if (AbstractFloatingView.getTopOpenView(mLauncher) != null) {
return true;
}
return false;
}
@Override
public final boolean onControllerTouchEvent(MotionEvent ev) {
return mSwipeDetector.onTouchEvent(ev);
}
private float getShiftRange() {
return mLauncher.getDeviceProfile().heightPx;
}
@Override
public void onDragStart(boolean start) {
initCurrentAnimation();
}
private void initCurrentAnimation() {
long accuracy = (long) (getShiftRange() * 2);
final AnimatorSet anim = new AnimatorSet();
if (mStartState == OVERVIEW) {
RecentsView recentsView = mLauncher.getOverviewPanel();
float pullbackDist = mPullbackDistance;
if (!recentsView.isRtl()) {
pullbackDist = -pullbackDist;
}
Animator pullback = ObjectAnimator.ofFloat(recentsView, TRANSLATION_X, pullbackDist);
pullback.setInterpolator(PULLBACK_INTERPOLATOR);
anim.play(pullback);
} else if (mStartState == ALL_APPS) {
AnimatorSetBuilder builder = new AnimatorSetBuilder();
AllAppsTransitionController allAppsController = mLauncher.getAllAppsController();
final float pullbackDistance = mLauncher.getDeviceProfile().allAppsIconSizePx / 2;
Animator allAppsProgress = ObjectAnimator.ofFloat(allAppsController, ALL_APPS_PROGRESS,
-pullbackDistance / allAppsController.getShiftRange());
-mPullbackDistance / allAppsController.getShiftRange());
allAppsProgress.setInterpolator(PULLBACK_INTERPOLATOR);
builder.play(allAppsProgress);
// Slightly fade out all apps content to further distinguish from scrolling.
@@ -90,52 +147,79 @@ public class NavBarToHomeTouchController extends AbstractStateChangeTouchControl
.mapToProgress(PULLBACK_INTERPOLATOR, 0, 0.5f));
AnimationConfig config = new AnimationConfig();
config.duration = accuracy;
allAppsController.setAlphas(mToState.getVisibleElements(mLauncher), config, builder);
anim = builder.build();
allAppsController.setAlphas(mEndState.getVisibleElements(mLauncher), config, builder);
anim.play(builder.build());
}
AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(mLauncher);
if (topView != null) {
Animator hintCloseAnim = topView.createHintCloseAnim(mPullbackDistance);
if (hintCloseAnim != null) {
hintCloseAnim.setInterpolator(PULLBACK_INTERPOLATOR);
anim.play(hintCloseAnim);
}
}
anim.setDuration(accuracy);
mCurrentAnimation = AnimatorPlaybackController.wrap(anim, accuracy, this::clearState);
return -1 / getShiftRange();
}
private void clearState() {
mCurrentAnimation = null;
mSwipeDetector.finishedScrolling();
mSwipeDetector.setDetectableScrollConditions(0, false);
}
@Override
public void onDragStart(boolean start) {
super.onDragStart(start);
mStartContainerType = LauncherLogProto.ContainerType.NAVBAR;
public boolean onDrag(float displacement) {
// Only allow swipe up.
displacement = Math.min(0, displacement);
float progress = Utilities.getProgress(displacement, 0, getShiftRange());
mCurrentAnimation.setPlayFraction(progress);
return true;
}
@Override
public void onDragEnd(float velocity, boolean fling) {
final int logAction = fling ? Touch.FLING : Touch.SWIPE;
float interpolatedProgress = PULLBACK_INTERPOLATOR.getInterpolation(
mCurrentAnimation.getProgressFraction());
if (interpolatedProgress >= SUCCESS_TRANSITION_PROGRESS || velocity < 0 && fling) {
mLauncher.getStateManager().goToState(mToState, true,
() -> onSwipeInteractionCompleted(mToState, logAction));
float progress = mCurrentAnimation.getProgressFraction();
float interpolatedProgress = PULLBACK_INTERPOLATOR.getInterpolation(progress);
boolean success = interpolatedProgress >= SUCCESS_TRANSITION_PROGRESS
|| (velocity < 0 && fling);
if (success) {
mLauncher.getStateManager().goToState(mEndState, true,
() -> onSwipeInteractionCompleted(mEndState));
if (mStartState != mEndState) {
logStateChange(mStartState.containerType, logAction);
}
AbstractFloatingView topOpenView = AbstractFloatingView.getTopOpenView(mLauncher);
if (topOpenView != null) {
AbstractFloatingView.closeAllOpenViews(mLauncher);
logStateChange(topOpenView.getLogContainerType(), logAction);
}
} else {
// Quickly return to the state we came from (we didn't move far).
AnimatorPlaybackController anim = mLauncher.getStateManager()
.createAnimationToNewWorkspace(mFromState, 80);
anim.setEndAction(() -> onSwipeInteractionCompleted(mFromState, logAction));
anim.start();
ValueAnimator anim = mCurrentAnimation.getAnimationPlayer();
anim.setFloatValues(progress, 0);
anim.addListener(new AnimationSuccessListener() {
@Override
public void onAnimationSuccess(Animator animator) {
onSwipeInteractionCompleted(mStartState);
}
});
anim.setDuration(80).start();
}
mCurrentAnimation.dispatchOnCancel();
}
@Override
protected int getDirectionForLog() {
return LauncherLogProto.Action.Direction.UP;
private void onSwipeInteractionCompleted(LauncherState targetState) {
clearState();
mLauncher.getStateManager().goToState(targetState, false /* animated */);
}
@Override
protected boolean goingBetweenNormalAndOverview(LauncherState fromState,
LauncherState toState) {
// We don't want to create an atomic animation to/from overview.
return false;
}
@Override
protected int getLogContainerTypeForNormalState() {
return LauncherLogProto.ContainerType.NAVBAR;
private void logStateChange(int startContainerType, int logAction) {
mLauncher.getUserEventDispatcher().logStateChangeAction(logAction,
LauncherLogProto.Action.Direction.UP,
LauncherLogProto.ContainerType.NAVBAR,
startContainerType,
mEndState.containerType,
mLauncher.getWorkspace().getCurrentPage());
}
}
@@ -23,7 +23,6 @@ import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.LauncherState.OVERVIEW;
import static com.android.launcher3.allapps.AllAppsTransitionController.SPRING_DAMPING_RATIO;
import static com.android.launcher3.allapps.AllAppsTransitionController.SPRING_STIFFNESS;
import static com.android.launcher3.anim.Interpolators.DEACCEL_3;
import static com.android.launcher3.anim.Interpolators.LINEAR;
import android.animation.Animator;
@@ -44,11 +43,8 @@ import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherInitListener;
import com.android.launcher3.LauncherState;
import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.allapps.AllAppsTransitionController;
import com.android.launcher3.allapps.DiscoveryBounce;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.AnimatorSetBuilder;
import com.android.launcher3.anim.SpringObjectAnimator;
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.userevent.nano.LauncherLogProto;
@@ -220,7 +216,7 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe
: mShelfState == ShelfAnimState.PEEK
? shelfPeekingProgress
: shelfOverviewProgress;
mShelfAnim = createShelfProgressAnim(activity, toProgress);
mShelfAnim = createShelfAnim(activity, toProgress);
mShelfAnim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
@@ -238,10 +234,10 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe
LauncherState fromState, long transitionLength,
Consumer<AnimatorPlaybackController> callback) {
LauncherState endState = OVERVIEW;
DeviceProfile dp = activity.getDeviceProfile();
long accuracy = 2 * Math.max(dp.widthPx, dp.heightPx);
if (wasVisible && fromState != BACKGROUND_APP) {
// If a translucent app was launched fom launcher, animate launcher states.
DeviceProfile dp = activity.getDeviceProfile();
long accuracy = 2 * Math.max(dp.widthPx, dp.heightPx);
callback.accept(activity.getStateManager()
.createAnimationToNewWorkspace(fromState, endState, accuracy));
return;
@@ -254,11 +250,10 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe
if (!activity.getDeviceProfile().isVerticalBarLayout()
&& SysUINavigationMode.getMode(activity) != Mode.NO_BUTTON) {
// Don't animate the shelf when the mode is NO_BUTTON, because we update it atomically.
Animator shiftAnim = createShelfProgressAnim(activity,
Animator shiftAnim = createShelfAnim(activity,
fromState.getVerticalProgress(activity),
endState.getVerticalProgress(activity));
anim.play(shiftAnim);
anim.play(createShelfAlphaAnim(activity, endState, accuracy));
}
playScaleDownAnim(anim, activity, endState);
@@ -275,7 +270,7 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe
callback.accept(controller);
}
private Animator createShelfProgressAnim(Launcher activity, float ... progressValues) {
private Animator createShelfAnim(Launcher activity, float ... progressValues) {
Animator shiftAnim = new SpringObjectAnimator<>(activity.getAllAppsController(),
"allAppsSpringFromACH", activity.getAllAppsController().getShiftRange(),
SPRING_DAMPING_RATIO, SPRING_STIFFNESS, progressValues);
@@ -283,19 +278,6 @@ public final class LauncherActivityControllerHelper implements ActivityControlHe
return shiftAnim;
}
/**
* Very quickly fade the alpha of shelf content.
*/
private Animator createShelfAlphaAnim(Launcher activity, LauncherState toState, long accuracy) {
AllAppsTransitionController allAppsController = activity.getAllAppsController();
AnimatorSetBuilder animBuilder = new AnimatorSetBuilder();
animBuilder.setInterpolator(AnimatorSetBuilder.ANIM_ALL_APPS_FADE, DEACCEL_3);
LauncherStateManager.AnimationConfig config = new LauncherStateManager.AnimationConfig();
config.duration = accuracy;
allAppsController.setAlphas(toState.getVisibleElements(activity), config, animBuilder);
return animBuilder.build();
}
/**
* Scale down recents from the center task being full screen to being in overview.
*/
@@ -136,17 +136,13 @@ public class OverviewInputConsumer<T extends BaseDraggingActivity>
}
private void sendEvent(MotionEvent ev) {
if (mInvalidated || !mTarget.verifyTouchDispatch(this, ev)) {
mInvalidated = true;
if (mInvalidated) {
return;
}
int flags = ev.getEdgeFlags();
ev.setEdgeFlags(flags | Utilities.EDGE_NAV_BAR);
ev.offsetLocation(-mLocationOnScreen[0], -mLocationOnScreen[1]);
if (ev.getAction() == ACTION_DOWN) {
mTarget.onInterceptTouchEvent(ev);
}
mTarget.onTouchEvent(ev);
mInvalidated = !mTarget.dispatchTouchEvent(this, ev);
ev.offsetLocation(mLocationOnScreen[0], mLocationOnScreen[1]);
ev.setEdgeFlags(flags);
}
@@ -290,6 +290,10 @@ public class TaskSystemShortcut<T extends SystemShortcut> extends SystemShortcut
if (sysUiProxy == null) {
return null;
}
if (SysUINavigationMode.getMode(activity) == SysUINavigationMode.Mode.NO_BUTTON) {
// TODO(b/130225926): Temporarily disable pinning while gesture nav is enabled
return null;
}
if (!ActivityManagerWrapper.getInstance().isScreenPinningEnabled()) {
return null;
}
@@ -101,7 +101,6 @@ public class TouchInteractionService extends Service implements
MAIN_THREAD_EXECUTOR.execute(TouchInteractionService.this::initInputMonitor);
runWhenUserUnlocked(() -> {
mRecentsModel.setSystemUiProxy(mISystemUiProxy);
mRecentsModel.onInitializeSystemUI(bundle);
mOverviewInteractionState.setSystemUiProxy(mISystemUiProxy);
});
}
@@ -426,7 +425,7 @@ public class TouchInteractionService extends Service implements
private InputConsumer newConsumer(boolean useSharedState, MotionEvent event) {
// TODO: this makes a binder call every touch down. we should move to a listener pattern.
if (mKM.isDeviceLocked()) {
if (!mIsUserUnlocked || mKM.isDeviceLocked()) {
// This handles apps launched in direct boot mode (e.g. dialer) as well as apps launched
// while device is locked even after exiting direct boot mode (e.g. camera).
return new DeviceLockedInputConsumer(this);
@@ -16,6 +16,8 @@
package com.android.quickstep.util;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.systemui.shared.system.QuickStepContract.getWindowCornerRadius;
import static com.android.systemui.shared.system.QuickStepContract.supportsRoundedCornersOnWindows;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
@@ -33,6 +35,7 @@ import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.BaseDragLayer;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.views.RecentsView;
@@ -97,12 +100,9 @@ public class ClipAnimationHelper {
(t, a1) -> a1;
public ClipAnimationHelper(Context context) {
mWindowCornerRadius = RecentsModel.INSTANCE.get(context).getWindowCornerRadius();
mSupportsRoundedCornersOnWindows = RecentsModel.INSTANCE.get(context)
.supportsRoundedCornersOnWindows();
int taskCornerRadiusRes = mSupportsRoundedCornersOnWindows ?
R.dimen.task_corner_radius : R.dimen.task_corner_radius_small;
mTaskCornerRadius = context.getResources().getDimension(taskCornerRadiusRes);
mWindowCornerRadius = getWindowCornerRadius(context.getResources());
mSupportsRoundedCornersOnWindows = supportsRoundedCornersOnWindows(context.getResources());
mTaskCornerRadius = Themes.getDialogCornerRadius(context);
}
private void updateSourceStack(RemoteAnimationTargetCompat target) {
@@ -40,6 +40,7 @@ import com.android.launcher3.R;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.BaseDragLayer;
import com.android.quickstep.TaskSystemShortcut;
import com.android.quickstep.TaskUtils;
@@ -270,7 +271,7 @@ public class TaskMenuView extends AbstractFloatingView {
}
private RoundedRectRevealOutlineProvider createOpenCloseOutlineProvider() {
float radius = getResources().getDimension(R.dimen.task_corner_radius);
float radius = Themes.getDialogCornerRadius(getContext());
Rect fromRect = new Rect(0, 0, getWidth(), 0);
Rect toRect = new Rect(0, 0, getWidth(), getHeight());
return new RoundedRectRevealOutlineProvider(radius, radius, fromRect, toRect);
@@ -46,11 +46,11 @@ import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.SystemUiController;
import com.android.launcher3.util.Themes;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.TaskOverlayFactory;
import com.android.quickstep.TaskOverlayFactory.TaskOverlay;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.QuickStepContract;
/**
* A task in the Recents view.
@@ -108,7 +108,7 @@ public class TaskThumbnailView extends View {
public TaskThumbnailView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mCornerRadius = getResources().getDimension(R.dimen.task_corner_radius);
mCornerRadius = Themes.getDialogCornerRadius(context);
mOverlay = TaskOverlayFactory.INSTANCE.get(context).createOverlay(this);
mPaint.setFilterBitmap(true);
mBackgroundPaint.setColor(Color.WHITE);
@@ -116,7 +116,7 @@ public class TaskThumbnailView extends View {
mDimmingPaintAfterClearing.setColor(Color.BLACK);
mActivity = BaseActivity.fromContext(context);
mIsDarkTextTheme = Themes.getAttrBoolean(mActivity, R.attr.isWorkspaceDarkText);
mWindowCornerRadius = RecentsModel.INSTANCE.get(context).getWindowCornerRadius();
mWindowCornerRadius = QuickStepContract.getWindowCornerRadius(context.getResources());
}
public void bind(Task task) {
@@ -51,6 +51,7 @@ import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Direction;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.util.PendingAnimation;
import com.android.launcher3.util.Themes;
import com.android.launcher3.util.ViewPool.Reusable;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.TaskIconCache;
@@ -195,7 +196,7 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
fromContext(context).getStatsLogManager().logTaskLaunch(getRecentsView(),
TaskUtils.getLaunchComponentKeyForTask(getTask().key));
});
setOutlineProvider(new TaskOutlineProvider(getResources()));
setOutlineProvider(new TaskOutlineProvider(context, getResources()));
}
@Override
@@ -513,9 +514,9 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
private final int mMarginTop;
private final float mRadius;
TaskOutlineProvider(Resources res) {
TaskOutlineProvider(Context context, Resources res) {
mMarginTop = res.getDimensionPixelSize(R.dimen.task_thumbnail_top_margin);
mRadius = res.getDimension(R.dimen.task_corner_radius);
mRadius = Themes.getDialogCornerRadius(context);
}
@Override
@@ -15,5 +15,5 @@
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
<solid android:color="#E61A73E8" />
<corners android:radius="@dimen/task_corner_radius" />
<corners android:radius="?android:attr/dialogCornerRadius" />
</shape>
+2 -2
View File
@@ -28,8 +28,8 @@
<!-- Background -->
<shape>
<corners
android:topLeftRadius="@dimen/task_corner_radius"
android:topRightRadius="@dimen/task_corner_radius"
android:topLeftRadius="?android:attr/dialogCornerRadius"
android:topRightRadius="?android:attr/dialogCornerRadius"
android:bottomLeftRadius="0dp"
android:bottomRightRadius="0dp" />
<solid android:color="?attr/popupColorPrimary" />
+4 -4
View File
@@ -19,9 +19,7 @@
<dimen name="task_thumbnail_top_margin">24dp</dimen>
<dimen name="task_thumbnail_half_top_margin">12dp</dimen>
<dimen name="task_thumbnail_icon_size">48dp</dimen>
<dimen name="task_corner_radius">8dp</dimen>
<!-- For screens without rounded corners -->
<dimen name="task_corner_radius_small">2dp</dimen>
<dimen name="recents_page_spacing">10dp</dimen>
<dimen name="recents_clear_all_deadzone_vertical_margin">70dp</dimen>
<dimen name="overview_peek_distance">32dp</dimen>
@@ -61,11 +59,13 @@
docked_stack_divider_thickness - 2 * docked_stack_divider_insets -->
<dimen name="multi_window_task_divider_size">10dp</dimen>
<dimen name="shelf_surface_radius">16dp</dimen>
<!-- same as vertical_drag_handle_size -->
<dimen name="shelf_surface_offset">24dp</dimen>
<!-- Assistant Gestures -->
<dimen name="gestures_assistant_size">28dp</dimen>
<dimen name="gestures_assistant_drag_threshold">70dp</dimen>
<!-- Distance to move elements when swiping up to go home from launcher -->
<dimen name="home_pullback_distance">28dp</dimen>
</resources>
@@ -31,6 +31,7 @@ import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.dragndrop.DragLayer.ALPHA_INDEX_TRANSITIONS;
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;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
@@ -63,12 +64,12 @@ import com.android.launcher3.shortcuts.DeepShortcutView;
import com.android.launcher3.util.MultiValueAlpha;
import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
import com.android.launcher3.views.FloatingIconView;
import com.android.quickstep.RecentsModel;
import com.android.quickstep.util.MultiValueUpdateListener;
import com.android.quickstep.util.RemoteAnimationProvider;
import com.android.quickstep.util.RemoteAnimationTargetSet;
import com.android.systemui.shared.system.ActivityCompat;
import com.android.systemui.shared.system.ActivityOptionsCompat;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.RemoteAnimationAdapterCompat;
import com.android.systemui.shared.system.RemoteAnimationDefinitionCompat;
import com.android.systemui.shared.system.RemoteAnimationRunnerCompat;
@@ -473,6 +474,10 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
});
float shapeRevealDuration = APP_LAUNCH_DURATION * SHAPE_PROGRESS_DURATION;
final float windowRadius = mDeviceProfile.isMultiWindowMode
? 0 : getWindowCornerRadius(mLauncher.getResources());
appAnimator.addUpdateListener(new MultiValueUpdateListener() {
FloatProp mDx = new FloatProp(0, dX, 0, xDuration, AGGRESSIVE_EASE);
FloatProp mDy = new FloatProp(0, dY, 0, yDuration, AGGRESSIVE_EASE);
@@ -514,13 +519,6 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
float transX0 = temp.left - offsetX;
float transY0 = temp.top - offsetY;
float windowRadius = 0;
if (!mDeviceProfile.isMultiWindowMode &&
RecentsModel.INSTANCE.get(mLauncher).supportsRoundedCornersOnWindows()) {
windowRadius = RecentsModel.INSTANCE.get(mLauncher)
.getWindowCornerRadius();
}
SurfaceParams[] params = new SurfaceParams[targets.length];
for (int i = targets.length - 1; i >= 0; i--) {
RemoteAnimationTargetCompat target = targets[i];
@@ -651,7 +649,7 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
ValueAnimator unlockAnimator = ValueAnimator.ofFloat(0, 1);
unlockAnimator.setDuration(CLOSING_TRANSITION_DURATION_MS);
float cornerRadius = mDeviceProfile.isMultiWindowMode ? 0 :
RecentsModel.INSTANCE.get(mLauncher).getWindowCornerRadius();
QuickStepContract.getWindowCornerRadius(mLauncher.getResources());
unlockAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
@@ -677,8 +675,8 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
Matrix matrix = new Matrix();
ValueAnimator closingAnimator = ValueAnimator.ofFloat(0, 1);
int duration = CLOSING_TRANSITION_DURATION_MS;
float windowCornerRadius = mDeviceProfile.isMultiWindowMode ? 0 :
RecentsModel.INSTANCE.get(mLauncher).getWindowCornerRadius();
float windowCornerRadius = mDeviceProfile.isMultiWindowMode
? 0 : getWindowCornerRadius(mLauncher.getResources());
closingAnimator.setDuration(duration);
closingAnimator.addUpdateListener(new MultiValueUpdateListener() {
FloatProp mDy = new FloatProp(0, mClosingWindowTransY, 0, duration, DEACCEL_1_7);
@@ -42,7 +42,6 @@ import com.android.launcher3.LauncherStateManager;
import com.android.launcher3.LauncherStateManager.StateHandler;
import com.android.launcher3.QuickstepAppTransitionManagerImpl;
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dragndrop.DragLayer;
import com.android.quickstep.OverviewInteractionState;
import com.android.quickstep.RecentsModel;
@@ -87,6 +86,9 @@ public class UiFactory extends RecentsUiFactory {
}
OverviewInteractionState.INSTANCE.get(launcher)
.setBackButtonAlpha(shouldBackButtonBeHidden ? 0 : 1, true /* animate */);
if (launcher != null && launcher.getDragLayer() != null) {
launcher.getDragLayer().setDisallowBackGesture(shouldBackButtonBeHidden);
}
}
public static void onCreate(Launcher launcher) {
@@ -44,7 +44,6 @@ import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.touch.AbstractStateChangeTouchController;
import com.android.launcher3.touch.SwipeDetector;
import com.android.launcher3.uioverrides.states.OverviewState;
import com.android.launcher3.uioverrides.touchcontrollers.PortraitOverviewStateTouchHelper;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action.Touch;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.quickstep.RecentsModel;
@@ -114,8 +113,10 @@ public class PortraitStatesTouchController extends AbstractStateChangeTouchContr
return false;
}
} else {
// If we are swiping to all apps instead of overview, allow it from anywhere.
boolean interceptAnywhere = mLauncher.isInState(NORMAL) && !mAllowDragToOverview;
// For all other states, only listen if the event originated below the hotseat height
if (!isTouchOverHotseat(mLauncher, ev)) {
if (!interceptAnywhere && !isTouchOverHotseat(mLauncher, ev)) {
return false;
}
}
@@ -35,6 +35,7 @@ import com.android.systemui.shared.recents.ISystemUiProxy;
import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.QuickStepContract;
import com.android.systemui.shared.system.TaskStackChangeListener;
import java.util.ArrayList;
@@ -62,9 +63,6 @@ public class RecentsModel extends TaskStackChangeListener {
private final TaskIconCache mIconCache;
private final TaskThumbnailCache mThumbnailCache;
private float mWindowCornerRadius = 0;
private boolean mSupportsRoundedCornersOnWindows;
private RecentsModel(Context context) {
mContext = context;
HandlerThread loaderThread = new HandlerThread("TaskThumbnailIconCache",
@@ -76,12 +74,6 @@ public class RecentsModel extends TaskStackChangeListener {
ActivityManagerWrapper.getInstance().registerTaskStackListener(this);
}
public void onInitializeSystemUI(Bundle params) {
mWindowCornerRadius = params.getFloat(KEY_EXTRA_WINDOW_CORNER_RADIUS, 0);
mSupportsRoundedCornersOnWindows =
params.getBoolean(KEY_EXTRA_SUPPORTS_WINDOW_CORNERS, false);
}
public TaskIconCache getIconCache() {
return mIconCache;
}
@@ -182,14 +174,6 @@ public class RecentsModel extends TaskStackChangeListener {
return mSystemUiProxy;
}
public float getWindowCornerRadius() {
return mWindowCornerRadius;
}
public boolean supportsRoundedCornersOnWindows() {
return mSupportsRoundedCornersOnWindows;
}
public void onTrimMemory(int level) {
if (level == ComponentCallbacks2.TRIM_MEMORY_UI_HIDDEN) {
mThumbnailCache.getHighResLoadingState().setVisible(false);
@@ -27,8 +27,6 @@ import androidx.annotation.IntDef;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.config.FeatureFlags;
import com.android.quickstep.SysUINavigationMode;
import com.android.quickstep.SysUINavigationMode.Mode;
import java.lang.annotation.Retention;
@@ -118,14 +116,10 @@ public class LayoutUtils {
}
public static int getShelfTrackingDistance(Context context, DeviceProfile dp) {
if (SysUINavigationMode.getMode(context) == Mode.NO_BUTTON) {
// Track the bottom of the window rather than the top of the shelf.
int shelfHeight = dp.hotseatBarSizePx + dp.getInsets().bottom;
int spaceBetweenShelfAndRecents = (int) context.getResources().getDimension(
R.dimen.task_card_vert_space);
return shelfHeight + spaceBetweenShelfAndRecents;
}
// Start from a third of bottom inset to provide some shelf overlap.
return dp.hotseatBarSizePx + dp.getInsets().bottom / 3 - dp.edgeMarginPx * 2;
// Track the bottom of the window.
int shelfHeight = dp.hotseatBarSizePx + dp.getInsets().bottom;
int spaceBetweenShelfAndRecents = (int) context.getResources().getDimension(
R.dimen.task_card_vert_space);
return shelfHeight + spaceBetweenShelfAndRecents;
}
}
@@ -53,6 +53,9 @@ public class ShelfScrimView extends ScrimView implements NavigationModeChangeLis
// cover the whole screen
private static final float SCRIM_CATCHUP_THRESHOLD = 0.2f;
// Temporarily needed until android.R.attr.bottomDialogCornerRadius becomes public
private static final float BOTTOM_CORNER_RADIUS_RATIO = 2f;
// In transposed layout, we simply draw a flat color.
private boolean mDrawingFlatColor;
@@ -87,7 +90,7 @@ public class ShelfScrimView extends ScrimView implements NavigationModeChangeLis
mMaxScrimAlpha = Math.round(OVERVIEW.getWorkspaceScrimAlpha(mLauncher) * 255);
mEndAlpha = Color.alpha(mEndScrim);
mRadius = mLauncher.getResources().getDimension(R.dimen.shelf_surface_radius);
mRadius = BOTTOM_CORNER_RADIUS_RATIO * Themes.getDialogCornerRadius(context);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mShelfOffset = context.getResources().getDimension(R.dimen.shelf_surface_offset);
@@ -32,7 +32,6 @@ import androidx.test.uiautomator.UiDevice;
import com.android.launcher3.tapl.LauncherInstrumentation;
import com.android.launcher3.tapl.TestHelpers;
import com.android.systemui.shared.system.QuickStepContract;
import org.junit.Assert;
import org.junit.rules.TestRule;
@@ -78,9 +77,9 @@ public class NavigationModeSwitchRule implements TestRule {
@Override
public void evaluate() throws Throwable {
final Context context = getInstrumentation().getContext();
final String prevOverlayPkg = QuickStepContract.isGesturalMode(context)
final String prevOverlayPkg = LauncherInstrumentation.isGesturalMode(context)
? NAV_BAR_MODE_GESTURAL_OVERLAY
: QuickStepContract.isSwipeUpMode(context)
: LauncherInstrumentation.isSwipeUpMode(context)
? NAV_BAR_MODE_2BUTTON_OVERLAY
: NAV_BAR_MODE_3BUTTON_OVERLAY;
final LauncherInstrumentation.NavigationModel originalMode =
@@ -150,4 +149,4 @@ public class NavigationModeSwitchRule implements TestRule {
return base;
}
}
}
}
+21
View File
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2019 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.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="?android:attr/colorPrimary" />
<corners android:radius="?android:attr/dialogCornerRadius" />
</shape>
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2018 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.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="?android:attr/colorPrimary" />
<corners
android:topLeftRadius="?android:attr/dialogCornerRadius"
android:topRightRadius="?android:attr/dialogCornerRadius"
android:bottomLeftRadius="0dp"
android:bottomRightRadius="0dp"
/>
</shape>
+1 -1
View File
@@ -33,7 +33,7 @@
<string name="long_accessible_way_to_add" msgid="4289502106628154155">"Tryk to gange, og hold fingeren nede for at vælge en widget eller bruge tilpassede handlinger."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d i bredden og %2$d i højden"</string>
<string name="add_item_request_drag_hint" msgid="5899764264480397019">"Tryk og hold fingeren nede for at placere manuelt"</string>
<string name="add_item_request_drag_hint" msgid="5899764264480397019">"Tryk og hold nede for at placere manuelt"</string>
<string name="place_automatically" msgid="8064208734425456485">"Tilføj automatisk"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Søg efter apps"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Indlæser apps…"</string>
+1 -1
View File
@@ -33,7 +33,7 @@
<string name="long_accessible_way_to_add" msgid="4289502106628154155">"വിജറ്റ് തിരഞ്ഞെടുക്കാനോ ഇഷ്ടാനുസൃത പ്രവർത്തനങ്ങൾ ഉപയോഗിക്കാനോ രണ്ടുതവണ ടാപ്പുചെയ്ത് പിടിക്കുക."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d വീതിയും %2$d ഉയരവും"</string>
<string name="add_item_request_drag_hint" msgid="5899764264480397019">"സ്വമേധയാ സ്ഥാപിക്കുന്നതിന് സ്‌പർശിച്ചുപിടിക്കുക"</string>
<string name="add_item_request_drag_hint" msgid="5899764264480397019">"നേരിട്ട് സ്ഥാപിക്കുന്നതിന് സ്‌പർശിച്ചുപിടിക്കുക"</string>
<string name="place_automatically" msgid="8064208734425456485">"സ്വയമേവ ചേർക്കുക"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"ആപ്പുകൾ തിരയുക"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"ആപ്പുകൾ ലോഡുചെയ്യുന്നു..."</string>
+1 -1
View File
@@ -34,7 +34,7 @@
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d रूंद बाय %2$d उंच"</string>
<string name="add_item_request_drag_hint" msgid="5899764264480397019">"स्वतः ठेवण्यासाठी स्पर्श करा आणि धरून ठेवा"</string>
<string name="place_automatically" msgid="8064208734425456485">"अापोआप जोडा"</string>
<string name="place_automatically" msgid="8064208734425456485">"पोआप जोडा"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"अॅप्स शोधा"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"अॅप्स लोड करत आहे…"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\" शी जुळणारे कोणतेही अॅप्स आढळले नाहीत"</string>
+1 -1
View File
@@ -104,7 +104,7 @@
<string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"<xliff:g id="NAME">%1$s</xliff:g> ਵਿਜੇਟ"</string>
<string name="widgets_list" msgid="796804551140113767">"ਵਿਜੇਟਾਂ ਦੀ ਸੂਚੀ"</string>
<string name="widgets_list_closed" msgid="6141506579418771922">"ਵਿਜੇਟਾਂ ਦੀ ਸੂਚੀ ਬੰਦ ਕੀਤੀ ਗਈ"</string>
<string name="action_add_to_workspace" msgid="8902165848117513641">"ਹੋਮ ਸਕ੍ਰੀਨ ਵਿੱਚ ਸ਼ਾਮਲ ਕਰੋ"</string>
<string name="action_add_to_workspace" msgid="8902165848117513641">"ਹੋਮ ਸਕ੍ਰੀਨ \'ਤੇ ਸ਼ਾਮਲ ਕਰੋ"</string>
<string name="action_move_here" msgid="2170188780612570250">"ਆਈਟਮ ਨੂੰ ਇੱਥੇ ਮੂਵ ਕਰੋ"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"ਆਈਟਮ ਨੂੰ ਹੋਮ ਸਕ੍ਰੀਨ ਵਿੱਚ ਜੋੜਿਆ ਗਿਆ"</string>
<string name="item_removed" msgid="851119963877842327">"ਅਈਟਮ ਹਟਾਈ ਗਈ"</string>
+2 -2
View File
@@ -33,8 +33,8 @@
<string name="long_accessible_way_to_add" msgid="4289502106628154155">"విడ్జెట్‌ను ఎంచుకోవడానికి లేదా అనుకూల చర్యలను ఉపయోగించడానికి రెండుసార్లు నొక్కి, ఉంచండి."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d వెడల్పు X %2$d ఎత్తు"</string>
<string name="add_item_request_drag_hint" msgid="5899764264480397019">"మాన్యువల్‌గా ఉంచడానికి నొక్కి &amp;amp పట్టుకోండి"</string>
<string name="place_automatically" msgid="8064208734425456485">"స్వయంచాలకంగా జోడించు"</string>
<string name="add_item_request_drag_hint" msgid="5899764264480397019">"మాన్యువల్‌గా ఉంచడానికి నొక్కి, పట్టుకోండి"</string>
<string name="place_automatically" msgid="8064208734425456485">"ఆటోమేటిక్‌గా జోడించు"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"అప్లికేషన్‌లను శోధించండి"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"అప్లికేషన్‌లను లోడ్ చేస్తోంది…"</string>
<string name="all_apps_no_search_results" msgid="3200346862396363786">"\"<xliff:g id="QUERY">%1$s</xliff:g>\"కి సరిపోలే అప్లికేషన్‌లేవీ కనుగొనబడలేదు"</string>
+1 -1
View File
@@ -104,7 +104,7 @@
<string name="widgets_bottom_sheet_title" msgid="2904559530954183366">"วิดเจ็ตของ <xliff:g id="NAME">%1$s</xliff:g>"</string>
<string name="widgets_list" msgid="796804551140113767">"รายการวิดเจ็ต"</string>
<string name="widgets_list_closed" msgid="6141506579418771922">"ปิดรายการวิดเจ็ตแล้ว"</string>
<string name="action_add_to_workspace" msgid="8902165848117513641">"เพิ่มลงในหน้าแรก"</string>
<string name="action_add_to_workspace" msgid="8902165848117513641">"เพิ่มลงในหน้าจอหลัก"</string>
<string name="action_move_here" msgid="2170188780612570250">"ย้ายรายการมาที่นี่"</string>
<string name="item_added_to_workspace" msgid="4211073925752213539">"เพิ่มรายการไปยังหน้าจอหลักแล้ว"</string>
<string name="item_removed" msgid="851119963877842327">"นำออกรายการออกแล้ว"</string>
+1 -1
View File
@@ -33,7 +33,7 @@
<string name="long_accessible_way_to_add" msgid="4289502106628154155">"I-double tap nang matagal upang pumili ng widget o gumamit ng mga custom na pagkilos."</string>
<string name="widget_dims_format" msgid="2370757736025621599">"%1$d × %2$d"</string>
<string name="widget_accessible_dims_format" msgid="3640149169885301790">"%1$d ang lapad at %2$d ang taas"</string>
<string name="add_item_request_drag_hint" msgid="5899764264480397019">"Pindutin nang matagal upang manual na ilagay"</string>
<string name="add_item_request_drag_hint" msgid="5899764264480397019">"Pindutin nang matagal para manual na ilagay"</string>
<string name="place_automatically" msgid="8064208734425456485">"Awtomatikong idagdag"</string>
<string name="all_apps_search_bar_hint" msgid="1390553134053255246">"Maghanap ng mga app"</string>
<string name="all_apps_loading_message" msgid="5813968043155271636">"Naglo-load ng mga app…"</string>
+4 -2
View File
@@ -37,8 +37,6 @@
<dimen name="dynamic_grid_hotseat_side_padding">0dp</dimen>
<!-- Hotseat/all-apps scrim -->
<dimen name="all_apps_scrim_radius">8dp</dimen>
<dimen name="all_apps_scrim_margin">8dp</dimen>
<dimen name="all_apps_scrim_blur">4dp</dimen>
<dimen name="vertical_drag_handle_size">24dp</dimen>
<dimen name="vertical_drag_handle_overlap_workspace">0dp</dimen>
@@ -237,4 +235,8 @@
<!-- Hints -->
<dimen name="chip_hint_height">26dp</dimen>
<dimen name="chip_hint_bottom_margin">194dp</dimen>
<!-- Theming related -->
<dimen name="default_dialog_corner_radius">8dp</dimen>
</resources>
+2 -2
View File
@@ -44,7 +44,7 @@
<item name="widgetsTheme">@style/WidgetContainerTheme</item>
<item name="folderDotColor">?android:attr/colorPrimary</item>
<item name="folderIconBorderColor">?android:attr/colorPrimary</item>
<item name="loadingIconColor">#FFF</item>
<item name="loadingIconColor">#CCFFFFFF</item>
<item name="android:windowTranslucentStatus">false</item>
<item name="android:windowTranslucentNavigation">false</item>
@@ -82,7 +82,7 @@
<item name="folderDotColor">#FF464646</item>
<item name="folderIconBorderColor">#FF80868B</item>
<item name="isMainColorDark">true</item>
<item name="loadingIconColor">#000</item>
<item name="loadingIconColor">#99FFFFFF</item>
</style>
<style name="LauncherTheme.Dark.DarkText" parent="@style/LauncherTheme.Dark">
@@ -26,7 +26,6 @@ public class FlagOverrideSampleTest {
@Test
public void withFlagOn() {
assertTrue(FeatureFlags.EXAMPLE_FLAG.get());
assertFalse(FeatureFlags.STYLE_WALLPAPER.get());
}
@@ -19,9 +19,11 @@ package com.android.launcher3;
import static android.view.accessibility.AccessibilityEvent.TYPE_VIEW_FOCUSED;
import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_CONTENT_CHANGED;
import static android.view.accessibility.AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED;
import static com.android.launcher3.compat.AccessibilityManagerCompat.isAccessibilityEnabled;
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
import android.animation.Animator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.util.AttributeSet;
@@ -30,7 +32,11 @@ import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;
import androidx.annotation.IntDef;
import androidx.annotation.Nullable;
import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.views.BaseDragLayer;
@@ -38,8 +44,6 @@ import com.android.launcher3.views.BaseDragLayer;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import androidx.annotation.IntDef;
/**
* Base class for a View which shows a floating UI on top of the launcher UI.
*/
@@ -124,8 +128,20 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch
protected abstract void handleClose(boolean animate);
/**
* Creates a user-controlled animation to hint that the view will be closed if completed.
* @param distanceToMove The max distance that elements should move from their starting point.
*/
public @Nullable Animator createHintCloseAnim(float distanceToMove) {
return null;
}
public abstract void logActionCommand(int command);
public int getLogContainerType() {
return ContainerType.DEFAULT_CONTAINERTYPE;
}
public final boolean isOpen() {
return mIsOpen;
}
@@ -50,6 +50,7 @@ import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.util.Locale;
import java.util.function.Supplier;
/**
* Layout parsing code for auto installs layout
@@ -76,12 +77,8 @@ public class AutoInstallsLayout {
if (customizationApkInfo == null) {
return null;
}
return get(context, customizationApkInfo.first, customizationApkInfo.second,
appWidgetHost, callback);
}
static AutoInstallsLayout get(Context context, String pkg, Resources targetRes,
AppWidgetHost appWidgetHost, LayoutParserCallback callback) {
String pkg = customizationApkInfo.first;
Resources targetRes = customizationApkInfo.second;
InvariantDeviceProfile grid = LauncherAppState.getIDP(context);
// Try with grid size and hotseat count
@@ -114,7 +111,7 @@ public class AutoInstallsLayout {
// Object Tags
private static final String TAG_INCLUDE = "include";
private static final String TAG_WORKSPACE = "workspace";
public static final String TAG_WORKSPACE = "workspace";
private static final String TAG_APP_ICON = "appicon";
private static final String TAG_AUTO_INSTALL = "autoinstall";
private static final String TAG_FOLDER = "folder";
@@ -156,7 +153,7 @@ public class AutoInstallsLayout {
protected final PackageManager mPackageManager;
protected final Resources mSourceRes;
protected final int mLayoutId;
protected final Supplier<XmlPullParser> mInitialLayoutSupplier;
private final InvariantDeviceProfile mIdp;
private final int mRowCount;
@@ -171,6 +168,12 @@ public class AutoInstallsLayout {
public AutoInstallsLayout(Context context, AppWidgetHost appWidgetHost,
LayoutParserCallback callback, Resources res,
int layoutId, String rootTag) {
this(context, appWidgetHost, callback, res, () -> res.getXml(layoutId), rootTag);
}
public AutoInstallsLayout(Context context, AppWidgetHost appWidgetHost,
LayoutParserCallback callback, Resources res,
Supplier<XmlPullParser> initialLayoutSupplier, String rootTag) {
mContext = context;
mAppWidgetHost = appWidgetHost;
mCallback = callback;
@@ -180,7 +183,7 @@ public class AutoInstallsLayout {
mRootTag = rootTag;
mSourceRes = res;
mLayoutId = layoutId;
mInitialLayoutSupplier = initialLayoutSupplier;
mIdp = LauncherAppState.getIDP(context);
mRowCount = mIdp.numRows;
@@ -193,9 +196,9 @@ public class AutoInstallsLayout {
public int loadLayout(SQLiteDatabase db, IntArray screenIds) {
mDb = db;
try {
return parseLayout(mLayoutId, screenIds);
return parseLayout(mInitialLayoutSupplier.get(), screenIds);
} catch (Exception e) {
Log.e(TAG, "Error parsing layout: " + e);
Log.e(TAG, "Error parsing layout: ", e);
return -1;
}
}
@@ -203,9 +206,8 @@ public class AutoInstallsLayout {
/**
* Parses the layout and returns the number of elements added on the homescreen.
*/
protected int parseLayout(int layoutId, IntArray screenIds)
protected int parseLayout(XmlPullParser parser, IntArray screenIds)
throws XmlPullParserException, IOException {
XmlPullParser parser = mSourceRes.getXml(layoutId);
beginDocument(parser, mRootTag);
final int depth = parser.getDepth();
int type;
@@ -248,7 +250,7 @@ public class AutoInstallsLayout {
final int resId = getAttributeResourceValue(parser, ATTR_WORKSPACE, 0);
if (resId != 0) {
// recursively load some more favorites, why not?
return parseLayout(resId, screenIds);
return parseLayout(mSourceRes.getXml(resId), screenIds);
} else {
return 0;
}
@@ -33,12 +33,20 @@ public class CheckLongPressHelper {
class CheckForLongPress implements Runnable {
public void run() {
if (com.android.launcher3.TestProtocol.sDebugTracing) {
android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
"CheckForLongPress1");
}
if ((mView.getParent() != null) && mView.hasWindowFocus()
&& !mHasPerformedLongPress) {
boolean handled;
if (mListener != null) {
handled = mListener.onLongClick(mView);
} else {
if (com.android.launcher3.TestProtocol.sDebugTracing) {
android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
"CheckForLongPress2");
}
handled = mView.performLongClick();
}
if (handled) {
@@ -73,11 +81,20 @@ public class CheckLongPressHelper {
}
mView.postDelayed(mPendingCheckForLongPress,
(long) (ViewConfiguration.getLongPressTimeout() * mLongPressTimeoutFactor));
if (com.android.launcher3.TestProtocol.sDebugTracing) {
android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
"postCheckForLongPress: " + ViewConfiguration.getLongPressTimeout() + " "
+ mLongPressTimeoutFactor);
}
}
public void cancelLongPress() {
mHasPerformedLongPress = false;
if (mPendingCheckForLongPress != null) {
if (com.android.launcher3.TestProtocol.sDebugTracing) {
android.util.Log.d(com.android.launcher3.TestProtocol.NO_DRAG_TAG,
"cancelLongPress");
}
mView.removeCallbacks(mPendingCheckForLongPress);
mPendingCheckForLongPress = null;
}
+7
View File
@@ -20,6 +20,7 @@ import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewDebug;
import android.view.ViewGroup;
@@ -98,4 +99,10 @@ public class Hotseat extends CellLayout implements LogContainerProvider, Insetta
setLayoutParams(lp);
InsettableFrameLayout.dispatchInsets(this, insets);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
// Don't let if follow through to workspace
return true;
}
}
-1
View File
@@ -974,7 +974,6 @@ public class Launcher extends BaseDraggingActivity implements LauncherExterns,
mDropTargetBar.setup(mDragController);
mAllAppsController.setupViews(mAppsView);
mHotseat.setOnInterceptTouchListener(mWorkspace::onInterceptHotseatTouch);
}
/**
+39 -17
View File
@@ -34,6 +34,7 @@ import android.content.Intent;
import android.content.OperationApplicationException;
import android.content.SharedPreferences;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ProviderInfo;
import android.content.res.Resources;
import android.database.Cursor;
import android.database.DatabaseUtils;
@@ -51,8 +52,10 @@ import android.os.Process;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.BaseColumns;
import android.provider.Settings;
import android.text.TextUtils;
import android.util.Log;
import android.util.Xml;
import com.android.launcher3.AutoInstallsLayout.LayoutParserCallback;
import com.android.launcher3.LauncherSettings.Favorites;
@@ -63,15 +66,21 @@ import com.android.launcher3.model.DbDowngradeHelper;
import com.android.launcher3.provider.LauncherDbUtils;
import com.android.launcher3.provider.LauncherDbUtils.SQLiteTransaction;
import com.android.launcher3.provider.RestoreDbTask;
import com.android.launcher3.util.IOUtils;
import com.android.launcher3.util.IntArray;
import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.NoLocaleSQLiteHelper;
import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.Thunk;
import org.xmlpull.v1.XmlPullParser;
import java.io.File;
import java.io.FileDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringReader;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.Arrays;
@@ -93,8 +102,6 @@ public class LauncherProvider extends ContentProvider {
static final String EMPTY_DATABASE_CREATED = "EMPTY_DATABASE_CREATED";
private static final String RESTRICTION_PACKAGE_NAME = "workspace.configuration.package.name";
private final ChangeListenerWrapper mListenerWrapper = new ChangeListenerWrapper();
private Handler mListenerHandler;
@@ -505,25 +512,40 @@ public class LauncherProvider extends ContentProvider {
*/
private AutoInstallsLayout createWorkspaceLoaderFromAppRestriction(AppWidgetHost widgetHost) {
Context ctx = getContext();
UserManager um = (UserManager) ctx.getSystemService(Context.USER_SERVICE);
Bundle bundle = um.getApplicationRestrictions(ctx.getPackageName());
if (bundle == null) {
InvariantDeviceProfile grid = LauncherAppState.getIDP(ctx);
String authority = Settings.Secure.getString(ctx.getContentResolver(),
"launcher3.layout.provider");
if (TextUtils.isEmpty(authority)) {
return null;
}
String packageName = bundle.getString(RESTRICTION_PACKAGE_NAME);
if (packageName != null) {
try {
Resources targetResources = ctx.getPackageManager()
.getResourcesForApplication(packageName);
return AutoInstallsLayout.get(ctx, packageName, targetResources,
widgetHost, mOpenHelper);
} catch (NameNotFoundException e) {
Log.e(TAG, "Target package for restricted profile not found", e);
return null;
}
ProviderInfo pi = ctx.getPackageManager().resolveContentProvider(authority, 0);
if (pi == null) {
Log.e(TAG, "No provider found for authority " + authority);
return null;
}
Uri uri = new Uri.Builder().scheme("content").authority(authority).path("launcher_layout")
.appendQueryParameter("version", "1")
.appendQueryParameter("gridWidth", Integer.toString(grid.numColumns))
.appendQueryParameter("gridHeight", Integer.toString(grid.numRows))
.appendQueryParameter("hotseatSize", Integer.toString(grid.numHotseatIcons))
.build();
try (InputStream in = ctx.getContentResolver().openInputStream(uri)) {
// Read the full xml so that we fail early in case of any IO error.
String layout = new String(IOUtils.toByteArray(in));
XmlPullParser parser = Xml.newPullParser();
parser.setInput(new StringReader(layout));
Log.d(TAG, "Loading layout from " + authority);
return new AutoInstallsLayout(ctx, widgetHost, mOpenHelper,
ctx.getPackageManager().getResourcesForApplication(pi.applicationInfo),
() -> parser, AutoInstallsLayout.TAG_WORKSPACE);
} catch (Exception e) {
Log.e(TAG, "Error getting layout stream from: " + authority , e);
return null;
}
return null;
}
private DefaultLayoutParser getDefaultLayoutParser(AppWidgetHost widgetHost) {
+1 -15
View File
@@ -133,9 +133,6 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
@Thunk int mPageIndicatorViewId;
protected T mPageIndicator;
// Convenience/caching
private static final Rect sTmpRect = new Rect();
protected final Rect mInsets = new Rect();
protected boolean mIsRtl;
@@ -805,12 +802,6 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
super.requestDisallowInterceptTouchEvent(disallowIntercept);
}
/** Returns whether x and y originated within the buffered viewport */
private boolean isTouchPointInViewportWithBuffer(float x, float y) {
sTmpRect.set(-getMeasuredWidth() / 2, 0, 3 * getMeasuredWidth() / 2, getMeasuredHeight());
return sTmpRect.contains((int) x, (int) y);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
/*
@@ -876,7 +867,7 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
pageEndTransition();
}
} else {
mIsBeingDragged = isTouchPointInViewportWithBuffer(mDownMotionX, mDownMotionY);
mIsBeingDragged = true;
}
break;
@@ -917,13 +908,8 @@ public abstract class PagedView<T extends View & PageIndicator> extends ViewGrou
final int pointerIndex = ev.findPointerIndex(mActivePointerId);
if (pointerIndex == -1) return;
// Disallow scrolling if we started the gesture from outside the viewport
final float x = ev.getX(pointerIndex);
final float y = ev.getY(pointerIndex);
if (!isTouchPointInViewportWithBuffer(x, y)) return;
final int xDiff = (int) Math.abs(x - mLastMotionX);
final int touchSlop = Math.round(touchSlopScale * mTouchSlop);
boolean xMoved = xDiff > touchSlop;
-7
View File
@@ -475,13 +475,6 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
super.onViewAdded(child);
}
protected boolean onInterceptHotseatTouch(View v, MotionEvent ev) {
// We don't want any clicks to go through to the hotseat unless the workspace is in
// the normal state or an accessible drag is in progress.
return !workspaceIconsCanBeDragged()
&& !mLauncher.getAccessibilityDelegate().isInAccessibleDrag();
}
/**
* Initializes and binds the first page
* @param qsb an existing qsb to recycle or null.
@@ -90,10 +90,6 @@ abstract class BaseFlags {
// trying to make them fit the orientation the device is in.
public static final boolean OVERVIEW_USE_SCREENSHOT_ORIENTATION = true;
public static final ToggleableGlobalSettingsFlag STYLE_WALLPAPER
= new ToggleableGlobalSettingsFlag("STYLE_WALLPAPER", false,
"Direct users to the new ThemePicker based WallpaperPicker");
/**
* Feature flag to handle define config changes dynamically instead of killing the process.
*/
@@ -24,10 +24,12 @@ import android.animation.AnimatorListenerAdapter;
import android.animation.TimeInterpolator;
import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.os.Build;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.MotionEvent;
@@ -43,6 +45,7 @@ import com.android.launcher3.DropTargetBar;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.ShortcutAndWidgetContainer;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.folder.Folder;
@@ -54,6 +57,8 @@ import com.android.launcher3.util.Thunk;
import com.android.launcher3.views.BaseDragLayer;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* A ViewGroup that coordinates dragging across its descendants
@@ -68,6 +73,9 @@ public class DragLayer extends BaseDragLayer<Launcher> {
public static final int ANIMATION_END_DISAPPEAR = 0;
public static final int ANIMATION_END_REMAIN_VISIBLE = 2;
private static final List<Rect> SYSTEM_GESTURE_EXCLUSION_RECT =
Collections.singletonList(new Rect());
@Thunk DragController mDragController;
// Variables relating to animation of views after drop
@@ -86,6 +94,8 @@ public class DragLayer extends BaseDragLayer<Launcher> {
private final ViewGroupFocusHelper mFocusIndicatorHelper;
private final WorkspaceAndHotseatScrim mScrim;
private boolean mDisallowBackGesture;
/**
* Used to create a new DragLayer from XML.
*
@@ -552,6 +562,24 @@ public class DragLayer extends BaseDragLayer<Launcher> {
mScrim.onInsetsChanged(insets);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
SYSTEM_GESTURE_EXCLUSION_RECT.get(0).set(l, t, r, b);
setDisallowBackGesture(mDisallowBackGesture);
}
@TargetApi(Build.VERSION_CODES.Q)
public void setDisallowBackGesture(boolean disallowBackGesture) {
if (!Utilities.ATLEAST_Q) {
return;
}
mDisallowBackGesture = disallowBackGesture;
setSystemGestureExclusionRects(mDisallowBackGesture
? SYSTEM_GESTURE_EXCLUSION_RECT
: Collections.emptyList());
}
public WorkspaceAndHotseatScrim getScrim() {
return mScrim;
}
+6 -1
View File
@@ -1406,7 +1406,12 @@ public class Folder extends AbstractFloatingView implements ClipPathView, DragSo
@Override
public void logActionCommand(int command) {
mLauncher.getUserEventDispatcher().logActionCommand(
command, getFolderIcon(), ContainerType.FOLDER);
command, getFolderIcon(), getLogContainerType());
}
@Override
public int getLogContainerType() {
return ContainerType.FOLDER;
}
@Override
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.graphics;
import static androidx.core.graphics.ColorUtils.compositeColors;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
@@ -47,7 +49,8 @@ public class PlaceHolderIconDrawable extends FastBitmapDrawable {
super(b, iconColor);
mProgressPath = progressPath;
mPaint.setColor(Themes.getAttrColor(context, R.attr.loadingIconColor));
mPaint.setColor(compositeColors(
Themes.getAttrColor(context, R.attr.loadingIconColor), iconColor));
}
@Override
@@ -425,7 +425,8 @@ public class LoaderTask implements Runnable {
} else if (installingPkgs.containsKey(targetPkg)) {
// App restore has started. Update the flag
c.restoreFlag |= ShortcutInfo.FLAG_RESTORE_STARTED;
c.updater().commit();
c.updater().put(LauncherSettings.Favorites.RESTORED,
c.restoreFlag).commit();
} else {
c.markDeleted("Unrestored app removed: " + targetPkg);
continue;
@@ -82,7 +82,7 @@ public abstract class ArrowPopup extends AbstractFloatingView {
public ArrowPopup(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mInflater = LayoutInflater.from(context);
mOutlineRadius = getResources().getDimension(R.dimen.bg_round_rect_radius);
mOutlineRadius = Themes.getDialogCornerRadius(context);
mLauncher = Launcher.getLauncher(context);
mIsRtl = Utilities.isRtl(getResources());
@@ -157,7 +157,12 @@ public class PopupContainerWithArrow extends ArrowPopup implements DragSource,
@Override
public void logActionCommand(int command) {
mLauncher.getUserEventDispatcher().logActionCommand(
command, mOriginalIcon, ContainerType.DEEPSHORTCUTS);
command, mOriginalIcon, getLogContainerType());
}
@Override
public int getLogContainerType() {
return ContainerType.DEEPSHORTCUTS;
}
public OnClickListener getItemClickListener() {
@@ -25,11 +25,25 @@ import android.util.AttributeSet;
import android.util.SparseArray;
import android.util.TypedValue;
import com.android.launcher3.R;
/**
* Various utility methods associated with theming.
*/
public class Themes {
public static float getDialogCornerRadius(Context context) {
return getDimension(context, android.R.attr.dialogCornerRadius,
context.getResources().getDimension(R.dimen.default_dialog_corner_radius));
}
public static float getDimension(Context context, int attr, float defaultValue) {
TypedArray ta = context.obtainStyledAttributes(new int[]{attr});
float value = ta.getDimension(0, defaultValue);
ta.recycle();
return value;
}
public static int getColorAccent(Context context) {
return getAttrColor(context, android.R.attr.colorAccent);
}
@@ -223,14 +223,18 @@ public abstract class BaseDragLayer<T extends Context & ActivityContext>
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
return verifyTouchDispatch(this, ev) && super.dispatchTouchEvent(ev);
return dispatchTouchEvent(this, ev);
}
public boolean dispatchTouchEvent(Object caller, MotionEvent ev) {
return verifyTouchDispatch(caller, ev) && super.dispatchTouchEvent(ev);
}
/**
* Returns true if the {@param caller} is allowed to dispatch {@param ev} on this view,
* false otherwise.
*/
public boolean verifyTouchDispatch(Object caller, MotionEvent ev) {
private boolean verifyTouchDispatch(Object caller, MotionEvent ev) {
int action = ev.getAction();
if (action == ACTION_DOWN) {
if (mCurrentTouchOwner != null) {
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.views;
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
import android.animation.PropertyValuesHolder;
import android.content.Context;
import android.graphics.Rect;
@@ -28,8 +30,7 @@ import com.android.launcher3.Insettable;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.anim.Interpolators;
import static com.android.launcher3.compat.AccessibilityManagerCompat.sendCustomAccessibilityEvent;
import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
public class BottomUserEducationView extends AbstractSlideInView implements Insettable {
@@ -70,6 +71,11 @@ public class BottomUserEducationView extends AbstractSlideInView implements Inse
// Since this is on-boarding popup, it is not a user controlled action.
}
@Override
public int getLogContainerType() {
return ContainerType.TIP;
}
@Override
protected boolean isOfType(int type) {
return (type & TYPE_ON_BOARD_POPUP) != 0;
@@ -34,6 +34,7 @@ import android.graphics.drawable.AdaptiveIconDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.CancellationSignal;
import android.os.Handler;
import android.os.Looper;
import android.view.View;
@@ -73,6 +74,7 @@ public class FloatingIconView extends View implements Animator.AnimatorListener,
private static final Rect sTmpRect = new Rect();
private Runnable mEndRunnable;
private CancellationSignal mLoadIconSignal;
private final int mBlurSizeOutline;
@@ -153,6 +155,9 @@ public class FloatingIconView extends View implements Animator.AnimatorListener,
@Override
public void onAnimationEnd(Animator animator) {
if (mLoadIconSignal != null) {
mLoadIconSignal.cancel();
}
if (mEndRunnable != null) {
mEndRunnable.run();
} else {
@@ -186,7 +191,7 @@ public class FloatingIconView extends View implements Animator.AnimatorListener,
@WorkerThread
private void getIcon(Launcher launcher, View v, ItemInfo info, boolean isOpening,
Runnable onIconLoadedRunnable) {
Runnable onIconLoadedRunnable, CancellationSignal loadIconSignal) {
final LayoutParams lp = (LayoutParams) getLayoutParams();
Drawable drawable = null;
boolean supportsAdaptiveIcons = ADAPTIVE_ICON_WINDOW_ANIM.get()
@@ -290,7 +295,9 @@ public class FloatingIconView extends View implements Animator.AnimatorListener,
setBackground(finalDrawable);
}
onIconLoadedRunnable.run();
if (!loadIconSignal.isCanceled()) {
onIconLoadedRunnable.run();
}
invalidate();
invalidateOutline();
});
@@ -386,6 +393,7 @@ public class FloatingIconView extends View implements Animator.AnimatorListener,
// Get the drawable on the background thread
// Must be called after matchPositionOf so that we know what size to load.
if (originalView.getTag() instanceof ItemInfo) {
view.mLoadIconSignal = new CancellationSignal();
Runnable onIconLoaded = () -> {
// Delay swapping views until the icon is loaded to prevent a flash.
view.setVisibility(VISIBLE);
@@ -393,9 +401,10 @@ public class FloatingIconView extends View implements Animator.AnimatorListener,
originalView.setVisibility(INVISIBLE);
}
};
CancellationSignal loadIconSignal = view.mLoadIconSignal;
new Handler(LauncherModel.getWorkerLooper()).postAtFrontOfQueue(() -> {
view.getIcon(launcher, originalView, (ItemInfo) originalView.getTag(), isOpening,
onIconLoaded);
onIconLoaded, loadIconSignal);
});
}
@@ -461,6 +470,10 @@ public class FloatingIconView extends View implements Animator.AnimatorListener,
setScaleY(1);
setAlpha(1);
setBackground(null);
if (mLoadIconSignal != null) {
mLoadIconSignal.cancel();
}
mLoadIconSignal = null;
mEndRunnable = null;
mIsAdaptiveIcon = false;
mForeground = null;
@@ -152,7 +152,7 @@ public class OptionsPopupView extends ArrowPopup
RectF target = new RectF(x - halfSize, y - halfSize, x + halfSize, y + halfSize);
ArrayList<OptionItem> options = new ArrayList<>();
int res = FeatureFlags.STYLE_WALLPAPER.get() && existsStyleWallpapers(launcher) ?
int res = existsStyleWallpapers(launcher) ?
R.string.styles_wallpaper_button_text : R.string.wallpaper_button_text;
options.add(new OptionItem(res, R.drawable.ic_wallpaper,
ControlType.WALLPAPER_BUTTON, OptionsPopupView::startWallpaperPicker));
@@ -210,7 +210,7 @@ public class OptionsPopupView extends ArrowPopup
.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
.putExtra(EXTRA_WALLPAPER_OFFSET,
launcher.getWorkspace().getWallpaperOffsetForCenterPage());
if (!FeatureFlags.STYLE_WALLPAPER.get()) {
if (!existsStyleWallpapers(launcher)) {
intent.putExtra(EXTRA_WALLPAPER_FLAVOR, "wallpaper_only");
}
String pickerPackage = launcher.getString(R.string.wallpaper_picker_package);
@@ -24,6 +24,7 @@ import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Property;
import android.view.MotionEvent;
@@ -31,13 +32,16 @@ import android.view.View;
import android.view.ViewConfiguration;
import android.widget.TextView;
import androidx.recyclerview.widget.RecyclerView;
import com.android.launcher3.BaseRecyclerView;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.graphics.FastScrollThumbDrawable;
import com.android.launcher3.util.Themes;
import androidx.recyclerview.widget.RecyclerView;
import java.util.Collections;
import java.util.List;
/**
* The track and scrollbar that shows when you scroll the list.
@@ -65,6 +69,9 @@ public class RecyclerViewFastScroller extends View {
private final static int SCROLL_BAR_VIS_DURATION = 150;
private static final float FAST_SCROLL_OVERLAY_Y_OFFSET_FACTOR = 0.75f;
private static final List<Rect> SYSTEM_GESTURE_EXCLUSION_RECT =
Collections.singletonList(new Rect());
private final int mMinWidth;
private final int mMaxWidth;
private final int mThumbPadding;
@@ -81,6 +88,8 @@ public class RecyclerViewFastScroller extends View {
private final Paint mThumbPaint;
protected final int mThumbHeight;
private final RectF mThumbBounds = new RectF();
private final Point mThumbDrawOffset = new Point();
private final Paint mTrackPaint;
@@ -292,15 +301,23 @@ public class RecyclerViewFastScroller extends View {
}
int saveCount = canvas.save();
canvas.translate(getWidth() / 2, mRv.getScrollBarTop());
mThumbDrawOffset.set(getWidth() / 2, mRv.getScrollBarTop());
// Draw the track
float halfW = mWidth / 2;
canvas.drawRoundRect(-halfW, 0, halfW, mRv.getScrollbarTrackHeight(),
mWidth, mWidth, mTrackPaint);
canvas.translate(0, mThumbOffsetY);
mThumbDrawOffset.y += mThumbOffsetY;
halfW += mThumbPadding;
float r = getScrollThumbRadius();
canvas.drawRoundRect(-halfW, 0, halfW, mThumbHeight, r, r, mThumbPaint);
mThumbBounds.set(-halfW, 0, halfW, mThumbHeight);
canvas.drawRoundRect(mThumbBounds, r, r, mThumbPaint);
if (Utilities.ATLEAST_Q) {
mThumbBounds.roundOut(SYSTEM_GESTURE_EXCLUSION_RECT.get(0));
SYSTEM_GESTURE_EXCLUSION_RECT.get(0).offset(mThumbDrawOffset.x, mThumbDrawOffset.y);
setSystemGestureExclusionRects(SYSTEM_GESTURE_EXCLUSION_RECT);
}
canvas.restoreToCount(saveCount);
}
@@ -162,11 +162,16 @@ abstract class BaseWidgetSheet extends AbstractSlideInView
@Override
public final void logActionCommand(int command) {
Target target = newContainerTarget(ContainerType.WIDGETS);
Target target = newContainerTarget(getLogContainerType());
target.cardinality = getElementsRowCount();
mLauncher.getUserEventDispatcher().logActionCommand(command, target);
}
@Override
public int getLogContainerType() {
return ContainerType.WIDGETS;
}
protected abstract int getElementsRowCount();
protected SystemUiController getSystemUiController() {
@@ -16,10 +16,13 @@
package com.android.launcher3.widget;
import android.animation.Animator;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.util.IntProperty;
import android.util.Pair;
import android.view.Gravity;
import android.view.LayoutInflater;
@@ -27,6 +30,8 @@ import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.Nullable;
import com.android.launcher3.Insettable;
import com.android.launcher3.ItemInfo;
import com.android.launcher3.LauncherAppState;
@@ -43,6 +48,20 @@ import java.util.List;
*/
public class WidgetsBottomSheet extends BaseWidgetSheet implements Insettable {
private static final IntProperty<View> PADDING_BOTTOM =
new IntProperty<View>("paddingBottom") {
@Override
public void setValue(View view, int paddingBottom) {
view.setPadding(view.getPaddingLeft(), view.getPaddingTop(),
view.getPaddingRight(), paddingBottom);
}
@Override
public Integer get(View view) {
return view.getPaddingBottom();
}
};
private static final int DEFAULT_CLOSE_DURATION = 200;
private ItemInfo mOriginalItemInfo;
private Rect mInsets;
@@ -158,8 +177,7 @@ public class WidgetsBottomSheet extends BaseWidgetSheet implements Insettable {
int rightInset = insets.right - mInsets.right;
int bottomInset = insets.bottom - mInsets.bottom;
mInsets.set(insets);
setPadding(getPaddingLeft() + leftInset, getPaddingTop(),
getPaddingRight() + rightInset, getPaddingBottom() + bottomInset);
setPadding(leftInset, getPaddingTop(), rightInset, bottomInset);
}
@Override
@@ -172,4 +190,10 @@ public class WidgetsBottomSheet extends BaseWidgetSheet implements Insettable {
return Pair.create(findViewById(R.id.title), getContext().getString(
mIsOpen ? R.string.widgets_list : R.string.widgets_list_closed));
}
@Nullable
@Override
public Animator createHintCloseAnim(float distanceToMove) {
return ObjectAnimator.ofInt(this, PADDING_BOTTOM, (int) (distanceToMove + mInsets.bottom));
}
}
@@ -17,6 +17,8 @@ package com.android.launcher3.widget;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.PropertyValuesHolder;
import android.content.Context;
import android.graphics.Rect;
@@ -27,6 +29,9 @@ import android.view.MotionEvent;
import android.view.View;
import android.view.animation.AnimationUtils;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.Insettable;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
@@ -35,8 +40,6 @@ import com.android.launcher3.R;
import com.android.launcher3.views.RecyclerViewFastScroller;
import com.android.launcher3.views.TopRoundedCornerView;
import androidx.annotation.VisibleForTesting;
/**
* Popup for showing the full list of available widgets
*/
@@ -235,4 +238,13 @@ public class WidgetsFullSheet extends BaseWidgetSheet
protected int getElementsRowCount() {
return mAdapter.getItemCount();
}
@Nullable
@Override
public Animator createHintCloseAnim(float distanceToMove) {
AnimatorSet anim = new AnimatorSet();
anim.play(ObjectAnimator.ofFloat(mRecyclerView, TRANSLATION_Y, -distanceToMove));
anim.play(ObjectAnimator.ofFloat(mRecyclerView, ALPHA, 0.5f));
return anim;
}
}
+8
View File
@@ -97,5 +97,13 @@
<category android:name="android.intent.category.LAUNCHER_APP" />
</intent-filter>
</activity>
<activity
android:name="com.android.launcher3.testcomponent.BaseTestingActivity"
android:label="LauncherTestApp">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
</application>
</manifest>
@@ -20,6 +20,7 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Color;
import android.os.Bundle;
import android.util.TypedValue;
import android.view.View;
@@ -63,6 +64,7 @@ public class BaseTestingActivity extends Activity implements View.OnClickListene
mView = new LinearLayout(this);
mView.setPadding(mMargin, mMargin, mMargin, mMargin);
mView.setOrientation(LinearLayout.VERTICAL);
mView.setBackgroundColor(Color.BLUE);
setContentView(mView);
registerReceiver(mCommandReceiver, new IntentFilter(mAction + SUFFIX_COMMAND));
@@ -18,6 +18,7 @@ package com.android.launcher3.testcomponent;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
import static android.content.pm.PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
import static android.content.pm.PackageManager.DONT_KILL_APP;
import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
import android.app.Activity;
import android.app.ActivityManager;
@@ -28,6 +29,13 @@ import android.content.ContentValues;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.os.ParcelFileDescriptor;
import android.util.Base64;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import androidx.test.InstrumentationRegistry;
@@ -104,4 +112,19 @@ public class TestCommandReceiver extends ContentProvider {
Uri uri = Uri.parse("content://" + inst.getContext().getPackageName() + ".commands");
return inst.getTargetContext().getContentResolver().call(uri, command, arg, null);
}
@Override
public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
String path = Base64.encodeToString(uri.getPath().getBytes(),
Base64.NO_CLOSE | Base64.NO_PADDING | Base64.NO_WRAP);
File file = new File(getContext().getCacheDir(), path);
if (!file.exists()) {
// Create an empty file so that we can pass its descriptor
try {
file.createNewFile();
} catch (IOException e) { }
}
return ParcelFileDescriptor.open(file, MODE_READ_WRITE);
}
}
@@ -102,10 +102,6 @@ public abstract class AbstractLauncherUiTest {
}
if (TestHelpers.isInLauncherProcess()) Utilities.enableRunningInTestHarnessForTests();
mLauncher = new LauncherInstrumentation(instrumentation);
try {
mDevice.executeShellCommand("settings delete secure assistant");
} catch (IOException e) {
}
}
@Rule
@@ -0,0 +1,138 @@
/**
* Copyright (C) 2019 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.ui;
import static org.junit.Assert.assertTrue;
import android.content.ComponentName;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.ProviderInfo;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.os.ParcelFileDescriptor.AutoCloseOutputStream;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.testcomponent.TestCommandReceiver;
import com.android.launcher3.util.LauncherLayoutBuilder;
import com.android.launcher3.util.rule.ShellCommandRule;
import com.android.launcher3.widget.LauncherAppWidgetHostView;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.OutputStreamWriter;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.MediumTest;
import androidx.test.runner.AndroidJUnit4;
import androidx.test.uiautomator.UiSelector;
@MediumTest
@RunWith(AndroidJUnit4.class)
public class DefaultLayoutProviderTest extends AbstractLauncherUiTest {
@Rule
public ShellCommandRule mGrantWidgetRule = ShellCommandRule.grantWidgetBind();
private static final String SETTINGS_APP = "com.android.settings";
private Context mContext;
private String mAuthority;
@Before
@Override
public void setUp() throws Exception {
super.setUp();
mContext = InstrumentationRegistry.getContext();
PackageManager pm = mTargetContext.getPackageManager();
ProviderInfo pi = pm.getProviderInfo(new ComponentName(mContext,
TestCommandReceiver.class), 0);
mAuthority = pi.authority;
}
@Test
public void testCustomProfileLoaded_with_icon_on_hotseat() throws Exception {
writeLayout(new LauncherLayoutBuilder().atHotseat(0).putApp(SETTINGS_APP, SETTINGS_APP));
// Launch the home activity
mActivityMonitor.startLauncher();
waitForModelLoaded();
// Verify widget present
UiSelector selector = new UiSelector().packageName(mTargetContext.getPackageName())
.description(getSettingsApp().getLabel().toString());
assertTrue(mDevice.findObject(selector).waitForExists(DEFAULT_UI_TIMEOUT));
}
@Test
public void testCustomProfileLoaded_with_widget() throws Exception {
// A non-restored widget with no config screen gets restored automatically.
LauncherAppWidgetProviderInfo info = TestViewHelpers.findWidgetProvider(this, false);
writeLayout(new LauncherLayoutBuilder().atWorkspace(0, 1, 0)
.putWidget(info.getComponent().getPackageName(),
info.getComponent().getClassName(), 2, 2));
// Launch the home activity
mActivityMonitor.startLauncher();
waitForModelLoaded();
// Verify widget present
UiSelector selector = new UiSelector().packageName(mTargetContext.getPackageName())
.className(LauncherAppWidgetHostView.class).description(info.label);
assertTrue(mDevice.findObject(selector).waitForExists(DEFAULT_UI_TIMEOUT));
}
@Test
public void testCustomProfileLoaded_with_folder() throws Exception {
writeLayout(new LauncherLayoutBuilder().atHotseat(0).putFolder(android.R.string.copy)
.addApp(SETTINGS_APP, SETTINGS_APP)
.addApp(SETTINGS_APP, SETTINGS_APP)
.addApp(SETTINGS_APP, SETTINGS_APP)
.build());
// Launch the home activity
mActivityMonitor.startLauncher();
waitForModelLoaded();
// Verify widget present
UiSelector selector = new UiSelector().packageName(mTargetContext.getPackageName())
.descriptionContains(mTargetContext.getString(android.R.string.copy));
assertTrue(mDevice.findObject(selector).waitForExists(DEFAULT_UI_TIMEOUT));
}
@After
public void cleanup() throws Exception {
mDevice.executeShellCommand("settings delete secure launcher3.layout.provider");
}
private void writeLayout(LauncherLayoutBuilder builder) throws Exception {
mDevice.executeShellCommand("settings put secure launcher3.layout.provider " + mAuthority);
ParcelFileDescriptor pfd = mTargetContext.getContentResolver().openFileDescriptor(
Uri.parse("content://" + mAuthority + "/launcher_layout"), "w");
try (OutputStreamWriter writer = new OutputStreamWriter(new AutoCloseOutputStream(pfd))) {
builder.build(writer);
}
clearLauncherData();
}
}
@@ -330,9 +330,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
public void testDragAppIcon() throws Throwable {
try {
TestProtocol.sDebugTracing = true;
LauncherActivityInfo settingsApp = getSettingsApp();
final String appName = settingsApp.getLabel().toString();
final String appName = "LauncherTestApp";
// 1. Open all apps and wait for load complete.
// 2. Drag icon to homescreen.
// 3. Verify that the icon works on homescreen.
@@ -341,7 +339,7 @@ public class TaplTestsLauncher3 extends AbstractLauncherUiTest {
getAppIcon(appName).
dragToWorkspace().
getWorkspaceAppIcon(appName).
launch(settingsApp.getComponentName().getPackageName());
launch(getInstrumentation().getContext().getPackageName());
} finally {
TestProtocol.sDebugTracing = false;
}
@@ -0,0 +1,172 @@
/**
* Copyright (C) 2019 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.android.launcher3.util;
import android.text.TextUtils;
import android.util.Pair;
import android.util.Xml;
import org.xmlpull.v1.XmlSerializer;
import java.io.IOException;
import java.io.StringWriter;
import java.io.Writer;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
/**
* Helper class to build xml for Launcher Layout
*/
public class LauncherLayoutBuilder {
// Object Tags
private static final String TAG_WORKSPACE = "workspace";
private static final String TAG_AUTO_INSTALL = "autoinstall";
private static final String TAG_FOLDER = "folder";
private static final String TAG_APPWIDGET = "appwidget";
private static final String TAG_EXTRA = "extra";
private static final String ATTR_CONTAINER = "container";
private static final String ATTR_RANK = "rank";
private static final String ATTR_PACKAGE_NAME = "packageName";
private static final String ATTR_CLASS_NAME = "className";
private static final String ATTR_TITLE = "title";
private static final String ATTR_SCREEN = "screen";
// x and y can be specified as negative integers, in which case -1 represents the
// last row / column, -2 represents the second last, and so on.
private static final String ATTR_X = "x";
private static final String ATTR_Y = "y";
private static final String ATTR_SPAN_X = "spanX";
private static final String ATTR_SPAN_Y = "spanY";
private static final String ATTR_CHILDREN = "children";
// Style attrs -- "Extra"
private static final String ATTR_KEY = "key";
private static final String ATTR_VALUE = "value";
private static final String CONTAINER_DESKTOP = "desktop";
private static final String CONTAINER_HOTSEAT = "hotseat";
private final ArrayList<Pair<String, HashMap<String, Object>>> mNodes = new ArrayList<>();
public Location atHotseat(int rank) {
Location l = new Location();
l.items.put(ATTR_CONTAINER, CONTAINER_HOTSEAT);
l.items.put(ATTR_RANK, Integer.toString(rank));
return l;
}
public Location atWorkspace(int x, int y, int screen) {
Location l = new Location();
l.items.put(ATTR_CONTAINER, CONTAINER_DESKTOP);
l.items.put(ATTR_X, Integer.toString(x));
l.items.put(ATTR_Y, Integer.toString(y));
l.items.put(ATTR_SCREEN, Integer.toString(screen));
return l;
}
public String build() throws IOException {
StringWriter writer = new StringWriter();
build(writer);
return writer.toString();
}
public void build(Writer writer) throws IOException {
XmlSerializer serializer = Xml.newSerializer();
serializer.setOutput(writer);
serializer.startDocument("UTF-8", true);
serializer.startTag(null, TAG_WORKSPACE);
writeNodes(serializer, mNodes);
serializer.endTag(null, TAG_WORKSPACE);
serializer.endDocument();
serializer.flush();
}
private static void writeNodes(XmlSerializer serializer,
ArrayList<Pair<String, HashMap<String, Object>>> nodes) throws IOException {
for (Pair<String, HashMap<String, Object>> node : nodes) {
ArrayList<Pair<String, HashMap<String, Object>>> children = null;
serializer.startTag(null, node.first);
for (Map.Entry<String, Object> attr : node.second.entrySet()) {
if (ATTR_CHILDREN.equals(attr.getKey())) {
children = (ArrayList<Pair<String, HashMap<String, Object>>>) attr.getValue();
} else {
serializer.attribute(null, attr.getKey(), (String) attr.getValue());
}
}
if (children != null) {
writeNodes(serializer, children);
}
serializer.endTag(null, node.first);
}
}
public class Location {
final HashMap<String, Object> items = new HashMap<>();
public LauncherLayoutBuilder putApp(String packageName, String className) {
items.put(ATTR_PACKAGE_NAME, packageName);
items.put(ATTR_CLASS_NAME, TextUtils.isEmpty(className) ? packageName : className);
mNodes.add(Pair.create(TAG_AUTO_INSTALL, items));
return LauncherLayoutBuilder.this;
}
public LauncherLayoutBuilder putWidget(String packageName, String className,
int spanX, int spanY) {
items.put(ATTR_PACKAGE_NAME, packageName);
items.put(ATTR_CLASS_NAME, className);
items.put(ATTR_SPAN_X, Integer.toString(spanX));
items.put(ATTR_SPAN_Y, Integer.toString(spanY));
mNodes.add(Pair.create(TAG_APPWIDGET, items));
return LauncherLayoutBuilder.this;
}
public FolderBuilder putFolder(int titleResId) {
FolderBuilder folderBuilder = new FolderBuilder();
items.put(ATTR_TITLE, Integer.toString(titleResId));
items.put(ATTR_CHILDREN, folderBuilder.mChildren);
mNodes.add(Pair.create(TAG_FOLDER, items));
return folderBuilder;
}
}
public class FolderBuilder {
final ArrayList<Pair<String, HashMap<String, Object>>> mChildren = new ArrayList<>();
public FolderBuilder addApp(String packageName, String className) {
HashMap<String, Object> items = new HashMap<>();
items.put(ATTR_PACKAGE_NAME, packageName);
items.put(ATTR_CLASS_NAME, TextUtils.isEmpty(className) ? packageName : className);
mChildren.add(Pair.create(TAG_AUTO_INSTALL, items));
return this;
}
public LauncherLayoutBuilder build() {
return LauncherLayoutBuilder.this;
}
}
}
@@ -28,7 +28,6 @@ import androidx.test.uiautomator.Until;
* Ancestor for AppIcon and AppMenuItem.
*/
class Launchable {
private static final int DRAG_SPEED = 500;
protected final LauncherInstrumentation mLauncher;
protected final UiObject2 mObject;
@@ -77,8 +76,7 @@ class Launchable {
Workspace.dragIconToWorkspace(
mLauncher,
this,
new Point(device.getDisplayWidth() / 2, device.getDisplayHeight() / 2),
DRAG_SPEED);
new Point(device.getDisplayWidth() / 2, device.getDisplayHeight() / 2));
try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer(
"dragged launchable to workspace")) {
return new Workspace(mLauncher);
@@ -16,12 +16,16 @@
package com.android.launcher3.tapl;
import static com.android.launcher3.TestProtocol.BACKGROUND_APP_STATE_ORDINAL;
import static com.android.launcher3.TestProtocol.NORMAL_STATE_ORDINAL;
import android.app.ActivityManager;
import android.app.Instrumentation;
import android.app.UiAutomation;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.graphics.Point;
import android.net.Uri;
import android.os.Build;
@@ -172,11 +176,11 @@ public final class LauncherInstrumentation {
// Workaround, use constructed context because both the instrumentation context and the
// app context are not constructed with resources that take overlays into account
final Context ctx = baseContext.createPackageContext("android", 0);
if (QuickStepContract.isGesturalMode(ctx)) {
if (isGesturalMode(ctx)) {
return NavigationModel.ZERO_BUTTON;
} else if (QuickStepContract.isSwipeUpMode(ctx)) {
} else if (isSwipeUpMode(ctx)) {
return NavigationModel.TWO_BUTTON;
} else if (QuickStepContract.isLegacyMode(ctx)) {
} else if (isLegacyMode(ctx)) {
return NavigationModel.THREE_BUTTON;
} else {
fail("Can't detect navigation mode");
@@ -343,18 +347,33 @@ public final class LauncherInstrumentation {
log(action = "0-button: already in workspace");
} else if (hasLauncherObject(OVERVIEW_RES_ID)) {
log(action = "0-button: from overview");
mDevice.pressHome();
final UiObject2 navBar = waitForSystemUiObject("navigation_bar_frame");
swipe(
navBar.getVisibleBounds().centerX(), navBar.getVisibleBounds().centerY(),
navBar.getVisibleBounds().centerX(), 0,
NORMAL_STATE_ORDINAL, ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME);
} else if (hasLauncherObject(WIDGETS_RES_ID)) {
log(action = "0-button: from widgets");
mDevice.pressHome();
} else if (hasLauncherObject(APPS_RES_ID)) {
log(action = "0-button: from all apps");
mDevice.pressHome();
final UiObject2 navBar = waitForSystemUiObject("navigation_bar_frame");
swipe(
navBar.getVisibleBounds().centerX(), navBar.getVisibleBounds().centerY(),
navBar.getVisibleBounds().centerX(), 0,
NORMAL_STATE_ORDINAL, ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME);
} else {
log(action = "0-button: from another app");
assertTrue("Launcher is visible, don't know how to go home",
!mDevice.hasObject(By.pkg(getLauncherPackageName())));
mDevice.pressHome();
final UiObject2 navBar = waitForSystemUiObject("navigation_bar_frame");
swipe(
navBar.getVisibleBounds().centerX(), navBar.getVisibleBounds().centerY(),
navBar.getVisibleBounds().centerX(), 0,
BACKGROUND_APP_STATE_ORDINAL, ZERO_BUTTON_STEPS_FROM_BACKGROUND_TO_HOME);
}
} else {
log(action = "clicking home button");
@@ -607,6 +626,46 @@ public final class LauncherInstrumentation {
}
}
public static boolean isGesturalMode(Context context) {
return QuickStepContract.isGesturalMode(getCurrentInteractionMode(context));
}
public static boolean isSwipeUpMode(Context context) {
return QuickStepContract.isSwipeUpMode(getCurrentInteractionMode(context));
}
public static boolean isLegacyMode(Context context) {
return QuickStepContract.isLegacyMode(getCurrentInteractionMode(context));
}
private static int getCurrentInteractionMode(Context context) {
return getSystemIntegerRes(context, "config_navBarInteractionMode");
}
private static int getSystemIntegerRes(Context context, String resName) {
Resources res = context.getResources();
int resId = res.getIdentifier(resName, "integer", "android");
if (resId != 0) {
return res.getInteger(resId);
} else {
Log.e(TAG, "Failed to get system resource ID. Incompatible framework version?");
return -1;
}
}
private static int getSystemDimensionResId(Context context, String resName) {
Resources res = context.getResources();
int resId = res.getIdentifier(resName, "dimen", "android");
if (resId != 0) {
return resId;
} else {
Log.e(TAG, "Failed to get system resource ID. Incompatible framework version?");
return -1;
}
}
static void sleep(int duration) {
try {
Thread.sleep(duration);
@@ -616,8 +675,10 @@ public final class LauncherInstrumentation {
int getEdgeSensitivityWidth() {
try {
return QuickStepContract.getEdgeSensitivityWidth(
mInstrumentation.getTargetContext().createPackageContext("android", 0)) + 1;
final Context context = mInstrumentation.getTargetContext().createPackageContext(
"android", 0);
return context.getResources().getDimensionPixelSize(
getSystemDimensionResId(context, "config_backGestureInset")) + 1;
} catch (PackageManager.NameNotFoundException e) {
fail("Can't get edge sensitivity: " + e);
return 0;
@@ -21,7 +21,9 @@ import static com.android.launcher3.TestProtocol.ALL_APPS_STATE_ORDINAL;
import static junit.framework.TestCase.assertTrue;
import android.graphics.Point;
import android.os.SystemClock;
import android.view.KeyEvent;
import android.view.MotionEvent;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -35,8 +37,8 @@ import com.android.launcher3.TestProtocol;
*/
public final class Workspace extends Home {
private static final float FLING_SPEED = 3500.0F;
private static final int DRAG_DURACTION = 2000;
private final UiObject2 mHotseat;
private final int ICON_DRAG_SPEED = LauncherInstrumentation.needSlowGestures() ? 100 : 570;
Workspace(LauncherInstrumentation launcher) {
super(launcher);
@@ -116,8 +118,7 @@ public final class Workspace extends Home {
mLauncher,
getHotseatAppIcon("Chrome"),
new Point(mLauncher.getDevice().getDisplayWidth(),
workspace.getVisibleBounds().centerY()),
(int) (ICON_DRAG_SPEED * mLauncher.getDisplayDensity()));
workspace.getVisibleBounds().centerY()));
verifyActiveContainer();
}
assertTrue("Home screen workspace didn't become scrollable",
@@ -134,10 +135,16 @@ public final class Workspace extends Home {
mHotseat, AppIcon.getAppIconSelector(appName, mLauncher)));
}
static void dragIconToWorkspace(LauncherInstrumentation launcher, Launchable launchable,
Point dest, int icon_drag_speed) {
static void dragIconToWorkspace(
LauncherInstrumentation launcher, Launchable launchable, Point dest) {
LauncherInstrumentation.log("dragIconToWorkspace: begin");
launchable.getObject().drag(dest, icon_drag_speed);
final Point launchableCenter = launchable.getObject().getVisibleCenter();
final long downTime = SystemClock.uptimeMillis();
launcher.sendPointer(downTime, downTime, MotionEvent.ACTION_DOWN, launchableCenter);
launcher.waitForLauncherObject("deep_shortcuts_container");
launcher.movePointer(downTime, DRAG_DURACTION, launchableCenter, dest);
launcher.sendPointer(
downTime, SystemClock.uptimeMillis(), MotionEvent.ACTION_UP, dest);
LauncherInstrumentation.log("dragIconToWorkspace: end");
launcher.waitUntilGone("drop_target_bar");
}