Merging from ub-launcher3-master @ build 6925377

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

Change-Id: I928b100c8f41abff34047df69d988622123f9939
This commit is contained in:
Winson Chung 2020-10-23 09:32:16 -07:00
commit 87f2b09072
100 changed files with 3111 additions and 2461 deletions

View File

@ -36,14 +36,12 @@ java_library_static {
name: "launcher_log_protos_lite", name: "launcher_log_protos_lite",
srcs: [ srcs: [
"protos/*.proto", "protos/*.proto",
"proto_overrides/*.proto",
], ],
sdk_version: "current", sdk_version: "current",
proto: { proto: {
type: "lite", type: "lite",
local_include_dirs:[ local_include_dirs:[
"protos", "protos",
"proto_overrides",
], ],
}, },
static_libs: ["libprotobuf-java-lite"], static_libs: ["libprotobuf-java-lite"],

View File

@ -30,6 +30,7 @@
with some minor changed based on the derivative app. with some minor changed based on the derivative app.
--> -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.CALL_PHONE" /> <uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.SET_WALLPAPER" /> <uses-permission android:name="android.permission.SET_WALLPAPER" />
<uses-permission android:name="android.permission.SET_WALLPAPER_HINTS" /> <uses-permission android:name="android.permission.SET_WALLPAPER_HINTS" />

View File

@ -1,38 +1,42 @@
160759508 171450807
144170434 170675311
144170434 170338029
161801331 170338170
162564471 160544577
160361464 171171594
161901771 170488559
160568387 171131394
162623012 171131394
162012217 171026321
160718310 170648272
160361464 170752716
160718310 170611866
162012217 170702596
160748731 170487752
154951045 170665892
162861289 168608912
149934536 170636685
149934536 169771796
144170434 141126144
144170434 166614700
162454040 168805872
161273376 170263425
149934536 169221288
149934536 143965596
158701272 169221287
162871508 167259591
149934536 156044202
161939759 169438169
154964045 164926736
161536946 168653219
149934536 169963211
161685099 170121063
162812884 169988381
162480567 169980192
162480567 169221288
149934536 169385783
149934536 168167693
169796517
169330678
168818961
168608912

View File

@ -1,24 +1,39 @@
144170434 141126144
149934536 143965596
154951045 156044202
154964045 160544577
158701272 164926736
160361464 166614700
160568387 167259591
160718310 168167693
160748731 168608912
160759508 168653219
161273376 168805872
161536946 168818961
161685099 169221287
161801331 169221288
161901771 169330678
161939759 169385783
162012217 169438169
162454040 169771796
162480567 169796517
162564471 169963211
162623012 169980192
162812884 169988381
162861289 170121063
162871508 170263425
170338029
170338170
170487752
170488559
170611866
170636685
170648272
170665892
170675311
170702596
170752716
171026321
171131394
171171594
171450807

File diff suppressed because it is too large Load Diff

View File

@ -1,30 +0,0 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
syntax = "proto2";
option java_package = "com.android.launcher3.userevent";
option java_outer_classname = "LauncherLogExtensions";
package userevent;
//
// Use this to add any app specific extensions to the proto.
//
message LauncherEventExtension {
}
message TargetExtension {
}

View File

@ -28,4 +28,40 @@ message LauncherTraceProto {
message TouchInteractionServiceProto { message TouchInteractionServiceProto {
optional bool service_connected = 1; optional bool service_connected = 1;
optional OverviewComponentObserverProto overview_component_obvserver = 2;
optional InputConsumerProto input_consumer = 3;
}
message OverviewComponentObserverProto {
optional bool overview_activity_started = 1;
optional bool overview_activity_resumed = 2;
}
message InputConsumerProto {
optional string name = 1;
optional SwipeHandlerProto swipe_handler = 2;
}
message SwipeHandlerProto {
optional GestureStateProto gesture_state = 1;
optional bool is_recents_attached_to_app_window = 2;
optional int32 scroll_offset = 3;
// Swipe up progress from 0 (app) to 1 (overview); can be > 1 if swiping past overview.
optional float app_to_overview_progress = 4;
}
message GestureStateProto {
optional GestureEndTarget endTarget = 1 [default = UNSET];
enum GestureEndTarget {
UNSET = 0;
HOME = 1;
RECENTS = 2;
NEW_TASK = 3;
LAST_TASK = 4;
}
} }

View File

@ -51,6 +51,8 @@
<dimen name="recents_empty_message_text_size">16sp</dimen> <dimen name="recents_empty_message_text_size">16sp</dimen>
<dimen name="recents_empty_message_text_padding">16dp</dimen> <dimen name="recents_empty_message_text_padding">16dp</dimen>
<dimen name="max_shadow_radius">5dp</dimen>
<!-- Total space (start + end) between the task card and the edge of the screen <!-- Total space (start + end) between the task card and the edge of the screen
in various configurations --> in various configurations -->
<dimen name="task_card_vert_space">40dp</dimen> <dimen name="task_card_vert_space">40dp</dimen>

View File

@ -16,14 +16,10 @@
package com.android.launcher3; package com.android.launcher3;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE; import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.quickstep.TaskViewUtils.createRecentsWindowAnimator;
import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch; import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet; import android.animation.AnimatorSet;
import android.animation.ObjectAnimator; import android.animation.ObjectAnimator;
import android.content.Context; import android.content.Context;
@ -32,11 +28,8 @@ import android.view.View;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.quickstep.TaskViewUtils;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PendingAnimation;
import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat; import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
/** /**
@ -53,60 +46,16 @@ public final class LauncherAppTransitionManagerImpl extends QuickstepAppTransiti
protected boolean isLaunchingFromRecents(@NonNull View v, protected boolean isLaunchingFromRecents(@NonNull View v,
@Nullable RemoteAnimationTargetCompat[] targets) { @Nullable RemoteAnimationTargetCompat[] targets) {
return mLauncher.getStateManager().getState().overviewUi return mLauncher.getStateManager().getState().overviewUi
&& findTaskViewToLaunch(mLauncher, v, targets) != null; && findTaskViewToLaunch(mLauncher.getOverviewPanel(), v, targets) != null;
} }
@Override @Override
protected void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v, protected void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
@NonNull RemoteAnimationTargetCompat[] appTargets, @NonNull RemoteAnimationTargetCompat[] appTargets,
@NonNull RemoteAnimationTargetCompat[] wallpaperTargets, boolean launcherClosing) { @NonNull RemoteAnimationTargetCompat[] wallpaperTargets, boolean launcherClosing) {
RecentsView recentsView = mLauncher.getOverviewPanel(); TaskViewUtils.composeRecentsLaunchAnimator(anim, v, appTargets, wallpaperTargets,
boolean skipLauncherChanges = !launcherClosing; launcherClosing, mLauncher.getStateManager(), mLauncher.getOverviewPanel(),
mLauncher.getDepthController());
TaskView taskView = findTaskViewToLaunch(mLauncher, v, appTargets);
PendingAnimation pa = new PendingAnimation(RECENTS_LAUNCH_DURATION);
createRecentsWindowAnimator(taskView, skipLauncherChanges, appTargets, wallpaperTargets,
mLauncher.getDepthController(), pa);
anim.play(pa.buildAnim());
Animator childStateAnimation = null;
// Found a visible recents task that matches the opening app, lets launch the app from there
Animator launcherAnim;
final AnimatorListenerAdapter windowAnimEndListener;
if (launcherClosing) {
launcherAnim = recentsView.createAdjacentPageAnimForTaskLaunch(taskView);
launcherAnim.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR);
launcherAnim.setDuration(RECENTS_LAUNCH_DURATION);
// Make sure recents gets fixed up by resetting task alphas and scales, etc.
windowAnimEndListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mLauncher.getStateManager().moveToRestState();
mLauncher.getStateManager().reapplyState();
}
};
} else {
AnimatorPlaybackController controller =
mLauncher.getStateManager().createAnimationToNewWorkspace(NORMAL,
RECENTS_LAUNCH_DURATION);
controller.dispatchOnStart();
childStateAnimation = controller.getTarget();
launcherAnim = controller.getAnimationPlayer().setDuration(RECENTS_LAUNCH_DURATION);
windowAnimEndListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mLauncher.getStateManager().goToState(NORMAL, false);
}
};
}
anim.play(launcherAnim);
// Set the current animation first, before adding windowAnimEndListener. Setting current
// animation adds some listeners which need to be called before windowAnimEndListener
// (the ordering of listeners matter in this case).
mLauncher.getStateManager().setCurrentAnimation(anim, childStateAnimation);
anim.addListener(windowAnimEndListener);
} }
@Override @Override

View File

@ -153,6 +153,7 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
private final float mContentTransY; private final float mContentTransY;
private final float mWorkspaceTransY; private final float mWorkspaceTransY;
private final float mClosingWindowTransY; private final float mClosingWindowTransY;
private final float mMaxShadowRadius;
private DeviceProfile mDeviceProfile; private DeviceProfile mDeviceProfile;
@ -186,6 +187,7 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
mContentTransY = res.getDimensionPixelSize(R.dimen.content_trans_y); mContentTransY = res.getDimensionPixelSize(R.dimen.content_trans_y);
mWorkspaceTransY = res.getDimensionPixelSize(R.dimen.workspace_trans_y); mWorkspaceTransY = res.getDimensionPixelSize(R.dimen.workspace_trans_y);
mClosingWindowTransY = res.getDimensionPixelSize(R.dimen.closing_window_trans_y); mClosingWindowTransY = res.getDimensionPixelSize(R.dimen.closing_window_trans_y);
mMaxShadowRadius = res.getDimensionPixelSize(R.dimen.max_shadow_radius);
mLauncher.addOnDeviceProfileChangeListener(this); mLauncher.addOnDeviceProfileChangeListener(this);
} }
@ -538,6 +540,8 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
EXAGGERATED_EASE); EXAGGERATED_EASE);
FloatProp mWindowRadius = new FloatProp(initialWindowRadius, windowRadius, 0, FloatProp mWindowRadius = new FloatProp(initialWindowRadius, windowRadius, 0,
RADIUS_DURATION, EXAGGERATED_EASE); RADIUS_DURATION, EXAGGERATED_EASE);
FloatProp mShadowRadius = new FloatProp(0, mMaxShadowRadius, 0,
APP_LAUNCH_DURATION, EXAGGERATED_EASE);
@Override @Override
public void onUpdate(float percent) { public void onUpdate(float percent) {
@ -600,7 +604,8 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
builder.withMatrix(matrix) builder.withMatrix(matrix)
.withWindowCrop(crop) .withWindowCrop(crop)
.withAlpha(1f - mIconAlpha.value) .withAlpha(1f - mIconAlpha.value)
.withCornerRadius(mWindowRadius.value); .withCornerRadius(mWindowRadius.value)
.withShadowRadius(mShadowRadius.value);
} else { } else {
tmpPos.set(target.position.x, target.position.y); tmpPos.set(target.position.x, target.position.y);
if (target.localBounds != null) { if (target.localBounds != null) {
@ -752,6 +757,8 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
FloatProp mDy = new FloatProp(0, mClosingWindowTransY, 0, duration, DEACCEL_1_7); FloatProp mDy = new FloatProp(0, mClosingWindowTransY, 0, duration, DEACCEL_1_7);
FloatProp mScale = new FloatProp(1f, 1f, 0, duration, DEACCEL_1_7); FloatProp mScale = new FloatProp(1f, 1f, 0, duration, DEACCEL_1_7);
FloatProp mAlpha = new FloatProp(1f, 0f, 25, 125, LINEAR); FloatProp mAlpha = new FloatProp(1f, 0f, 25, 125, LINEAR);
FloatProp mShadowRadius = new FloatProp(mMaxShadowRadius, 0, 0, duration,
DEACCEL_1_7);
@Override @Override
public void onUpdate(float percent) { public void onUpdate(float percent) {
@ -773,7 +780,8 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
matrix.postTranslate(tmpPos.x, tmpPos.y); matrix.postTranslate(tmpPos.x, tmpPos.y);
builder.withMatrix(matrix) builder.withMatrix(matrix)
.withAlpha(mAlpha.value) .withAlpha(mAlpha.value)
.withCornerRadius(windowCornerRadius); .withCornerRadius(windowCornerRadius)
.withShadowRadius(mShadowRadius.value);
} else { } else {
matrix.setTranslate(tmpPos.x, tmpPos.y); matrix.setTranslate(tmpPos.x, tmpPos.y);
builder.withMatrix(matrix) builder.withMatrix(matrix)
@ -798,7 +806,6 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
} }
private void addCujInstrumentation(Animator anim, int cuj, String transition) { private void addCujInstrumentation(Animator anim, int cuj, String transition) {
if (Trace.isEnabled()) {
anim.addListener(new AnimationSuccessListener() { anim.addListener(new AnimationSuccessListener() {
@Override @Override
public void onAnimationStart(Animator animation) { public void onAnimationStart(Animator animation) {
@ -825,7 +832,6 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
} }
}); });
} }
}
/** /**
* Remote animation runner for animation from the app to Launcher, including recents. * Remote animation runner for animation from the app to Launcher, including recents.

View File

@ -108,6 +108,8 @@ public class PredictionRowView extends LinearLayout implements
AllAppsSectionDecorator.SectionDecorationHandler mDecorationHandler; AllAppsSectionDecorator.SectionDecorationHandler mDecorationHandler;
@Nullable private List<ItemInfo> mPendingPredictedItems;
public PredictionRowView(@NonNull Context context) { public PredictionRowView(@NonNull Context context) {
this(context, null); this(context, null);
} }
@ -164,6 +166,7 @@ public class PredictionRowView extends LinearLayout implements
} }
mDecorationHandler.onDraw(canvas); mDecorationHandler.onDraw(canvas);
mDecorationHandler.onFocusDraw(canvas, getFocusedChild()); mDecorationHandler.onFocusDraw(canvas, getFocusedChild());
mLauncher.getAppsView().getActiveRecyclerView().invalidateItemDecorations();
} }
mFocusHelper.draw(canvas); mFocusHelper.draw(canvas);
super.dispatchDraw(canvas); super.dispatchDraw(canvas);
@ -203,6 +206,16 @@ public class PredictionRowView extends LinearLayout implements
* we can optimize by swapping them in place. * we can optimize by swapping them in place.
*/ */
public void setPredictedApps(List<ItemInfo> items) { public void setPredictedApps(List<ItemInfo> items) {
if (!mLauncher.isWorkspaceLoading() && isShown() && getWindowVisibility() == View.VISIBLE) {
mPendingPredictedItems = items;
return;
}
applyPredictedApps(items);
}
private void applyPredictedApps(List<ItemInfo> items) {
mPendingPredictedItems = null;
mPredictedApps.clear(); mPredictedApps.clear();
items.stream() items.stream()
.filter(itemInfo -> itemInfo instanceof WorkspaceItemInfo) .filter(itemInfo -> itemInfo instanceof WorkspaceItemInfo)
@ -341,4 +354,13 @@ public class PredictionRowView extends LinearLayout implements
public View getFocusedChild() { public View getFocusedChild() {
return getChildAt(0); return getChildAt(0);
} }
@Override
public void onVisibilityAggregated(boolean isVisible) {
super.onVisibilityAggregated(isVisible);
if (mPendingPredictedItems != null && !isVisible) {
applyPredictedApps(mPendingPredictedItems);
}
}
} }

View File

@ -251,6 +251,28 @@ public class HotseatPredictionController implements DragController.DragListener,
* Sets or updates the predicted items * Sets or updates the predicted items
*/ */
public void setPredictedItems(FixedContainerItems items) { public void setPredictedItems(FixedContainerItems items) {
if (!mLauncher.isWorkspaceLoading()
&& mHotseat.isShown()
&& mHotseat.getWindowVisibility() == View.VISIBLE) {
mHotseat.setOnVisibilityAggregatedCallback((isVisible) -> {
if (isVisible) {
return;
}
mHotseat.setOnVisibilityAggregatedCallback(null);
applyPredictedItems(items);
});
} else {
mHotseat.setOnVisibilityAggregatedCallback(null);
applyPredictedItems(items);
}
}
/**
* Sets or updates the predicted items only once the hotseat becomes hidden to the user
*/
private void applyPredictedItems(FixedContainerItems items) {
mPredictedItems = items.items; mPredictedItems = items.items;
if (mPredictedItems.isEmpty()) { if (mPredictedItems.isEmpty()) {
HotseatRestoreHelper.restoreBackup(mLauncher); HotseatRestoreHelper.restoreBackup(mLauncher);

View File

@ -250,6 +250,9 @@ public class AppEventProducer implements StatsLogConsumer {
case PREDICTION_CONTAINER: { case PREDICTION_CONTAINER: {
return "predictions"; return "predictions";
} }
case SHORTCUTS_CONTAINER: {
return "deep-shortcuts";
}
case FOLDER: { case FOLDER: {
FolderContainer fc = ci.getFolder(); FolderContainer fc = ci.getFolder();
switch (fc.getParentContainerCase()) { switch (fc.getParentContainerCase()) {

View File

@ -18,8 +18,7 @@ package com.android.launcher3.model;
import static android.content.ContentResolver.SCHEME_CONTENT; import static android.content.ContentResolver.SCHEME_CONTENT;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.Utilities.newContentObserver;
import static com.android.launcher3.util.Executors.createAndStartNewLooper;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.app.RemoteAction; import android.app.RemoteAction;
@ -35,7 +34,7 @@ import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.DeadObjectException; import android.os.DeadObjectException;
import android.os.Handler; import android.os.Handler;
import android.os.Message; import android.os.Looper;
import android.os.Process; import android.os.Process;
import android.os.UserHandle; import android.os.UserHandle;
import android.text.TextUtils; import android.text.TextUtils;
@ -43,7 +42,7 @@ import android.util.ArrayMap;
import android.util.Log; import android.util.Log;
import androidx.annotation.MainThread; import androidx.annotation.MainThread;
import androidx.annotation.NonNull; import androidx.annotation.Nullable;
import androidx.annotation.WorkerThread; import androidx.annotation.WorkerThread;
import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.BaseDraggingActivity;
@ -55,6 +54,7 @@ import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.popup.RemoteActionShortcut; import com.android.launcher3.popup.RemoteActionShortcut;
import com.android.launcher3.popup.SystemShortcut; import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.util.BgObjectWithLooper;
import com.android.launcher3.util.MainThreadInitializedObject; import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.Preconditions; import com.android.launcher3.util.Preconditions;
@ -68,15 +68,11 @@ import java.util.Map;
* Data model for digital wellbeing status of apps. * Data model for digital wellbeing status of apps.
*/ */
@TargetApi(Build.VERSION_CODES.Q) @TargetApi(Build.VERSION_CODES.Q)
public final class WellbeingModel { public final class WellbeingModel extends BgObjectWithLooper {
private static final String TAG = "WellbeingModel"; private static final String TAG = "WellbeingModel";
private static final int[] RETRY_TIMES_MS = {5000, 15000, 30000}; private static final int[] RETRY_TIMES_MS = {5000, 15000, 30000};
private static final boolean DEBUG = false; private static final boolean DEBUG = false;
private static final int MSG_PACKAGE_ADDED = 1;
private static final int MSG_PACKAGE_REMOVED = 2;
private static final int MSG_FULL_REFRESH = 3;
private static final int UNKNOWN_MINIMAL_DEVICE_STATE = 0; private static final int UNKNOWN_MINIMAL_DEVICE_STATE = 0;
private static final int IN_MINIMAL_DEVICE = 2; private static final int IN_MINIMAL_DEVICE = 2;
@ -98,9 +94,9 @@ public final class WellbeingModel {
private final Context mContext; private final Context mContext;
private final String mWellbeingProviderPkg; private final String mWellbeingProviderPkg;
private final Handler mWorkerHandler;
private final ContentObserver mContentObserver; private Handler mWorkerHandler;
private ContentObserver mContentObserver;
private final Object mModelLock = new Object(); private final Object mModelLock = new Object();
// Maps the action Id to the corresponding RemoteAction // Maps the action Id to the corresponding RemoteAction
@ -111,60 +107,73 @@ public final class WellbeingModel {
private WellbeingModel(final Context context) { private WellbeingModel(final Context context) {
mContext = context; mContext = context;
mWorkerHandler =
new Handler(createAndStartNewLooper("WellbeingHandler"), this::handleMessage);
mWellbeingProviderPkg = mContext.getString(R.string.wellbeing_provider_pkg); mWellbeingProviderPkg = mContext.getString(R.string.wellbeing_provider_pkg);
mContentObserver = new ContentObserver(MAIN_EXECUTOR.getHandler()) { initializeInBackground("WellbeingHandler");
}
@Override @Override
public void onChange(boolean selfChange, Uri uri) { protected void onInitialized(Looper looper) {
if (DEBUG || mIsInTest) { mWorkerHandler = new Handler(looper);
Log.d(TAG, "ContentObserver.onChange() called with: selfChange = [" mContentObserver = newContentObserver(mWorkerHandler, this::onWellbeingUriChanged);
+ selfChange + "], uri = [" + uri + "]");
}
Preconditions.assertUIThread();
if (uri.getPath().contains(PATH_ACTIONS)) {
// Wellbeing reports that app actions have changed.
updateWellbeingData();
} else if (uri.getPath().contains(PATH_MINIMAL_DEVICE)) {
// Wellbeing reports that minimal device state or config is changed.
updateLauncherModel(context);
}
}
};
FeatureFlags.ENABLE_MINIMAL_DEVICE.addChangeListener(mContext, () ->
updateLauncherModel(context));
if (!TextUtils.isEmpty(mWellbeingProviderPkg)) { if (!TextUtils.isEmpty(mWellbeingProviderPkg)) {
context.registerReceiver( mContext.registerReceiver(
new SimpleBroadcastReceiver(this::onWellbeingProviderChanged), new SimpleBroadcastReceiver(t -> restartObserver()),
PackageManagerHelper.getPackageFilter(mWellbeingProviderPkg, PackageManagerHelper.getPackageFilter(mWellbeingProviderPkg,
Intent.ACTION_PACKAGE_ADDED, Intent.ACTION_PACKAGE_CHANGED, Intent.ACTION_PACKAGE_ADDED, Intent.ACTION_PACKAGE_CHANGED,
Intent.ACTION_PACKAGE_REMOVED, Intent.ACTION_PACKAGE_DATA_CLEARED, Intent.ACTION_PACKAGE_REMOVED, Intent.ACTION_PACKAGE_DATA_CLEARED,
Intent.ACTION_PACKAGE_RESTARTED)); Intent.ACTION_PACKAGE_RESTARTED),
null, mWorkerHandler);
IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED); IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
filter.addAction(Intent.ACTION_PACKAGE_REMOVED); filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
filter.addDataScheme("package"); filter.addDataScheme("package");
context.registerReceiver(new SimpleBroadcastReceiver(this::onAppPackageChanged), mContext.registerReceiver(new SimpleBroadcastReceiver(this::onAppPackageChanged),
filter); filter, null, mWorkerHandler);
restartObserver(); restartObserver();
} }
} }
@WorkerThread
private void onWellbeingUriChanged(Uri uri) {
Preconditions.assertNonUiThread();
if (DEBUG || mIsInTest) {
Log.d(TAG, "ContentObserver.onChange() called with: uri = [" + uri + "]");
}
if (uri.getPath().contains(PATH_ACTIONS)) {
// Wellbeing reports that app actions have changed.
updateAllPackages();
} else if (uri.getPath().contains(PATH_MINIMAL_DEVICE)) {
// Wellbeing reports that minimal device state or config is changed.
if (!FeatureFlags.ENABLE_MINIMAL_DEVICE.get()) {
return;
}
// Temporary bug fix for b/169771796. Wellbeing provides the layout configuration when
// minimal device is enabled. We always want to reload the configuration from Wellbeing
// since the layout configuration might have changed.
mContext.deleteDatabase(DB_NAME_MINIMAL_DEVICE);
final Bundle extras = new Bundle();
String dbFile;
if (isInMinimalDeviceMode()) {
dbFile = DB_NAME_MINIMAL_DEVICE;
extras.putString(LauncherProvider.KEY_LAYOUT_PROVIDER_AUTHORITY,
mWellbeingProviderPkg + ".api");
} else {
dbFile = InvariantDeviceProfile.INSTANCE.get(mContext).dbFile;
}
LauncherSettings.Settings.call(mContext.getContentResolver(),
LauncherSettings.Settings.METHOD_SWITCH_DATABASE,
dbFile, extras);
}
}
public void setInTest(boolean inTest) { public void setInTest(boolean inTest) {
mIsInTest = inTest; mIsInTest = inTest;
} }
protected void onWellbeingProviderChanged(Intent intent) { @WorkerThread
if (DEBUG || mIsInTest) {
Log.d(TAG, "Changes to Wellbeing package: intent = [" + intent + "]");
}
restartObserver();
}
private void restartObserver() { private void restartObserver() {
final ContentResolver resolver = mContext.getContentResolver(); final ContentResolver resolver = mContext.getContentResolver();
resolver.unregisterContentObserver(mContentObserver); resolver.unregisterContentObserver(mContentObserver);
@ -179,7 +188,7 @@ public final class WellbeingModel {
Log.e(TAG, "Failed to register content observer for " + actionsUri + ": " + e); Log.e(TAG, "Failed to register content observer for " + actionsUri + ": " + e);
if (mIsInTest) throw new RuntimeException(e); if (mIsInTest) throw new RuntimeException(e);
} }
updateWellbeingData(); updateAllPackages();
} }
@MainThread @MainThread
@ -212,39 +221,6 @@ public final class WellbeingModel {
} }
} }
private void updateWellbeingData() {
mWorkerHandler.sendEmptyMessage(MSG_FULL_REFRESH);
}
private void updateLauncherModel(@NonNull final Context context) {
if (!FeatureFlags.ENABLE_MINIMAL_DEVICE.get()) {
reloadLauncherInNormalMode(context);
return;
}
mWorkerHandler.post(() -> {
if (isInMinimalDeviceMode()) {
reloadLauncherInMinimalMode(context);
} else {
reloadLauncherInNormalMode(context);
}
});
}
private void reloadLauncherInNormalMode(@NonNull final Context context) {
LauncherSettings.Settings.call(context.getContentResolver(),
LauncherSettings.Settings.METHOD_SWITCH_DATABASE,
InvariantDeviceProfile.INSTANCE.get(context).dbFile);
}
private void reloadLauncherInMinimalMode(@NonNull final Context context) {
final Bundle extras = new Bundle();
extras.putString(LauncherProvider.KEY_LAYOUT_PROVIDER_AUTHORITY,
mWellbeingProviderPkg + ".api");
LauncherSettings.Settings.call(context.getContentResolver(),
LauncherSettings.Settings.METHOD_SWITCH_DATABASE,
DB_NAME_MINIMAL_DEVICE, extras);
}
private Uri.Builder apiBuilder() { private Uri.Builder apiBuilder() {
return new Uri.Builder() return new Uri.Builder()
.scheme(SCHEME_CONTENT) .scheme(SCHEME_CONTENT)
@ -277,7 +253,8 @@ public final class WellbeingModel {
return false; return false;
} }
private boolean updateActions(String... packageNames) { @WorkerThread
private boolean updateActions(String[] packageNames) {
if (packageNames.length == 0) { if (packageNames.length == 0) {
return true; return true;
} }
@ -340,68 +317,51 @@ public final class WellbeingModel {
return true; return true;
} }
private boolean handleMessage(Message msg) { @WorkerThread
switch (msg.what) { private void updateActionsWithRetry(int retryCount, @Nullable String packageName) {
case MSG_PACKAGE_REMOVED: { String[] packageNames = TextUtils.isEmpty(packageName)
String packageName = (String) msg.obj; ? mContext.getSystemService(LauncherApps.class)
mWorkerHandler.removeCallbacksAndMessages(packageName);
synchronized (mModelLock) {
mPackageToActionId.remove(packageName);
}
return true;
}
case MSG_PACKAGE_ADDED: {
String packageName = (String) msg.obj;
mWorkerHandler.removeCallbacksAndMessages(packageName);
if (!updateActions(packageName)) {
scheduleRefreshRetry(msg);
}
return true;
}
case MSG_FULL_REFRESH: {
// Remove all existing messages
mWorkerHandler.removeCallbacksAndMessages(null);
final String[] packageNames = mContext.getSystemService(LauncherApps.class)
.getActivityList(null, Process.myUserHandle()).stream() .getActivityList(null, Process.myUserHandle()).stream()
.map(li -> li.getApplicationInfo().packageName).distinct() .map(li -> li.getApplicationInfo().packageName).distinct()
.toArray(String[]::new); .toArray(String[]::new)
if (!updateActions(packageNames)) { : new String[] { packageName };
scheduleRefreshRetry(msg);
}
return true;
}
}
return false;
}
private void scheduleRefreshRetry(Message originalMsg) { mWorkerHandler.removeCallbacksAndMessages(packageName);
int retryCount = originalMsg.arg1; if (updateActions(packageNames)) {
return;
}
if (retryCount >= RETRY_TIMES_MS.length) { if (retryCount >= RETRY_TIMES_MS.length) {
// To many retries, skip // To many retries, skip
return; return;
} }
mWorkerHandler.postDelayed(
Message msg = Message.obtain(originalMsg); () -> updateActionsWithRetry(retryCount + 1, packageName),
msg.arg1 = retryCount + 1; packageName, RETRY_TIMES_MS[retryCount]);
mWorkerHandler.sendMessageDelayed(msg, RETRY_TIMES_MS[retryCount]);
} }
@WorkerThread
private void updateAllPackages() {
updateActionsWithRetry(0, null);
}
@WorkerThread
private void onAppPackageChanged(Intent intent) { private void onAppPackageChanged(Intent intent) {
if (DEBUG || mIsInTest) Log.d(TAG, "Changes in apps: intent = [" + intent + "]"); if (DEBUG || mIsInTest) Log.d(TAG, "Changes in apps: intent = [" + intent + "]");
Preconditions.assertUIThread(); Preconditions.assertNonUiThread();
final String packageName = intent.getData().getSchemeSpecificPart(); final String packageName = intent.getData().getSchemeSpecificPart();
if (packageName == null || packageName.length() == 0) { if (packageName == null || packageName.length() == 0) {
// they sent us a bad intent // they sent us a bad intent
return; return;
} }
final String action = intent.getAction(); final String action = intent.getAction();
if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) { if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
Message.obtain(mWorkerHandler, MSG_PACKAGE_REMOVED, packageName).sendToTarget(); mWorkerHandler.removeCallbacksAndMessages(packageName);
synchronized (mModelLock) {
mPackageToActionId.remove(packageName);
}
} else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) { } else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
Message.obtain(mWorkerHandler, MSG_PACKAGE_ADDED, packageName).sendToTarget(); updateActionsWithRetry(0, packageName);
} }
} }

View File

@ -41,11 +41,14 @@ import static com.android.quickstep.GestureState.STATE_END_TARGET_ANIMATION_FINI
import static com.android.quickstep.GestureState.STATE_END_TARGET_SET; import static com.android.quickstep.GestureState.STATE_END_TARGET_SET;
import static com.android.quickstep.GestureState.STATE_RECENTS_SCROLLING_FINISHED; import static com.android.quickstep.GestureState.STATE_RECENTS_SCROLLING_FINISHED;
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES; import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD; import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME; import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
import android.animation.Animator; import android.animation.Animator;
import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ValueAnimator; import android.animation.ValueAnimator;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.app.ActivityManager; import android.app.ActivityManager;
@ -78,6 +81,8 @@ import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.logging.StatsLogManager.StatsLogger; import com.android.launcher3.logging.StatsLogManager.StatsLogger;
import com.android.launcher3.statemanager.StatefulActivity; import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.tracing.InputConsumerProto;
import com.android.launcher3.tracing.SwipeHandlerProto;
import com.android.launcher3.util.TraceHelper; import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.util.VibratorWrapper; import com.android.launcher3.util.VibratorWrapper;
import com.android.launcher3.util.WindowBounds; import com.android.launcher3.util.WindowBounds;
@ -90,6 +95,7 @@ import com.android.quickstep.util.AnimatorControllerWithResistance;
import com.android.quickstep.util.InputConsumerProxy; import com.android.quickstep.util.InputConsumerProxy;
import com.android.quickstep.util.MotionPauseDetector; import com.android.quickstep.util.MotionPauseDetector;
import com.android.quickstep.util.RecentsOrientedState; import com.android.quickstep.util.RecentsOrientedState;
import com.android.quickstep.util.ProtoTracer;
import com.android.quickstep.util.RectFSpringAnim; import com.android.quickstep.util.RectFSpringAnim;
import com.android.quickstep.util.SurfaceTransactionApplier; import com.android.quickstep.util.SurfaceTransactionApplier;
import com.android.quickstep.util.SwipePipToHomeAnimator; import com.android.quickstep.util.SwipePipToHomeAnimator;
@ -107,6 +113,7 @@ import com.android.systemui.shared.system.TaskInfoCompat;
import com.android.systemui.shared.system.TaskStackChangeListener; import com.android.systemui.shared.system.TaskStackChangeListener;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.function.Consumer; import java.util.function.Consumer;
/** /**
@ -614,13 +621,6 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<?>, Q extends
updateSysUiFlags(mCurrentShift.value); updateSysUiFlags(mCurrentShift.value);
applyWindowTransform(); applyWindowTransform();
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
if (mRecentsAnimationTargets != null) {
LiveTileOverlay.INSTANCE.update(
mTaskViewSimulator.getCurrentRect(),
mTaskViewSimulator.getCurrentCornerRadius());
}
}
updateLauncherTransitionProgress(); updateLauncherTransitionProgress();
} }
@ -719,6 +719,8 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<?>, Q extends
@UiThread @UiThread
public void onGestureStarted(boolean isLikelyToStartNewTask) { public void onGestureStarted(boolean isLikelyToStartNewTask) {
InteractionJankMonitorWrapper.begin(
InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH, 2000 /* ms timeout */);
notifyGestureStartedAsync(); notifyGestureStartedAsync();
setIsLikelyToStartNewTask(isLikelyToStartNewTask, false /* animate */); setIsLikelyToStartNewTask(isLikelyToStartNewTask, false /* animate */);
mStateCallback.setStateOnUiThread(STATE_GESTURE_STARTED); mStateCallback.setStateOnUiThread(STATE_GESTURE_STARTED);
@ -794,7 +796,8 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<?>, Q extends
} }
private void onSettledOnEndTarget() { private void onSettledOnEndTarget() {
switch (mGestureState.getEndTarget()) { final GestureEndTarget endTarget = mGestureState.getEndTarget();
switch (endTarget) {
case HOME: case HOME:
mStateCallback.setState(STATE_SCALED_CONTROLLER_HOME | STATE_CAPTURE_SCREENSHOT); mStateCallback.setState(STATE_SCALED_CONTROLLER_HOME | STATE_CAPTURE_SCREENSHOT);
// Notify swipe-to-home (recents animation) is finished // Notify swipe-to-home (recents animation) is finished
@ -811,7 +814,10 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<?>, Q extends
mStateCallback.setState(STATE_RESUME_LAST_TASK); mStateCallback.setState(STATE_RESUME_LAST_TASK);
break; break;
} }
ActiveGestureLog.INSTANCE.addLog("onSettledOnEndTarget " + mGestureState.getEndTarget()); ActiveGestureLog.INSTANCE.addLog("onSettledOnEndTarget " + endTarget);
if (endTarget != NEW_TASK) {
InteractionJankMonitorWrapper.cancel(InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH);
}
} }
/** @return Whether this was the task we were waiting to appear, and thus handled it. */ /** @return Whether this was the task we were waiting to appear, and thus handled it. */
@ -1388,7 +1394,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<?>, Q extends
if (taskView != null && !mCanceled) { if (taskView != null && !mCanceled) {
// Defer finishing the animation until the next launcher frame with the // Defer finishing the animation until the next launcher frame with the
// new thumbnail // new thumbnail
finishTransitionPosted = ViewUtils.postDraw(taskView, finishTransitionPosted = ViewUtils.postFrameDrawn(taskView,
() -> mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED), () -> mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED),
this::isCanceled); this::isCanceled);
} }
@ -1450,17 +1456,64 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<?>, Q extends
private void setupLauncherUiAfterSwipeUpToRecentsAnimation() { private void setupLauncherUiAfterSwipeUpToRecentsAnimation() {
endLauncherTransitionController(); endLauncherTransitionController();
mActivityInterface.onSwipeUpToRecentsComplete(); mActivityInterface.onSwipeUpToRecentsComplete();
if (mRecentsAnimationController != null) {
mRecentsAnimationController.setDeferCancelUntilNextTransition(true /* defer */,
true /* screenshot */);
}
mRecentsView.onSwipeUpAnimationSuccess(); mRecentsView.onSwipeUpAnimationSuccess();
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
mTaskAnimationManager.setLaunchOtherTaskInLiveTileModeHandler(
this::launchOtherTaskInLiveTileMode);
}
SystemUiProxy.INSTANCE.get(mContext).onOverviewShown(false, TAG); SystemUiProxy.INSTANCE.get(mContext).onOverviewShown(false, TAG);
doLogGesture(RECENTS, mRecentsView.getCurrentPageTaskView()); doLogGesture(RECENTS, mRecentsView.getCurrentPageTaskView());
reset(); reset();
} }
private void launchOtherTaskInLiveTileMode(RemoteAnimationTargetCompat appearedTaskTarget) {
TaskView taskView = mRecentsView.getTaskView(appearedTaskTarget.taskId);
if (taskView == null) {
return;
}
RemoteAnimationTargetCompat[] apps = Arrays.copyOf(
mRecentsAnimationTargets.apps,
mRecentsAnimationTargets.apps.length + 1);
apps[apps.length - 1] = appearedTaskTarget;
boolean launcherClosing =
taskIsATargetWithMode(apps, mActivity.getTaskId(), MODE_CLOSING);
AnimatorSet anim = new AnimatorSet();
TaskViewUtils.composeRecentsLaunchAnimator(
anim, taskView, apps,
mRecentsAnimationTargets.wallpapers, launcherClosing,
mActivity.getStateManager(), mRecentsView,
mActivityInterface.getDepthController());
anim.addListener(new AnimatorListenerAdapter(){
@Override
public void onAnimationEnd(Animator animator) {
cleanUp(false);
}
@Override
public void onAnimationCancel(Animator animator) {
cleanUp(true);
}
private void cleanUp(boolean canceled) {
if (mRecentsAnimationController != null) {
mRecentsAnimationController.finish(false /* toRecents */,
null /* onFinishComplete */);
if (canceled) {
mRecentsAnimationController = null;
} else {
mActivityInterface.onLaunchTaskSuccess();
}
ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", false);
}
}
});
anim.start();
}
private void addLiveTileOverlay() { private void addLiveTileOverlay() {
if (LiveTileOverlay.INSTANCE.attach(mActivity.getRootView().getOverlay())) { if (LiveTileOverlay.INSTANCE.attach(mActivity.getRootView().getOverlay())) {
mRecentsView.setLiveTileOverlayAttached(true); mRecentsView.setLiveTileOverlayAttached(true);
@ -1523,11 +1576,6 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<?>, Q extends
protected void startNewTask(Consumer<Boolean> resultCallback) { protected void startNewTask(Consumer<Boolean> resultCallback) {
// Launch the task user scrolled to (mRecentsView.getNextPage()). // Launch the task user scrolled to (mRecentsView.getNextPage()).
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
// We finish recents animation inside launchTask() when live tile is enabled.
mRecentsView.getNextPageTaskView().launchTask(false /* animate */,
true /* freezeTaskList */);
} else {
if (!mCanceled) { if (!mCanceled) {
TaskView nextTask = mRecentsView.getNextPageTaskView(); TaskView nextTask = mRecentsView.getNextPageTaskView();
if (nextTask != null) { if (nextTask != null) {
@ -1556,7 +1604,6 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<?>, Q extends
} }
mCanceled = false; mCanceled = false;
} }
}
/** /**
* Runs the given {@param action} if the recents animation has already started, or queues it to * Runs the given {@param action} if the recents animation has already started, or queues it to
@ -1641,6 +1688,32 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<?>, Q extends
} }
mTaskViewSimulator.apply(mTransformParams); mTaskViewSimulator.apply(mTransformParams);
} }
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mRecentsAnimationTargets != null) {
LiveTileOverlay.INSTANCE.update(
mTaskViewSimulator.getCurrentRect(),
mTaskViewSimulator.getCurrentCornerRadius());
}
ProtoTracer.INSTANCE.get(mContext).scheduleFrameUpdate();
}
/**
* Used for winscope tracing, see launcher_trace.proto
* @see com.android.systemui.shared.tracing.ProtoTraceable#writeToProto
* @param inputConsumerProto The parent of this proto message.
*/
public void writeToProto(InputConsumerProto.Builder inputConsumerProto) {
SwipeHandlerProto.Builder swipeHandlerProto = SwipeHandlerProto.newBuilder();
mGestureState.writeToProto(swipeHandlerProto);
swipeHandlerProto.setIsRecentsAttachedToAppWindow(
mAnimationFactory.isRecentsAttachedToAppWindow());
swipeHandlerProto.setScrollOffset(mRecentsView == null
? 0
: mRecentsView.getScrollOffset());
swipeHandlerProto.setAppToOverviewProgress(mCurrentShift.value);
inputConsumerProto.setSwipeHandler(swipeHandlerProto);
} }
public interface Factory { public interface Factory {

View File

@ -297,6 +297,10 @@ public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_T
* @param animate Whether to animate recents to/from its new attached state. * @param animate Whether to animate recents to/from its new attached state.
*/ */
default void setRecentsAttachedToAppWindow(boolean attached, boolean animate) { } default void setRecentsAttachedToAppWindow(boolean attached, boolean animate) { }
default boolean isRecentsAttachedToAppWindow() {
return false;
}
} }
class DefaultAnimationFactory implements AnimationFactory { class DefaultAnimationFactory implements AnimationFactory {
@ -388,6 +392,11 @@ public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_T
fadeAnim.setDuration(animate ? RECENTS_ATTACH_DURATION : 0).start(); fadeAnim.setDuration(animate ? RECENTS_ATTACH_DURATION : 0).start();
} }
@Override
public boolean isRecentsAttachedToAppWindow() {
return mIsAttachedToWindow;
}
protected void createBackgroundToOverviewAnim(ACTIVITY_TYPE activity, PendingAnimation pa) { protected void createBackgroundToOverviewAnim(ACTIVITY_TYPE activity, PendingAnimation pa) {
// Scale down recents from being full screen to being in overview. // Scale down recents from being full screen to being in overview.
RecentsView recentsView = activity.getOverviewPanel(); RecentsView recentsView = activity.getOverviewPanel();

View File

@ -26,6 +26,8 @@ import android.content.Intent;
import android.os.Build; import android.os.Build;
import com.android.launcher3.statemanager.StatefulActivity; import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.tracing.GestureStateProto;
import com.android.launcher3.tracing.SwipeHandlerProto;
import com.android.quickstep.util.ActiveGestureLog; import com.android.quickstep.util.ActiveGestureLog;
import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat; import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@ -46,19 +48,22 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
* Defines the end targets of a gesture and the associated state. * Defines the end targets of a gesture and the associated state.
*/ */
public enum GestureEndTarget { public enum GestureEndTarget {
HOME(true, LAUNCHER_STATE_HOME, false), HOME(true, LAUNCHER_STATE_HOME, false, GestureStateProto.GestureEndTarget.HOME),
RECENTS(true, LAUNCHER_STATE_OVERVIEW, true), RECENTS(true, LAUNCHER_STATE_OVERVIEW, true, GestureStateProto.GestureEndTarget.RECENTS),
NEW_TASK(false, LAUNCHER_STATE_BACKGROUND, true), NEW_TASK(false, LAUNCHER_STATE_BACKGROUND, true,
GestureStateProto.GestureEndTarget.NEW_TASK),
LAST_TASK(false, LAUNCHER_STATE_BACKGROUND, true); LAST_TASK(false, LAUNCHER_STATE_BACKGROUND, true,
GestureStateProto.GestureEndTarget.LAST_TASK);
GestureEndTarget(boolean isLauncher, int containerType, GestureEndTarget(boolean isLauncher, int containerType, boolean recentsAttachedToAppWindow,
boolean recentsAttachedToAppWindow) { GestureStateProto.GestureEndTarget protoEndTarget) {
this.isLauncher = isLauncher; this.isLauncher = isLauncher;
this.containerType = containerType; this.containerType = containerType;
this.recentsAttachedToAppWindow = recentsAttachedToAppWindow; this.recentsAttachedToAppWindow = recentsAttachedToAppWindow;
this.protoEndTarget = protoEndTarget;
} }
/** Whether the target is in the launcher activity. Implicitly, if the end target is going /** Whether the target is in the launcher activity. Implicitly, if the end target is going
@ -68,6 +73,8 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
public final int containerType; public final int containerType;
/** Whether RecentsView should be attached to the window as we animate to this target */ /** Whether RecentsView should be attached to the window as we animate to this target */
public final boolean recentsAttachedToAppWindow; public final boolean recentsAttachedToAppWindow;
/** The GestureStateProto enum value, used for winscope tracing. See launcher_trace.proto */
public final GestureStateProto.GestureEndTarget protoEndTarget;
} }
private static final String TAG = "GestureState"; private static final String TAG = "GestureState";
@ -345,4 +352,17 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
pw.println(" lastStartedTaskId=" + mLastStartedTaskId); pw.println(" lastStartedTaskId=" + mLastStartedTaskId);
pw.println(" isRecentsAnimationRunning=" + isRecentsAnimationRunning()); pw.println(" isRecentsAnimationRunning=" + isRecentsAnimationRunning());
} }
/**
* Used for winscope tracing, see launcher_trace.proto
* @see com.android.systemui.shared.tracing.ProtoTraceable#writeToProto
* @param swipeHandlerProto The parent of this proto message.
*/
public void writeToProto(SwipeHandlerProto.Builder swipeHandlerProto) {
GestureStateProto.Builder gestureStateProto = GestureStateProto.newBuilder();
gestureStateProto.setEndTarget(mEndTarget == null
? GestureStateProto.GestureEndTarget.UNSET
: mEndTarget.protoEndTarget);
swipeHandlerProto.setGestureState(gestureStateProto);
}
} }

View File

@ -21,6 +21,9 @@ import android.view.InputEvent;
import android.view.KeyEvent; import android.view.KeyEvent;
import android.view.MotionEvent; import android.view.MotionEvent;
import com.android.launcher3.tracing.InputConsumerProto;
import com.android.launcher3.tracing.TouchInteractionServiceProto;
@TargetApi(Build.VERSION_CODES.O) @TargetApi(Build.VERSION_CODES.O)
public interface InputConsumer { public interface InputConsumer {
@ -116,4 +119,21 @@ public interface InputConsumer {
} }
return name.toString(); return name.toString();
} }
/**
* Used for winscope tracing, see launcher_trace.proto
* @see com.android.systemui.shared.tracing.ProtoTraceable#writeToProto
* @param serviceProto The parent of this proto message.
*/
default void writeToProto(TouchInteractionServiceProto.Builder serviceProto) {
InputConsumerProto.Builder inputConsumerProto = InputConsumerProto.newBuilder();
inputConsumerProto.setName(getName());
writeToProtoInternal(inputConsumerProto);
serviceProto.setInputConsumer(inputConsumerProto);
}
/**
* @see #writeToProto - allows subclasses to write additional info to the proto.
*/
default void writeToProtoInternal(InputConsumerProto.Builder inputConsumerProto) {}
} }

View File

@ -35,6 +35,7 @@ import com.android.quickstep.util.RemoteAnimationProvider;
import com.android.quickstep.views.RecentsView; import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskView; import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.systemui.shared.system.LatencyTrackerCompat; import com.android.systemui.shared.system.LatencyTrackerCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat; import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@ -174,6 +175,9 @@ public class OverviewCommandHelper {
return; return;
} }
InteractionJankMonitorWrapper.begin(
InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH, 2000 /* ms timout */);
// Otherwise, start overview. // Otherwise, start overview.
mListener = mActivityInterface.createActivityInitListener(this::onActivityReady); mListener = mActivityInterface.createActivityInitListener(this::onActivityReady);
mListener.registerAndStartActivity(mOverviewComponentObserver.getOverviewIntent(), mListener.registerAndStartActivity(mOverviewComponentObserver.getOverviewIntent(),

View File

@ -35,6 +35,8 @@ import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo; import android.content.pm.ResolveInfo;
import android.util.SparseIntArray; import android.util.SparseIntArray;
import com.android.launcher3.tracing.OverviewComponentObserverProto;
import com.android.launcher3.tracing.TouchInteractionServiceProto;
import com.android.launcher3.util.SimpleBroadcastReceiver; import com.android.launcher3.util.SimpleBroadcastReceiver;
import com.android.systemui.shared.system.PackageManagerWrapper; import com.android.systemui.shared.system.PackageManagerWrapper;
@ -262,4 +264,17 @@ public final class OverviewComponentObserver {
pw.println(" overviewIntent=" + mOverviewIntent); pw.println(" overviewIntent=" + mOverviewIntent);
pw.println(" homeIntent=" + mCurrentHomeIntent); pw.println(" homeIntent=" + mCurrentHomeIntent);
} }
/**
* Used for winscope tracing, see launcher_trace.proto
* @see com.android.systemui.shared.tracing.ProtoTraceable#writeToProto
* @param serviceProto The parent of this proto message.
*/
public void writeToProto(TouchInteractionServiceProto.Builder serviceProto) {
OverviewComponentObserverProto.Builder overviewComponentObserver =
OverviewComponentObserverProto.newBuilder();
overviewComponentObserver.setOverviewActivityStarted(mActivityInterface.isStarted());
overviewComponentObserver.setOverviewActivityResumed(mActivityInterface.isResumed());
serviceProto.setOverviewComponentObvserver(overviewComponentObserver);
}
} }

