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:
commit
87f2b09072
|
@ -36,14 +36,12 @@ java_library_static {
|
|||
name: "launcher_log_protos_lite",
|
||||
srcs: [
|
||||
"protos/*.proto",
|
||||
"proto_overrides/*.proto",
|
||||
],
|
||||
sdk_version: "current",
|
||||
proto: {
|
||||
type: "lite",
|
||||
local_include_dirs:[
|
||||
"protos",
|
||||
"proto_overrides",
|
||||
],
|
||||
},
|
||||
static_libs: ["libprotobuf-java-lite"],
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
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.SET_WALLPAPER" />
|
||||
<uses-permission android:name="android.permission.SET_WALLPAPER_HINTS" />
|
||||
|
|
80
buglist.txt
80
buglist.txt
|
@ -1,38 +1,42 @@
|
|||
160759508
|
||||
144170434
|
||||
144170434
|
||||
161801331
|
||||
162564471
|
||||
160361464
|
||||
161901771
|
||||
160568387
|
||||
162623012
|
||||
162012217
|
||||
160718310
|
||||
160361464
|
||||
160718310
|
||||
162012217
|
||||
160748731
|
||||
154951045
|
||||
162861289
|
||||
149934536
|
||||
149934536
|
||||
144170434
|
||||
144170434
|
||||
162454040
|
||||
161273376
|
||||
149934536
|
||||
149934536
|
||||
158701272
|
||||
162871508
|
||||
149934536
|
||||
161939759
|
||||
154964045
|
||||
161536946
|
||||
149934536
|
||||
161685099
|
||||
162812884
|
||||
162480567
|
||||
162480567
|
||||
149934536
|
||||
149934536
|
||||
171450807
|
||||
170675311
|
||||
170338029
|
||||
170338170
|
||||
160544577
|
||||
171171594
|
||||
170488559
|
||||
171131394
|
||||
171131394
|
||||
171026321
|
||||
170648272
|
||||
170752716
|
||||
170611866
|
||||
170702596
|
||||
170487752
|
||||
170665892
|
||||
168608912
|
||||
170636685
|
||||
169771796
|
||||
141126144
|
||||
166614700
|
||||
168805872
|
||||
170263425
|
||||
169221288
|
||||
143965596
|
||||
169221287
|
||||
167259591
|
||||
156044202
|
||||
169438169
|
||||
164926736
|
||||
168653219
|
||||
169963211
|
||||
170121063
|
||||
169988381
|
||||
169980192
|
||||
169221288
|
||||
169385783
|
||||
168167693
|
||||
169796517
|
||||
169330678
|
||||
168818961
|
||||
168608912
|
||||
|
|
|
@ -1,24 +1,39 @@
|
|||
144170434
|
||||
149934536
|
||||
154951045
|
||||
154964045
|
||||
158701272
|
||||
160361464
|
||||
160568387
|
||||
160718310
|
||||
160748731
|
||||
160759508
|
||||
161273376
|
||||
161536946
|
||||
161685099
|
||||
161801331
|
||||
161901771
|
||||
161939759
|
||||
162012217
|
||||
162454040
|
||||
162480567
|
||||
162564471
|
||||
162623012
|
||||
162812884
|
||||
162861289
|
||||
162871508
|
||||
141126144
|
||||
143965596
|
||||
156044202
|
||||
160544577
|
||||
164926736
|
||||
166614700
|
||||
167259591
|
||||
168167693
|
||||
168608912
|
||||
168653219
|
||||
168805872
|
||||
168818961
|
||||
169221287
|
||||
169221288
|
||||
169330678
|
||||
169385783
|
||||
169438169
|
||||
169771796
|
||||
169796517
|
||||
169963211
|
||||
169980192
|
||||
169988381
|
||||
170121063
|
||||
170263425
|
||||
170338029
|
||||
170338170
|
||||
170487752
|
||||
170488559
|
||||
170611866
|
||||
170636685
|
||||
170648272
|
||||
170665892
|
||||
170675311
|
||||
170702596
|
||||
170752716
|
||||
171026321
|
||||
171131394
|
||||
171171594
|
||||
171450807
|
||||
|
|
1835
commitlist.txt
1835
commitlist.txt
File diff suppressed because it is too large
Load Diff
|
@ -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 {
|
||||
}
|
|
@ -28,4 +28,40 @@ message LauncherTraceProto {
|
|||
message TouchInteractionServiceProto {
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,6 +51,8 @@
|
|||
<dimen name="recents_empty_message_text_size">16sp</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
|
||||
in various configurations -->
|
||||
<dimen name="task_card_vert_space">40dp</dimen>
|
||||
|
|
|
@ -16,14 +16,10 @@
|
|||
|
||||
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.LINEAR;
|
||||
import static com.android.quickstep.TaskViewUtils.createRecentsWindowAnimator;
|
||||
import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.content.Context;
|
||||
|
@ -32,11 +28,8 @@ import android.view.View;
|
|||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
import com.android.launcher3.anim.Interpolators;
|
||||
import com.android.launcher3.anim.PendingAnimation;
|
||||
import com.android.quickstep.TaskViewUtils;
|
||||
import com.android.quickstep.views.RecentsView;
|
||||
import com.android.quickstep.views.TaskView;
|
||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||
|
||||
/**
|
||||
|
@ -53,60 +46,16 @@ public final class LauncherAppTransitionManagerImpl extends QuickstepAppTransiti
|
|||
protected boolean isLaunchingFromRecents(@NonNull View v,
|
||||
@Nullable RemoteAnimationTargetCompat[] targets) {
|
||||
return mLauncher.getStateManager().getState().overviewUi
|
||||
&& findTaskViewToLaunch(mLauncher, v, targets) != null;
|
||||
&& findTaskViewToLaunch(mLauncher.getOverviewPanel(), v, targets) != null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
|
||||
@NonNull RemoteAnimationTargetCompat[] appTargets,
|
||||
@NonNull RemoteAnimationTargetCompat[] wallpaperTargets, boolean launcherClosing) {
|
||||
RecentsView recentsView = mLauncher.getOverviewPanel();
|
||||
boolean skipLauncherChanges = !launcherClosing;
|
||||
|
||||
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);
|
||||
TaskViewUtils.composeRecentsLaunchAnimator(anim, v, appTargets, wallpaperTargets,
|
||||
launcherClosing, mLauncher.getStateManager(), mLauncher.getOverviewPanel(),
|
||||
mLauncher.getDepthController());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -153,6 +153,7 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
|
|||
private final float mContentTransY;
|
||||
private final float mWorkspaceTransY;
|
||||
private final float mClosingWindowTransY;
|
||||
private final float mMaxShadowRadius;
|
||||
|
||||
private DeviceProfile mDeviceProfile;
|
||||
|
||||
|
@ -186,6 +187,7 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
|
|||
mContentTransY = res.getDimensionPixelSize(R.dimen.content_trans_y);
|
||||
mWorkspaceTransY = res.getDimensionPixelSize(R.dimen.workspace_trans_y);
|
||||
mClosingWindowTransY = res.getDimensionPixelSize(R.dimen.closing_window_trans_y);
|
||||
mMaxShadowRadius = res.getDimensionPixelSize(R.dimen.max_shadow_radius);
|
||||
|
||||
mLauncher.addOnDeviceProfileChangeListener(this);
|
||||
}
|
||||
|
@ -538,6 +540,8 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
|
|||
EXAGGERATED_EASE);
|
||||
FloatProp mWindowRadius = new FloatProp(initialWindowRadius, windowRadius, 0,
|
||||
RADIUS_DURATION, EXAGGERATED_EASE);
|
||||
FloatProp mShadowRadius = new FloatProp(0, mMaxShadowRadius, 0,
|
||||
APP_LAUNCH_DURATION, EXAGGERATED_EASE);
|
||||
|
||||
@Override
|
||||
public void onUpdate(float percent) {
|
||||
|
@ -600,7 +604,8 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
|
|||
builder.withMatrix(matrix)
|
||||
.withWindowCrop(crop)
|
||||
.withAlpha(1f - mIconAlpha.value)
|
||||
.withCornerRadius(mWindowRadius.value);
|
||||
.withCornerRadius(mWindowRadius.value)
|
||||
.withShadowRadius(mShadowRadius.value);
|
||||
} else {
|
||||
tmpPos.set(target.position.x, target.position.y);
|
||||
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 mScale = new FloatProp(1f, 1f, 0, duration, DEACCEL_1_7);
|
||||
FloatProp mAlpha = new FloatProp(1f, 0f, 25, 125, LINEAR);
|
||||
FloatProp mShadowRadius = new FloatProp(mMaxShadowRadius, 0, 0, duration,
|
||||
DEACCEL_1_7);
|
||||
|
||||
@Override
|
||||
public void onUpdate(float percent) {
|
||||
|
@ -773,7 +780,8 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
|
|||
matrix.postTranslate(tmpPos.x, tmpPos.y);
|
||||
builder.withMatrix(matrix)
|
||||
.withAlpha(mAlpha.value)
|
||||
.withCornerRadius(windowCornerRadius);
|
||||
.withCornerRadius(windowCornerRadius)
|
||||
.withShadowRadius(mShadowRadius.value);
|
||||
} else {
|
||||
matrix.setTranslate(tmpPos.x, tmpPos.y);
|
||||
builder.withMatrix(matrix)
|
||||
|
@ -798,33 +806,31 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
|
|||
}
|
||||
|
||||
private void addCujInstrumentation(Animator anim, int cuj, String transition) {
|
||||
if (Trace.isEnabled()) {
|
||||
anim.addListener(new AnimationSuccessListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
Trace.beginAsyncSection(transition, 0);
|
||||
InteractionJankMonitorWrapper.begin(cuj);
|
||||
super.onAnimationStart(animation);
|
||||
}
|
||||
anim.addListener(new AnimationSuccessListener() {
|
||||
@Override
|
||||
public void onAnimationStart(Animator animation) {
|
||||
Trace.beginAsyncSection(transition, 0);
|
||||
InteractionJankMonitorWrapper.begin(cuj);
|
||||
super.onAnimationStart(animation);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
super.onAnimationCancel(animation);
|
||||
InteractionJankMonitorWrapper.cancel(cuj);
|
||||
}
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animation) {
|
||||
super.onAnimationCancel(animation);
|
||||
InteractionJankMonitorWrapper.cancel(cuj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationSuccess(Animator animator) {
|
||||
InteractionJankMonitorWrapper.end(cuj);
|
||||
}
|
||||
@Override
|
||||
public void onAnimationSuccess(Animator animator) {
|
||||
InteractionJankMonitorWrapper.end(cuj);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
super.onAnimationEnd(animation);
|
||||
Trace.endAsyncSection(TRANSITION_OPEN_LAUNCHER, 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
super.onAnimationEnd(animation);
|
||||
Trace.endAsyncSection(TRANSITION_OPEN_LAUNCHER, 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -108,6 +108,8 @@ public class PredictionRowView extends LinearLayout implements
|
|||
|
||||
AllAppsSectionDecorator.SectionDecorationHandler mDecorationHandler;
|
||||
|
||||
@Nullable private List<ItemInfo> mPendingPredictedItems;
|
||||
|
||||
public PredictionRowView(@NonNull Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
@ -164,6 +166,7 @@ public class PredictionRowView extends LinearLayout implements
|
|||
}
|
||||
mDecorationHandler.onDraw(canvas);
|
||||
mDecorationHandler.onFocusDraw(canvas, getFocusedChild());
|
||||
mLauncher.getAppsView().getActiveRecyclerView().invalidateItemDecorations();
|
||||
}
|
||||
mFocusHelper.draw(canvas);
|
||||
super.dispatchDraw(canvas);
|
||||
|
@ -203,6 +206,16 @@ public class PredictionRowView extends LinearLayout implements
|
|||
* we can optimize by swapping them in place.
|
||||
*/
|
||||
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();
|
||||
items.stream()
|
||||
.filter(itemInfo -> itemInfo instanceof WorkspaceItemInfo)
|
||||
|
@ -341,4 +354,13 @@ public class PredictionRowView extends LinearLayout implements
|
|||
public View getFocusedChild() {
|
||||
return getChildAt(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onVisibilityAggregated(boolean isVisible) {
|
||||
super.onVisibilityAggregated(isVisible);
|
||||
|
||||
if (mPendingPredictedItems != null && !isVisible) {
|
||||
applyPredictedApps(mPendingPredictedItems);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -251,6 +251,28 @@ public class HotseatPredictionController implements DragController.DragListener,
|
|||
* Sets or updates the predicted 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;
|
||||
if (mPredictedItems.isEmpty()) {
|
||||
HotseatRestoreHelper.restoreBackup(mLauncher);
|
||||
|
|
|
@ -250,6 +250,9 @@ public class AppEventProducer implements StatsLogConsumer {
|
|||
case PREDICTION_CONTAINER: {
|
||||
return "predictions";
|
||||
}
|
||||
case SHORTCUTS_CONTAINER: {
|
||||
return "deep-shortcuts";
|
||||
}
|
||||
case FOLDER: {
|
||||
FolderContainer fc = ci.getFolder();
|
||||
switch (fc.getParentContainerCase()) {
|
||||
|
|
|
@ -18,8 +18,7 @@ package com.android.launcher3.model;
|
|||
|
||||
import static android.content.ContentResolver.SCHEME_CONTENT;
|
||||
|
||||
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
||||
import static com.android.launcher3.util.Executors.createAndStartNewLooper;
|
||||
import static com.android.launcher3.Utilities.newContentObserver;
|
||||
|
||||
import android.annotation.TargetApi;
|
||||
import android.app.RemoteAction;
|
||||
|
@ -35,7 +34,7 @@ import android.os.Build;
|
|||
import android.os.Bundle;
|
||||
import android.os.DeadObjectException;
|
||||
import android.os.Handler;
|
||||
import android.os.Message;
|
||||
import android.os.Looper;
|
||||
import android.os.Process;
|
||||
import android.os.UserHandle;
|
||||
import android.text.TextUtils;
|
||||
|
@ -43,7 +42,7 @@ import android.util.ArrayMap;
|
|||
import android.util.Log;
|
||||
|
||||
import androidx.annotation.MainThread;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.WorkerThread;
|
||||
|
||||
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.popup.RemoteActionShortcut;
|
||||
import com.android.launcher3.popup.SystemShortcut;
|
||||
import com.android.launcher3.util.BgObjectWithLooper;
|
||||
import com.android.launcher3.util.MainThreadInitializedObject;
|
||||
import com.android.launcher3.util.PackageManagerHelper;
|
||||
import com.android.launcher3.util.Preconditions;
|
||||
|
@ -68,15 +68,11 @@ import java.util.Map;
|
|||
* Data model for digital wellbeing status of apps.
|
||||
*/
|
||||
@TargetApi(Build.VERSION_CODES.Q)
|
||||
public final class WellbeingModel {
|
||||
public final class WellbeingModel extends BgObjectWithLooper {
|
||||
private static final String TAG = "WellbeingModel";
|
||||
private static final int[] RETRY_TIMES_MS = {5000, 15000, 30000};
|
||||
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 IN_MINIMAL_DEVICE = 2;
|
||||
|
||||
|
@ -98,9 +94,9 @@ public final class WellbeingModel {
|
|||
|
||||
private final Context mContext;
|
||||
private final String mWellbeingProviderPkg;
|
||||
private final Handler mWorkerHandler;
|
||||
|
||||
private final ContentObserver mContentObserver;
|
||||
private Handler mWorkerHandler;
|
||||
private ContentObserver mContentObserver;
|
||||
|
||||
private final Object mModelLock = new Object();
|
||||
// Maps the action Id to the corresponding RemoteAction
|
||||
|
@ -111,60 +107,73 @@ public final class WellbeingModel {
|
|||
|
||||
private WellbeingModel(final Context context) {
|
||||
mContext = context;
|
||||
mWorkerHandler =
|
||||
new Handler(createAndStartNewLooper("WellbeingHandler"), this::handleMessage);
|
||||
|
||||
mWellbeingProviderPkg = mContext.getString(R.string.wellbeing_provider_pkg);
|
||||
mContentObserver = new ContentObserver(MAIN_EXECUTOR.getHandler()) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange, Uri uri) {
|
||||
if (DEBUG || mIsInTest) {
|
||||
Log.d(TAG, "ContentObserver.onChange() called with: selfChange = ["
|
||||
+ 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));
|
||||
initializeInBackground("WellbeingHandler");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onInitialized(Looper looper) {
|
||||
mWorkerHandler = new Handler(looper);
|
||||
mContentObserver = newContentObserver(mWorkerHandler, this::onWellbeingUriChanged);
|
||||
if (!TextUtils.isEmpty(mWellbeingProviderPkg)) {
|
||||
context.registerReceiver(
|
||||
new SimpleBroadcastReceiver(this::onWellbeingProviderChanged),
|
||||
mContext.registerReceiver(
|
||||
new SimpleBroadcastReceiver(t -> restartObserver()),
|
||||
PackageManagerHelper.getPackageFilter(mWellbeingProviderPkg,
|
||||
Intent.ACTION_PACKAGE_ADDED, Intent.ACTION_PACKAGE_CHANGED,
|
||||
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);
|
||||
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
|
||||
filter.addDataScheme("package");
|
||||
context.registerReceiver(new SimpleBroadcastReceiver(this::onAppPackageChanged),
|
||||
filter);
|
||||
mContext.registerReceiver(new SimpleBroadcastReceiver(this::onAppPackageChanged),
|
||||
filter, null, mWorkerHandler);
|
||||
|
||||
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) {
|
||||
mIsInTest = inTest;
|
||||
}
|
||||
|
||||
protected void onWellbeingProviderChanged(Intent intent) {
|
||||
if (DEBUG || mIsInTest) {
|
||||
Log.d(TAG, "Changes to Wellbeing package: intent = [" + intent + "]");
|
||||
}
|
||||
restartObserver();
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private void restartObserver() {
|
||||
final ContentResolver resolver = mContext.getContentResolver();
|
||||
resolver.unregisterContentObserver(mContentObserver);
|
||||
|
@ -179,7 +188,7 @@ public final class WellbeingModel {
|
|||
Log.e(TAG, "Failed to register content observer for " + actionsUri + ": " + e);
|
||||
if (mIsInTest) throw new RuntimeException(e);
|
||||
}
|
||||
updateWellbeingData();
|
||||
updateAllPackages();
|
||||
}
|
||||
|
||||
@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() {
|
||||
return new Uri.Builder()
|
||||
.scheme(SCHEME_CONTENT)
|
||||
|
@ -277,7 +253,8 @@ public final class WellbeingModel {
|
|||
return false;
|
||||
}
|
||||
|
||||
private boolean updateActions(String... packageNames) {
|
||||
@WorkerThread
|
||||
private boolean updateActions(String[] packageNames) {
|
||||
if (packageNames.length == 0) {
|
||||
return true;
|
||||
}
|
||||
|
@ -340,68 +317,51 @@ public final class WellbeingModel {
|
|||
return true;
|
||||
}
|
||||
|
||||
private boolean handleMessage(Message msg) {
|
||||
switch (msg.what) {
|
||||
case MSG_PACKAGE_REMOVED: {
|
||||
String packageName = (String) msg.obj;
|
||||
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;
|
||||
}
|
||||
@WorkerThread
|
||||
private void updateActionsWithRetry(int retryCount, @Nullable String packageName) {
|
||||
String[] packageNames = TextUtils.isEmpty(packageName)
|
||||
? mContext.getSystemService(LauncherApps.class)
|
||||
.getActivityList(null, Process.myUserHandle()).stream()
|
||||
.map(li -> li.getApplicationInfo().packageName).distinct()
|
||||
.toArray(String[]::new)
|
||||
: new String[] { packageName };
|
||||
|
||||
case MSG_FULL_REFRESH: {
|
||||
// Remove all existing messages
|
||||
mWorkerHandler.removeCallbacksAndMessages(null);
|
||||
final String[] packageNames = mContext.getSystemService(LauncherApps.class)
|
||||
.getActivityList(null, Process.myUserHandle()).stream()
|
||||
.map(li -> li.getApplicationInfo().packageName).distinct()
|
||||
.toArray(String[]::new);
|
||||
if (!updateActions(packageNames)) {
|
||||
scheduleRefreshRetry(msg);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
mWorkerHandler.removeCallbacksAndMessages(packageName);
|
||||
if (updateActions(packageNames)) {
|
||||
return;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void scheduleRefreshRetry(Message originalMsg) {
|
||||
int retryCount = originalMsg.arg1;
|
||||
if (retryCount >= RETRY_TIMES_MS.length) {
|
||||
// To many retries, skip
|
||||
return;
|
||||
}
|
||||
|
||||
Message msg = Message.obtain(originalMsg);
|
||||
msg.arg1 = retryCount + 1;
|
||||
mWorkerHandler.sendMessageDelayed(msg, RETRY_TIMES_MS[retryCount]);
|
||||
mWorkerHandler.postDelayed(
|
||||
() -> updateActionsWithRetry(retryCount + 1, packageName),
|
||||
packageName, RETRY_TIMES_MS[retryCount]);
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private void updateAllPackages() {
|
||||
updateActionsWithRetry(0, null);
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private void onAppPackageChanged(Intent intent) {
|
||||
if (DEBUG || mIsInTest) Log.d(TAG, "Changes in apps: intent = [" + intent + "]");
|
||||
Preconditions.assertUIThread();
|
||||
Preconditions.assertNonUiThread();
|
||||
|
||||
final String packageName = intent.getData().getSchemeSpecificPart();
|
||||
if (packageName == null || packageName.length() == 0) {
|
||||
// they sent us a bad intent
|
||||
return;
|
||||
}
|
||||
|
||||
final String action = intent.getAction();
|
||||
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)) {
|
||||
Message.obtain(mWorkerHandler, MSG_PACKAGE_ADDED, packageName).sendToTarget();
|
||||
updateActionsWithRetry(0, packageName);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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_RECENTS_SCROLLING_FINISHED;
|
||||
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.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
|
||||
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.annotation.TargetApi;
|
||||
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.statemanager.StatefulActivity;
|
||||
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.VibratorWrapper;
|
||||
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.MotionPauseDetector;
|
||||
import com.android.quickstep.util.RecentsOrientedState;
|
||||
import com.android.quickstep.util.ProtoTracer;
|
||||
import com.android.quickstep.util.RectFSpringAnim;
|
||||
import com.android.quickstep.util.SurfaceTransactionApplier;
|
||||
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 java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
|
@ -614,13 +621,6 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<?>, Q extends
|
|||
|
||||
updateSysUiFlags(mCurrentShift.value);
|
||||
applyWindowTransform();
|
||||
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
|
||||
if (mRecentsAnimationTargets != null) {
|
||||
LiveTileOverlay.INSTANCE.update(
|
||||
mTaskViewSimulator.getCurrentRect(),
|
||||
mTaskViewSimulator.getCurrentCornerRadius());
|
||||
}
|
||||
}
|
||||
|
||||
updateLauncherTransitionProgress();
|
||||
}
|
||||
|
@ -719,6 +719,8 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<?>, Q extends
|
|||
|
||||
@UiThread
|
||||
public void onGestureStarted(boolean isLikelyToStartNewTask) {
|
||||
InteractionJankMonitorWrapper.begin(
|
||||
InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH, 2000 /* ms timeout */);
|
||||
notifyGestureStartedAsync();
|
||||
setIsLikelyToStartNewTask(isLikelyToStartNewTask, false /* animate */);
|
||||
mStateCallback.setStateOnUiThread(STATE_GESTURE_STARTED);
|
||||
|
@ -794,7 +796,8 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<?>, Q extends
|
|||
}
|
||||
|
||||
private void onSettledOnEndTarget() {
|
||||
switch (mGestureState.getEndTarget()) {
|
||||
final GestureEndTarget endTarget = mGestureState.getEndTarget();
|
||||
switch (endTarget) {
|
||||
case HOME:
|
||||
mStateCallback.setState(STATE_SCALED_CONTROLLER_HOME | STATE_CAPTURE_SCREENSHOT);
|
||||
// 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);
|
||||
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. */
|
||||
|
@ -1388,7 +1394,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<?>, Q extends
|
|||
if (taskView != null && !mCanceled) {
|
||||
// Defer finishing the animation until the next launcher frame with the
|
||||
// new thumbnail
|
||||
finishTransitionPosted = ViewUtils.postDraw(taskView,
|
||||
finishTransitionPosted = ViewUtils.postFrameDrawn(taskView,
|
||||
() -> mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED),
|
||||
this::isCanceled);
|
||||
}
|
||||
|
@ -1450,17 +1456,64 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<?>, Q extends
|
|||
private void setupLauncherUiAfterSwipeUpToRecentsAnimation() {
|
||||
endLauncherTransitionController();
|
||||
mActivityInterface.onSwipeUpToRecentsComplete();
|
||||
if (mRecentsAnimationController != null) {
|
||||
mRecentsAnimationController.setDeferCancelUntilNextTransition(true /* defer */,
|
||||
true /* screenshot */);
|
||||
}
|
||||
mRecentsView.onSwipeUpAnimationSuccess();
|
||||
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
|
||||
mTaskAnimationManager.setLaunchOtherTaskInLiveTileModeHandler(
|
||||
this::launchOtherTaskInLiveTileMode);
|
||||
}
|
||||
|
||||
SystemUiProxy.INSTANCE.get(mContext).onOverviewShown(false, TAG);
|
||||
doLogGesture(RECENTS, mRecentsView.getCurrentPageTaskView());
|
||||
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() {
|
||||
if (LiveTileOverlay.INSTANCE.attach(mActivity.getRootView().getOverlay())) {
|
||||
mRecentsView.setLiveTileOverlayAttached(true);
|
||||
|
@ -1523,39 +1576,33 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<?>, Q extends
|
|||
|
||||
protected void startNewTask(Consumer<Boolean> resultCallback) {
|
||||
// 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) {
|
||||
TaskView nextTask = mRecentsView.getNextPageTaskView();
|
||||
if (nextTask != null) {
|
||||
int taskId = nextTask.getTask().key.id;
|
||||
mGestureState.updateLastStartedTaskId(taskId);
|
||||
boolean hasTaskPreviouslyAppeared = mGestureState.getPreviouslyAppearedTaskIds()
|
||||
.contains(taskId);
|
||||
nextTask.launchTask(false /* animate */, true /* freezeTaskList */,
|
||||
success -> {
|
||||
resultCallback.accept(success);
|
||||
if (success) {
|
||||
if (hasTaskPreviouslyAppeared) {
|
||||
onRestartPreviouslyAppearedTask();
|
||||
}
|
||||
} else {
|
||||
mActivityInterface.onLaunchTaskFailed();
|
||||
nextTask.notifyTaskLaunchFailed(TAG);
|
||||
mRecentsAnimationController.finish(true /* toRecents */, null);
|
||||
if (!mCanceled) {
|
||||
TaskView nextTask = mRecentsView.getNextPageTaskView();
|
||||
if (nextTask != null) {
|
||||
int taskId = nextTask.getTask().key.id;
|
||||
mGestureState.updateLastStartedTaskId(taskId);
|
||||
boolean hasTaskPreviouslyAppeared = mGestureState.getPreviouslyAppearedTaskIds()
|
||||
.contains(taskId);
|
||||
nextTask.launchTask(false /* animate */, true /* freezeTaskList */,
|
||||
success -> {
|
||||
resultCallback.accept(success);
|
||||
if (success) {
|
||||
if (hasTaskPreviouslyAppeared) {
|
||||
onRestartPreviouslyAppearedTask();
|
||||
}
|
||||
}, MAIN_EXECUTOR.getHandler());
|
||||
} else {
|
||||
mActivityInterface.onLaunchTaskFailed();
|
||||
Toast.makeText(mContext, R.string.activity_not_available, LENGTH_SHORT).show();
|
||||
mRecentsAnimationController.finish(true /* toRecents */, null);
|
||||
}
|
||||
} else {
|
||||
mActivityInterface.onLaunchTaskFailed();
|
||||
nextTask.notifyTaskLaunchFailed(TAG);
|
||||
mRecentsAnimationController.finish(true /* toRecents */, null);
|
||||
}
|
||||
}, MAIN_EXECUTOR.getHandler());
|
||||
} else {
|
||||
mActivityInterface.onLaunchTaskFailed();
|
||||
Toast.makeText(mContext, R.string.activity_not_available, LENGTH_SHORT).show();
|
||||
mRecentsAnimationController.finish(true /* toRecents */, null);
|
||||
}
|
||||
mCanceled = false;
|
||||
}
|
||||
mCanceled = false;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1641,6 +1688,32 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<?>, Q extends
|
|||
}
|
||||
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 {
|
||||
|
|
|
@ -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.
|
||||
*/
|
||||
default void setRecentsAttachedToAppWindow(boolean attached, boolean animate) { }
|
||||
|
||||
default boolean isRecentsAttachedToAppWindow() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isRecentsAttachedToAppWindow() {
|
||||
return mIsAttachedToWindow;
|
||||
}
|
||||
|
||||
protected void createBackgroundToOverviewAnim(ACTIVITY_TYPE activity, PendingAnimation pa) {
|
||||
// Scale down recents from being full screen to being in overview.
|
||||
RecentsView recentsView = activity.getOverviewPanel();
|
||||
|
|
|
@ -26,6 +26,8 @@ import android.content.Intent;
|
|||
import android.os.Build;
|
||||
|
||||
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.systemui.shared.recents.model.ThumbnailData;
|
||||
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.
|
||||
*/
|
||||
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,
|
||||
boolean recentsAttachedToAppWindow) {
|
||||
GestureEndTarget(boolean isLauncher, int containerType, boolean recentsAttachedToAppWindow,
|
||||
GestureStateProto.GestureEndTarget protoEndTarget) {
|
||||
this.isLauncher = isLauncher;
|
||||
this.containerType = containerType;
|
||||
this.recentsAttachedToAppWindow = recentsAttachedToAppWindow;
|
||||
this.protoEndTarget = protoEndTarget;
|
||||
}
|
||||
|
||||
/** 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;
|
||||
/** Whether RecentsView should be attached to the window as we animate to this target */
|
||||
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";
|
||||
|
@ -345,4 +352,17 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
|
|||
pw.println(" lastStartedTaskId=" + mLastStartedTaskId);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,9 @@ import android.view.InputEvent;
|
|||
import android.view.KeyEvent;
|
||||
import android.view.MotionEvent;
|
||||
|
||||
import com.android.launcher3.tracing.InputConsumerProto;
|
||||
import com.android.launcher3.tracing.TouchInteractionServiceProto;
|
||||
|
||||
@TargetApi(Build.VERSION_CODES.O)
|
||||
public interface InputConsumer {
|
||||
|
||||
|
@ -116,4 +119,21 @@ public interface InputConsumer {
|
|||
}
|
||||
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) {}
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ import com.android.quickstep.util.RemoteAnimationProvider;
|
|||
import com.android.quickstep.views.RecentsView;
|
||||
import com.android.quickstep.views.TaskView;
|
||||
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.RemoteAnimationTargetCompat;
|
||||
|
||||
|
@ -174,6 +175,9 @@ public class OverviewCommandHelper {
|
|||
return;
|
||||
}
|
||||
|
||||
InteractionJankMonitorWrapper.begin(
|
||||
InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH, 2000 /* ms timout */);
|
||||
|
||||
// Otherwise, start overview.
|
||||
mListener = mActivityInterface.createActivityInitListener(this::onActivityReady);
|
||||
mListener.registerAndStartActivity(mOverviewComponentObserver.getOverviewIntent(),
|
||||
|
|
|
@ -35,6 +35,8 @@ import android.content.pm.PackageManager;
|
|||
import android.content.pm.ResolveInfo;
|
||||
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.systemui.shared.system.PackageManagerWrapper;
|
||||
|
||||
|
@ -262,4 +264,17 @@ public final class OverviewComponentObserver {
|
|||
pw.println(" overviewIntent=" + mOverviewIntent);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,7 @@ import androidx.annotation.UiThread;
|
|||
|
||||
import com.android.launcher3.util.Preconditions;
|
||||
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.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
|
||||
* {@link RecentsAnimationCallbacks#onTaskAppeared(RemoteAnimationTargetCompat)}}.
|
||||
|
@ -151,6 +134,7 @@ public class RecentsAnimationController {
|
|||
mOnFinishedListener.accept(this);
|
||||
UI_HELPER_EXECUTOR.execute(() -> {
|
||||
mController.finish(toRecents, sendUserLeaveHint);
|
||||
InteractionJankMonitorWrapper.end(InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH);
|
||||
if (callback != null) {
|
||||
MAIN_EXECUTOR.execute(callback);
|
||||
}
|
||||
|
|
|
@ -29,6 +29,7 @@ import androidx.annotation.NonNull;
|
|||
import androidx.annotation.UiThread;
|
||||
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.anim.AnimationSuccessListener;
|
||||
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.
|
||||
protected float mDragLengthFactor = 1;
|
||||
|
||||
protected final float mMaxShadowRadius;
|
||||
|
||||
protected AnimatorControllerWithResistance mWindowTransitionController;
|
||||
|
||||
public SwipeUpAnimationLogic(Context context, RecentsAnimationDeviceState deviceState,
|
||||
|
@ -80,6 +83,9 @@ public abstract class SwipeUpAnimationLogic {
|
|||
mDeviceState.getRotationTouchHelper().getCurrentActiveRotation(),
|
||||
mDeviceState.getRotationTouchHelper().getDisplayRotation());
|
||||
mTaskViewSimulator.setDrawsBelowRecents(true);
|
||||
|
||||
mMaxShadowRadius = context.getResources().getDimensionPixelSize(R.dimen.max_shadow_radius);
|
||||
mTransformParams.setShadowRadius(mMaxShadowRadius);
|
||||
}
|
||||
|
||||
protected void initTransitionEndpoints(DeviceProfile dp) {
|
||||
|
@ -259,9 +265,11 @@ public abstract class SwipeUpAnimationLogic {
|
|||
|
||||
mMatrix.setRectToRect(mCropRectF, mWindowCurrentRect, ScaleToFit.FILL);
|
||||
float cornerRadius = Utilities.mapRange(progress, mStartRadius, mEndRadius);
|
||||
float shadowRadius = Utilities.mapRange(progress, mMaxShadowRadius, 0);
|
||||
mTransformParams
|
||||
.setTargetAlpha(getWindowAlpha(progress))
|
||||
.setCornerRadius(cornerRadius);
|
||||
.setCornerRadius(cornerRadius)
|
||||
.setShadowRadius(shadowRadius);
|
||||
|
||||
mTransformParams.applySurfaceParams(mTransformParams.createSurfaceParams(this));
|
||||
mAnimationFactory.update(currentRect, progress, mMatrix.mapRadius(cornerRadius));
|
||||
|
@ -272,7 +280,8 @@ public abstract class SwipeUpAnimationLogic {
|
|||
Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
|
||||
builder.withMatrix(mMatrix)
|
||||
.withWindowCrop(mCropRect)
|
||||
.withCornerRadius(params.getCornerRadius());
|
||||
.withCornerRadius(params.getCornerRadius())
|
||||
.withShadowRadius(params.getShadowRadius());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -17,6 +17,7 @@ package com.android.quickstep;
|
|||
|
||||
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
||||
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
|
||||
import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
|
||||
import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_INITIALIZED;
|
||||
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.RemoteAnimationTargetCompat;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAnimationListener {
|
||||
|
||||
private RecentsAnimationController mController;
|
||||
|
@ -39,6 +42,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
|
|||
// Temporary until we can hook into gesture state events
|
||||
private GestureState mLastGestureState;
|
||||
private RemoteAnimationTargetCompat mLastAppearedTaskTarget;
|
||||
private Consumer<RemoteAnimationTargetCompat> mLaunchOtherTaskHandler;
|
||||
|
||||
/**
|
||||
* Preloads the recents animation.
|
||||
|
@ -88,22 +92,21 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
|
|||
|
||||
@Override
|
||||
public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
|
||||
if (thumbnailData != null) {
|
||||
// If a screenshot is provided, switch to the screenshot before cleaning up
|
||||
activityInterface.switchRunningTaskViewToScreenshot(thumbnailData,
|
||||
() -> cleanUpRecentsAnimation(thumbnailData));
|
||||
} else {
|
||||
cleanUpRecentsAnimation(null /* canceledThumbnail */);
|
||||
}
|
||||
cleanUpRecentsAnimation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRecentsAnimationFinished(RecentsAnimationController controller) {
|
||||
cleanUpRecentsAnimation(null /* canceledThumbnail */);
|
||||
cleanUpRecentsAnimation();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
|
||||
if (mLaunchOtherTaskHandler != null
|
||||
&& mLastGestureState.getEndTarget() == RECENTS) {
|
||||
mLaunchOtherTaskHandler.accept(appearedTaskTarget);
|
||||
return;
|
||||
}
|
||||
if (mController != null) {
|
||||
if (mLastAppearedTaskTarget == null
|
||||
|| appearedTaskTarget.taskId != mLastAppearedTaskTarget.taskId) {
|
||||
|
@ -138,6 +141,15 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
|
|||
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.
|
||||
*/
|
||||
|
@ -147,7 +159,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
|
|||
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), toHome
|
||||
? mController::finishAnimationToHome
|
||||
: mController::finishAnimationToApp);
|
||||
cleanUpRecentsAnimation(null /* canceledThumbnail */);
|
||||
cleanUpRecentsAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -174,12 +186,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
|
|||
/**
|
||||
* Cleans up the recents animation entirely.
|
||||
*/
|
||||
private void cleanUpRecentsAnimation(ThumbnailData canceledThumbnail) {
|
||||
// Clean up the screenshot if necessary
|
||||
if (mController != null && canceledThumbnail != null) {
|
||||
mController.cleanupScreenshot();
|
||||
}
|
||||
|
||||
private void cleanUpRecentsAnimation() {
|
||||
// Release all the target leashes
|
||||
if (mTargets != null) {
|
||||
mTargets.release();
|
||||
|
@ -195,6 +202,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
|
|||
mTargets = null;
|
||||
mLastGestureState = null;
|
||||
mLastAppearedTaskTarget = null;
|
||||
mLaunchOtherTaskHandler = null;
|
||||
}
|
||||
|
||||
public void dump() {
|
||||
|
|
|
@ -21,12 +21,14 @@ import static com.android.launcher3.uioverrides.QuickstepLauncher.GO_LOW_RAM_REC
|
|||
import android.app.ActivityManager.TaskDescription;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.os.UserHandle;
|
||||
import android.text.TextUtils;
|
||||
import android.util.SparseArray;
|
||||
import android.view.accessibility.AccessibilityManager;
|
||||
|
||||
|
@ -34,6 +36,7 @@ import androidx.annotation.WorkerThread;
|
|||
|
||||
import com.android.launcher3.FastBitmapDrawable;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.icons.BitmapInfo;
|
||||
import com.android.launcher3.icons.IconProvider;
|
||||
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.systemui.shared.recents.model.Task;
|
||||
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.TaskDescriptionCompat;
|
||||
|
||||
|
@ -163,9 +165,8 @@ public class TaskIconCache {
|
|||
key.getComponent(), key.userId);
|
||||
}
|
||||
if (activityInfo != null) {
|
||||
entry.contentDescription = ActivityManagerWrapper.getInstance()
|
||||
.getBadgedContentDescription(activityInfo, task.key.userId,
|
||||
task.taskDescription);
|
||||
entry.contentDescription = getBadgedContentDescription(
|
||||
activityInfo, task.key.userId, task.taskDescription);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -173,6 +174,21 @@ public class TaskIconCache {
|
|||
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
|
||||
private Drawable getDefaultIcon(int userId) {
|
||||
synchronized (mDefaultIcons) {
|
||||
|
|
|
@ -17,15 +17,20 @@ package com.android.quickstep;
|
|||
|
||||
import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
|
||||
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.anim.Interpolators.LINEAR;
|
||||
import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
|
||||
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.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
|
||||
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.annotation.TargetApi;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
|
@ -35,12 +40,18 @@ import android.graphics.RectF;
|
|||
import android.os.Build;
|
||||
import android.view.View;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.BaseActivity;
|
||||
import com.android.launcher3.BaseDraggingActivity;
|
||||
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.model.data.ItemInfo;
|
||||
import com.android.launcher3.statehandlers.DepthController;
|
||||
import com.android.launcher3.statemanager.StateManager;
|
||||
import com.android.launcher3.util.DisplayController;
|
||||
import com.android.quickstep.util.SurfaceTransactionApplier;
|
||||
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.TaskView;
|
||||
import com.android.systemui.shared.recents.model.Task;
|
||||
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
|
||||
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.
|
||||
*/
|
||||
public static TaskView findTaskViewToLaunch(
|
||||
BaseDraggingActivity activity, View v, RemoteAnimationTargetCompat[] targets) {
|
||||
RecentsView recentsView = activity.getOverviewPanel();
|
||||
RecentsView recentsView, View v, RemoteAnimationTargetCompat[] targets) {
|
||||
if (v instanceof TaskView) {
|
||||
TaskView taskView = (TaskView) v;
|
||||
return recentsView.isTaskViewVisible(taskView) ? taskView : null;
|
||||
|
@ -119,6 +130,21 @@ public final class TaskViewUtils {
|
|||
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
|
||||
* animation.
|
||||
|
@ -126,16 +152,25 @@ public final class TaskViewUtils {
|
|||
public static void createRecentsWindowAnimator(TaskView v, boolean skipViewChanges,
|
||||
RemoteAnimationTargetCompat[] appTargets,
|
||||
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 =
|
||||
new RemoteAnimationTargets(appTargets, wallpaperTargets, MODE_OPENING);
|
||||
targets.addReleaseCheck(applier);
|
||||
new RemoteAnimationTargets(appTargets, wallpaperTargets,
|
||||
inLiveTileMode ? MODE_CLOSING : MODE_OPENING);
|
||||
|
||||
TransformParams params = new TransformParams()
|
||||
if (params == null) {
|
||||
SurfaceTransactionApplier applier = new SurfaceTransactionApplier(v);
|
||||
targets.addReleaseCheck(applier);
|
||||
|
||||
params = new TransformParams()
|
||||
.setSyncTransactionApplier(applier)
|
||||
.setTargetSet(targets);
|
||||
}
|
||||
|
||||
final RecentsView recentsView = v.getRecentsView();
|
||||
int taskIndex = recentsView.indexOfChild(v);
|
||||
|
@ -149,8 +184,9 @@ public final class TaskViewUtils {
|
|||
int displayRotation = DisplayController.getDefaultDisplay(context).getInfo().rotation;
|
||||
|
||||
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.setLayoutRotation(displayRotation, displayRotation);
|
||||
tsv.setPreview(targets.apps[targets.apps.length - 1]);
|
||||
|
@ -158,19 +194,24 @@ public final class TaskViewUtils {
|
|||
tsv.recentsViewScale.value = 1;
|
||||
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,
|
||||
AnimatedFloat.VALUE, 1, TOUCH_RESPONSE_INTERPOLATOR);
|
||||
out.setFloat(tsv.recentsViewScale,
|
||||
AnimatedFloat.VALUE, tsv.getFullScreenScale(), 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;
|
||||
}
|
||||
|
||||
// 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) {
|
||||
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
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
targets.release();
|
||||
super.onAnimationEnd(animation);
|
||||
}
|
||||
});
|
||||
|
||||
|
@ -235,4 +285,57 @@ public final class TaskViewUtils {
|
|||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -737,8 +737,11 @@ public class TouchInteractionService extends Service implements PluginListener<O
|
|||
private void reset() {
|
||||
mConsumer = mUncheckedConsumer = mResetGestureInputConsumer;
|
||||
mGestureState = DEFAULT_STATE;
|
||||
// By default, use batching of the input events
|
||||
mInputEventReceiver.setBatchingEnabled(true);
|
||||
// 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);
|
||||
}
|
||||
}
|
||||
|
||||
private void preloadOverview(boolean fromInit) {
|
||||
|
@ -895,6 +898,12 @@ public class TouchInteractionService extends Service implements PluginListener<O
|
|||
TouchInteractionServiceProto.Builder serviceProto =
|
||||
TouchInteractionServiceProto.newBuilder();
|
||||
serviceProto.setServiceConnected(true);
|
||||
|
||||
if (mOverviewComponentObserver != null) {
|
||||
mOverviewComponentObserver.writeToProto(serviceProto);
|
||||
}
|
||||
mConsumer.writeToProto(serviceProto);
|
||||
|
||||
proto.setTouchInteractionService(serviceProto);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,21 +15,23 @@
|
|||
*/
|
||||
package com.android.quickstep;
|
||||
|
||||
import android.graphics.Canvas;
|
||||
import android.os.Handler;
|
||||
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.LongConsumer;
|
||||
|
||||
/**
|
||||
* Utility class for helpful methods related to {@link View} objects.
|
||||
*/
|
||||
public class ViewUtils {
|
||||
|
||||
/** See {@link #postDraw(View, Runnable, BooleanSupplier)}} */
|
||||
public static boolean postDraw(View view, Runnable onFinishRunnable) {
|
||||
return postDraw(view, onFinishRunnable, () -> false);
|
||||
/** See {@link #postFrameDrawn(View, Runnable, BooleanSupplier)}} */
|
||||
public static boolean postFrameDrawn(View view, Runnable onFinishRunnable) {
|
||||
return postFrameDrawn(view, onFinishRunnable, () -> false);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -38,37 +40,55 @@ public class ViewUtils {
|
|||
*
|
||||
* @param onFinishRunnable runnable to be run right after the view finishes drawing.
|
||||
*/
|
||||
public static boolean postDraw(View view, Runnable onFinishRunnable, BooleanSupplier canceled) {
|
||||
// Defer finishing the animation until the next launcher frame with the
|
||||
// new thumbnail
|
||||
return new WindowCallbacksCompat(view) {
|
||||
// The number of frames to defer until we actually finish the animation
|
||||
private int mDeferFrameCount = 2;
|
||||
public static boolean postFrameDrawn(
|
||||
View view, Runnable onFinishRunnable, BooleanSupplier canceled) {
|
||||
return new FrameHandler(view, onFinishRunnable, canceled).schedule();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPostDraw(Canvas canvas) {
|
||||
// If we were cancelled after this was attached, do not update
|
||||
// the state.
|
||||
if (canceled.getAsBoolean()) {
|
||||
detach();
|
||||
return;
|
||||
}
|
||||
private static class FrameHandler implements LongConsumer {
|
||||
|
||||
if (mDeferFrameCount > 0) {
|
||||
mDeferFrameCount--;
|
||||
// Workaround, detach and reattach to invalidate the root node for
|
||||
// another draw
|
||||
detach();
|
||||
attach();
|
||||
view.invalidate();
|
||||
return;
|
||||
}
|
||||
final ViewRootImplCompat mViewRoot;
|
||||
final Runnable mFinishCallback;
|
||||
final BooleanSupplier mCancelled;
|
||||
final Handler mHandler;
|
||||
|
||||
if (onFinishRunnable != null) {
|
||||
onFinishRunnable.run();
|
||||
}
|
||||
detach();
|
||||
int mDeferFrameCount = 1;
|
||||
|
||||
FrameHandler(View view, Runnable finishCallback, BooleanSupplier cancelled) {
|
||||
mViewRoot = new ViewRootImplCompat(view);
|
||||
mFinishCallback = finishCallback;
|
||||
mCancelled = cancelled;
|
||||
mHandler = new Handler();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(long l) {
|
||||
Utilities.postAsyncCallback(mHandler, this::onFrame);
|
||||
}
|
||||
|
||||
private void onFrame() {
|
||||
if (mCancelled.getAsBoolean()) {
|
||||
return;
|
||||
}
|
||||
}.attach();
|
||||
|
||||
if (mDeferFrameCount > 0) {
|
||||
mDeferFrameCount--;
|
||||
schedule();
|
||||
return;
|
||||
}
|
||||
|
||||
if (mFinishCallback != null) {
|
||||
mFinishCallback.run();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean schedule() {
|
||||
if (mViewRoot.isValid()) {
|
||||
mViewRoot.registerRtFrameCallback(this);
|
||||
mViewRoot.getView().invalidate();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@ import android.view.MotionEvent;
|
|||
|
||||
import com.android.launcher3.testing.TestLogging;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
import com.android.launcher3.tracing.InputConsumerProto;
|
||||
import com.android.quickstep.InputConsumer;
|
||||
import com.android.systemui.shared.system.InputMonitorCompat;
|
||||
|
||||
|
@ -53,4 +54,9 @@ public abstract class DelegateInputConsumer implements InputConsumer {
|
|||
mDelegate.onMotionEvent(event);
|
||||
event.recycle();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToProtoInternal(InputConsumerProto.Builder inputConsumerProto) {
|
||||
mDelegate.writeToProtoInternal(inputConsumerProto);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -51,6 +51,7 @@ import androidx.annotation.UiThread;
|
|||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.testing.TestLogging;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
import com.android.launcher3.tracing.InputConsumerProto;
|
||||
import com.android.launcher3.util.Preconditions;
|
||||
import com.android.launcher3.util.TraceHelper;
|
||||
import com.android.quickstep.AbsSwipeUpHandler;
|
||||
|
@ -478,4 +479,11 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
|
|||
public boolean allowInterceptByParent() {
|
||||
return !mPassedPilferInputSlop || mGestureState.hasState(STATE_OVERSCROLL_WINDOW_CREATED);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToProtoInternal(InputConsumerProto.Builder inputConsumerProto) {
|
||||
if (mInteractionHandler != null) {
|
||||
mInteractionHandler.writeToProto(inputConsumerProto);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -104,7 +104,13 @@ final class AssistantGestureTutorialController extends TutorialController {
|
|||
hideFeedback();
|
||||
hideHandCoachingAnimation();
|
||||
showRippleEffect(
|
||||
() -> mTutorialFragment.changeController(ASSISTANT_COMPLETE));
|
||||
() -> {
|
||||
if (mTutorialFragment.isTutorialComplete()) {
|
||||
mTutorialFragment.changeController(ASSISTANT_COMPLETE);
|
||||
} else {
|
||||
mTutorialFragment.continueTutorial();
|
||||
}
|
||||
});
|
||||
break;
|
||||
case ASSISTANT_NOT_STARTED_BAD_ANGLE:
|
||||
showFeedback(R.string.assistant_gesture_feedback_swipe_not_diagonal);
|
||||
|
|
|
@ -130,7 +130,13 @@ final class BackGestureTutorialController extends TutorialController {
|
|||
hideFeedback();
|
||||
hideHandCoachingAnimation();
|
||||
showRippleEffect(
|
||||
() -> mTutorialFragment.changeController(BACK_NAVIGATION_COMPLETE));
|
||||
() -> {
|
||||
if (mTutorialFragment.isTutorialComplete()) {
|
||||
mTutorialFragment.changeController(BACK_NAVIGATION_COMPLETE);
|
||||
} else {
|
||||
mTutorialFragment.continueTutorial();
|
||||
}
|
||||
});
|
||||
break;
|
||||
case BACK_CANCELLED_FROM_LEFT:
|
||||
showFeedback(R.string.back_gesture_feedback_cancelled_left_edge);
|
||||
|
|
|
@ -15,8 +15,6 @@
|
|||
*/
|
||||
package com.android.quickstep.interaction;
|
||||
|
||||
import static com.android.quickstep.interaction.TutorialFragment.KEY_TUTORIAL_TYPE;
|
||||
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Rect;
|
||||
import android.os.Bundle;
|
||||
|
@ -25,11 +23,14 @@ import android.view.Display;
|
|||
import android.view.View;
|
||||
import android.view.Window;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.fragment.app.FragmentActivity;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
import com.android.quickstep.interaction.TutorialController.TutorialType;
|
||||
|
||||
import java.util.ArrayDeque;
|
||||
import java.util.Deque;
|
||||
import java.util.List;
|
||||
|
||||
/** 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 KEY_TUTORIAL_STEPS = "tutorial_steps";
|
||||
|
||||
private Deque<TutorialType> mTutorialSteps;
|
||||
private TutorialFragment mFragment;
|
||||
|
||||
@Override
|
||||
|
@ -45,7 +49,9 @@ public class GestureSandboxActivity extends FragmentActivity {
|
|||
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
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()
|
||||
.add(R.id.gesture_tutorial_fragment_container, mFragment)
|
||||
.commit();
|
||||
|
@ -72,17 +78,65 @@ public class GestureSandboxActivity extends FragmentActivity {
|
|||
}
|
||||
}
|
||||
|
||||
private TutorialType getTutorialType(Bundle extras) {
|
||||
TutorialType defaultType = TutorialType.RIGHT_EDGE_BACK_NAVIGATION;
|
||||
@Override
|
||||
protected void onSaveInstanceState(@NonNull Bundle savedInstanceState) {
|
||||
savedInstanceState.putStringArray(KEY_TUTORIAL_STEPS, getTutorialStepNames());
|
||||
super.onSaveInstanceState(savedInstanceState);
|
||||
}
|
||||
|
||||
if (extras == null || !extras.containsKey(KEY_TUTORIAL_TYPE)) {
|
||||
return defaultType;
|
||||
/** Returns true iff there aren't anymore tutorial types to display to the user. */
|
||||
public boolean isTutorialComplete() {
|
||||
return mTutorialSteps.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces the current TutorialFragment, continuing to the next tutorial step if there is one.
|
||||
*
|
||||
* If there is no following step, the tutorial is closed.
|
||||
*/
|
||||
public void continueTutorial() {
|
||||
if (isTutorialComplete()) {
|
||||
mFragment.closeTutorial();
|
||||
return;
|
||||
}
|
||||
try {
|
||||
return TutorialType.valueOf(extras.getString(KEY_TUTORIAL_TYPE, ""));
|
||||
} catch (IllegalArgumentException e) {
|
||||
return defaultType;
|
||||
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() {
|
||||
|
|
|
@ -94,8 +94,13 @@ final class HomeGestureTutorialController extends SwipeUpGestureTutorialControll
|
|||
case HOME_NAVIGATION:
|
||||
switch (result) {
|
||||
case HOME_GESTURE_COMPLETED: {
|
||||
animateFakeTaskViewHome(finalVelocity, () ->
|
||||
mTutorialFragment.changeController(HOME_NAVIGATION_COMPLETE));
|
||||
animateFakeTaskViewHome(finalVelocity, () -> {
|
||||
if (mTutorialFragment.isTutorialComplete()) {
|
||||
mTutorialFragment.changeController(HOME_NAVIGATION_COMPLETE);
|
||||
} else {
|
||||
mTutorialFragment.continueTutorial();
|
||||
}
|
||||
});
|
||||
break;
|
||||
}
|
||||
case HOME_NOT_STARTED_TOO_FAR_FROM_EDGE:
|
||||
|
|
|
@ -104,8 +104,13 @@ final class OverviewGestureTutorialController extends SwipeUpGestureTutorialCont
|
|||
showFeedback(R.string.overview_gesture_feedback_swipe_too_far_from_edge);
|
||||
break;
|
||||
case OVERVIEW_GESTURE_COMPLETED:
|
||||
fadeOutFakeTaskView(true, () ->
|
||||
mTutorialFragment.changeController(OVERVIEW_NAVIGATION_COMPLETE));
|
||||
fadeOutFakeTaskView(true, () -> {
|
||||
if (mTutorialFragment.isTutorialComplete()) {
|
||||
mTutorialFragment.changeController(OVERVIEW_NAVIGATION_COMPLETE);
|
||||
} else {
|
||||
mTutorialFragment.continueTutorial();
|
||||
}
|
||||
});
|
||||
break;
|
||||
case HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION:
|
||||
case HOME_OR_OVERVIEW_CANCELLED:
|
||||
|
|
|
@ -54,6 +54,7 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener {
|
|||
fragment = new BackGestureTutorialFragment();
|
||||
tutorialType = TutorialType.RIGHT_EDGE_BACK_NAVIGATION;
|
||||
}
|
||||
|
||||
Bundle args = new Bundle();
|
||||
args.putSerializable(KEY_TUTORIAL_TYPE, tutorialType);
|
||||
fragment.setArguments(args);
|
||||
|
@ -197,6 +198,20 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener {
|
|||
return mHandCoachingAnimation;
|
||||
}
|
||||
|
||||
void continueTutorial() {
|
||||
if (!(getContext() instanceof GestureSandboxActivity)) {
|
||||
closeTutorial();
|
||||
return;
|
||||
}
|
||||
GestureSandboxActivity gestureSandboxActivity = (GestureSandboxActivity) getContext();
|
||||
|
||||
if (gestureSandboxActivity == null) {
|
||||
closeTutorial();
|
||||
return;
|
||||
}
|
||||
gestureSandboxActivity.continueTutorial();
|
||||
}
|
||||
|
||||
void closeTutorial() {
|
||||
FragmentActivity activity = getActivity();
|
||||
if (activity != null) {
|
||||
|
@ -207,4 +222,13 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener {
|
|||
void startSystemNavigationSetting() {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@ import static android.view.Surface.ROTATION_180;
|
|||
import static android.view.Surface.ROTATION_270;
|
||||
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.util.Executors.UI_HELPER_EXECUTOR;
|
||||
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 String DELIMITER_DOT = "\\.";
|
||||
|
||||
private ContentObserver mSystemAutoRotateObserver = new ContentObserver(new Handler()) {
|
||||
@Override
|
||||
public void onChange(boolean selfChange) {
|
||||
updateAutoRotateSetting();
|
||||
}
|
||||
};
|
||||
private ContentObserver mSystemAutoRotateObserver =
|
||||
newContentObserver(new Handler(), t -> updateAutoRotateSetting());
|
||||
|
||||
@Retention(SOURCE)
|
||||
@IntDef({ROTATION_0, ROTATION_90, ROTATION_180, ROTATION_270})
|
||||
public @interface SurfaceRotation {}
|
||||
|
@ -541,6 +539,7 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre
|
|||
* @return "MyObject@1234"
|
||||
*/
|
||||
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) : "";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -322,7 +322,8 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
|
|||
Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
|
||||
builder.withMatrix(mMatrix)
|
||||
.withWindowCrop(mTmpCropRect)
|
||||
.withCornerRadius(getCurrentCornerRadius());
|
||||
.withCornerRadius(getCurrentCornerRadius())
|
||||
.withShadowRadius(params.getShadowRadius());
|
||||
|
||||
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && params.getRecentsSurface() != null) {
|
||||
// When relativeLayer = 0, it reverts the surfaces back to the original order.
|
||||
|
|
|
@ -57,6 +57,7 @@ public class TransformParams {
|
|||
private float mProgress;
|
||||
private float mTargetAlpha;
|
||||
private float mCornerRadius;
|
||||
private float mShadowRadius;
|
||||
private RemoteAnimationTargets mTargetSet;
|
||||
private SurfaceTransactionApplier mSyncTransactionApplier;
|
||||
private SurfaceControl mRecentsSurface;
|
||||
|
@ -68,6 +69,7 @@ public class TransformParams {
|
|||
mProgress = 0;
|
||||
mTargetAlpha = 1;
|
||||
mCornerRadius = -1;
|
||||
mShadowRadius = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -90,6 +92,14 @@ public class TransformParams {
|
|||
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.
|
||||
*/
|
||||
|
@ -197,6 +207,10 @@ public class TransformParams {
|
|||
return mCornerRadius;
|
||||
}
|
||||
|
||||
public float getShadowRadius() {
|
||||
return mShadowRadius;
|
||||
}
|
||||
|
||||
public SurfaceControl getRecentsSurface() {
|
||||
return mRecentsSurface;
|
||||
}
|
||||
|
|
|
@ -65,6 +65,10 @@ public class LiveTileOverlay extends Drawable {
|
|||
invalidateSelf();
|
||||
}
|
||||
|
||||
public void update(float left, float top, float right, float bottom) {
|
||||
mCurrentRect.set(left, top, right, bottom);
|
||||
}
|
||||
|
||||
public void setIcon(Drawable icon) {
|
||||
mIcon = icon;
|
||||
}
|
||||
|
@ -94,18 +98,16 @@ public class LiveTileOverlay extends Drawable {
|
|||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
if (mCurrentRect != null) {
|
||||
canvas.drawRoundRect(mCurrentRect, mCornerRadius, mCornerRadius, mPaint);
|
||||
if (mIcon != null && mIconAnimationProgress > 0f) {
|
||||
canvas.save();
|
||||
float scale = Interpolators.clampToProgress(FAST_OUT_SLOW_IN, 0f,
|
||||
1f).getInterpolation(mIconAnimationProgress);
|
||||
canvas.translate(mCurrentRect.centerX() - mIcon.getBounds().width() / 2 * scale,
|
||||
mCurrentRect.top - mIcon.getBounds().height() / 2 * scale);
|
||||
canvas.scale(scale, scale);
|
||||
mIcon.draw(canvas);
|
||||
canvas.restore();
|
||||
}
|
||||
canvas.drawRoundRect(mCurrentRect, mCornerRadius, mCornerRadius, mPaint);
|
||||
if (mIcon != null && mIconAnimationProgress > 0f) {
|
||||
canvas.save();
|
||||
float scale = Interpolators.clampToProgress(FAST_OUT_SLOW_IN, 0f,
|
||||
1f).getInterpolation(mIconAnimationProgress);
|
||||
canvas.translate(mCurrentRect.centerX() - mIcon.getBounds().width() / 2 * scale,
|
||||
mCurrentRect.top - mIcon.getBounds().height() / 2 * scale);
|
||||
canvas.scale(scale, scale);
|
||||
mIcon.draw(canvas);
|
||||
canvas.restore();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -230,6 +230,7 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
|
|||
view.setScaleX(scale);
|
||||
view.setScaleY(scale);
|
||||
view.mLastComputedTaskPushOutDistance = null;
|
||||
view.mLiveTileTaskViewSimulator.recentsViewScale.value = scale;
|
||||
view.updatePageOffsets();
|
||||
view.setTaskViewsSecondaryTranslation(view.mTaskViewsSecondaryTranslation);
|
||||
}
|
||||
|
@ -539,6 +540,9 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
|
|||
@Override
|
||||
protected void onWindowVisibilityChanged(int visibility) {
|
||||
super.onWindowVisibilityChanged(visibility);
|
||||
if (visibility == GONE && ENABLE_QUICKSTEP_LIVE_TILE.get()) {
|
||||
finishRecentsAnimation(true /* toRecents */, null);
|
||||
}
|
||||
updateTaskStackListenerState();
|
||||
}
|
||||
|
||||
|
@ -873,6 +877,10 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
|
|||
mLiveTileTaskViewSimulator.fullScreenProgress.value = 0;
|
||||
mLiveTileTaskViewSimulator.recentsViewScale.value = 1;
|
||||
mLiveTileTaskViewSimulator.setOffsetY(0);
|
||||
|
||||
// Reset the live tile rect
|
||||
DeviceProfile deviceProfile = mActivity.getDeviceProfile();
|
||||
LiveTileOverlay.INSTANCE.update(0, 0, deviceProfile.widthPx, deviceProfile.heightPx);
|
||||
}
|
||||
if (mRunningTaskTileHidden) {
|
||||
setRunningTaskHidden(mRunningTaskTileHidden);
|
||||
|
@ -1292,19 +1300,26 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
|
|||
}
|
||||
|
||||
public void showNextTask() {
|
||||
TaskView runningTaskView = getRunningTaskView();
|
||||
final TaskView runningTaskView = getRunningTaskView();
|
||||
final TaskView targetTask;
|
||||
|
||||
if (runningTaskView == null) {
|
||||
// Launch the first task
|
||||
if (getTaskViewCount() > 0) {
|
||||
getTaskViewAt(0).launchTask(true);
|
||||
targetTask = getTaskViewAt(0);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
if (getNextTaskView() != null) {
|
||||
getNextTaskView().launchTask(true);
|
||||
final TaskView nextTask = getNextTaskView();
|
||||
if (nextTask != null) {
|
||||
targetTask = nextTask;
|
||||
} else {
|
||||
runningTaskView.launchTask(true);
|
||||
targetTask = runningTaskView;
|
||||
}
|
||||
}
|
||||
targetTask.setEndQuickswitchCuj(true);
|
||||
targetTask.launchTask(true);
|
||||
}
|
||||
|
||||
public void setRunningTaskIconScaledDown(boolean isScaledDown) {
|
||||
|
@ -1976,6 +1991,7 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
|
|||
TaskView task = getTaskViewAt(i);
|
||||
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;
|
||||
}
|
||||
|
||||
public TransformParams getLiveTileParams() {
|
||||
return mLiveTileParams;
|
||||
}
|
||||
|
||||
// TODO: To be removed in a follow up CL
|
||||
public void setRecentsAnimationTargets(RecentsAnimationController recentsAnimationController,
|
||||
RecentsAnimationTargets recentsAnimationTargets) {
|
||||
|
@ -2442,7 +2462,7 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
|
|||
} else {
|
||||
taskView.getThumbnail().refresh();
|
||||
}
|
||||
ViewUtils.postDraw(taskView, onFinishRunnable);
|
||||
ViewUtils.postFrameDrawn(taskView, onFinishRunnable);
|
||||
} else {
|
||||
onFinishRunnable.run();
|
||||
}
|
||||
|
|
|
@ -39,6 +39,7 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH
|
|||
|
||||
import android.animation.Animator;
|
||||
import android.animation.AnimatorListenerAdapter;
|
||||
import android.animation.AnimatorSet;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.TimeInterpolator;
|
||||
import android.animation.ValueAnimator;
|
||||
|
@ -65,7 +66,6 @@ import android.view.accessibility.AccessibilityNodeInfo;
|
|||
import android.widget.FrameLayout;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.android.launcher3.BaseDraggingActivity;
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.LauncherSettings;
|
||||
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.model.data.WorkspaceItemInfo;
|
||||
import com.android.launcher3.popup.SystemShortcut;
|
||||
import com.android.launcher3.statemanager.StatefulActivity;
|
||||
import com.android.launcher3.testing.TestLogging;
|
||||
import com.android.launcher3.testing.TestProtocol;
|
||||
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.ViewPool.Reusable;
|
||||
import com.android.quickstep.RecentsModel;
|
||||
import com.android.quickstep.RemoteAnimationTargets;
|
||||
import com.android.quickstep.TaskIconCache;
|
||||
import com.android.quickstep.TaskOverlayFactory;
|
||||
import com.android.quickstep.TaskThumbnailCache;
|
||||
import com.android.quickstep.TaskUtils;
|
||||
import com.android.quickstep.TaskViewUtils;
|
||||
import com.android.quickstep.util.CancellableTask;
|
||||
import com.android.quickstep.util.RecentsOrientedState;
|
||||
import com.android.quickstep.util.TaskCornerRadius;
|
||||
|
@ -175,7 +178,7 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
|
|||
private float mCurveScale;
|
||||
private float mFullscreenProgress;
|
||||
private final FullscreenDrawParams mCurrentFullscreenParams;
|
||||
private final BaseDraggingActivity mActivity;
|
||||
private final StatefulActivity mActivity;
|
||||
|
||||
private ObjectAnimator mIconAndDimAnimator;
|
||||
private float mIconScaleAnimStartProgress = 0;
|
||||
|
@ -189,6 +192,8 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
|
|||
private CancellableTask mThumbnailLoadRequest;
|
||||
private CancellableTask mIconLoadRequest;
|
||||
|
||||
private boolean mEndQuickswitchCuj;
|
||||
|
||||
// Order in which the footers appear. Lower order appear below higher order.
|
||||
public static final int INDEX_DIGITAL_WELLBEING_TOAST = 0;
|
||||
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) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
mActivity = BaseDraggingActivity.fromContext(context);
|
||||
mActivity = StatefulActivity.fromContext(context);
|
||||
setOnClickListener((view) -> {
|
||||
if (getTask() == null) {
|
||||
return;
|
||||
}
|
||||
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
|
||||
if (isRunningTask()) {
|
||||
// TODO: Replace this animation with createRecentsWindowAnimator
|
||||
createLaunchAnimationForRunningTask().start();
|
||||
} else {
|
||||
launchTask(true /* animate */);
|
||||
}
|
||||
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && isRunningTask()) {
|
||||
RecentsView recentsView = getRecentsView();
|
||||
RemoteAnimationTargets targets = recentsView.getLiveTileParams().getTargetSet();
|
||||
recentsView.getLiveTileTaskViewSimulator().setDrawsBelowRecents(false);
|
||||
|
||||
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 {
|
||||
launchTask(true /* animate */);
|
||||
}
|
||||
|
@ -801,6 +819,14 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
|
|||
return false;
|
||||
}
|
||||
|
||||
public boolean isEndQuickswitchCuj() {
|
||||
return mEndQuickswitchCuj;
|
||||
}
|
||||
|
||||
public void setEndQuickswitchCuj(boolean endQuickswitchCuj) {
|
||||
mEndQuickswitchCuj = endQuickswitchCuj;
|
||||
}
|
||||
|
||||
private static final class TaskOutlineProvider extends ViewOutlineProvider {
|
||||
|
||||
private final int mMarginTop;
|
||||
|
|
|
@ -19,6 +19,6 @@
|
|||
android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0">
|
||||
<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" />
|
||||
</vector>
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2016 The Android Open Source Project
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2016 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.
|
||||
|
@ -16,8 +15,7 @@
|
|||
<!-- 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
|
||||
will bake the left/right padding into that view's background itself. -->
|
||||
<com.android.launcher3.allapps.LauncherAllAppsContainerView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<com.android.launcher3.allapps.LauncherAllAppsContainerView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/apps_view"
|
||||
android:theme="?attr/allAppsTheme"
|
||||
android:layout_width="match_parent"
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2015 The Android Open Source Project
|
||||
<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2015 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.
|
||||
|
@ -13,16 +12,10 @@
|
|||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<com.android.launcher3.BubbleTextView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<com.android.launcher3.BubbleTextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:launcher="http://schemas.android.com/apk/res-auto"
|
||||
style="@style/BaseIcon"
|
||||
style="@style/BaseIcon.AllApps"
|
||||
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:centerVertically="true"
|
||||
android:paddingLeft="@dimen/dynamic_grid_cell_padding_x"
|
||||
android:paddingRight="@dimen/dynamic_grid_cell_padding_x" />
|
||||
launcher:centerVertically="true" />
|
||||
|
||||
|
|
|
@ -26,7 +26,6 @@
|
|||
android:clipChildren="true"
|
||||
android:clipToPadding="false"
|
||||
android:descendantFocusability="afterDescendants"
|
||||
android:paddingTop="@dimen/all_apps_header_top_padding"
|
||||
launcher:pageIndicator="@+id/tabs" >
|
||||
|
||||
<include layout="@layout/all_apps_rv_layout" />
|
||||
|
|
|
@ -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" />
|
||||
|
|
@ -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");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
|
|
@ -67,7 +67,6 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start|center_vertical"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:text="@string/search_action_try_now">
|
||||
</Button>
|
||||
|
||||
|
|
|
@ -12,27 +12,24 @@
|
|||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<com.android.launcher3.views.SearchResultSuggestRow xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
style="@style/TextHeadline"
|
||||
android:id="@+id/section_title"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
<com.android.launcher3.views.SearchResultIconRow xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:launcher="http://schemas.android.com/apk/res-auto"
|
||||
style="@style/BaseIcon"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center_vertical"
|
||||
android:padding="4dp"
|
||||
android:minHeight="48dp"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="start|center_vertical"
|
||||
android:textAlignment="viewStart"
|
||||
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
|
||||
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>
|
||||
</com.android.launcher3.views.SearchResultIconRow>
|
|
@ -17,7 +17,9 @@
|
|||
android:id="@+id/section_title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:fontFamily="@style/TextHeadline"
|
||||
android:padding="4dp"
|
||||
style="@style/TextHeadline"
|
||||
android:paddingStart="4dp"
|
||||
android:paddingBottom="2dp"
|
||||
android:paddingTop="12dp"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:textSize="14sp" />
|
||||
android:textSize="18sp" />
|
|
@ -26,7 +26,7 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_setting"
|
||||
android:tint="@android:color/black"
|
||||
android:forceDarkAllowed="true"
|
||||
android:padding="12dp"
|
||||
android:background="?android:attr/selectableItemBackgroundBorderless" />
|
||||
|
||||
|
|
|
@ -69,6 +69,13 @@
|
|||
<attr name="folderDotColor" />
|
||||
</declare-styleable>
|
||||
|
||||
<declare-styleable name="SearchResultIconRow">
|
||||
<attr name="customIcon" format="reference" />
|
||||
<attr name="matchTextInsetWithQuery" format="boolean" />
|
||||
</declare-styleable>
|
||||
|
||||
|
||||
|
||||
<declare-styleable name="ShadowInfo">
|
||||
<attr name="ambientShadowColor" format="color" />
|
||||
<attr name="ambientShadowBlur" format="dimension" />
|
||||
|
|
|
@ -59,7 +59,7 @@
|
|||
<!-- Search bar text in the apps view. [CHAR_LIMIT=50] -->
|
||||
<string name="all_apps_search_bar_hint">Search apps</string>
|
||||
<!-- 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] -->
|
||||
<string name="all_apps_loading_message">Loading apps…</string>
|
||||
<!-- No-search-results text. [CHAR_LIMIT=50] -->
|
||||
|
|
|
@ -223,6 +223,16 @@
|
|||
<item name="android:lines">1</item>
|
||||
</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 -->
|
||||
<style name="BaseIcon.Workspace" >
|
||||
<item name="android:shadowRadius">2.0</item>
|
||||
|
|
|
@ -17,6 +17,7 @@
|
|||
package com.android.launcher3;
|
||||
|
||||
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.icons.GraphicsUtils.setColorAlphaBound;
|
||||
|
||||
|
@ -27,15 +28,18 @@ import android.animation.ValueAnimator;
|
|||
import android.content.Context;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.BlurMaskFilter;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.Path;
|
||||
import android.graphics.PointF;
|
||||
import android.graphics.PorterDuff.Mode;
|
||||
import android.graphics.PorterDuffColorFilter;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Process;
|
||||
import android.text.TextUtils.TruncateAt;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Property;
|
||||
|
@ -50,6 +54,8 @@ import androidx.core.graphics.ColorUtils;
|
|||
|
||||
import com.android.launcher3.Launcher.OnResumeCallback;
|
||||
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.dragndrop.DraggableView;
|
||||
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.IconCache.IconLoadRequest;
|
||||
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.ItemInfo;
|
||||
import com.android.launcher3.model.data.ItemInfoWithIcon;
|
||||
|
@ -79,7 +86,7 @@ import java.text.NumberFormat;
|
|||
* too aggressive.
|
||||
*/
|
||||
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_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[] 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 mTranslationForReorderPreview = new PointF(0, 0);
|
||||
|
@ -95,6 +104,11 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
|||
|
||||
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
|
||||
= new Property<BubbleTextView, Float>(Float.TYPE, "dotScale") {
|
||||
@Override
|
||||
|
@ -208,6 +222,11 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
|||
setEllipsize(TruncateAt.END);
|
||||
setAccessibilityDelegate(mActivity.getAccessibilityDelegate());
|
||||
setTextAlpha(1f);
|
||||
|
||||
int shadowSize = context.getResources().getDimensionPixelSize(
|
||||
R.dimen.blur_size_click_shadow);
|
||||
mHighlightShadowFilter = new BlurMaskFilter(shadowSize, BlurMaskFilter.Blur.INNER);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -421,8 +440,38 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
|||
|
||||
@Override
|
||||
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);
|
||||
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.
|
||||
*/
|
||||
private void setIcon(Drawable icon) {
|
||||
protected void setIcon(Drawable icon) {
|
||||
if (mIsIconVisible) {
|
||||
applyCompoundDrawables(icon);
|
||||
}
|
||||
|
@ -787,10 +836,11 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
|||
|
||||
@Override
|
||||
public SafeCloseable prepareDrawDragView() {
|
||||
int highlightColor = mHighlightColor;
|
||||
mHighlightColor = Color.TRANSPARENT;
|
||||
resetIconScale();
|
||||
setForceHideDot(true);
|
||||
return () -> {
|
||||
};
|
||||
return () -> mHighlightColor = highlightColor;
|
||||
}
|
||||
|
||||
private void resetIconScale() {
|
||||
|
@ -827,4 +877,17 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
|||
});
|
||||
iconUpdateAnimation.start();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void decorate(int color) {
|
||||
mHighlightColor = color;
|
||||
invalidate();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void removeDecoration() {
|
||||
mHighlightColor = Color.TRANSPARENT;
|
||||
invalidate();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,6 +25,10 @@ import android.view.ViewDebug;
|
|||
import android.view.ViewGroup;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* 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 Workspace mWorkspace;
|
||||
private boolean mSendTouchToWorkspace;
|
||||
@Nullable private Consumer<Boolean> mOnVisibilityAggregatedCallback;
|
||||
|
||||
public Hotseat(Context context) {
|
||||
this(context, null);
|
||||
|
@ -129,4 +134,18 @@ public class Hotseat extends CellLayout implements Insettable {
|
|||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -201,7 +201,7 @@ public class InvariantDeviceProfile {
|
|||
DisplayController.getDefaultDisplay(context).getInfo(),
|
||||
getPredefinedDeviceProfiles(context, gridName));
|
||||
|
||||
Info myInfo = new Info(context, display);
|
||||
Info myInfo = new Info(display);
|
||||
DisplayOption myDisplayOption = invDistWeightedInterpolate(
|
||||
myInfo, getPredefinedDeviceProfiles(context, gridName));
|
||||
|
||||
|
|
|
@ -34,6 +34,7 @@ import android.content.pm.PackageManager;
|
|||
import android.content.pm.ResolveInfo;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.content.res.Resources;
|
||||
import android.database.ContentObserver;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Matrix;
|
||||
|
@ -44,6 +45,7 @@ import android.graphics.RectF;
|
|||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.InsetDrawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.DeadObjectException;
|
||||
import android.os.Handler;
|
||||
|
@ -83,6 +85,7 @@ import java.lang.reflect.Method;
|
|||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
|
@ -661,6 +664,18 @@ public final class Utilities {
|
|||
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 final int mSize;
|
||||
|
|
|
@ -2078,40 +2078,40 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
|||
mLastReorderY = -1;
|
||||
}
|
||||
|
||||
/*
|
||||
*
|
||||
* Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's
|
||||
* coordinate space. The argument xy is modified with the return result.
|
||||
*/
|
||||
private void mapPointFromSelfToChild(View v, float[] xy) {
|
||||
xy[0] = xy[0] - v.getLeft();
|
||||
xy[1] = xy[1] - v.getTop();
|
||||
}
|
||||
/*
|
||||
*
|
||||
* Convert the 2D coordinate xy from the parent View's coordinate space to this CellLayout's
|
||||
* coordinate space. The argument xy is modified with the return result.
|
||||
*/
|
||||
private void mapPointFromSelfToChild(View v, float[] xy) {
|
||||
xy[0] = xy[0] - v.getLeft();
|
||||
xy[1] = xy[1] - v.getTop();
|
||||
}
|
||||
|
||||
boolean isPointInSelfOverHotseat(int x, int y) {
|
||||
mTempFXY[0] = x;
|
||||
mTempFXY[1] = y;
|
||||
mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempFXY, true);
|
||||
View hotseat = mLauncher.getHotseat();
|
||||
return mTempFXY[0] >= hotseat.getLeft() &&
|
||||
mTempFXY[0] <= hotseat.getRight() &&
|
||||
mTempFXY[1] >= hotseat.getTop() &&
|
||||
mTempFXY[1] <= hotseat.getBottom();
|
||||
}
|
||||
boolean isPointInSelfOverHotseat(int x, int y) {
|
||||
mTempFXY[0] = x;
|
||||
mTempFXY[1] = y;
|
||||
mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempFXY, true);
|
||||
View hotseat = mLauncher.getHotseat();
|
||||
return mTempFXY[0] >= hotseat.getLeft()
|
||||
&& mTempFXY[0] <= hotseat.getRight()
|
||||
&& mTempFXY[1] >= hotseat.getTop()
|
||||
&& mTempFXY[1] <= hotseat.getBottom();
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the point in {@param xy} to point to the co-ordinate space of {@param layout}
|
||||
* @param layout either hotseat of a page in workspace
|
||||
* @param xy the point location in workspace co-ordinate space
|
||||
*/
|
||||
private void mapPointFromDropLayout(CellLayout layout, float[] xy) {
|
||||
if (mLauncher.isHotseatLayout(layout)) {
|
||||
mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, xy, true);
|
||||
mLauncher.getDragLayer().mapCoordInSelfToDescendant(layout, xy);
|
||||
} else {
|
||||
mapPointFromSelfToChild(layout, xy);
|
||||
}
|
||||
}
|
||||
private void mapPointFromDropLayout(CellLayout layout, float[] xy) {
|
||||
if (mLauncher.isHotseatLayout(layout)) {
|
||||
mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, xy, true);
|
||||
mLauncher.getDragLayer().mapCoordInSelfToDescendant(layout, xy);
|
||||
} else {
|
||||
mapPointFromSelfToChild(layout, xy);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isDragWidget(DragObject d) {
|
||||
return (d.dragInfo instanceof LauncherAppWidgetInfo ||
|
||||
|
@ -2393,9 +2393,9 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
|||
spanY = mDragInfo.spanY;
|
||||
}
|
||||
|
||||
final int container = mLauncher.isHotseatLayout(cellLayout) ?
|
||||
LauncherSettings.Favorites.CONTAINER_HOTSEAT :
|
||||
LauncherSettings.Favorites.CONTAINER_DESKTOP;
|
||||
final int container = mLauncher.isHotseatLayout(cellLayout)
|
||||
? LauncherSettings.Favorites.CONTAINER_HOTSEAT
|
||||
: LauncherSettings.Favorites.CONTAINER_DESKTOP;
|
||||
final int screenId = getIdForScreen(cellLayout);
|
||||
if (!mLauncher.isHotseatLayout(cellLayout)
|
||||
&& screenId != getScreenIdForPageIndex(mCurrentPage)
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
package com.android.launcher3.allapps;
|
||||
|
||||
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_QUIET_MODE_CHANGE_PERMISSION;
|
||||
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.R;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.allapps.search.SearchEventTracker;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.keyboard.FocusedItemDecorator;
|
||||
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.views.RecyclerViewFastScroller;
|
||||
import com.android.launcher3.views.SpringRelativeLayout;
|
||||
import com.android.systemui.plugins.shared.SearchTargetEvent;
|
||||
|
||||
import java.util.function.IntConsumer;
|
||||
import com.android.systemui.plugins.shared.SearchTarget;
|
||||
|
||||
/**
|
||||
* The all apps view container.
|
||||
|
@ -546,13 +545,9 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
|
|||
return mLauncher.startActivitySafely(v, headerItem.getIntent(), headerItem);
|
||||
}
|
||||
AdapterItem focusedItem = getActiveRecyclerView().getApps().getFocusedChild();
|
||||
if (focusedItem instanceof AdapterItemWithPayload) {
|
||||
IntConsumer onSelection =
|
||||
((AdapterItemWithPayload) focusedItem).getSelectionHandler();
|
||||
if (onSelection != null) {
|
||||
onSelection.accept(SearchTargetEvent.QUICK_SELECT);
|
||||
return true;
|
||||
}
|
||||
if (focusedItem instanceof SearchAdapterItem) {
|
||||
SearchTarget searchTarget = ((SearchAdapterItem) focusedItem).getSearchTarget();
|
||||
SearchEventTracker.INSTANCE.get(getContext()).quickSelect(searchTarget);
|
||||
}
|
||||
if (focusedItem.appInfo != null) {
|
||||
ItemInfo itemInfo = focusedItem.appInfo;
|
||||
|
|
|
@ -20,8 +20,6 @@ import static com.android.launcher3.touch.ItemLongClickListener.INSTANCE_ALL_APP
|
|||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.res.Resources;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
|
@ -37,29 +35,22 @@ import androidx.annotation.Nullable;
|
|||
import androidx.core.view.accessibility.AccessibilityEventCompat;
|
||||
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
|
||||
import androidx.core.view.accessibility.AccessibilityRecordCompat;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.recyclerview.widget.GridLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.slice.Slice;
|
||||
import androidx.slice.widget.SliceLiveData;
|
||||
import androidx.slice.widget.SliceView;
|
||||
|
||||
import com.android.launcher3.BaseDraggingActivity;
|
||||
import com.android.launcher3.BubbleTextView;
|
||||
import com.android.launcher3.Launcher;
|
||||
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.config.FeatureFlags;
|
||||
import com.android.launcher3.model.data.AppInfo;
|
||||
import com.android.launcher3.util.PackageManagerHelper;
|
||||
import com.android.launcher3.views.HeroSearchResultView;
|
||||
import com.android.systemui.plugins.AllAppsSearchPlugin;
|
||||
import com.android.launcher3.views.SearchSliceWrapper;
|
||||
import com.android.systemui.plugins.shared.SearchTarget;
|
||||
import com.android.systemui.plugins.shared.SearchTargetEvent;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.function.IntConsumer;
|
||||
|
||||
/**
|
||||
* 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_ICON = 1 << 14;
|
||||
|
||||
// Common view type masks
|
||||
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.
|
||||
|
@ -192,56 +185,25 @@ public class AllAppsGridAdapter extends
|
|||
|| viewType == VIEW_TYPE_SEARCH_PEOPLE
|
||||
|| viewType == VIEW_TYPE_SEARCH_THUMBNAIL
|
||||
|| viewType == VIEW_TYPE_SEARCH_ICON_ROW
|
||||
|| viewType == VIEW_TYPE_SEARCH_ICON
|
||||
|| viewType == VIEW_TYPE_SEARCH_SUGGEST;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Extension of AdapterItem that contains an extra payload specific to item
|
||||
*
|
||||
* @param <T> Play load Type
|
||||
*/
|
||||
public static class AdapterItemWithPayload<T> extends AdapterItem {
|
||||
private T mPayload;
|
||||
private String mSearchSessionId;
|
||||
private AllAppsSearchPlugin mPlugin;
|
||||
private IntConsumer mSelectionHandler;
|
||||
public static class SearchAdapterItem extends AdapterItem {
|
||||
private SearchTarget mSearchTarget;
|
||||
|
||||
public AllAppsSearchPlugin getPlugin() {
|
||||
return mPlugin;
|
||||
}
|
||||
|
||||
public void setPlugin(AllAppsSearchPlugin plugin) {
|
||||
mPlugin = plugin;
|
||||
}
|
||||
|
||||
public AdapterItemWithPayload(T payload, int type, AllAppsSearchPlugin plugin) {
|
||||
mPayload = payload;
|
||||
public SearchAdapterItem(SearchTarget searchTarget, int type) {
|
||||
mSearchTarget = searchTarget;
|
||||
viewType = type;
|
||||
mPlugin = plugin;
|
||||
}
|
||||
|
||||
public void setSelectionHandler(IntConsumer runnable) {
|
||||
mSelectionHandler = runnable;
|
||||
public SearchTarget getSearchTarget() {
|
||||
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);
|
||||
icon.setLongPressTimeoutFactor(1f);
|
||||
icon.setOnFocusChangeListener(mIconFocusListener);
|
||||
if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
|
||||
icon.setOnClickListener(mOnIconClickListener);
|
||||
icon.setOnLongClickListener(mOnIconLongClickListener);
|
||||
}
|
||||
|
||||
icon.setOnClickListener(mOnIconClickListener);
|
||||
icon.setOnLongClickListener(mOnIconLongClickListener);
|
||||
// Ensure the all apps icon height matches the workspace icons in portrait mode.
|
||||
icon.getLayoutParams().height = mLauncher.getDeviceProfile().allAppsCellHeightPx;
|
||||
return new ViewHolder(icon);
|
||||
|
@ -446,6 +405,9 @@ public class AllAppsGridAdapter extends
|
|||
case VIEW_TYPE_ALL_APPS_DIVIDER:
|
||||
return new ViewHolder(mLayoutInflater.inflate(
|
||||
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:
|
||||
return new ViewHolder(
|
||||
mLayoutInflater.inflate(R.layout.search_section_title, parent, false));
|
||||
|
@ -480,6 +442,10 @@ public class AllAppsGridAdapter extends
|
|||
|
||||
@Override
|
||||
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()) {
|
||||
case VIEW_TYPE_ICON:
|
||||
AdapterItem adapterItem = mApps.getAdapterItems().get(position);
|
||||
|
@ -487,34 +453,6 @@ public class AllAppsGridAdapter extends
|
|||
BubbleTextView icon = (BubbleTextView) holder.itemView;
|
||||
icon.reset();
|
||||
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;
|
||||
case VIEW_TYPE_EMPTY_SEARCH:
|
||||
TextView emptyViewText = (TextView) holder.itemView;
|
||||
|
@ -532,39 +470,25 @@ public class AllAppsGridAdapter extends
|
|||
break;
|
||||
case VIEW_TYPE_SEARCH_SLICE:
|
||||
SliceView sliceView = (SliceView) holder.itemView;
|
||||
AdapterItemWithPayload<Uri> slicePayload =
|
||||
(AdapterItemWithPayload<Uri>) mApps.getAdapterItems().get(position);
|
||||
sliceView.setOnSliceActionListener((info1, s) -> {
|
||||
if (slicePayload.getPlugin() != null) {
|
||||
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) {
|
||||
}
|
||||
SearchAdapterItem slicePayload = (SearchAdapterItem) mApps.getAdapterItems().get(
|
||||
position);
|
||||
SearchTarget searchTarget = slicePayload.getSearchTarget();
|
||||
sliceView.setTag(new SearchSliceWrapper(mLauncher, sliceView, searchTarget));
|
||||
|
||||
break;
|
||||
case VIEW_TYPE_SEARCH_CORPUS_TITLE:
|
||||
case VIEW_TYPE_SEARCH_ROW_WITH_BUTTON:
|
||||
case VIEW_TYPE_SEARCH_HERO_APP:
|
||||
case VIEW_TYPE_SEARCH_ROW:
|
||||
case VIEW_TYPE_SEARCH_ICON:
|
||||
case VIEW_TYPE_SEARCH_ICON_ROW:
|
||||
case VIEW_TYPE_SEARCH_PEOPLE:
|
||||
case VIEW_TYPE_SEARCH_THUMBNAIL:
|
||||
case VIEW_TYPE_SEARCH_SUGGEST:
|
||||
AdapterItemWithPayload item =
|
||||
(AdapterItemWithPayload) mApps.getAdapterItems().get(position);
|
||||
PayloadResultHandler payloadResultView = (PayloadResultHandler) holder.itemView;
|
||||
payloadResultView.setup(item);
|
||||
SearchAdapterItem item =
|
||||
(SearchAdapterItem) mApps.getAdapterItems().get(position);
|
||||
SearchTargetHandler payloadResultView = (SearchTargetHandler) holder.itemView;
|
||||
payloadResultView.applySearchTarget(item.getSearchTarget());
|
||||
break;
|
||||
case VIEW_TYPE_ALL_APPS_DIVIDER:
|
||||
// nothing to do
|
||||
|
@ -576,17 +500,15 @@ public class AllAppsGridAdapter extends
|
|||
public void onViewRecycled(@NonNull ViewHolder holder) {
|
||||
super.onViewRecycled(holder);
|
||||
if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()) return;
|
||||
if (holder.itemView instanceof BubbleTextView) {
|
||||
BubbleTextView icon = (BubbleTextView) holder.itemView;
|
||||
icon.setOnClickListener(null);
|
||||
icon.setOnLongClickListener(null);
|
||||
} else if (holder.itemView instanceof SliceView) {
|
||||
if (holder.itemView instanceof AllAppsSectionDecorator.SelfDecoratingView) {
|
||||
((AllAppsSectionDecorator.SelfDecoratingView) holder.itemView).removeDecoration();
|
||||
}
|
||||
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 (sliceView.getTag() instanceof SearchSliceWrapper) {
|
||||
((SearchSliceWrapper) sliceView.getTag()).destroy();
|
||||
}
|
||||
sliceView.setTag(null);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,14 +20,16 @@ import android.util.AttributeSet;
|
|||
import android.view.MotionEvent;
|
||||
|
||||
import com.android.launcher3.PagedView;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
|
||||
public class AllAppsPagedView extends PagedView<PersonalWorkSlidingTabStrip> {
|
||||
|
||||
final static float START_DAMPING_TOUCH_SLOP_ANGLE = (float) Math.PI / 6;
|
||||
final static float MAX_SWIPE_ANGLE = (float) Math.PI / 3;
|
||||
final static float TOUCH_SLOP_DAMPING_FACTOR = 4;
|
||||
static final float START_DAMPING_TOUCH_SLOP_ANGLE = (float) Math.PI / 6;
|
||||
static final float MAX_SWIPE_ANGLE = (float) Math.PI / 3;
|
||||
static final float TOUCH_SLOP_DAMPING_FACTOR = 4;
|
||||
|
||||
public AllAppsPagedView(Context context) {
|
||||
public AllAppsPagedView(Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
|
@ -37,6 +39,10 @@ public class AllAppsPagedView extends PagedView<PersonalWorkSlidingTabStrip> {
|
|||
|
||||
public AllAppsPagedView(Context context, AttributeSet attrs, int 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
|
||||
|
|
|
@ -24,6 +24,7 @@ import android.content.res.Resources;
|
|||
import android.graphics.Canvas;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.util.SparseIntArray;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
|
@ -45,6 +46,8 @@ import java.util.List;
|
|||
* A RecyclerView with custom fast scroll support for the all apps view.
|
||||
*/
|
||||
public class AllAppsRecyclerView extends BaseRecyclerView {
|
||||
private static final String TAG = "AllAppsContainerView";
|
||||
private static final boolean DEBUG = true;
|
||||
|
||||
private AlphabeticalAppsList mApps;
|
||||
private final int mNumAppsPerRow;
|
||||
|
@ -131,7 +134,9 @@ public class AllAppsRecyclerView extends BaseRecyclerView {
|
|||
if (mEmptySearchBackground != null && mEmptySearchBackground.getAlpha() > 0) {
|
||||
mEmptySearchBackground.draw(c);
|
||||
}
|
||||
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, "onDraw at = " + System.currentTimeMillis());
|
||||
}
|
||||
super.onDraw(c);
|
||||
}
|
||||
|
||||
|
|
|
@ -26,6 +26,7 @@ import androidx.core.graphics.ColorUtils;
|
|||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.allapps.AllAppsGridAdapter.AppsGridLayoutManager;
|
||||
import com.android.launcher3.allapps.search.SearchSectionInfo;
|
||||
import com.android.launcher3.util.Themes;
|
||||
|
||||
|
@ -53,6 +54,9 @@ public class AllAppsSectionDecorator extends RecyclerView.ItemDecoration {
|
|||
int i = 0;
|
||||
while (i < itemCount) {
|
||||
View view = parent.getChildAt(i);
|
||||
if (view instanceof SelfDecoratingView) {
|
||||
((SelfDecoratingView) view).removeDecoration();
|
||||
}
|
||||
int position = parent.getChildAdapterPosition(view);
|
||||
AllAppsGridAdapter.AdapterItem adapterItem = adapterItems.get(position);
|
||||
if (adapterItem.searchSectionInfo != null) {
|
||||
|
@ -90,7 +94,10 @@ public class AllAppsSectionDecorator extends RecyclerView.ItemDecoration {
|
|||
if (mAppsView.getFloatingHeaderView().getFocusedChild() == null
|
||||
&& mAppsView.getApps().getFocusedChild() != null) {
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
@ -101,8 +108,8 @@ public class AllAppsSectionDecorator extends RecyclerView.ItemDecoration {
|
|||
* Handles grouping and drawing of items in the same all apps sections.
|
||||
*/
|
||||
public static class SectionDecorationHandler {
|
||||
private static final int FILL_ALPHA = (int) (.3f * 255);
|
||||
private static final int FOCUS_ALPHA = (int) (.8f * 255);
|
||||
private static final int FILL_ALPHA = 0;
|
||||
private static final int FOCUS_ALPHA = (int) (.9f * 255);
|
||||
|
||||
protected RectF mBounds = new RectF();
|
||||
private final boolean mIsFullWidth;
|
||||
|
@ -152,6 +159,10 @@ public class AllAppsSectionDecorator extends RecyclerView.ItemDecoration {
|
|||
if (view == null) {
|
||||
return;
|
||||
}
|
||||
if (view instanceof SelfDecoratingView) {
|
||||
((SelfDecoratingView) view).decorate(mFocusColor);
|
||||
return;
|
||||
}
|
||||
mPaint.setColor(mFocusColor);
|
||||
canvas.drawRoundRect(view.getLeft(), view.getTop(),
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -68,16 +68,16 @@ public class AllAppsTransitionController implements StateHandler<LauncherState>,
|
|||
public static final FloatProperty<AllAppsTransitionController> ALL_APPS_PROGRESS =
|
||||
new FloatProperty<AllAppsTransitionController>("allAppsProgress") {
|
||||
|
||||
@Override
|
||||
public Float get(AllAppsTransitionController controller) {
|
||||
return controller.mProgress;
|
||||
}
|
||||
@Override
|
||||
public Float get(AllAppsTransitionController controller) {
|
||||
return controller.mProgress;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setValue(AllAppsTransitionController controller, float progress) {
|
||||
controller.setProgress(progress);
|
||||
}
|
||||
};
|
||||
@Override
|
||||
public void setValue(AllAppsTransitionController controller, float progress) {
|
||||
controller.setProgress(progress);
|
||||
}
|
||||
};
|
||||
|
||||
private static final int APPS_VIEW_ALPHA_CHANNEL_INDEX = 0;
|
||||
|
||||
|
@ -133,7 +133,6 @@ public class AllAppsTransitionController implements StateHandler<LauncherState>,
|
|||
* 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
|
||||
*
|
||||
* @see #setState(LauncherState)
|
||||
* @see #setStateWithAnimation(LauncherState, StateAnimationConfig, PendingAnimation)
|
||||
*/
|
||||
|
@ -238,7 +237,7 @@ public class AllAppsTransitionController implements StateHandler<LauncherState>,
|
|||
mInsetController = new AllAppsInsetTransitionController(mShiftRange, mAppsView);
|
||||
mLauncher.getSystemUiController().updateUiState(UI_STATE_ALLAPPS,
|
||||
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
|
||||
| View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -178,16 +178,46 @@ public class AlphabeticalAppsList implements AllAppsStore.OnUpdateListener {
|
|||
/**
|
||||
* Sets results list for search
|
||||
*/
|
||||
public boolean setSearchResults(ArrayList<AdapterItem> f) {
|
||||
if (f == null || mSearchResults != f) {
|
||||
boolean same = mSearchResults != null && mSearchResults.equals(f);
|
||||
mSearchResults = f;
|
||||
public boolean setSearchResults(ArrayList<AdapterItem> results) {
|
||||
if (results == null || mSearchResults != results) {
|
||||
boolean same = mSearchResults != null && mSearchResults.equals(results);
|
||||
mSearchResults = results;
|
||||
onAppsUpdated();
|
||||
return !same;
|
||||
}
|
||||
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.
|
||||
*/
|
||||
|
@ -294,28 +324,7 @@ public class AlphabeticalAppsList implements AllAppsStore.OnUpdateListener {
|
|||
}
|
||||
appSection.setPosEnd(mApps.isEmpty() ? appSection.getPosStart() : position - 1);
|
||||
} else {
|
||||
List<AppInfo> appInfos = new ArrayList<>();
|
||||
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++;
|
||||
}
|
||||
}
|
||||
updateSearchAdapterItems(mSearchResults, 0);
|
||||
if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
|
||||
// Append the search market item
|
||||
if (hasNoFilteredResults()) {
|
||||
|
|
|
@ -111,8 +111,8 @@ public class FloatingHeaderView extends LinearLayout implements
|
|||
|
||||
public FloatingHeaderView(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mHeaderTopPadding = context.getResources()
|
||||
.getDimensionPixelSize(R.dimen.all_apps_header_top_padding);
|
||||
mHeaderTopPadding = FeatureFlags.ENABLE_DEVICE_SEARCH.get() ? 0 :
|
||||
context.getResources().getDimensionPixelSize(R.dimen.all_apps_header_top_padding);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -130,6 +130,7 @@ public class FloatingHeaderView extends LinearLayout implements
|
|||
}
|
||||
}
|
||||
mFixedRows = rows.toArray(new FloatingHeaderRow[rows.size()]);
|
||||
setPadding(0, mHeaderTopPadding, 0, 0);
|
||||
mAllRows = mFixedRows;
|
||||
}
|
||||
|
||||
|
@ -247,7 +248,9 @@ public class FloatingHeaderView extends LinearLayout implements
|
|||
|
||||
public int getMaxTranslation() {
|
||||
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) {
|
||||
return mMaxTranslation + getPaddingTop();
|
||||
} else {
|
||||
|
|
|
@ -30,13 +30,11 @@ import com.android.launcher3.BaseDraggingActivity;
|
|||
import com.android.launcher3.ExtendedEditText;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.allapps.AllAppsGridAdapter;
|
||||
import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload;
|
||||
import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.util.PackageManagerHelper;
|
||||
import com.android.systemui.plugins.AllAppsSearchPlugin;
|
||||
import com.android.systemui.plugins.shared.SearchTarget;
|
||||
import com.android.systemui.plugins.shared.SearchTargetEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
@ -114,7 +112,7 @@ public class AllAppsSearchBarController
|
|||
@Override
|
||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||
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
|
||||
if (Launcher.getLauncher(mLauncher).getAppsView().selectFocusedView(v)) {
|
||||
return true;
|
||||
|
@ -196,9 +194,16 @@ public class AllAppsSearchBarController
|
|||
/**
|
||||
* 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.
|
||||
|
@ -208,50 +213,20 @@ public class AllAppsSearchBarController
|
|||
|
||||
/**
|
||||
* An interface for supporting dynamic search results
|
||||
*
|
||||
* @param <T> Type of payload
|
||||
*/
|
||||
public interface PayloadResultHandler<T> {
|
||||
/**
|
||||
* Updates View using Adapter's payload
|
||||
*/
|
||||
public interface SearchTargetHandler {
|
||||
|
||||
default void setup(AdapterItemWithPayload<T> adapterItemWithPayload) {
|
||||
Object[] targetInfo = getTargetInfo();
|
||||
if (targetInfo != null) {
|
||||
targetInfo[0] = adapterItemWithPayload.getSearchSessionId();
|
||||
targetInfo[1] = adapterItemWithPayload.position;
|
||||
}
|
||||
applyAdapterInfo(adapterItemWithPayload);
|
||||
/**
|
||||
* Update view using values from {@link SearchTarget}
|
||||
*/
|
||||
void applySearchTarget(SearchTarget searchTarget);
|
||||
|
||||
/**
|
||||
* Handles selection of SearchTarget
|
||||
*/
|
||||
default void handleSelection(int eventType) {
|
||||
}
|
||||
|
||||
void applyAdapterInfo(AdapterItemWithPayload<T> adapterItemWithPayload);
|
||||
|
||||
/**
|
||||
* Gets object created by {@link PayloadResultHandler#createTargetInfo()}
|
||||
*/
|
||||
Object[] getTargetInfo();
|
||||
|
||||
/**
|
||||
* Creates a wrapper object to hold searchSessionId and item position
|
||||
*/
|
||||
default Object[] createTargetInfo() {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ import com.android.launcher3.Insettable;
|
|||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.R;
|
||||
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.AlphabeticalAppsList;
|
||||
import com.android.launcher3.allapps.SearchUiManager;
|
||||
|
@ -173,7 +173,7 @@ public class AppsSearchContainerLayout extends ExtendedEditText
|
|||
}
|
||||
|
||||
@Override
|
||||
public void onSearchResult(String query, ArrayList<AllAppsGridAdapter.AdapterItem> items) {
|
||||
public void onSearchResult(String query, ArrayList<AdapterItem> items) {
|
||||
if (items != null) {
|
||||
mApps.setSearchResults(items);
|
||||
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
|
||||
public void clearSearchResult() {
|
||||
if (mApps.setSearchResults(null)) {
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
package com.android.launcher3.allapps.search;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.CancellationSignal;
|
||||
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
|
||||
|
@ -47,11 +48,12 @@ public class AppsSearchPipeline implements SearchPipeline {
|
|||
}
|
||||
|
||||
@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() {
|
||||
@Override
|
||||
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));
|
||||
}
|
||||
});
|
||||
|
|
|
@ -46,8 +46,10 @@ public class DefaultAppSearchAlgorithm implements SearchAlgorithm {
|
|||
@Override
|
||||
public void doSearch(final String query,
|
||||
final AllAppsSearchBarController.Callbacks callback) {
|
||||
mAppsSearchPipeline.performSearch(query,
|
||||
results -> mResultHandler.post(() -> callback.onSearchResult(query, results)));
|
||||
mAppsSearchPipeline.query(query,
|
||||
results -> mResultHandler.post(
|
||||
() -> callback.onSearchResult(query, results)),
|
||||
null);
|
||||
}
|
||||
|
||||
public static boolean matches(AppInfo info, String query, StringMatcher matcher) {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -15,6 +15,8 @@
|
|||
*/
|
||||
package com.android.launcher3.allapps.search;
|
||||
|
||||
import android.os.CancellationSignal;
|
||||
|
||||
import com.android.launcher3.allapps.AllAppsGridAdapter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -23,10 +25,13 @@ import java.util.function.Consumer;
|
|||
/**
|
||||
* An interface for handling search within pipeline
|
||||
*/
|
||||
// Remove when System Service API is added.
|
||||
public interface SearchPipeline {
|
||||
|
||||
/**
|
||||
* Perform query
|
||||
*/
|
||||
void performSearch(String query, Consumer<ArrayList<AllAppsGridAdapter.AdapterItem>> cb);
|
||||
void query(String input,
|
||||
Consumer<ArrayList<AllAppsGridAdapter.AdapterItem>> callback,
|
||||
CancellationSignal cancellationSignal);
|
||||
}
|
||||
|
|
|
@ -22,7 +22,7 @@ import com.android.launcher3.allapps.AllAppsSectionDecorator.SectionDecorationHa
|
|||
*/
|
||||
public class SearchSectionInfo {
|
||||
|
||||
private String mTitle;
|
||||
private String mSectionId;
|
||||
private SectionDecorationHandler mDecorationHandler;
|
||||
|
||||
public int getPosStart() {
|
||||
|
@ -48,8 +48,8 @@ public class SearchSectionInfo {
|
|||
this(null);
|
||||
}
|
||||
|
||||
public SearchSectionInfo(String title) {
|
||||
mTitle = title;
|
||||
public SearchSectionInfo(String sectionId) {
|
||||
mSectionId = sectionId;
|
||||
}
|
||||
|
||||
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() {
|
||||
return mTitle == null ? "" : mTitle;
|
||||
public String getSectionId() {
|
||||
return mSectionId == null ? "" : mSectionId;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -156,10 +156,14 @@ public final class FeatureFlags {
|
|||
"ENABLE_DATABASE_RESTORE", true,
|
||||
"Enable database restore when new restore session is created");
|
||||
|
||||
public static final BooleanFlag ENABLE_UNIVERSAL_SMARTSPACE = getDebugFlag(
|
||||
"ENABLE_UNIVERSAL_SMARTSPACE", false,
|
||||
public static final BooleanFlag ENABLE_SMARTSPACE_UNIVERSAL = getDebugFlag(
|
||||
"ENABLE_SMARTSPACE_UNIVERSAL", false,
|
||||
"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(
|
||||
"ENABLE_SYSTEM_VELOCITY_PROVIDER", true,
|
||||
"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");
|
||||
|
||||
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.");
|
||||
|
||||
public static void initialize(Context context) {
|
||||
|
|
|
@ -53,8 +53,8 @@ import com.android.launcher3.R;
|
|||
import com.android.launcher3.logging.StatsLogManager;
|
||||
import com.android.launcher3.model.ItemInstallQueue;
|
||||
import com.android.launcher3.model.WidgetItem;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.pm.PinRequestHelper;
|
||||
import com.android.launcher3.util.InstantAppResolver;
|
||||
import com.android.launcher3.views.BaseDragLayer;
|
||||
import com.android.launcher3.widget.PendingAddShortcutInfo;
|
||||
import com.android.launcher3.widget.PendingAddWidgetInfo;
|
||||
|
@ -87,7 +87,6 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener
|
|||
private Bundle mWidgetOptions;
|
||||
|
||||
private boolean mFinishOnPause = false;
|
||||
private InstantAppResolver mInstantAppResolver;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
|
@ -101,7 +100,6 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener
|
|||
|
||||
mApp = LauncherAppState.getInstance(this);
|
||||
mIdp = mApp.getInvariantDeviceProfile();
|
||||
mInstantAppResolver = InstantAppResolver.newInstance(this);
|
||||
|
||||
// Use the application context to get the device profile, as in multiwindow-mode, the
|
||||
// confirmation activity might be rotated.
|
||||
|
@ -322,6 +320,8 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener
|
|||
}
|
||||
|
||||
private void logCommand(StatsLogManager.EventEnum command) {
|
||||
getStatsLogManager().logger().log(command);
|
||||
getStatsLogManager().logger()
|
||||
.withItemInfo((ItemInfo) mWidgetCell.getWidgetView().getTag())
|
||||
.log(command);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -50,6 +50,7 @@ import android.util.LongSparseArray;
|
|||
import android.util.TimingLogger;
|
||||
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.LauncherAppWidgetProviderInfo;
|
||||
import com.android.launcher3.LauncherModel;
|
||||
import com.android.launcher3.LauncherSettings;
|
||||
import com.android.launcher3.Utilities;
|
||||
|
@ -315,6 +316,7 @@ public class LoaderTask implements Runnable {
|
|||
final PackageManagerHelper pmHelper = new PackageManagerHelper(context);
|
||||
final boolean isSafeMode = pmHelper.isSafeMode();
|
||||
final boolean isSdCardReady = Utilities.isBootCompleted();
|
||||
final WidgetManagerHelper widgetHelper = new WidgetManagerHelper(context);
|
||||
|
||||
boolean clearDb = false;
|
||||
try {
|
||||
|
@ -402,6 +404,7 @@ public class LoaderTask implements Runnable {
|
|||
|
||||
WorkspaceItemInfo info;
|
||||
LauncherAppWidgetInfo appWidgetInfo;
|
||||
LauncherAppWidgetProviderInfo widgetProviderInfo;
|
||||
Intent intent;
|
||||
String targetPkg;
|
||||
|
||||
|
@ -731,6 +734,19 @@ public class LoaderTask implements Runnable {
|
|||
+ appWidgetInfo.spanX + "x" + appWidgetInfo.spanY);
|
||||
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()) {
|
||||
c.markDeleted("Widget found where container != " +
|
||||
"CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!");
|
||||
|
|
|
@ -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_TASK;
|
||||
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.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.SearchResultContainer;
|
||||
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.TaskSwitcherContainer;
|
||||
import com.android.launcher3.model.ModelWriter;
|
||||
import com.android.launcher3.shortcuts.ShortcutKey;
|
||||
import com.android.launcher3.util.ContentWriter;
|
||||
|
||||
import java.util.Optional;
|
||||
|
@ -282,9 +283,14 @@ public class ItemInfo {
|
|||
case ITEM_TYPE_DEEP_SHORTCUT:
|
||||
itemBuilder
|
||||
.setShortcut(nullableComponent
|
||||
.map(component -> LauncherAtom.Shortcut.newBuilder()
|
||||
.setShortcutName(component.flattenToShortString())
|
||||
.setShortcutId(ShortcutKey.fromItemInfo(this).getId()))
|
||||
.map(component -> {
|
||||
Shortcut.Builder lsb = Shortcut.newBuilder()
|
||||
.setShortcutName(component.flattenToShortString());
|
||||
Optional.ofNullable(getIntent())
|
||||
.map(i -> i.getStringExtra(EXTRA_SHORTCUT_ID))
|
||||
.ifPresent(lsb::setShortcutId);
|
||||
return lsb;
|
||||
})
|
||||
.orElse(LauncherAtom.Shortcut.newBuilder()));
|
||||
break;
|
||||
case ITEM_TYPE_SHORTCUT:
|
||||
|
|
|
@ -61,4 +61,8 @@ public class RemoteActionItemInfo extends ItemInfoWithIcon {
|
|||
public boolean shouldStartInLauncher() {
|
||||
return mShouldStart;
|
||||
}
|
||||
|
||||
public boolean isEscapeHatch() {
|
||||
return mToken.contains("item_type:[ESCAPE_HATCH]");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -275,7 +275,8 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat {
|
|||
launchBackTutorialPreference.setSummary("Learn how to use the Back gesture");
|
||||
launchBackTutorialPreference.setOnPreferenceClickListener(preference -> {
|
||||
startActivity(launchSandboxIntent.putExtra(
|
||||
"tutorial_type", "RIGHT_EDGE_BACK_NAVIGATION"));
|
||||
"tutorial_steps",
|
||||
new String[] {"RIGHT_EDGE_BACK_NAVIGATION"}));
|
||||
return true;
|
||||
});
|
||||
sandboxCategory.addPreference(launchBackTutorialPreference);
|
||||
|
@ -284,7 +285,9 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat {
|
|||
launchHomeTutorialPreference.setTitle("Launch Home Tutorial");
|
||||
launchHomeTutorialPreference.setSummary("Learn how to use the Home gesture");
|
||||
launchHomeTutorialPreference.setOnPreferenceClickListener(preference -> {
|
||||
startActivity(launchSandboxIntent.putExtra("tutorial_type", "HOME_NAVIGATION"));
|
||||
startActivity(launchSandboxIntent.putExtra(
|
||||
"tutorial_steps",
|
||||
new String[] {"HOME_NAVIGATION"}));
|
||||
return true;
|
||||
});
|
||||
sandboxCategory.addPreference(launchHomeTutorialPreference);
|
||||
|
@ -293,7 +296,9 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat {
|
|||
launchOverviewTutorialPreference.setTitle("Launch Overview Tutorial");
|
||||
launchOverviewTutorialPreference.setSummary("Learn how to use the Overview gesture");
|
||||
launchOverviewTutorialPreference.setOnPreferenceClickListener(preference -> {
|
||||
startActivity(launchSandboxIntent.putExtra("tutorial_type", "OVERVIEW_NAVIGATION"));
|
||||
startActivity(launchSandboxIntent.putExtra(
|
||||
"tutorial_steps",
|
||||
new String[] {"OVERVIEW_NAVIGATION"}));
|
||||
return true;
|
||||
});
|
||||
sandboxCategory.addPreference(launchOverviewTutorialPreference);
|
||||
|
@ -302,7 +307,9 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat {
|
|||
launchAssistantTutorialPreference.setTitle("Launch Assistant Tutorial");
|
||||
launchAssistantTutorialPreference.setSummary("Learn how to use the Assistant gesture");
|
||||
launchAssistantTutorialPreference.setOnPreferenceClickListener(preference -> {
|
||||
startActivity(launchSandboxIntent.putExtra("tutorial_type", "ASSISTANT"));
|
||||
startActivity(launchSandboxIntent.putExtra(
|
||||
"tutorial_steps",
|
||||
new String[] {"ASSISTANT"}));
|
||||
return true;
|
||||
});
|
||||
sandboxCategory.addPreference(launchAssistantTutorialPreference);
|
||||
|
@ -311,7 +318,9 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat {
|
|||
launchSandboxModeTutorialPreference.setTitle("Launch Sandbox Mode");
|
||||
launchSandboxModeTutorialPreference.setSummary("Practice navigation gestures");
|
||||
launchSandboxModeTutorialPreference.setOnPreferenceClickListener(preference -> {
|
||||
startActivity(launchSandboxIntent.putExtra("tutorial_type", "SANDBOX_MODE"));
|
||||
startActivity(launchSandboxIntent.putExtra(
|
||||
"tutorial_steps",
|
||||
new String[] {"SANDBOX_MODE"}));
|
||||
return true;
|
||||
});
|
||||
sandboxCategory.addPreference(launchSandboxModeTutorialPreference);
|
||||
|
|
|
@ -21,14 +21,9 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
|
|||
import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.ContentResolver;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
||||
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.Utilities;
|
||||
|
@ -43,16 +38,6 @@ public class RotationHelper implements OnSharedPreferenceChangeListener {
|
|||
|
||||
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() {
|
||||
// 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.
|
||||
|
@ -106,20 +91,6 @@ public class RotationHelper implements OnSharedPreferenceChangeListener {
|
|||
} else {
|
||||
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
|
||||
|
@ -129,7 +100,6 @@ public class RotationHelper implements OnSharedPreferenceChangeListener {
|
|||
getAllowRotationDefaultValue());
|
||||
if (mHomeRotationEnabled != wasRotationEnabled) {
|
||||
notifyChange();
|
||||
updateAutoRotateSetting();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -165,11 +135,6 @@ public class RotationHelper implements OnSharedPreferenceChangeListener {
|
|||
if (!mInitialized) {
|
||||
mInitialized = true;
|
||||
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) {
|
||||
mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this);
|
||||
}
|
||||
mContentResolver.unregisterContentObserver(mSystemAutoRotateObserver);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -225,9 +189,8 @@ public class RotationHelper implements OnSharedPreferenceChangeListener {
|
|||
@Override
|
||||
public String toString() {
|
||||
return String.format("[mStateHandlerRequest=%d, mCurrentStateRequest=%d,"
|
||||
+ " mLastActivityFlags=%d, mIgnoreAutoRotateSettings=%b, mHomeRotationEnabled=%b,"
|
||||
+ " mSystemAutoRotateEnabled=%b]",
|
||||
+ " mLastActivityFlags=%d, mIgnoreAutoRotateSettings=%b, mHomeRotationEnabled=%b]",
|
||||
mStateHandlerRequest, mCurrentStateRequest, mLastActivityFlags,
|
||||
mIgnoreAutoRotateSettings, mHomeRotationEnabled, mSystemAutoRotateEnabled);
|
||||
mIgnoreAutoRotateSettings, mHomeRotationEnabled);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -31,6 +31,8 @@ import android.view.Display;
|
|||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.launcher3.Utilities;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
/**
|
||||
|
@ -157,13 +159,13 @@ public class DisplayController implements DisplayListener {
|
|||
private final ArrayList<DisplayInfoChangeListener> mListeners = new ArrayList<>();
|
||||
private DisplayController.Info mInfo;
|
||||
|
||||
private DisplayHolder(Context displayContext) {
|
||||
private DisplayHolder(Context displayContext, Display display) {
|
||||
mDisplayContext = displayContext;
|
||||
// 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
|
||||
// Application to provide the actual state of device that excludes the additional
|
||||
// adjustment and override.
|
||||
mInfo = new DisplayController.Info(mDisplayContext);
|
||||
mInfo = new DisplayController.Info(display);
|
||||
mId = mInfo.id;
|
||||
}
|
||||
|
||||
|
@ -180,22 +182,31 @@ public class DisplayController implements DisplayListener {
|
|||
}
|
||||
|
||||
protected void handleOnChange() {
|
||||
Display display = Utilities.ATLEAST_R
|
||||
? mDisplayContext.getDisplay()
|
||||
: mDisplayContext
|
||||
.getSystemService(DisplayManager.class)
|
||||
.getDisplay(mId);
|
||||
if (display == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
Info oldInfo = mInfo;
|
||||
Info info = new Info(mDisplayContext);
|
||||
Info newInfo = new Info(display);
|
||||
|
||||
int change = 0;
|
||||
if (info.hasDifferentSize(oldInfo)) {
|
||||
if (newInfo.hasDifferentSize(oldInfo)) {
|
||||
change |= CHANGE_SIZE;
|
||||
}
|
||||
if (oldInfo.rotation != info.rotation) {
|
||||
if (newInfo.rotation != oldInfo.rotation) {
|
||||
change |= CHANGE_ROTATION;
|
||||
}
|
||||
if (info.singleFrameMs != oldInfo.singleFrameMs) {
|
||||
if (newInfo.singleFrameMs != oldInfo.singleFrameMs) {
|
||||
change |= CHANGE_FRAME_DELAY;
|
||||
}
|
||||
|
||||
if (change != 0) {
|
||||
mInfo = info;
|
||||
mInfo = newInfo;
|
||||
final int flags = change;
|
||||
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
|
||||
// Resources.
|
||||
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;
|
||||
}
|
||||
|
||||
private Info(Context context) {
|
||||
this(context, context.getSystemService(DisplayManager.class)
|
||||
.getDisplay(DEFAULT_DISPLAY));
|
||||
}
|
||||
|
||||
public Info(Context context, Display display) {
|
||||
public Info(Display display) {
|
||||
id = display.getDisplayId();
|
||||
rotation = display.getRotation();
|
||||
|
||||
|
@ -262,7 +268,8 @@ public class DisplayController implements DisplayListener {
|
|||
display.getRealSize(realSize);
|
||||
display.getCurrentSizeRange(smallestSize, largestSize);
|
||||
|
||||
metrics = context.getResources().getDisplayMetrics();
|
||||
metrics = new DisplayMetrics();
|
||||
display.getMetrics(metrics);
|
||||
}
|
||||
|
||||
private boolean hasDifferentSize(Info info) {
|
||||
|
|
|
@ -20,10 +20,8 @@ import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
|||
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.graphics.Point;
|
||||
import android.os.Bundle;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Pair;
|
||||
import android.view.View;
|
||||
|
@ -37,8 +35,9 @@ import com.android.launcher3.DropTarget;
|
|||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload;
|
||||
import com.android.launcher3.allapps.search.AllAppsSearchBarController.PayloadResultHandler;
|
||||
import com.android.launcher3.allapps.AllAppsStore;
|
||||
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.DraggableView;
|
||||
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.shortcuts.ShortcutDragPreviewProvider;
|
||||
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.SearchTargetEvent;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 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,
|
||||
PayloadResultHandler<List<Pair<ShortcutInfo, ItemInfoWithIcon>>> {
|
||||
public class HeroSearchResultView extends LinearLayout implements DragSource, SearchTargetHandler {
|
||||
|
||||
public static final String TARGET_TYPE_HERO_APP = "hero_app";
|
||||
|
||||
public static final int MAX_SHORTCUTS_COUNT = 2;
|
||||
private final Object[] mTargetInfo = createTargetInfo();
|
||||
BubbleTextView mBubbleTextView;
|
||||
View mIconView;
|
||||
BubbleTextView[] mDeepShortcutTextViews = new BubbleTextView[2];
|
||||
AllAppsSearchPlugin mPlugin;
|
||||
|
||||
private SearchTarget mSearchTarget;
|
||||
private BubbleTextView mBubbleTextView;
|
||||
private View mIconView;
|
||||
private BubbleTextView[] mDeepShortcutTextViews = new BubbleTextView[2];
|
||||
|
||||
|
||||
public HeroSearchResultView(Context context) {
|
||||
super(context);
|
||||
|
@ -96,8 +99,6 @@ public class HeroSearchResultView extends LinearLayout implements DragSource,
|
|||
launcher.getItemOnClickListener().onClick(view);
|
||||
});
|
||||
mBubbleTextView.setOnLongClickListener(new HeroItemDragHandler(getContext(), this));
|
||||
setLayoutParams(
|
||||
new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, grid.allAppsCellHeightPx));
|
||||
|
||||
|
||||
mDeepShortcutTextViews[0] = findViewById(R.id.shortcut_0);
|
||||
|
@ -108,35 +109,39 @@ public class HeroSearchResultView extends LinearLayout implements DragSource,
|
|||
grid.allAppsIconSizePx));
|
||||
bubbleTextView.setOnClickListener(view -> {
|
||||
WorkspaceItemInfo itemInfo = (WorkspaceItemInfo) bubbleTextView.getTag();
|
||||
SearchTargetEvent event = getSearchTargetEvent(
|
||||
SearchTarget.ItemType.APP_HERO,
|
||||
SearchTargetEvent.CHILD_SELECT);
|
||||
event.bundle = getAppBundle(itemInfo);
|
||||
event.bundle.putString("shortcut_id", itemInfo.getDeepShortcutId());
|
||||
if (mPlugin != null) {
|
||||
mPlugin.notifySearchTargetEvent(event);
|
||||
}
|
||||
SearchTargetEvent event = new SearchTargetEvent.Builder(mSearchTarget,
|
||||
SearchTargetEvent.CHILD_SELECT).setShortcutPosition(itemInfo.rank).build();
|
||||
SearchEventTracker.getInstance(getContext()).notifySearchTargetEvent(event);
|
||||
launcher.getItemOnClickListener().onClick(view);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply {@link ItemInfo} for appIcon and shortcut Icons
|
||||
*/
|
||||
@Override
|
||||
public void applyAdapterInfo(
|
||||
AdapterItemWithPayload<List<Pair<ShortcutInfo, ItemInfoWithIcon>>> adapterItem) {
|
||||
mBubbleTextView.applyFromApplicationInfo(adapterItem.appInfo);
|
||||
public void applySearchTarget(SearchTarget searchTarget) {
|
||||
mSearchTarget = searchTarget;
|
||||
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.setTag(adapterItem.appInfo);
|
||||
List<Pair<ShortcutInfo, ItemInfoWithIcon>> shortcutDetails = adapterItem.getPayload();
|
||||
mIconView.setTag(appInfo);
|
||||
LauncherAppState appState = LauncherAppState.getInstance(getContext());
|
||||
for (int i = 0; i < mDeepShortcutTextViews.length; i++) {
|
||||
BubbleTextView shortcutView = mDeepShortcutTextViews[i];
|
||||
mDeepShortcutTextViews[i].setVisibility(shortcutDetails.size() > i ? VISIBLE : GONE);
|
||||
if (i < shortcutDetails.size()) {
|
||||
Pair<ShortcutInfo, ItemInfoWithIcon> p = shortcutDetails.get(i);
|
||||
mDeepShortcutTextViews[i].setVisibility(shortcuts.size() > i ? VISIBLE : GONE);
|
||||
if (i < shortcuts.size()) {
|
||||
Pair<ShortcutInfo, ItemInfoWithIcon> p = shortcuts.get(i);
|
||||
//apply ItemInfo and prepare view
|
||||
shortcutView.applyFromWorkspaceItem((WorkspaceItemInfo) p.second);
|
||||
MODEL_EXECUTOR.execute(() -> {
|
||||
|
@ -146,13 +151,7 @@ public class HeroSearchResultView extends LinearLayout implements DragSource,
|
|||
});
|
||||
}
|
||||
}
|
||||
mPlugin = adapterItem.getPlugin();
|
||||
adapterItem.setSelectionHandler(this::handleSelection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getTargetInfo() {
|
||||
return mTargetInfo;
|
||||
SearchEventTracker.INSTANCE.get(getContext()).registerWeakHandler(searchTarget, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -190,38 +189,21 @@ public class HeroSearchResultView extends LinearLayout implements DragSource,
|
|||
mLauncher.getWorkspace().beginDragShared(mContainer.mBubbleTextView,
|
||||
draggableView, mContainer, itemInfo, previewProvider, new DragOptions());
|
||||
|
||||
SearchTargetEvent event = mContainer.getSearchTargetEvent(
|
||||
SearchTarget.ItemType.APP_HERO, SearchTargetEvent.LONG_PRESS);
|
||||
event.bundle = getAppBundle(itemInfo);
|
||||
if (mContainer.mPlugin != null) {
|
||||
mContainer.mPlugin.notifySearchTargetEvent(event);
|
||||
}
|
||||
|
||||
SearchTargetEvent event = new SearchTargetEvent.Builder(mContainer.mSearchTarget,
|
||||
SearchTargetEvent.LONG_PRESS).build();
|
||||
SearchEventTracker.INSTANCE.get(mLauncher).notifySearchTargetEvent(event);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void handleSelection(int eventType) {
|
||||
@Override
|
||||
public void handleSelection(int eventType) {
|
||||
ItemInfo itemInfo = (ItemInfo) mBubbleTextView.getTag();
|
||||
if (itemInfo == null) return;
|
||||
Launcher launcher = Launcher.getLauncher(getContext());
|
||||
launcher.startActivitySafely(this, itemInfo.getIntent(), itemInfo);
|
||||
|
||||
SearchTargetEvent event = getSearchTargetEvent(
|
||||
SearchTarget.ItemType.APP_HERO, eventType);
|
||||
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;
|
||||
SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(
|
||||
new SearchTargetEvent.Builder(mSearchTarget, eventType).build());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -22,100 +22,159 @@ import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
|
|||
import android.app.RemoteAction;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.util.AttributeSet;
|
||||
import android.widget.EditText;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.Launcher;
|
||||
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.SearchEventTracker;
|
||||
import com.android.launcher3.icons.BitmapInfo;
|
||||
import com.android.launcher3.icons.LauncherIcons;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.model.data.ItemInfoWithIcon;
|
||||
import com.android.launcher3.model.data.RemoteActionItemInfo;
|
||||
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
||||
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.ItemType;
|
||||
import com.android.systemui.plugins.shared.SearchTargetEvent;
|
||||
|
||||
/**
|
||||
* A view representing a stand alone shortcut search result
|
||||
*/
|
||||
public class SearchResultIconRow extends DoubleShadowBubbleTextView implements
|
||||
AllAppsSearchBarController.PayloadResultHandler<SearchTarget> {
|
||||
AllAppsSearchBarController.SearchTargetHandler {
|
||||
|
||||
private final Object[] mTargetInfo = createTargetInfo();
|
||||
private ShortcutInfo mShortcutInfo;
|
||||
private AllAppsSearchPlugin mPlugin;
|
||||
private AdapterItemWithPayload<SearchTarget> mAdapterItem;
|
||||
|
||||
public static final String TARGET_TYPE_REMOTE_ACTION = "remote_action";
|
||||
public static final String TARGET_TYPE_SUGGEST = "suggest";
|
||||
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) {
|
||||
super(context);
|
||||
this(context, null, 0);
|
||||
}
|
||||
|
||||
public SearchResultIconRow(@NonNull Context context,
|
||||
@Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public SearchResultIconRow(@NonNull Context context, @Nullable AttributeSet attrs,
|
||||
int 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
|
||||
public void applyAdapterInfo(AdapterItemWithPayload<SearchTarget> adapterItemWithPayload) {
|
||||
if (mAdapterItem != null) {
|
||||
mAdapterItem.setSelectionHandler(null);
|
||||
}
|
||||
mAdapterItem = adapterItemWithPayload;
|
||||
SearchTarget payload = adapterItemWithPayload.getPayload();
|
||||
mPlugin = adapterItemWithPayload.getPlugin();
|
||||
protected void drawFocusHighlight(Canvas canvas) {
|
||||
mHighlightPaint.setColor(mHighlightColor);
|
||||
float r = Themes.getDialogCornerRadius(getContext());
|
||||
canvas.drawRoundRect(0, 0, getWidth(), getHeight(), r, r, mHighlightPaint);
|
||||
}
|
||||
|
||||
if (payload.mRemoteAction != null) {
|
||||
prepareUsingRemoteAction(payload.mRemoteAction,
|
||||
payload.bundle.getString(SearchTarget.REMOTE_ACTION_TOKEN),
|
||||
payload.bundle.getBoolean(SearchTarget.REMOTE_ACTION_SHOULD_START));
|
||||
} else {
|
||||
prepareUsingShortcutInfo(payload.shortcuts.get(0));
|
||||
|
||||
@Override
|
||||
public void applySearchTarget(SearchTarget searchTarget) {
|
||||
mSearchTarget = searchTarget;
|
||||
String type = searchTarget.getItemType();
|
||||
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));
|
||||
adapterItemWithPayload.setSelectionHandler(this::handleSelection);
|
||||
SearchEventTracker.INSTANCE.get(getContext()).registerWeakHandler(searchTarget, this);
|
||||
}
|
||||
|
||||
private void prepareUsingShortcutInfo(ShortcutInfo shortcutInfo) {
|
||||
mShortcutInfo = shortcutInfo;
|
||||
WorkspaceItemInfo workspaceItemInfo = new WorkspaceItemInfo(mShortcutInfo, getContext());
|
||||
WorkspaceItemInfo workspaceItemInfo = new WorkspaceItemInfo(shortcutInfo, getContext());
|
||||
applyFromWorkspaceItem(workspaceItemInfo);
|
||||
LauncherAppState launcherAppState = LauncherAppState.getInstance(getContext());
|
||||
MODEL_EXECUTOR.execute(() -> {
|
||||
launcherAppState.getIconCache().getShortcutIcon(workspaceItemInfo, mShortcutInfo);
|
||||
reapplyItemInfoAsync(workspaceItemInfo);
|
||||
});
|
||||
if (!loadIconFromResource()) {
|
||||
MODEL_EXECUTOR.execute(() -> {
|
||||
launcherAppState.getIconCache().getShortcutIcon(workspaceItemInfo, shortcutInfo);
|
||||
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);
|
||||
|
||||
applyFromRemoteActionInfo(itemInfo);
|
||||
UI_HELPER_EXECUTOR.post(() -> {
|
||||
// If the Drawable from the remote action is not AdaptiveBitmap, styling will not work.
|
||||
try (LauncherIcons li = LauncherIcons.obtain(getContext())) {
|
||||
Drawable d = itemInfo.getRemoteAction().getIcon().loadDrawable(getContext());
|
||||
itemInfo.bitmap = li.createBadgedIconBitmap(d, itemInfo.user,
|
||||
Build.VERSION.SDK_INT);
|
||||
reapplyItemInfoAsync(itemInfo);
|
||||
}
|
||||
});
|
||||
if (itemInfo.isEscapeHatch() || !loadIconFromResource()) {
|
||||
UI_HELPER_EXECUTOR.post(() -> {
|
||||
// If the Drawable from the remote action is not AdaptiveBitmap, styling will not
|
||||
// work.
|
||||
try (LauncherIcons li = LauncherIcons.obtain(getContext())) {
|
||||
Drawable d = itemInfo.getRemoteAction().getIcon().loadDrawable(getContext());
|
||||
BitmapInfo bitmap = li.createBadgedIconBitmap(d, itemInfo.user,
|
||||
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);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private boolean loadIconFromResource() {
|
||||
if (mCustomIconResId == 0) return false;
|
||||
setIcon(Launcher.getLauncher(getContext()).getDrawable(mCustomIconResId));
|
||||
return true;
|
||||
}
|
||||
|
||||
void reapplyItemInfoAsync(ItemInfoWithIcon itemInfoWithIcon) {
|
||||
|
@ -123,33 +182,15 @@ public class SearchResultIconRow extends DoubleShadowBubbleTextView implements
|
|||
}
|
||||
|
||||
@Override
|
||||
public Object[] getTargetInfo() {
|
||||
return mTargetInfo;
|
||||
}
|
||||
|
||||
private void handleSelection(int eventType) {
|
||||
public void handleSelection(int eventType) {
|
||||
ItemInfo itemInfo = (ItemInfo) getTag();
|
||||
Launcher launcher = Launcher.getLauncher(getContext());
|
||||
final SearchTargetEvent searchTargetEvent;
|
||||
if (itemInfo instanceof WorkspaceItemInfo) {
|
||||
ItemClickHandler.onClickAppShortcut(this, (WorkspaceItemInfo) itemInfo, launcher);
|
||||
searchTargetEvent = getSearchTargetEvent(SearchTarget.ItemType.SHORTCUT,
|
||||
eventType);
|
||||
searchTargetEvent.shortcut = mShortcutInfo;
|
||||
} else {
|
||||
RemoteActionItemInfo remoteItemInfo = (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);
|
||||
ItemClickHandler.onClickRemoteAction(launcher, (RemoteActionItemInfo) itemInfo);
|
||||
}
|
||||
SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(
|
||||
new SearchTargetEvent.Builder(mSearchTarget, eventType).build());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,9 +23,12 @@ import android.content.Intent;
|
|||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Process;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.ImageButton;
|
||||
|
@ -39,10 +42,10 @@ import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
|
|||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.allapps.AllAppsGridAdapter;
|
||||
import com.android.launcher3.allapps.search.AllAppsSearchBarController;
|
||||
import com.android.launcher3.util.Themes;
|
||||
import com.android.systemui.plugins.AllAppsSearchPlugin;
|
||||
import com.android.launcher3.allapps.search.SearchEventTracker;
|
||||
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.SearchTargetEvent;
|
||||
|
||||
|
@ -52,7 +55,9 @@ import java.util.ArrayList;
|
|||
* A view representing a single people search result in all apps
|
||||
*/
|
||||
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 mButtonSize;
|
||||
|
@ -60,9 +65,10 @@ public class SearchResultPeopleView extends LinearLayout implements
|
|||
private View mIconView;
|
||||
private TextView mTitleView;
|
||||
private ImageButton[] mProviderButtons = new ImageButton[3];
|
||||
private AllAppsSearchPlugin mPlugin;
|
||||
private Intent mIntent;
|
||||
private final Object[] mTargetInfo = createTargetInfo();
|
||||
|
||||
|
||||
private SearchTarget mSearchTarget;
|
||||
|
||||
public SearchResultPeopleView(Context context) {
|
||||
this(context, null, 0);
|
||||
|
@ -99,21 +105,19 @@ public class SearchResultPeopleView extends LinearLayout implements
|
|||
}
|
||||
|
||||
@Override
|
||||
public void applyAdapterInfo(
|
||||
AllAppsGridAdapter.AdapterItemWithPayload<Bundle> adapterItemWithPayload) {
|
||||
Bundle payload = adapterItemWithPayload.getPayload();
|
||||
mPlugin = adapterItemWithPayload.getPlugin();
|
||||
public void applySearchTarget(SearchTarget searchTarget) {
|
||||
mSearchTarget = searchTarget;
|
||||
Bundle payload = searchTarget.getExtras();
|
||||
mTitleView.setText(payload.getString("title"));
|
||||
mIntent = payload.getParcelable("intent");
|
||||
Bitmap icon = payload.getParcelable("icon");
|
||||
if (icon != null) {
|
||||
RoundedBitmapDrawable d = RoundedBitmapDrawableFactory.create(getResources(), icon);
|
||||
float radius = Themes.getDialogCornerRadius(getContext());
|
||||
d.setCornerRadius(radius);
|
||||
d.setBounds(0, 0, mIconSize, mIconSize);
|
||||
BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(),
|
||||
Bitmap.createScaledBitmap(icon, mIconSize, mIconSize, false));
|
||||
mIconView.setBackground(d);
|
||||
Bitmap contactIcon = payload.getParcelable("icon");
|
||||
try (LauncherIcons li = LauncherIcons.obtain(getContext())) {
|
||||
BitmapInfo badgeInfo = li.createBadgedIconBitmap(
|
||||
getAppIcon(mIntent.getPackage()), Process.myUserHandle(),
|
||||
Build.VERSION.SDK_INT);
|
||||
setIcon(li.badgeBitmap(roundBitmap(contactIcon), badgeInfo).icon, false);
|
||||
} catch (Exception e) {
|
||||
setIcon(contactIcon, true);
|
||||
}
|
||||
|
||||
ArrayList<Bundle> providers = payload.getParcelableArrayList("providers");
|
||||
|
@ -122,59 +126,80 @@ public class SearchResultPeopleView extends LinearLayout implements
|
|||
if (providers != null && i < providers.size()) {
|
||||
Bundle provider = providers.get(i);
|
||||
Intent intent = provider.getParcelable("intent");
|
||||
setupProviderButton(button, provider, intent, adapterItemWithPayload);
|
||||
String pkg = provider.getString("package_name");
|
||||
setupProviderButton(button, provider, intent);
|
||||
UI_HELPER_EXECUTOR.post(() -> {
|
||||
try {
|
||||
ApplicationInfo applicationInfo = mPackageManager.getApplicationInfo(
|
||||
pkg, 0);
|
||||
Drawable appIcon = applicationInfo.loadIcon(mPackageManager);
|
||||
String pkg = provider.getString("package_name");
|
||||
Drawable appIcon = getAppIcon(pkg);
|
||||
if (appIcon != null) {
|
||||
MAIN_EXECUTOR.post(() -> button.setImageDrawable(appIcon));
|
||||
} catch (PackageManager.NameNotFoundException ignored) {
|
||||
}
|
||||
|
||||
});
|
||||
button.setVisibility(VISIBLE);
|
||||
} else {
|
||||
button.setVisibility(GONE);
|
||||
}
|
||||
}
|
||||
adapterItemWithPayload.setSelectionHandler(this::handleSelection);
|
||||
SearchEventTracker.INSTANCE.get(getContext()).registerWeakHandler(searchTarget, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getTargetInfo() {
|
||||
return mTargetInfo;
|
||||
/**
|
||||
* Normalizes the bitmap to look like rounded App Icon
|
||||
* 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,
|
||||
AllAppsGridAdapter.AdapterItem adapterItem) {
|
||||
private void setIcon(Bitmap icon, Boolean round) {
|
||||
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());
|
||||
button.setOnClickListener(b -> {
|
||||
launcher.startActivitySafely(b, intent, null);
|
||||
SearchTargetEvent searchTargetEvent = getSearchTargetEvent(
|
||||
SearchTarget.ItemType.PEOPLE,
|
||||
SearchTargetEvent.CHILD_SELECT);
|
||||
searchTargetEvent.bundle = new Bundle();
|
||||
searchTargetEvent.bundle.putParcelable("intent", mIntent);
|
||||
searchTargetEvent.bundle.putBundle("provider", provider);
|
||||
if (mPlugin != null) {
|
||||
mPlugin.notifySearchTargetEvent(searchTargetEvent);
|
||||
}
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putBundle("provider", provider);
|
||||
SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(
|
||||
new SearchTargetEvent.Builder(mSearchTarget,
|
||||
SearchTargetEvent.CHILD_SELECT).setExtras(bundle).build());
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
private void handleSelection(int eventType) {
|
||||
@Override
|
||||
public void handleSelection(int eventType) {
|
||||
if (mIntent != null) {
|
||||
Launcher launcher = Launcher.getLauncher(getContext());
|
||||
launcher.startActivitySafely(this, mIntent, null);
|
||||
SearchTargetEvent searchTargetEvent = getSearchTargetEvent(SearchTarget.ItemType.PEOPLE,
|
||||
eventType);
|
||||
searchTargetEvent.bundle = new Bundle();
|
||||
searchTargetEvent.bundle.putParcelable("intent", mIntent);
|
||||
if (mPlugin != null) {
|
||||
mPlugin.notifySearchTargetEvent(searchTargetEvent);
|
||||
}
|
||||
SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(
|
||||
new SearchTargetEvent.Builder(mSearchTarget, eventType).build());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,11 @@ import android.content.Context;
|
|||
import android.content.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
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.net.Uri;
|
||||
import android.os.Bundle;
|
||||
|
@ -36,20 +41,28 @@ import androidx.annotation.Nullable;
|
|||
import com.android.launcher3.DeviceProfile;
|
||||
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.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.SearchTargetEvent;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URL;
|
||||
import java.net.URLConnection;
|
||||
|
||||
/**
|
||||
* A View representing a PlayStore item.
|
||||
*/
|
||||
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 View mIconView;
|
||||
private TextView mTitleView;
|
||||
|
@ -57,8 +70,8 @@ public class SearchResultPlayItem extends LinearLayout implements
|
|||
private Button mPreviewButton;
|
||||
private String mPackageName;
|
||||
private boolean mIsInstantGame;
|
||||
private AllAppsSearchPlugin mPlugin;
|
||||
private final Object[] mTargetInfo = createTargetInfo();
|
||||
|
||||
private SearchTarget mSearchTarget;
|
||||
|
||||
|
||||
public SearchResultPlayItem(Context context) {
|
||||
|
@ -91,14 +104,35 @@ public class SearchResultPlayItem extends LinearLayout implements
|
|||
iconParams.height = mDeviceProfile.allAppsIconSizePx;
|
||||
iconParams.width = mDeviceProfile.allAppsIconSizePx;
|
||||
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
|
||||
public void applyAdapterInfo(AdapterItemWithPayload<Bundle> adapterItemWithPayload) {
|
||||
Bundle bundle = adapterItemWithPayload.getPayload();
|
||||
mPlugin = adapterItemWithPayload.getPlugin();
|
||||
adapterItemWithPayload.setSelectionHandler(this::handleSelection);
|
||||
public void applySearchTarget(SearchTarget searchTarget) {
|
||||
mSearchTarget = searchTarget;
|
||||
Bundle bundle = searchTarget.getExtras();
|
||||
SearchEventTracker.INSTANCE.get(getContext()).registerWeakHandler(searchTarget, this);
|
||||
if (bundle.getString("package", "").equals(mPackageName)) {
|
||||
return;
|
||||
}
|
||||
|
@ -109,17 +143,19 @@ public class SearchResultPlayItem extends LinearLayout implements
|
|||
// TODO: Should use a generic type to get values b/165320033
|
||||
showIfNecessary(mDetailViews[0], bundle.getString("price"));
|
||||
showIfNecessary(mDetailViews[1], bundle.getString("rating"));
|
||||
showIfNecessary(mDetailViews[2], bundle.getString("category"));
|
||||
|
||||
mIconView.setBackgroundResource(R.drawable.ic_deepshortcut_placeholder);
|
||||
UI_HELPER_EXECUTOR.execute(() -> {
|
||||
try {
|
||||
// TODO: Handle caching
|
||||
URL url = new URL(bundle.getString("icon_url"));
|
||||
Bitmap bitmap = BitmapFactory.decodeStream(url.openStream());
|
||||
BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(),
|
||||
URLConnection con = url.openConnection();
|
||||
// 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,
|
||||
mDeviceProfile.allAppsIconSizePx, false));
|
||||
mDeviceProfile.allAppsIconSizePx, false)));
|
||||
mIconView.post(() -> mIconView.setBackground(bitmapDrawable));
|
||||
} catch (IOException e) {
|
||||
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) {
|
||||
if (string == null || string.isEmpty()) {
|
||||
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;
|
||||
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(
|
||||
"https://play.google.com/store/apps/details?id="
|
||||
|
@ -167,12 +199,7 @@ public class SearchResultPlayItem extends LinearLayout implements
|
|||
}
|
||||
|
||||
private void logSearchEvent(int eventType) {
|
||||
SearchTargetEvent searchTargetEvent = getSearchTargetEvent(
|
||||
SearchTarget.ItemType.PLAY_RESULTS, eventType);
|
||||
searchTargetEvent.bundle = new Bundle();
|
||||
searchTargetEvent.bundle.putString("package_name", mPackageName);
|
||||
if (mPlugin != null) {
|
||||
mPlugin.notifySearchTargetEvent(searchTargetEvent);
|
||||
}
|
||||
SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(
|
||||
new SearchTargetEvent.Builder(mSearchTarget, eventType).build());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -21,14 +21,16 @@ import android.widget.TextView;
|
|||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.allapps.AllAppsGridAdapter;
|
||||
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
|
||||
*/
|
||||
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) {
|
||||
super(context);
|
||||
}
|
||||
|
@ -43,8 +45,8 @@ public class SearchSectionHeaderView extends TextView implements
|
|||
}
|
||||
|
||||
@Override
|
||||
public void applyAdapterInfo(AllAppsGridAdapter.AdapterItemWithPayload<String> adapterItem) {
|
||||
String title = adapterItem.getPayload();
|
||||
public void applySearchTarget(SearchTarget searchTarget) {
|
||||
String title = searchTarget.getExtras().getString("title");
|
||||
if (title == null || !title.isEmpty()) {
|
||||
setText(title);
|
||||
setVisibility(VISIBLE);
|
||||
|
@ -52,9 +54,4 @@ public class SearchSectionHeaderView extends TextView implements
|
|||
setVisibility(INVISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getTargetInfo() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -29,9 +29,8 @@ import androidx.annotation.Nullable;
|
|||
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.allapps.AllAppsGridAdapter;
|
||||
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.SearchTargetEvent;
|
||||
|
||||
|
@ -41,14 +40,16 @@ import java.util.ArrayList;
|
|||
* A row of tappable TextViews with a breadcrumb for settings search.
|
||||
*/
|
||||
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 mDescriptionView;
|
||||
private TextView mBreadcrumbsView;
|
||||
private Intent mIntent;
|
||||
private AllAppsSearchPlugin mPlugin;
|
||||
private final Object[] mTargetInfo = createTargetInfo();
|
||||
private SearchTarget mSearchTarget;
|
||||
|
||||
|
||||
public SearchSettingsRowView(@NonNull Context context) {
|
||||
|
@ -75,10 +76,9 @@ public class SearchSettingsRowView extends LinearLayout implements
|
|||
}
|
||||
|
||||
@Override
|
||||
public void applyAdapterInfo(
|
||||
AllAppsGridAdapter.AdapterItemWithPayload<Bundle> adapterItemWithPayload) {
|
||||
Bundle bundle = adapterItemWithPayload.getPayload();
|
||||
mPlugin = adapterItemWithPayload.getPlugin();
|
||||
public void applySearchTarget(SearchTarget searchTarget) {
|
||||
mSearchTarget = searchTarget;
|
||||
Bundle bundle = searchTarget.getExtras();
|
||||
mIntent = bundle.getParcelable("intent");
|
||||
showIfAvailable(mTitleView, bundle.getString("title"));
|
||||
showIfAvailable(mDescriptionView, bundle.getString("description"));
|
||||
|
@ -86,12 +86,7 @@ public class SearchSettingsRowView extends LinearLayout implements
|
|||
//TODO: implement RTL friendly breadcrumbs view
|
||||
showIfAvailable(mBreadcrumbsView, breadcrumbs != null
|
||||
? String.join(" > ", breadcrumbs) : null);
|
||||
adapterItemWithPayload.setSelectionHandler(this::handleSelection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getTargetInfo() {
|
||||
return mTargetInfo;
|
||||
SearchEventTracker.INSTANCE.get(getContext()).registerWeakHandler(searchTarget, this);
|
||||
}
|
||||
|
||||
private void showIfAvailable(TextView view, @Nullable String string) {
|
||||
|
@ -108,19 +103,15 @@ public class SearchSettingsRowView extends LinearLayout implements
|
|||
handleSelection(SearchTargetEvent.SELECT);
|
||||
}
|
||||
|
||||
private void handleSelection(int eventType) {
|
||||
@Override
|
||||
public void handleSelection(int eventType) {
|
||||
if (mIntent == null) return;
|
||||
// TODO: create ItemInfo object and then use it to call startActivityForResult for proper
|
||||
// WW logging
|
||||
Launcher launcher = Launcher.getLauncher(getContext());
|
||||
launcher.startActivityForResult(mIntent, 0);
|
||||
|
||||
SearchTargetEvent searchTargetEvent = getSearchTargetEvent(
|
||||
SearchTarget.ItemType.SETTINGS_ROW, eventType);
|
||||
searchTargetEvent.bundle = new Bundle();
|
||||
searchTargetEvent.bundle.putParcelable("intent", mIntent);
|
||||
if (mPlugin != null) {
|
||||
mPlugin.notifySearchTargetEvent(searchTargetEvent);
|
||||
}
|
||||
SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(
|
||||
new SearchTargetEvent.Builder(mSearchTarget, eventType).build());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -15,6 +15,9 @@
|
|||
*/
|
||||
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.Intent;
|
||||
import android.graphics.Bitmap;
|
||||
|
@ -26,14 +29,13 @@ import androidx.core.graphics.drawable.RoundedBitmapDrawable;
|
|||
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
|
||||
|
||||
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.SearchEventTracker;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.model.data.RemoteActionItemInfo;
|
||||
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
||||
import com.android.launcher3.touch.ItemClickHandler;
|
||||
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.SearchTargetEvent;
|
||||
|
||||
|
@ -41,11 +43,12 @@ import com.android.systemui.plugins.shared.SearchTargetEvent;
|
|||
* A view representing a high confidence app search result that includes shortcuts
|
||||
*/
|
||||
public class ThumbnailSearchResultView extends androidx.appcompat.widget.AppCompatImageView
|
||||
implements AllAppsSearchBarController.PayloadResultHandler<SearchTarget> {
|
||||
implements AllAppsSearchBarController.SearchTargetHandler {
|
||||
|
||||
private final Object[] mTargetInfo = createTargetInfo();
|
||||
AllAppsSearchPlugin mPlugin;
|
||||
int mPosition;
|
||||
public static final String TARGET_TYPE_SCREENSHOT = "screenshot";
|
||||
public static final String TARGET_TYPE_SCREENSHOT_LEGACY = "screenshot_legacy";
|
||||
|
||||
private SearchTarget mSearchTarget;
|
||||
|
||||
public ThumbnailSearchResultView(Context context) {
|
||||
super(context);
|
||||
|
@ -59,7 +62,8 @@ public class ThumbnailSearchResultView extends androidx.appcompat.widget.AppComp
|
|||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
private void handleSelection(int eventType) {
|
||||
@Override
|
||||
public void handleSelection(int eventType) {
|
||||
Launcher launcher = Launcher.getLauncher(getContext());
|
||||
ItemInfo itemInfo = (ItemInfo) getTag();
|
||||
if (itemInfo instanceof RemoteActionItemInfo) {
|
||||
|
@ -68,33 +72,29 @@ public class ThumbnailSearchResultView extends androidx.appcompat.widget.AppComp
|
|||
} else {
|
||||
ItemClickHandler.onClickAppShortcut(this, (WorkspaceItemInfo) itemInfo, launcher);
|
||||
}
|
||||
if (mPlugin != null) {
|
||||
SearchTargetEvent event = getSearchTargetEvent(
|
||||
SearchTarget.ItemType.SCREENSHOT, eventType);
|
||||
mPlugin.notifySearchTargetEvent(event);
|
||||
}
|
||||
SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(
|
||||
new SearchTargetEvent.Builder(mSearchTarget, eventType).build());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void applyAdapterInfo(AdapterItemWithPayload<SearchTarget> adapterItem) {
|
||||
Launcher launcher = Launcher.getLauncher(getContext());
|
||||
mPosition = adapterItem.position;
|
||||
|
||||
SearchTarget target = adapterItem.getPayload();
|
||||
public void applySearchTarget(SearchTarget target) {
|
||||
mSearchTarget = target;
|
||||
Bitmap bitmap;
|
||||
if (target.mRemoteAction != null) {
|
||||
RemoteActionItemInfo itemInfo = new RemoteActionItemInfo(target.mRemoteAction,
|
||||
target.bundle.getString(SearchTarget.REMOTE_ACTION_TOKEN),
|
||||
target.bundle.getBoolean(SearchTarget.REMOTE_ACTION_SHOULD_START));
|
||||
ItemClickHandler.onClickRemoteAction(launcher, itemInfo);
|
||||
bitmap = ((BitmapDrawable) target.mRemoteAction.getIcon()
|
||||
if (target.getRemoteAction() != null) {
|
||||
RemoteActionItemInfo itemInfo = new RemoteActionItemInfo(target.getRemoteAction(),
|
||||
target.getExtras().getString(REMOTE_ACTION_TOKEN),
|
||||
target.getExtras().getBoolean(REMOTE_ACTION_SHOULD_START));
|
||||
bitmap = ((BitmapDrawable) target.getRemoteAction().getIcon()
|
||||
.loadDrawable(getContext())).getBitmap();
|
||||
setTag(itemInfo);
|
||||
// crop
|
||||
bitmap = Bitmap.createBitmap(bitmap, 0,
|
||||
bitmap.getHeight() / 2 - bitmap.getWidth() / 2,
|
||||
bitmap.getWidth(), bitmap.getWidth());
|
||||
} else {
|
||||
bitmap = (Bitmap) target.bundle.getParcelable("bitmap");
|
||||
bitmap = (Bitmap) target.getExtras().getParcelable("bitmap");
|
||||
WorkspaceItemInfo itemInfo = new WorkspaceItemInfo();
|
||||
itemInfo.intent = new Intent(Intent.ACTION_VIEW)
|
||||
.setData(Uri.parse(target.bundle.getString("uri")))
|
||||
.setData(Uri.parse(target.getExtras().getString("uri")))
|
||||
.setType("image/*")
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
setTag(itemInfo);
|
||||
|
@ -103,12 +103,6 @@ public class ThumbnailSearchResultView extends androidx.appcompat.widget.AppComp
|
|||
drawable.setCornerRadius(Themes.getDialogCornerRadius(getContext()));
|
||||
setImageDrawable(drawable);
|
||||
setOnClickListener(v -> handleSelection(SearchTargetEvent.SELECT));
|
||||
mPlugin = adapterItem.getPlugin();
|
||||
adapterItem.setSelectionHandler(this::handleSelection);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object[] getTargetInfo() {
|
||||
return mTargetInfo;
|
||||
SearchEventTracker.INSTANCE.get(getContext()).registerWeakHandler(target, this);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@
|
|||
package com.android.systemui.plugins;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.Bundle;
|
||||
import android.os.CancellationSignal;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.systemui.plugins.annotations.ProvidesInterface;
|
||||
|
@ -32,7 +34,7 @@ import java.util.function.Consumer;
|
|||
@ProvidesInterface(action = AllAppsSearchPlugin.ACTION, version = AllAppsSearchPlugin.VERSION)
|
||||
public interface AllAppsSearchPlugin extends Plugin {
|
||||
String ACTION = "com.android.systemui.action.PLUGIN_ALL_APPS_SEARCH_ACTIONS";
|
||||
int VERSION = 7;
|
||||
int VERSION = 8;
|
||||
|
||||
void setup(Activity activity, View view);
|
||||
|
||||
|
@ -49,10 +51,21 @@ public interface AllAppsSearchPlugin extends Plugin {
|
|||
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 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
|
||||
|
|
|
@ -16,8 +16,10 @@
|
|||
package com.android.systemui.plugins.shared;
|
||||
|
||||
import android.app.RemoteAction;
|
||||
import android.content.ComponentName;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.os.Bundle;
|
||||
import android.os.UserHandle;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
@ -26,139 +28,145 @@ import java.util.List;
|
|||
*/
|
||||
public class SearchTarget implements Comparable<SearchTarget> {
|
||||
|
||||
private final String mItemId;
|
||||
private final String mItemType;
|
||||
private final float mScore;
|
||||
|
||||
/**
|
||||
* A bundle key for boolean value of whether remote action should be started in launcher or not
|
||||
*/
|
||||
public static final String REMOTE_ACTION_SHOULD_START = "should_start_for_result";
|
||||
public static final String REMOTE_ACTION_TOKEN = "action_token";
|
||||
private final ComponentName mComponentName;
|
||||
private final UserHandle mUserHandle;
|
||||
private final List<ShortcutInfo> mShortcutInfos;
|
||||
//TODO: (sfufa) replace with a list of a custom type
|
||||
private final RemoteAction mRemoteAction;
|
||||
private final Bundle mExtras;
|
||||
|
||||
|
||||
public enum ViewType {
|
||||
|
||||
/**
|
||||
* Consists of N number of icons. (N: launcher column count)
|
||||
*/
|
||||
TOP_HIT(0),
|
||||
|
||||
/**
|
||||
* Consists of 1 icon and two subsidiary icons.
|
||||
*/
|
||||
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() {
|
||||
return mId;
|
||||
}
|
||||
private SearchTarget(String itemId, String itemType, float score,
|
||||
ComponentName componentName, UserHandle userHandle, List<ShortcutInfo> shortcutInfos,
|
||||
RemoteAction remoteAction, Bundle extras) {
|
||||
mItemId = itemId;
|
||||
mItemType = itemType;
|
||||
mScore = score;
|
||||
mComponentName = componentName;
|
||||
mUserHandle = userHandle;
|
||||
mShortcutInfos = shortcutInfos;
|
||||
mExtras = extras;
|
||||
mRemoteAction = remoteAction;
|
||||
}
|
||||
|
||||
public enum ItemType {
|
||||
PLAY_RESULTS(0, "Play Store", ViewType.DETAIL),
|
||||
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() {
|
||||
return mViewType;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return mTitle;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return mId;
|
||||
}
|
||||
public String getItemId() {
|
||||
return mItemId;
|
||||
}
|
||||
|
||||
public ItemType type;
|
||||
public List<ShortcutInfo> shortcuts;
|
||||
public Bundle bundle;
|
||||
public float score;
|
||||
public String mSessionId;
|
||||
public RemoteAction mRemoteAction;
|
||||
public String getItemType() {
|
||||
return mItemType;
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructor to create the search target. Bundle is currently temporary to hold
|
||||
* search target primitives that cannot be expressed as java primitive objects
|
||||
* or AOSP native objects.
|
||||
*/
|
||||
public SearchTarget(ItemType itemType, List<ShortcutInfo> shortcuts,
|
||||
Bundle bundle, float score, String sessionId) {
|
||||
this.type = itemType;
|
||||
this.shortcuts = shortcuts;
|
||||
this.bundle = bundle;
|
||||
this.score = score;
|
||||
this.mSessionId = sessionId;
|
||||
public ComponentName getComponentName() {
|
||||
return mComponentName;
|
||||
}
|
||||
|
||||
public UserHandle getUserHandle() {
|
||||
return mUserHandle;
|
||||
}
|
||||
|
||||
public float getScore() {
|
||||
return mScore;
|
||||
}
|
||||
|
||||
public List<ShortcutInfo> getShortcutInfos() {
|
||||
return mShortcutInfos;
|
||||
}
|
||||
|
||||
public Bundle getExtras() {
|
||||
return mExtras;
|
||||
}
|
||||
|
||||
public RemoteAction getRemoteAction() {
|
||||
return mRemoteAction;
|
||||
}
|
||||
|
||||
@Override
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,32 +15,76 @@
|
|||
*/
|
||||
package com.android.systemui.plugins.shared;
|
||||
|
||||
import android.app.RemoteAction;
|
||||
import android.content.pm.ShortcutInfo;
|
||||
import android.os.Bundle;
|
||||
|
||||
/**
|
||||
* Event used for the feedback loop to the plugin. (and future aiai)
|
||||
*/
|
||||
public class SearchTargetEvent {
|
||||
public static final int POSITION_NONE = -1;
|
||||
|
||||
public static final int SELECT = 0;
|
||||
public static final int QUICK_SELECT = 1;
|
||||
public static final int LONG_PRESS = 2;
|
||||
public static final int CHILD_SELECT = 3;
|
||||
|
||||
public SearchTarget.ItemType type;
|
||||
public ShortcutInfo shortcut;
|
||||
public RemoteAction remoteAction;
|
||||
public int eventType;
|
||||
public Bundle bundle;
|
||||
public int index;
|
||||
public String sessionIdentifier;
|
||||
private final SearchTarget mSearchTarget;
|
||||
private final int mEventType;
|
||||
private final int mShortcutPosition;
|
||||
private final Bundle mExtras;
|
||||
|
||||
public SearchTargetEvent(SearchTarget.ItemType itemType, int eventType, int index,
|
||||
String sessionId) {
|
||||
this.type = itemType;
|
||||
this.eventType = eventType;
|
||||
this.index = index;
|
||||
this.sessionIdentifier = sessionId;
|
||||
public SearchTargetEvent(SearchTarget searchTarget, int eventType, int shortcutPosition,
|
||||
Bundle extras) {
|
||||
mSearchTarget = searchTarget;
|
||||
mEventType = eventType;
|
||||
mShortcutPosition = shortcutPosition;
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<appwidget-provider
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:minWidth="180dp"
|
||||
android:minHeight="110dp"
|
||||
android:minWidth="1dp"
|
||||
android:minHeight="1dp"
|
||||
android:updatePeriodMillis="86400000"
|
||||
android:initialLayout="@layout/test_layout_appwidget_blue"
|
||||
android:resizeMode="horizontal|vertical"
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<appwidget-provider
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:minWidth="180dp"
|
||||
android:minHeight="110dp"
|
||||
android:minWidth="1dp"
|
||||
android:minHeight="1dp"
|
||||
android:updatePeriodMillis="86400000"
|
||||
android:initialLayout="@layout/test_layout_appwidget_red"
|
||||
android:resizeMode="horizontal|vertical"
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<appwidget-provider
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:minWidth="180dp"
|
||||
android:minHeight="110dp"
|
||||
android:minWidth="1dp"
|
||||
android:minHeight="1dp"
|
||||
android:updatePeriodMillis="86400000"
|
||||
android:initialLayout="@layout/test_layout_appwidget_blue"
|
||||
android:configure="com.android.launcher3.testcomponent.WidgetConfigActivity"
|
||||
|
|
|
@ -88,10 +88,6 @@ public class Background extends LauncherInstrumentation.VisibleContainer {
|
|||
? LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE
|
||||
: LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER;
|
||||
|
||||
// b/156044202
|
||||
mLauncher.log("Hierarchy before swiping up to overview:");
|
||||
mLauncher.dumpViewHierarchy();
|
||||
|
||||
mLauncher.sendPointer(
|
||||
downTime, downTime, MotionEvent.ACTION_DOWN, start, gestureScope);
|
||||
mLauncher.executeAndWaitForLauncherEvent(
|
||||
|
|
Loading…
Reference in New Issue