View File

@ -25,6 +25,7 @@ import androidx.annotation.UiThread;
import com.android.launcher3.util.Preconditions; import com.android.launcher3.util.Preconditions;
import com.android.systemui.shared.recents.model.ThumbnailData; import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.systemui.shared.system.RecentsAnimationControllerCompat; import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat; import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
@ -90,24 +91,6 @@ public class RecentsAnimationController {
} }
} }
/**
* Notifies the controller that we want to defer cancel until the next app transition starts.
* If {@param screenshot} is set, then we will receive a screenshot on the next
* {@link RecentsAnimationCallbacks#onAnimationCanceled(ThumbnailData)} and we must also call
* {@link #cleanupScreenshot()} when that screenshot is no longer used.
*/
public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) {
mController.setDeferCancelUntilNextTransition(defer, screenshot);
}
/**
* Cleans up the screenshot previously returned from
* {@link RecentsAnimationCallbacks#onAnimationCanceled(ThumbnailData)}.
*/
public void cleanupScreenshot() {
UI_HELPER_EXECUTOR.execute(() -> mController.cleanupScreenshot());
}
/** /**
* Remove task remote animation target from * Remove task remote animation target from
* {@link RecentsAnimationCallbacks#onTaskAppeared(RemoteAnimationTargetCompat)}}. * {@link RecentsAnimationCallbacks#onTaskAppeared(RemoteAnimationTargetCompat)}}.
@ -151,6 +134,7 @@ public class RecentsAnimationController {
mOnFinishedListener.accept(this); mOnFinishedListener.accept(this);
UI_HELPER_EXECUTOR.execute(() -> { UI_HELPER_EXECUTOR.execute(() -> {
mController.finish(toRecents, sendUserLeaveHint); mController.finish(toRecents, sendUserLeaveHint);
InteractionJankMonitorWrapper.end(InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH);
if (callback != null) { if (callback != null) {
MAIN_EXECUTOR.execute(callback); MAIN_EXECUTOR.execute(callback);
} }

View File

@ -29,6 +29,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.UiThread; import androidx.annotation.UiThread;
import com.android.launcher3.DeviceProfile; import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.Utilities; import com.android.launcher3.Utilities;
import com.android.launcher3.anim.AnimationSuccessListener; import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController; import com.android.launcher3.anim.AnimatorPlaybackController;
@ -66,6 +67,8 @@ public abstract class SwipeUpAnimationLogic {
// How much further we can drag past recents, as a factor of mTransitionDragLength. // How much further we can drag past recents, as a factor of mTransitionDragLength.
protected float mDragLengthFactor = 1; protected float mDragLengthFactor = 1;
protected final float mMaxShadowRadius;
protected AnimatorControllerWithResistance mWindowTransitionController; protected AnimatorControllerWithResistance mWindowTransitionController;
public SwipeUpAnimationLogic(Context context, RecentsAnimationDeviceState deviceState, public SwipeUpAnimationLogic(Context context, RecentsAnimationDeviceState deviceState,
@ -80,6 +83,9 @@ public abstract class SwipeUpAnimationLogic {
mDeviceState.getRotationTouchHelper().getCurrentActiveRotation(), mDeviceState.getRotationTouchHelper().getCurrentActiveRotation(),
mDeviceState.getRotationTouchHelper().getDisplayRotation()); mDeviceState.getRotationTouchHelper().getDisplayRotation());
mTaskViewSimulator.setDrawsBelowRecents(true); mTaskViewSimulator.setDrawsBelowRecents(true);
mMaxShadowRadius = context.getResources().getDimensionPixelSize(R.dimen.max_shadow_radius);
mTransformParams.setShadowRadius(mMaxShadowRadius);
} }
protected void initTransitionEndpoints(DeviceProfile dp) { protected void initTransitionEndpoints(DeviceProfile dp) {
@ -259,9 +265,11 @@ public abstract class SwipeUpAnimationLogic {
mMatrix.setRectToRect(mCropRectF, mWindowCurrentRect, ScaleToFit.FILL); mMatrix.setRectToRect(mCropRectF, mWindowCurrentRect, ScaleToFit.FILL);
float cornerRadius = Utilities.mapRange(progress, mStartRadius, mEndRadius); float cornerRadius = Utilities.mapRange(progress, mStartRadius, mEndRadius);
float shadowRadius = Utilities.mapRange(progress, mMaxShadowRadius, 0);
mTransformParams mTransformParams
.setTargetAlpha(getWindowAlpha(progress)) .setTargetAlpha(getWindowAlpha(progress))
.setCornerRadius(cornerRadius); .setCornerRadius(cornerRadius)
.setShadowRadius(shadowRadius);
mTransformParams.applySurfaceParams(mTransformParams.createSurfaceParams(this)); mTransformParams.applySurfaceParams(mTransformParams.createSurfaceParams(this));
mAnimationFactory.update(currentRect, progress, mMatrix.mapRadius(cornerRadius)); mAnimationFactory.update(currentRect, progress, mMatrix.mapRadius(cornerRadius));
@ -272,7 +280,8 @@ public abstract class SwipeUpAnimationLogic {
Builder builder, RemoteAnimationTargetCompat app, TransformParams params) { Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
builder.withMatrix(mMatrix) builder.withMatrix(mMatrix)
.withWindowCrop(mCropRect) .withWindowCrop(mCropRect)
.withCornerRadius(params.getCornerRadius()); .withCornerRadius(params.getCornerRadius())
.withShadowRadius(params.getShadowRadius());
} }
@Override @Override

View File

@ -17,6 +17,7 @@ package com.android.quickstep;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR; import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_INITIALIZED; import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_INITIALIZED;
import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_STARTED; import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_STARTED;
@ -31,6 +32,8 @@ import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.ActivityManagerWrapper; import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat; import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
import java.util.function.Consumer;
public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAnimationListener { public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAnimationListener {
private RecentsAnimationController mController; private RecentsAnimationController mController;
@ -39,6 +42,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
// Temporary until we can hook into gesture state events // Temporary until we can hook into gesture state events
private GestureState mLastGestureState; private GestureState mLastGestureState;
private RemoteAnimationTargetCompat mLastAppearedTaskTarget; private RemoteAnimationTargetCompat mLastAppearedTaskTarget;
private Consumer<RemoteAnimationTargetCompat> mLaunchOtherTaskHandler;
/** /**
* Preloads the recents animation. * Preloads the recents animation.
@ -88,22 +92,21 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
@Override @Override
public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) { public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
if (thumbnailData != null) { cleanUpRecentsAnimation();
// If a screenshot is provided, switch to the screenshot before cleaning up
activityInterface.switchRunningTaskViewToScreenshot(thumbnailData,
() -> cleanUpRecentsAnimation(thumbnailData));
} else {
cleanUpRecentsAnimation(null /* canceledThumbnail */);
}
} }
@Override @Override
public void onRecentsAnimationFinished(RecentsAnimationController controller) { public void onRecentsAnimationFinished(RecentsAnimationController controller) {
cleanUpRecentsAnimation(null /* canceledThumbnail */); cleanUpRecentsAnimation();
} }
@Override @Override
public void onTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) { public void onTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
if (mLaunchOtherTaskHandler != null
&& mLastGestureState.getEndTarget() == RECENTS) {
mLaunchOtherTaskHandler.accept(appearedTaskTarget);
return;
}
if (mController != null) { if (mController != null) {
if (mLastAppearedTaskTarget == null if (mLastAppearedTaskTarget == null
|| appearedTaskTarget.taskId != mLastAppearedTaskTarget.taskId) { || appearedTaskTarget.taskId != mLastAppearedTaskTarget.taskId) {
@ -138,6 +141,15 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
return mCallbacks; return mCallbacks;
} }
/**
* The passed-in handler is used to render side task launch animation in recents in live tile
* mode.
*/
public void setLaunchOtherTaskInLiveTileModeHandler(
Consumer<RemoteAnimationTargetCompat> handler) {
mLaunchOtherTaskHandler = handler;
}
/** /**
* Finishes the running recents animation. * Finishes the running recents animation.
*/ */
@ -147,7 +159,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), toHome Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), toHome
? mController::finishAnimationToHome ? mController::finishAnimationToHome
: mController::finishAnimationToApp); : mController::finishAnimationToApp);
cleanUpRecentsAnimation(null /* canceledThumbnail */); cleanUpRecentsAnimation();
} }
} }
@ -174,12 +186,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
/** /**
* Cleans up the recents animation entirely. * Cleans up the recents animation entirely.
*/ */
private void cleanUpRecentsAnimation(ThumbnailData canceledThumbnail) { private void cleanUpRecentsAnimation() {
// Clean up the screenshot if necessary
if (mController != null && canceledThumbnail != null) {
mController.cleanupScreenshot();
}
// Release all the target leashes // Release all the target leashes
if (mTargets != null) { if (mTargets != null) {
mTargets.release(); mTargets.release();
@ -195,6 +202,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
mTargets = null; mTargets = null;
mLastGestureState = null; mLastGestureState = null;
mLastAppearedTaskTarget = null; mLastAppearedTaskTarget = null;
mLaunchOtherTaskHandler = null;
} }
public void dump() { public void dump() {

View File

@ -21,12 +21,14 @@ import static com.android.launcher3.uioverrides.QuickstepLauncher.GO_LOW_RAM_REC
import android.app.ActivityManager.TaskDescription; import android.app.ActivityManager.TaskDescription;
import android.content.Context; import android.content.Context;
import android.content.pm.ActivityInfo; import android.content.pm.ActivityInfo;
import android.content.pm.PackageManager;
import android.content.res.Resources; import android.content.res.Resources;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Build; import android.os.Build;
import android.os.UserHandle; import android.os.UserHandle;
import android.text.TextUtils;
import android.util.SparseArray; import android.util.SparseArray;
import android.view.accessibility.AccessibilityManager; import android.view.accessibility.AccessibilityManager;
@ -34,6 +36,7 @@ import androidx.annotation.WorkerThread;
import com.android.launcher3.FastBitmapDrawable; import com.android.launcher3.FastBitmapDrawable;
import com.android.launcher3.R; import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.icons.BitmapInfo; import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.IconProvider; import com.android.launcher3.icons.IconProvider;
import com.android.launcher3.icons.LauncherIcons; import com.android.launcher3.icons.LauncherIcons;
@ -42,7 +45,6 @@ import com.android.quickstep.util.CancellableTask;
import com.android.quickstep.util.TaskKeyLruCache; import com.android.quickstep.util.TaskKeyLruCache;
import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.recents.model.Task.TaskKey; import com.android.systemui.shared.recents.model.Task.TaskKey;
import com.android.systemui.shared.system.ActivityManagerWrapper;
import com.android.systemui.shared.system.PackageManagerWrapper; import com.android.systemui.shared.system.PackageManagerWrapper;
import com.android.systemui.shared.system.TaskDescriptionCompat; import com.android.systemui.shared.system.TaskDescriptionCompat;
@ -163,9 +165,8 @@ public class TaskIconCache {
key.getComponent(), key.userId); key.getComponent(), key.userId);
} }
if (activityInfo != null) { if (activityInfo != null) {
entry.contentDescription = ActivityManagerWrapper.getInstance() entry.contentDescription = getBadgedContentDescription(
.getBadgedContentDescription(activityInfo, task.key.userId, activityInfo, task.key.userId, task.taskDescription);
task.taskDescription);
} }
} }
@ -173,6 +174,21 @@ public class TaskIconCache {
return entry; return entry;
} }
private String getBadgedContentDescription(ActivityInfo info, int userId, TaskDescription td) {
PackageManager pm = mContext.getPackageManager();
String taskLabel = td == null ? null : Utilities.trim(td.getLabel());
if (TextUtils.isEmpty(taskLabel)) {
taskLabel = Utilities.trim(info.loadLabel(pm));
}
String applicationLabel = Utilities.trim(info.applicationInfo.loadLabel(pm));
String badgedApplicationLabel = userId != UserHandle.myUserId()
? pm.getUserBadgedLabel(applicationLabel, UserHandle.of(userId)).toString()
: applicationLabel;
return applicationLabel.equals(taskLabel)
? badgedApplicationLabel : badgedApplicationLabel + " " + taskLabel;
}
@WorkerThread @WorkerThread
private Drawable getDefaultIcon(int userId) { private Drawable getDefaultIcon(int userId) {
synchronized (mDefaultIcons) { synchronized (mDefaultIcons) {

View File

@ -17,15 +17,20 @@ package com.android.quickstep;
import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA; import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
import static com.android.launcher3.LauncherState.BACKGROUND_APP; import static com.android.launcher3.LauncherState.BACKGROUND_APP;
import static com.android.launcher3.LauncherState.NORMAL;
import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION;
import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor; import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
import static com.android.launcher3.anim.Interpolators.LINEAR; import static com.android.launcher3.anim.Interpolators.LINEAR;
import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR; import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
import static com.android.launcher3.anim.Interpolators.clampToProgress; import static com.android.launcher3.anim.Interpolators.clampToProgress;
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
import static com.android.launcher3.statehandlers.DepthController.DEPTH; import static com.android.launcher3.statehandlers.DepthController.DEPTH;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING; import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
import android.animation.Animator; import android.animation.Animator;
import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.annotation.TargetApi; import android.annotation.TargetApi;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.Context; import android.content.Context;
@ -35,12 +40,18 @@ import android.graphics.RectF;
import android.os.Build; import android.os.Build;
import android.view.View; import android.view.View;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.BaseActivity; import com.android.launcher3.BaseActivity;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile; import com.android.launcher3.DeviceProfile;
import com.android.launcher3.anim.AnimationSuccessListener;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.statehandlers.DepthController; import com.android.launcher3.statehandlers.DepthController;
import com.android.launcher3.statemanager.StateManager;
import com.android.launcher3.util.DisplayController; import com.android.launcher3.util.DisplayController;
import com.android.quickstep.util.SurfaceTransactionApplier; import com.android.quickstep.util.SurfaceTransactionApplier;
import com.android.quickstep.util.TaskViewSimulator; import com.android.quickstep.util.TaskViewSimulator;
@ -49,6 +60,7 @@ import com.android.quickstep.views.RecentsView;
import com.android.quickstep.views.TaskThumbnailView; import com.android.quickstep.views.TaskThumbnailView;
import com.android.quickstep.views.TaskView; import com.android.quickstep.views.TaskView;
import com.android.systemui.shared.recents.model.Task; import com.android.systemui.shared.recents.model.Task;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import com.android.systemui.shared.system.RemoteAnimationTargetCompat; import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
/** /**
@ -67,8 +79,7 @@ public final class TaskViewUtils {
* opening remote target (which we don't get until onAnimationStart) will resolve to a TaskView. * opening remote target (which we don't get until onAnimationStart) will resolve to a TaskView.
*/ */
public static TaskView findTaskViewToLaunch( public static TaskView findTaskViewToLaunch(
BaseDraggingActivity activity, View v, RemoteAnimationTargetCompat[] targets) { RecentsView recentsView, View v, RemoteAnimationTargetCompat[] targets) {
RecentsView recentsView = activity.getOverviewPanel();
if (v instanceof TaskView) { if (v instanceof TaskView) {
TaskView taskView = (TaskView) v; TaskView taskView = (TaskView) v;
return recentsView.isTaskViewVisible(taskView) ? taskView : null; return recentsView.isTaskViewVisible(taskView) ? taskView : null;
@ -119,6 +130,21 @@ public final class TaskViewUtils {
return taskView; return taskView;
} }
public static void createRecentsWindowAnimator(TaskView v, boolean skipViewChanges,
RemoteAnimationTargetCompat[] appTargets,
RemoteAnimationTargetCompat[] wallpaperTargets, DepthController depthController,
PendingAnimation out) {
boolean isRunningTask = v.isRunningTask();
TransformParams params = null;
TaskViewSimulator tsv = null;
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && isRunningTask) {
params = v.getRecentsView().getLiveTileParams();
tsv = v.getRecentsView().getLiveTileTaskViewSimulator();
}
createRecentsWindowAnimator(v, skipViewChanges, appTargets, wallpaperTargets,
depthController, out, params, tsv);
}
/** /**
* Creates an animation that controls the window of the opening targets for the recents launch * Creates an animation that controls the window of the opening targets for the recents launch
* animation. * animation.
@ -126,16 +152,25 @@ public final class TaskViewUtils {
public static void createRecentsWindowAnimator(TaskView v, boolean skipViewChanges, public static void createRecentsWindowAnimator(TaskView v, boolean skipViewChanges,
RemoteAnimationTargetCompat[] appTargets, RemoteAnimationTargetCompat[] appTargets,
RemoteAnimationTargetCompat[] wallpaperTargets, DepthController depthController, RemoteAnimationTargetCompat[] wallpaperTargets, DepthController depthController,
PendingAnimation out) { PendingAnimation out, @Nullable TransformParams params,
@Nullable TaskViewSimulator tsv) {
boolean isQuickSwitch = v.isEndQuickswitchCuj();
v.setEndQuickswitchCuj(false);
SurfaceTransactionApplier applier = new SurfaceTransactionApplier(v); boolean inLiveTileMode =
ENABLE_QUICKSTEP_LIVE_TILE.get() && v.getRecentsView().getRunningTaskIndex() != -1;
final RemoteAnimationTargets targets = final RemoteAnimationTargets targets =
new RemoteAnimationTargets(appTargets, wallpaperTargets, MODE_OPENING); new RemoteAnimationTargets(appTargets, wallpaperTargets,
inLiveTileMode ? MODE_CLOSING : MODE_OPENING);
if (params == null) {
SurfaceTransactionApplier applier = new SurfaceTransactionApplier(v);
targets.addReleaseCheck(applier); targets.addReleaseCheck(applier);
TransformParams params = new TransformParams() params = new TransformParams()
.setSyncTransactionApplier(applier) .setSyncTransactionApplier(applier)
.setTargetSet(targets); .setTargetSet(targets);
}
final RecentsView recentsView = v.getRecentsView(); final RecentsView recentsView = v.getRecentsView();
int taskIndex = recentsView.indexOfChild(v); int taskIndex = recentsView.indexOfChild(v);
@ -149,8 +184,9 @@ public final class TaskViewUtils {
int displayRotation = DisplayController.getDefaultDisplay(context).getInfo().rotation; int displayRotation = DisplayController.getDefaultDisplay(context).getInfo().rotation;
TaskViewSimulator topMostSimulator = null; TaskViewSimulator topMostSimulator = null;
if (targets.apps.length > 0) {
TaskViewSimulator tsv = new TaskViewSimulator(context, recentsView.getSizeStrategy()); if (tsv == null && targets.apps.length > 0) {
tsv = new TaskViewSimulator(context, recentsView.getSizeStrategy());
tsv.setDp(dp); tsv.setDp(dp);
tsv.setLayoutRotation(displayRotation, displayRotation); tsv.setLayoutRotation(displayRotation, displayRotation);
tsv.setPreview(targets.apps[targets.apps.length - 1]); tsv.setPreview(targets.apps[targets.apps.length - 1]);
@ -158,19 +194,24 @@ public final class TaskViewUtils {
tsv.recentsViewScale.value = 1; tsv.recentsViewScale.value = 1;
tsv.setScroll(startScroll); tsv.setScroll(startScroll);
// Fade in the task during the initial 20% of the animation
out.addFloat(params, TransformParams.TARGET_ALPHA, 0, 1,
clampToProgress(LINEAR, 0, 0.2f));
}
if (tsv != null) {
out.setFloat(tsv.fullScreenProgress, out.setFloat(tsv.fullScreenProgress,
AnimatedFloat.VALUE, 1, TOUCH_RESPONSE_INTERPOLATOR); AnimatedFloat.VALUE, 1, TOUCH_RESPONSE_INTERPOLATOR);
out.setFloat(tsv.recentsViewScale, out.setFloat(tsv.recentsViewScale,
AnimatedFloat.VALUE, tsv.getFullScreenScale(), TOUCH_RESPONSE_INTERPOLATOR); AnimatedFloat.VALUE, tsv.getFullScreenScale(), TOUCH_RESPONSE_INTERPOLATOR);
out.setInt(tsv, TaskViewSimulator.SCROLL, 0, TOUCH_RESPONSE_INTERPOLATOR); out.setInt(tsv, TaskViewSimulator.SCROLL, 0, TOUCH_RESPONSE_INTERPOLATOR);
out.addOnFrameCallback(() -> tsv.apply(params)); TaskViewSimulator finalTsv = tsv;
TransformParams finalParams = params;
out.addOnFrameCallback(() -> finalTsv.apply(finalParams));
topMostSimulator = tsv; topMostSimulator = tsv;
} }
// Fade in the task during the initial 20% of the animation
out.addFloat(params, TransformParams.TARGET_ALPHA, 0, 1, clampToProgress(LINEAR, 0, 0.2f));
if (!skipViewChanges && parallaxCenterAndAdjacentTask && topMostSimulator != null) { if (!skipViewChanges && parallaxCenterAndAdjacentTask && topMostSimulator != null) {
out.addFloat(v, VIEW_ALPHA, 1, 0, clampToProgress(LINEAR, 0.2f, 0.4f)); out.addFloat(v, VIEW_ALPHA, 1, 0, clampToProgress(LINEAR, 0.2f, 0.4f));
@ -223,10 +264,19 @@ public final class TaskViewUtils {
}); });
} }
out.addListener(new AnimatorListenerAdapter() { out.addListener(new AnimationSuccessListener() {
@Override
public void onAnimationSuccess(Animator animator) {
if (isQuickSwitch) {
InteractionJankMonitorWrapper.end(
InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH);
}
}
@Override @Override
public void onAnimationEnd(Animator animation) { public void onAnimationEnd(Animator animation) {
targets.release(); targets.release();
super.onAnimationEnd(animation);
} }
}); });
@ -235,4 +285,57 @@ public final class TaskViewUtils {
TOUCH_RESPONSE_INTERPOLATOR); TOUCH_RESPONSE_INTERPOLATOR);
} }
} }
public static void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
@NonNull RemoteAnimationTargetCompat[] appTargets,
@NonNull RemoteAnimationTargetCompat[] wallpaperTargets, boolean launcherClosing,
@NonNull StateManager stateManager, @NonNull RecentsView recentsView,
@NonNull DepthController depthController) {
boolean skipLauncherChanges = !launcherClosing;
TaskView taskView = findTaskViewToLaunch(recentsView, v, appTargets);
PendingAnimation pa = new PendingAnimation(RECENTS_LAUNCH_DURATION);
createRecentsWindowAnimator(taskView, skipLauncherChanges, appTargets, wallpaperTargets,
depthController, pa);
anim.play(pa.buildAnim());
Animator childStateAnimation = null;
// Found a visible recents task that matches the opening app, lets launch the app from there
Animator launcherAnim;
final AnimatorListenerAdapter windowAnimEndListener;
if (launcherClosing) {
launcherAnim = recentsView.createAdjacentPageAnimForTaskLaunch(taskView);
launcherAnim.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR);
launcherAnim.setDuration(RECENTS_LAUNCH_DURATION);
// Make sure recents gets fixed up by resetting task alphas and scales, etc.
windowAnimEndListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
stateManager.moveToRestState();
stateManager.reapplyState();
}
};
} else {
AnimatorPlaybackController controller =
stateManager.createAnimationToNewWorkspace(NORMAL,
RECENTS_LAUNCH_DURATION);
controller.dispatchOnStart();
childStateAnimation = controller.getTarget();
launcherAnim = controller.getAnimationPlayer().setDuration(RECENTS_LAUNCH_DURATION);
windowAnimEndListener = new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
stateManager.goToState(NORMAL, false);
}
};
}
anim.play(launcherAnim);
// Set the current animation first, before adding windowAnimEndListener. Setting current
// animation adds some listeners which need to be called before windowAnimEndListener
// (the ordering of listeners matter in this case).
stateManager.setCurrentAnimation(anim, childStateAnimation);
anim.addListener(windowAnimEndListener);
}
} }

View File

@ -737,9 +737,12 @@ public class TouchInteractionService extends Service implements PluginListener<O
private void reset() { private void reset() {
mConsumer = mUncheckedConsumer = mResetGestureInputConsumer; mConsumer = mUncheckedConsumer = mResetGestureInputConsumer;
mGestureState = DEFAULT_STATE; mGestureState = DEFAULT_STATE;
// By default, use batching of the input events // By default, use batching of the input events, but check receiver before using in the rare
// case that the monitor was disposed before the swipe settled
if (mInputEventReceiver != null) {
mInputEventReceiver.setBatchingEnabled(true); mInputEventReceiver.setBatchingEnabled(true);
} }
}
private void preloadOverview(boolean fromInit) { private void preloadOverview(boolean fromInit) {
if (!mDeviceState.isUserUnlocked()) { if (!mDeviceState.isUserUnlocked()) {
@ -895,6 +898,12 @@ public class TouchInteractionService extends Service implements PluginListener<O
TouchInteractionServiceProto.Builder serviceProto = TouchInteractionServiceProto.Builder serviceProto =
TouchInteractionServiceProto.newBuilder(); TouchInteractionServiceProto.newBuilder();
serviceProto.setServiceConnected(true); serviceProto.setServiceConnected(true);
if (mOverviewComponentObserver != null) {
mOverviewComponentObserver.writeToProto(serviceProto);
}
mConsumer.writeToProto(serviceProto);
proto.setTouchInteractionService(serviceProto); proto.setTouchInteractionService(serviceProto);
} }
} }

View File

@ -15,21 +15,23 @@
*/ */
package com.android.quickstep; package com.android.quickstep;
import android.graphics.Canvas; import android.os.Handler;
import android.view.View; import android.view.View;
import com.android.systemui.shared.system.WindowCallbacksCompat; import com.android.launcher3.Utilities;
import com.android.systemui.shared.system.ViewRootImplCompat;
import java.util.function.BooleanSupplier; import java.util.function.BooleanSupplier;
import java.util.function.LongConsumer;
/** /**
* Utility class for helpful methods related to {@link View} objects. * Utility class for helpful methods related to {@link View} objects.
*/ */
public class ViewUtils { public class ViewUtils {
/** See {@link #postDraw(View, Runnable, BooleanSupplier)}} */ /** See {@link #postFrameDrawn(View, Runnable, BooleanSupplier)}} */
public static boolean postDraw(View view, Runnable onFinishRunnable) { public static boolean postFrameDrawn(View view, Runnable onFinishRunnable) {
return postDraw(view, onFinishRunnable, () -> false); return postFrameDrawn(view, onFinishRunnable, () -> false);
} }
/** /**
@ -38,37 +40,55 @@ public class ViewUtils {
* *
* @param onFinishRunnable runnable to be run right after the view finishes drawing. * @param onFinishRunnable runnable to be run right after the view finishes drawing.
*/ */
public static boolean postDraw(View view, Runnable onFinishRunnable, BooleanSupplier canceled) { public static boolean postFrameDrawn(
// Defer finishing the animation until the next launcher frame with the View view, Runnable onFinishRunnable, BooleanSupplier canceled) {
// new thumbnail return new FrameHandler(view, onFinishRunnable, canceled).schedule();
return new WindowCallbacksCompat(view) { }
// The number of frames to defer until we actually finish the animation
private int mDeferFrameCount = 2; private static class FrameHandler implements LongConsumer {
final ViewRootImplCompat mViewRoot;
final Runnable mFinishCallback;
final BooleanSupplier mCancelled;
final Handler mHandler;
int mDeferFrameCount = 1;
FrameHandler(View view, Runnable finishCallback, BooleanSupplier cancelled) {
mViewRoot = new ViewRootImplCompat(view);
mFinishCallback = finishCallback;
mCancelled = cancelled;
mHandler = new Handler();
}
@Override @Override
public void onPostDraw(Canvas canvas) { public void accept(long l) {
// If we were cancelled after this was attached, do not update Utilities.postAsyncCallback(mHandler, this::onFrame);
// the state. }
if (canceled.getAsBoolean()) {
detach(); private void onFrame() {
if (mCancelled.getAsBoolean()) {
return; return;
} }
if (mDeferFrameCount > 0) { if (mDeferFrameCount > 0) {
mDeferFrameCount--; mDeferFrameCount--;
// Workaround, detach and reattach to invalidate the root node for schedule();
// another draw
detach();
attach();
view.invalidate();
return; return;
} }
if (onFinishRunnable != null) { if (mFinishCallback != null) {
onFinishRunnable.run(); mFinishCallback.run();
} }
detach(); }
}
}.attach(); private boolean schedule() {
if (mViewRoot.isValid()) {
mViewRoot.registerRtFrameCallback(this);
mViewRoot.getView().invalidate();
return true;
}
return false;
}
} }
} }

View File

@ -4,6 +4,7 @@ import android.view.MotionEvent;
import com.android.launcher3.testing.TestLogging; import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.tracing.InputConsumerProto;
import com.android.quickstep.InputConsumer; import com.android.quickstep.InputConsumer;
import com.android.systemui.shared.system.InputMonitorCompat; import com.android.systemui.shared.system.InputMonitorCompat;
@ -53,4 +54,9 @@ public abstract class DelegateInputConsumer implements InputConsumer {
mDelegate.onMotionEvent(event); mDelegate.onMotionEvent(event);
event.recycle(); event.recycle();
} }
@Override
public void writeToProtoInternal(InputConsumerProto.Builder inputConsumerProto) {
mDelegate.writeToProtoInternal(inputConsumerProto);
}
} }

View File

@ -51,6 +51,7 @@ import androidx.annotation.UiThread;
import com.android.launcher3.R; import com.android.launcher3.R;
import com.android.launcher3.testing.TestLogging; import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.tracing.InputConsumerProto;
import com.android.launcher3.util.Preconditions; import com.android.launcher3.util.Preconditions;
import com.android.launcher3.util.TraceHelper; import com.android.launcher3.util.TraceHelper;
import com.android.quickstep.AbsSwipeUpHandler; import com.android.quickstep.AbsSwipeUpHandler;
@ -478,4 +479,11 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
public boolean allowInterceptByParent() { public boolean allowInterceptByParent() {
return !mPassedPilferInputSlop || mGestureState.hasState(STATE_OVERSCROLL_WINDOW_CREATED); return !mPassedPilferInputSlop || mGestureState.hasState(STATE_OVERSCROLL_WINDOW_CREATED);
} }
@Override
public void writeToProtoInternal(InputConsumerProto.Builder inputConsumerProto) {
if (mInteractionHandler != null) {
mInteractionHandler.writeToProto(inputConsumerProto);
}
}
} }

View File

@ -104,7 +104,13 @@ final class AssistantGestureTutorialController extends TutorialController {
hideFeedback(); hideFeedback();
hideHandCoachingAnimation(); hideHandCoachingAnimation();
showRippleEffect( showRippleEffect(
() -> mTutorialFragment.changeController(ASSISTANT_COMPLETE)); () -> {
if (mTutorialFragment.isTutorialComplete()) {
mTutorialFragment.changeController(ASSISTANT_COMPLETE);
} else {
mTutorialFragment.continueTutorial();
}
});
break; break;
case ASSISTANT_NOT_STARTED_BAD_ANGLE: case ASSISTANT_NOT_STARTED_BAD_ANGLE:
showFeedback(R.string.assistant_gesture_feedback_swipe_not_diagonal); showFeedback(R.string.assistant_gesture_feedback_swipe_not_diagonal);

View File

@ -130,7 +130,13 @@ final class BackGestureTutorialController extends TutorialController {
hideFeedback(); hideFeedback();
hideHandCoachingAnimation(); hideHandCoachingAnimation();
showRippleEffect( showRippleEffect(
() -> mTutorialFragment.changeController(BACK_NAVIGATION_COMPLETE)); () -> {
if (mTutorialFragment.isTutorialComplete()) {
mTutorialFragment.changeController(BACK_NAVIGATION_COMPLETE);
} else {
mTutorialFragment.continueTutorial();
}
});
break; break;
case BACK_CANCELLED_FROM_LEFT: case BACK_CANCELLED_FROM_LEFT:
showFeedback(R.string.back_gesture_feedback_cancelled_left_edge); showFeedback(R.string.back_gesture_feedback_cancelled_left_edge);

View File

@ -15,8 +15,6 @@
*/ */
package com.android.quickstep.interaction; package com.android.quickstep.interaction;
import static com.android.quickstep.interaction.TutorialFragment.KEY_TUTORIAL_TYPE;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.Rect; import android.graphics.Rect;
import android.os.Bundle; import android.os.Bundle;
@ -25,11 +23,14 @@ import android.view.Display;
import android.view.View; import android.view.View;
import android.view.Window; import android.view.Window;
import androidx.annotation.NonNull;
import androidx.fragment.app.FragmentActivity; import androidx.fragment.app.FragmentActivity;
import com.android.launcher3.R; import com.android.launcher3.R;
import com.android.quickstep.interaction.TutorialController.TutorialType; import com.android.quickstep.interaction.TutorialController.TutorialType;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.List; import java.util.List;
/** Shows the gesture interactive sandbox in full screen mode. */ /** Shows the gesture interactive sandbox in full screen mode. */
@ -37,6 +38,9 @@ public class GestureSandboxActivity extends FragmentActivity {
private static final String LOG_TAG = "GestureSandboxActivity"; private static final String LOG_TAG = "GestureSandboxActivity";
private static final String KEY_TUTORIAL_STEPS = "tutorial_steps";
private Deque<TutorialType> mTutorialSteps;
private TutorialFragment mFragment; private TutorialFragment mFragment;
@Override @Override
@ -45,7 +49,9 @@ public class GestureSandboxActivity extends FragmentActivity {
requestWindowFeature(Window.FEATURE_NO_TITLE); requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.gesture_tutorial_activity); setContentView(R.layout.gesture_tutorial_activity);
mFragment = TutorialFragment.newInstance(getTutorialType(getIntent().getExtras())); Bundle args = savedInstanceState == null ? getIntent().getExtras() : savedInstanceState;
mTutorialSteps = getTutorialSteps(args);
mFragment = TutorialFragment.newInstance(mTutorialSteps.pop());
getSupportFragmentManager().beginTransaction() getSupportFragmentManager().beginTransaction()
.add(R.id.gesture_tutorial_fragment_container, mFragment) .add(R.id.gesture_tutorial_fragment_container, mFragment)
.commit(); .commit();
@ -72,17 +78,65 @@ public class GestureSandboxActivity extends FragmentActivity {
} }
} }
private TutorialType getTutorialType(Bundle extras) { @Override
TutorialType defaultType = TutorialType.RIGHT_EDGE_BACK_NAVIGATION; protected void onSaveInstanceState(@NonNull Bundle savedInstanceState) {
savedInstanceState.putStringArray(KEY_TUTORIAL_STEPS, getTutorialStepNames());
super.onSaveInstanceState(savedInstanceState);
}
if (extras == null || !extras.containsKey(KEY_TUTORIAL_TYPE)) { /** Returns true iff there aren't anymore tutorial types to display to the user. */
return defaultType; public boolean isTutorialComplete() {
return mTutorialSteps.isEmpty();
} }
try {
return TutorialType.valueOf(extras.getString(KEY_TUTORIAL_TYPE, "")); /**
} catch (IllegalArgumentException e) { * Replaces the current TutorialFragment, continuing to the next tutorial step if there is one.
return defaultType; *
* If there is no following step, the tutorial is closed.
*/
public void continueTutorial() {
if (isTutorialComplete()) {
mFragment.closeTutorial();
return;
} }
mFragment = TutorialFragment.newInstance(mTutorialSteps.pop());
getSupportFragmentManager().beginTransaction()
.replace(R.id.gesture_tutorial_fragment_container, mFragment)
.runOnCommit(() -> mFragment.onAttachedToWindow())
.commit();
}
private String[] getTutorialStepNames() {
String[] tutorialStepNames = new String[mTutorialSteps.size()];
int i = 0;
for (TutorialType tutorialStep : mTutorialSteps) {
tutorialStepNames[i++] = tutorialStep.name();
}
return tutorialStepNames;
}
private Deque<TutorialType> getTutorialSteps(Bundle extras) {
Deque<TutorialType> defaultSteps = new ArrayDeque<>();
defaultSteps.push(TutorialType.RIGHT_EDGE_BACK_NAVIGATION);
if (extras == null || !extras.containsKey(KEY_TUTORIAL_STEPS)) {
return defaultSteps;
}
String[] tutorialStepNames = extras.getStringArray(KEY_TUTORIAL_STEPS);
if (tutorialStepNames == null) {
return defaultSteps;
}
Deque<TutorialType> tutorialSteps = new ArrayDeque<>();
for (String tutorialStepName : tutorialStepNames) {
tutorialSteps.addLast(TutorialType.valueOf(tutorialStepName));
}
return tutorialSteps;
} }
private void hideSystemUI() { private void hideSystemUI() {

View File

@ -94,8 +94,13 @@ final class HomeGestureTutorialController extends SwipeUpGestureTutorialControll
case HOME_NAVIGATION: case HOME_NAVIGATION:
switch (result) { switch (result) {
case HOME_GESTURE_COMPLETED: { case HOME_GESTURE_COMPLETED: {
animateFakeTaskViewHome(finalVelocity, () -> animateFakeTaskViewHome(finalVelocity, () -> {
mTutorialFragment.changeController(HOME_NAVIGATION_COMPLETE)); if (mTutorialFragment.isTutorialComplete()) {
mTutorialFragment.changeController(HOME_NAVIGATION_COMPLETE);
} else {
mTutorialFragment.continueTutorial();
}
});
break; break;
} }
case HOME_NOT_STARTED_TOO_FAR_FROM_EDGE: case HOME_NOT_STARTED_TOO_FAR_FROM_EDGE:

View File

@ -104,8 +104,13 @@ final class OverviewGestureTutorialController extends SwipeUpGestureTutorialCont
showFeedback(R.string.overview_gesture_feedback_swipe_too_far_from_edge); showFeedback(R.string.overview_gesture_feedback_swipe_too_far_from_edge);
break; break;
case OVERVIEW_GESTURE_COMPLETED: case OVERVIEW_GESTURE_COMPLETED:
fadeOutFakeTaskView(true, () -> fadeOutFakeTaskView(true, () -> {
mTutorialFragment.changeController(OVERVIEW_NAVIGATION_COMPLETE)); if (mTutorialFragment.isTutorialComplete()) {
mTutorialFragment.changeController(OVERVIEW_NAVIGATION_COMPLETE);
} else {
mTutorialFragment.continueTutorial();
}
});
break; break;
case HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION: case HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION:
case HOME_OR_OVERVIEW_CANCELLED: case HOME_OR_OVERVIEW_CANCELLED:

View File

@ -54,6 +54,7 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener {
fragment = new BackGestureTutorialFragment(); fragment = new BackGestureTutorialFragment();
tutorialType = TutorialType.RIGHT_EDGE_BACK_NAVIGATION; tutorialType = TutorialType.RIGHT_EDGE_BACK_NAVIGATION;
} }
Bundle args = new Bundle(); Bundle args = new Bundle();
args.putSerializable(KEY_TUTORIAL_TYPE, tutorialType); args.putSerializable(KEY_TUTORIAL_TYPE, tutorialType);
fragment.setArguments(args); fragment.setArguments(args);
@ -197,6 +198,20 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener {
return mHandCoachingAnimation; return mHandCoachingAnimation;
} }
void continueTutorial() {
if (!(getContext() instanceof GestureSandboxActivity)) {
closeTutorial();
return;
}
GestureSandboxActivity gestureSandboxActivity = (GestureSandboxActivity) getContext();
if (gestureSandboxActivity == null) {
closeTutorial();
return;
}
gestureSandboxActivity.continueTutorial();
}
void closeTutorial() { void closeTutorial() {
FragmentActivity activity = getActivity(); FragmentActivity activity = getActivity();
if (activity != null) { if (activity != null) {
@ -207,4 +222,13 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener {
void startSystemNavigationSetting() { void startSystemNavigationSetting() {
startActivity(new Intent("com.android.settings.GESTURE_NAVIGATION_SETTINGS")); startActivity(new Intent("com.android.settings.GESTURE_NAVIGATION_SETTINGS"));
} }
boolean isTutorialComplete() {
if (!(getContext() instanceof GestureSandboxActivity)) {
return true;
}
GestureSandboxActivity gestureSandboxActivity = (GestureSandboxActivity) getContext();
return gestureSandboxActivity == null || gestureSandboxActivity.isTutorialComplete();
}
} }

View File

@ -23,6 +23,7 @@ import static android.view.Surface.ROTATION_180;
import static android.view.Surface.ROTATION_270; import static android.view.Surface.ROTATION_270;
import static android.view.Surface.ROTATION_90; import static android.view.Surface.ROTATION_90;
import static com.android.launcher3.Utilities.newContentObserver;
import static com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY; import static com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR; import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS; import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
@ -73,12 +74,9 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre
private static final boolean DEBUG = false; private static final boolean DEBUG = false;
private static final String DELIMITER_DOT = "\\."; private static final String DELIMITER_DOT = "\\.";
private ContentObserver mSystemAutoRotateObserver = new ContentObserver(new Handler()) { private ContentObserver mSystemAutoRotateObserver =
@Override newContentObserver(new Handler(), t -> updateAutoRotateSetting());
public void onChange(boolean selfChange) {
updateAutoRotateSetting();
}
};
@Retention(SOURCE) @Retention(SOURCE)
@IntDef({ROTATION_0, ROTATION_90, ROTATION_180, ROTATION_270}) @IntDef({ROTATION_0, ROTATION_90, ROTATION_180, ROTATION_270})
public @interface SurfaceRotation {} public @interface SurfaceRotation {}
@ -541,6 +539,7 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre
* @return "MyObject@1234" * @return "MyObject@1234"
*/ */
private static String extractObjectNameAndAddress(String stringToExtract) { private static String extractObjectNameAndAddress(String stringToExtract) {
return stringToExtract.substring(stringToExtract.lastIndexOf(DELIMITER_DOT)); int index = stringToExtract.lastIndexOf(DELIMITER_DOT);
return index >= 0 ? stringToExtract.substring(index) : "";
} }
} }

View File

@ -322,7 +322,8 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
Builder builder, RemoteAnimationTargetCompat app, TransformParams params) { Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
builder.withMatrix(mMatrix) builder.withMatrix(mMatrix)
.withWindowCrop(mTmpCropRect) .withWindowCrop(mTmpCropRect)
.withCornerRadius(getCurrentCornerRadius()); .withCornerRadius(getCurrentCornerRadius())
.withShadowRadius(params.getShadowRadius());
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && params.getRecentsSurface() != null) { if (ENABLE_QUICKSTEP_LIVE_TILE.get() && params.getRecentsSurface() != null) {
// When relativeLayer = 0, it reverts the surfaces back to the original order. // When relativeLayer = 0, it reverts the surfaces back to the original order.

View File

@ -57,6 +57,7 @@ public class TransformParams {
private float mProgress; private float mProgress;
private float mTargetAlpha; private float mTargetAlpha;
private float mCornerRadius; private float mCornerRadius;
private float mShadowRadius;
private RemoteAnimationTargets mTargetSet; private RemoteAnimationTargets mTargetSet;
private SurfaceTransactionApplier mSyncTransactionApplier; private SurfaceTransactionApplier mSyncTransactionApplier;
private SurfaceControl mRecentsSurface; private SurfaceControl mRecentsSurface;
@ -68,6 +69,7 @@ public class TransformParams {
mProgress = 0; mProgress = 0;
mTargetAlpha = 1; mTargetAlpha = 1;
mCornerRadius = -1; mCornerRadius = -1;
mShadowRadius = 0;
} }
/** /**
@ -90,6 +92,14 @@ public class TransformParams {
return this; return this;
} }
/**
* Sets the shadow radius of the transformed window, in pixels.
*/
public TransformParams setShadowRadius(float shadowRadius) {
mShadowRadius = shadowRadius;
return this;
}
/** /**
* Specifies the alpha of the transformed window. Default is 1. * Specifies the alpha of the transformed window. Default is 1.
*/ */
@ -197,6 +207,10 @@ public class TransformParams {
return mCornerRadius; return mCornerRadius;
} }
public float getShadowRadius() {
return mShadowRadius;
}
public SurfaceControl getRecentsSurface() { public SurfaceControl getRecentsSurface() {
return mRecentsSurface; return mRecentsSurface;
} }

View File

@ -65,6 +65,10 @@ public class LiveTileOverlay extends Drawable {
invalidateSelf(); invalidateSelf();
} }
public void update(float left, float top, float right, float bottom) {
mCurrentRect.set(left, top, right, bottom);
}
public void setIcon(Drawable icon) { public void setIcon(Drawable icon) {
mIcon = icon; mIcon = icon;
} }
@ -94,7 +98,6 @@ public class LiveTileOverlay extends Drawable {
@Override @Override
public void draw(Canvas canvas) { public void draw(Canvas canvas) {
if (mCurrentRect != null) {
canvas.drawRoundRect(mCurrentRect, mCornerRadius, mCornerRadius, mPaint); canvas.drawRoundRect(mCurrentRect, mCornerRadius, mCornerRadius, mPaint);
if (mIcon != null && mIconAnimationProgress > 0f) { if (mIcon != null && mIconAnimationProgress > 0f) {
canvas.save(); canvas.save();
@ -107,7 +110,6 @@ public class LiveTileOverlay extends Drawable {
canvas.restore(); canvas.restore();
} }
} }
}
@Override @Override
public void setAlpha(int i) { } public void setAlpha(int i) { }

View File

@ -230,6 +230,7 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
view.setScaleX(scale); view.setScaleX(scale);
view.setScaleY(scale); view.setScaleY(scale);
view.mLastComputedTaskPushOutDistance = null; view.mLastComputedTaskPushOutDistance = null;
view.mLiveTileTaskViewSimulator.recentsViewScale.value = scale;
view.updatePageOffsets(); view.updatePageOffsets();
view.setTaskViewsSecondaryTranslation(view.mTaskViewsSecondaryTranslation); view.setTaskViewsSecondaryTranslation(view.mTaskViewsSecondaryTranslation);
} }
@ -539,6 +540,9 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
@Override @Override
protected void onWindowVisibilityChanged(int visibility) { protected void onWindowVisibilityChanged(int visibility) {
super.onWindowVisibilityChanged(visibility); super.onWindowVisibilityChanged(visibility);
if (visibility == GONE && ENABLE_QUICKSTEP_LIVE_TILE.get()) {
finishRecentsAnimation(true /* toRecents */, null);
}
updateTaskStackListenerState(); updateTaskStackListenerState();
} }
@ -873,6 +877,10 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
mLiveTileTaskViewSimulator.fullScreenProgress.value = 0; mLiveTileTaskViewSimulator.fullScreenProgress.value = 0;
mLiveTileTaskViewSimulator.recentsViewScale.value = 1; mLiveTileTaskViewSimulator.recentsViewScale.value = 1;
mLiveTileTaskViewSimulator.setOffsetY(0); mLiveTileTaskViewSimulator.setOffsetY(0);
// Reset the live tile rect
DeviceProfile deviceProfile = mActivity.getDeviceProfile();
LiveTileOverlay.INSTANCE.update(0, 0, deviceProfile.widthPx, deviceProfile.heightPx);
} }
if (mRunningTaskTileHidden) { if (mRunningTaskTileHidden) {
setRunningTaskHidden(mRunningTaskTileHidden); setRunningTaskHidden(mRunningTaskTileHidden);
@ -1292,19 +1300,26 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
} }
public void showNextTask() { public void showNextTask() {
TaskView runningTaskView = getRunningTaskView(); final TaskView runningTaskView = getRunningTaskView();
final TaskView targetTask;
if (runningTaskView == null) { if (runningTaskView == null) {
// Launch the first task // Launch the first task
if (getTaskViewCount() > 0) { if (getTaskViewCount() > 0) {
getTaskViewAt(0).launchTask(true); targetTask = getTaskViewAt(0);
} else {
return;
} }
} else { } else {
if (getNextTaskView() != null) { final TaskView nextTask = getNextTaskView();
getNextTaskView().launchTask(true); if (nextTask != null) {
targetTask = nextTask;
} else { } else {
runningTaskView.launchTask(true); targetTask = runningTaskView;
} }
} }
targetTask.setEndQuickswitchCuj(true);
targetTask.launchTask(true);
} }
public void setRunningTaskIconScaledDown(boolean isScaledDown) { public void setRunningTaskIconScaledDown(boolean isScaledDown) {
@ -1976,6 +1991,7 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
TaskView task = getTaskViewAt(i); TaskView task = getTaskViewAt(i);
mOrientationHandler.getSecondaryViewTranslate().set(task, translation / getScaleY()); mOrientationHandler.getSecondaryViewTranslate().set(task, translation / getScaleY());
} }
mLiveTileTaskViewSimulator.recentsViewSecondaryTranslation.value = translation;
} }
/** /**
@ -2254,6 +2270,10 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
return mLiveTileTaskViewSimulator; return mLiveTileTaskViewSimulator;
} }
public TransformParams getLiveTileParams() {
return mLiveTileParams;
}
// TODO: To be removed in a follow up CL // TODO: To be removed in a follow up CL
public void setRecentsAnimationTargets(RecentsAnimationController recentsAnimationController, public void setRecentsAnimationTargets(RecentsAnimationController recentsAnimationController,
RecentsAnimationTargets recentsAnimationTargets) { RecentsAnimationTargets recentsAnimationTargets) {
@ -2442,7 +2462,7 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
} else { } else {
taskView.getThumbnail().refresh(); taskView.getThumbnail().refresh();
} }
ViewUtils.postDraw(taskView, onFinishRunnable); ViewUtils.postFrameDrawn(taskView, onFinishRunnable);
} else { } else {
onFinishRunnable.run(); onFinishRunnable.run();
} }

View File

@ -39,6 +39,7 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH
import android.animation.Animator; import android.animation.Animator;
import android.animation.AnimatorListenerAdapter; import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator; import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator; import android.animation.TimeInterpolator;
import android.animation.ValueAnimator; import android.animation.ValueAnimator;
@ -65,7 +66,6 @@ import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.Toast; import android.widget.Toast;
import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.DeviceProfile; import com.android.launcher3.DeviceProfile;
import com.android.launcher3.LauncherSettings; import com.android.launcher3.LauncherSettings;
import com.android.launcher3.R; import com.android.launcher3.R;
@ -75,6 +75,7 @@ import com.android.launcher3.anim.Interpolators;
import com.android.launcher3.anim.PendingAnimation; import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.popup.SystemShortcut; import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.statemanager.StatefulActivity;
import com.android.launcher3.testing.TestLogging; import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.TestProtocol; import com.android.launcher3.testing.TestProtocol;
import com.android.launcher3.touch.PagedOrientationHandler; import com.android.launcher3.touch.PagedOrientationHandler;
@ -82,10 +83,12 @@ import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.TransformingTouchDelegate; import com.android.launcher3.util.TransformingTouchDelegate;
import com.android.launcher3.util.ViewPool.Reusable; import com.android.launcher3.util.ViewPool.Reusable;
import com.android.quickstep.RecentsModel; import com.android.quickstep.RecentsModel;
import com.android.quickstep.RemoteAnimationTargets;
import com.android.quickstep.TaskIconCache; import com.android.quickstep.TaskIconCache;
import com.android.quickstep.TaskOverlayFactory; import com.android.quickstep.TaskOverlayFactory;
import com.android.quickstep.TaskThumbnailCache; import com.android.quickstep.TaskThumbnailCache;
import com.android.quickstep.TaskUtils; import com.android.quickstep.TaskUtils;
import com.android.quickstep.TaskViewUtils;
import com.android.quickstep.util.CancellableTask; import com.android.quickstep.util.CancellableTask;
import com.android.quickstep.util.RecentsOrientedState; import com.android.quickstep.util.RecentsOrientedState;
import com.android.quickstep.util.TaskCornerRadius; import com.android.quickstep.util.TaskCornerRadius;
@ -175,7 +178,7 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
private float mCurveScale; private float mCurveScale;
private float mFullscreenProgress; private float mFullscreenProgress;
private final FullscreenDrawParams mCurrentFullscreenParams; private final FullscreenDrawParams mCurrentFullscreenParams;
private final BaseDraggingActivity mActivity; private final StatefulActivity mActivity;
private ObjectAnimator mIconAndDimAnimator; private ObjectAnimator mIconAndDimAnimator;
private float mIconScaleAnimStartProgress = 0; private float mIconScaleAnimStartProgress = 0;
@ -189,6 +192,8 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
private CancellableTask mThumbnailLoadRequest; private CancellableTask mThumbnailLoadRequest;
private CancellableTask mIconLoadRequest; private CancellableTask mIconLoadRequest;
private boolean mEndQuickswitchCuj;
// Order in which the footers appear. Lower order appear below higher order. // Order in which the footers appear. Lower order appear below higher order.
public static final int INDEX_DIGITAL_WELLBEING_TOAST = 0; public static final int INDEX_DIGITAL_WELLBEING_TOAST = 0;
private final FooterWrapper[] mFooters = new FooterWrapper[2]; private final FooterWrapper[] mFooters = new FooterWrapper[2];
@ -210,18 +215,31 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
public TaskView(Context context, AttributeSet attrs, int defStyleAttr) { public TaskView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr); super(context, attrs, defStyleAttr);
mActivity = BaseDraggingActivity.fromContext(context); mActivity = StatefulActivity.fromContext(context);
setOnClickListener((view) -> { setOnClickListener((view) -> {
if (getTask() == null) { if (getTask() == null) {
return; return;
} }
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) { if (ENABLE_QUICKSTEP_LIVE_TILE.get() && isRunningTask()) {
if (isRunningTask()) { RecentsView recentsView = getRecentsView();
// TODO: Replace this animation with createRecentsWindowAnimator RemoteAnimationTargets targets = recentsView.getLiveTileParams().getTargetSet();
createLaunchAnimationForRunningTask().start(); recentsView.getLiveTileTaskViewSimulator().setDrawsBelowRecents(false);
} else {
launchTask(true /* animate */); AnimatorSet anim = new AnimatorSet();
TaskViewUtils.composeRecentsLaunchAnimator(
anim, this, targets.apps,
targets.wallpapers, true /* launcherClosing */,
mActivity.getStateManager(), recentsView,
recentsView.getDepthController());
anim.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animator) {
recentsView.getLiveTileTaskViewSimulator().setDrawsBelowRecents(true);
recentsView.finishRecentsAnimation(false, null);
} }
});
anim.start();
} else { } else {
launchTask(true /* animate */); launchTask(true /* animate */);
} }
@ -801,6 +819,14 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
return false; return false;
} }
public boolean isEndQuickswitchCuj() {
return mEndQuickswitchCuj;
}
public void setEndQuickswitchCuj(boolean endQuickswitchCuj) {
mEndQuickswitchCuj = endQuickswitchCuj;
}
private static final class TaskOutlineProvider extends ViewOutlineProvider { private static final class TaskOutlineProvider extends ViewOutlineProvider {
private final int mMarginTop; private final int mMarginTop;

View File

@ -19,6 +19,6 @@
android:viewportHeight="24.0" android:viewportHeight="24.0"
android:viewportWidth="24.0"> android:viewportWidth="24.0">
<path <path
android:fillColor="?android:attr/colorAccent" android:fillColor="?android:attr/textColorTertiary"
android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z" /> android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z" />
</vector> </vector>

View File

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2016 The Android Open Source Project
<!-- Copyright (C) 2016 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -16,8 +15,7 @@
<!-- The top and bottom paddings are defined in this container, but since we want <!-- The top and bottom paddings are defined in this container, but since we want
the list view to span the full width (for touch interception purposes), we the list view to span the full width (for touch interception purposes), we
will bake the left/right padding into that view's background itself. --> will bake the left/right padding into that view's background itself. -->
<com.android.launcher3.allapps.LauncherAllAppsContainerView <com.android.launcher3.allapps.LauncherAllAppsContainerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/apps_view" android:id="@+id/apps_view"
android:theme="?attr/allAppsTheme" android:theme="?attr/allAppsTheme"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2015 The Android Open Source Project
<!-- Copyright (C) 2015 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.
@ -13,16 +12,10 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<com.android.launcher3.BubbleTextView <com.android.launcher3.BubbleTextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:launcher="http://schemas.android.com/apk/res-auto" xmlns:launcher="http://schemas.android.com/apk/res-auto"
style="@style/BaseIcon" style="@style/BaseIcon.AllApps"
android:id="@+id/icon" android:id="@+id/icon"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:stateListAnimator="@animator/all_apps_fastscroll_icon_anim"
launcher:iconDisplay="all_apps" launcher:iconDisplay="all_apps"
launcher:centerVertically="true" launcher:centerVertically="true" />
android:paddingLeft="@dimen/dynamic_grid_cell_padding_x"
android:paddingRight="@dimen/dynamic_grid_cell_padding_x" />

View File

@ -26,7 +26,6 @@
android:clipChildren="true" android:clipChildren="true"
android:clipToPadding="false" android:clipToPadding="false"
android:descendantFocusability="afterDescendants" android:descendantFocusability="afterDescendants"
android:paddingTop="@dimen/all_apps_header_top_padding"
launcher:pageIndicator="@+id/tabs" > launcher:pageIndicator="@+id/tabs" >
<include layout="@layout/all_apps_rv_layout" /> <include layout="@layout/all_apps_rv_layout" />

View File

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2008 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<com.android.launcher3.views.SearchResultIcon xmlns:launcher="http://schemas.android.com/apk/res-auto"
style="@style/BaseIcon.AllApps"
launcher:iconDisplay="all_apps"
launcher:centerVertically="true" />

View File

@ -1,4 +1,4 @@
<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2020 The Android Open Source Project <?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2020 The Android Open Source Projectza
Licensed under the Apache License, Version 2.0 (the "License"); Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License. you may not use this file except in compliance with the License.

View File

@ -67,7 +67,6 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="start|center_vertical" android:layout_gravity="start|center_vertical"
android:background="?android:attr/selectableItemBackground"
android:text="@string/search_action_try_now"> android:text="@string/search_action_try_now">
</Button> </Button>

View File

@ -12,27 +12,24 @@
See the License for the specific language governing permissions and See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
--> -->
<com.android.launcher3.views.SearchResultSuggestRow xmlns:android="http://schemas.android.com/apk/res/android" <com.android.launcher3.views.SearchResultIconRow xmlns:android="http://schemas.android.com/apk/res/android"
style="@style/TextHeadline" xmlns:launcher="http://schemas.android.com/apk/res-auto"
android:id="@+id/section_title" style="@style/BaseIcon"
android:background="?android:attr/selectableItemBackground"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:orientation="vertical" android:orientation="horizontal"
android:gravity="center_vertical" android:gravity="start|center_vertical"
android:padding="4dp" android:textAlignment="viewStart"
android:minHeight="48dp"
android:textColor="?android:attr/textColorPrimary" android:textColor="?android:attr/textColorPrimary"
android:textSize="14sp"> android:textSize="16sp"
android:padding="@dimen/dynamic_grid_edge_margin"
launcher:iconDisplay="hero_app"
android:drawableTint="?android:attr/textColorPrimary"
launcher:customIcon="@drawable/ic_allapps_search"
launcher:iconSizeOverride="24dp"
launcher:matchTextInsetWithQuery="true"
launcher:layoutHorizontal="true"
android:drawablePadding="@dimen/dynamic_grid_icon_drawable_padding"
>
<TextView </com.android.launcher3.views.SearchResultIconRow>
android:id="@+id/title"
style="@style/TextTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="8dp"
android:layout_marginBottom="4dp"
android:textColor="?android:attr/textColorPrimary"
android:textSize="14sp" />
</com.android.launcher3.views.SearchResultSuggestRow>

View File

@ -17,7 +17,9 @@
android:id="@+id/section_title" android:id="@+id/section_title"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:fontFamily="@style/TextHeadline" style="@style/TextHeadline"
android:padding="4dp" android:paddingStart="4dp"
android:paddingBottom="2dp"
android:paddingTop="12dp"
android:textColor="?android:attr/textColorPrimary" android:textColor="?android:attr/textColorPrimary"
android:textSize="14sp" /> android:textSize="18sp" />

View File

@ -26,7 +26,7 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:src="@drawable/ic_setting" android:src="@drawable/ic_setting"
android:tint="@android:color/black" android:forceDarkAllowed="true"
android:padding="12dp" android:padding="12dp"
android:background="?android:attr/selectableItemBackgroundBorderless" /> android:background="?android:attr/selectableItemBackgroundBorderless" />

View File

@ -69,6 +69,13 @@
<attr name="folderDotColor" /> <attr name="folderDotColor" />
</declare-styleable> </declare-styleable>
<declare-styleable name="SearchResultIconRow">
<attr name="customIcon" format="reference" />
<attr name="matchTextInsetWithQuery" format="boolean" />
</declare-styleable>
<declare-styleable name="ShadowInfo"> <declare-styleable name="ShadowInfo">
<attr name="ambientShadowColor" format="color" /> <attr name="ambientShadowColor" format="color" />
<attr name="ambientShadowBlur" format="dimension" /> <attr name="ambientShadowBlur" format="dimension" />

View File

@ -59,7 +59,7 @@
<!-- Search bar text in the apps view. [CHAR_LIMIT=50] --> <!-- Search bar text in the apps view. [CHAR_LIMIT=50] -->
<string name="all_apps_search_bar_hint">Search apps</string> <string name="all_apps_search_bar_hint">Search apps</string>
<!-- Search bar text in the apps view. [CHAR_LIMIT=50] --> <!-- Search bar text in the apps view. [CHAR_LIMIT=50] -->
<string name="all_apps_on_device_search_bar_hint">Search this phone and more...</string> <string name="all_apps_on_device_search_bar_hint">Search this phone and more</string>
<!-- Loading apps text. [CHAR_LIMIT=50] --> <!-- Loading apps text. [CHAR_LIMIT=50] -->
<string name="all_apps_loading_message">Loading apps&#8230;</string> <string name="all_apps_loading_message">Loading apps&#8230;</string>
<!-- No-search-results text. [CHAR_LIMIT=50] --> <!-- No-search-results text. [CHAR_LIMIT=50] -->

View File

@ -223,6 +223,16 @@
<item name="android:lines">1</item> <item name="android:lines">1</item>
</style> </style>
<!-- Base theme for AllApps BubbleTextViews -->
<style name="BaseIcon.AllApps" parent="BaseIcon">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:stateListAnimator">@animator/all_apps_fastscroll_icon_anim</item>
<item name="android:paddingLeft">@dimen/dynamic_grid_cell_padding_x</item>
<item name="android:paddingRight">@dimen/dynamic_grid_cell_padding_x</item>
</style>
<!-- Icon displayed on the workspace --> <!-- Icon displayed on the workspace -->
<style name="BaseIcon.Workspace" > <style name="BaseIcon.Workspace" >
<item name="android:shadowRadius">2.0</item> <item name="android:shadowRadius">2.0</item>

View File

@ -17,6 +17,7 @@
package com.android.launcher3; package com.android.launcher3;
import static com.android.launcher3.FastBitmapDrawable.newIcon; import static com.android.launcher3.FastBitmapDrawable.newIcon;
import static com.android.launcher3.graphics.IconShape.getShape;
import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon; import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon;
import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound; import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
@ -27,15 +28,18 @@ import android.animation.ValueAnimator;
import android.content.Context; import android.content.Context;
import android.content.res.ColorStateList; import android.content.res.ColorStateList;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.graphics.BlurMaskFilter;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.Paint; import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF; import android.graphics.PointF;
import android.graphics.PorterDuff.Mode; import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffColorFilter; import android.graphics.PorterDuffColorFilter;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Process;
import android.text.TextUtils.TruncateAt; import android.text.TextUtils.TruncateAt;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Property; import android.util.Property;
@ -50,6 +54,8 @@ import androidx.core.graphics.ColorUtils;
import com.android.launcher3.Launcher.OnResumeCallback; import com.android.launcher3.Launcher.OnResumeCallback;
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate; import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
import com.android.launcher3.allapps.AllAppsSectionDecorator;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.dot.DotInfo; import com.android.launcher3.dot.DotInfo;
import com.android.launcher3.dragndrop.DraggableView; import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.folder.FolderIcon; import com.android.launcher3.folder.FolderIcon;
@ -60,6 +66,7 @@ import com.android.launcher3.graphics.PreloadIconDrawable;
import com.android.launcher3.icons.DotRenderer; import com.android.launcher3.icons.DotRenderer;
import com.android.launcher3.icons.IconCache.IconLoadRequest; import com.android.launcher3.icons.IconCache.IconLoadRequest;
import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver; import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon; import com.android.launcher3.model.data.ItemInfoWithIcon;
@ -79,7 +86,7 @@ import java.text.NumberFormat;
* too aggressive. * too aggressive.
*/ */
public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, OnResumeCallback, public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, OnResumeCallback,
IconLabelDotView, DraggableView, Reorderable { IconLabelDotView, DraggableView, Reorderable, AllAppsSectionDecorator.SelfDecoratingView {
private static final int DISPLAY_WORKSPACE = 0; private static final int DISPLAY_WORKSPACE = 0;
private static final int DISPLAY_ALL_APPS = 1; private static final int DISPLAY_ALL_APPS = 1;
@ -87,6 +94,8 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
private static final int DISPLAY_HERO_APP = 5; private static final int DISPLAY_HERO_APP = 5;
private static final int[] STATE_PRESSED = new int[]{android.R.attr.state_pressed}; private static final int[] STATE_PRESSED = new int[]{android.R.attr.state_pressed};
private static final float HIGHLIGHT_SCALE = 1.16f;
private final PointF mTranslationForReorderBounce = new PointF(0, 0); private final PointF mTranslationForReorderBounce = new PointF(0, 0);
private final PointF mTranslationForReorderPreview = new PointF(0, 0); private final PointF mTranslationForReorderPreview = new PointF(0, 0);
@ -95,6 +104,11 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
private float mScaleForReorderBounce = 1f; private float mScaleForReorderBounce = 1f;
protected final Paint mHighlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
private final Path mHighlightPath = new Path();
protected int mHighlightColor = Color.TRANSPARENT;
private final BlurMaskFilter mHighlightShadowFilter;
private static final Property<BubbleTextView, Float> DOT_SCALE_PROPERTY private static final Property<BubbleTextView, Float> DOT_SCALE_PROPERTY
= new Property<BubbleTextView, Float>(Float.TYPE, "dotScale") { = new Property<BubbleTextView, Float>(Float.TYPE, "dotScale") {
@Override @Override
@ -208,6 +222,11 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
setEllipsize(TruncateAt.END); setEllipsize(TruncateAt.END);
setAccessibilityDelegate(mActivity.getAccessibilityDelegate()); setAccessibilityDelegate(mActivity.getAccessibilityDelegate());
setTextAlpha(1f); setTextAlpha(1f);
int shadowSize = context.getResources().getDimensionPixelSize(
R.dimen.blur_size_click_shadow);
mHighlightShadowFilter = new BlurMaskFilter(shadowSize, BlurMaskFilter.Blur.INNER);
} }
@Override @Override
@ -421,8 +440,38 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
@Override @Override
public void onDraw(Canvas canvas) { public void onDraw(Canvas canvas) {
if (FeatureFlags.ENABLE_DEVICE_SEARCH.get() && mHighlightColor != Color.TRANSPARENT) {
int count = canvas.save();
drawFocusHighlight(canvas);
canvas.restoreToCount(count);
}
super.onDraw(canvas); super.onDraw(canvas);
drawDotIfNecessary(canvas); }
protected void drawFocusHighlight(Canvas canvas) {
boolean isBadged = getTag() instanceof ItemInfo && !Process.myUserHandle().equals(
((ItemInfo) getTag()).user);
float insetScale = (HIGHLIGHT_SCALE - 1) / 2;
canvas.translate(-getIconSize() * insetScale, -insetScale * getIconSize());
float outlineSize = getIconSize() * HIGHLIGHT_SCALE;
mHighlightPath.reset();
mHighlightPaint.reset();
getIconBounds(mDotParams.iconBounds);
getShape().addToPath(mHighlightPath, mDotParams.iconBounds.left, mDotParams.iconBounds.top,
outlineSize / 2);
if (isBadged) {
float borderSize = outlineSize - getIconSize();
float badgeSize = LauncherIcons.getBadgeSizeForIconSize(getIconSize()) + borderSize;
float badgeInset = outlineSize - badgeSize;
getShape().addToPath(mHighlightPath, mDotParams.iconBounds.left + badgeInset,
mDotParams.iconBounds.top + badgeInset, badgeSize / 2);
}
mHighlightPaint.setMaskFilter(mHighlightShadowFilter);
mHighlightPaint.setColor(mDotParams.color);
canvas.drawPath(mHighlightPath, mHighlightPaint);
mHighlightPaint.setMaskFilter(null);
mHighlightPaint.setColor(mHighlightColor);
canvas.drawPath(mHighlightPath, mHighlightPaint);
} }
/** /**
@ -627,7 +676,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
/** /**
* Sets the icon for this view based on the layout direction. * Sets the icon for this view based on the layout direction.
*/ */
private void setIcon(Drawable icon) { protected void setIcon(Drawable icon) {
if (mIsIconVisible) { if (mIsIconVisible) {
applyCompoundDrawables(icon); applyCompoundDrawables(icon);
} }
@ -787,10 +836,11 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
@Override @Override
public SafeCloseable prepareDrawDragView() { public SafeCloseable prepareDrawDragView() {
int highlightColor = mHighlightColor;
mHighlightColor = Color.TRANSPARENT;
resetIconScale(); resetIconScale();
setForceHideDot(true); setForceHideDot(true);
return () -> { return () -> mHighlightColor = highlightColor;
};
} }
private void resetIconScale() { private void resetIconScale() {
@ -827,4 +877,17 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
}); });
iconUpdateAnimation.start(); iconUpdateAnimation.start();
} }
@Override
public void decorate(int color) {
mHighlightColor = color;
invalidate();
}
@Override
public void removeDecoration() {
mHighlightColor = Color.TRANSPARENT;
invalidate();
}
} }

View File

@ -25,6 +25,10 @@ import android.view.ViewDebug;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import androidx.annotation.Nullable;
import java.util.function.Consumer;
/** /**
* View class that represents the bottom row of the home screen. * View class that represents the bottom row of the home screen.
*/ */
@ -34,6 +38,7 @@ public class Hotseat extends CellLayout implements Insettable {
private boolean mHasVerticalHotseat; private boolean mHasVerticalHotseat;
private Workspace mWorkspace; private Workspace mWorkspace;
private boolean mSendTouchToWorkspace; private boolean mSendTouchToWorkspace;
@Nullable private Consumer<Boolean> mOnVisibilityAggregatedCallback;
public Hotseat(Context context) { public Hotseat(Context context) {
this(context, null); this(context, null);
@ -129,4 +134,18 @@ public class Hotseat extends CellLayout implements Insettable {
} }
return event.getY() > getCellHeight(); return event.getY() > getCellHeight();
} }
@Override
public void onVisibilityAggregated(boolean isVisible) {
super.onVisibilityAggregated(isVisible);
if (mOnVisibilityAggregatedCallback != null) {
mOnVisibilityAggregatedCallback.accept(isVisible);
}
}
/** Sets a callback to be called onVisibilityAggregated */
public void setOnVisibilityAggregatedCallback(@Nullable Consumer<Boolean> callback) {
mOnVisibilityAggregatedCallback = callback;
}
} }

View File

@ -201,7 +201,7 @@ public class InvariantDeviceProfile {
DisplayController.getDefaultDisplay(context).getInfo(), DisplayController.getDefaultDisplay(context).getInfo(),
getPredefinedDeviceProfiles(context, gridName)); getPredefinedDeviceProfiles(context, gridName));
Info myInfo = new Info(context, display); Info myInfo = new Info(display);
DisplayOption myDisplayOption = invDistWeightedInterpolate( DisplayOption myDisplayOption = invDistWeightedInterpolate(
myInfo, getPredefinedDeviceProfiles(context, gridName)); myInfo, getPredefinedDeviceProfiles(context, gridName));

View File

@ -34,6 +34,7 @@ import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo; import android.content.pm.ResolveInfo;
import android.content.pm.ShortcutInfo; import android.content.pm.ShortcutInfo;
import android.content.res.Resources; import android.content.res.Resources;
import android.database.ContentObserver;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.Matrix; import android.graphics.Matrix;
@ -44,6 +45,7 @@ import android.graphics.RectF;
import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.graphics.drawable.InsetDrawable; import android.graphics.drawable.InsetDrawable;
import android.net.Uri;
import android.os.Build; import android.os.Build;
import android.os.DeadObjectException; import android.os.DeadObjectException;
import android.os.Handler; import android.os.Handler;
@ -83,6 +85,7 @@ import java.lang.reflect.Method;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.function.Consumer;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -661,6 +664,18 @@ public final class Utilities {
return slop * slop; return slop * slop;
} }
/**
* Helper method to create a content provider
*/
public static ContentObserver newContentObserver(Handler handler, Consumer<Uri> command) {
return new ContentObserver(handler) {
@Override
public void onChange(boolean selfChange, Uri uri) {
command.accept(uri);
}
};
}
private static class FixedSizeEmptyDrawable extends ColorDrawable { private static class FixedSizeEmptyDrawable extends ColorDrawable {
private final int mSize; private final int mSize;

View File

@ -2093,10 +2093,10 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
mTempFXY[1] = y; mTempFXY[1] = y;
mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempFXY, true); mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempFXY, true);
View hotseat = mLauncher.getHotseat(); View hotseat = mLauncher.getHotseat();
return mTempFXY[0] >= hotseat.getLeft() && return mTempFXY[0] >= hotseat.getLeft()
mTempFXY[0] <= hotseat.getRight() && && mTempFXY[0] <= hotseat.getRight()
mTempFXY[1] >= hotseat.getTop() && && mTempFXY[1] >= hotseat.getTop()
mTempFXY[1] <= hotseat.getBottom(); && mTempFXY[1] <= hotseat.getBottom();
} }
/** /**
@ -2393,9 +2393,9 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
spanY = mDragInfo.spanY; spanY = mDragInfo.spanY;
} }
final int container = mLauncher.isHotseatLayout(cellLayout) ? final int container = mLauncher.isHotseatLayout(cellLayout)
LauncherSettings.Favorites.CONTAINER_HOTSEAT : ? LauncherSettings.Favorites.CONTAINER_HOTSEAT
LauncherSettings.Favorites.CONTAINER_DESKTOP; : LauncherSettings.Favorites.CONTAINER_DESKTOP;
final int screenId = getIdForScreen(cellLayout); final int screenId = getIdForScreen(cellLayout);
if (!mLauncher.isHotseatLayout(cellLayout) if (!mLauncher.isHotseatLayout(cellLayout)
&& screenId != getScreenIdForPageIndex(mCurrentPage) && screenId != getScreenIdForPageIndex(mCurrentPage)

View File

@ -16,7 +16,7 @@
package com.android.launcher3.allapps; package com.android.launcher3.allapps;
import static com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem; import static com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
import static com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload; import static com.android.launcher3.allapps.AllAppsGridAdapter.SearchAdapterItem;
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_HAS_SHORTCUT_PERMISSION; import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_HAS_SHORTCUT_PERMISSION;
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_CHANGE_PERMISSION; import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_CHANGE_PERMISSION;
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED; import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED;
@ -57,6 +57,7 @@ import com.android.launcher3.Insettable;
import com.android.launcher3.InsettableFrameLayout; import com.android.launcher3.InsettableFrameLayout;
import com.android.launcher3.R; import com.android.launcher3.R;
import com.android.launcher3.Utilities; import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.search.SearchEventTracker;
import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.keyboard.FocusedItemDecorator; import com.android.launcher3.keyboard.FocusedItemDecorator;
import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.AppInfo;
@ -67,9 +68,7 @@ import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
import com.android.launcher3.util.Themes; import com.android.launcher3.util.Themes;
import com.android.launcher3.views.RecyclerViewFastScroller; import com.android.launcher3.views.RecyclerViewFastScroller;
import com.android.launcher3.views.SpringRelativeLayout; import com.android.launcher3.views.SpringRelativeLayout;
import com.android.systemui.plugins.shared.SearchTargetEvent; import com.android.systemui.plugins.shared.SearchTarget;
import java.util.function.IntConsumer;
/** /**
* The all apps view container. * The all apps view container.
@ -546,13 +545,9 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
return mLauncher.startActivitySafely(v, headerItem.getIntent(), headerItem); return mLauncher.startActivitySafely(v, headerItem.getIntent(), headerItem);
} }
AdapterItem focusedItem = getActiveRecyclerView().getApps().getFocusedChild(); AdapterItem focusedItem = getActiveRecyclerView().getApps().getFocusedChild();
if (focusedItem instanceof AdapterItemWithPayload) { if (focusedItem instanceof SearchAdapterItem) {
IntConsumer onSelection = SearchTarget searchTarget = ((SearchAdapterItem) focusedItem).getSearchTarget();
((AdapterItemWithPayload) focusedItem).getSelectionHandler(); SearchEventTracker.INSTANCE.get(getContext()).quickSelect(searchTarget);
if (onSelection != null) {
onSelection.accept(SearchTargetEvent.QUICK_SELECT);
return true;
}
} }
if (focusedItem.appInfo != null) { if (focusedItem.appInfo != null) {
ItemInfo itemInfo = focusedItem.appInfo; ItemInfo itemInfo = focusedItem.appInfo;

View File

@ -20,8 +20,6 @@ import static com.android.launcher3.touch.ItemLongClickListener.INSTANCE_ALL_APP
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.res.Resources; import android.content.res.Resources;
import android.net.Uri;
import android.os.Bundle;
import android.view.Gravity; import android.view.Gravity;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
@ -37,29 +35,22 @@ import androidx.annotation.Nullable;
import androidx.core.view.accessibility.AccessibilityEventCompat; import androidx.core.view.accessibility.AccessibilityEventCompat;
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
import androidx.core.view.accessibility.AccessibilityRecordCompat; import androidx.core.view.accessibility.AccessibilityRecordCompat;
import androidx.lifecycle.LiveData;
import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import androidx.slice.Slice;
import androidx.slice.widget.SliceLiveData;
import androidx.slice.widget.SliceView; import androidx.slice.widget.SliceView;
import com.android.launcher3.BaseDraggingActivity; import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.BubbleTextView; import com.android.launcher3.BubbleTextView;
import com.android.launcher3.Launcher;
import com.android.launcher3.R; import com.android.launcher3.R;
import com.android.launcher3.allapps.search.AllAppsSearchBarController.PayloadResultHandler; import com.android.launcher3.allapps.search.AllAppsSearchBarController.SearchTargetHandler;
import com.android.launcher3.allapps.search.SearchSectionInfo; import com.android.launcher3.allapps.search.SearchSectionInfo;
import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.data.AppInfo; import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.views.HeroSearchResultView; import com.android.launcher3.views.SearchSliceWrapper;
import com.android.systemui.plugins.AllAppsSearchPlugin;
import com.android.systemui.plugins.shared.SearchTarget; import com.android.systemui.plugins.shared.SearchTarget;
import com.android.systemui.plugins.shared.SearchTargetEvent;
import java.util.List; import java.util.List;
import java.util.function.IntConsumer;
/** /**
* The grid view adapter of all the apps. * The grid view adapter of all the apps.
@ -100,9 +91,11 @@ public class AllAppsGridAdapter extends
public static final int VIEW_TYPE_SEARCH_SUGGEST = 1 << 13; public static final int VIEW_TYPE_SEARCH_SUGGEST = 1 << 13;
public static final int VIEW_TYPE_SEARCH_ICON = 1 << 14;
// Common view type masks // Common view type masks
public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_ALL_APPS_DIVIDER; public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_ALL_APPS_DIVIDER;
public static final int VIEW_TYPE_MASK_ICON = VIEW_TYPE_ICON; public static final int VIEW_TYPE_MASK_ICON = VIEW_TYPE_ICON | VIEW_TYPE_SEARCH_ICON;
/** /**
* ViewHolder for each icon. * ViewHolder for each icon.
@ -192,56 +185,25 @@ public class AllAppsGridAdapter extends
|| viewType == VIEW_TYPE_SEARCH_PEOPLE || viewType == VIEW_TYPE_SEARCH_PEOPLE
|| viewType == VIEW_TYPE_SEARCH_THUMBNAIL || viewType == VIEW_TYPE_SEARCH_THUMBNAIL
|| viewType == VIEW_TYPE_SEARCH_ICON_ROW || viewType == VIEW_TYPE_SEARCH_ICON_ROW
|| viewType == VIEW_TYPE_SEARCH_ICON
|| viewType == VIEW_TYPE_SEARCH_SUGGEST; || viewType == VIEW_TYPE_SEARCH_SUGGEST;
} }
} }
/** /**
* Extension of AdapterItem that contains an extra payload specific to item * Extension of AdapterItem that contains an extra payload specific to item
*
* @param <T> Play load Type
*/ */
public static class AdapterItemWithPayload<T> extends AdapterItem { public static class SearchAdapterItem extends AdapterItem {
private T mPayload; private SearchTarget mSearchTarget;
private String mSearchSessionId;
private AllAppsSearchPlugin mPlugin;
private IntConsumer mSelectionHandler;
public AllAppsSearchPlugin getPlugin() { public SearchAdapterItem(SearchTarget searchTarget, int type) {
return mPlugin; mSearchTarget = searchTarget;
}
public void setPlugin(AllAppsSearchPlugin plugin) {
mPlugin = plugin;
}
public AdapterItemWithPayload(T payload, int type, AllAppsSearchPlugin plugin) {
mPayload = payload;
viewType = type; viewType = type;
mPlugin = plugin;
} }
public void setSelectionHandler(IntConsumer runnable) { public SearchTarget getSearchTarget() {
mSelectionHandler = runnable; return mSearchTarget;
} }
public void setSearchSessionId(String searchSessionId) {
mSearchSessionId = searchSessionId;
}
public String getSearchSessionId() {
return mSearchSessionId;
}
public IntConsumer getSelectionHandler() {
return mSelectionHandler;
}
public T getPayload() {
return mPayload;
}
} }
/** /**
@ -426,11 +388,8 @@ public class AllAppsGridAdapter extends
R.layout.all_apps_icon, parent, false); R.layout.all_apps_icon, parent, false);
icon.setLongPressTimeoutFactor(1f); icon.setLongPressTimeoutFactor(1f);
icon.setOnFocusChangeListener(mIconFocusListener); icon.setOnFocusChangeListener(mIconFocusListener);
if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
icon.setOnClickListener(mOnIconClickListener); icon.setOnClickListener(mOnIconClickListener);
icon.setOnLongClickListener(mOnIconLongClickListener); icon.setOnLongClickListener(mOnIconLongClickListener);
}
// Ensure the all apps icon height matches the workspace icons in portrait mode. // Ensure the all apps icon height matches the workspace icons in portrait mode.
icon.getLayoutParams().height = mLauncher.getDeviceProfile().allAppsCellHeightPx; icon.getLayoutParams().height = mLauncher.getDeviceProfile().allAppsCellHeightPx;
return new ViewHolder(icon); return new ViewHolder(icon);
@ -446,6 +405,9 @@ public class AllAppsGridAdapter extends
case VIEW_TYPE_ALL_APPS_DIVIDER: case VIEW_TYPE_ALL_APPS_DIVIDER:
return new ViewHolder(mLayoutInflater.inflate( return new ViewHolder(mLayoutInflater.inflate(
R.layout.all_apps_divider, parent, false)); R.layout.all_apps_divider, parent, false));
case VIEW_TYPE_SEARCH_ICON:
return new ViewHolder(mLayoutInflater.inflate(
R.layout.search_result_icon, parent, false));
case VIEW_TYPE_SEARCH_CORPUS_TITLE: case VIEW_TYPE_SEARCH_CORPUS_TITLE:
return new ViewHolder( return new ViewHolder(
mLayoutInflater.inflate(R.layout.search_section_title, parent, false)); mLayoutInflater.inflate(R.layout.search_section_title, parent, false));
@ -480,6 +442,10 @@ public class AllAppsGridAdapter extends
@Override @Override
public void onBindViewHolder(ViewHolder holder, int position) { public void onBindViewHolder(ViewHolder holder, int position) {
if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()
&& holder.itemView instanceof AllAppsSectionDecorator.SelfDecoratingView) {
((AllAppsSectionDecorator.SelfDecoratingView) holder.itemView).removeDecoration();
}
switch (holder.getItemViewType()) { switch (holder.getItemViewType()) {
case VIEW_TYPE_ICON: case VIEW_TYPE_ICON:
AdapterItem adapterItem = mApps.getAdapterItems().get(position); AdapterItem adapterItem = mApps.getAdapterItems().get(position);
@ -487,34 +453,6 @@ public class AllAppsGridAdapter extends
BubbleTextView icon = (BubbleTextView) holder.itemView; BubbleTextView icon = (BubbleTextView) holder.itemView;
icon.reset(); icon.reset();
icon.applyFromApplicationInfo(info); icon.applyFromApplicationInfo(info);
if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
break;
}
//TODO: replace with custom TopHitBubbleTextView with support for both shortcut
// and apps
if (adapterItem instanceof AdapterItemWithPayload) {
AdapterItemWithPayload item = (AdapterItemWithPayload) adapterItem;
item.setSelectionHandler(type -> {
SearchTargetEvent e = new SearchTargetEvent(SearchTarget.ItemType.APP,
type, item.position, item.getSearchSessionId());
e.bundle = HeroSearchResultView.getAppBundle(info);
if (item.getPlugin() != null) {
item.getPlugin().notifySearchTargetEvent(e);
}
});
icon.setOnClickListener(view -> {
item.getSelectionHandler().accept(SearchTargetEvent.SELECT);
mOnIconClickListener.onClick(view);
});
icon.setOnLongClickListener(view -> {
item.getSelectionHandler().accept(SearchTargetEvent.SELECT);
return mOnIconLongClickListener.onLongClick(view);
});
}
else {
icon.setOnClickListener(mOnIconClickListener);
icon.setOnLongClickListener(mOnIconLongClickListener);
}
break; break;
case VIEW_TYPE_EMPTY_SEARCH: case VIEW_TYPE_EMPTY_SEARCH:
TextView emptyViewText = (TextView) holder.itemView; TextView emptyViewText = (TextView) holder.itemView;
@ -532,39 +470,25 @@ public class AllAppsGridAdapter extends
break; break;
case VIEW_TYPE_SEARCH_SLICE: case VIEW_TYPE_SEARCH_SLICE:
SliceView sliceView = (SliceView) holder.itemView; SliceView sliceView = (SliceView) holder.itemView;
AdapterItemWithPayload<Uri> slicePayload = SearchAdapterItem slicePayload = (SearchAdapterItem) mApps.getAdapterItems().get(
(AdapterItemWithPayload<Uri>) mApps.getAdapterItems().get(position); position);
sliceView.setOnSliceActionListener((info1, s) -> { SearchTarget searchTarget = slicePayload.getSearchTarget();
if (slicePayload.getPlugin() != null) { sliceView.setTag(new SearchSliceWrapper(mLauncher, sliceView, searchTarget));
SearchTargetEvent searchTargetEvent = new SearchTargetEvent(
SearchTarget.ItemType.SETTINGS_SLICE,
SearchTargetEvent.CHILD_SELECT, slicePayload.position,
slicePayload.getSearchSessionId());
searchTargetEvent.bundle = new Bundle();
searchTargetEvent.bundle.putParcelable("uri", slicePayload.getPayload());
slicePayload.getPlugin().notifySearchTargetEvent(searchTargetEvent);
}
});
try {
LiveData<Slice> liveData = SliceLiveData.fromUri(mLauncher,
slicePayload.getPayload());
liveData.observe((Launcher) mLauncher, sliceView);
sliceView.setTag(liveData);
} catch (Exception ignored) {
}
break; break;
case VIEW_TYPE_SEARCH_CORPUS_TITLE: case VIEW_TYPE_SEARCH_CORPUS_TITLE:
case VIEW_TYPE_SEARCH_ROW_WITH_BUTTON: case VIEW_TYPE_SEARCH_ROW_WITH_BUTTON:
case VIEW_TYPE_SEARCH_HERO_APP: case VIEW_TYPE_SEARCH_HERO_APP:
case VIEW_TYPE_SEARCH_ROW: case VIEW_TYPE_SEARCH_ROW:
case VIEW_TYPE_SEARCH_ICON:
case VIEW_TYPE_SEARCH_ICON_ROW: case VIEW_TYPE_SEARCH_ICON_ROW:
case VIEW_TYPE_SEARCH_PEOPLE: case VIEW_TYPE_SEARCH_PEOPLE:
case VIEW_TYPE_SEARCH_THUMBNAIL: case VIEW_TYPE_SEARCH_THUMBNAIL:
case VIEW_TYPE_SEARCH_SUGGEST: case VIEW_TYPE_SEARCH_SUGGEST:
AdapterItemWithPayload item = SearchAdapterItem item =
(AdapterItemWithPayload) mApps.getAdapterItems().get(position); (SearchAdapterItem) mApps.getAdapterItems().get(position);
PayloadResultHandler payloadResultView = (PayloadResultHandler) holder.itemView; SearchTargetHandler payloadResultView = (SearchTargetHandler) holder.itemView;
payloadResultView.setup(item); payloadResultView.applySearchTarget(item.getSearchTarget());
break; break;
case VIEW_TYPE_ALL_APPS_DIVIDER: case VIEW_TYPE_ALL_APPS_DIVIDER:
// nothing to do // nothing to do
@ -576,17 +500,15 @@ public class AllAppsGridAdapter extends
public void onViewRecycled(@NonNull ViewHolder holder) { public void onViewRecycled(@NonNull ViewHolder holder) {
super.onViewRecycled(holder); super.onViewRecycled(holder);
if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()) return; if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()) return;
if (holder.itemView instanceof BubbleTextView) { if (holder.itemView instanceof AllAppsSectionDecorator.SelfDecoratingView) {
BubbleTextView icon = (BubbleTextView) holder.itemView; ((AllAppsSectionDecorator.SelfDecoratingView) holder.itemView).removeDecoration();
icon.setOnClickListener(null);
icon.setOnLongClickListener(null);
} else if (holder.itemView instanceof SliceView) {
SliceView sliceView = (SliceView) holder.itemView;
sliceView.setOnSliceActionListener(null);
if (sliceView.getTag() instanceof LiveData) {
LiveData sliceLiveData = (LiveData) sliceView.getTag();
sliceLiveData.removeObservers((Launcher) mLauncher);
} }
if (holder.itemView instanceof SliceView) {
SliceView sliceView = (SliceView) holder.itemView;
if (sliceView.getTag() instanceof SearchSliceWrapper) {
((SearchSliceWrapper) sliceView.getTag()).destroy();
}
sliceView.setTag(null);
} }
} }

View File

@ -20,12 +20,14 @@ import android.util.AttributeSet;
import android.view.MotionEvent; import android.view.MotionEvent;
import com.android.launcher3.PagedView; import com.android.launcher3.PagedView;
import com.android.launcher3.R;
import com.android.launcher3.config.FeatureFlags;
public class AllAppsPagedView extends PagedView<PersonalWorkSlidingTabStrip> { public class AllAppsPagedView extends PagedView<PersonalWorkSlidingTabStrip> {
final static float START_DAMPING_TOUCH_SLOP_ANGLE = (float) Math.PI / 6; static final float START_DAMPING_TOUCH_SLOP_ANGLE = (float) Math.PI / 6;
final static float MAX_SWIPE_ANGLE = (float) Math.PI / 3; static final float MAX_SWIPE_ANGLE = (float) Math.PI / 3;
final static float TOUCH_SLOP_DAMPING_FACTOR = 4; static final float TOUCH_SLOP_DAMPING_FACTOR = 4;
public AllAppsPagedView(Context context) { public AllAppsPagedView(Context context) {
this(context, null); this(context, null);
@ -37,6 +39,10 @@ public class AllAppsPagedView extends PagedView<PersonalWorkSlidingTabStrip> {
public AllAppsPagedView(Context context, AttributeSet attrs, int defStyle) { public AllAppsPagedView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle); super(context, attrs, defStyle);
int topPadding = FeatureFlags.ENABLE_DEVICE_SEARCH.get() ? 0
: context.getResources().getDimensionPixelOffset(
R.dimen.all_apps_header_top_padding);
setPadding(0, topPadding, 0, 0);
} }
@Override @Override

View File

@ -24,6 +24,7 @@ import android.content.res.Resources;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Log;
import android.util.SparseIntArray; import android.util.SparseIntArray;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
@ -45,6 +46,8 @@ import java.util.List;
* A RecyclerView with custom fast scroll support for the all apps view. * A RecyclerView with custom fast scroll support for the all apps view.
*/ */
public class AllAppsRecyclerView extends BaseRecyclerView { public class AllAppsRecyclerView extends BaseRecyclerView {
private static final String TAG = "AllAppsContainerView";
private static final boolean DEBUG = true;
private AlphabeticalAppsList mApps; private AlphabeticalAppsList mApps;
private final int mNumAppsPerRow; private final int mNumAppsPerRow;
@ -131,7 +134,9 @@ public class AllAppsRecyclerView extends BaseRecyclerView {
if (mEmptySearchBackground != null && mEmptySearchBackground.getAlpha() > 0) { if (mEmptySearchBackground != null && mEmptySearchBackground.getAlpha() > 0) {
mEmptySearchBackground.draw(c); mEmptySearchBackground.draw(c);
} }
if (DEBUG) {
Log.d(TAG, "onDraw at = " + System.currentTimeMillis());
}
super.onDraw(c); super.onDraw(c);
} }

View File

@ -26,6 +26,7 @@ import androidx.core.graphics.ColorUtils;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.android.launcher3.R; import com.android.launcher3.R;
import com.android.launcher3.allapps.AllAppsGridAdapter.AppsGridLayoutManager;
import com.android.launcher3.allapps.search.SearchSectionInfo; import com.android.launcher3.allapps.search.SearchSectionInfo;
import com.android.launcher3.util.Themes; import com.android.launcher3.util.Themes;
@ -53,6 +54,9 @@ public class AllAppsSectionDecorator extends RecyclerView.ItemDecoration {
int i = 0; int i = 0;
while (i < itemCount) { while (i < itemCount) {
View view = parent.getChildAt(i); View view = parent.getChildAt(i);
if (view instanceof SelfDecoratingView) {
((SelfDecoratingView) view).removeDecoration();
}
int position = parent.getChildAdapterPosition(view); int position = parent.getChildAdapterPosition(view);
AllAppsGridAdapter.AdapterItem adapterItem = adapterItems.get(position); AllAppsGridAdapter.AdapterItem adapterItem = adapterItems.get(position);
if (adapterItem.searchSectionInfo != null) { if (adapterItem.searchSectionInfo != null) {
@ -90,7 +94,10 @@ public class AllAppsSectionDecorator extends RecyclerView.ItemDecoration {
if (mAppsView.getFloatingHeaderView().getFocusedChild() == null if (mAppsView.getFloatingHeaderView().getFocusedChild() == null
&& mAppsView.getApps().getFocusedChild() != null) { && mAppsView.getApps().getFocusedChild() != null) {
int index = mAppsView.getApps().getFocusedChildIndex(); int index = mAppsView.getApps().getFocusedChildIndex();
if (index >= 0 && index < parent.getChildCount()) { AppsGridLayoutManager layoutManager = (AppsGridLayoutManager)
mAppsView.getActiveRecyclerView().getLayoutManager();
if (layoutManager.findFirstVisibleItemPosition() <= index
&& index < parent.getChildCount()) {
decorationHandler.onFocusDraw(c, parent.getChildAt(index)); decorationHandler.onFocusDraw(c, parent.getChildAt(index));
} }
} }
@ -101,8 +108,8 @@ public class AllAppsSectionDecorator extends RecyclerView.ItemDecoration {
* Handles grouping and drawing of items in the same all apps sections. * Handles grouping and drawing of items in the same all apps sections.
*/ */
public static class SectionDecorationHandler { public static class SectionDecorationHandler {
private static final int FILL_ALPHA = (int) (.3f * 255); private static final int FILL_ALPHA = 0;
private static final int FOCUS_ALPHA = (int) (.8f * 255); private static final int FOCUS_ALPHA = (int) (.9f * 255);
protected RectF mBounds = new RectF(); protected RectF mBounds = new RectF();
private final boolean mIsFullWidth; private final boolean mIsFullWidth;
@ -152,6 +159,10 @@ public class AllAppsSectionDecorator extends RecyclerView.ItemDecoration {
if (view == null) { if (view == null) {
return; return;
} }
if (view instanceof SelfDecoratingView) {
((SelfDecoratingView) view).decorate(mFocusColor);
return;
}
mPaint.setColor(mFocusColor); mPaint.setColor(mFocusColor);
canvas.drawRoundRect(view.getLeft(), view.getTop(), canvas.drawRoundRect(view.getLeft(), view.getTop(),
view.getRight(), view.getBottom(), mRadius, mRadius, mPaint); view.getRight(), view.getBottom(), mRadius, mRadius, mPaint);
@ -165,4 +176,18 @@ public class AllAppsSectionDecorator extends RecyclerView.ItemDecoration {
} }
} }
/**
* An interface for a view to draw highlight indicator
*/
public interface SelfDecoratingView {
/**
* Removes decorations drawing if focus is acquired by another view
*/
void removeDecoration();
/**
* Draws highlight indicator on view.
*/
void decorate(int focusColor);
}
} }

View File

@ -133,7 +133,6 @@ public class AllAppsTransitionController implements StateHandler<LauncherState>,
* in xml-based animations which also handle updating the appropriate UI. * in xml-based animations which also handle updating the appropriate UI.
* *
* @param progress value between 0 and 1, 0 shows all apps and 1 shows workspace * @param progress value between 0 and 1, 0 shows all apps and 1 shows workspace
*
* @see #setState(LauncherState) * @see #setState(LauncherState)
* @see #setStateWithAnimation(LauncherState, StateAnimationConfig, PendingAnimation) * @see #setStateWithAnimation(LauncherState, StateAnimationConfig, PendingAnimation)
*/ */

View File

@ -178,16 +178,46 @@ public class AlphabeticalAppsList implements AllAppsStore.OnUpdateListener {
/** /**
* Sets results list for search * Sets results list for search
*/ */
public boolean setSearchResults(ArrayList<AdapterItem> f) { public boolean setSearchResults(ArrayList<AdapterItem> results) {
if (f == null || mSearchResults != f) { if (results == null || mSearchResults != results) {
boolean same = mSearchResults != null && mSearchResults.equals(f); boolean same = mSearchResults != null && mSearchResults.equals(results);
mSearchResults = f; mSearchResults = results;
onAppsUpdated(); onAppsUpdated();
return !same; return !same;
} }
return false; return false;
} }
public boolean appendSearchResults(ArrayList<AdapterItem> results) {
if (mSearchResults != null && results != null && results.size() > 0) {
updateSearchAdapterItems(results, mSearchResults.size());
refreshRecyclerView();
return true;
}
return false;
}
void updateSearchAdapterItems(ArrayList<AdapterItem> list, int offset) {
SearchSectionInfo lastSection = null;
for (int i = 0; i < list.size(); i++) {
AdapterItem adapterItem = list.get(i);
adapterItem.position = offset + i;
mAdapterItems.add(adapterItem);
if (adapterItem.searchSectionInfo != lastSection) {
if (adapterItem.searchSectionInfo != null) {
adapterItem.searchSectionInfo.setPosStart(adapterItem.position);
}
if (lastSection != null) {
lastSection.setPosEnd(adapterItem.position - 1);
}
lastSection = adapterItem.searchSectionInfo;
}
if (adapterItem.isCountedForAccessibility()) {
mAccessibilityResultsCount++;
}
}
}
/** /**
* Updates internals when the set of apps are updated. * Updates internals when the set of apps are updated.
*/ */
@ -294,28 +324,7 @@ public class AlphabeticalAppsList implements AllAppsStore.OnUpdateListener {
} }
appSection.setPosEnd(mApps.isEmpty() ? appSection.getPosStart() : position - 1); appSection.setPosEnd(mApps.isEmpty() ? appSection.getPosStart() : position - 1);
} else { } else {
List<AppInfo> appInfos = new ArrayList<>(); updateSearchAdapterItems(mSearchResults, 0);
SearchSectionInfo lastSection = null;
for (int i = 0; i < mSearchResults.size(); i++) {
AdapterItem adapterItem = mSearchResults.get(i);
adapterItem.position = i;
mAdapterItems.add(adapterItem);
if (adapterItem.searchSectionInfo != lastSection) {
if (adapterItem.searchSectionInfo != null) {
adapterItem.searchSectionInfo.setPosStart(i);
}
if (lastSection != null) {
lastSection.setPosEnd(i - 1);
}
lastSection = adapterItem.searchSectionInfo;
}
if (AllAppsGridAdapter.isIconViewType(adapterItem.viewType)) {
appInfos.add(adapterItem.appInfo);
}
if (adapterItem.isCountedForAccessibility()) {
mAccessibilityResultsCount++;
}
}
if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()) { if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
// Append the search market item // Append the search market item
if (hasNoFilteredResults()) { if (hasNoFilteredResults()) {

View File

@ -111,8 +111,8 @@ public class FloatingHeaderView extends LinearLayout implements
public FloatingHeaderView(@NonNull Context context, @Nullable AttributeSet attrs) { public FloatingHeaderView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs); super(context, attrs);
mHeaderTopPadding = context.getResources() mHeaderTopPadding = FeatureFlags.ENABLE_DEVICE_SEARCH.get() ? 0 :
.getDimensionPixelSize(R.dimen.all_apps_header_top_padding); context.getResources().getDimensionPixelSize(R.dimen.all_apps_header_top_padding);
} }
@Override @Override
@ -130,6 +130,7 @@ public class FloatingHeaderView extends LinearLayout implements
} }
} }
mFixedRows = rows.toArray(new FloatingHeaderRow[rows.size()]); mFixedRows = rows.toArray(new FloatingHeaderRow[rows.size()]);
setPadding(0, mHeaderTopPadding, 0, 0);
mAllRows = mFixedRows; mAllRows = mFixedRows;
} }
@ -247,7 +248,9 @@ public class FloatingHeaderView extends LinearLayout implements
public int getMaxTranslation() { public int getMaxTranslation() {
if (mMaxTranslation == 0 && mTabsHidden) { if (mMaxTranslation == 0 && mTabsHidden) {
return getResources().getDimensionPixelSize(R.dimen.all_apps_search_bar_bottom_padding); int paddingOffset = getResources().getDimensionPixelSize(
R.dimen.all_apps_search_bar_bottom_padding);
return FeatureFlags.ENABLE_DEVICE_SEARCH.get() ? 0 : paddingOffset;
} else if (mMaxTranslation > 0 && mTabsHidden) { } else if (mMaxTranslation > 0 && mTabsHidden) {
return mMaxTranslation + getPaddingTop(); return mMaxTranslation + getPaddingTop();
} else { } else {

View File

@ -30,13 +30,11 @@ import com.android.launcher3.BaseDraggingActivity;
import com.android.launcher3.ExtendedEditText; import com.android.launcher3.ExtendedEditText;
import com.android.launcher3.Launcher; import com.android.launcher3.Launcher;
import com.android.launcher3.Utilities; import com.android.launcher3.Utilities;
import com.android.launcher3.allapps.AllAppsGridAdapter; import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload;
import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.util.PackageManagerHelper; import com.android.launcher3.util.PackageManagerHelper;
import com.android.systemui.plugins.AllAppsSearchPlugin; import com.android.systemui.plugins.AllAppsSearchPlugin;
import com.android.systemui.plugins.shared.SearchTarget; import com.android.systemui.plugins.shared.SearchTarget;
import com.android.systemui.plugins.shared.SearchTargetEvent;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -114,7 +112,7 @@ public class AllAppsSearchBarController
@Override @Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) { if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
if (actionId == EditorInfo.IME_ACTION_SEARCH) { if (actionId == EditorInfo.IME_ACTION_SEARCH || actionId == EditorInfo.IME_ACTION_GO) {
// selectFocusedView should return SearchTargetEvent that is passed onto onClick // selectFocusedView should return SearchTargetEvent that is passed onto onClick
if (Launcher.getLauncher(mLauncher).getAppsView().selectFocusedView(v)) { if (Launcher.getLauncher(mLauncher).getAppsView().selectFocusedView(v)) {
return true; return true;
@ -196,9 +194,16 @@ public class AllAppsSearchBarController
/** /**
* Called when the search from primary source is complete. * Called when the search from primary source is complete.
* *
* @param items sorted list of search result adapter items. * @param items sorted list of search result adapter items
*/ */
void onSearchResult(String query, ArrayList<AllAppsGridAdapter.AdapterItem> items); void onSearchResult(String query, ArrayList<AdapterItem> items);
/**
* Called when the search from secondary source is complete.
*
* @param items sorted list of search result adapter items
*/
void onAppendSearchResult(String query, ArrayList<AdapterItem> items);
/** /**
* Called when the search results should be cleared. * Called when the search results should be cleared.
@ -208,50 +213,20 @@ public class AllAppsSearchBarController
/** /**
* An interface for supporting dynamic search results * An interface for supporting dynamic search results
*
* @param <T> Type of payload
*/ */
public interface PayloadResultHandler<T> { public interface SearchTargetHandler {
/**
* Updates View using Adapter's payload
*/
default void setup(AdapterItemWithPayload<T> adapterItemWithPayload) {
Object[] targetInfo = getTargetInfo();
if (targetInfo != null) {
targetInfo[0] = adapterItemWithPayload.getSearchSessionId();
targetInfo[1] = adapterItemWithPayload.position;
}
applyAdapterInfo(adapterItemWithPayload);
}
void applyAdapterInfo(AdapterItemWithPayload<T> adapterItemWithPayload);
/** /**
* Gets object created by {@link PayloadResultHandler#createTargetInfo()} * Update view using values from {@link SearchTarget}
*/ */
Object[] getTargetInfo(); void applySearchTarget(SearchTarget searchTarget);
/** /**
* Creates a wrapper object to hold searchSessionId and item position * Handles selection of SearchTarget
*/ */
default Object[] createTargetInfo() { default void handleSelection(int eventType) {
return new Object[2];
} }
/**
* Generates a SearchTargetEvent object for a PayloadHandlerView
*/
default SearchTargetEvent getSearchTargetEvent(SearchTarget.ItemType itemType,
int eventType) {
Object[] targetInfo = getTargetInfo();
if (targetInfo == null) return null;
String searchSessionId = (String) targetInfo[0];
int position = (int) targetInfo[1];
return new SearchTargetEvent(itemType, eventType,
position, searchSessionId);
}
} }

View File

@ -42,7 +42,7 @@ import com.android.launcher3.Insettable;
import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R; import com.android.launcher3.R;
import com.android.launcher3.allapps.AllAppsContainerView; import com.android.launcher3.allapps.AllAppsContainerView;
import com.android.launcher3.allapps.AllAppsGridAdapter; import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
import com.android.launcher3.allapps.AllAppsStore; import com.android.launcher3.allapps.AllAppsStore;
import com.android.launcher3.allapps.AlphabeticalAppsList; import com.android.launcher3.allapps.AlphabeticalAppsList;
import com.android.launcher3.allapps.SearchUiManager; import com.android.launcher3.allapps.SearchUiManager;
@ -173,7 +173,7 @@ public class AppsSearchContainerLayout extends ExtendedEditText
} }
@Override @Override
public void onSearchResult(String query, ArrayList<AllAppsGridAdapter.AdapterItem> items) { public void onSearchResult(String query, ArrayList<AdapterItem> items) {
if (items != null) { if (items != null) {
mApps.setSearchResults(items); mApps.setSearchResults(items);
notifyResultChanged(); notifyResultChanged();
@ -181,6 +181,14 @@ public class AppsSearchContainerLayout extends ExtendedEditText
} }
} }
@Override
public void onAppendSearchResult(String query, ArrayList<AdapterItem> items) {
if (items != null) {
mApps.appendSearchResults(items);
notifyResultChanged();
}
}
@Override @Override
public void clearSearchResult() { public void clearSearchResult() {
if (mApps.setSearchResults(null)) { if (mApps.setSearchResults(null)) {

View File

@ -16,6 +16,7 @@
package com.android.launcher3.allapps.search; package com.android.launcher3.allapps.search;
import android.content.Context; import android.content.Context;
import android.os.CancellationSignal;
import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppState;
import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem; import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
@ -47,11 +48,12 @@ public class AppsSearchPipeline implements SearchPipeline {
} }
@Override @Override
public void performSearch(String query, Consumer<ArrayList<AdapterItem>> callback) { public void query(String input, Consumer<ArrayList<AdapterItem>> callback,
CancellationSignal cancellationSignal) {
mLauncherAppState.getModel().enqueueModelUpdateTask(new BaseModelUpdateTask() { mLauncherAppState.getModel().enqueueModelUpdateTask(new BaseModelUpdateTask() {
@Override @Override
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) { public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
List<AppInfo> matchingResults = getTitleMatchResult(apps.data, query); List<AppInfo> matchingResults = getTitleMatchResult(apps.data, input);
callback.accept(getAdapterItems(matchingResults)); callback.accept(getAdapterItems(matchingResults));
} }
}); });

View File

@ -46,8 +46,10 @@ public class DefaultAppSearchAlgorithm implements SearchAlgorithm {
@Override @Override
public void doSearch(final String query, public void doSearch(final String query,
final AllAppsSearchBarController.Callbacks callback) { final AllAppsSearchBarController.Callbacks callback) {
mAppsSearchPipeline.performSearch(query, mAppsSearchPipeline.query(query,
results -> mResultHandler.post(() -> callback.onSearchResult(query, results))); results -> mResultHandler.post(
() -> callback.onSearchResult(query, results)),
null);
} }
public static boolean matches(AppInfo info, String query, StringMatcher matcher) { public static boolean matches(AppInfo info, String query, StringMatcher matcher) {

View File

@ -0,0 +1,93 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3.allapps.search;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import android.content.Context;
import androidx.annotation.Nullable;
import com.android.launcher3.allapps.search.AllAppsSearchBarController.SearchTargetHandler;
import com.android.launcher3.util.MainThreadInitializedObject;
import com.android.systemui.plugins.AllAppsSearchPlugin;
import com.android.systemui.plugins.shared.SearchTarget;
import com.android.systemui.plugins.shared.SearchTargetEvent;
import java.util.WeakHashMap;
/**
* A singleton class to track and report search events to search provider
*/
public class SearchEventTracker {
@Nullable
private AllAppsSearchPlugin mPlugin;
private final WeakHashMap<SearchTarget, SearchTargetHandler>
mCallbacks = new WeakHashMap<>();
public static final MainThreadInitializedObject<SearchEventTracker> INSTANCE =
new MainThreadInitializedObject<>(SearchEventTracker::new);
private SearchEventTracker(Context context) {
}
/**
* Returns instance of SearchEventTracker
*/
public static SearchEventTracker getInstance(Context context) {
return SearchEventTracker.INSTANCE.get(context);
}
/**
* Sets current connected plugin for event reporting
*/
public void setPlugin(@Nullable AllAppsSearchPlugin plugin) {
mPlugin = plugin;
}
/**
* Sends SearchTargetEvent to search provider
*/
public void notifySearchTargetEvent(SearchTargetEvent searchTargetEvent) {
if (mPlugin != null) {
UI_HELPER_EXECUTOR.post(() -> mPlugin.notifySearchTargetEvent(searchTargetEvent));
}
}
/**
* Registers a {@link SearchTargetHandler} to handle quick launch for specified SearchTarget.
*/
public void registerWeakHandler(SearchTarget searchTarget, SearchTargetHandler targetHandler) {
mCallbacks.put(searchTarget, targetHandler);
}
/**
* Handles quick select for SearchTarget
*/
public void quickSelect(SearchTarget searchTarget) {
SearchTargetHandler searchTargetHandler = mCallbacks.get(searchTarget);
if (searchTargetHandler != null) {
searchTargetHandler.handleSelection(SearchTargetEvent.QUICK_SELECT);
}
}
/**
* flushes all registered quick select handlers
*/
public void clearHandlers() {
mCallbacks.clear();
}
}

View File

@ -15,6 +15,8 @@
*/ */
package com.android.launcher3.allapps.search; package com.android.launcher3.allapps.search;
import android.os.CancellationSignal;
import com.android.launcher3.allapps.AllAppsGridAdapter; import com.android.launcher3.allapps.AllAppsGridAdapter;
import java.util.ArrayList; import java.util.ArrayList;
@ -23,10 +25,13 @@ import java.util.function.Consumer;
/** /**
* An interface for handling search within pipeline * An interface for handling search within pipeline
*/ */
// Remove when System Service API is added.
public interface SearchPipeline { public interface SearchPipeline {
/** /**
* Perform query * Perform query
*/ */
void performSearch(String query, Consumer<ArrayList<AllAppsGridAdapter.AdapterItem>> cb); void query(String input,
Consumer<ArrayList<AllAppsGridAdapter.AdapterItem>> callback,
CancellationSignal cancellationSignal);
} }

View File

@ -22,7 +22,7 @@ import com.android.launcher3.allapps.AllAppsSectionDecorator.SectionDecorationHa
*/ */
public class SearchSectionInfo { public class SearchSectionInfo {
private String mTitle; private String mSectionId;
private SectionDecorationHandler mDecorationHandler; private SectionDecorationHandler mDecorationHandler;
public int getPosStart() { public int getPosStart() {
@ -48,8 +48,8 @@ public class SearchSectionInfo {
this(null); this(null);
} }
public SearchSectionInfo(String title) { public SearchSectionInfo(String sectionId) {
mTitle = title; mSectionId = sectionId;
} }
public void setDecorationHandler(SectionDecorationHandler sectionDecorationHandler) { public void setDecorationHandler(SectionDecorationHandler sectionDecorationHandler) {
@ -62,9 +62,9 @@ public class SearchSectionInfo {
} }
/** /**
* Returns the section's title * Returns the section's ID
*/ */
public String getTitle() { public String getSectionId() {
return mTitle == null ? "" : mTitle; return mSectionId == null ? "" : mSectionId;
} }
} }

View File

@ -156,10 +156,14 @@ public final class FeatureFlags {
"ENABLE_DATABASE_RESTORE", true, "ENABLE_DATABASE_RESTORE", true,
"Enable database restore when new restore session is created"); "Enable database restore when new restore session is created");
public static final BooleanFlag ENABLE_UNIVERSAL_SMARTSPACE = getDebugFlag( public static final BooleanFlag ENABLE_SMARTSPACE_UNIVERSAL = getDebugFlag(
"ENABLE_UNIVERSAL_SMARTSPACE", false, "ENABLE_SMARTSPACE_UNIVERSAL", false,
"Replace Smartspace with a version rendered by System UI."); "Replace Smartspace with a version rendered by System UI.");
public static final BooleanFlag ENABLE_SMARTSPACE_BLUECHIP = getDebugFlag(
"ENABLE_SMARTSPACE_BLUECHIP", false,
"Replace Smartspace with the Bluechip version. Ignored if ENABLE_SMARTSPACE_UNIVERSAL is enabled.");
public static final BooleanFlag ENABLE_SYSTEM_VELOCITY_PROVIDER = getDebugFlag( public static final BooleanFlag ENABLE_SYSTEM_VELOCITY_PROVIDER = getDebugFlag(
"ENABLE_SYSTEM_VELOCITY_PROVIDER", true, "ENABLE_SYSTEM_VELOCITY_PROVIDER", true,
"Use system VelocityTracker's algorithm for motion pause detection."); "Use system VelocityTracker's algorithm for motion pause detection.");
@ -178,7 +182,7 @@ public final class FeatureFlags {
"Uses a separate recents activity instead of using the integrated recents+Launcher UI"); "Uses a separate recents activity instead of using the integrated recents+Launcher UI");
public static final BooleanFlag ENABLE_MINIMAL_DEVICE = getDebugFlag( public static final BooleanFlag ENABLE_MINIMAL_DEVICE = getDebugFlag(
"ENABLE_MINIMAL_DEVICE", true, "ENABLE_MINIMAL_DEVICE", false,
"Allow user to toggle minimal device mode in launcher."); "Allow user to toggle minimal device mode in launcher.");
public static void initialize(Context context) { public static void initialize(Context context) {

View File

@ -53,8 +53,8 @@ import com.android.launcher3.R;
import com.android.launcher3.logging.StatsLogManager; import com.android.launcher3.logging.StatsLogManager;
import com.android.launcher3.model.ItemInstallQueue; import com.android.launcher3.model.ItemInstallQueue;
import com.android.launcher3.model.WidgetItem; import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.pm.PinRequestHelper; import com.android.launcher3.pm.PinRequestHelper;
import com.android.launcher3.util.InstantAppResolver;
import com.android.launcher3.views.BaseDragLayer; import com.android.launcher3.views.BaseDragLayer;
import com.android.launcher3.widget.PendingAddShortcutInfo; import com.android.launcher3.widget.PendingAddShortcutInfo;
import com.android.launcher3.widget.PendingAddWidgetInfo; import com.android.launcher3.widget.PendingAddWidgetInfo;
@ -87,7 +87,6 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener
private Bundle mWidgetOptions; private Bundle mWidgetOptions;
private boolean mFinishOnPause = false; private boolean mFinishOnPause = false;
private InstantAppResolver mInstantAppResolver;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@ -101,7 +100,6 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener
mApp = LauncherAppState.getInstance(this); mApp = LauncherAppState.getInstance(this);
mIdp = mApp.getInvariantDeviceProfile(); mIdp = mApp.getInvariantDeviceProfile();
mInstantAppResolver = InstantAppResolver.newInstance(this);
// Use the application context to get the device profile, as in multiwindow-mode, the // Use the application context to get the device profile, as in multiwindow-mode, the
// confirmation activity might be rotated. // confirmation activity might be rotated.
@ -322,6 +320,8 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener
} }
private void logCommand(StatsLogManager.EventEnum command) { private void logCommand(StatsLogManager.EventEnum command) {
getStatsLogManager().logger().log(command); getStatsLogManager().logger()
.withItemInfo((ItemInfo) mWidgetCell.getWidgetView().getTag())
.log(command);
} }
} }

View File

@ -50,6 +50,7 @@ import android.util.LongSparseArray;
import android.util.TimingLogger; import android.util.TimingLogger;
import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherAppWidgetProviderInfo;
import com.android.launcher3.LauncherModel; import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherSettings; import com.android.launcher3.LauncherSettings;
import com.android.launcher3.Utilities; import com.android.launcher3.Utilities;
@ -315,6 +316,7 @@ public class LoaderTask implements Runnable {
final PackageManagerHelper pmHelper = new PackageManagerHelper(context); final PackageManagerHelper pmHelper = new PackageManagerHelper(context);
final boolean isSafeMode = pmHelper.isSafeMode(); final boolean isSafeMode = pmHelper.isSafeMode();
final boolean isSdCardReady = Utilities.isBootCompleted(); final boolean isSdCardReady = Utilities.isBootCompleted();
final WidgetManagerHelper widgetHelper = new WidgetManagerHelper(context);
boolean clearDb = false; boolean clearDb = false;
try { try {
@ -402,6 +404,7 @@ public class LoaderTask implements Runnable {
WorkspaceItemInfo info; WorkspaceItemInfo info;
LauncherAppWidgetInfo appWidgetInfo; LauncherAppWidgetInfo appWidgetInfo;
LauncherAppWidgetProviderInfo widgetProviderInfo;
Intent intent; Intent intent;
String targetPkg; String targetPkg;
@ -731,6 +734,19 @@ public class LoaderTask implements Runnable {
+ appWidgetInfo.spanX + "x" + appWidgetInfo.spanY); + appWidgetInfo.spanX + "x" + appWidgetInfo.spanY);
continue; continue;
} }
widgetProviderInfo =
widgetHelper.getLauncherAppWidgetInfo(appWidgetId);
if (widgetProviderInfo != null
&& (appWidgetInfo.spanX < widgetProviderInfo.minSpanX
|| appWidgetInfo.spanY < widgetProviderInfo.minSpanY)) {
// This can happen when display size changes.
c.markDeleted("Widget removed, min sizes not met: "
+ "span=" + appWidgetInfo.spanX + "x"
+ appWidgetInfo.spanY + " minSpan="
+ widgetProviderInfo.minSpanX + "x"
+ widgetProviderInfo.minSpanY);
continue;
}
if (!c.isOnWorkspaceOrHotseat()) { if (!c.isOnWorkspaceOrHotseat()) {
c.markDeleted("Widget found where container != " + c.markDeleted("Widget found where container != " +
"CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!"); "CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!");

View File

@ -32,6 +32,7 @@ import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SH
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT; import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_TASK; import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_TASK;
import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.CONTAINER_NOT_SET; import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.CONTAINER_NOT_SET;
import static com.android.launcher3.shortcuts.ShortcutKey.EXTRA_SHORTCUT_ID;
import android.content.ComponentName; import android.content.ComponentName;
import android.content.ContentValues; import android.content.ContentValues;
@ -50,10 +51,10 @@ import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
import com.android.launcher3.logger.LauncherAtom.PredictionContainer; import com.android.launcher3.logger.LauncherAtom.PredictionContainer;
import com.android.launcher3.logger.LauncherAtom.SearchResultContainer; import com.android.launcher3.logger.LauncherAtom.SearchResultContainer;
import com.android.launcher3.logger.LauncherAtom.SettingsContainer; import com.android.launcher3.logger.LauncherAtom.SettingsContainer;
import com.android.launcher3.logger.LauncherAtom.Shortcut;
import com.android.launcher3.logger.LauncherAtom.ShortcutsContainer; import com.android.launcher3.logger.LauncherAtom.ShortcutsContainer;
import com.android.launcher3.logger.LauncherAtom.TaskSwitcherContainer; import com.android.launcher3.logger.LauncherAtom.TaskSwitcherContainer;
import com.android.launcher3.model.ModelWriter; import com.android.launcher3.model.ModelWriter;
import com.android.launcher3.shortcuts.ShortcutKey;
import com.android.launcher3.util.ContentWriter; import com.android.launcher3.util.ContentWriter;
import java.util.Optional; import java.util.Optional;
@ -282,9 +283,14 @@ public class ItemInfo {
case ITEM_TYPE_DEEP_SHORTCUT: case ITEM_TYPE_DEEP_SHORTCUT:
itemBuilder itemBuilder
.setShortcut(nullableComponent .setShortcut(nullableComponent
.map(component -> LauncherAtom.Shortcut.newBuilder() .map(component -> {
.setShortcutName(component.flattenToShortString()) Shortcut.Builder lsb = Shortcut.newBuilder()
.setShortcutId(ShortcutKey.fromItemInfo(this).getId())) .setShortcutName(component.flattenToShortString());
Optional.ofNullable(getIntent())
.map(i -> i.getStringExtra(EXTRA_SHORTCUT_ID))
.ifPresent(lsb::setShortcutId);
return lsb;
})
.orElse(LauncherAtom.Shortcut.newBuilder())); .orElse(LauncherAtom.Shortcut.newBuilder()));
break; break;
case ITEM_TYPE_SHORTCUT: case ITEM_TYPE_SHORTCUT:

View File

@ -61,4 +61,8 @@ public class RemoteActionItemInfo extends ItemInfoWithIcon {
public boolean shouldStartInLauncher() { public boolean shouldStartInLauncher() {
return mShouldStart; return mShouldStart;
} }
public boolean isEscapeHatch() {
return mToken.contains("item_type:[ESCAPE_HATCH]");
}
} }

View File

@ -275,7 +275,8 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat {
launchBackTutorialPreference.setSummary("Learn how to use the Back gesture"); launchBackTutorialPreference.setSummary("Learn how to use the Back gesture");
launchBackTutorialPreference.setOnPreferenceClickListener(preference -> { launchBackTutorialPreference.setOnPreferenceClickListener(preference -> {
startActivity(launchSandboxIntent.putExtra( startActivity(launchSandboxIntent.putExtra(
"tutorial_type", "RIGHT_EDGE_BACK_NAVIGATION")); "tutorial_steps",
new String[] {"RIGHT_EDGE_BACK_NAVIGATION"}));
return true; return true;
}); });
sandboxCategory.addPreference(launchBackTutorialPreference); sandboxCategory.addPreference(launchBackTutorialPreference);
@ -284,7 +285,9 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat {
launchHomeTutorialPreference.setTitle("Launch Home Tutorial"); launchHomeTutorialPreference.setTitle("Launch Home Tutorial");
launchHomeTutorialPreference.setSummary("Learn how to use the Home gesture"); launchHomeTutorialPreference.setSummary("Learn how to use the Home gesture");
launchHomeTutorialPreference.setOnPreferenceClickListener(preference -> { launchHomeTutorialPreference.setOnPreferenceClickListener(preference -> {
startActivity(launchSandboxIntent.putExtra("tutorial_type", "HOME_NAVIGATION")); startActivity(launchSandboxIntent.putExtra(
"tutorial_steps",
new String[] {"HOME_NAVIGATION"}));
return true; return true;
}); });
sandboxCategory.addPreference(launchHomeTutorialPreference); sandboxCategory.addPreference(launchHomeTutorialPreference);
@ -293,7 +296,9 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat {
launchOverviewTutorialPreference.setTitle("Launch Overview Tutorial"); launchOverviewTutorialPreference.setTitle("Launch Overview Tutorial");
launchOverviewTutorialPreference.setSummary("Learn how to use the Overview gesture"); launchOverviewTutorialPreference.setSummary("Learn how to use the Overview gesture");
launchOverviewTutorialPreference.setOnPreferenceClickListener(preference -> { launchOverviewTutorialPreference.setOnPreferenceClickListener(preference -> {
startActivity(launchSandboxIntent.putExtra("tutorial_type", "OVERVIEW_NAVIGATION")); startActivity(launchSandboxIntent.putExtra(
"tutorial_steps",
new String[] {"OVERVIEW_NAVIGATION"}));
return true; return true;
}); });
sandboxCategory.addPreference(launchOverviewTutorialPreference); sandboxCategory.addPreference(launchOverviewTutorialPreference);
@ -302,7 +307,9 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat {
launchAssistantTutorialPreference.setTitle("Launch Assistant Tutorial"); launchAssistantTutorialPreference.setTitle("Launch Assistant Tutorial");
launchAssistantTutorialPreference.setSummary("Learn how to use the Assistant gesture"); launchAssistantTutorialPreference.setSummary("Learn how to use the Assistant gesture");
launchAssistantTutorialPreference.setOnPreferenceClickListener(preference -> { launchAssistantTutorialPreference.setOnPreferenceClickListener(preference -> {
startActivity(launchSandboxIntent.putExtra("tutorial_type", "ASSISTANT")); startActivity(launchSandboxIntent.putExtra(
"tutorial_steps",
new String[] {"ASSISTANT"}));
return true; return true;
}); });
sandboxCategory.addPreference(launchAssistantTutorialPreference); sandboxCategory.addPreference(launchAssistantTutorialPreference);
@ -311,7 +318,9 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat {
launchSandboxModeTutorialPreference.setTitle("Launch Sandbox Mode"); launchSandboxModeTutorialPreference.setTitle("Launch Sandbox Mode");
launchSandboxModeTutorialPreference.setSummary("Practice navigation gestures"); launchSandboxModeTutorialPreference.setSummary("Practice navigation gestures");
launchSandboxModeTutorialPreference.setOnPreferenceClickListener(preference -> { launchSandboxModeTutorialPreference.setOnPreferenceClickListener(preference -> {
startActivity(launchSandboxIntent.putExtra("tutorial_type", "SANDBOX_MODE")); startActivity(launchSandboxIntent.putExtra(
"tutorial_steps",
new String[] {"SANDBOX_MODE"}));
return true; return true;
}); });
sandboxCategory.addPreference(launchSandboxModeTutorialPreference); sandboxCategory.addPreference(launchSandboxModeTutorialPreference);

View File

@ -21,14 +21,9 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE; import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE;
import android.app.Activity; import android.app.Activity;
import android.content.ContentResolver;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.res.Resources; import android.content.res.Resources;
import android.database.ContentObserver;
import android.os.Handler;
import android.provider.Settings;
import android.util.Log;
import com.android.launcher3.R; import com.android.launcher3.R;
import com.android.launcher3.Utilities; import com.android.launcher3.Utilities;
@ -43,16 +38,6 @@ public class RotationHelper implements OnSharedPreferenceChangeListener {
public static final String ALLOW_ROTATION_PREFERENCE_KEY = "pref_allowRotation"; public static final String ALLOW_ROTATION_PREFERENCE_KEY = "pref_allowRotation";
private final ContentResolver mContentResolver;
private boolean mSystemAutoRotateEnabled;
private ContentObserver mSystemAutoRotateObserver = new ContentObserver(new Handler()) {
@Override
public void onChange(boolean selfChange) {
updateAutoRotateSetting();
}
};
public static boolean getAllowRotationDefaultValue() { public static boolean getAllowRotationDefaultValue() {
// If the device's pixel density was scaled (usually via settings for A11y), use the // If the device's pixel density was scaled (usually via settings for A11y), use the
// original dimensions to determine if rotation is allowed of not. // original dimensions to determine if rotation is allowed of not.
@ -106,20 +91,6 @@ public class RotationHelper implements OnSharedPreferenceChangeListener {
} else { } else {
mSharedPrefs = null; mSharedPrefs = null;
} }
mContentResolver = activity.getContentResolver();
}
private void updateAutoRotateSetting() {
int autoRotateEnabled = 0;
try {
autoRotateEnabled = Settings.System.getInt(mContentResolver,
Settings.System.ACCELEROMETER_ROTATION);
} catch (Settings.SettingNotFoundException e) {
Log.e(TAG, "autorotate setting not found", e);
}
mSystemAutoRotateEnabled = autoRotateEnabled == 1;
} }
@Override @Override
@ -129,7 +100,6 @@ public class RotationHelper implements OnSharedPreferenceChangeListener {
getAllowRotationDefaultValue()); getAllowRotationDefaultValue());
if (mHomeRotationEnabled != wasRotationEnabled) { if (mHomeRotationEnabled != wasRotationEnabled) {
notifyChange(); notifyChange();
updateAutoRotateSetting();
} }
} }
@ -165,11 +135,6 @@ public class RotationHelper implements OnSharedPreferenceChangeListener {
if (!mInitialized) { if (!mInitialized) {
mInitialized = true; mInitialized = true;
notifyChange(); notifyChange();
mContentResolver.registerContentObserver(
Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION),
false, mSystemAutoRotateObserver);
updateAutoRotateSetting();
} }
} }
@ -179,7 +144,6 @@ public class RotationHelper implements OnSharedPreferenceChangeListener {
if (mSharedPrefs != null) { if (mSharedPrefs != null) {
mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this); mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this);
} }
mContentResolver.unregisterContentObserver(mSystemAutoRotateObserver);
} }
} }
@ -225,9 +189,8 @@ public class RotationHelper implements OnSharedPreferenceChangeListener {
@Override @Override
public String toString() { public String toString() {
return String.format("[mStateHandlerRequest=%d, mCurrentStateRequest=%d," return String.format("[mStateHandlerRequest=%d, mCurrentStateRequest=%d,"
+ " mLastActivityFlags=%d, mIgnoreAutoRotateSettings=%b, mHomeRotationEnabled=%b," + " mLastActivityFlags=%d, mIgnoreAutoRotateSettings=%b, mHomeRotationEnabled=%b]",
+ " mSystemAutoRotateEnabled=%b]",
mStateHandlerRequest, mCurrentStateRequest, mLastActivityFlags, mStateHandlerRequest, mCurrentStateRequest, mLastActivityFlags,
mIgnoreAutoRotateSettings, mHomeRotationEnabled, mSystemAutoRotateEnabled); mIgnoreAutoRotateSettings, mHomeRotationEnabled);
} }
} }

View File

@ -0,0 +1,46 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3.util;
import android.os.Looper;
import androidx.annotation.WorkerThread;
/**
* Utility class to define an object which does most of it's processing on a
* dedicated background thread.
*/
public abstract class BgObjectWithLooper {
/**
* Start initialization of the object
*/
public final void initializeInBackground(String threadName) {
new Thread(this::runOnThread, threadName).start();
}
private void runOnThread() {
Looper.prepare();
onInitialized(Looper.myLooper());
Looper.loop();
}
/**
* Called on the background thread to handle initialization
*/
@WorkerThread
protected abstract void onInitialized(Looper looper);
}

View File

@ -31,6 +31,8 @@ import android.view.Display;
import androidx.annotation.VisibleForTesting; import androidx.annotation.VisibleForTesting;
import com.android.launcher3.Utilities;
import java.util.ArrayList; import java.util.ArrayList;
/** /**
@ -157,13 +159,13 @@ public class DisplayController implements DisplayListener {
private final ArrayList<DisplayInfoChangeListener> mListeners = new ArrayList<>(); private final ArrayList<DisplayInfoChangeListener> mListeners = new ArrayList<>();
private DisplayController.Info mInfo; private DisplayController.Info mInfo;
private DisplayHolder(Context displayContext) { private DisplayHolder(Context displayContext, Display display) {
mDisplayContext = displayContext; mDisplayContext = displayContext;
// Note that the Display object must be obtained from DisplayManager which is // Note that the Display object must be obtained from DisplayManager which is
// associated to the display context, so the Display is isolated from Activity and // associated to the display context, so the Display is isolated from Activity and
// Application to provide the actual state of device that excludes the additional // Application to provide the actual state of device that excludes the additional
// adjustment and override. // adjustment and override.
mInfo = new DisplayController.Info(mDisplayContext); mInfo = new DisplayController.Info(display);
mId = mInfo.id; mId = mInfo.id;
} }
@ -180,22 +182,31 @@ public class DisplayController implements DisplayListener {
} }
protected void handleOnChange() { protected void handleOnChange() {
Display display = Utilities.ATLEAST_R
? mDisplayContext.getDisplay()
: mDisplayContext
.getSystemService(DisplayManager.class)
.getDisplay(mId);
if (display == null) {
return;
}
Info oldInfo = mInfo; Info oldInfo = mInfo;
Info info = new Info(mDisplayContext); Info newInfo = new Info(display);
int change = 0; int change = 0;
if (info.hasDifferentSize(oldInfo)) { if (newInfo.hasDifferentSize(oldInfo)) {
change |= CHANGE_SIZE; change |= CHANGE_SIZE;
} }
if (oldInfo.rotation != info.rotation) { if (newInfo.rotation != oldInfo.rotation) {
change |= CHANGE_ROTATION; change |= CHANGE_ROTATION;
} }
if (info.singleFrameMs != oldInfo.singleFrameMs) { if (newInfo.singleFrameMs != oldInfo.singleFrameMs) {
change |= CHANGE_FRAME_DELAY; change |= CHANGE_FRAME_DELAY;
} }
if (change != 0) { if (change != 0) {
mInfo = info; mInfo = newInfo;
final int flags = change; final int flags = change;
MAIN_EXECUTOR.execute(() -> notifyChange(flags)); MAIN_EXECUTOR.execute(() -> notifyChange(flags));
} }
@ -216,7 +227,7 @@ public class DisplayController implements DisplayListener {
// Use application context to create display context so that it can have its own // Use application context to create display context so that it can have its own
// Resources. // Resources.
Context displayContext = context.getApplicationContext().createDisplayContext(display); Context displayContext = context.getApplicationContext().createDisplayContext(display);
return new DisplayHolder(displayContext); return new DisplayHolder(displayContext, display);
} }
} }
@ -244,12 +255,7 @@ public class DisplayController implements DisplayListener {
this.metrics = metrics; this.metrics = metrics;
} }
private Info(Context context) { public Info(Display display) {
this(context, context.getSystemService(DisplayManager.class)
.getDisplay(DEFAULT_DISPLAY));
}
public Info(Context context, Display display) {
id = display.getDisplayId(); id = display.getDisplayId();
rotation = display.getRotation(); rotation = display.getRotation();
@ -262,7 +268,8 @@ public class DisplayController implements DisplayListener {
display.getRealSize(realSize); display.getRealSize(realSize);
display.getCurrentSizeRange(smallestSize, largestSize); display.getCurrentSizeRange(smallestSize, largestSize);
metrics = context.getResources().getDisplayMetrics(); metrics = new DisplayMetrics();
display.getMetrics(metrics);
} }
private boolean hasDifferentSize(Info info) { private boolean hasDifferentSize(Info info) {

View File

@ -20,10 +20,8 @@ import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR; import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.content.pm.ShortcutInfo; import android.content.pm.ShortcutInfo;
import android.graphics.Point; import android.graphics.Point;
import android.os.Bundle;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.util.Pair; import android.util.Pair;
import android.view.View; import android.view.View;
@ -37,8 +35,9 @@ import com.android.launcher3.DropTarget;
import com.android.launcher3.Launcher; import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R; import com.android.launcher3.R;
import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload; import com.android.launcher3.allapps.AllAppsStore;
import com.android.launcher3.allapps.search.AllAppsSearchBarController.PayloadResultHandler; import com.android.launcher3.allapps.search.AllAppsSearchBarController.SearchTargetHandler;
import com.android.launcher3.allapps.search.SearchEventTracker;
import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.dragndrop.DragOptions;
import com.android.launcher3.dragndrop.DraggableView; import com.android.launcher3.dragndrop.DraggableView;
import com.android.launcher3.graphics.DragPreviewProvider; import com.android.launcher3.graphics.DragPreviewProvider;
@ -48,24 +47,28 @@ import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider; import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
import com.android.launcher3.touch.ItemLongClickListener; import com.android.launcher3.touch.ItemLongClickListener;
import com.android.systemui.plugins.AllAppsSearchPlugin; import com.android.launcher3.util.ComponentKey;
import com.android.systemui.plugins.shared.SearchTarget; import com.android.systemui.plugins.shared.SearchTarget;
import com.android.systemui.plugins.shared.SearchTargetEvent; import com.android.systemui.plugins.shared.SearchTargetEvent;
import java.util.ArrayList;
import java.util.List; import java.util.List;
/** /**
* A view representing a high confidence app search result that includes shortcuts * A view representing a high confidence app search result that includes shortcuts
* TODO (sfufa@) consolidate this with SearchResultIconRow
*/ */
public class HeroSearchResultView extends LinearLayout implements DragSource, public class HeroSearchResultView extends LinearLayout implements DragSource, SearchTargetHandler {
PayloadResultHandler<List<Pair<ShortcutInfo, ItemInfoWithIcon>>> {
public static final String TARGET_TYPE_HERO_APP = "hero_app";
public static final int MAX_SHORTCUTS_COUNT = 2; public static final int MAX_SHORTCUTS_COUNT = 2;
private final Object[] mTargetInfo = createTargetInfo();
BubbleTextView mBubbleTextView; private SearchTarget mSearchTarget;
View mIconView; private BubbleTextView mBubbleTextView;
BubbleTextView[] mDeepShortcutTextViews = new BubbleTextView[2]; private View mIconView;
AllAppsSearchPlugin mPlugin; private BubbleTextView[] mDeepShortcutTextViews = new BubbleTextView[2];
public HeroSearchResultView(Context context) { public HeroSearchResultView(Context context) {
super(context); super(context);
@ -96,8 +99,6 @@ public class HeroSearchResultView extends LinearLayout implements DragSource,
launcher.getItemOnClickListener().onClick(view); launcher.getItemOnClickListener().onClick(view);
}); });
mBubbleTextView.setOnLongClickListener(new HeroItemDragHandler(getContext(), this)); mBubbleTextView.setOnLongClickListener(new HeroItemDragHandler(getContext(), this));
setLayoutParams(
new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, grid.allAppsCellHeightPx));
mDeepShortcutTextViews[0] = findViewById(R.id.shortcut_0); mDeepShortcutTextViews[0] = findViewById(R.id.shortcut_0);
@ -108,35 +109,39 @@ public class HeroSearchResultView extends LinearLayout implements DragSource,
grid.allAppsIconSizePx)); grid.allAppsIconSizePx));
bubbleTextView.setOnClickListener(view -> { bubbleTextView.setOnClickListener(view -> {
WorkspaceItemInfo itemInfo = (WorkspaceItemInfo) bubbleTextView.getTag(); WorkspaceItemInfo itemInfo = (WorkspaceItemInfo) bubbleTextView.getTag();
SearchTargetEvent event = getSearchTargetEvent( SearchTargetEvent event = new SearchTargetEvent.Builder(mSearchTarget,
SearchTarget.ItemType.APP_HERO, SearchTargetEvent.CHILD_SELECT).setShortcutPosition(itemInfo.rank).build();
SearchTargetEvent.CHILD_SELECT); SearchEventTracker.getInstance(getContext()).notifySearchTargetEvent(event);
event.bundle = getAppBundle(itemInfo);
event.bundle.putString("shortcut_id", itemInfo.getDeepShortcutId());
if (mPlugin != null) {
mPlugin.notifySearchTargetEvent(event);
}
launcher.getItemOnClickListener().onClick(view); launcher.getItemOnClickListener().onClick(view);
}); });
} }
} }
/**
* Apply {@link ItemInfo} for appIcon and shortcut Icons
*/
@Override @Override
public void applyAdapterInfo( public void applySearchTarget(SearchTarget searchTarget) {
AdapterItemWithPayload<List<Pair<ShortcutInfo, ItemInfoWithIcon>>> adapterItem) { mSearchTarget = searchTarget;
mBubbleTextView.applyFromApplicationInfo(adapterItem.appInfo); AllAppsStore apps = Launcher.getLauncher(getContext()).getAppsView().getAppsStore();
AppInfo appInfo = apps.getApp(new ComponentKey(searchTarget.getComponentName(),
searchTarget.getUserHandle()));
List<ShortcutInfo> infos = mSearchTarget.getShortcutInfos();
ArrayList<Pair<ShortcutInfo, ItemInfoWithIcon>> shortcuts = new ArrayList<>();
for (int i = 0; infos != null && i < infos.size() && i < MAX_SHORTCUTS_COUNT; i++) {
ShortcutInfo shortcutInfo = infos.get(i);
ItemInfoWithIcon si = new WorkspaceItemInfo(shortcutInfo, getContext());
si.rank = i;
shortcuts.add(new Pair<>(shortcutInfo, si));
}
mBubbleTextView.applyFromApplicationInfo(appInfo);
mIconView.setBackground(mBubbleTextView.getIcon()); mIconView.setBackground(mBubbleTextView.getIcon());
mIconView.setTag(adapterItem.appInfo); mIconView.setTag(appInfo);
List<Pair<ShortcutInfo, ItemInfoWithIcon>> shortcutDetails = adapterItem.getPayload();
LauncherAppState appState = LauncherAppState.getInstance(getContext()); LauncherAppState appState = LauncherAppState.getInstance(getContext());
for (int i = 0; i < mDeepShortcutTextViews.length; i++) { for (int i = 0; i < mDeepShortcutTextViews.length; i++) {
BubbleTextView shortcutView = mDeepShortcutTextViews[i]; BubbleTextView shortcutView = mDeepShortcutTextViews[i];
mDeepShortcutTextViews[i].setVisibility(shortcutDetails.size() > i ? VISIBLE : GONE); mDeepShortcutTextViews[i].setVisibility(shortcuts.size() > i ? VISIBLE : GONE);
if (i < shortcutDetails.size()) { if (i < shortcuts.size()) {
Pair<ShortcutInfo, ItemInfoWithIcon> p = shortcutDetails.get(i); Pair<ShortcutInfo, ItemInfoWithIcon> p = shortcuts.get(i);
//apply ItemInfo and prepare view //apply ItemInfo and prepare view
shortcutView.applyFromWorkspaceItem((WorkspaceItemInfo) p.second); shortcutView.applyFromWorkspaceItem((WorkspaceItemInfo) p.second);
MODEL_EXECUTOR.execute(() -> { MODEL_EXECUTOR.execute(() -> {
@ -146,13 +151,7 @@ public class HeroSearchResultView extends LinearLayout implements DragSource,
}); });
} }
} }
mPlugin = adapterItem.getPlugin(); SearchEventTracker.INSTANCE.get(getContext()).registerWeakHandler(searchTarget, this);
adapterItem.setSelectionHandler(this::handleSelection);
}
@Override
public Object[] getTargetInfo() {
return mTargetInfo;
} }
@Override @Override
@ -190,38 +189,21 @@ public class HeroSearchResultView extends LinearLayout implements DragSource,
mLauncher.getWorkspace().beginDragShared(mContainer.mBubbleTextView, mLauncher.getWorkspace().beginDragShared(mContainer.mBubbleTextView,
draggableView, mContainer, itemInfo, previewProvider, new DragOptions()); draggableView, mContainer, itemInfo, previewProvider, new DragOptions());
SearchTargetEvent event = mContainer.getSearchTargetEvent( SearchTargetEvent event = new SearchTargetEvent.Builder(mContainer.mSearchTarget,
SearchTarget.ItemType.APP_HERO, SearchTargetEvent.LONG_PRESS); SearchTargetEvent.LONG_PRESS).build();
event.bundle = getAppBundle(itemInfo); SearchEventTracker.INSTANCE.get(mLauncher).notifySearchTargetEvent(event);
if (mContainer.mPlugin != null) {
mContainer.mPlugin.notifySearchTargetEvent(event);
}
return false; return false;
} }
} }
private void handleSelection(int eventType) { @Override
public void handleSelection(int eventType) {
ItemInfo itemInfo = (ItemInfo) mBubbleTextView.getTag(); ItemInfo itemInfo = (ItemInfo) mBubbleTextView.getTag();
if (itemInfo == null) return; if (itemInfo == null) return;
Launcher launcher = Launcher.getLauncher(getContext()); Launcher launcher = Launcher.getLauncher(getContext());
launcher.startActivitySafely(this, itemInfo.getIntent(), itemInfo); launcher.startActivitySafely(this, itemInfo.getIntent(), itemInfo);
SearchTargetEvent event = getSearchTargetEvent( SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(
SearchTarget.ItemType.APP_HERO, eventType); new SearchTargetEvent.Builder(mSearchTarget, eventType).build());
event.bundle = getAppBundle(itemInfo);
if (mPlugin != null) {
mPlugin.notifySearchTargetEvent(event);
}
}
/**
* Helper method to generate {@link SearchTargetEvent} bundle from {@link ItemInfo}
*/
public static Bundle getAppBundle(ItemInfo itemInfo) {
Bundle b = new Bundle();
b.putParcelable(Intent.EXTRA_COMPONENT_NAME, itemInfo.getTargetComponent());
b.putParcelable(Intent.EXTRA_USER, itemInfo.user);
return b;
} }
} }

View File

@ -0,0 +1,100 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3.views;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.Launcher;
import com.android.launcher3.allapps.AllAppsStore;
import com.android.launcher3.allapps.search.AllAppsSearchBarController;
import com.android.launcher3.allapps.search.SearchEventTracker;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.touch.ItemLongClickListener;
import com.android.launcher3.util.ComponentKey;
import com.android.systemui.plugins.shared.SearchTarget;
import com.android.systemui.plugins.shared.SearchTargetEvent;
/**
* A {@link BubbleTextView} representing a single cell result in AllApps
*/
public class SearchResultIcon extends BubbleTextView implements
AllAppsSearchBarController.SearchTargetHandler, View.OnClickListener,
View.OnLongClickListener {
public static final String TARGET_TYPE_APP = "app";
private final Launcher mLauncher;
private SearchTarget mSearchTarget;
public SearchResultIcon(Context context) {
this(context, null, 0);
}
public SearchResultIcon(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SearchResultIcon(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mLauncher = Launcher.getLauncher(getContext());
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
setLongPressTimeoutFactor(1f);
setOnFocusChangeListener(mLauncher.getFocusHandler());
setOnClickListener(this);
setOnLongClickListener(this);
getLayoutParams().height = mLauncher.getDeviceProfile().allAppsCellHeightPx;
}
@Override
public void applySearchTarget(SearchTarget searchTarget) {
mSearchTarget = searchTarget;
AllAppsStore appsStore = mLauncher.getAppsView().getAppsStore();
SearchEventTracker.getInstance(getContext()).registerWeakHandler(mSearchTarget, this);
if (searchTarget.getItemType().equals(TARGET_TYPE_APP)) {
AppInfo appInfo = appsStore.getApp(new ComponentKey(searchTarget.getComponentName(),
searchTarget.getUserHandle()));
applyFromApplicationInfo(appInfo);
}
}
@Override
public void handleSelection(int eventType) {
mLauncher.getItemOnClickListener().onClick(this);
SearchEventTracker.INSTANCE.get(mLauncher).notifySearchTargetEvent(
new SearchTargetEvent.Builder(mSearchTarget, eventType).build());
}
@Override
public void onClick(View view) {
handleSelection(SearchTargetEvent.SELECT);
}
@Override
public boolean onLongClick(View view) {
SearchEventTracker.INSTANCE.get(mLauncher).notifySearchTargetEvent(
new SearchTargetEvent.Builder(mSearchTarget, SearchTargetEvent.LONG_PRESS).build());
return ItemLongClickListener.INSTANCE_ALL_APPS.onLongClick(view);
}
}

View File

@ -22,134 +22,175 @@ import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import android.app.RemoteAction; import android.app.RemoteAction;
import android.content.Context; import android.content.Context;
import android.content.pm.ShortcutInfo; import android.content.pm.ShortcutInfo;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Build; import android.os.Build;
import android.os.Bundle;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.widget.EditText;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.android.launcher3.Launcher; import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState; import com.android.launcher3.LauncherAppState;
import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload; import com.android.launcher3.R;
import com.android.launcher3.allapps.search.AllAppsSearchBarController; import com.android.launcher3.allapps.search.AllAppsSearchBarController;
import com.android.launcher3.allapps.search.SearchEventTracker;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.LauncherIcons; import com.android.launcher3.icons.LauncherIcons;
import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.ItemInfoWithIcon; import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.RemoteActionItemInfo; import com.android.launcher3.model.data.RemoteActionItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.touch.ItemClickHandler; import com.android.launcher3.touch.ItemClickHandler;
import com.android.systemui.plugins.AllAppsSearchPlugin; import com.android.launcher3.util.Themes;
import com.android.systemui.plugins.shared.SearchTarget; import com.android.systemui.plugins.shared.SearchTarget;
import com.android.systemui.plugins.shared.SearchTarget.ItemType;
import com.android.systemui.plugins.shared.SearchTargetEvent; import com.android.systemui.plugins.shared.SearchTargetEvent;
/** /**
* A view representing a stand alone shortcut search result * A view representing a stand alone shortcut search result
*/ */
public class SearchResultIconRow extends DoubleShadowBubbleTextView implements public class SearchResultIconRow extends DoubleShadowBubbleTextView implements
AllAppsSearchBarController.PayloadResultHandler<SearchTarget> { AllAppsSearchBarController.SearchTargetHandler {
private final Object[] mTargetInfo = createTargetInfo();
private ShortcutInfo mShortcutInfo; public static final String TARGET_TYPE_REMOTE_ACTION = "remote_action";
private AllAppsSearchPlugin mPlugin; public static final String TARGET_TYPE_SUGGEST = "suggest";
private AdapterItemWithPayload<SearchTarget> mAdapterItem; public static final String TARGET_TYPE_SHORTCUT = "shortcut";
public static final String REMOTE_ACTION_SHOULD_START = "should_start_for_result";
public static final String REMOTE_ACTION_TOKEN = "action_token";
private final int mCustomIconResId;
private final boolean mMatchesInset;
private SearchTarget mSearchTarget;
public SearchResultIconRow(@NonNull Context context) { public SearchResultIconRow(@NonNull Context context) {
super(context); this(context, null, 0);
} }
public SearchResultIconRow(@NonNull Context context, public SearchResultIconRow(@NonNull Context context,
@Nullable AttributeSet attrs) { @Nullable AttributeSet attrs) {
super(context, attrs); this(context, attrs, 0);
} }
public SearchResultIconRow(@NonNull Context context, @Nullable AttributeSet attrs, public SearchResultIconRow(@NonNull Context context, @Nullable AttributeSet attrs,
int defStyleAttr) { int defStyleAttr) {
super(context, attrs, defStyleAttr); super(context, attrs, defStyleAttr);
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.SearchResultIconRow, defStyleAttr, 0);
mCustomIconResId = a.getResourceId(R.styleable.SearchResultIconRow_customIcon, 0);
mMatchesInset = a.getBoolean(R.styleable.SearchResultIconRow_matchTextInsetWithQuery,
false);
a.recycle();
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
Launcher launcher = Launcher.getLauncher(getContext());
if (mMatchesInset && launcher.getAppsView() != null && getParent() != null) {
EditText editText = launcher.getAppsView().getSearchUiManager().getEditText();
if (editText != null) {
int counterOffset = getIconSize() + getCompoundDrawablePadding() / 2;
setPadding(editText.getLeft() - counterOffset, getPaddingTop(),
getPaddingRight(), getPaddingBottom());
}
}
} }
@Override @Override
public void applyAdapterInfo(AdapterItemWithPayload<SearchTarget> adapterItemWithPayload) { protected void drawFocusHighlight(Canvas canvas) {
if (mAdapterItem != null) { mHighlightPaint.setColor(mHighlightColor);
mAdapterItem.setSelectionHandler(null); float r = Themes.getDialogCornerRadius(getContext());
canvas.drawRoundRect(0, 0, getWidth(), getHeight(), r, r, mHighlightPaint);
} }
mAdapterItem = adapterItemWithPayload;
SearchTarget payload = adapterItemWithPayload.getPayload();
mPlugin = adapterItemWithPayload.getPlugin();
if (payload.mRemoteAction != null) {
prepareUsingRemoteAction(payload.mRemoteAction, @Override
payload.bundle.getString(SearchTarget.REMOTE_ACTION_TOKEN), public void applySearchTarget(SearchTarget searchTarget) {
payload.bundle.getBoolean(SearchTarget.REMOTE_ACTION_SHOULD_START)); mSearchTarget = searchTarget;
} else { String type = searchTarget.getItemType();
prepareUsingShortcutInfo(payload.shortcuts.get(0)); if (type.equals(TARGET_TYPE_REMOTE_ACTION) || type.equals(TARGET_TYPE_SUGGEST)) {
prepareUsingRemoteAction(searchTarget.getRemoteAction(),
searchTarget.getExtras().getString(REMOTE_ACTION_TOKEN),
searchTarget.getExtras().getBoolean(REMOTE_ACTION_SHOULD_START),
type.equals(TARGET_TYPE_REMOTE_ACTION));
} else if (type.equals(TARGET_TYPE_SHORTCUT)) {
prepareUsingShortcutInfo(searchTarget.getShortcutInfos().get(0));
} }
setOnClickListener(v -> handleSelection(SearchTargetEvent.SELECT)); setOnClickListener(v -> handleSelection(SearchTargetEvent.SELECT));
adapterItemWithPayload.setSelectionHandler(this::handleSelection); SearchEventTracker.INSTANCE.get(getContext()).registerWeakHandler(searchTarget, this);
} }
private void prepareUsingShortcutInfo(ShortcutInfo shortcutInfo) { private void prepareUsingShortcutInfo(ShortcutInfo shortcutInfo) {
mShortcutInfo = shortcutInfo; WorkspaceItemInfo workspaceItemInfo = new WorkspaceItemInfo(shortcutInfo, getContext());
WorkspaceItemInfo workspaceItemInfo = new WorkspaceItemInfo(mShortcutInfo, getContext());
applyFromWorkspaceItem(workspaceItemInfo); applyFromWorkspaceItem(workspaceItemInfo);
LauncherAppState launcherAppState = LauncherAppState.getInstance(getContext()); LauncherAppState launcherAppState = LauncherAppState.getInstance(getContext());
if (!loadIconFromResource()) {
MODEL_EXECUTOR.execute(() -> { MODEL_EXECUTOR.execute(() -> {
launcherAppState.getIconCache().getShortcutIcon(workspaceItemInfo, mShortcutInfo); launcherAppState.getIconCache().getShortcutIcon(workspaceItemInfo, shortcutInfo);
reapplyItemInfoAsync(workspaceItemInfo); reapplyItemInfoAsync(workspaceItemInfo);
}); });
} }
}
private void prepareUsingRemoteAction(RemoteAction remoteAction, String token, boolean start) { private void prepareUsingRemoteAction(RemoteAction remoteAction, String token, boolean start,
boolean useIconToBadge) {
RemoteActionItemInfo itemInfo = new RemoteActionItemInfo(remoteAction, token, start); RemoteActionItemInfo itemInfo = new RemoteActionItemInfo(remoteAction, token, start);
applyFromRemoteActionInfo(itemInfo); applyFromRemoteActionInfo(itemInfo);
if (itemInfo.isEscapeHatch() || !loadIconFromResource()) {
UI_HELPER_EXECUTOR.post(() -> { UI_HELPER_EXECUTOR.post(() -> {
// If the Drawable from the remote action is not AdaptiveBitmap, styling will not work. // If the Drawable from the remote action is not AdaptiveBitmap, styling will not
// work.
try (LauncherIcons li = LauncherIcons.obtain(getContext())) { try (LauncherIcons li = LauncherIcons.obtain(getContext())) {
Drawable d = itemInfo.getRemoteAction().getIcon().loadDrawable(getContext()); Drawable d = itemInfo.getRemoteAction().getIcon().loadDrawable(getContext());
itemInfo.bitmap = li.createBadgedIconBitmap(d, itemInfo.user, BitmapInfo bitmap = li.createBadgedIconBitmap(d, itemInfo.user,
Build.VERSION.SDK_INT); Build.VERSION.SDK_INT);
if (useIconToBadge) {
BitmapInfo placeholder = li.createIconBitmap(
itemInfo.getRemoteAction().getTitle().toString().substring(0, 1),
bitmap.color);
itemInfo.bitmap = li.badgeBitmap(placeholder.icon, bitmap);
} else {
itemInfo.bitmap = bitmap;
}
reapplyItemInfoAsync(itemInfo); reapplyItemInfoAsync(itemInfo);
} }
}); });
}
} }
private boolean loadIconFromResource() {
if (mCustomIconResId == 0) return false;
setIcon(Launcher.getLauncher(getContext()).getDrawable(mCustomIconResId));
return true;
}
void reapplyItemInfoAsync(ItemInfoWithIcon itemInfoWithIcon) { void reapplyItemInfoAsync(ItemInfoWithIcon itemInfoWithIcon) {
MAIN_EXECUTOR.post(() -> reapplyItemInfo(itemInfoWithIcon)); MAIN_EXECUTOR.post(() -> reapplyItemInfo(itemInfoWithIcon));
} }
@Override @Override
public Object[] getTargetInfo() { public void handleSelection(int eventType) {
return mTargetInfo;
}
private void handleSelection(int eventType) {
ItemInfo itemInfo = (ItemInfo) getTag(); ItemInfo itemInfo = (ItemInfo) getTag();
Launcher launcher = Launcher.getLauncher(getContext()); Launcher launcher = Launcher.getLauncher(getContext());
final SearchTargetEvent searchTargetEvent;
if (itemInfo instanceof WorkspaceItemInfo) { if (itemInfo instanceof WorkspaceItemInfo) {
ItemClickHandler.onClickAppShortcut(this, (WorkspaceItemInfo) itemInfo, launcher); ItemClickHandler.onClickAppShortcut(this, (WorkspaceItemInfo) itemInfo, launcher);
searchTargetEvent = getSearchTargetEvent(SearchTarget.ItemType.SHORTCUT,
eventType);
searchTargetEvent.shortcut = mShortcutInfo;
} else { } else {
RemoteActionItemInfo remoteItemInfo = (RemoteActionItemInfo) itemInfo; ItemClickHandler.onClickRemoteAction(launcher, (RemoteActionItemInfo) itemInfo);
ItemClickHandler.onClickRemoteAction(launcher, remoteItemInfo);
searchTargetEvent = getSearchTargetEvent(ItemType.ACTION,
eventType);
searchTargetEvent.bundle = new Bundle();
searchTargetEvent.remoteAction = remoteItemInfo.getRemoteAction();
searchTargetEvent.bundle.putBoolean(SearchTarget.REMOTE_ACTION_SHOULD_START,
remoteItemInfo.shouldStartInLauncher());
searchTargetEvent.bundle.putString(SearchTarget.REMOTE_ACTION_TOKEN,
remoteItemInfo.getToken());
}
if (mPlugin != null) {
mPlugin.notifySearchTargetEvent(searchTargetEvent);
} }
SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(
new SearchTargetEvent.Builder(mSearchTarget, eventType).build());
} }
} }

View File

@ -23,9 +23,12 @@ import android.content.Intent;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.os.Process;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.View; import android.view.View;
import android.widget.ImageButton; import android.widget.ImageButton;
@ -39,10 +42,10 @@ import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
import com.android.launcher3.DeviceProfile; import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher; import com.android.launcher3.Launcher;
import com.android.launcher3.R; import com.android.launcher3.R;
import com.android.launcher3.allapps.AllAppsGridAdapter;
import com.android.launcher3.allapps.search.AllAppsSearchBarController; import com.android.launcher3.allapps.search.AllAppsSearchBarController;
import com.android.launcher3.util.Themes; import com.android.launcher3.allapps.search.SearchEventTracker;
import com.android.systemui.plugins.AllAppsSearchPlugin; import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.LauncherIcons;
import com.android.systemui.plugins.shared.SearchTarget; import com.android.systemui.plugins.shared.SearchTarget;
import com.android.systemui.plugins.shared.SearchTargetEvent; import com.android.systemui.plugins.shared.SearchTargetEvent;
@ -52,7 +55,9 @@ import java.util.ArrayList;
* A view representing a single people search result in all apps * A view representing a single people search result in all apps
*/ */
public class SearchResultPeopleView extends LinearLayout implements public class SearchResultPeopleView extends LinearLayout implements
AllAppsSearchBarController.PayloadResultHandler<Bundle> { AllAppsSearchBarController.SearchTargetHandler {
public static final String TARGET_TYPE_PEOPLE = "people";
private final int mIconSize; private final int mIconSize;
private final int mButtonSize; private final int mButtonSize;
@ -60,9 +65,10 @@ public class SearchResultPeopleView extends LinearLayout implements
private View mIconView; private View mIconView;
private TextView mTitleView; private TextView mTitleView;
private ImageButton[] mProviderButtons = new ImageButton[3]; private ImageButton[] mProviderButtons = new ImageButton[3];
private AllAppsSearchPlugin mPlugin;
private Intent mIntent; private Intent mIntent;
private final Object[] mTargetInfo = createTargetInfo();
private SearchTarget mSearchTarget;
public SearchResultPeopleView(Context context) { public SearchResultPeopleView(Context context) {
this(context, null, 0); this(context, null, 0);
@ -99,21 +105,19 @@ public class SearchResultPeopleView extends LinearLayout implements
} }
@Override @Override
public void applyAdapterInfo( public void applySearchTarget(SearchTarget searchTarget) {
AllAppsGridAdapter.AdapterItemWithPayload<Bundle> adapterItemWithPayload) { mSearchTarget = searchTarget;
Bundle payload = adapterItemWithPayload.getPayload(); Bundle payload = searchTarget.getExtras();
mPlugin = adapterItemWithPayload.getPlugin();
mTitleView.setText(payload.getString("title")); mTitleView.setText(payload.getString("title"));
mIntent = payload.getParcelable("intent"); mIntent = payload.getParcelable("intent");
Bitmap icon = payload.getParcelable("icon"); Bitmap contactIcon = payload.getParcelable("icon");
if (icon != null) { try (LauncherIcons li = LauncherIcons.obtain(getContext())) {
RoundedBitmapDrawable d = RoundedBitmapDrawableFactory.create(getResources(), icon); BitmapInfo badgeInfo = li.createBadgedIconBitmap(
float radius = Themes.getDialogCornerRadius(getContext()); getAppIcon(mIntent.getPackage()), Process.myUserHandle(),
d.setCornerRadius(radius); Build.VERSION.SDK_INT);
d.setBounds(0, 0, mIconSize, mIconSize); setIcon(li.badgeBitmap(roundBitmap(contactIcon), badgeInfo).icon, false);
BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(), } catch (Exception e) {
Bitmap.createScaledBitmap(icon, mIconSize, mIconSize, false)); setIcon(contactIcon, true);
mIconView.setBackground(d);
} }
ArrayList<Bundle> providers = payload.getParcelableArrayList("providers"); ArrayList<Bundle> providers = payload.getParcelableArrayList("providers");
@ -122,59 +126,80 @@ public class SearchResultPeopleView extends LinearLayout implements
if (providers != null && i < providers.size()) { if (providers != null && i < providers.size()) {
Bundle provider = providers.get(i); Bundle provider = providers.get(i);
Intent intent = provider.getParcelable("intent"); Intent intent = provider.getParcelable("intent");
setupProviderButton(button, provider, intent, adapterItemWithPayload); setupProviderButton(button, provider, intent);
String pkg = provider.getString("package_name");
UI_HELPER_EXECUTOR.post(() -> { UI_HELPER_EXECUTOR.post(() -> {
try { String pkg = provider.getString("package_name");
ApplicationInfo applicationInfo = mPackageManager.getApplicationInfo( Drawable appIcon = getAppIcon(pkg);
pkg, 0); if (appIcon != null) {
Drawable appIcon = applicationInfo.loadIcon(mPackageManager);
MAIN_EXECUTOR.post(() -> button.setImageDrawable(appIcon)); MAIN_EXECUTOR.post(() -> button.setImageDrawable(appIcon));
} catch (PackageManager.NameNotFoundException ignored) {
} }
}); });
button.setVisibility(VISIBLE);
} else { } else {
button.setVisibility(GONE); button.setVisibility(GONE);
} }
} }
adapterItemWithPayload.setSelectionHandler(this::handleSelection); SearchEventTracker.INSTANCE.get(getContext()).registerWeakHandler(searchTarget, this);
} }
@Override /**
public Object[] getTargetInfo() { * Normalizes the bitmap to look like rounded App Icon
return mTargetInfo; * TODO(b/170234747) to support styling, generate adaptive icon drawable and generate
* bitmap from it.
*/
private Bitmap roundBitmap(Bitmap icon) {
final RoundedBitmapDrawable d = RoundedBitmapDrawableFactory.create(getResources(), icon);
d.setCornerRadius(R.attr.folderIconRadius);
d.setBounds(0, 0, mIconSize, mIconSize);
final Bitmap bitmap = Bitmap.createBitmap(d.getBounds().width(), d.getBounds().height(),
Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
d.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
d.draw(canvas);
return bitmap;
} }
private void setupProviderButton(ImageButton button, Bundle provider, Intent intent, private void setIcon(Bitmap icon, Boolean round) {
AllAppsGridAdapter.AdapterItem adapterItem) { if (round) {
RoundedBitmapDrawable d = RoundedBitmapDrawableFactory.create(getResources(), icon);
d.setCornerRadius(R.attr.folderIconRadius);
d.setBounds(0, 0, mIconSize, mIconSize);
mIconView.setBackground(d);
} else {
mIconView.setBackground(new BitmapDrawable(getResources(), icon));
}
}
private Drawable getAppIcon(String pkg) {
try {
ApplicationInfo applicationInfo = mPackageManager.getApplicationInfo(
pkg, 0);
return applicationInfo.loadIcon(mPackageManager);
} catch (PackageManager.NameNotFoundException ignored) {
return null;
}
}
private void setupProviderButton(ImageButton button, Bundle provider, Intent intent) {
Launcher launcher = Launcher.getLauncher(getContext()); Launcher launcher = Launcher.getLauncher(getContext());
button.setOnClickListener(b -> { button.setOnClickListener(b -> {
launcher.startActivitySafely(b, intent, null); launcher.startActivitySafely(b, intent, null);
SearchTargetEvent searchTargetEvent = getSearchTargetEvent( Bundle bundle = new Bundle();
SearchTarget.ItemType.PEOPLE, bundle.putBundle("provider", provider);
SearchTargetEvent.CHILD_SELECT); SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(
searchTargetEvent.bundle = new Bundle(); new SearchTargetEvent.Builder(mSearchTarget,
searchTargetEvent.bundle.putParcelable("intent", mIntent); SearchTargetEvent.CHILD_SELECT).setExtras(bundle).build());
searchTargetEvent.bundle.putBundle("provider", provider);
if (mPlugin != null) {
mPlugin.notifySearchTargetEvent(searchTargetEvent);
}
}); });
} }
@Override
private void handleSelection(int eventType) { public void handleSelection(int eventType) {
if (mIntent != null) { if (mIntent != null) {
Launcher launcher = Launcher.getLauncher(getContext()); Launcher launcher = Launcher.getLauncher(getContext());
launcher.startActivitySafely(this, mIntent, null); launcher.startActivitySafely(this, mIntent, null);
SearchTargetEvent searchTargetEvent = getSearchTargetEvent(SearchTarget.ItemType.PEOPLE, SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(
eventType); new SearchTargetEvent.Builder(mSearchTarget, eventType).build());
searchTargetEvent.bundle = new Bundle();
searchTargetEvent.bundle.putParcelable("intent", mIntent);
if (mPlugin != null) {
mPlugin.notifySearchTargetEvent(searchTargetEvent);
}
} }
} }
} }

View File

@ -21,6 +21,11 @@ import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.BitmapDrawable;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
@ -36,20 +41,28 @@ import androidx.annotation.Nullable;
import com.android.launcher3.DeviceProfile; import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher; import com.android.launcher3.Launcher;
import com.android.launcher3.R; import com.android.launcher3.R;
import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload;
import com.android.launcher3.allapps.search.AllAppsSearchBarController; import com.android.launcher3.allapps.search.AllAppsSearchBarController;
import com.android.systemui.plugins.AllAppsSearchPlugin; import com.android.launcher3.allapps.search.SearchEventTracker;
import com.android.launcher3.icons.BitmapRenderer;
import com.android.launcher3.util.Themes;
import com.android.systemui.plugins.shared.SearchTarget; import com.android.systemui.plugins.shared.SearchTarget;
import com.android.systemui.plugins.shared.SearchTargetEvent; import com.android.systemui.plugins.shared.SearchTargetEvent;
import java.io.IOException; import java.io.IOException;
import java.net.URL; import java.net.URL;
import java.net.URLConnection;
/** /**
* A View representing a PlayStore item. * A View representing a PlayStore item.
*/ */
public class SearchResultPlayItem extends LinearLayout implements public class SearchResultPlayItem extends LinearLayout implements
AllAppsSearchBarController.PayloadResultHandler<Bundle> { AllAppsSearchBarController.SearchTargetHandler {
public static final String TARGET_TYPE_PLAY = "play";
private static final int BITMAP_CROP_MASK_COLOR = 0xff424242;
final Paint mIconPaint = new Paint();
final Rect mTempRect = new Rect();
private final DeviceProfile mDeviceProfile; private final DeviceProfile mDeviceProfile;
private View mIconView; private View mIconView;
private TextView mTitleView; private TextView mTitleView;
@ -57,8 +70,8 @@ public class SearchResultPlayItem extends LinearLayout implements
private Button mPreviewButton; private Button mPreviewButton;
private String mPackageName; private String mPackageName;
private boolean mIsInstantGame; private boolean mIsInstantGame;
private AllAppsSearchPlugin mPlugin;
private final Object[] mTargetInfo = createTargetInfo(); private SearchTarget mSearchTarget;
public SearchResultPlayItem(Context context) { public SearchResultPlayItem(Context context) {
@ -91,14 +104,35 @@ public class SearchResultPlayItem extends LinearLayout implements
iconParams.height = mDeviceProfile.allAppsIconSizePx; iconParams.height = mDeviceProfile.allAppsIconSizePx;
iconParams.width = mDeviceProfile.allAppsIconSizePx; iconParams.width = mDeviceProfile.allAppsIconSizePx;
setOnClickListener(view -> handleSelection(SearchTargetEvent.SELECT)); setOnClickListener(view -> handleSelection(SearchTargetEvent.SELECT));
} }
private Bitmap getRoundedBitmap(Bitmap bitmap) {
final int iconSize = bitmap.getWidth();
final float radius = Themes.getDialogCornerRadius(getContext());
Bitmap output = BitmapRenderer.createHardwareBitmap(iconSize, iconSize, (canvas) -> {
mTempRect.set(0, 0, iconSize, iconSize);
final RectF rectF = new RectF(mTempRect);
mIconPaint.setAntiAlias(true);
mIconPaint.reset();
canvas.drawARGB(0, 0, 0, 0);
mIconPaint.setColor(BITMAP_CROP_MASK_COLOR);
canvas.drawRoundRect(rectF, radius, radius, mIconPaint);
mIconPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(bitmap, mTempRect, mTempRect, mIconPaint);
});
return output;
}
@Override @Override
public void applyAdapterInfo(AdapterItemWithPayload<Bundle> adapterItemWithPayload) { public void applySearchTarget(SearchTarget searchTarget) {
Bundle bundle = adapterItemWithPayload.getPayload(); mSearchTarget = searchTarget;
mPlugin = adapterItemWithPayload.getPlugin(); Bundle bundle = searchTarget.getExtras();
adapterItemWithPayload.setSelectionHandler(this::handleSelection); SearchEventTracker.INSTANCE.get(getContext()).registerWeakHandler(searchTarget, this);
if (bundle.getString("package", "").equals(mPackageName)) { if (bundle.getString("package", "").equals(mPackageName)) {
return; return;
} }
@ -109,17 +143,19 @@ public class SearchResultPlayItem extends LinearLayout implements
// TODO: Should use a generic type to get values b/165320033 // TODO: Should use a generic type to get values b/165320033
showIfNecessary(mDetailViews[0], bundle.getString("price")); showIfNecessary(mDetailViews[0], bundle.getString("price"));
showIfNecessary(mDetailViews[1], bundle.getString("rating")); showIfNecessary(mDetailViews[1], bundle.getString("rating"));
showIfNecessary(mDetailViews[2], bundle.getString("category"));
mIconView.setBackgroundResource(R.drawable.ic_deepshortcut_placeholder); mIconView.setBackgroundResource(R.drawable.ic_deepshortcut_placeholder);
UI_HELPER_EXECUTOR.execute(() -> { UI_HELPER_EXECUTOR.execute(() -> {
try { try {
// TODO: Handle caching
URL url = new URL(bundle.getString("icon_url")); URL url = new URL(bundle.getString("icon_url"));
Bitmap bitmap = BitmapFactory.decodeStream(url.openStream()); URLConnection con = url.openConnection();
BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(), // TODO: monitor memory and investigate if it's better to use glide
con.addRequestProperty("Cache-Control", "max-age: 0");
con.setUseCaches(true);
Bitmap bitmap = BitmapFactory.decodeStream(con.getInputStream());
BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(), getRoundedBitmap(
Bitmap.createScaledBitmap(bitmap, mDeviceProfile.allAppsIconSizePx, Bitmap.createScaledBitmap(bitmap, mDeviceProfile.allAppsIconSizePx,
mDeviceProfile.allAppsIconSizePx, false)); mDeviceProfile.allAppsIconSizePx, false)));
mIconView.post(() -> mIconView.setBackground(bitmapDrawable)); mIconView.post(() -> mIconView.setBackground(bitmapDrawable));
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); e.printStackTrace();
@ -127,11 +163,6 @@ public class SearchResultPlayItem extends LinearLayout implements
}); });
} }
@Override
public Object[] getTargetInfo() {
return mTargetInfo;
}
private void showIfNecessary(TextView textView, @Nullable String string) { private void showIfNecessary(TextView textView, @Nullable String string) {
if (string == null || string.isEmpty()) { if (string == null || string.isEmpty()) {
textView.setVisibility(GONE); textView.setVisibility(GONE);
@ -141,7 +172,8 @@ public class SearchResultPlayItem extends LinearLayout implements
} }
} }
private void handleSelection(int eventType) { @Override
public void handleSelection(int eventType) {
if (mPackageName == null) return; if (mPackageName == null) return;
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse( Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(
"https://play.google.com/store/apps/details?id=" "https://play.google.com/store/apps/details?id="
@ -167,12 +199,7 @@ public class SearchResultPlayItem extends LinearLayout implements
} }
private void logSearchEvent(int eventType) { private void logSearchEvent(int eventType) {
SearchTargetEvent searchTargetEvent = getSearchTargetEvent( SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(
SearchTarget.ItemType.PLAY_RESULTS, eventType); new SearchTargetEvent.Builder(mSearchTarget, eventType).build());
searchTargetEvent.bundle = new Bundle();
searchTargetEvent.bundle.putString("package_name", mPackageName);
if (mPlugin != null) {
mPlugin.notifySearchTargetEvent(searchTargetEvent);
}
} }
} }

View File

@ -1,131 +0,0 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3.views;
import static com.android.systemui.plugins.shared.SearchTarget.ItemType.SUGGEST;
import android.content.Context;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.launcher3.Launcher;
import com.android.launcher3.R;
import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload;
import com.android.launcher3.allapps.search.AllAppsSearchBarController;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.RemoteActionItemInfo;
import com.android.launcher3.touch.ItemClickHandler;
import com.android.systemui.plugins.AllAppsSearchPlugin;
import com.android.systemui.plugins.shared.SearchTarget;
import com.android.systemui.plugins.shared.SearchTargetEvent;
/**
* A view representing a fallback search suggestion row.
*/
public class SearchResultSuggestRow extends LinearLayout implements
View.OnClickListener, AllAppsSearchBarController.PayloadResultHandler<SearchTarget> {
private final Object[] mTargetInfo = createTargetInfo();
private AllAppsSearchPlugin mPlugin;
private AdapterItemWithPayload<SearchTarget> mAdapterItem;
private TextView mTitle;
public SearchResultSuggestRow(@NonNull Context context) {
super(context);
}
public SearchResultSuggestRow(@NonNull Context context,
@Nullable AttributeSet attrs) {
super(context, attrs);
}
public SearchResultSuggestRow(@NonNull Context context, @Nullable AttributeSet attrs,
int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
mTitle = findViewById(R.id.title);
setOnClickListener(this);
}
@Override
public void applyAdapterInfo(AdapterItemWithPayload<SearchTarget> adapterItemWithPayload) {
mAdapterItem = adapterItemWithPayload;
SearchTarget payload = adapterItemWithPayload.getPayload();
mPlugin = adapterItemWithPayload.getPlugin();
if (payload.mRemoteAction != null) {
RemoteActionItemInfo itemInfo = new RemoteActionItemInfo(payload.mRemoteAction,
payload.bundle.getString(SearchTarget.REMOTE_ACTION_TOKEN),
payload.bundle.getBoolean(SearchTarget.REMOTE_ACTION_SHOULD_START));
setTag(itemInfo);
}
showIfAvailable(mTitle, payload.mRemoteAction.getTitle().toString());
setOnClickListener(v -> handleSelection(SearchTargetEvent.SELECT));
adapterItemWithPayload.setSelectionHandler(this::handleSelection);
}
@Override
public Object[] getTargetInfo() {
return mTargetInfo;
}
private void handleSelection(int eventType) {
ItemInfo itemInfo = (ItemInfo) getTag();
Launcher launcher = Launcher.getLauncher(getContext());
if (itemInfo instanceof RemoteActionItemInfo) return;
RemoteActionItemInfo remoteItemInfo = (RemoteActionItemInfo) itemInfo;
ItemClickHandler.onClickRemoteAction(launcher, remoteItemInfo);
SearchTargetEvent searchTargetEvent = getSearchTargetEvent(SUGGEST, eventType);
searchTargetEvent.bundle = new Bundle();
searchTargetEvent.remoteAction = remoteItemInfo.getRemoteAction();
searchTargetEvent.bundle.putBoolean(SearchTarget.REMOTE_ACTION_SHOULD_START,
remoteItemInfo.shouldStartInLauncher());
searchTargetEvent.bundle.putString(SearchTarget.REMOTE_ACTION_TOKEN,
remoteItemInfo.getToken());
if (mPlugin != null) {
mPlugin.notifySearchTargetEvent(searchTargetEvent);
}
}
@Override
public void onClick(View view) {
handleSelection(SearchTargetEvent.SELECT);
}
private void showIfAvailable(TextView view, @Nullable String string) {
System.out.println("Plugin suggest string:" + string);
if (TextUtils.isEmpty(string)) {
view.setVisibility(GONE);
} else {
System.out.println("Plugin suggest string:" + string);
view.setVisibility(VISIBLE);
view.setText(string);
}
}
}

View File

@ -21,14 +21,16 @@ import android.widget.TextView;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.android.launcher3.allapps.AllAppsGridAdapter;
import com.android.launcher3.allapps.search.AllAppsSearchBarController; import com.android.launcher3.allapps.search.AllAppsSearchBarController;
import com.android.systemui.plugins.shared.SearchTarget;
/** /**
* Header text view that shows a title for a given section in All apps search * Header text view that shows a title for a given section in All apps search
*/ */
public class SearchSectionHeaderView extends TextView implements public class SearchSectionHeaderView extends TextView implements
AllAppsSearchBarController.PayloadResultHandler<String> { AllAppsSearchBarController.SearchTargetHandler {
public static final String TARGET_TYPE_SECTION_HEADER = "section_header";
public SearchSectionHeaderView(Context context) { public SearchSectionHeaderView(Context context) {
super(context); super(context);
} }
@ -43,8 +45,8 @@ public class SearchSectionHeaderView extends TextView implements
} }
@Override @Override
public void applyAdapterInfo(AllAppsGridAdapter.AdapterItemWithPayload<String> adapterItem) { public void applySearchTarget(SearchTarget searchTarget) {
String title = adapterItem.getPayload(); String title = searchTarget.getExtras().getString("title");
if (title == null || !title.isEmpty()) { if (title == null || !title.isEmpty()) {
setText(title); setText(title);
setVisibility(VISIBLE); setVisibility(VISIBLE);
@ -52,9 +54,4 @@ public class SearchSectionHeaderView extends TextView implements
setVisibility(INVISIBLE); setVisibility(INVISIBLE);
} }
} }
@Override
public Object[] getTargetInfo() {
return null;
}
} }

View File

@ -29,9 +29,8 @@ import androidx.annotation.Nullable;
import com.android.launcher3.Launcher; import com.android.launcher3.Launcher;
import com.android.launcher3.R; import com.android.launcher3.R;
import com.android.launcher3.allapps.AllAppsGridAdapter;
import com.android.launcher3.allapps.search.AllAppsSearchBarController; import com.android.launcher3.allapps.search.AllAppsSearchBarController;
import com.android.systemui.plugins.AllAppsSearchPlugin; import com.android.launcher3.allapps.search.SearchEventTracker;
import com.android.systemui.plugins.shared.SearchTarget; import com.android.systemui.plugins.shared.SearchTarget;
import com.android.systemui.plugins.shared.SearchTargetEvent; import com.android.systemui.plugins.shared.SearchTargetEvent;
@ -41,14 +40,16 @@ import java.util.ArrayList;
* A row of tappable TextViews with a breadcrumb for settings search. * A row of tappable TextViews with a breadcrumb for settings search.
*/ */
public class SearchSettingsRowView extends LinearLayout implements public class SearchSettingsRowView extends LinearLayout implements
View.OnClickListener, AllAppsSearchBarController.PayloadResultHandler<Bundle> { View.OnClickListener, AllAppsSearchBarController.SearchTargetHandler {
public static final String TARGET_TYPE_SETTINGS_ROW = "settings_row";
private TextView mTitleView; private TextView mTitleView;
private TextView mDescriptionView; private TextView mDescriptionView;
private TextView mBreadcrumbsView; private TextView mBreadcrumbsView;
private Intent mIntent; private Intent mIntent;
private AllAppsSearchPlugin mPlugin; private SearchTarget mSearchTarget;
private final Object[] mTargetInfo = createTargetInfo();
public SearchSettingsRowView(@NonNull Context context) { public SearchSettingsRowView(@NonNull Context context) {
@ -75,10 +76,9 @@ public class SearchSettingsRowView extends LinearLayout implements
} }
@Override @Override
public void applyAdapterInfo( public void applySearchTarget(SearchTarget searchTarget) {
AllAppsGridAdapter.AdapterItemWithPayload<Bundle> adapterItemWithPayload) { mSearchTarget = searchTarget;
Bundle bundle = adapterItemWithPayload.getPayload(); Bundle bundle = searchTarget.getExtras();
mPlugin = adapterItemWithPayload.getPlugin();
mIntent = bundle.getParcelable("intent"); mIntent = bundle.getParcelable("intent");
showIfAvailable(mTitleView, bundle.getString("title")); showIfAvailable(mTitleView, bundle.getString("title"));
showIfAvailable(mDescriptionView, bundle.getString("description")); showIfAvailable(mDescriptionView, bundle.getString("description"));
@ -86,12 +86,7 @@ public class SearchSettingsRowView extends LinearLayout implements
//TODO: implement RTL friendly breadcrumbs view //TODO: implement RTL friendly breadcrumbs view
showIfAvailable(mBreadcrumbsView, breadcrumbs != null showIfAvailable(mBreadcrumbsView, breadcrumbs != null
? String.join(" > ", breadcrumbs) : null); ? String.join(" > ", breadcrumbs) : null);
adapterItemWithPayload.setSelectionHandler(this::handleSelection); SearchEventTracker.INSTANCE.get(getContext()).registerWeakHandler(searchTarget, this);
}
@Override
public Object[] getTargetInfo() {
return mTargetInfo;
} }
private void showIfAvailable(TextView view, @Nullable String string) { private void showIfAvailable(TextView view, @Nullable String string) {
@ -108,19 +103,15 @@ public class SearchSettingsRowView extends LinearLayout implements
handleSelection(SearchTargetEvent.SELECT); handleSelection(SearchTargetEvent.SELECT);
} }
private void handleSelection(int eventType) { @Override
public void handleSelection(int eventType) {
if (mIntent == null) return; if (mIntent == null) return;
// TODO: create ItemInfo object and then use it to call startActivityForResult for proper // TODO: create ItemInfo object and then use it to call startActivityForResult for proper
// WW logging // WW logging
Launcher launcher = Launcher.getLauncher(getContext()); Launcher launcher = Launcher.getLauncher(getContext());
launcher.startActivityForResult(mIntent, 0); launcher.startActivityForResult(mIntent, 0);
SearchTargetEvent searchTargetEvent = getSearchTargetEvent( SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(
SearchTarget.ItemType.SETTINGS_ROW, eventType); new SearchTargetEvent.Builder(mSearchTarget, eventType).build());
searchTargetEvent.bundle = new Bundle();
searchTargetEvent.bundle.putParcelable("intent", mIntent);
if (mPlugin != null) {
mPlugin.notifySearchTargetEvent(searchTargetEvent);
}
} }
} }

View File

@ -0,0 +1,82 @@
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.launcher3.views;
import android.content.Context;
import android.net.Uri;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData;
import androidx.slice.Slice;
import androidx.slice.SliceItem;
import androidx.slice.widget.EventInfo;
import androidx.slice.widget.SliceLiveData;
import androidx.slice.widget.SliceView;
import com.android.launcher3.Launcher;
import com.android.launcher3.allapps.search.SearchEventTracker;
import com.android.systemui.plugins.shared.SearchTarget;
import com.android.systemui.plugins.shared.SearchTargetEvent;
/**
* A Wrapper class for {@link SliceView} search results
*/
public class SearchSliceWrapper implements SliceView.OnSliceActionListener {
public static final String TARGET_TYPE_SLICE = "settings_slice";
private static final String TAG = "SearchSliceController";
private static final String URI_EXTRA_KEY = "slice_uri";
private final Launcher mLauncher;
private final SearchTarget mSearchTarget;
private final SliceView mSliceView;
private LiveData<Slice> mSliceLiveData;
public SearchSliceWrapper(Context context, SliceView sliceView, SearchTarget searchTarget) {
mLauncher = Launcher.getLauncher(context);
mSearchTarget = searchTarget;
mSliceView = sliceView;
sliceView.setOnSliceActionListener(this);
try {
mSliceLiveData = SliceLiveData.fromUri(mLauncher, getSliceUri());
mSliceLiveData.observe((Launcher) mLauncher, sliceView);
} catch (Exception ex) {
Log.e(TAG, "unable to bind slice", ex);
}
}
/**
* Unregisters event handlers and removes lifecycle observer
*/
public void destroy() {
mSliceView.setOnSliceActionListener(null);
mSliceLiveData.removeObservers(mLauncher);
}
@Override
public void onSliceAction(@NonNull EventInfo info, @NonNull SliceItem item) {
SearchEventTracker.INSTANCE.get(mLauncher).notifySearchTargetEvent(
new SearchTargetEvent.Builder(mSearchTarget,
SearchTargetEvent.CHILD_SELECT).build());
}
private Uri getSliceUri() {
return mSearchTarget.getExtras().getParcelable(URI_EXTRA_KEY);
}
}

View File

@ -15,6 +15,9 @@
*/ */
package com.android.launcher3.views; package com.android.launcher3.views;
import static com.android.launcher3.views.SearchResultIconRow.REMOTE_ACTION_SHOULD_START;
import static com.android.launcher3.views.SearchResultIconRow.REMOTE_ACTION_TOKEN;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.graphics.Bitmap; import android.graphics.Bitmap;
@ -26,14 +29,13 @@ import androidx.core.graphics.drawable.RoundedBitmapDrawable;
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory; import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
import com.android.launcher3.Launcher; import com.android.launcher3.Launcher;
import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload;
import com.android.launcher3.allapps.search.AllAppsSearchBarController; import com.android.launcher3.allapps.search.AllAppsSearchBarController;
import com.android.launcher3.allapps.search.SearchEventTracker;
import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.RemoteActionItemInfo; import com.android.launcher3.model.data.RemoteActionItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo; import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.touch.ItemClickHandler; import com.android.launcher3.touch.ItemClickHandler;
import com.android.launcher3.util.Themes; import com.android.launcher3.util.Themes;
import com.android.systemui.plugins.AllAppsSearchPlugin;
import com.android.systemui.plugins.shared.SearchTarget; import com.android.systemui.plugins.shared.SearchTarget;
import com.android.systemui.plugins.shared.SearchTargetEvent; import com.android.systemui.plugins.shared.SearchTargetEvent;
@ -41,11 +43,12 @@ import com.android.systemui.plugins.shared.SearchTargetEvent;
* A view representing a high confidence app search result that includes shortcuts * A view representing a high confidence app search result that includes shortcuts
*/ */
public class ThumbnailSearchResultView extends androidx.appcompat.widget.AppCompatImageView public class ThumbnailSearchResultView extends androidx.appcompat.widget.AppCompatImageView
implements AllAppsSearchBarController.PayloadResultHandler<SearchTarget> { implements AllAppsSearchBarController.SearchTargetHandler {
private final Object[] mTargetInfo = createTargetInfo(); public static final String TARGET_TYPE_SCREENSHOT = "screenshot";
AllAppsSearchPlugin mPlugin; public static final String TARGET_TYPE_SCREENSHOT_LEGACY = "screenshot_legacy";
int mPosition;
private SearchTarget mSearchTarget;
public ThumbnailSearchResultView(Context context) { public ThumbnailSearchResultView(Context context) {
super(context); super(context);
@ -59,7 +62,8 @@ public class ThumbnailSearchResultView extends androidx.appcompat.widget.AppComp
super(context, attrs, defStyleAttr); super(context, attrs, defStyleAttr);
} }
private void handleSelection(int eventType) { @Override
public void handleSelection(int eventType) {
Launcher launcher = Launcher.getLauncher(getContext()); Launcher launcher = Launcher.getLauncher(getContext());
ItemInfo itemInfo = (ItemInfo) getTag(); ItemInfo itemInfo = (ItemInfo) getTag();
if (itemInfo instanceof RemoteActionItemInfo) { if (itemInfo instanceof RemoteActionItemInfo) {
@ -68,33 +72,29 @@ public class ThumbnailSearchResultView extends androidx.appcompat.widget.AppComp
} else { } else {
ItemClickHandler.onClickAppShortcut(this, (WorkspaceItemInfo) itemInfo, launcher); ItemClickHandler.onClickAppShortcut(this, (WorkspaceItemInfo) itemInfo, launcher);
} }
if (mPlugin != null) { SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(
SearchTargetEvent event = getSearchTargetEvent( new SearchTargetEvent.Builder(mSearchTarget, eventType).build());
SearchTarget.ItemType.SCREENSHOT, eventType);
mPlugin.notifySearchTargetEvent(event);
}
} }
@Override @Override
public void applyAdapterInfo(AdapterItemWithPayload<SearchTarget> adapterItem) { public void applySearchTarget(SearchTarget target) {
Launcher launcher = Launcher.getLauncher(getContext()); mSearchTarget = target;
mPosition = adapterItem.position;
SearchTarget target = adapterItem.getPayload();
Bitmap bitmap; Bitmap bitmap;
if (target.mRemoteAction != null) { if (target.getRemoteAction() != null) {
RemoteActionItemInfo itemInfo = new RemoteActionItemInfo(target.mRemoteAction, RemoteActionItemInfo itemInfo = new RemoteActionItemInfo(target.getRemoteAction(),
target.bundle.getString(SearchTarget.REMOTE_ACTION_TOKEN), target.getExtras().getString(REMOTE_ACTION_TOKEN),
target.bundle.getBoolean(SearchTarget.REMOTE_ACTION_SHOULD_START)); target.getExtras().getBoolean(REMOTE_ACTION_SHOULD_START));
ItemClickHandler.onClickRemoteAction(launcher, itemInfo); bitmap = ((BitmapDrawable) target.getRemoteAction().getIcon()
bitmap = ((BitmapDrawable) target.mRemoteAction.getIcon()
.loadDrawable(getContext())).getBitmap(); .loadDrawable(getContext())).getBitmap();
setTag(itemInfo); // crop
bitmap = Bitmap.createBitmap(bitmap, 0,
bitmap.getHeight() / 2 - bitmap.getWidth() / 2,
bitmap.getWidth(), bitmap.getWidth());
} else { } else {
bitmap = (Bitmap) target.bundle.getParcelable("bitmap"); bitmap = (Bitmap) target.getExtras().getParcelable("bitmap");
WorkspaceItemInfo itemInfo = new WorkspaceItemInfo(); WorkspaceItemInfo itemInfo = new WorkspaceItemInfo();
itemInfo.intent = new Intent(Intent.ACTION_VIEW) itemInfo.intent = new Intent(Intent.ACTION_VIEW)
.setData(Uri.parse(target.bundle.getString("uri"))) .setData(Uri.parse(target.getExtras().getString("uri")))
.setType("image/*") .setType("image/*")
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
setTag(itemInfo); setTag(itemInfo);
@ -103,12 +103,6 @@ public class ThumbnailSearchResultView extends androidx.appcompat.widget.AppComp
drawable.setCornerRadius(Themes.getDialogCornerRadius(getContext())); drawable.setCornerRadius(Themes.getDialogCornerRadius(getContext()));
setImageDrawable(drawable); setImageDrawable(drawable);
setOnClickListener(v -> handleSelection(SearchTargetEvent.SELECT)); setOnClickListener(v -> handleSelection(SearchTargetEvent.SELECT));
mPlugin = adapterItem.getPlugin(); SearchEventTracker.INSTANCE.get(getContext()).registerWeakHandler(target, this);
adapterItem.setSelectionHandler(this::handleSelection);
}
@Override
public Object[] getTargetInfo() {
return mTargetInfo;
} }
} }

View File

@ -17,6 +17,8 @@
package com.android.systemui.plugins; package com.android.systemui.plugins;
import android.app.Activity; import android.app.Activity;
import android.os.Bundle;
import android.os.CancellationSignal;
import android.view.View; import android.view.View;
import com.android.systemui.plugins.annotations.ProvidesInterface; import com.android.systemui.plugins.annotations.ProvidesInterface;
@ -32,7 +34,7 @@ import java.util.function.Consumer;
@ProvidesInterface(action = AllAppsSearchPlugin.ACTION, version = AllAppsSearchPlugin.VERSION) @ProvidesInterface(action = AllAppsSearchPlugin.ACTION, version = AllAppsSearchPlugin.VERSION)
public interface AllAppsSearchPlugin extends Plugin { public interface AllAppsSearchPlugin extends Plugin {
String ACTION = "com.android.systemui.action.PLUGIN_ALL_APPS_SEARCH_ACTIONS"; String ACTION = "com.android.systemui.action.PLUGIN_ALL_APPS_SEARCH_ACTIONS";
int VERSION = 7; int VERSION = 8;
void setup(Activity activity, View view); void setup(Activity activity, View view);
@ -49,10 +51,21 @@ public interface AllAppsSearchPlugin extends Plugin {
void onWindowVisibilityChanged(int visibility); void onWindowVisibilityChanged(int visibility);
/** /**
* Send signal when user starts typing, perform search, when search ends * Send signal when user starts typing, perform search, notify search target
* event when search ends.
*/ */
void startedSearchSession(); void startedSearchSession();
void performSearch(String query, Consumer<List<SearchTarget>> results);
/**
* Main function that triggers search.
*
* @param input string that has been typed by a user
* @param inputArgs extra info that may be relevant for the input query
* @param results contains the result that will be rendered in all apps search surface
* @param cancellationSignal {@link CancellationSignal} can be used to share status of current
*/
void query(String input, Bundle inputArgs, Consumer<List<SearchTarget>> results,
CancellationSignal cancellationSignal);
/** /**
* Send over search target interaction events to Plugin * Send over search target interaction events to Plugin

View File

@ -16,8 +16,10 @@
package com.android.systemui.plugins.shared; package com.android.systemui.plugins.shared;
import android.app.RemoteAction; import android.app.RemoteAction;
import android.content.ComponentName;
import android.content.pm.ShortcutInfo; import android.content.pm.ShortcutInfo;
import android.os.Bundle; import android.os.Bundle;
import android.os.UserHandle;
import java.util.List; import java.util.List;
@ -26,139 +28,145 @@ import java.util.List;
*/ */
public class SearchTarget implements Comparable<SearchTarget> { public class SearchTarget implements Comparable<SearchTarget> {
private final String mItemId;
private final String mItemType;
private final float mScore;
/** private final ComponentName mComponentName;
* A bundle key for boolean value of whether remote action should be started in launcher or not private final UserHandle mUserHandle;
*/ private final List<ShortcutInfo> mShortcutInfos;
public static final String REMOTE_ACTION_SHOULD_START = "should_start_for_result"; //TODO: (sfufa) replace with a list of a custom type
public static final String REMOTE_ACTION_TOKEN = "action_token"; private final RemoteAction mRemoteAction;
private final Bundle mExtras;
private SearchTarget(String itemId, String itemType, float score,
public enum ViewType { ComponentName componentName, UserHandle userHandle, List<ShortcutInfo> shortcutInfos,
RemoteAction remoteAction, Bundle extras) {
/** mItemId = itemId;
* Consists of N number of icons. (N: launcher column count) mItemType = itemType;
*/ mScore = score;
TOP_HIT(0), mComponentName = componentName;
mUserHandle = userHandle;
/** mShortcutInfos = shortcutInfos;
* Consists of 1 icon and two subsidiary icons. mExtras = extras;
*/ mRemoteAction = remoteAction;
HERO(1),
/**
* Main/sub/breadcrumb texts are rendered.
*/
DETAIL(2),
/**
* Consists of an icon, three detail strings.
*/
ROW(3),
/**
* Consists of an icon, three detail strings and a button.
*/
ROW_WITH_BUTTON(4),
/**
* Consists of a single slice view
*/
SLICE(5),
/**
* Similar to hero section.
*/
SHORTCUT(6),
/**
* Person icon and handling app icons are rendered.
*/
PEOPLE(7),
/**
* N number of 1x1 ratio thumbnail is rendered.
* (current N = 3)
*/
THUMBNAIL(8),
/**
* Fallback search icon and relevant text is rendered.
*/
SUGGEST(9);
private final int mId;
ViewType(int id) {
mId = id;
} }
public int get() { public String getItemId() {
return mId; return mItemId;
}
} }
public enum ItemType { public String getItemType() {
PLAY_RESULTS(0, "Play Store", ViewType.DETAIL), return mItemType;
SETTINGS_ROW(1, "Settings", ViewType.ROW),
SETTINGS_SLICE(2, "Settings", ViewType.SLICE),
APP(3, "", ViewType.TOP_HIT),
APP_HERO(4, "", ViewType.HERO),
SHORTCUT(5, "Shortcuts", ViewType.SHORTCUT),
PEOPLE(6, "People", ViewType.PEOPLE),
SCREENSHOT(7, "Screenshots", ViewType.THUMBNAIL),
ACTION(8, "Actions", ViewType.SHORTCUT),
SUGGEST(9, "Fallback Search", ViewType.SUGGEST),
CHROME_TAB(10, "Chrome Tab", ViewType.SHORTCUT);
private final int mId;
/** Used to render section title. */
private final String mTitle;
private final ViewType mViewType;
ItemType(int id, String title, ViewType type) {
mId = id;
mTitle = title;
mViewType = type;
} }
public ViewType getViewType() { public ComponentName getComponentName() {
return mViewType; return mComponentName;
} }
public String getTitle() { public UserHandle getUserHandle() {
return mTitle; return mUserHandle;
} }
public int getId() { public float getScore() {
return mId; return mScore;
}
} }
public ItemType type; public List<ShortcutInfo> getShortcutInfos() {
public List<ShortcutInfo> shortcuts; return mShortcutInfos;
public Bundle bundle; }
public float score;
public String mSessionId;
public RemoteAction mRemoteAction;
/** public Bundle getExtras() {
* Constructor to create the search target. Bundle is currently temporary to hold return mExtras;
* search target primitives that cannot be expressed as java primitive objects }
* or AOSP native objects.
*/ public RemoteAction getRemoteAction() {
public SearchTarget(ItemType itemType, List<ShortcutInfo> shortcuts, return mRemoteAction;
Bundle bundle, float score, String sessionId) {
this.type = itemType;
this.shortcuts = shortcuts;
this.bundle = bundle;
this.score = score;
this.mSessionId = sessionId;
} }
@Override @Override
public int compareTo(SearchTarget o) { public int compareTo(SearchTarget o) {
return Float.compare(o.score, score); return Float.compare(o.mScore, mScore);
}
/**
* A builder for {@link SearchTarget}
*/
public static final class Builder {
private String mItemId;
private final String mItemType;
private final float mScore;
private ComponentName mComponentName;
private UserHandle mUserHandle;
private List<ShortcutInfo> mShortcutInfos;
private Bundle mExtras;
private RemoteAction mRemoteAction;
public Builder(String itemType, float score) {
this(itemType, score, null, null);
}
public Builder(String itemType, float score, ComponentName cn,
UserHandle user) {
mItemType = itemType;
mScore = score;
mComponentName = cn;
mUserHandle = user;
}
public String getItemId() {
return mItemId;
}
public float getScore() {
return mScore;
}
public Builder setItemId(String itemId) {
mItemId = itemId;
return this;
}
public Builder setComponentName(ComponentName componentName) {
mComponentName = componentName;
return this;
}
public Builder setUserHandle(UserHandle userHandle) {
mUserHandle = userHandle;
return this;
}
public Builder setShortcutInfos(List<ShortcutInfo> shortcutInfos) {
mShortcutInfos = shortcutInfos;
return this;
}
public Builder setExtras(Bundle extras) {
mExtras = extras;
return this;
}
public Builder setRemoteAction(RemoteAction remoteAction) {
mRemoteAction = remoteAction;
return this;
}
/**
* Builds a {@link SearchTarget}
*/
public SearchTarget build() {
if (mItemId == null) {
throw new IllegalStateException("Item ID is required for building SearchTarget");
}
return new SearchTarget(mItemId, mItemType, mScore, mComponentName, mUserHandle,
mShortcutInfos,
mRemoteAction, mExtras);
}
} }
} }

View File

@ -15,32 +15,76 @@
*/ */
package com.android.systemui.plugins.shared; package com.android.systemui.plugins.shared;
import android.app.RemoteAction;
import android.content.pm.ShortcutInfo;
import android.os.Bundle; import android.os.Bundle;
/** /**
* Event used for the feedback loop to the plugin. (and future aiai) * Event used for the feedback loop to the plugin. (and future aiai)
*/ */
public class SearchTargetEvent { public class SearchTargetEvent {
public static final int POSITION_NONE = -1;
public static final int SELECT = 0; public static final int SELECT = 0;
public static final int QUICK_SELECT = 1; public static final int QUICK_SELECT = 1;
public static final int LONG_PRESS = 2; public static final int LONG_PRESS = 2;
public static final int CHILD_SELECT = 3; public static final int CHILD_SELECT = 3;
public SearchTarget.ItemType type; private final SearchTarget mSearchTarget;
public ShortcutInfo shortcut; private final int mEventType;
public RemoteAction remoteAction; private final int mShortcutPosition;
public int eventType; private final Bundle mExtras;
public Bundle bundle;
public int index;
public String sessionIdentifier;
public SearchTargetEvent(SearchTarget.ItemType itemType, int eventType, int index, public SearchTargetEvent(SearchTarget searchTarget, int eventType, int shortcutPosition,
String sessionId) { Bundle extras) {
this.type = itemType; mSearchTarget = searchTarget;
this.eventType = eventType; mEventType = eventType;
this.index = index; mShortcutPosition = shortcutPosition;
this.sessionIdentifier = sessionId; mExtras = extras;
}
public SearchTarget getSearchTarget() {
return mSearchTarget;
}
public int getShortcutPosition() {
return mShortcutPosition;
}
public int getEventType() {
return mEventType;
}
public Bundle getExtras() {
return mExtras;
}
/**
* A builder for {@link SearchTarget}
*/
public static final class Builder {
private final SearchTarget mSearchTarget;
private final int mEventType;
private int mShortcutPosition = POSITION_NONE;
private Bundle mExtras;
public Builder(SearchTarget searchTarget, int eventType) {
mSearchTarget = searchTarget;
mEventType = eventType;
}
public Builder setShortcutPosition(int shortcutPosition) {
mShortcutPosition = shortcutPosition;
return this;
}
public Builder setExtras(Bundle extras) {
mExtras = extras;
return this;
}
public SearchTargetEvent build() {
return new SearchTargetEvent(mSearchTarget, mEventType, mShortcutPosition, mExtras);
} }
} }
}

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<appwidget-provider <appwidget-provider
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="180dp" android:minWidth="1dp"
android:minHeight="110dp" android:minHeight="1dp"
android:updatePeriodMillis="86400000" android:updatePeriodMillis="86400000"
android:initialLayout="@layout/test_layout_appwidget_blue" android:initialLayout="@layout/test_layout_appwidget_blue"
android:resizeMode="horizontal|vertical" android:resizeMode="horizontal|vertical"

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<appwidget-provider <appwidget-provider
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="180dp" android:minWidth="1dp"
android:minHeight="110dp" android:minHeight="1dp"
android:updatePeriodMillis="86400000" android:updatePeriodMillis="86400000"
android:initialLayout="@layout/test_layout_appwidget_red" android:initialLayout="@layout/test_layout_appwidget_red"
android:resizeMode="horizontal|vertical" android:resizeMode="horizontal|vertical"

View File

@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<appwidget-provider <appwidget-provider
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="180dp" android:minWidth="1dp"
android:minHeight="110dp" android:minHeight="1dp"
android:updatePeriodMillis="86400000" android:updatePeriodMillis="86400000"
android:initialLayout="@layout/test_layout_appwidget_blue" android:initialLayout="@layout/test_layout_appwidget_blue"
android:configure="com.android.launcher3.testcomponent.WidgetConfigActivity" android:configure="com.android.launcher3.testcomponent.WidgetConfigActivity"

View File

@ -88,10 +88,6 @@ public class Background extends LauncherInstrumentation.VisibleContainer {
? LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE ? LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE
: LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER; : LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER;
// b/156044202
mLauncher.log("Hierarchy before swiping up to overview:");
mLauncher.dumpViewHierarchy();
mLauncher.sendPointer( mLauncher.sendPointer(
downTime, downTime, MotionEvent.ACTION_DOWN, start, gestureScope); downTime, downTime, MotionEvent.ACTION_DOWN, start, gestureScope);
mLauncher.executeAndWaitForLauncherEvent( mLauncher.executeAndWaitForLauncherEvent(