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",
|
name: "launcher_log_protos_lite",
|
||||||
srcs: [
|
srcs: [
|
||||||
"protos/*.proto",
|
"protos/*.proto",
|
||||||
"proto_overrides/*.proto",
|
|
||||||
],
|
],
|
||||||
sdk_version: "current",
|
sdk_version: "current",
|
||||||
proto: {
|
proto: {
|
||||||
type: "lite",
|
type: "lite",
|
||||||
local_include_dirs:[
|
local_include_dirs:[
|
||||||
"protos",
|
"protos",
|
||||||
"proto_overrides",
|
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
static_libs: ["libprotobuf-java-lite"],
|
static_libs: ["libprotobuf-java-lite"],
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
with some minor changed based on the derivative app.
|
with some minor changed based on the derivative app.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.CALL_PHONE" />
|
<uses-permission android:name="android.permission.CALL_PHONE" />
|
||||||
<uses-permission android:name="android.permission.SET_WALLPAPER" />
|
<uses-permission android:name="android.permission.SET_WALLPAPER" />
|
||||||
<uses-permission android:name="android.permission.SET_WALLPAPER_HINTS" />
|
<uses-permission android:name="android.permission.SET_WALLPAPER_HINTS" />
|
||||||
|
|
80
buglist.txt
80
buglist.txt
|
@ -1,38 +1,42 @@
|
||||||
160759508
|
171450807
|
||||||
144170434
|
170675311
|
||||||
144170434
|
170338029
|
||||||
161801331
|
170338170
|
||||||
162564471
|
160544577
|
||||||
160361464
|
171171594
|
||||||
161901771
|
170488559
|
||||||
160568387
|
171131394
|
||||||
162623012
|
171131394
|
||||||
162012217
|
171026321
|
||||||
160718310
|
170648272
|
||||||
160361464
|
170752716
|
||||||
160718310
|
170611866
|
||||||
162012217
|
170702596
|
||||||
160748731
|
170487752
|
||||||
154951045
|
170665892
|
||||||
162861289
|
168608912
|
||||||
149934536
|
170636685
|
||||||
149934536
|
169771796
|
||||||
144170434
|
141126144
|
||||||
144170434
|
166614700
|
||||||
162454040
|
168805872
|
||||||
161273376
|
170263425
|
||||||
149934536
|
169221288
|
||||||
149934536
|
143965596
|
||||||
158701272
|
169221287
|
||||||
162871508
|
167259591
|
||||||
149934536
|
156044202
|
||||||
161939759
|
169438169
|
||||||
154964045
|
164926736
|
||||||
161536946
|
168653219
|
||||||
149934536
|
169963211
|
||||||
161685099
|
170121063
|
||||||
162812884
|
169988381
|
||||||
162480567
|
169980192
|
||||||
162480567
|
169221288
|
||||||
149934536
|
169385783
|
||||||
149934536
|
168167693
|
||||||
|
169796517
|
||||||
|
169330678
|
||||||
|
168818961
|
||||||
|
168608912
|
||||||
|
|
|
@ -1,24 +1,39 @@
|
||||||
144170434
|
141126144
|
||||||
149934536
|
143965596
|
||||||
154951045
|
156044202
|
||||||
154964045
|
160544577
|
||||||
158701272
|
164926736
|
||||||
160361464
|
166614700
|
||||||
160568387
|
167259591
|
||||||
160718310
|
168167693
|
||||||
160748731
|
168608912
|
||||||
160759508
|
168653219
|
||||||
161273376
|
168805872
|
||||||
161536946
|
168818961
|
||||||
161685099
|
169221287
|
||||||
161801331
|
169221288
|
||||||
161901771
|
169330678
|
||||||
161939759
|
169385783
|
||||||
162012217
|
169438169
|
||||||
162454040
|
169771796
|
||||||
162480567
|
169796517
|
||||||
162564471
|
169963211
|
||||||
162623012
|
169980192
|
||||||
162812884
|
169988381
|
||||||
162861289
|
170121063
|
||||||
162871508
|
170263425
|
||||||
|
170338029
|
||||||
|
170338170
|
||||||
|
170487752
|
||||||
|
170488559
|
||||||
|
170611866
|
||||||
|
170636685
|
||||||
|
170648272
|
||||||
|
170665892
|
||||||
|
170675311
|
||||||
|
170702596
|
||||||
|
170752716
|
||||||
|
171026321
|
||||||
|
171131394
|
||||||
|
171171594
|
||||||
|
171450807
|
||||||
|
|
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 {
|
message TouchInteractionServiceProto {
|
||||||
|
|
||||||
optional bool service_connected = 1;
|
optional bool service_connected = 1;
|
||||||
|
optional OverviewComponentObserverProto overview_component_obvserver = 2;
|
||||||
|
optional InputConsumerProto input_consumer = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message OverviewComponentObserverProto {
|
||||||
|
|
||||||
|
optional bool overview_activity_started = 1;
|
||||||
|
optional bool overview_activity_resumed = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message InputConsumerProto {
|
||||||
|
|
||||||
|
optional string name = 1;
|
||||||
|
optional SwipeHandlerProto swipe_handler = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SwipeHandlerProto {
|
||||||
|
|
||||||
|
optional GestureStateProto gesture_state = 1;
|
||||||
|
optional bool is_recents_attached_to_app_window = 2;
|
||||||
|
optional int32 scroll_offset = 3;
|
||||||
|
// Swipe up progress from 0 (app) to 1 (overview); can be > 1 if swiping past overview.
|
||||||
|
optional float app_to_overview_progress = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GestureStateProto {
|
||||||
|
|
||||||
|
optional GestureEndTarget endTarget = 1 [default = UNSET];
|
||||||
|
|
||||||
|
enum GestureEndTarget {
|
||||||
|
UNSET = 0;
|
||||||
|
HOME = 1;
|
||||||
|
RECENTS = 2;
|
||||||
|
NEW_TASK = 3;
|
||||||
|
LAST_TASK = 4;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,8 @@
|
||||||
<dimen name="recents_empty_message_text_size">16sp</dimen>
|
<dimen name="recents_empty_message_text_size">16sp</dimen>
|
||||||
<dimen name="recents_empty_message_text_padding">16dp</dimen>
|
<dimen name="recents_empty_message_text_padding">16dp</dimen>
|
||||||
|
|
||||||
|
<dimen name="max_shadow_radius">5dp</dimen>
|
||||||
|
|
||||||
<!-- Total space (start + end) between the task card and the edge of the screen
|
<!-- Total space (start + end) between the task card and the edge of the screen
|
||||||
in various configurations -->
|
in various configurations -->
|
||||||
<dimen name="task_card_vert_space">40dp</dimen>
|
<dimen name="task_card_vert_space">40dp</dimen>
|
||||||
|
|
|
@ -16,14 +16,10 @@
|
||||||
|
|
||||||
package com.android.launcher3;
|
package com.android.launcher3;
|
||||||
|
|
||||||
import static com.android.launcher3.LauncherState.NORMAL;
|
|
||||||
import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
|
import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
|
||||||
import static com.android.launcher3.anim.Interpolators.LINEAR;
|
import static com.android.launcher3.anim.Interpolators.LINEAR;
|
||||||
import static com.android.quickstep.TaskViewUtils.createRecentsWindowAnimator;
|
|
||||||
import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch;
|
import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch;
|
||||||
|
|
||||||
import android.animation.Animator;
|
|
||||||
import android.animation.AnimatorListenerAdapter;
|
|
||||||
import android.animation.AnimatorSet;
|
import android.animation.AnimatorSet;
|
||||||
import android.animation.ObjectAnimator;
|
import android.animation.ObjectAnimator;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
@ -32,11 +28,8 @@ import android.view.View;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
import com.android.quickstep.TaskViewUtils;
|
||||||
import com.android.launcher3.anim.Interpolators;
|
|
||||||
import com.android.launcher3.anim.PendingAnimation;
|
|
||||||
import com.android.quickstep.views.RecentsView;
|
import com.android.quickstep.views.RecentsView;
|
||||||
import com.android.quickstep.views.TaskView;
|
|
||||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -53,60 +46,16 @@ public final class LauncherAppTransitionManagerImpl extends QuickstepAppTransiti
|
||||||
protected boolean isLaunchingFromRecents(@NonNull View v,
|
protected boolean isLaunchingFromRecents(@NonNull View v,
|
||||||
@Nullable RemoteAnimationTargetCompat[] targets) {
|
@Nullable RemoteAnimationTargetCompat[] targets) {
|
||||||
return mLauncher.getStateManager().getState().overviewUi
|
return mLauncher.getStateManager().getState().overviewUi
|
||||||
&& findTaskViewToLaunch(mLauncher, v, targets) != null;
|
&& findTaskViewToLaunch(mLauncher.getOverviewPanel(), v, targets) != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
|
protected void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
|
||||||
@NonNull RemoteAnimationTargetCompat[] appTargets,
|
@NonNull RemoteAnimationTargetCompat[] appTargets,
|
||||||
@NonNull RemoteAnimationTargetCompat[] wallpaperTargets, boolean launcherClosing) {
|
@NonNull RemoteAnimationTargetCompat[] wallpaperTargets, boolean launcherClosing) {
|
||||||
RecentsView recentsView = mLauncher.getOverviewPanel();
|
TaskViewUtils.composeRecentsLaunchAnimator(anim, v, appTargets, wallpaperTargets,
|
||||||
boolean skipLauncherChanges = !launcherClosing;
|
launcherClosing, mLauncher.getStateManager(), mLauncher.getOverviewPanel(),
|
||||||
|
mLauncher.getDepthController());
|
||||||
TaskView taskView = findTaskViewToLaunch(mLauncher, v, appTargets);
|
|
||||||
PendingAnimation pa = new PendingAnimation(RECENTS_LAUNCH_DURATION);
|
|
||||||
createRecentsWindowAnimator(taskView, skipLauncherChanges, appTargets, wallpaperTargets,
|
|
||||||
mLauncher.getDepthController(), pa);
|
|
||||||
anim.play(pa.buildAnim());
|
|
||||||
|
|
||||||
Animator childStateAnimation = null;
|
|
||||||
// Found a visible recents task that matches the opening app, lets launch the app from there
|
|
||||||
Animator launcherAnim;
|
|
||||||
final AnimatorListenerAdapter windowAnimEndListener;
|
|
||||||
if (launcherClosing) {
|
|
||||||
launcherAnim = recentsView.createAdjacentPageAnimForTaskLaunch(taskView);
|
|
||||||
launcherAnim.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR);
|
|
||||||
launcherAnim.setDuration(RECENTS_LAUNCH_DURATION);
|
|
||||||
|
|
||||||
// Make sure recents gets fixed up by resetting task alphas and scales, etc.
|
|
||||||
windowAnimEndListener = new AnimatorListenerAdapter() {
|
|
||||||
@Override
|
|
||||||
public void onAnimationEnd(Animator animation) {
|
|
||||||
mLauncher.getStateManager().moveToRestState();
|
|
||||||
mLauncher.getStateManager().reapplyState();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else {
|
|
||||||
AnimatorPlaybackController controller =
|
|
||||||
mLauncher.getStateManager().createAnimationToNewWorkspace(NORMAL,
|
|
||||||
RECENTS_LAUNCH_DURATION);
|
|
||||||
controller.dispatchOnStart();
|
|
||||||
childStateAnimation = controller.getTarget();
|
|
||||||
launcherAnim = controller.getAnimationPlayer().setDuration(RECENTS_LAUNCH_DURATION);
|
|
||||||
windowAnimEndListener = new AnimatorListenerAdapter() {
|
|
||||||
@Override
|
|
||||||
public void onAnimationEnd(Animator animation) {
|
|
||||||
mLauncher.getStateManager().goToState(NORMAL, false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
anim.play(launcherAnim);
|
|
||||||
|
|
||||||
// Set the current animation first, before adding windowAnimEndListener. Setting current
|
|
||||||
// animation adds some listeners which need to be called before windowAnimEndListener
|
|
||||||
// (the ordering of listeners matter in this case).
|
|
||||||
mLauncher.getStateManager().setCurrentAnimation(anim, childStateAnimation);
|
|
||||||
anim.addListener(windowAnimEndListener);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -153,6 +153,7 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
|
||||||
private final float mContentTransY;
|
private final float mContentTransY;
|
||||||
private final float mWorkspaceTransY;
|
private final float mWorkspaceTransY;
|
||||||
private final float mClosingWindowTransY;
|
private final float mClosingWindowTransY;
|
||||||
|
private final float mMaxShadowRadius;
|
||||||
|
|
||||||
private DeviceProfile mDeviceProfile;
|
private DeviceProfile mDeviceProfile;
|
||||||
|
|
||||||
|
@ -186,6 +187,7 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
|
||||||
mContentTransY = res.getDimensionPixelSize(R.dimen.content_trans_y);
|
mContentTransY = res.getDimensionPixelSize(R.dimen.content_trans_y);
|
||||||
mWorkspaceTransY = res.getDimensionPixelSize(R.dimen.workspace_trans_y);
|
mWorkspaceTransY = res.getDimensionPixelSize(R.dimen.workspace_trans_y);
|
||||||
mClosingWindowTransY = res.getDimensionPixelSize(R.dimen.closing_window_trans_y);
|
mClosingWindowTransY = res.getDimensionPixelSize(R.dimen.closing_window_trans_y);
|
||||||
|
mMaxShadowRadius = res.getDimensionPixelSize(R.dimen.max_shadow_radius);
|
||||||
|
|
||||||
mLauncher.addOnDeviceProfileChangeListener(this);
|
mLauncher.addOnDeviceProfileChangeListener(this);
|
||||||
}
|
}
|
||||||
|
@ -538,6 +540,8 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
|
||||||
EXAGGERATED_EASE);
|
EXAGGERATED_EASE);
|
||||||
FloatProp mWindowRadius = new FloatProp(initialWindowRadius, windowRadius, 0,
|
FloatProp mWindowRadius = new FloatProp(initialWindowRadius, windowRadius, 0,
|
||||||
RADIUS_DURATION, EXAGGERATED_EASE);
|
RADIUS_DURATION, EXAGGERATED_EASE);
|
||||||
|
FloatProp mShadowRadius = new FloatProp(0, mMaxShadowRadius, 0,
|
||||||
|
APP_LAUNCH_DURATION, EXAGGERATED_EASE);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onUpdate(float percent) {
|
public void onUpdate(float percent) {
|
||||||
|
@ -600,7 +604,8 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
|
||||||
builder.withMatrix(matrix)
|
builder.withMatrix(matrix)
|
||||||
.withWindowCrop(crop)
|
.withWindowCrop(crop)
|
||||||
.withAlpha(1f - mIconAlpha.value)
|
.withAlpha(1f - mIconAlpha.value)
|
||||||
.withCornerRadius(mWindowRadius.value);
|
.withCornerRadius(mWindowRadius.value)
|
||||||
|
.withShadowRadius(mShadowRadius.value);
|
||||||
} else {
|
} else {
|
||||||
tmpPos.set(target.position.x, target.position.y);
|
tmpPos.set(target.position.x, target.position.y);
|
||||||
if (target.localBounds != null) {
|
if (target.localBounds != null) {
|
||||||
|
@ -752,6 +757,8 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
|
||||||
FloatProp mDy = new FloatProp(0, mClosingWindowTransY, 0, duration, DEACCEL_1_7);
|
FloatProp mDy = new FloatProp(0, mClosingWindowTransY, 0, duration, DEACCEL_1_7);
|
||||||
FloatProp mScale = new FloatProp(1f, 1f, 0, duration, DEACCEL_1_7);
|
FloatProp mScale = new FloatProp(1f, 1f, 0, duration, DEACCEL_1_7);
|
||||||
FloatProp mAlpha = new FloatProp(1f, 0f, 25, 125, LINEAR);
|
FloatProp mAlpha = new FloatProp(1f, 0f, 25, 125, LINEAR);
|
||||||
|
FloatProp mShadowRadius = new FloatProp(mMaxShadowRadius, 0, 0, duration,
|
||||||
|
DEACCEL_1_7);
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onUpdate(float percent) {
|
public void onUpdate(float percent) {
|
||||||
|
@ -773,7 +780,8 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
|
||||||
matrix.postTranslate(tmpPos.x, tmpPos.y);
|
matrix.postTranslate(tmpPos.x, tmpPos.y);
|
||||||
builder.withMatrix(matrix)
|
builder.withMatrix(matrix)
|
||||||
.withAlpha(mAlpha.value)
|
.withAlpha(mAlpha.value)
|
||||||
.withCornerRadius(windowCornerRadius);
|
.withCornerRadius(windowCornerRadius)
|
||||||
|
.withShadowRadius(mShadowRadius.value);
|
||||||
} else {
|
} else {
|
||||||
matrix.setTranslate(tmpPos.x, tmpPos.y);
|
matrix.setTranslate(tmpPos.x, tmpPos.y);
|
||||||
builder.withMatrix(matrix)
|
builder.withMatrix(matrix)
|
||||||
|
@ -798,7 +806,6 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addCujInstrumentation(Animator anim, int cuj, String transition) {
|
private void addCujInstrumentation(Animator anim, int cuj, String transition) {
|
||||||
if (Trace.isEnabled()) {
|
|
||||||
anim.addListener(new AnimationSuccessListener() {
|
anim.addListener(new AnimationSuccessListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onAnimationStart(Animator animation) {
|
public void onAnimationStart(Animator animation) {
|
||||||
|
@ -825,7 +832,6 @@ public abstract class QuickstepAppTransitionManagerImpl extends LauncherAppTrans
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remote animation runner for animation from the app to Launcher, including recents.
|
* Remote animation runner for animation from the app to Launcher, including recents.
|
||||||
|
|
|
@ -108,6 +108,8 @@ public class PredictionRowView extends LinearLayout implements
|
||||||
|
|
||||||
AllAppsSectionDecorator.SectionDecorationHandler mDecorationHandler;
|
AllAppsSectionDecorator.SectionDecorationHandler mDecorationHandler;
|
||||||
|
|
||||||
|
@Nullable private List<ItemInfo> mPendingPredictedItems;
|
||||||
|
|
||||||
public PredictionRowView(@NonNull Context context) {
|
public PredictionRowView(@NonNull Context context) {
|
||||||
this(context, null);
|
this(context, null);
|
||||||
}
|
}
|
||||||
|
@ -164,6 +166,7 @@ public class PredictionRowView extends LinearLayout implements
|
||||||
}
|
}
|
||||||
mDecorationHandler.onDraw(canvas);
|
mDecorationHandler.onDraw(canvas);
|
||||||
mDecorationHandler.onFocusDraw(canvas, getFocusedChild());
|
mDecorationHandler.onFocusDraw(canvas, getFocusedChild());
|
||||||
|
mLauncher.getAppsView().getActiveRecyclerView().invalidateItemDecorations();
|
||||||
}
|
}
|
||||||
mFocusHelper.draw(canvas);
|
mFocusHelper.draw(canvas);
|
||||||
super.dispatchDraw(canvas);
|
super.dispatchDraw(canvas);
|
||||||
|
@ -203,6 +206,16 @@ public class PredictionRowView extends LinearLayout implements
|
||||||
* we can optimize by swapping them in place.
|
* we can optimize by swapping them in place.
|
||||||
*/
|
*/
|
||||||
public void setPredictedApps(List<ItemInfo> items) {
|
public void setPredictedApps(List<ItemInfo> items) {
|
||||||
|
if (!mLauncher.isWorkspaceLoading() && isShown() && getWindowVisibility() == View.VISIBLE) {
|
||||||
|
mPendingPredictedItems = items;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
applyPredictedApps(items);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void applyPredictedApps(List<ItemInfo> items) {
|
||||||
|
mPendingPredictedItems = null;
|
||||||
mPredictedApps.clear();
|
mPredictedApps.clear();
|
||||||
items.stream()
|
items.stream()
|
||||||
.filter(itemInfo -> itemInfo instanceof WorkspaceItemInfo)
|
.filter(itemInfo -> itemInfo instanceof WorkspaceItemInfo)
|
||||||
|
@ -341,4 +354,13 @@ public class PredictionRowView extends LinearLayout implements
|
||||||
public View getFocusedChild() {
|
public View getFocusedChild() {
|
||||||
return getChildAt(0);
|
return getChildAt(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onVisibilityAggregated(boolean isVisible) {
|
||||||
|
super.onVisibilityAggregated(isVisible);
|
||||||
|
|
||||||
|
if (mPendingPredictedItems != null && !isVisible) {
|
||||||
|
applyPredictedApps(mPendingPredictedItems);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -251,6 +251,28 @@ public class HotseatPredictionController implements DragController.DragListener,
|
||||||
* Sets or updates the predicted items
|
* Sets or updates the predicted items
|
||||||
*/
|
*/
|
||||||
public void setPredictedItems(FixedContainerItems items) {
|
public void setPredictedItems(FixedContainerItems items) {
|
||||||
|
if (!mLauncher.isWorkspaceLoading()
|
||||||
|
&& mHotseat.isShown()
|
||||||
|
&& mHotseat.getWindowVisibility() == View.VISIBLE) {
|
||||||
|
mHotseat.setOnVisibilityAggregatedCallback((isVisible) -> {
|
||||||
|
if (isVisible) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mHotseat.setOnVisibilityAggregatedCallback(null);
|
||||||
|
|
||||||
|
applyPredictedItems(items);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
mHotseat.setOnVisibilityAggregatedCallback(null);
|
||||||
|
|
||||||
|
applyPredictedItems(items);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets or updates the predicted items only once the hotseat becomes hidden to the user
|
||||||
|
*/
|
||||||
|
private void applyPredictedItems(FixedContainerItems items) {
|
||||||
mPredictedItems = items.items;
|
mPredictedItems = items.items;
|
||||||
if (mPredictedItems.isEmpty()) {
|
if (mPredictedItems.isEmpty()) {
|
||||||
HotseatRestoreHelper.restoreBackup(mLauncher);
|
HotseatRestoreHelper.restoreBackup(mLauncher);
|
||||||
|
|
|
@ -250,6 +250,9 @@ public class AppEventProducer implements StatsLogConsumer {
|
||||||
case PREDICTION_CONTAINER: {
|
case PREDICTION_CONTAINER: {
|
||||||
return "predictions";
|
return "predictions";
|
||||||
}
|
}
|
||||||
|
case SHORTCUTS_CONTAINER: {
|
||||||
|
return "deep-shortcuts";
|
||||||
|
}
|
||||||
case FOLDER: {
|
case FOLDER: {
|
||||||
FolderContainer fc = ci.getFolder();
|
FolderContainer fc = ci.getFolder();
|
||||||
switch (fc.getParentContainerCase()) {
|
switch (fc.getParentContainerCase()) {
|
||||||
|
|
|
@ -18,8 +18,7 @@ package com.android.launcher3.model;
|
||||||
|
|
||||||
import static android.content.ContentResolver.SCHEME_CONTENT;
|
import static android.content.ContentResolver.SCHEME_CONTENT;
|
||||||
|
|
||||||
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
import static com.android.launcher3.Utilities.newContentObserver;
|
||||||
import static com.android.launcher3.util.Executors.createAndStartNewLooper;
|
|
||||||
|
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
import android.app.RemoteAction;
|
import android.app.RemoteAction;
|
||||||
|
@ -35,7 +34,7 @@ import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.DeadObjectException;
|
import android.os.DeadObjectException;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Message;
|
import android.os.Looper;
|
||||||
import android.os.Process;
|
import android.os.Process;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
@ -43,7 +42,7 @@ import android.util.ArrayMap;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import androidx.annotation.MainThread;
|
import androidx.annotation.MainThread;
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.Nullable;
|
||||||
import androidx.annotation.WorkerThread;
|
import androidx.annotation.WorkerThread;
|
||||||
|
|
||||||
import com.android.launcher3.BaseDraggingActivity;
|
import com.android.launcher3.BaseDraggingActivity;
|
||||||
|
@ -55,6 +54,7 @@ import com.android.launcher3.config.FeatureFlags;
|
||||||
import com.android.launcher3.model.data.ItemInfo;
|
import com.android.launcher3.model.data.ItemInfo;
|
||||||
import com.android.launcher3.popup.RemoteActionShortcut;
|
import com.android.launcher3.popup.RemoteActionShortcut;
|
||||||
import com.android.launcher3.popup.SystemShortcut;
|
import com.android.launcher3.popup.SystemShortcut;
|
||||||
|
import com.android.launcher3.util.BgObjectWithLooper;
|
||||||
import com.android.launcher3.util.MainThreadInitializedObject;
|
import com.android.launcher3.util.MainThreadInitializedObject;
|
||||||
import com.android.launcher3.util.PackageManagerHelper;
|
import com.android.launcher3.util.PackageManagerHelper;
|
||||||
import com.android.launcher3.util.Preconditions;
|
import com.android.launcher3.util.Preconditions;
|
||||||
|
@ -68,15 +68,11 @@ import java.util.Map;
|
||||||
* Data model for digital wellbeing status of apps.
|
* Data model for digital wellbeing status of apps.
|
||||||
*/
|
*/
|
||||||
@TargetApi(Build.VERSION_CODES.Q)
|
@TargetApi(Build.VERSION_CODES.Q)
|
||||||
public final class WellbeingModel {
|
public final class WellbeingModel extends BgObjectWithLooper {
|
||||||
private static final String TAG = "WellbeingModel";
|
private static final String TAG = "WellbeingModel";
|
||||||
private static final int[] RETRY_TIMES_MS = {5000, 15000, 30000};
|
private static final int[] RETRY_TIMES_MS = {5000, 15000, 30000};
|
||||||
private static final boolean DEBUG = false;
|
private static final boolean DEBUG = false;
|
||||||
|
|
||||||
private static final int MSG_PACKAGE_ADDED = 1;
|
|
||||||
private static final int MSG_PACKAGE_REMOVED = 2;
|
|
||||||
private static final int MSG_FULL_REFRESH = 3;
|
|
||||||
|
|
||||||
private static final int UNKNOWN_MINIMAL_DEVICE_STATE = 0;
|
private static final int UNKNOWN_MINIMAL_DEVICE_STATE = 0;
|
||||||
private static final int IN_MINIMAL_DEVICE = 2;
|
private static final int IN_MINIMAL_DEVICE = 2;
|
||||||
|
|
||||||
|
@ -98,9 +94,9 @@ public final class WellbeingModel {
|
||||||
|
|
||||||
private final Context mContext;
|
private final Context mContext;
|
||||||
private final String mWellbeingProviderPkg;
|
private final String mWellbeingProviderPkg;
|
||||||
private final Handler mWorkerHandler;
|
|
||||||
|
|
||||||
private final ContentObserver mContentObserver;
|
private Handler mWorkerHandler;
|
||||||
|
private ContentObserver mContentObserver;
|
||||||
|
|
||||||
private final Object mModelLock = new Object();
|
private final Object mModelLock = new Object();
|
||||||
// Maps the action Id to the corresponding RemoteAction
|
// Maps the action Id to the corresponding RemoteAction
|
||||||
|
@ -111,60 +107,73 @@ public final class WellbeingModel {
|
||||||
|
|
||||||
private WellbeingModel(final Context context) {
|
private WellbeingModel(final Context context) {
|
||||||
mContext = context;
|
mContext = context;
|
||||||
mWorkerHandler =
|
|
||||||
new Handler(createAndStartNewLooper("WellbeingHandler"), this::handleMessage);
|
|
||||||
|
|
||||||
mWellbeingProviderPkg = mContext.getString(R.string.wellbeing_provider_pkg);
|
mWellbeingProviderPkg = mContext.getString(R.string.wellbeing_provider_pkg);
|
||||||
mContentObserver = new ContentObserver(MAIN_EXECUTOR.getHandler()) {
|
initializeInBackground("WellbeingHandler");
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onChange(boolean selfChange, Uri uri) {
|
protected void onInitialized(Looper looper) {
|
||||||
if (DEBUG || mIsInTest) {
|
mWorkerHandler = new Handler(looper);
|
||||||
Log.d(TAG, "ContentObserver.onChange() called with: selfChange = ["
|
mContentObserver = newContentObserver(mWorkerHandler, this::onWellbeingUriChanged);
|
||||||
+ selfChange + "], uri = [" + uri + "]");
|
|
||||||
}
|
|
||||||
Preconditions.assertUIThread();
|
|
||||||
|
|
||||||
if (uri.getPath().contains(PATH_ACTIONS)) {
|
|
||||||
// Wellbeing reports that app actions have changed.
|
|
||||||
updateWellbeingData();
|
|
||||||
} else if (uri.getPath().contains(PATH_MINIMAL_DEVICE)) {
|
|
||||||
// Wellbeing reports that minimal device state or config is changed.
|
|
||||||
updateLauncherModel(context);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
FeatureFlags.ENABLE_MINIMAL_DEVICE.addChangeListener(mContext, () ->
|
|
||||||
updateLauncherModel(context));
|
|
||||||
|
|
||||||
if (!TextUtils.isEmpty(mWellbeingProviderPkg)) {
|
if (!TextUtils.isEmpty(mWellbeingProviderPkg)) {
|
||||||
context.registerReceiver(
|
mContext.registerReceiver(
|
||||||
new SimpleBroadcastReceiver(this::onWellbeingProviderChanged),
|
new SimpleBroadcastReceiver(t -> restartObserver()),
|
||||||
PackageManagerHelper.getPackageFilter(mWellbeingProviderPkg,
|
PackageManagerHelper.getPackageFilter(mWellbeingProviderPkg,
|
||||||
Intent.ACTION_PACKAGE_ADDED, Intent.ACTION_PACKAGE_CHANGED,
|
Intent.ACTION_PACKAGE_ADDED, Intent.ACTION_PACKAGE_CHANGED,
|
||||||
Intent.ACTION_PACKAGE_REMOVED, Intent.ACTION_PACKAGE_DATA_CLEARED,
|
Intent.ACTION_PACKAGE_REMOVED, Intent.ACTION_PACKAGE_DATA_CLEARED,
|
||||||
Intent.ACTION_PACKAGE_RESTARTED));
|
Intent.ACTION_PACKAGE_RESTARTED),
|
||||||
|
null, mWorkerHandler);
|
||||||
|
|
||||||
IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
|
IntentFilter filter = new IntentFilter(Intent.ACTION_PACKAGE_ADDED);
|
||||||
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
|
filter.addAction(Intent.ACTION_PACKAGE_REMOVED);
|
||||||
filter.addDataScheme("package");
|
filter.addDataScheme("package");
|
||||||
context.registerReceiver(new SimpleBroadcastReceiver(this::onAppPackageChanged),
|
mContext.registerReceiver(new SimpleBroadcastReceiver(this::onAppPackageChanged),
|
||||||
filter);
|
filter, null, mWorkerHandler);
|
||||||
|
|
||||||
restartObserver();
|
restartObserver();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@WorkerThread
|
||||||
|
private void onWellbeingUriChanged(Uri uri) {
|
||||||
|
Preconditions.assertNonUiThread();
|
||||||
|
if (DEBUG || mIsInTest) {
|
||||||
|
Log.d(TAG, "ContentObserver.onChange() called with: uri = [" + uri + "]");
|
||||||
|
}
|
||||||
|
if (uri.getPath().contains(PATH_ACTIONS)) {
|
||||||
|
// Wellbeing reports that app actions have changed.
|
||||||
|
updateAllPackages();
|
||||||
|
} else if (uri.getPath().contains(PATH_MINIMAL_DEVICE)) {
|
||||||
|
// Wellbeing reports that minimal device state or config is changed.
|
||||||
|
if (!FeatureFlags.ENABLE_MINIMAL_DEVICE.get()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Temporary bug fix for b/169771796. Wellbeing provides the layout configuration when
|
||||||
|
// minimal device is enabled. We always want to reload the configuration from Wellbeing
|
||||||
|
// since the layout configuration might have changed.
|
||||||
|
mContext.deleteDatabase(DB_NAME_MINIMAL_DEVICE);
|
||||||
|
|
||||||
|
final Bundle extras = new Bundle();
|
||||||
|
String dbFile;
|
||||||
|
if (isInMinimalDeviceMode()) {
|
||||||
|
dbFile = DB_NAME_MINIMAL_DEVICE;
|
||||||
|
extras.putString(LauncherProvider.KEY_LAYOUT_PROVIDER_AUTHORITY,
|
||||||
|
mWellbeingProviderPkg + ".api");
|
||||||
|
} else {
|
||||||
|
dbFile = InvariantDeviceProfile.INSTANCE.get(mContext).dbFile;
|
||||||
|
}
|
||||||
|
LauncherSettings.Settings.call(mContext.getContentResolver(),
|
||||||
|
LauncherSettings.Settings.METHOD_SWITCH_DATABASE,
|
||||||
|
dbFile, extras);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void setInTest(boolean inTest) {
|
public void setInTest(boolean inTest) {
|
||||||
mIsInTest = inTest;
|
mIsInTest = inTest;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void onWellbeingProviderChanged(Intent intent) {
|
@WorkerThread
|
||||||
if (DEBUG || mIsInTest) {
|
|
||||||
Log.d(TAG, "Changes to Wellbeing package: intent = [" + intent + "]");
|
|
||||||
}
|
|
||||||
restartObserver();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void restartObserver() {
|
private void restartObserver() {
|
||||||
final ContentResolver resolver = mContext.getContentResolver();
|
final ContentResolver resolver = mContext.getContentResolver();
|
||||||
resolver.unregisterContentObserver(mContentObserver);
|
resolver.unregisterContentObserver(mContentObserver);
|
||||||
|
@ -179,7 +188,7 @@ public final class WellbeingModel {
|
||||||
Log.e(TAG, "Failed to register content observer for " + actionsUri + ": " + e);
|
Log.e(TAG, "Failed to register content observer for " + actionsUri + ": " + e);
|
||||||
if (mIsInTest) throw new RuntimeException(e);
|
if (mIsInTest) throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
updateWellbeingData();
|
updateAllPackages();
|
||||||
}
|
}
|
||||||
|
|
||||||
@MainThread
|
@MainThread
|
||||||
|
@ -212,39 +221,6 @@ public final class WellbeingModel {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void updateWellbeingData() {
|
|
||||||
mWorkerHandler.sendEmptyMessage(MSG_FULL_REFRESH);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateLauncherModel(@NonNull final Context context) {
|
|
||||||
if (!FeatureFlags.ENABLE_MINIMAL_DEVICE.get()) {
|
|
||||||
reloadLauncherInNormalMode(context);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
mWorkerHandler.post(() -> {
|
|
||||||
if (isInMinimalDeviceMode()) {
|
|
||||||
reloadLauncherInMinimalMode(context);
|
|
||||||
} else {
|
|
||||||
reloadLauncherInNormalMode(context);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void reloadLauncherInNormalMode(@NonNull final Context context) {
|
|
||||||
LauncherSettings.Settings.call(context.getContentResolver(),
|
|
||||||
LauncherSettings.Settings.METHOD_SWITCH_DATABASE,
|
|
||||||
InvariantDeviceProfile.INSTANCE.get(context).dbFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void reloadLauncherInMinimalMode(@NonNull final Context context) {
|
|
||||||
final Bundle extras = new Bundle();
|
|
||||||
extras.putString(LauncherProvider.KEY_LAYOUT_PROVIDER_AUTHORITY,
|
|
||||||
mWellbeingProviderPkg + ".api");
|
|
||||||
LauncherSettings.Settings.call(context.getContentResolver(),
|
|
||||||
LauncherSettings.Settings.METHOD_SWITCH_DATABASE,
|
|
||||||
DB_NAME_MINIMAL_DEVICE, extras);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Uri.Builder apiBuilder() {
|
private Uri.Builder apiBuilder() {
|
||||||
return new Uri.Builder()
|
return new Uri.Builder()
|
||||||
.scheme(SCHEME_CONTENT)
|
.scheme(SCHEME_CONTENT)
|
||||||
|
@ -277,7 +253,8 @@ public final class WellbeingModel {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean updateActions(String... packageNames) {
|
@WorkerThread
|
||||||
|
private boolean updateActions(String[] packageNames) {
|
||||||
if (packageNames.length == 0) {
|
if (packageNames.length == 0) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -340,68 +317,51 @@ public final class WellbeingModel {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean handleMessage(Message msg) {
|
@WorkerThread
|
||||||
switch (msg.what) {
|
private void updateActionsWithRetry(int retryCount, @Nullable String packageName) {
|
||||||
case MSG_PACKAGE_REMOVED: {
|
String[] packageNames = TextUtils.isEmpty(packageName)
|
||||||
String packageName = (String) msg.obj;
|
? mContext.getSystemService(LauncherApps.class)
|
||||||
mWorkerHandler.removeCallbacksAndMessages(packageName);
|
|
||||||
synchronized (mModelLock) {
|
|
||||||
mPackageToActionId.remove(packageName);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
case MSG_PACKAGE_ADDED: {
|
|
||||||
String packageName = (String) msg.obj;
|
|
||||||
mWorkerHandler.removeCallbacksAndMessages(packageName);
|
|
||||||
if (!updateActions(packageName)) {
|
|
||||||
scheduleRefreshRetry(msg);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
case MSG_FULL_REFRESH: {
|
|
||||||
// Remove all existing messages
|
|
||||||
mWorkerHandler.removeCallbacksAndMessages(null);
|
|
||||||
final String[] packageNames = mContext.getSystemService(LauncherApps.class)
|
|
||||||
.getActivityList(null, Process.myUserHandle()).stream()
|
.getActivityList(null, Process.myUserHandle()).stream()
|
||||||
.map(li -> li.getApplicationInfo().packageName).distinct()
|
.map(li -> li.getApplicationInfo().packageName).distinct()
|
||||||
.toArray(String[]::new);
|
.toArray(String[]::new)
|
||||||
if (!updateActions(packageNames)) {
|
: new String[] { packageName };
|
||||||
scheduleRefreshRetry(msg);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void scheduleRefreshRetry(Message originalMsg) {
|
mWorkerHandler.removeCallbacksAndMessages(packageName);
|
||||||
int retryCount = originalMsg.arg1;
|
if (updateActions(packageNames)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (retryCount >= RETRY_TIMES_MS.length) {
|
if (retryCount >= RETRY_TIMES_MS.length) {
|
||||||
// To many retries, skip
|
// To many retries, skip
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
mWorkerHandler.postDelayed(
|
||||||
Message msg = Message.obtain(originalMsg);
|
() -> updateActionsWithRetry(retryCount + 1, packageName),
|
||||||
msg.arg1 = retryCount + 1;
|
packageName, RETRY_TIMES_MS[retryCount]);
|
||||||
mWorkerHandler.sendMessageDelayed(msg, RETRY_TIMES_MS[retryCount]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@WorkerThread
|
||||||
|
private void updateAllPackages() {
|
||||||
|
updateActionsWithRetry(0, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@WorkerThread
|
||||||
private void onAppPackageChanged(Intent intent) {
|
private void onAppPackageChanged(Intent intent) {
|
||||||
if (DEBUG || mIsInTest) Log.d(TAG, "Changes in apps: intent = [" + intent + "]");
|
if (DEBUG || mIsInTest) Log.d(TAG, "Changes in apps: intent = [" + intent + "]");
|
||||||
Preconditions.assertUIThread();
|
Preconditions.assertNonUiThread();
|
||||||
|
|
||||||
final String packageName = intent.getData().getSchemeSpecificPart();
|
final String packageName = intent.getData().getSchemeSpecificPart();
|
||||||
if (packageName == null || packageName.length() == 0) {
|
if (packageName == null || packageName.length() == 0) {
|
||||||
// they sent us a bad intent
|
// they sent us a bad intent
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
final String action = intent.getAction();
|
final String action = intent.getAction();
|
||||||
if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
|
if (Intent.ACTION_PACKAGE_REMOVED.equals(action)) {
|
||||||
Message.obtain(mWorkerHandler, MSG_PACKAGE_REMOVED, packageName).sendToTarget();
|
mWorkerHandler.removeCallbacksAndMessages(packageName);
|
||||||
|
synchronized (mModelLock) {
|
||||||
|
mPackageToActionId.remove(packageName);
|
||||||
|
}
|
||||||
} else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
|
} else if (Intent.ACTION_PACKAGE_ADDED.equals(action)) {
|
||||||
Message.obtain(mWorkerHandler, MSG_PACKAGE_ADDED, packageName).sendToTarget();
|
updateActionsWithRetry(0, packageName);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -41,11 +41,14 @@ import static com.android.quickstep.GestureState.STATE_END_TARGET_ANIMATION_FINI
|
||||||
import static com.android.quickstep.GestureState.STATE_END_TARGET_SET;
|
import static com.android.quickstep.GestureState.STATE_END_TARGET_SET;
|
||||||
import static com.android.quickstep.GestureState.STATE_RECENTS_SCROLLING_FINISHED;
|
import static com.android.quickstep.GestureState.STATE_RECENTS_SCROLLING_FINISHED;
|
||||||
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
|
import static com.android.quickstep.MultiStateCallback.DEBUG_STATES;
|
||||||
|
import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
|
||||||
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
|
import static com.android.quickstep.views.RecentsView.UPDATE_SYSUI_FLAGS_THRESHOLD;
|
||||||
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
|
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.ACTIVITY_TYPE_HOME;
|
||||||
|
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
|
||||||
|
|
||||||
import android.animation.Animator;
|
import android.animation.Animator;
|
||||||
import android.animation.AnimatorListenerAdapter;
|
import android.animation.AnimatorListenerAdapter;
|
||||||
|
import android.animation.AnimatorSet;
|
||||||
import android.animation.ValueAnimator;
|
import android.animation.ValueAnimator;
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
import android.app.ActivityManager;
|
import android.app.ActivityManager;
|
||||||
|
@ -78,6 +81,8 @@ import com.android.launcher3.logging.StatsLogManager;
|
||||||
import com.android.launcher3.logging.StatsLogManager.StatsLogger;
|
import com.android.launcher3.logging.StatsLogManager.StatsLogger;
|
||||||
import com.android.launcher3.statemanager.StatefulActivity;
|
import com.android.launcher3.statemanager.StatefulActivity;
|
||||||
import com.android.launcher3.testing.TestProtocol;
|
import com.android.launcher3.testing.TestProtocol;
|
||||||
|
import com.android.launcher3.tracing.InputConsumerProto;
|
||||||
|
import com.android.launcher3.tracing.SwipeHandlerProto;
|
||||||
import com.android.launcher3.util.TraceHelper;
|
import com.android.launcher3.util.TraceHelper;
|
||||||
import com.android.launcher3.util.VibratorWrapper;
|
import com.android.launcher3.util.VibratorWrapper;
|
||||||
import com.android.launcher3.util.WindowBounds;
|
import com.android.launcher3.util.WindowBounds;
|
||||||
|
@ -90,6 +95,7 @@ import com.android.quickstep.util.AnimatorControllerWithResistance;
|
||||||
import com.android.quickstep.util.InputConsumerProxy;
|
import com.android.quickstep.util.InputConsumerProxy;
|
||||||
import com.android.quickstep.util.MotionPauseDetector;
|
import com.android.quickstep.util.MotionPauseDetector;
|
||||||
import com.android.quickstep.util.RecentsOrientedState;
|
import com.android.quickstep.util.RecentsOrientedState;
|
||||||
|
import com.android.quickstep.util.ProtoTracer;
|
||||||
import com.android.quickstep.util.RectFSpringAnim;
|
import com.android.quickstep.util.RectFSpringAnim;
|
||||||
import com.android.quickstep.util.SurfaceTransactionApplier;
|
import com.android.quickstep.util.SurfaceTransactionApplier;
|
||||||
import com.android.quickstep.util.SwipePipToHomeAnimator;
|
import com.android.quickstep.util.SwipePipToHomeAnimator;
|
||||||
|
@ -107,6 +113,7 @@ import com.android.systemui.shared.system.TaskInfoCompat;
|
||||||
import com.android.systemui.shared.system.TaskStackChangeListener;
|
import com.android.systemui.shared.system.TaskStackChangeListener;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -614,13 +621,6 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<?>, Q extends
|
||||||
|
|
||||||
updateSysUiFlags(mCurrentShift.value);
|
updateSysUiFlags(mCurrentShift.value);
|
||||||
applyWindowTransform();
|
applyWindowTransform();
|
||||||
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
|
|
||||||
if (mRecentsAnimationTargets != null) {
|
|
||||||
LiveTileOverlay.INSTANCE.update(
|
|
||||||
mTaskViewSimulator.getCurrentRect(),
|
|
||||||
mTaskViewSimulator.getCurrentCornerRadius());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
updateLauncherTransitionProgress();
|
updateLauncherTransitionProgress();
|
||||||
}
|
}
|
||||||
|
@ -719,6 +719,8 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<?>, Q extends
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
public void onGestureStarted(boolean isLikelyToStartNewTask) {
|
public void onGestureStarted(boolean isLikelyToStartNewTask) {
|
||||||
|
InteractionJankMonitorWrapper.begin(
|
||||||
|
InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH, 2000 /* ms timeout */);
|
||||||
notifyGestureStartedAsync();
|
notifyGestureStartedAsync();
|
||||||
setIsLikelyToStartNewTask(isLikelyToStartNewTask, false /* animate */);
|
setIsLikelyToStartNewTask(isLikelyToStartNewTask, false /* animate */);
|
||||||
mStateCallback.setStateOnUiThread(STATE_GESTURE_STARTED);
|
mStateCallback.setStateOnUiThread(STATE_GESTURE_STARTED);
|
||||||
|
@ -794,7 +796,8 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<?>, Q extends
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onSettledOnEndTarget() {
|
private void onSettledOnEndTarget() {
|
||||||
switch (mGestureState.getEndTarget()) {
|
final GestureEndTarget endTarget = mGestureState.getEndTarget();
|
||||||
|
switch (endTarget) {
|
||||||
case HOME:
|
case HOME:
|
||||||
mStateCallback.setState(STATE_SCALED_CONTROLLER_HOME | STATE_CAPTURE_SCREENSHOT);
|
mStateCallback.setState(STATE_SCALED_CONTROLLER_HOME | STATE_CAPTURE_SCREENSHOT);
|
||||||
// Notify swipe-to-home (recents animation) is finished
|
// Notify swipe-to-home (recents animation) is finished
|
||||||
|
@ -811,7 +814,10 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<?>, Q extends
|
||||||
mStateCallback.setState(STATE_RESUME_LAST_TASK);
|
mStateCallback.setState(STATE_RESUME_LAST_TASK);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
ActiveGestureLog.INSTANCE.addLog("onSettledOnEndTarget " + mGestureState.getEndTarget());
|
ActiveGestureLog.INSTANCE.addLog("onSettledOnEndTarget " + endTarget);
|
||||||
|
if (endTarget != NEW_TASK) {
|
||||||
|
InteractionJankMonitorWrapper.cancel(InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return Whether this was the task we were waiting to appear, and thus handled it. */
|
/** @return Whether this was the task we were waiting to appear, and thus handled it. */
|
||||||
|
@ -1388,7 +1394,7 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<?>, Q extends
|
||||||
if (taskView != null && !mCanceled) {
|
if (taskView != null && !mCanceled) {
|
||||||
// Defer finishing the animation until the next launcher frame with the
|
// Defer finishing the animation until the next launcher frame with the
|
||||||
// new thumbnail
|
// new thumbnail
|
||||||
finishTransitionPosted = ViewUtils.postDraw(taskView,
|
finishTransitionPosted = ViewUtils.postFrameDrawn(taskView,
|
||||||
() -> mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED),
|
() -> mStateCallback.setStateOnUiThread(STATE_SCREENSHOT_CAPTURED),
|
||||||
this::isCanceled);
|
this::isCanceled);
|
||||||
}
|
}
|
||||||
|
@ -1450,17 +1456,64 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<?>, Q extends
|
||||||
private void setupLauncherUiAfterSwipeUpToRecentsAnimation() {
|
private void setupLauncherUiAfterSwipeUpToRecentsAnimation() {
|
||||||
endLauncherTransitionController();
|
endLauncherTransitionController();
|
||||||
mActivityInterface.onSwipeUpToRecentsComplete();
|
mActivityInterface.onSwipeUpToRecentsComplete();
|
||||||
if (mRecentsAnimationController != null) {
|
|
||||||
mRecentsAnimationController.setDeferCancelUntilNextTransition(true /* defer */,
|
|
||||||
true /* screenshot */);
|
|
||||||
}
|
|
||||||
mRecentsView.onSwipeUpAnimationSuccess();
|
mRecentsView.onSwipeUpAnimationSuccess();
|
||||||
|
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
|
||||||
|
mTaskAnimationManager.setLaunchOtherTaskInLiveTileModeHandler(
|
||||||
|
this::launchOtherTaskInLiveTileMode);
|
||||||
|
}
|
||||||
|
|
||||||
SystemUiProxy.INSTANCE.get(mContext).onOverviewShown(false, TAG);
|
SystemUiProxy.INSTANCE.get(mContext).onOverviewShown(false, TAG);
|
||||||
doLogGesture(RECENTS, mRecentsView.getCurrentPageTaskView());
|
doLogGesture(RECENTS, mRecentsView.getCurrentPageTaskView());
|
||||||
reset();
|
reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void launchOtherTaskInLiveTileMode(RemoteAnimationTargetCompat appearedTaskTarget) {
|
||||||
|
TaskView taskView = mRecentsView.getTaskView(appearedTaskTarget.taskId);
|
||||||
|
if (taskView == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RemoteAnimationTargetCompat[] apps = Arrays.copyOf(
|
||||||
|
mRecentsAnimationTargets.apps,
|
||||||
|
mRecentsAnimationTargets.apps.length + 1);
|
||||||
|
apps[apps.length - 1] = appearedTaskTarget;
|
||||||
|
boolean launcherClosing =
|
||||||
|
taskIsATargetWithMode(apps, mActivity.getTaskId(), MODE_CLOSING);
|
||||||
|
|
||||||
|
AnimatorSet anim = new AnimatorSet();
|
||||||
|
TaskViewUtils.composeRecentsLaunchAnimator(
|
||||||
|
anim, taskView, apps,
|
||||||
|
mRecentsAnimationTargets.wallpapers, launcherClosing,
|
||||||
|
mActivity.getStateManager(), mRecentsView,
|
||||||
|
mActivityInterface.getDepthController());
|
||||||
|
anim.addListener(new AnimatorListenerAdapter(){
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animator) {
|
||||||
|
cleanUp(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAnimationCancel(Animator animator) {
|
||||||
|
cleanUp(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cleanUp(boolean canceled) {
|
||||||
|
if (mRecentsAnimationController != null) {
|
||||||
|
mRecentsAnimationController.finish(false /* toRecents */,
|
||||||
|
null /* onFinishComplete */);
|
||||||
|
if (canceled) {
|
||||||
|
mRecentsAnimationController = null;
|
||||||
|
} else {
|
||||||
|
mActivityInterface.onLaunchTaskSuccess();
|
||||||
|
}
|
||||||
|
ActiveGestureLog.INSTANCE.addLog("finishRecentsAnimation", false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
anim.start();
|
||||||
|
}
|
||||||
|
|
||||||
private void addLiveTileOverlay() {
|
private void addLiveTileOverlay() {
|
||||||
if (LiveTileOverlay.INSTANCE.attach(mActivity.getRootView().getOverlay())) {
|
if (LiveTileOverlay.INSTANCE.attach(mActivity.getRootView().getOverlay())) {
|
||||||
mRecentsView.setLiveTileOverlayAttached(true);
|
mRecentsView.setLiveTileOverlayAttached(true);
|
||||||
|
@ -1523,11 +1576,6 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<?>, Q extends
|
||||||
|
|
||||||
protected void startNewTask(Consumer<Boolean> resultCallback) {
|
protected void startNewTask(Consumer<Boolean> resultCallback) {
|
||||||
// Launch the task user scrolled to (mRecentsView.getNextPage()).
|
// Launch the task user scrolled to (mRecentsView.getNextPage()).
|
||||||
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
|
|
||||||
// We finish recents animation inside launchTask() when live tile is enabled.
|
|
||||||
mRecentsView.getNextPageTaskView().launchTask(false /* animate */,
|
|
||||||
true /* freezeTaskList */);
|
|
||||||
} else {
|
|
||||||
if (!mCanceled) {
|
if (!mCanceled) {
|
||||||
TaskView nextTask = mRecentsView.getNextPageTaskView();
|
TaskView nextTask = mRecentsView.getNextPageTaskView();
|
||||||
if (nextTask != null) {
|
if (nextTask != null) {
|
||||||
|
@ -1556,7 +1604,6 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<?>, Q extends
|
||||||
}
|
}
|
||||||
mCanceled = false;
|
mCanceled = false;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Runs the given {@param action} if the recents animation has already started, or queues it to
|
* Runs the given {@param action} if the recents animation has already started, or queues it to
|
||||||
|
@ -1641,6 +1688,32 @@ public abstract class AbsSwipeUpHandler<T extends StatefulActivity<?>, Q extends
|
||||||
}
|
}
|
||||||
mTaskViewSimulator.apply(mTransformParams);
|
mTaskViewSimulator.apply(mTransformParams);
|
||||||
}
|
}
|
||||||
|
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && mRecentsAnimationTargets != null) {
|
||||||
|
LiveTileOverlay.INSTANCE.update(
|
||||||
|
mTaskViewSimulator.getCurrentRect(),
|
||||||
|
mTaskViewSimulator.getCurrentCornerRadius());
|
||||||
|
}
|
||||||
|
ProtoTracer.INSTANCE.get(mContext).scheduleFrameUpdate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for winscope tracing, see launcher_trace.proto
|
||||||
|
* @see com.android.systemui.shared.tracing.ProtoTraceable#writeToProto
|
||||||
|
* @param inputConsumerProto The parent of this proto message.
|
||||||
|
*/
|
||||||
|
public void writeToProto(InputConsumerProto.Builder inputConsumerProto) {
|
||||||
|
SwipeHandlerProto.Builder swipeHandlerProto = SwipeHandlerProto.newBuilder();
|
||||||
|
|
||||||
|
mGestureState.writeToProto(swipeHandlerProto);
|
||||||
|
|
||||||
|
swipeHandlerProto.setIsRecentsAttachedToAppWindow(
|
||||||
|
mAnimationFactory.isRecentsAttachedToAppWindow());
|
||||||
|
swipeHandlerProto.setScrollOffset(mRecentsView == null
|
||||||
|
? 0
|
||||||
|
: mRecentsView.getScrollOffset());
|
||||||
|
swipeHandlerProto.setAppToOverviewProgress(mCurrentShift.value);
|
||||||
|
|
||||||
|
inputConsumerProto.setSwipeHandler(swipeHandlerProto);
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface Factory {
|
public interface Factory {
|
||||||
|
|
|
@ -297,6 +297,10 @@ public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_T
|
||||||
* @param animate Whether to animate recents to/from its new attached state.
|
* @param animate Whether to animate recents to/from its new attached state.
|
||||||
*/
|
*/
|
||||||
default void setRecentsAttachedToAppWindow(boolean attached, boolean animate) { }
|
default void setRecentsAttachedToAppWindow(boolean attached, boolean animate) { }
|
||||||
|
|
||||||
|
default boolean isRecentsAttachedToAppWindow() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class DefaultAnimationFactory implements AnimationFactory {
|
class DefaultAnimationFactory implements AnimationFactory {
|
||||||
|
@ -388,6 +392,11 @@ public abstract class BaseActivityInterface<STATE_TYPE extends BaseState<STATE_T
|
||||||
fadeAnim.setDuration(animate ? RECENTS_ATTACH_DURATION : 0).start();
|
fadeAnim.setDuration(animate ? RECENTS_ATTACH_DURATION : 0).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRecentsAttachedToAppWindow() {
|
||||||
|
return mIsAttachedToWindow;
|
||||||
|
}
|
||||||
|
|
||||||
protected void createBackgroundToOverviewAnim(ACTIVITY_TYPE activity, PendingAnimation pa) {
|
protected void createBackgroundToOverviewAnim(ACTIVITY_TYPE activity, PendingAnimation pa) {
|
||||||
// Scale down recents from being full screen to being in overview.
|
// Scale down recents from being full screen to being in overview.
|
||||||
RecentsView recentsView = activity.getOverviewPanel();
|
RecentsView recentsView = activity.getOverviewPanel();
|
||||||
|
|
|
@ -26,6 +26,8 @@ import android.content.Intent;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
|
||||||
import com.android.launcher3.statemanager.StatefulActivity;
|
import com.android.launcher3.statemanager.StatefulActivity;
|
||||||
|
import com.android.launcher3.tracing.GestureStateProto;
|
||||||
|
import com.android.launcher3.tracing.SwipeHandlerProto;
|
||||||
import com.android.quickstep.util.ActiveGestureLog;
|
import com.android.quickstep.util.ActiveGestureLog;
|
||||||
import com.android.systemui.shared.recents.model.ThumbnailData;
|
import com.android.systemui.shared.recents.model.ThumbnailData;
|
||||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||||
|
@ -46,19 +48,22 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
|
||||||
* Defines the end targets of a gesture and the associated state.
|
* Defines the end targets of a gesture and the associated state.
|
||||||
*/
|
*/
|
||||||
public enum GestureEndTarget {
|
public enum GestureEndTarget {
|
||||||
HOME(true, LAUNCHER_STATE_HOME, false),
|
HOME(true, LAUNCHER_STATE_HOME, false, GestureStateProto.GestureEndTarget.HOME),
|
||||||
|
|
||||||
RECENTS(true, LAUNCHER_STATE_OVERVIEW, true),
|
RECENTS(true, LAUNCHER_STATE_OVERVIEW, true, GestureStateProto.GestureEndTarget.RECENTS),
|
||||||
|
|
||||||
NEW_TASK(false, LAUNCHER_STATE_BACKGROUND, true),
|
NEW_TASK(false, LAUNCHER_STATE_BACKGROUND, true,
|
||||||
|
GestureStateProto.GestureEndTarget.NEW_TASK),
|
||||||
|
|
||||||
LAST_TASK(false, LAUNCHER_STATE_BACKGROUND, true);
|
LAST_TASK(false, LAUNCHER_STATE_BACKGROUND, true,
|
||||||
|
GestureStateProto.GestureEndTarget.LAST_TASK);
|
||||||
|
|
||||||
GestureEndTarget(boolean isLauncher, int containerType,
|
GestureEndTarget(boolean isLauncher, int containerType, boolean recentsAttachedToAppWindow,
|
||||||
boolean recentsAttachedToAppWindow) {
|
GestureStateProto.GestureEndTarget protoEndTarget) {
|
||||||
this.isLauncher = isLauncher;
|
this.isLauncher = isLauncher;
|
||||||
this.containerType = containerType;
|
this.containerType = containerType;
|
||||||
this.recentsAttachedToAppWindow = recentsAttachedToAppWindow;
|
this.recentsAttachedToAppWindow = recentsAttachedToAppWindow;
|
||||||
|
this.protoEndTarget = protoEndTarget;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Whether the target is in the launcher activity. Implicitly, if the end target is going
|
/** Whether the target is in the launcher activity. Implicitly, if the end target is going
|
||||||
|
@ -68,6 +73,8 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
|
||||||
public final int containerType;
|
public final int containerType;
|
||||||
/** Whether RecentsView should be attached to the window as we animate to this target */
|
/** Whether RecentsView should be attached to the window as we animate to this target */
|
||||||
public final boolean recentsAttachedToAppWindow;
|
public final boolean recentsAttachedToAppWindow;
|
||||||
|
/** The GestureStateProto enum value, used for winscope tracing. See launcher_trace.proto */
|
||||||
|
public final GestureStateProto.GestureEndTarget protoEndTarget;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static final String TAG = "GestureState";
|
private static final String TAG = "GestureState";
|
||||||
|
@ -345,4 +352,17 @@ public class GestureState implements RecentsAnimationCallbacks.RecentsAnimationL
|
||||||
pw.println(" lastStartedTaskId=" + mLastStartedTaskId);
|
pw.println(" lastStartedTaskId=" + mLastStartedTaskId);
|
||||||
pw.println(" isRecentsAnimationRunning=" + isRecentsAnimationRunning());
|
pw.println(" isRecentsAnimationRunning=" + isRecentsAnimationRunning());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for winscope tracing, see launcher_trace.proto
|
||||||
|
* @see com.android.systemui.shared.tracing.ProtoTraceable#writeToProto
|
||||||
|
* @param swipeHandlerProto The parent of this proto message.
|
||||||
|
*/
|
||||||
|
public void writeToProto(SwipeHandlerProto.Builder swipeHandlerProto) {
|
||||||
|
GestureStateProto.Builder gestureStateProto = GestureStateProto.newBuilder();
|
||||||
|
gestureStateProto.setEndTarget(mEndTarget == null
|
||||||
|
? GestureStateProto.GestureEndTarget.UNSET
|
||||||
|
: mEndTarget.protoEndTarget);
|
||||||
|
swipeHandlerProto.setGestureState(gestureStateProto);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,9 @@ import android.view.InputEvent;
|
||||||
import android.view.KeyEvent;
|
import android.view.KeyEvent;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
|
|
||||||
|
import com.android.launcher3.tracing.InputConsumerProto;
|
||||||
|
import com.android.launcher3.tracing.TouchInteractionServiceProto;
|
||||||
|
|
||||||
@TargetApi(Build.VERSION_CODES.O)
|
@TargetApi(Build.VERSION_CODES.O)
|
||||||
public interface InputConsumer {
|
public interface InputConsumer {
|
||||||
|
|
||||||
|
@ -116,4 +119,21 @@ public interface InputConsumer {
|
||||||
}
|
}
|
||||||
return name.toString();
|
return name.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for winscope tracing, see launcher_trace.proto
|
||||||
|
* @see com.android.systemui.shared.tracing.ProtoTraceable#writeToProto
|
||||||
|
* @param serviceProto The parent of this proto message.
|
||||||
|
*/
|
||||||
|
default void writeToProto(TouchInteractionServiceProto.Builder serviceProto) {
|
||||||
|
InputConsumerProto.Builder inputConsumerProto = InputConsumerProto.newBuilder();
|
||||||
|
inputConsumerProto.setName(getName());
|
||||||
|
writeToProtoInternal(inputConsumerProto);
|
||||||
|
serviceProto.setInputConsumer(inputConsumerProto);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @see #writeToProto - allows subclasses to write additional info to the proto.
|
||||||
|
*/
|
||||||
|
default void writeToProtoInternal(InputConsumerProto.Builder inputConsumerProto) {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,6 +35,7 @@ import com.android.quickstep.util.RemoteAnimationProvider;
|
||||||
import com.android.quickstep.views.RecentsView;
|
import com.android.quickstep.views.RecentsView;
|
||||||
import com.android.quickstep.views.TaskView;
|
import com.android.quickstep.views.TaskView;
|
||||||
import com.android.systemui.shared.system.ActivityManagerWrapper;
|
import com.android.systemui.shared.system.ActivityManagerWrapper;
|
||||||
|
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
|
||||||
import com.android.systemui.shared.system.LatencyTrackerCompat;
|
import com.android.systemui.shared.system.LatencyTrackerCompat;
|
||||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||||
|
|
||||||
|
@ -174,6 +175,9 @@ public class OverviewCommandHelper {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
InteractionJankMonitorWrapper.begin(
|
||||||
|
InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH, 2000 /* ms timout */);
|
||||||
|
|
||||||
// Otherwise, start overview.
|
// Otherwise, start overview.
|
||||||
mListener = mActivityInterface.createActivityInitListener(this::onActivityReady);
|
mListener = mActivityInterface.createActivityInitListener(this::onActivityReady);
|
||||||
mListener.registerAndStartActivity(mOverviewComponentObserver.getOverviewIntent(),
|
mListener.registerAndStartActivity(mOverviewComponentObserver.getOverviewIntent(),
|
||||||
|
|
|
@ -35,6 +35,8 @@ import android.content.pm.PackageManager;
|
||||||
import android.content.pm.ResolveInfo;
|
import android.content.pm.ResolveInfo;
|
||||||
import android.util.SparseIntArray;
|
import android.util.SparseIntArray;
|
||||||
|
|
||||||
|
import com.android.launcher3.tracing.OverviewComponentObserverProto;
|
||||||
|
import com.android.launcher3.tracing.TouchInteractionServiceProto;
|
||||||
import com.android.launcher3.util.SimpleBroadcastReceiver;
|
import com.android.launcher3.util.SimpleBroadcastReceiver;
|
||||||
import com.android.systemui.shared.system.PackageManagerWrapper;
|
import com.android.systemui.shared.system.PackageManagerWrapper;
|
||||||
|
|
||||||
|
@ -262,4 +264,17 @@ public final class OverviewComponentObserver {
|
||||||
pw.println(" overviewIntent=" + mOverviewIntent);
|
pw.println(" overviewIntent=" + mOverviewIntent);
|
||||||
pw.println(" homeIntent=" + mCurrentHomeIntent);
|
pw.println(" homeIntent=" + mCurrentHomeIntent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used for winscope tracing, see launcher_trace.proto
|
||||||
|
* @see com.android.systemui.shared.tracing.ProtoTraceable#writeToProto
|
||||||
|
* @param serviceProto The parent of this proto message.
|
||||||
|
*/
|
||||||
|
public void writeToProto(TouchInteractionServiceProto.Builder serviceProto) {
|
||||||
|
OverviewComponentObserverProto.Builder overviewComponentObserver =
|
||||||
|
OverviewComponentObserverProto.newBuilder();
|
||||||
|
overviewComponentObserver.setOverviewActivityStarted(mActivityInterface.isStarted());
|
||||||
|
overviewComponentObserver.setOverviewActivityResumed(mActivityInterface.isResumed());
|
||||||
|
serviceProto.setOverviewComponentObvserver(overviewComponentObserver);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,7 @@ import androidx.annotation.UiThread;
|
||||||
|
|
||||||
import com.android.launcher3.util.Preconditions;
|
import com.android.launcher3.util.Preconditions;
|
||||||
import com.android.systemui.shared.recents.model.ThumbnailData;
|
import com.android.systemui.shared.recents.model.ThumbnailData;
|
||||||
|
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
|
||||||
import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
|
import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
|
||||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||||
|
|
||||||
|
@ -90,24 +91,6 @@ public class RecentsAnimationController {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Notifies the controller that we want to defer cancel until the next app transition starts.
|
|
||||||
* If {@param screenshot} is set, then we will receive a screenshot on the next
|
|
||||||
* {@link RecentsAnimationCallbacks#onAnimationCanceled(ThumbnailData)} and we must also call
|
|
||||||
* {@link #cleanupScreenshot()} when that screenshot is no longer used.
|
|
||||||
*/
|
|
||||||
public void setDeferCancelUntilNextTransition(boolean defer, boolean screenshot) {
|
|
||||||
mController.setDeferCancelUntilNextTransition(defer, screenshot);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cleans up the screenshot previously returned from
|
|
||||||
* {@link RecentsAnimationCallbacks#onAnimationCanceled(ThumbnailData)}.
|
|
||||||
*/
|
|
||||||
public void cleanupScreenshot() {
|
|
||||||
UI_HELPER_EXECUTOR.execute(() -> mController.cleanupScreenshot());
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Remove task remote animation target from
|
* Remove task remote animation target from
|
||||||
* {@link RecentsAnimationCallbacks#onTaskAppeared(RemoteAnimationTargetCompat)}}.
|
* {@link RecentsAnimationCallbacks#onTaskAppeared(RemoteAnimationTargetCompat)}}.
|
||||||
|
@ -151,6 +134,7 @@ public class RecentsAnimationController {
|
||||||
mOnFinishedListener.accept(this);
|
mOnFinishedListener.accept(this);
|
||||||
UI_HELPER_EXECUTOR.execute(() -> {
|
UI_HELPER_EXECUTOR.execute(() -> {
|
||||||
mController.finish(toRecents, sendUserLeaveHint);
|
mController.finish(toRecents, sendUserLeaveHint);
|
||||||
|
InteractionJankMonitorWrapper.end(InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH);
|
||||||
if (callback != null) {
|
if (callback != null) {
|
||||||
MAIN_EXECUTOR.execute(callback);
|
MAIN_EXECUTOR.execute(callback);
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,6 +29,7 @@ import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.UiThread;
|
import androidx.annotation.UiThread;
|
||||||
|
|
||||||
import com.android.launcher3.DeviceProfile;
|
import com.android.launcher3.DeviceProfile;
|
||||||
|
import com.android.launcher3.R;
|
||||||
import com.android.launcher3.Utilities;
|
import com.android.launcher3.Utilities;
|
||||||
import com.android.launcher3.anim.AnimationSuccessListener;
|
import com.android.launcher3.anim.AnimationSuccessListener;
|
||||||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||||
|
@ -66,6 +67,8 @@ public abstract class SwipeUpAnimationLogic {
|
||||||
// How much further we can drag past recents, as a factor of mTransitionDragLength.
|
// How much further we can drag past recents, as a factor of mTransitionDragLength.
|
||||||
protected float mDragLengthFactor = 1;
|
protected float mDragLengthFactor = 1;
|
||||||
|
|
||||||
|
protected final float mMaxShadowRadius;
|
||||||
|
|
||||||
protected AnimatorControllerWithResistance mWindowTransitionController;
|
protected AnimatorControllerWithResistance mWindowTransitionController;
|
||||||
|
|
||||||
public SwipeUpAnimationLogic(Context context, RecentsAnimationDeviceState deviceState,
|
public SwipeUpAnimationLogic(Context context, RecentsAnimationDeviceState deviceState,
|
||||||
|
@ -80,6 +83,9 @@ public abstract class SwipeUpAnimationLogic {
|
||||||
mDeviceState.getRotationTouchHelper().getCurrentActiveRotation(),
|
mDeviceState.getRotationTouchHelper().getCurrentActiveRotation(),
|
||||||
mDeviceState.getRotationTouchHelper().getDisplayRotation());
|
mDeviceState.getRotationTouchHelper().getDisplayRotation());
|
||||||
mTaskViewSimulator.setDrawsBelowRecents(true);
|
mTaskViewSimulator.setDrawsBelowRecents(true);
|
||||||
|
|
||||||
|
mMaxShadowRadius = context.getResources().getDimensionPixelSize(R.dimen.max_shadow_radius);
|
||||||
|
mTransformParams.setShadowRadius(mMaxShadowRadius);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void initTransitionEndpoints(DeviceProfile dp) {
|
protected void initTransitionEndpoints(DeviceProfile dp) {
|
||||||
|
@ -259,9 +265,11 @@ public abstract class SwipeUpAnimationLogic {
|
||||||
|
|
||||||
mMatrix.setRectToRect(mCropRectF, mWindowCurrentRect, ScaleToFit.FILL);
|
mMatrix.setRectToRect(mCropRectF, mWindowCurrentRect, ScaleToFit.FILL);
|
||||||
float cornerRadius = Utilities.mapRange(progress, mStartRadius, mEndRadius);
|
float cornerRadius = Utilities.mapRange(progress, mStartRadius, mEndRadius);
|
||||||
|
float shadowRadius = Utilities.mapRange(progress, mMaxShadowRadius, 0);
|
||||||
mTransformParams
|
mTransformParams
|
||||||
.setTargetAlpha(getWindowAlpha(progress))
|
.setTargetAlpha(getWindowAlpha(progress))
|
||||||
.setCornerRadius(cornerRadius);
|
.setCornerRadius(cornerRadius)
|
||||||
|
.setShadowRadius(shadowRadius);
|
||||||
|
|
||||||
mTransformParams.applySurfaceParams(mTransformParams.createSurfaceParams(this));
|
mTransformParams.applySurfaceParams(mTransformParams.createSurfaceParams(this));
|
||||||
mAnimationFactory.update(currentRect, progress, mMatrix.mapRadius(cornerRadius));
|
mAnimationFactory.update(currentRect, progress, mMatrix.mapRadius(cornerRadius));
|
||||||
|
@ -272,7 +280,8 @@ public abstract class SwipeUpAnimationLogic {
|
||||||
Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
|
Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
|
||||||
builder.withMatrix(mMatrix)
|
builder.withMatrix(mMatrix)
|
||||||
.withWindowCrop(mCropRect)
|
.withWindowCrop(mCropRect)
|
||||||
.withCornerRadius(params.getCornerRadius());
|
.withCornerRadius(params.getCornerRadius())
|
||||||
|
.withShadowRadius(params.getShadowRadius());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -17,6 +17,7 @@ package com.android.quickstep;
|
||||||
|
|
||||||
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
||||||
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
|
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
|
||||||
|
import static com.android.quickstep.GestureState.GestureEndTarget.RECENTS;
|
||||||
import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_INITIALIZED;
|
import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_INITIALIZED;
|
||||||
import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_STARTED;
|
import static com.android.quickstep.GestureState.STATE_RECENTS_ANIMATION_STARTED;
|
||||||
|
|
||||||
|
@ -31,6 +32,8 @@ import com.android.systemui.shared.recents.model.ThumbnailData;
|
||||||
import com.android.systemui.shared.system.ActivityManagerWrapper;
|
import com.android.systemui.shared.system.ActivityManagerWrapper;
|
||||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAnimationListener {
|
public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAnimationListener {
|
||||||
|
|
||||||
private RecentsAnimationController mController;
|
private RecentsAnimationController mController;
|
||||||
|
@ -39,6 +42,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
|
||||||
// Temporary until we can hook into gesture state events
|
// Temporary until we can hook into gesture state events
|
||||||
private GestureState mLastGestureState;
|
private GestureState mLastGestureState;
|
||||||
private RemoteAnimationTargetCompat mLastAppearedTaskTarget;
|
private RemoteAnimationTargetCompat mLastAppearedTaskTarget;
|
||||||
|
private Consumer<RemoteAnimationTargetCompat> mLaunchOtherTaskHandler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Preloads the recents animation.
|
* Preloads the recents animation.
|
||||||
|
@ -88,22 +92,21 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
|
public void onRecentsAnimationCanceled(ThumbnailData thumbnailData) {
|
||||||
if (thumbnailData != null) {
|
cleanUpRecentsAnimation();
|
||||||
// If a screenshot is provided, switch to the screenshot before cleaning up
|
|
||||||
activityInterface.switchRunningTaskViewToScreenshot(thumbnailData,
|
|
||||||
() -> cleanUpRecentsAnimation(thumbnailData));
|
|
||||||
} else {
|
|
||||||
cleanUpRecentsAnimation(null /* canceledThumbnail */);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onRecentsAnimationFinished(RecentsAnimationController controller) {
|
public void onRecentsAnimationFinished(RecentsAnimationController controller) {
|
||||||
cleanUpRecentsAnimation(null /* canceledThumbnail */);
|
cleanUpRecentsAnimation();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
|
public void onTaskAppeared(RemoteAnimationTargetCompat appearedTaskTarget) {
|
||||||
|
if (mLaunchOtherTaskHandler != null
|
||||||
|
&& mLastGestureState.getEndTarget() == RECENTS) {
|
||||||
|
mLaunchOtherTaskHandler.accept(appearedTaskTarget);
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (mController != null) {
|
if (mController != null) {
|
||||||
if (mLastAppearedTaskTarget == null
|
if (mLastAppearedTaskTarget == null
|
||||||
|| appearedTaskTarget.taskId != mLastAppearedTaskTarget.taskId) {
|
|| appearedTaskTarget.taskId != mLastAppearedTaskTarget.taskId) {
|
||||||
|
@ -138,6 +141,15 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
|
||||||
return mCallbacks;
|
return mCallbacks;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The passed-in handler is used to render side task launch animation in recents in live tile
|
||||||
|
* mode.
|
||||||
|
*/
|
||||||
|
public void setLaunchOtherTaskInLiveTileModeHandler(
|
||||||
|
Consumer<RemoteAnimationTargetCompat> handler) {
|
||||||
|
mLaunchOtherTaskHandler = handler;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finishes the running recents animation.
|
* Finishes the running recents animation.
|
||||||
*/
|
*/
|
||||||
|
@ -147,7 +159,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
|
||||||
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), toHome
|
Utilities.postAsyncCallback(MAIN_EXECUTOR.getHandler(), toHome
|
||||||
? mController::finishAnimationToHome
|
? mController::finishAnimationToHome
|
||||||
: mController::finishAnimationToApp);
|
: mController::finishAnimationToApp);
|
||||||
cleanUpRecentsAnimation(null /* canceledThumbnail */);
|
cleanUpRecentsAnimation();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -174,12 +186,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
|
||||||
/**
|
/**
|
||||||
* Cleans up the recents animation entirely.
|
* Cleans up the recents animation entirely.
|
||||||
*/
|
*/
|
||||||
private void cleanUpRecentsAnimation(ThumbnailData canceledThumbnail) {
|
private void cleanUpRecentsAnimation() {
|
||||||
// Clean up the screenshot if necessary
|
|
||||||
if (mController != null && canceledThumbnail != null) {
|
|
||||||
mController.cleanupScreenshot();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Release all the target leashes
|
// Release all the target leashes
|
||||||
if (mTargets != null) {
|
if (mTargets != null) {
|
||||||
mTargets.release();
|
mTargets.release();
|
||||||
|
@ -195,6 +202,7 @@ public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAn
|
||||||
mTargets = null;
|
mTargets = null;
|
||||||
mLastGestureState = null;
|
mLastGestureState = null;
|
||||||
mLastAppearedTaskTarget = null;
|
mLastAppearedTaskTarget = null;
|
||||||
|
mLaunchOtherTaskHandler = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void dump() {
|
public void dump() {
|
||||||
|
|
|
@ -21,12 +21,14 @@ import static com.android.launcher3.uioverrides.QuickstepLauncher.GO_LOW_RAM_REC
|
||||||
import android.app.ActivityManager.TaskDescription;
|
import android.app.ActivityManager.TaskDescription;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.pm.ActivityInfo;
|
import android.content.pm.ActivityInfo;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.drawable.BitmapDrawable;
|
import android.graphics.drawable.BitmapDrawable;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.UserHandle;
|
import android.os.UserHandle;
|
||||||
|
import android.text.TextUtils;
|
||||||
import android.util.SparseArray;
|
import android.util.SparseArray;
|
||||||
import android.view.accessibility.AccessibilityManager;
|
import android.view.accessibility.AccessibilityManager;
|
||||||
|
|
||||||
|
@ -34,6 +36,7 @@ import androidx.annotation.WorkerThread;
|
||||||
|
|
||||||
import com.android.launcher3.FastBitmapDrawable;
|
import com.android.launcher3.FastBitmapDrawable;
|
||||||
import com.android.launcher3.R;
|
import com.android.launcher3.R;
|
||||||
|
import com.android.launcher3.Utilities;
|
||||||
import com.android.launcher3.icons.BitmapInfo;
|
import com.android.launcher3.icons.BitmapInfo;
|
||||||
import com.android.launcher3.icons.IconProvider;
|
import com.android.launcher3.icons.IconProvider;
|
||||||
import com.android.launcher3.icons.LauncherIcons;
|
import com.android.launcher3.icons.LauncherIcons;
|
||||||
|
@ -42,7 +45,6 @@ import com.android.quickstep.util.CancellableTask;
|
||||||
import com.android.quickstep.util.TaskKeyLruCache;
|
import com.android.quickstep.util.TaskKeyLruCache;
|
||||||
import com.android.systemui.shared.recents.model.Task;
|
import com.android.systemui.shared.recents.model.Task;
|
||||||
import com.android.systemui.shared.recents.model.Task.TaskKey;
|
import com.android.systemui.shared.recents.model.Task.TaskKey;
|
||||||
import com.android.systemui.shared.system.ActivityManagerWrapper;
|
|
||||||
import com.android.systemui.shared.system.PackageManagerWrapper;
|
import com.android.systemui.shared.system.PackageManagerWrapper;
|
||||||
import com.android.systemui.shared.system.TaskDescriptionCompat;
|
import com.android.systemui.shared.system.TaskDescriptionCompat;
|
||||||
|
|
||||||
|
@ -163,9 +165,8 @@ public class TaskIconCache {
|
||||||
key.getComponent(), key.userId);
|
key.getComponent(), key.userId);
|
||||||
}
|
}
|
||||||
if (activityInfo != null) {
|
if (activityInfo != null) {
|
||||||
entry.contentDescription = ActivityManagerWrapper.getInstance()
|
entry.contentDescription = getBadgedContentDescription(
|
||||||
.getBadgedContentDescription(activityInfo, task.key.userId,
|
activityInfo, task.key.userId, task.taskDescription);
|
||||||
task.taskDescription);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,6 +174,21 @@ public class TaskIconCache {
|
||||||
return entry;
|
return entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String getBadgedContentDescription(ActivityInfo info, int userId, TaskDescription td) {
|
||||||
|
PackageManager pm = mContext.getPackageManager();
|
||||||
|
String taskLabel = td == null ? null : Utilities.trim(td.getLabel());
|
||||||
|
if (TextUtils.isEmpty(taskLabel)) {
|
||||||
|
taskLabel = Utilities.trim(info.loadLabel(pm));
|
||||||
|
}
|
||||||
|
|
||||||
|
String applicationLabel = Utilities.trim(info.applicationInfo.loadLabel(pm));
|
||||||
|
String badgedApplicationLabel = userId != UserHandle.myUserId()
|
||||||
|
? pm.getUserBadgedLabel(applicationLabel, UserHandle.of(userId)).toString()
|
||||||
|
: applicationLabel;
|
||||||
|
return applicationLabel.equals(taskLabel)
|
||||||
|
? badgedApplicationLabel : badgedApplicationLabel + " " + taskLabel;
|
||||||
|
}
|
||||||
|
|
||||||
@WorkerThread
|
@WorkerThread
|
||||||
private Drawable getDefaultIcon(int userId) {
|
private Drawable getDefaultIcon(int userId) {
|
||||||
synchronized (mDefaultIcons) {
|
synchronized (mDefaultIcons) {
|
||||||
|
|
|
@ -17,15 +17,20 @@ package com.android.quickstep;
|
||||||
|
|
||||||
import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
|
import static com.android.launcher3.LauncherAnimUtils.VIEW_ALPHA;
|
||||||
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
|
import static com.android.launcher3.LauncherState.BACKGROUND_APP;
|
||||||
|
import static com.android.launcher3.LauncherState.NORMAL;
|
||||||
|
import static com.android.launcher3.QuickstepAppTransitionManagerImpl.RECENTS_LAUNCH_DURATION;
|
||||||
import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
|
import static com.android.launcher3.Utilities.getDescendantCoordRelativeToAncestor;
|
||||||
import static com.android.launcher3.anim.Interpolators.LINEAR;
|
import static com.android.launcher3.anim.Interpolators.LINEAR;
|
||||||
import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
|
import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
|
||||||
import static com.android.launcher3.anim.Interpolators.clampToProgress;
|
import static com.android.launcher3.anim.Interpolators.clampToProgress;
|
||||||
|
import static com.android.launcher3.config.FeatureFlags.ENABLE_QUICKSTEP_LIVE_TILE;
|
||||||
import static com.android.launcher3.statehandlers.DepthController.DEPTH;
|
import static com.android.launcher3.statehandlers.DepthController.DEPTH;
|
||||||
|
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
|
||||||
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
|
import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
|
||||||
|
|
||||||
import android.animation.Animator;
|
import android.animation.Animator;
|
||||||
import android.animation.AnimatorListenerAdapter;
|
import android.animation.AnimatorListenerAdapter;
|
||||||
|
import android.animation.AnimatorSet;
|
||||||
import android.annotation.TargetApi;
|
import android.annotation.TargetApi;
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
@ -35,12 +40,18 @@ import android.graphics.RectF;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.android.launcher3.BaseActivity;
|
import com.android.launcher3.BaseActivity;
|
||||||
import com.android.launcher3.BaseDraggingActivity;
|
|
||||||
import com.android.launcher3.DeviceProfile;
|
import com.android.launcher3.DeviceProfile;
|
||||||
|
import com.android.launcher3.anim.AnimationSuccessListener;
|
||||||
|
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||||
|
import com.android.launcher3.anim.Interpolators;
|
||||||
import com.android.launcher3.anim.PendingAnimation;
|
import com.android.launcher3.anim.PendingAnimation;
|
||||||
import com.android.launcher3.model.data.ItemInfo;
|
import com.android.launcher3.model.data.ItemInfo;
|
||||||
import com.android.launcher3.statehandlers.DepthController;
|
import com.android.launcher3.statehandlers.DepthController;
|
||||||
|
import com.android.launcher3.statemanager.StateManager;
|
||||||
import com.android.launcher3.util.DisplayController;
|
import com.android.launcher3.util.DisplayController;
|
||||||
import com.android.quickstep.util.SurfaceTransactionApplier;
|
import com.android.quickstep.util.SurfaceTransactionApplier;
|
||||||
import com.android.quickstep.util.TaskViewSimulator;
|
import com.android.quickstep.util.TaskViewSimulator;
|
||||||
|
@ -49,6 +60,7 @@ import com.android.quickstep.views.RecentsView;
|
||||||
import com.android.quickstep.views.TaskThumbnailView;
|
import com.android.quickstep.views.TaskThumbnailView;
|
||||||
import com.android.quickstep.views.TaskView;
|
import com.android.quickstep.views.TaskView;
|
||||||
import com.android.systemui.shared.recents.model.Task;
|
import com.android.systemui.shared.recents.model.Task;
|
||||||
|
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
|
||||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -67,8 +79,7 @@ public final class TaskViewUtils {
|
||||||
* opening remote target (which we don't get until onAnimationStart) will resolve to a TaskView.
|
* opening remote target (which we don't get until onAnimationStart) will resolve to a TaskView.
|
||||||
*/
|
*/
|
||||||
public static TaskView findTaskViewToLaunch(
|
public static TaskView findTaskViewToLaunch(
|
||||||
BaseDraggingActivity activity, View v, RemoteAnimationTargetCompat[] targets) {
|
RecentsView recentsView, View v, RemoteAnimationTargetCompat[] targets) {
|
||||||
RecentsView recentsView = activity.getOverviewPanel();
|
|
||||||
if (v instanceof TaskView) {
|
if (v instanceof TaskView) {
|
||||||
TaskView taskView = (TaskView) v;
|
TaskView taskView = (TaskView) v;
|
||||||
return recentsView.isTaskViewVisible(taskView) ? taskView : null;
|
return recentsView.isTaskViewVisible(taskView) ? taskView : null;
|
||||||
|
@ -119,6 +130,21 @@ public final class TaskViewUtils {
|
||||||
return taskView;
|
return taskView;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void createRecentsWindowAnimator(TaskView v, boolean skipViewChanges,
|
||||||
|
RemoteAnimationTargetCompat[] appTargets,
|
||||||
|
RemoteAnimationTargetCompat[] wallpaperTargets, DepthController depthController,
|
||||||
|
PendingAnimation out) {
|
||||||
|
boolean isRunningTask = v.isRunningTask();
|
||||||
|
TransformParams params = null;
|
||||||
|
TaskViewSimulator tsv = null;
|
||||||
|
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && isRunningTask) {
|
||||||
|
params = v.getRecentsView().getLiveTileParams();
|
||||||
|
tsv = v.getRecentsView().getLiveTileTaskViewSimulator();
|
||||||
|
}
|
||||||
|
createRecentsWindowAnimator(v, skipViewChanges, appTargets, wallpaperTargets,
|
||||||
|
depthController, out, params, tsv);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates an animation that controls the window of the opening targets for the recents launch
|
* Creates an animation that controls the window of the opening targets for the recents launch
|
||||||
* animation.
|
* animation.
|
||||||
|
@ -126,16 +152,25 @@ public final class TaskViewUtils {
|
||||||
public static void createRecentsWindowAnimator(TaskView v, boolean skipViewChanges,
|
public static void createRecentsWindowAnimator(TaskView v, boolean skipViewChanges,
|
||||||
RemoteAnimationTargetCompat[] appTargets,
|
RemoteAnimationTargetCompat[] appTargets,
|
||||||
RemoteAnimationTargetCompat[] wallpaperTargets, DepthController depthController,
|
RemoteAnimationTargetCompat[] wallpaperTargets, DepthController depthController,
|
||||||
PendingAnimation out) {
|
PendingAnimation out, @Nullable TransformParams params,
|
||||||
|
@Nullable TaskViewSimulator tsv) {
|
||||||
|
boolean isQuickSwitch = v.isEndQuickswitchCuj();
|
||||||
|
v.setEndQuickswitchCuj(false);
|
||||||
|
|
||||||
SurfaceTransactionApplier applier = new SurfaceTransactionApplier(v);
|
boolean inLiveTileMode =
|
||||||
|
ENABLE_QUICKSTEP_LIVE_TILE.get() && v.getRecentsView().getRunningTaskIndex() != -1;
|
||||||
final RemoteAnimationTargets targets =
|
final RemoteAnimationTargets targets =
|
||||||
new RemoteAnimationTargets(appTargets, wallpaperTargets, MODE_OPENING);
|
new RemoteAnimationTargets(appTargets, wallpaperTargets,
|
||||||
|
inLiveTileMode ? MODE_CLOSING : MODE_OPENING);
|
||||||
|
|
||||||
|
if (params == null) {
|
||||||
|
SurfaceTransactionApplier applier = new SurfaceTransactionApplier(v);
|
||||||
targets.addReleaseCheck(applier);
|
targets.addReleaseCheck(applier);
|
||||||
|
|
||||||
TransformParams params = new TransformParams()
|
params = new TransformParams()
|
||||||
.setSyncTransactionApplier(applier)
|
.setSyncTransactionApplier(applier)
|
||||||
.setTargetSet(targets);
|
.setTargetSet(targets);
|
||||||
|
}
|
||||||
|
|
||||||
final RecentsView recentsView = v.getRecentsView();
|
final RecentsView recentsView = v.getRecentsView();
|
||||||
int taskIndex = recentsView.indexOfChild(v);
|
int taskIndex = recentsView.indexOfChild(v);
|
||||||
|
@ -149,8 +184,9 @@ public final class TaskViewUtils {
|
||||||
int displayRotation = DisplayController.getDefaultDisplay(context).getInfo().rotation;
|
int displayRotation = DisplayController.getDefaultDisplay(context).getInfo().rotation;
|
||||||
|
|
||||||
TaskViewSimulator topMostSimulator = null;
|
TaskViewSimulator topMostSimulator = null;
|
||||||
if (targets.apps.length > 0) {
|
|
||||||
TaskViewSimulator tsv = new TaskViewSimulator(context, recentsView.getSizeStrategy());
|
if (tsv == null && targets.apps.length > 0) {
|
||||||
|
tsv = new TaskViewSimulator(context, recentsView.getSizeStrategy());
|
||||||
tsv.setDp(dp);
|
tsv.setDp(dp);
|
||||||
tsv.setLayoutRotation(displayRotation, displayRotation);
|
tsv.setLayoutRotation(displayRotation, displayRotation);
|
||||||
tsv.setPreview(targets.apps[targets.apps.length - 1]);
|
tsv.setPreview(targets.apps[targets.apps.length - 1]);
|
||||||
|
@ -158,19 +194,24 @@ public final class TaskViewUtils {
|
||||||
tsv.recentsViewScale.value = 1;
|
tsv.recentsViewScale.value = 1;
|
||||||
tsv.setScroll(startScroll);
|
tsv.setScroll(startScroll);
|
||||||
|
|
||||||
|
// Fade in the task during the initial 20% of the animation
|
||||||
|
out.addFloat(params, TransformParams.TARGET_ALPHA, 0, 1,
|
||||||
|
clampToProgress(LINEAR, 0, 0.2f));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tsv != null) {
|
||||||
out.setFloat(tsv.fullScreenProgress,
|
out.setFloat(tsv.fullScreenProgress,
|
||||||
AnimatedFloat.VALUE, 1, TOUCH_RESPONSE_INTERPOLATOR);
|
AnimatedFloat.VALUE, 1, TOUCH_RESPONSE_INTERPOLATOR);
|
||||||
out.setFloat(tsv.recentsViewScale,
|
out.setFloat(tsv.recentsViewScale,
|
||||||
AnimatedFloat.VALUE, tsv.getFullScreenScale(), TOUCH_RESPONSE_INTERPOLATOR);
|
AnimatedFloat.VALUE, tsv.getFullScreenScale(), TOUCH_RESPONSE_INTERPOLATOR);
|
||||||
out.setInt(tsv, TaskViewSimulator.SCROLL, 0, TOUCH_RESPONSE_INTERPOLATOR);
|
out.setInt(tsv, TaskViewSimulator.SCROLL, 0, TOUCH_RESPONSE_INTERPOLATOR);
|
||||||
|
|
||||||
out.addOnFrameCallback(() -> tsv.apply(params));
|
TaskViewSimulator finalTsv = tsv;
|
||||||
|
TransformParams finalParams = params;
|
||||||
|
out.addOnFrameCallback(() -> finalTsv.apply(finalParams));
|
||||||
topMostSimulator = tsv;
|
topMostSimulator = tsv;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fade in the task during the initial 20% of the animation
|
|
||||||
out.addFloat(params, TransformParams.TARGET_ALPHA, 0, 1, clampToProgress(LINEAR, 0, 0.2f));
|
|
||||||
|
|
||||||
if (!skipViewChanges && parallaxCenterAndAdjacentTask && topMostSimulator != null) {
|
if (!skipViewChanges && parallaxCenterAndAdjacentTask && topMostSimulator != null) {
|
||||||
out.addFloat(v, VIEW_ALPHA, 1, 0, clampToProgress(LINEAR, 0.2f, 0.4f));
|
out.addFloat(v, VIEW_ALPHA, 1, 0, clampToProgress(LINEAR, 0.2f, 0.4f));
|
||||||
|
|
||||||
|
@ -223,10 +264,19 @@ public final class TaskViewUtils {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
out.addListener(new AnimatorListenerAdapter() {
|
out.addListener(new AnimationSuccessListener() {
|
||||||
|
@Override
|
||||||
|
public void onAnimationSuccess(Animator animator) {
|
||||||
|
if (isQuickSwitch) {
|
||||||
|
InteractionJankMonitorWrapper.end(
|
||||||
|
InteractionJankMonitorWrapper.CUJ_QUICK_SWITCH);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAnimationEnd(Animator animation) {
|
public void onAnimationEnd(Animator animation) {
|
||||||
targets.release();
|
targets.release();
|
||||||
|
super.onAnimationEnd(animation);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -235,4 +285,57 @@ public final class TaskViewUtils {
|
||||||
TOUCH_RESPONSE_INTERPOLATOR);
|
TOUCH_RESPONSE_INTERPOLATOR);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void composeRecentsLaunchAnimator(@NonNull AnimatorSet anim, @NonNull View v,
|
||||||
|
@NonNull RemoteAnimationTargetCompat[] appTargets,
|
||||||
|
@NonNull RemoteAnimationTargetCompat[] wallpaperTargets, boolean launcherClosing,
|
||||||
|
@NonNull StateManager stateManager, @NonNull RecentsView recentsView,
|
||||||
|
@NonNull DepthController depthController) {
|
||||||
|
boolean skipLauncherChanges = !launcherClosing;
|
||||||
|
|
||||||
|
TaskView taskView = findTaskViewToLaunch(recentsView, v, appTargets);
|
||||||
|
PendingAnimation pa = new PendingAnimation(RECENTS_LAUNCH_DURATION);
|
||||||
|
createRecentsWindowAnimator(taskView, skipLauncherChanges, appTargets, wallpaperTargets,
|
||||||
|
depthController, pa);
|
||||||
|
anim.play(pa.buildAnim());
|
||||||
|
|
||||||
|
Animator childStateAnimation = null;
|
||||||
|
// Found a visible recents task that matches the opening app, lets launch the app from there
|
||||||
|
Animator launcherAnim;
|
||||||
|
final AnimatorListenerAdapter windowAnimEndListener;
|
||||||
|
if (launcherClosing) {
|
||||||
|
launcherAnim = recentsView.createAdjacentPageAnimForTaskLaunch(taskView);
|
||||||
|
launcherAnim.setInterpolator(Interpolators.TOUCH_RESPONSE_INTERPOLATOR);
|
||||||
|
launcherAnim.setDuration(RECENTS_LAUNCH_DURATION);
|
||||||
|
|
||||||
|
// Make sure recents gets fixed up by resetting task alphas and scales, etc.
|
||||||
|
windowAnimEndListener = new AnimatorListenerAdapter() {
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation) {
|
||||||
|
stateManager.moveToRestState();
|
||||||
|
stateManager.reapplyState();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
AnimatorPlaybackController controller =
|
||||||
|
stateManager.createAnimationToNewWorkspace(NORMAL,
|
||||||
|
RECENTS_LAUNCH_DURATION);
|
||||||
|
controller.dispatchOnStart();
|
||||||
|
childStateAnimation = controller.getTarget();
|
||||||
|
launcherAnim = controller.getAnimationPlayer().setDuration(RECENTS_LAUNCH_DURATION);
|
||||||
|
windowAnimEndListener = new AnimatorListenerAdapter() {
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animation) {
|
||||||
|
stateManager.goToState(NORMAL, false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
anim.play(launcherAnim);
|
||||||
|
|
||||||
|
// Set the current animation first, before adding windowAnimEndListener. Setting current
|
||||||
|
// animation adds some listeners which need to be called before windowAnimEndListener
|
||||||
|
// (the ordering of listeners matter in this case).
|
||||||
|
stateManager.setCurrentAnimation(anim, childStateAnimation);
|
||||||
|
anim.addListener(windowAnimEndListener);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -737,9 +737,12 @@ public class TouchInteractionService extends Service implements PluginListener<O
|
||||||
private void reset() {
|
private void reset() {
|
||||||
mConsumer = mUncheckedConsumer = mResetGestureInputConsumer;
|
mConsumer = mUncheckedConsumer = mResetGestureInputConsumer;
|
||||||
mGestureState = DEFAULT_STATE;
|
mGestureState = DEFAULT_STATE;
|
||||||
// By default, use batching of the input events
|
// By default, use batching of the input events, but check receiver before using in the rare
|
||||||
|
// case that the monitor was disposed before the swipe settled
|
||||||
|
if (mInputEventReceiver != null) {
|
||||||
mInputEventReceiver.setBatchingEnabled(true);
|
mInputEventReceiver.setBatchingEnabled(true);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void preloadOverview(boolean fromInit) {
|
private void preloadOverview(boolean fromInit) {
|
||||||
if (!mDeviceState.isUserUnlocked()) {
|
if (!mDeviceState.isUserUnlocked()) {
|
||||||
|
@ -895,6 +898,12 @@ public class TouchInteractionService extends Service implements PluginListener<O
|
||||||
TouchInteractionServiceProto.Builder serviceProto =
|
TouchInteractionServiceProto.Builder serviceProto =
|
||||||
TouchInteractionServiceProto.newBuilder();
|
TouchInteractionServiceProto.newBuilder();
|
||||||
serviceProto.setServiceConnected(true);
|
serviceProto.setServiceConnected(true);
|
||||||
|
|
||||||
|
if (mOverviewComponentObserver != null) {
|
||||||
|
mOverviewComponentObserver.writeToProto(serviceProto);
|
||||||
|
}
|
||||||
|
mConsumer.writeToProto(serviceProto);
|
||||||
|
|
||||||
proto.setTouchInteractionService(serviceProto);
|
proto.setTouchInteractionService(serviceProto);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,21 +15,23 @@
|
||||||
*/
|
*/
|
||||||
package com.android.quickstep;
|
package com.android.quickstep;
|
||||||
|
|
||||||
import android.graphics.Canvas;
|
import android.os.Handler;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import com.android.systemui.shared.system.WindowCallbacksCompat;
|
import com.android.launcher3.Utilities;
|
||||||
|
import com.android.systemui.shared.system.ViewRootImplCompat;
|
||||||
|
|
||||||
import java.util.function.BooleanSupplier;
|
import java.util.function.BooleanSupplier;
|
||||||
|
import java.util.function.LongConsumer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Utility class for helpful methods related to {@link View} objects.
|
* Utility class for helpful methods related to {@link View} objects.
|
||||||
*/
|
*/
|
||||||
public class ViewUtils {
|
public class ViewUtils {
|
||||||
|
|
||||||
/** See {@link #postDraw(View, Runnable, BooleanSupplier)}} */
|
/** See {@link #postFrameDrawn(View, Runnable, BooleanSupplier)}} */
|
||||||
public static boolean postDraw(View view, Runnable onFinishRunnable) {
|
public static boolean postFrameDrawn(View view, Runnable onFinishRunnable) {
|
||||||
return postDraw(view, onFinishRunnable, () -> false);
|
return postFrameDrawn(view, onFinishRunnable, () -> false);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -38,37 +40,55 @@ public class ViewUtils {
|
||||||
*
|
*
|
||||||
* @param onFinishRunnable runnable to be run right after the view finishes drawing.
|
* @param onFinishRunnable runnable to be run right after the view finishes drawing.
|
||||||
*/
|
*/
|
||||||
public static boolean postDraw(View view, Runnable onFinishRunnable, BooleanSupplier canceled) {
|
public static boolean postFrameDrawn(
|
||||||
// Defer finishing the animation until the next launcher frame with the
|
View view, Runnable onFinishRunnable, BooleanSupplier canceled) {
|
||||||
// new thumbnail
|
return new FrameHandler(view, onFinishRunnable, canceled).schedule();
|
||||||
return new WindowCallbacksCompat(view) {
|
}
|
||||||
// The number of frames to defer until we actually finish the animation
|
|
||||||
private int mDeferFrameCount = 2;
|
private static class FrameHandler implements LongConsumer {
|
||||||
|
|
||||||
|
final ViewRootImplCompat mViewRoot;
|
||||||
|
final Runnable mFinishCallback;
|
||||||
|
final BooleanSupplier mCancelled;
|
||||||
|
final Handler mHandler;
|
||||||
|
|
||||||
|
int mDeferFrameCount = 1;
|
||||||
|
|
||||||
|
FrameHandler(View view, Runnable finishCallback, BooleanSupplier cancelled) {
|
||||||
|
mViewRoot = new ViewRootImplCompat(view);
|
||||||
|
mFinishCallback = finishCallback;
|
||||||
|
mCancelled = cancelled;
|
||||||
|
mHandler = new Handler();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPostDraw(Canvas canvas) {
|
public void accept(long l) {
|
||||||
// If we were cancelled after this was attached, do not update
|
Utilities.postAsyncCallback(mHandler, this::onFrame);
|
||||||
// the state.
|
}
|
||||||
if (canceled.getAsBoolean()) {
|
|
||||||
detach();
|
private void onFrame() {
|
||||||
|
if (mCancelled.getAsBoolean()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (mDeferFrameCount > 0) {
|
if (mDeferFrameCount > 0) {
|
||||||
mDeferFrameCount--;
|
mDeferFrameCount--;
|
||||||
// Workaround, detach and reattach to invalidate the root node for
|
schedule();
|
||||||
// another draw
|
|
||||||
detach();
|
|
||||||
attach();
|
|
||||||
view.invalidate();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (onFinishRunnable != null) {
|
if (mFinishCallback != null) {
|
||||||
onFinishRunnable.run();
|
mFinishCallback.run();
|
||||||
}
|
}
|
||||||
detach();
|
}
|
||||||
}
|
|
||||||
}.attach();
|
private boolean schedule() {
|
||||||
|
if (mViewRoot.isValid()) {
|
||||||
|
mViewRoot.registerRtFrameCallback(this);
|
||||||
|
mViewRoot.getView().invalidate();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ import android.view.MotionEvent;
|
||||||
|
|
||||||
import com.android.launcher3.testing.TestLogging;
|
import com.android.launcher3.testing.TestLogging;
|
||||||
import com.android.launcher3.testing.TestProtocol;
|
import com.android.launcher3.testing.TestProtocol;
|
||||||
|
import com.android.launcher3.tracing.InputConsumerProto;
|
||||||
import com.android.quickstep.InputConsumer;
|
import com.android.quickstep.InputConsumer;
|
||||||
import com.android.systemui.shared.system.InputMonitorCompat;
|
import com.android.systemui.shared.system.InputMonitorCompat;
|
||||||
|
|
||||||
|
@ -53,4 +54,9 @@ public abstract class DelegateInputConsumer implements InputConsumer {
|
||||||
mDelegate.onMotionEvent(event);
|
mDelegate.onMotionEvent(event);
|
||||||
event.recycle();
|
event.recycle();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToProtoInternal(InputConsumerProto.Builder inputConsumerProto) {
|
||||||
|
mDelegate.writeToProtoInternal(inputConsumerProto);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -51,6 +51,7 @@ import androidx.annotation.UiThread;
|
||||||
import com.android.launcher3.R;
|
import com.android.launcher3.R;
|
||||||
import com.android.launcher3.testing.TestLogging;
|
import com.android.launcher3.testing.TestLogging;
|
||||||
import com.android.launcher3.testing.TestProtocol;
|
import com.android.launcher3.testing.TestProtocol;
|
||||||
|
import com.android.launcher3.tracing.InputConsumerProto;
|
||||||
import com.android.launcher3.util.Preconditions;
|
import com.android.launcher3.util.Preconditions;
|
||||||
import com.android.launcher3.util.TraceHelper;
|
import com.android.launcher3.util.TraceHelper;
|
||||||
import com.android.quickstep.AbsSwipeUpHandler;
|
import com.android.quickstep.AbsSwipeUpHandler;
|
||||||
|
@ -478,4 +479,11 @@ public class OtherActivityInputConsumer extends ContextWrapper implements InputC
|
||||||
public boolean allowInterceptByParent() {
|
public boolean allowInterceptByParent() {
|
||||||
return !mPassedPilferInputSlop || mGestureState.hasState(STATE_OVERSCROLL_WINDOW_CREATED);
|
return !mPassedPilferInputSlop || mGestureState.hasState(STATE_OVERSCROLL_WINDOW_CREATED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToProtoInternal(InputConsumerProto.Builder inputConsumerProto) {
|
||||||
|
if (mInteractionHandler != null) {
|
||||||
|
mInteractionHandler.writeToProto(inputConsumerProto);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -104,7 +104,13 @@ final class AssistantGestureTutorialController extends TutorialController {
|
||||||
hideFeedback();
|
hideFeedback();
|
||||||
hideHandCoachingAnimation();
|
hideHandCoachingAnimation();
|
||||||
showRippleEffect(
|
showRippleEffect(
|
||||||
() -> mTutorialFragment.changeController(ASSISTANT_COMPLETE));
|
() -> {
|
||||||
|
if (mTutorialFragment.isTutorialComplete()) {
|
||||||
|
mTutorialFragment.changeController(ASSISTANT_COMPLETE);
|
||||||
|
} else {
|
||||||
|
mTutorialFragment.continueTutorial();
|
||||||
|
}
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
case ASSISTANT_NOT_STARTED_BAD_ANGLE:
|
case ASSISTANT_NOT_STARTED_BAD_ANGLE:
|
||||||
showFeedback(R.string.assistant_gesture_feedback_swipe_not_diagonal);
|
showFeedback(R.string.assistant_gesture_feedback_swipe_not_diagonal);
|
||||||
|
|
|
@ -130,7 +130,13 @@ final class BackGestureTutorialController extends TutorialController {
|
||||||
hideFeedback();
|
hideFeedback();
|
||||||
hideHandCoachingAnimation();
|
hideHandCoachingAnimation();
|
||||||
showRippleEffect(
|
showRippleEffect(
|
||||||
() -> mTutorialFragment.changeController(BACK_NAVIGATION_COMPLETE));
|
() -> {
|
||||||
|
if (mTutorialFragment.isTutorialComplete()) {
|
||||||
|
mTutorialFragment.changeController(BACK_NAVIGATION_COMPLETE);
|
||||||
|
} else {
|
||||||
|
mTutorialFragment.continueTutorial();
|
||||||
|
}
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
case BACK_CANCELLED_FROM_LEFT:
|
case BACK_CANCELLED_FROM_LEFT:
|
||||||
showFeedback(R.string.back_gesture_feedback_cancelled_left_edge);
|
showFeedback(R.string.back_gesture_feedback_cancelled_left_edge);
|
||||||
|
|
|
@ -15,8 +15,6 @@
|
||||||
*/
|
*/
|
||||||
package com.android.quickstep.interaction;
|
package com.android.quickstep.interaction;
|
||||||
|
|
||||||
import static com.android.quickstep.interaction.TutorialFragment.KEY_TUTORIAL_TYPE;
|
|
||||||
|
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
@ -25,11 +23,14 @@ import android.view.Display;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.Window;
|
import android.view.Window;
|
||||||
|
|
||||||
|
import androidx.annotation.NonNull;
|
||||||
import androidx.fragment.app.FragmentActivity;
|
import androidx.fragment.app.FragmentActivity;
|
||||||
|
|
||||||
import com.android.launcher3.R;
|
import com.android.launcher3.R;
|
||||||
import com.android.quickstep.interaction.TutorialController.TutorialType;
|
import com.android.quickstep.interaction.TutorialController.TutorialType;
|
||||||
|
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.Deque;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/** Shows the gesture interactive sandbox in full screen mode. */
|
/** Shows the gesture interactive sandbox in full screen mode. */
|
||||||
|
@ -37,6 +38,9 @@ public class GestureSandboxActivity extends FragmentActivity {
|
||||||
|
|
||||||
private static final String LOG_TAG = "GestureSandboxActivity";
|
private static final String LOG_TAG = "GestureSandboxActivity";
|
||||||
|
|
||||||
|
private static final String KEY_TUTORIAL_STEPS = "tutorial_steps";
|
||||||
|
|
||||||
|
private Deque<TutorialType> mTutorialSteps;
|
||||||
private TutorialFragment mFragment;
|
private TutorialFragment mFragment;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -45,7 +49,9 @@ public class GestureSandboxActivity extends FragmentActivity {
|
||||||
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||||
setContentView(R.layout.gesture_tutorial_activity);
|
setContentView(R.layout.gesture_tutorial_activity);
|
||||||
|
|
||||||
mFragment = TutorialFragment.newInstance(getTutorialType(getIntent().getExtras()));
|
Bundle args = savedInstanceState == null ? getIntent().getExtras() : savedInstanceState;
|
||||||
|
mTutorialSteps = getTutorialSteps(args);
|
||||||
|
mFragment = TutorialFragment.newInstance(mTutorialSteps.pop());
|
||||||
getSupportFragmentManager().beginTransaction()
|
getSupportFragmentManager().beginTransaction()
|
||||||
.add(R.id.gesture_tutorial_fragment_container, mFragment)
|
.add(R.id.gesture_tutorial_fragment_container, mFragment)
|
||||||
.commit();
|
.commit();
|
||||||
|
@ -72,17 +78,65 @@ public class GestureSandboxActivity extends FragmentActivity {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private TutorialType getTutorialType(Bundle extras) {
|
@Override
|
||||||
TutorialType defaultType = TutorialType.RIGHT_EDGE_BACK_NAVIGATION;
|
protected void onSaveInstanceState(@NonNull Bundle savedInstanceState) {
|
||||||
|
savedInstanceState.putStringArray(KEY_TUTORIAL_STEPS, getTutorialStepNames());
|
||||||
|
super.onSaveInstanceState(savedInstanceState);
|
||||||
|
}
|
||||||
|
|
||||||
if (extras == null || !extras.containsKey(KEY_TUTORIAL_TYPE)) {
|
/** Returns true iff there aren't anymore tutorial types to display to the user. */
|
||||||
return defaultType;
|
public boolean isTutorialComplete() {
|
||||||
|
return mTutorialSteps.isEmpty();
|
||||||
}
|
}
|
||||||
try {
|
|
||||||
return TutorialType.valueOf(extras.getString(KEY_TUTORIAL_TYPE, ""));
|
/**
|
||||||
} catch (IllegalArgumentException e) {
|
* Replaces the current TutorialFragment, continuing to the next tutorial step if there is one.
|
||||||
return defaultType;
|
*
|
||||||
|
* If there is no following step, the tutorial is closed.
|
||||||
|
*/
|
||||||
|
public void continueTutorial() {
|
||||||
|
if (isTutorialComplete()) {
|
||||||
|
mFragment.closeTutorial();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
mFragment = TutorialFragment.newInstance(mTutorialSteps.pop());
|
||||||
|
getSupportFragmentManager().beginTransaction()
|
||||||
|
.replace(R.id.gesture_tutorial_fragment_container, mFragment)
|
||||||
|
.runOnCommit(() -> mFragment.onAttachedToWindow())
|
||||||
|
.commit();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String[] getTutorialStepNames() {
|
||||||
|
String[] tutorialStepNames = new String[mTutorialSteps.size()];
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
for (TutorialType tutorialStep : mTutorialSteps) {
|
||||||
|
tutorialStepNames[i++] = tutorialStep.name();
|
||||||
|
}
|
||||||
|
|
||||||
|
return tutorialStepNames;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Deque<TutorialType> getTutorialSteps(Bundle extras) {
|
||||||
|
Deque<TutorialType> defaultSteps = new ArrayDeque<>();
|
||||||
|
defaultSteps.push(TutorialType.RIGHT_EDGE_BACK_NAVIGATION);
|
||||||
|
|
||||||
|
if (extras == null || !extras.containsKey(KEY_TUTORIAL_STEPS)) {
|
||||||
|
return defaultSteps;
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] tutorialStepNames = extras.getStringArray(KEY_TUTORIAL_STEPS);
|
||||||
|
|
||||||
|
if (tutorialStepNames == null) {
|
||||||
|
return defaultSteps;
|
||||||
|
}
|
||||||
|
|
||||||
|
Deque<TutorialType> tutorialSteps = new ArrayDeque<>();
|
||||||
|
for (String tutorialStepName : tutorialStepNames) {
|
||||||
|
tutorialSteps.addLast(TutorialType.valueOf(tutorialStepName));
|
||||||
|
}
|
||||||
|
|
||||||
|
return tutorialSteps;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void hideSystemUI() {
|
private void hideSystemUI() {
|
||||||
|
|
|
@ -94,8 +94,13 @@ final class HomeGestureTutorialController extends SwipeUpGestureTutorialControll
|
||||||
case HOME_NAVIGATION:
|
case HOME_NAVIGATION:
|
||||||
switch (result) {
|
switch (result) {
|
||||||
case HOME_GESTURE_COMPLETED: {
|
case HOME_GESTURE_COMPLETED: {
|
||||||
animateFakeTaskViewHome(finalVelocity, () ->
|
animateFakeTaskViewHome(finalVelocity, () -> {
|
||||||
mTutorialFragment.changeController(HOME_NAVIGATION_COMPLETE));
|
if (mTutorialFragment.isTutorialComplete()) {
|
||||||
|
mTutorialFragment.changeController(HOME_NAVIGATION_COMPLETE);
|
||||||
|
} else {
|
||||||
|
mTutorialFragment.continueTutorial();
|
||||||
|
}
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case HOME_NOT_STARTED_TOO_FAR_FROM_EDGE:
|
case HOME_NOT_STARTED_TOO_FAR_FROM_EDGE:
|
||||||
|
|
|
@ -104,8 +104,13 @@ final class OverviewGestureTutorialController extends SwipeUpGestureTutorialCont
|
||||||
showFeedback(R.string.overview_gesture_feedback_swipe_too_far_from_edge);
|
showFeedback(R.string.overview_gesture_feedback_swipe_too_far_from_edge);
|
||||||
break;
|
break;
|
||||||
case OVERVIEW_GESTURE_COMPLETED:
|
case OVERVIEW_GESTURE_COMPLETED:
|
||||||
fadeOutFakeTaskView(true, () ->
|
fadeOutFakeTaskView(true, () -> {
|
||||||
mTutorialFragment.changeController(OVERVIEW_NAVIGATION_COMPLETE));
|
if (mTutorialFragment.isTutorialComplete()) {
|
||||||
|
mTutorialFragment.changeController(OVERVIEW_NAVIGATION_COMPLETE);
|
||||||
|
} else {
|
||||||
|
mTutorialFragment.continueTutorial();
|
||||||
|
}
|
||||||
|
});
|
||||||
break;
|
break;
|
||||||
case HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION:
|
case HOME_OR_OVERVIEW_NOT_STARTED_WRONG_SWIPE_DIRECTION:
|
||||||
case HOME_OR_OVERVIEW_CANCELLED:
|
case HOME_OR_OVERVIEW_CANCELLED:
|
||||||
|
|
|
@ -54,6 +54,7 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener {
|
||||||
fragment = new BackGestureTutorialFragment();
|
fragment = new BackGestureTutorialFragment();
|
||||||
tutorialType = TutorialType.RIGHT_EDGE_BACK_NAVIGATION;
|
tutorialType = TutorialType.RIGHT_EDGE_BACK_NAVIGATION;
|
||||||
}
|
}
|
||||||
|
|
||||||
Bundle args = new Bundle();
|
Bundle args = new Bundle();
|
||||||
args.putSerializable(KEY_TUTORIAL_TYPE, tutorialType);
|
args.putSerializable(KEY_TUTORIAL_TYPE, tutorialType);
|
||||||
fragment.setArguments(args);
|
fragment.setArguments(args);
|
||||||
|
@ -197,6 +198,20 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener {
|
||||||
return mHandCoachingAnimation;
|
return mHandCoachingAnimation;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void continueTutorial() {
|
||||||
|
if (!(getContext() instanceof GestureSandboxActivity)) {
|
||||||
|
closeTutorial();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
GestureSandboxActivity gestureSandboxActivity = (GestureSandboxActivity) getContext();
|
||||||
|
|
||||||
|
if (gestureSandboxActivity == null) {
|
||||||
|
closeTutorial();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
gestureSandboxActivity.continueTutorial();
|
||||||
|
}
|
||||||
|
|
||||||
void closeTutorial() {
|
void closeTutorial() {
|
||||||
FragmentActivity activity = getActivity();
|
FragmentActivity activity = getActivity();
|
||||||
if (activity != null) {
|
if (activity != null) {
|
||||||
|
@ -207,4 +222,13 @@ abstract class TutorialFragment extends Fragment implements OnTouchListener {
|
||||||
void startSystemNavigationSetting() {
|
void startSystemNavigationSetting() {
|
||||||
startActivity(new Intent("com.android.settings.GESTURE_NAVIGATION_SETTINGS"));
|
startActivity(new Intent("com.android.settings.GESTURE_NAVIGATION_SETTINGS"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
boolean isTutorialComplete() {
|
||||||
|
if (!(getContext() instanceof GestureSandboxActivity)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
GestureSandboxActivity gestureSandboxActivity = (GestureSandboxActivity) getContext();
|
||||||
|
|
||||||
|
return gestureSandboxActivity == null || gestureSandboxActivity.isTutorialComplete();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import static android.view.Surface.ROTATION_180;
|
||||||
import static android.view.Surface.ROTATION_270;
|
import static android.view.Surface.ROTATION_270;
|
||||||
import static android.view.Surface.ROTATION_90;
|
import static android.view.Surface.ROTATION_90;
|
||||||
|
|
||||||
|
import static com.android.launcher3.Utilities.newContentObserver;
|
||||||
import static com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY;
|
import static com.android.launcher3.states.RotationHelper.ALLOW_ROTATION_PREFERENCE_KEY;
|
||||||
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
|
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
|
||||||
import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
|
import static com.android.quickstep.SysUINavigationMode.Mode.TWO_BUTTONS;
|
||||||
|
@ -73,12 +74,9 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre
|
||||||
private static final boolean DEBUG = false;
|
private static final boolean DEBUG = false;
|
||||||
private static final String DELIMITER_DOT = "\\.";
|
private static final String DELIMITER_DOT = "\\.";
|
||||||
|
|
||||||
private ContentObserver mSystemAutoRotateObserver = new ContentObserver(new Handler()) {
|
private ContentObserver mSystemAutoRotateObserver =
|
||||||
@Override
|
newContentObserver(new Handler(), t -> updateAutoRotateSetting());
|
||||||
public void onChange(boolean selfChange) {
|
|
||||||
updateAutoRotateSetting();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@Retention(SOURCE)
|
@Retention(SOURCE)
|
||||||
@IntDef({ROTATION_0, ROTATION_90, ROTATION_180, ROTATION_270})
|
@IntDef({ROTATION_0, ROTATION_90, ROTATION_180, ROTATION_270})
|
||||||
public @interface SurfaceRotation {}
|
public @interface SurfaceRotation {}
|
||||||
|
@ -541,6 +539,7 @@ public final class RecentsOrientedState implements SharedPreferences.OnSharedPre
|
||||||
* @return "MyObject@1234"
|
* @return "MyObject@1234"
|
||||||
*/
|
*/
|
||||||
private static String extractObjectNameAndAddress(String stringToExtract) {
|
private static String extractObjectNameAndAddress(String stringToExtract) {
|
||||||
return stringToExtract.substring(stringToExtract.lastIndexOf(DELIMITER_DOT));
|
int index = stringToExtract.lastIndexOf(DELIMITER_DOT);
|
||||||
|
return index >= 0 ? stringToExtract.substring(index) : "";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -322,7 +322,8 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
|
||||||
Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
|
Builder builder, RemoteAnimationTargetCompat app, TransformParams params) {
|
||||||
builder.withMatrix(mMatrix)
|
builder.withMatrix(mMatrix)
|
||||||
.withWindowCrop(mTmpCropRect)
|
.withWindowCrop(mTmpCropRect)
|
||||||
.withCornerRadius(getCurrentCornerRadius());
|
.withCornerRadius(getCurrentCornerRadius())
|
||||||
|
.withShadowRadius(params.getShadowRadius());
|
||||||
|
|
||||||
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && params.getRecentsSurface() != null) {
|
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && params.getRecentsSurface() != null) {
|
||||||
// When relativeLayer = 0, it reverts the surfaces back to the original order.
|
// When relativeLayer = 0, it reverts the surfaces back to the original order.
|
||||||
|
|
|
@ -57,6 +57,7 @@ public class TransformParams {
|
||||||
private float mProgress;
|
private float mProgress;
|
||||||
private float mTargetAlpha;
|
private float mTargetAlpha;
|
||||||
private float mCornerRadius;
|
private float mCornerRadius;
|
||||||
|
private float mShadowRadius;
|
||||||
private RemoteAnimationTargets mTargetSet;
|
private RemoteAnimationTargets mTargetSet;
|
||||||
private SurfaceTransactionApplier mSyncTransactionApplier;
|
private SurfaceTransactionApplier mSyncTransactionApplier;
|
||||||
private SurfaceControl mRecentsSurface;
|
private SurfaceControl mRecentsSurface;
|
||||||
|
@ -68,6 +69,7 @@ public class TransformParams {
|
||||||
mProgress = 0;
|
mProgress = 0;
|
||||||
mTargetAlpha = 1;
|
mTargetAlpha = 1;
|
||||||
mCornerRadius = -1;
|
mCornerRadius = -1;
|
||||||
|
mShadowRadius = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -90,6 +92,14 @@ public class TransformParams {
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the shadow radius of the transformed window, in pixels.
|
||||||
|
*/
|
||||||
|
public TransformParams setShadowRadius(float shadowRadius) {
|
||||||
|
mShadowRadius = shadowRadius;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specifies the alpha of the transformed window. Default is 1.
|
* Specifies the alpha of the transformed window. Default is 1.
|
||||||
*/
|
*/
|
||||||
|
@ -197,6 +207,10 @@ public class TransformParams {
|
||||||
return mCornerRadius;
|
return mCornerRadius;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public float getShadowRadius() {
|
||||||
|
return mShadowRadius;
|
||||||
|
}
|
||||||
|
|
||||||
public SurfaceControl getRecentsSurface() {
|
public SurfaceControl getRecentsSurface() {
|
||||||
return mRecentsSurface;
|
return mRecentsSurface;
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,6 +65,10 @@ public class LiveTileOverlay extends Drawable {
|
||||||
invalidateSelf();
|
invalidateSelf();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void update(float left, float top, float right, float bottom) {
|
||||||
|
mCurrentRect.set(left, top, right, bottom);
|
||||||
|
}
|
||||||
|
|
||||||
public void setIcon(Drawable icon) {
|
public void setIcon(Drawable icon) {
|
||||||
mIcon = icon;
|
mIcon = icon;
|
||||||
}
|
}
|
||||||
|
@ -94,7 +98,6 @@ public class LiveTileOverlay extends Drawable {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void draw(Canvas canvas) {
|
public void draw(Canvas canvas) {
|
||||||
if (mCurrentRect != null) {
|
|
||||||
canvas.drawRoundRect(mCurrentRect, mCornerRadius, mCornerRadius, mPaint);
|
canvas.drawRoundRect(mCurrentRect, mCornerRadius, mCornerRadius, mPaint);
|
||||||
if (mIcon != null && mIconAnimationProgress > 0f) {
|
if (mIcon != null && mIconAnimationProgress > 0f) {
|
||||||
canvas.save();
|
canvas.save();
|
||||||
|
@ -107,7 +110,6 @@ public class LiveTileOverlay extends Drawable {
|
||||||
canvas.restore();
|
canvas.restore();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setAlpha(int i) { }
|
public void setAlpha(int i) { }
|
||||||
|
|
|
@ -230,6 +230,7 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
|
||||||
view.setScaleX(scale);
|
view.setScaleX(scale);
|
||||||
view.setScaleY(scale);
|
view.setScaleY(scale);
|
||||||
view.mLastComputedTaskPushOutDistance = null;
|
view.mLastComputedTaskPushOutDistance = null;
|
||||||
|
view.mLiveTileTaskViewSimulator.recentsViewScale.value = scale;
|
||||||
view.updatePageOffsets();
|
view.updatePageOffsets();
|
||||||
view.setTaskViewsSecondaryTranslation(view.mTaskViewsSecondaryTranslation);
|
view.setTaskViewsSecondaryTranslation(view.mTaskViewsSecondaryTranslation);
|
||||||
}
|
}
|
||||||
|
@ -539,6 +540,9 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
|
||||||
@Override
|
@Override
|
||||||
protected void onWindowVisibilityChanged(int visibility) {
|
protected void onWindowVisibilityChanged(int visibility) {
|
||||||
super.onWindowVisibilityChanged(visibility);
|
super.onWindowVisibilityChanged(visibility);
|
||||||
|
if (visibility == GONE && ENABLE_QUICKSTEP_LIVE_TILE.get()) {
|
||||||
|
finishRecentsAnimation(true /* toRecents */, null);
|
||||||
|
}
|
||||||
updateTaskStackListenerState();
|
updateTaskStackListenerState();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -873,6 +877,10 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
|
||||||
mLiveTileTaskViewSimulator.fullScreenProgress.value = 0;
|
mLiveTileTaskViewSimulator.fullScreenProgress.value = 0;
|
||||||
mLiveTileTaskViewSimulator.recentsViewScale.value = 1;
|
mLiveTileTaskViewSimulator.recentsViewScale.value = 1;
|
||||||
mLiveTileTaskViewSimulator.setOffsetY(0);
|
mLiveTileTaskViewSimulator.setOffsetY(0);
|
||||||
|
|
||||||
|
// Reset the live tile rect
|
||||||
|
DeviceProfile deviceProfile = mActivity.getDeviceProfile();
|
||||||
|
LiveTileOverlay.INSTANCE.update(0, 0, deviceProfile.widthPx, deviceProfile.heightPx);
|
||||||
}
|
}
|
||||||
if (mRunningTaskTileHidden) {
|
if (mRunningTaskTileHidden) {
|
||||||
setRunningTaskHidden(mRunningTaskTileHidden);
|
setRunningTaskHidden(mRunningTaskTileHidden);
|
||||||
|
@ -1292,19 +1300,26 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
|
||||||
}
|
}
|
||||||
|
|
||||||
public void showNextTask() {
|
public void showNextTask() {
|
||||||
TaskView runningTaskView = getRunningTaskView();
|
final TaskView runningTaskView = getRunningTaskView();
|
||||||
|
final TaskView targetTask;
|
||||||
|
|
||||||
if (runningTaskView == null) {
|
if (runningTaskView == null) {
|
||||||
// Launch the first task
|
// Launch the first task
|
||||||
if (getTaskViewCount() > 0) {
|
if (getTaskViewCount() > 0) {
|
||||||
getTaskViewAt(0).launchTask(true);
|
targetTask = getTaskViewAt(0);
|
||||||
|
} else {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (getNextTaskView() != null) {
|
final TaskView nextTask = getNextTaskView();
|
||||||
getNextTaskView().launchTask(true);
|
if (nextTask != null) {
|
||||||
|
targetTask = nextTask;
|
||||||
} else {
|
} else {
|
||||||
runningTaskView.launchTask(true);
|
targetTask = runningTaskView;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
targetTask.setEndQuickswitchCuj(true);
|
||||||
|
targetTask.launchTask(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setRunningTaskIconScaledDown(boolean isScaledDown) {
|
public void setRunningTaskIconScaledDown(boolean isScaledDown) {
|
||||||
|
@ -1976,6 +1991,7 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
|
||||||
TaskView task = getTaskViewAt(i);
|
TaskView task = getTaskViewAt(i);
|
||||||
mOrientationHandler.getSecondaryViewTranslate().set(task, translation / getScaleY());
|
mOrientationHandler.getSecondaryViewTranslate().set(task, translation / getScaleY());
|
||||||
}
|
}
|
||||||
|
mLiveTileTaskViewSimulator.recentsViewSecondaryTranslation.value = translation;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2254,6 +2270,10 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
|
||||||
return mLiveTileTaskViewSimulator;
|
return mLiveTileTaskViewSimulator;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TransformParams getLiveTileParams() {
|
||||||
|
return mLiveTileParams;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: To be removed in a follow up CL
|
// TODO: To be removed in a follow up CL
|
||||||
public void setRecentsAnimationTargets(RecentsAnimationController recentsAnimationController,
|
public void setRecentsAnimationTargets(RecentsAnimationController recentsAnimationController,
|
||||||
RecentsAnimationTargets recentsAnimationTargets) {
|
RecentsAnimationTargets recentsAnimationTargets) {
|
||||||
|
@ -2442,7 +2462,7 @@ public abstract class RecentsView<T extends StatefulActivity> extends PagedView
|
||||||
} else {
|
} else {
|
||||||
taskView.getThumbnail().refresh();
|
taskView.getThumbnail().refresh();
|
||||||
}
|
}
|
||||||
ViewUtils.postDraw(taskView, onFinishRunnable);
|
ViewUtils.postFrameDrawn(taskView, onFinishRunnable);
|
||||||
} else {
|
} else {
|
||||||
onFinishRunnable.run();
|
onFinishRunnable.run();
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,6 +39,7 @@ import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCH
|
||||||
|
|
||||||
import android.animation.Animator;
|
import android.animation.Animator;
|
||||||
import android.animation.AnimatorListenerAdapter;
|
import android.animation.AnimatorListenerAdapter;
|
||||||
|
import android.animation.AnimatorSet;
|
||||||
import android.animation.ObjectAnimator;
|
import android.animation.ObjectAnimator;
|
||||||
import android.animation.TimeInterpolator;
|
import android.animation.TimeInterpolator;
|
||||||
import android.animation.ValueAnimator;
|
import android.animation.ValueAnimator;
|
||||||
|
@ -65,7 +66,6 @@ import android.view.accessibility.AccessibilityNodeInfo;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
|
||||||
import com.android.launcher3.BaseDraggingActivity;
|
|
||||||
import com.android.launcher3.DeviceProfile;
|
import com.android.launcher3.DeviceProfile;
|
||||||
import com.android.launcher3.LauncherSettings;
|
import com.android.launcher3.LauncherSettings;
|
||||||
import com.android.launcher3.R;
|
import com.android.launcher3.R;
|
||||||
|
@ -75,6 +75,7 @@ import com.android.launcher3.anim.Interpolators;
|
||||||
import com.android.launcher3.anim.PendingAnimation;
|
import com.android.launcher3.anim.PendingAnimation;
|
||||||
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
||||||
import com.android.launcher3.popup.SystemShortcut;
|
import com.android.launcher3.popup.SystemShortcut;
|
||||||
|
import com.android.launcher3.statemanager.StatefulActivity;
|
||||||
import com.android.launcher3.testing.TestLogging;
|
import com.android.launcher3.testing.TestLogging;
|
||||||
import com.android.launcher3.testing.TestProtocol;
|
import com.android.launcher3.testing.TestProtocol;
|
||||||
import com.android.launcher3.touch.PagedOrientationHandler;
|
import com.android.launcher3.touch.PagedOrientationHandler;
|
||||||
|
@ -82,10 +83,12 @@ import com.android.launcher3.util.ComponentKey;
|
||||||
import com.android.launcher3.util.TransformingTouchDelegate;
|
import com.android.launcher3.util.TransformingTouchDelegate;
|
||||||
import com.android.launcher3.util.ViewPool.Reusable;
|
import com.android.launcher3.util.ViewPool.Reusable;
|
||||||
import com.android.quickstep.RecentsModel;
|
import com.android.quickstep.RecentsModel;
|
||||||
|
import com.android.quickstep.RemoteAnimationTargets;
|
||||||
import com.android.quickstep.TaskIconCache;
|
import com.android.quickstep.TaskIconCache;
|
||||||
import com.android.quickstep.TaskOverlayFactory;
|
import com.android.quickstep.TaskOverlayFactory;
|
||||||
import com.android.quickstep.TaskThumbnailCache;
|
import com.android.quickstep.TaskThumbnailCache;
|
||||||
import com.android.quickstep.TaskUtils;
|
import com.android.quickstep.TaskUtils;
|
||||||
|
import com.android.quickstep.TaskViewUtils;
|
||||||
import com.android.quickstep.util.CancellableTask;
|
import com.android.quickstep.util.CancellableTask;
|
||||||
import com.android.quickstep.util.RecentsOrientedState;
|
import com.android.quickstep.util.RecentsOrientedState;
|
||||||
import com.android.quickstep.util.TaskCornerRadius;
|
import com.android.quickstep.util.TaskCornerRadius;
|
||||||
|
@ -175,7 +178,7 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
|
||||||
private float mCurveScale;
|
private float mCurveScale;
|
||||||
private float mFullscreenProgress;
|
private float mFullscreenProgress;
|
||||||
private final FullscreenDrawParams mCurrentFullscreenParams;
|
private final FullscreenDrawParams mCurrentFullscreenParams;
|
||||||
private final BaseDraggingActivity mActivity;
|
private final StatefulActivity mActivity;
|
||||||
|
|
||||||
private ObjectAnimator mIconAndDimAnimator;
|
private ObjectAnimator mIconAndDimAnimator;
|
||||||
private float mIconScaleAnimStartProgress = 0;
|
private float mIconScaleAnimStartProgress = 0;
|
||||||
|
@ -189,6 +192,8 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
|
||||||
private CancellableTask mThumbnailLoadRequest;
|
private CancellableTask mThumbnailLoadRequest;
|
||||||
private CancellableTask mIconLoadRequest;
|
private CancellableTask mIconLoadRequest;
|
||||||
|
|
||||||
|
private boolean mEndQuickswitchCuj;
|
||||||
|
|
||||||
// Order in which the footers appear. Lower order appear below higher order.
|
// Order in which the footers appear. Lower order appear below higher order.
|
||||||
public static final int INDEX_DIGITAL_WELLBEING_TOAST = 0;
|
public static final int INDEX_DIGITAL_WELLBEING_TOAST = 0;
|
||||||
private final FooterWrapper[] mFooters = new FooterWrapper[2];
|
private final FooterWrapper[] mFooters = new FooterWrapper[2];
|
||||||
|
@ -210,18 +215,31 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
|
||||||
|
|
||||||
public TaskView(Context context, AttributeSet attrs, int defStyleAttr) {
|
public TaskView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
super(context, attrs, defStyleAttr);
|
super(context, attrs, defStyleAttr);
|
||||||
mActivity = BaseDraggingActivity.fromContext(context);
|
mActivity = StatefulActivity.fromContext(context);
|
||||||
setOnClickListener((view) -> {
|
setOnClickListener((view) -> {
|
||||||
if (getTask() == null) {
|
if (getTask() == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (ENABLE_QUICKSTEP_LIVE_TILE.get()) {
|
if (ENABLE_QUICKSTEP_LIVE_TILE.get() && isRunningTask()) {
|
||||||
if (isRunningTask()) {
|
RecentsView recentsView = getRecentsView();
|
||||||
// TODO: Replace this animation with createRecentsWindowAnimator
|
RemoteAnimationTargets targets = recentsView.getLiveTileParams().getTargetSet();
|
||||||
createLaunchAnimationForRunningTask().start();
|
recentsView.getLiveTileTaskViewSimulator().setDrawsBelowRecents(false);
|
||||||
} else {
|
|
||||||
launchTask(true /* animate */);
|
AnimatorSet anim = new AnimatorSet();
|
||||||
|
TaskViewUtils.composeRecentsLaunchAnimator(
|
||||||
|
anim, this, targets.apps,
|
||||||
|
targets.wallpapers, true /* launcherClosing */,
|
||||||
|
mActivity.getStateManager(), recentsView,
|
||||||
|
recentsView.getDepthController());
|
||||||
|
anim.addListener(new AnimatorListenerAdapter() {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAnimationEnd(Animator animator) {
|
||||||
|
recentsView.getLiveTileTaskViewSimulator().setDrawsBelowRecents(true);
|
||||||
|
recentsView.finishRecentsAnimation(false, null);
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
anim.start();
|
||||||
} else {
|
} else {
|
||||||
launchTask(true /* animate */);
|
launchTask(true /* animate */);
|
||||||
}
|
}
|
||||||
|
@ -801,6 +819,14 @@ public class TaskView extends FrameLayout implements PageCallbacks, Reusable {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isEndQuickswitchCuj() {
|
||||||
|
return mEndQuickswitchCuj;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setEndQuickswitchCuj(boolean endQuickswitchCuj) {
|
||||||
|
mEndQuickswitchCuj = endQuickswitchCuj;
|
||||||
|
}
|
||||||
|
|
||||||
private static final class TaskOutlineProvider extends ViewOutlineProvider {
|
private static final class TaskOutlineProvider extends ViewOutlineProvider {
|
||||||
|
|
||||||
private final int mMarginTop;
|
private final int mMarginTop;
|
||||||
|
|
|
@ -19,6 +19,6 @@
|
||||||
android:viewportHeight="24.0"
|
android:viewportHeight="24.0"
|
||||||
android:viewportWidth="24.0">
|
android:viewportWidth="24.0">
|
||||||
<path
|
<path
|
||||||
android:fillColor="?android:attr/colorAccent"
|
android:fillColor="?android:attr/textColorTertiary"
|
||||||
android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z" />
|
android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z" />
|
||||||
</vector>
|
</vector>
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2016 The Android Open Source Project
|
||||||
<!-- Copyright (C) 2016 The Android Open Source Project
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -16,8 +15,7 @@
|
||||||
<!-- The top and bottom paddings are defined in this container, but since we want
|
<!-- The top and bottom paddings are defined in this container, but since we want
|
||||||
the list view to span the full width (for touch interception purposes), we
|
the list view to span the full width (for touch interception purposes), we
|
||||||
will bake the left/right padding into that view's background itself. -->
|
will bake the left/right padding into that view's background itself. -->
|
||||||
<com.android.launcher3.allapps.LauncherAllAppsContainerView
|
<com.android.launcher3.allapps.LauncherAllAppsContainerView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:id="@+id/apps_view"
|
android:id="@+id/apps_view"
|
||||||
android:theme="?attr/allAppsTheme"
|
android:theme="?attr/allAppsTheme"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|
|
@ -1,5 +1,4 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2015 The Android Open Source Project
|
||||||
<!-- Copyright (C) 2015 The Android Open Source Project
|
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -13,16 +12,10 @@
|
||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
-->
|
-->
|
||||||
<com.android.launcher3.BubbleTextView
|
<com.android.launcher3.BubbleTextView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
xmlns:launcher="http://schemas.android.com/apk/res-auto"
|
xmlns:launcher="http://schemas.android.com/apk/res-auto"
|
||||||
style="@style/BaseIcon"
|
style="@style/BaseIcon.AllApps"
|
||||||
android:id="@+id/icon"
|
android:id="@+id/icon"
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:stateListAnimator="@animator/all_apps_fastscroll_icon_anim"
|
|
||||||
launcher:iconDisplay="all_apps"
|
launcher:iconDisplay="all_apps"
|
||||||
launcher:centerVertically="true"
|
launcher:centerVertically="true" />
|
||||||
android:paddingLeft="@dimen/dynamic_grid_cell_padding_x"
|
|
||||||
android:paddingRight="@dimen/dynamic_grid_cell_padding_x" />
|
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,6 @@
|
||||||
android:clipChildren="true"
|
android:clipChildren="true"
|
||||||
android:clipToPadding="false"
|
android:clipToPadding="false"
|
||||||
android:descendantFocusability="afterDescendants"
|
android:descendantFocusability="afterDescendants"
|
||||||
android:paddingTop="@dimen/all_apps_header_top_padding"
|
|
||||||
launcher:pageIndicator="@+id/tabs" >
|
launcher:pageIndicator="@+id/tabs" >
|
||||||
|
|
||||||
<include layout="@layout/all_apps_rv_layout" />
|
<include layout="@layout/all_apps_rv_layout" />
|
||||||
|
|
|
@ -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");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
|
|
@ -67,7 +67,6 @@
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="start|center_vertical"
|
android:layout_gravity="start|center_vertical"
|
||||||
android:background="?android:attr/selectableItemBackground"
|
|
||||||
android:text="@string/search_action_try_now">
|
android:text="@string/search_action_try_now">
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
|
|
@ -12,27 +12,24 @@
|
||||||
See the License for the specific language governing permissions and
|
See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
-->
|
-->
|
||||||
<com.android.launcher3.views.SearchResultSuggestRow xmlns:android="http://schemas.android.com/apk/res/android"
|
<com.android.launcher3.views.SearchResultIconRow xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
style="@style/TextHeadline"
|
xmlns:launcher="http://schemas.android.com/apk/res-auto"
|
||||||
android:id="@+id/section_title"
|
style="@style/BaseIcon"
|
||||||
android:background="?android:attr/selectableItemBackground"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical"
|
android:orientation="horizontal"
|
||||||
android:gravity="center_vertical"
|
android:gravity="start|center_vertical"
|
||||||
android:padding="4dp"
|
android:textAlignment="viewStart"
|
||||||
android:minHeight="48dp"
|
|
||||||
android:textColor="?android:attr/textColorPrimary"
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
android:textSize="14sp">
|
android:textSize="16sp"
|
||||||
|
android:padding="@dimen/dynamic_grid_edge_margin"
|
||||||
|
launcher:iconDisplay="hero_app"
|
||||||
|
android:drawableTint="?android:attr/textColorPrimary"
|
||||||
|
launcher:customIcon="@drawable/ic_allapps_search"
|
||||||
|
launcher:iconSizeOverride="24dp"
|
||||||
|
launcher:matchTextInsetWithQuery="true"
|
||||||
|
launcher:layoutHorizontal="true"
|
||||||
|
android:drawablePadding="@dimen/dynamic_grid_icon_drawable_padding"
|
||||||
|
>
|
||||||
|
|
||||||
<TextView
|
</com.android.launcher3.views.SearchResultIconRow>
|
||||||
android:id="@+id/title"
|
|
||||||
style="@style/TextTitle"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:paddingStart="8dp"
|
|
||||||
android:layout_marginBottom="4dp"
|
|
||||||
android:textColor="?android:attr/textColorPrimary"
|
|
||||||
android:textSize="14sp" />
|
|
||||||
|
|
||||||
</com.android.launcher3.views.SearchResultSuggestRow>
|
|
|
@ -17,7 +17,9 @@
|
||||||
android:id="@+id/section_title"
|
android:id="@+id/section_title"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:fontFamily="@style/TextHeadline"
|
style="@style/TextHeadline"
|
||||||
android:padding="4dp"
|
android:paddingStart="4dp"
|
||||||
|
android:paddingBottom="2dp"
|
||||||
|
android:paddingTop="12dp"
|
||||||
android:textColor="?android:attr/textColorPrimary"
|
android:textColor="?android:attr/textColorPrimary"
|
||||||
android:textSize="14sp" />
|
android:textSize="18sp" />
|
|
@ -26,7 +26,7 @@
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:src="@drawable/ic_setting"
|
android:src="@drawable/ic_setting"
|
||||||
android:tint="@android:color/black"
|
android:forceDarkAllowed="true"
|
||||||
android:padding="12dp"
|
android:padding="12dp"
|
||||||
android:background="?android:attr/selectableItemBackgroundBorderless" />
|
android:background="?android:attr/selectableItemBackgroundBorderless" />
|
||||||
|
|
||||||
|
|
|
@ -69,6 +69,13 @@
|
||||||
<attr name="folderDotColor" />
|
<attr name="folderDotColor" />
|
||||||
</declare-styleable>
|
</declare-styleable>
|
||||||
|
|
||||||
|
<declare-styleable name="SearchResultIconRow">
|
||||||
|
<attr name="customIcon" format="reference" />
|
||||||
|
<attr name="matchTextInsetWithQuery" format="boolean" />
|
||||||
|
</declare-styleable>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
<declare-styleable name="ShadowInfo">
|
<declare-styleable name="ShadowInfo">
|
||||||
<attr name="ambientShadowColor" format="color" />
|
<attr name="ambientShadowColor" format="color" />
|
||||||
<attr name="ambientShadowBlur" format="dimension" />
|
<attr name="ambientShadowBlur" format="dimension" />
|
||||||
|
|
|
@ -59,7 +59,7 @@
|
||||||
<!-- Search bar text in the apps view. [CHAR_LIMIT=50] -->
|
<!-- Search bar text in the apps view. [CHAR_LIMIT=50] -->
|
||||||
<string name="all_apps_search_bar_hint">Search apps</string>
|
<string name="all_apps_search_bar_hint">Search apps</string>
|
||||||
<!-- Search bar text in the apps view. [CHAR_LIMIT=50] -->
|
<!-- Search bar text in the apps view. [CHAR_LIMIT=50] -->
|
||||||
<string name="all_apps_on_device_search_bar_hint">Search this phone and more...</string>
|
<string name="all_apps_on_device_search_bar_hint">Search this phone and more…</string>
|
||||||
<!-- Loading apps text. [CHAR_LIMIT=50] -->
|
<!-- Loading apps text. [CHAR_LIMIT=50] -->
|
||||||
<string name="all_apps_loading_message">Loading apps…</string>
|
<string name="all_apps_loading_message">Loading apps…</string>
|
||||||
<!-- No-search-results text. [CHAR_LIMIT=50] -->
|
<!-- No-search-results text. [CHAR_LIMIT=50] -->
|
||||||
|
|
|
@ -223,6 +223,16 @@
|
||||||
<item name="android:lines">1</item>
|
<item name="android:lines">1</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<!-- Base theme for AllApps BubbleTextViews -->
|
||||||
|
<style name="BaseIcon.AllApps" parent="BaseIcon">
|
||||||
|
<item name="android:layout_width">match_parent</item>
|
||||||
|
<item name="android:layout_height">wrap_content</item>
|
||||||
|
<item name="android:stateListAnimator">@animator/all_apps_fastscroll_icon_anim</item>
|
||||||
|
<item name="android:paddingLeft">@dimen/dynamic_grid_cell_padding_x</item>
|
||||||
|
<item name="android:paddingRight">@dimen/dynamic_grid_cell_padding_x</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
<!-- Icon displayed on the workspace -->
|
<!-- Icon displayed on the workspace -->
|
||||||
<style name="BaseIcon.Workspace" >
|
<style name="BaseIcon.Workspace" >
|
||||||
<item name="android:shadowRadius">2.0</item>
|
<item name="android:shadowRadius">2.0</item>
|
||||||
|
|
|
@ -17,6 +17,7 @@
|
||||||
package com.android.launcher3;
|
package com.android.launcher3;
|
||||||
|
|
||||||
import static com.android.launcher3.FastBitmapDrawable.newIcon;
|
import static com.android.launcher3.FastBitmapDrawable.newIcon;
|
||||||
|
import static com.android.launcher3.graphics.IconShape.getShape;
|
||||||
import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon;
|
import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon;
|
||||||
import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
|
import static com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound;
|
||||||
|
|
||||||
|
@ -27,15 +28,18 @@ import android.animation.ValueAnimator;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.ColorStateList;
|
import android.content.res.ColorStateList;
|
||||||
import android.content.res.TypedArray;
|
import android.content.res.TypedArray;
|
||||||
|
import android.graphics.BlurMaskFilter;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.Paint;
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.Path;
|
||||||
import android.graphics.PointF;
|
import android.graphics.PointF;
|
||||||
import android.graphics.PorterDuff.Mode;
|
import android.graphics.PorterDuff.Mode;
|
||||||
import android.graphics.PorterDuffColorFilter;
|
import android.graphics.PorterDuffColorFilter;
|
||||||
import android.graphics.Rect;
|
import android.graphics.Rect;
|
||||||
import android.graphics.drawable.ColorDrawable;
|
import android.graphics.drawable.ColorDrawable;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.os.Process;
|
||||||
import android.text.TextUtils.TruncateAt;
|
import android.text.TextUtils.TruncateAt;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.util.Property;
|
import android.util.Property;
|
||||||
|
@ -50,6 +54,8 @@ import androidx.core.graphics.ColorUtils;
|
||||||
|
|
||||||
import com.android.launcher3.Launcher.OnResumeCallback;
|
import com.android.launcher3.Launcher.OnResumeCallback;
|
||||||
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
|
import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
|
||||||
|
import com.android.launcher3.allapps.AllAppsSectionDecorator;
|
||||||
|
import com.android.launcher3.config.FeatureFlags;
|
||||||
import com.android.launcher3.dot.DotInfo;
|
import com.android.launcher3.dot.DotInfo;
|
||||||
import com.android.launcher3.dragndrop.DraggableView;
|
import com.android.launcher3.dragndrop.DraggableView;
|
||||||
import com.android.launcher3.folder.FolderIcon;
|
import com.android.launcher3.folder.FolderIcon;
|
||||||
|
@ -60,6 +66,7 @@ import com.android.launcher3.graphics.PreloadIconDrawable;
|
||||||
import com.android.launcher3.icons.DotRenderer;
|
import com.android.launcher3.icons.DotRenderer;
|
||||||
import com.android.launcher3.icons.IconCache.IconLoadRequest;
|
import com.android.launcher3.icons.IconCache.IconLoadRequest;
|
||||||
import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
|
import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
|
||||||
|
import com.android.launcher3.icons.LauncherIcons;
|
||||||
import com.android.launcher3.model.data.AppInfo;
|
import com.android.launcher3.model.data.AppInfo;
|
||||||
import com.android.launcher3.model.data.ItemInfo;
|
import com.android.launcher3.model.data.ItemInfo;
|
||||||
import com.android.launcher3.model.data.ItemInfoWithIcon;
|
import com.android.launcher3.model.data.ItemInfoWithIcon;
|
||||||
|
@ -79,7 +86,7 @@ import java.text.NumberFormat;
|
||||||
* too aggressive.
|
* too aggressive.
|
||||||
*/
|
*/
|
||||||
public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, OnResumeCallback,
|
public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver, OnResumeCallback,
|
||||||
IconLabelDotView, DraggableView, Reorderable {
|
IconLabelDotView, DraggableView, Reorderable, AllAppsSectionDecorator.SelfDecoratingView {
|
||||||
|
|
||||||
private static final int DISPLAY_WORKSPACE = 0;
|
private static final int DISPLAY_WORKSPACE = 0;
|
||||||
private static final int DISPLAY_ALL_APPS = 1;
|
private static final int DISPLAY_ALL_APPS = 1;
|
||||||
|
@ -87,6 +94,8 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||||
private static final int DISPLAY_HERO_APP = 5;
|
private static final int DISPLAY_HERO_APP = 5;
|
||||||
|
|
||||||
private static final int[] STATE_PRESSED = new int[]{android.R.attr.state_pressed};
|
private static final int[] STATE_PRESSED = new int[]{android.R.attr.state_pressed};
|
||||||
|
private static final float HIGHLIGHT_SCALE = 1.16f;
|
||||||
|
|
||||||
|
|
||||||
private final PointF mTranslationForReorderBounce = new PointF(0, 0);
|
private final PointF mTranslationForReorderBounce = new PointF(0, 0);
|
||||||
private final PointF mTranslationForReorderPreview = new PointF(0, 0);
|
private final PointF mTranslationForReorderPreview = new PointF(0, 0);
|
||||||
|
@ -95,6 +104,11 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||||
|
|
||||||
private float mScaleForReorderBounce = 1f;
|
private float mScaleForReorderBounce = 1f;
|
||||||
|
|
||||||
|
protected final Paint mHighlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
|
||||||
|
private final Path mHighlightPath = new Path();
|
||||||
|
protected int mHighlightColor = Color.TRANSPARENT;
|
||||||
|
private final BlurMaskFilter mHighlightShadowFilter;
|
||||||
|
|
||||||
private static final Property<BubbleTextView, Float> DOT_SCALE_PROPERTY
|
private static final Property<BubbleTextView, Float> DOT_SCALE_PROPERTY
|
||||||
= new Property<BubbleTextView, Float>(Float.TYPE, "dotScale") {
|
= new Property<BubbleTextView, Float>(Float.TYPE, "dotScale") {
|
||||||
@Override
|
@Override
|
||||||
|
@ -208,6 +222,11 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||||
setEllipsize(TruncateAt.END);
|
setEllipsize(TruncateAt.END);
|
||||||
setAccessibilityDelegate(mActivity.getAccessibilityDelegate());
|
setAccessibilityDelegate(mActivity.getAccessibilityDelegate());
|
||||||
setTextAlpha(1f);
|
setTextAlpha(1f);
|
||||||
|
|
||||||
|
int shadowSize = context.getResources().getDimensionPixelSize(
|
||||||
|
R.dimen.blur_size_click_shadow);
|
||||||
|
mHighlightShadowFilter = new BlurMaskFilter(shadowSize, BlurMaskFilter.Blur.INNER);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -421,8 +440,38 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDraw(Canvas canvas) {
|
public void onDraw(Canvas canvas) {
|
||||||
|
if (FeatureFlags.ENABLE_DEVICE_SEARCH.get() && mHighlightColor != Color.TRANSPARENT) {
|
||||||
|
int count = canvas.save();
|
||||||
|
drawFocusHighlight(canvas);
|
||||||
|
canvas.restoreToCount(count);
|
||||||
|
}
|
||||||
super.onDraw(canvas);
|
super.onDraw(canvas);
|
||||||
drawDotIfNecessary(canvas);
|
}
|
||||||
|
|
||||||
|
protected void drawFocusHighlight(Canvas canvas) {
|
||||||
|
boolean isBadged = getTag() instanceof ItemInfo && !Process.myUserHandle().equals(
|
||||||
|
((ItemInfo) getTag()).user);
|
||||||
|
float insetScale = (HIGHLIGHT_SCALE - 1) / 2;
|
||||||
|
canvas.translate(-getIconSize() * insetScale, -insetScale * getIconSize());
|
||||||
|
float outlineSize = getIconSize() * HIGHLIGHT_SCALE;
|
||||||
|
mHighlightPath.reset();
|
||||||
|
mHighlightPaint.reset();
|
||||||
|
getIconBounds(mDotParams.iconBounds);
|
||||||
|
getShape().addToPath(mHighlightPath, mDotParams.iconBounds.left, mDotParams.iconBounds.top,
|
||||||
|
outlineSize / 2);
|
||||||
|
if (isBadged) {
|
||||||
|
float borderSize = outlineSize - getIconSize();
|
||||||
|
float badgeSize = LauncherIcons.getBadgeSizeForIconSize(getIconSize()) + borderSize;
|
||||||
|
float badgeInset = outlineSize - badgeSize;
|
||||||
|
getShape().addToPath(mHighlightPath, mDotParams.iconBounds.left + badgeInset,
|
||||||
|
mDotParams.iconBounds.top + badgeInset, badgeSize / 2);
|
||||||
|
}
|
||||||
|
mHighlightPaint.setMaskFilter(mHighlightShadowFilter);
|
||||||
|
mHighlightPaint.setColor(mDotParams.color);
|
||||||
|
canvas.drawPath(mHighlightPath, mHighlightPaint);
|
||||||
|
mHighlightPaint.setMaskFilter(null);
|
||||||
|
mHighlightPaint.setColor(mHighlightColor);
|
||||||
|
canvas.drawPath(mHighlightPath, mHighlightPaint);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -627,7 +676,7 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||||
/**
|
/**
|
||||||
* Sets the icon for this view based on the layout direction.
|
* Sets the icon for this view based on the layout direction.
|
||||||
*/
|
*/
|
||||||
private void setIcon(Drawable icon) {
|
protected void setIcon(Drawable icon) {
|
||||||
if (mIsIconVisible) {
|
if (mIsIconVisible) {
|
||||||
applyCompoundDrawables(icon);
|
applyCompoundDrawables(icon);
|
||||||
}
|
}
|
||||||
|
@ -787,10 +836,11 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public SafeCloseable prepareDrawDragView() {
|
public SafeCloseable prepareDrawDragView() {
|
||||||
|
int highlightColor = mHighlightColor;
|
||||||
|
mHighlightColor = Color.TRANSPARENT;
|
||||||
resetIconScale();
|
resetIconScale();
|
||||||
setForceHideDot(true);
|
setForceHideDot(true);
|
||||||
return () -> {
|
return () -> mHighlightColor = highlightColor;
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void resetIconScale() {
|
private void resetIconScale() {
|
||||||
|
@ -827,4 +877,17 @@ public class BubbleTextView extends TextView implements ItemInfoUpdateReceiver,
|
||||||
});
|
});
|
||||||
iconUpdateAnimation.start();
|
iconUpdateAnimation.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void decorate(int color) {
|
||||||
|
mHighlightColor = color;
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void removeDecoration() {
|
||||||
|
mHighlightColor = Color.TRANSPARENT;
|
||||||
|
invalidate();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,6 +25,10 @@ import android.view.ViewDebug;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.FrameLayout;
|
import android.widget.FrameLayout;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* View class that represents the bottom row of the home screen.
|
* View class that represents the bottom row of the home screen.
|
||||||
*/
|
*/
|
||||||
|
@ -34,6 +38,7 @@ public class Hotseat extends CellLayout implements Insettable {
|
||||||
private boolean mHasVerticalHotseat;
|
private boolean mHasVerticalHotseat;
|
||||||
private Workspace mWorkspace;
|
private Workspace mWorkspace;
|
||||||
private boolean mSendTouchToWorkspace;
|
private boolean mSendTouchToWorkspace;
|
||||||
|
@Nullable private Consumer<Boolean> mOnVisibilityAggregatedCallback;
|
||||||
|
|
||||||
public Hotseat(Context context) {
|
public Hotseat(Context context) {
|
||||||
this(context, null);
|
this(context, null);
|
||||||
|
@ -129,4 +134,18 @@ public class Hotseat extends CellLayout implements Insettable {
|
||||||
}
|
}
|
||||||
return event.getY() > getCellHeight();
|
return event.getY() > getCellHeight();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onVisibilityAggregated(boolean isVisible) {
|
||||||
|
super.onVisibilityAggregated(isVisible);
|
||||||
|
|
||||||
|
if (mOnVisibilityAggregatedCallback != null) {
|
||||||
|
mOnVisibilityAggregatedCallback.accept(isVisible);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Sets a callback to be called onVisibilityAggregated */
|
||||||
|
public void setOnVisibilityAggregatedCallback(@Nullable Consumer<Boolean> callback) {
|
||||||
|
mOnVisibilityAggregatedCallback = callback;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -201,7 +201,7 @@ public class InvariantDeviceProfile {
|
||||||
DisplayController.getDefaultDisplay(context).getInfo(),
|
DisplayController.getDefaultDisplay(context).getInfo(),
|
||||||
getPredefinedDeviceProfiles(context, gridName));
|
getPredefinedDeviceProfiles(context, gridName));
|
||||||
|
|
||||||
Info myInfo = new Info(context, display);
|
Info myInfo = new Info(display);
|
||||||
DisplayOption myDisplayOption = invDistWeightedInterpolate(
|
DisplayOption myDisplayOption = invDistWeightedInterpolate(
|
||||||
myInfo, getPredefinedDeviceProfiles(context, gridName));
|
myInfo, getPredefinedDeviceProfiles(context, gridName));
|
||||||
|
|
||||||
|
|
|
@ -34,6 +34,7 @@ import android.content.pm.PackageManager;
|
||||||
import android.content.pm.ResolveInfo;
|
import android.content.pm.ResolveInfo;
|
||||||
import android.content.pm.ShortcutInfo;
|
import android.content.pm.ShortcutInfo;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
|
import android.database.ContentObserver;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.Color;
|
import android.graphics.Color;
|
||||||
import android.graphics.Matrix;
|
import android.graphics.Matrix;
|
||||||
|
@ -44,6 +45,7 @@ import android.graphics.RectF;
|
||||||
import android.graphics.drawable.ColorDrawable;
|
import android.graphics.drawable.ColorDrawable;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.graphics.drawable.InsetDrawable;
|
import android.graphics.drawable.InsetDrawable;
|
||||||
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.DeadObjectException;
|
import android.os.DeadObjectException;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
|
@ -83,6 +85,7 @@ import java.lang.reflect.Method;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
|
import java.util.function.Consumer;
|
||||||
import java.util.regex.Matcher;
|
import java.util.regex.Matcher;
|
||||||
import java.util.regex.Pattern;
|
import java.util.regex.Pattern;
|
||||||
|
|
||||||
|
@ -661,6 +664,18 @@ public final class Utilities {
|
||||||
return slop * slop;
|
return slop * slop;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper method to create a content provider
|
||||||
|
*/
|
||||||
|
public static ContentObserver newContentObserver(Handler handler, Consumer<Uri> command) {
|
||||||
|
return new ContentObserver(handler) {
|
||||||
|
@Override
|
||||||
|
public void onChange(boolean selfChange, Uri uri) {
|
||||||
|
command.accept(uri);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
private static class FixedSizeEmptyDrawable extends ColorDrawable {
|
private static class FixedSizeEmptyDrawable extends ColorDrawable {
|
||||||
|
|
||||||
private final int mSize;
|
private final int mSize;
|
||||||
|
|
|
@ -2093,10 +2093,10 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
||||||
mTempFXY[1] = y;
|
mTempFXY[1] = y;
|
||||||
mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempFXY, true);
|
mLauncher.getDragLayer().getDescendantCoordRelativeToSelf(this, mTempFXY, true);
|
||||||
View hotseat = mLauncher.getHotseat();
|
View hotseat = mLauncher.getHotseat();
|
||||||
return mTempFXY[0] >= hotseat.getLeft() &&
|
return mTempFXY[0] >= hotseat.getLeft()
|
||||||
mTempFXY[0] <= hotseat.getRight() &&
|
&& mTempFXY[0] <= hotseat.getRight()
|
||||||
mTempFXY[1] >= hotseat.getTop() &&
|
&& mTempFXY[1] >= hotseat.getTop()
|
||||||
mTempFXY[1] <= hotseat.getBottom();
|
&& mTempFXY[1] <= hotseat.getBottom();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2393,9 +2393,9 @@ public class Workspace extends PagedView<WorkspacePageIndicator>
|
||||||
spanY = mDragInfo.spanY;
|
spanY = mDragInfo.spanY;
|
||||||
}
|
}
|
||||||
|
|
||||||
final int container = mLauncher.isHotseatLayout(cellLayout) ?
|
final int container = mLauncher.isHotseatLayout(cellLayout)
|
||||||
LauncherSettings.Favorites.CONTAINER_HOTSEAT :
|
? LauncherSettings.Favorites.CONTAINER_HOTSEAT
|
||||||
LauncherSettings.Favorites.CONTAINER_DESKTOP;
|
: LauncherSettings.Favorites.CONTAINER_DESKTOP;
|
||||||
final int screenId = getIdForScreen(cellLayout);
|
final int screenId = getIdForScreen(cellLayout);
|
||||||
if (!mLauncher.isHotseatLayout(cellLayout)
|
if (!mLauncher.isHotseatLayout(cellLayout)
|
||||||
&& screenId != getScreenIdForPageIndex(mCurrentPage)
|
&& screenId != getScreenIdForPageIndex(mCurrentPage)
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
package com.android.launcher3.allapps;
|
package com.android.launcher3.allapps;
|
||||||
|
|
||||||
import static com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
|
import static com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
|
||||||
import static com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload;
|
import static com.android.launcher3.allapps.AllAppsGridAdapter.SearchAdapterItem;
|
||||||
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_HAS_SHORTCUT_PERMISSION;
|
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_HAS_SHORTCUT_PERMISSION;
|
||||||
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_CHANGE_PERMISSION;
|
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_CHANGE_PERMISSION;
|
||||||
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED;
|
import static com.android.launcher3.model.BgDataModel.Callbacks.FLAG_QUIET_MODE_ENABLED;
|
||||||
|
@ -57,6 +57,7 @@ import com.android.launcher3.Insettable;
|
||||||
import com.android.launcher3.InsettableFrameLayout;
|
import com.android.launcher3.InsettableFrameLayout;
|
||||||
import com.android.launcher3.R;
|
import com.android.launcher3.R;
|
||||||
import com.android.launcher3.Utilities;
|
import com.android.launcher3.Utilities;
|
||||||
|
import com.android.launcher3.allapps.search.SearchEventTracker;
|
||||||
import com.android.launcher3.config.FeatureFlags;
|
import com.android.launcher3.config.FeatureFlags;
|
||||||
import com.android.launcher3.keyboard.FocusedItemDecorator;
|
import com.android.launcher3.keyboard.FocusedItemDecorator;
|
||||||
import com.android.launcher3.model.data.AppInfo;
|
import com.android.launcher3.model.data.AppInfo;
|
||||||
|
@ -67,9 +68,7 @@ import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
|
||||||
import com.android.launcher3.util.Themes;
|
import com.android.launcher3.util.Themes;
|
||||||
import com.android.launcher3.views.RecyclerViewFastScroller;
|
import com.android.launcher3.views.RecyclerViewFastScroller;
|
||||||
import com.android.launcher3.views.SpringRelativeLayout;
|
import com.android.launcher3.views.SpringRelativeLayout;
|
||||||
import com.android.systemui.plugins.shared.SearchTargetEvent;
|
import com.android.systemui.plugins.shared.SearchTarget;
|
||||||
|
|
||||||
import java.util.function.IntConsumer;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The all apps view container.
|
* The all apps view container.
|
||||||
|
@ -546,13 +545,9 @@ public class AllAppsContainerView extends SpringRelativeLayout implements DragSo
|
||||||
return mLauncher.startActivitySafely(v, headerItem.getIntent(), headerItem);
|
return mLauncher.startActivitySafely(v, headerItem.getIntent(), headerItem);
|
||||||
}
|
}
|
||||||
AdapterItem focusedItem = getActiveRecyclerView().getApps().getFocusedChild();
|
AdapterItem focusedItem = getActiveRecyclerView().getApps().getFocusedChild();
|
||||||
if (focusedItem instanceof AdapterItemWithPayload) {
|
if (focusedItem instanceof SearchAdapterItem) {
|
||||||
IntConsumer onSelection =
|
SearchTarget searchTarget = ((SearchAdapterItem) focusedItem).getSearchTarget();
|
||||||
((AdapterItemWithPayload) focusedItem).getSelectionHandler();
|
SearchEventTracker.INSTANCE.get(getContext()).quickSelect(searchTarget);
|
||||||
if (onSelection != null) {
|
|
||||||
onSelection.accept(SearchTargetEvent.QUICK_SELECT);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (focusedItem.appInfo != null) {
|
if (focusedItem.appInfo != null) {
|
||||||
ItemInfo itemInfo = focusedItem.appInfo;
|
ItemInfo itemInfo = focusedItem.appInfo;
|
||||||
|
|
|
@ -20,8 +20,6 @@ import static com.android.launcher3.touch.ItemLongClickListener.INSTANCE_ALL_APP
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.net.Uri;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.view.Gravity;
|
import android.view.Gravity;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
@ -37,29 +35,22 @@ import androidx.annotation.Nullable;
|
||||||
import androidx.core.view.accessibility.AccessibilityEventCompat;
|
import androidx.core.view.accessibility.AccessibilityEventCompat;
|
||||||
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
|
import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
|
||||||
import androidx.core.view.accessibility.AccessibilityRecordCompat;
|
import androidx.core.view.accessibility.AccessibilityRecordCompat;
|
||||||
import androidx.lifecycle.LiveData;
|
|
||||||
import androidx.recyclerview.widget.GridLayoutManager;
|
import androidx.recyclerview.widget.GridLayoutManager;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
import androidx.slice.Slice;
|
|
||||||
import androidx.slice.widget.SliceLiveData;
|
|
||||||
import androidx.slice.widget.SliceView;
|
import androidx.slice.widget.SliceView;
|
||||||
|
|
||||||
import com.android.launcher3.BaseDraggingActivity;
|
import com.android.launcher3.BaseDraggingActivity;
|
||||||
import com.android.launcher3.BubbleTextView;
|
import com.android.launcher3.BubbleTextView;
|
||||||
import com.android.launcher3.Launcher;
|
|
||||||
import com.android.launcher3.R;
|
import com.android.launcher3.R;
|
||||||
import com.android.launcher3.allapps.search.AllAppsSearchBarController.PayloadResultHandler;
|
import com.android.launcher3.allapps.search.AllAppsSearchBarController.SearchTargetHandler;
|
||||||
import com.android.launcher3.allapps.search.SearchSectionInfo;
|
import com.android.launcher3.allapps.search.SearchSectionInfo;
|
||||||
import com.android.launcher3.config.FeatureFlags;
|
import com.android.launcher3.config.FeatureFlags;
|
||||||
import com.android.launcher3.model.data.AppInfo;
|
import com.android.launcher3.model.data.AppInfo;
|
||||||
import com.android.launcher3.util.PackageManagerHelper;
|
import com.android.launcher3.util.PackageManagerHelper;
|
||||||
import com.android.launcher3.views.HeroSearchResultView;
|
import com.android.launcher3.views.SearchSliceWrapper;
|
||||||
import com.android.systemui.plugins.AllAppsSearchPlugin;
|
|
||||||
import com.android.systemui.plugins.shared.SearchTarget;
|
import com.android.systemui.plugins.shared.SearchTarget;
|
||||||
import com.android.systemui.plugins.shared.SearchTargetEvent;
|
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.function.IntConsumer;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The grid view adapter of all the apps.
|
* The grid view adapter of all the apps.
|
||||||
|
@ -100,9 +91,11 @@ public class AllAppsGridAdapter extends
|
||||||
|
|
||||||
public static final int VIEW_TYPE_SEARCH_SUGGEST = 1 << 13;
|
public static final int VIEW_TYPE_SEARCH_SUGGEST = 1 << 13;
|
||||||
|
|
||||||
|
public static final int VIEW_TYPE_SEARCH_ICON = 1 << 14;
|
||||||
|
|
||||||
// Common view type masks
|
// Common view type masks
|
||||||
public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_ALL_APPS_DIVIDER;
|
public static final int VIEW_TYPE_MASK_DIVIDER = VIEW_TYPE_ALL_APPS_DIVIDER;
|
||||||
public static final int VIEW_TYPE_MASK_ICON = VIEW_TYPE_ICON;
|
public static final int VIEW_TYPE_MASK_ICON = VIEW_TYPE_ICON | VIEW_TYPE_SEARCH_ICON;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ViewHolder for each icon.
|
* ViewHolder for each icon.
|
||||||
|
@ -192,56 +185,25 @@ public class AllAppsGridAdapter extends
|
||||||
|| viewType == VIEW_TYPE_SEARCH_PEOPLE
|
|| viewType == VIEW_TYPE_SEARCH_PEOPLE
|
||||||
|| viewType == VIEW_TYPE_SEARCH_THUMBNAIL
|
|| viewType == VIEW_TYPE_SEARCH_THUMBNAIL
|
||||||
|| viewType == VIEW_TYPE_SEARCH_ICON_ROW
|
|| viewType == VIEW_TYPE_SEARCH_ICON_ROW
|
||||||
|
|| viewType == VIEW_TYPE_SEARCH_ICON
|
||||||
|| viewType == VIEW_TYPE_SEARCH_SUGGEST;
|
|| viewType == VIEW_TYPE_SEARCH_SUGGEST;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extension of AdapterItem that contains an extra payload specific to item
|
* Extension of AdapterItem that contains an extra payload specific to item
|
||||||
*
|
|
||||||
* @param <T> Play load Type
|
|
||||||
*/
|
*/
|
||||||
public static class AdapterItemWithPayload<T> extends AdapterItem {
|
public static class SearchAdapterItem extends AdapterItem {
|
||||||
private T mPayload;
|
private SearchTarget mSearchTarget;
|
||||||
private String mSearchSessionId;
|
|
||||||
private AllAppsSearchPlugin mPlugin;
|
|
||||||
private IntConsumer mSelectionHandler;
|
|
||||||
|
|
||||||
public AllAppsSearchPlugin getPlugin() {
|
public SearchAdapterItem(SearchTarget searchTarget, int type) {
|
||||||
return mPlugin;
|
mSearchTarget = searchTarget;
|
||||||
}
|
|
||||||
|
|
||||||
public void setPlugin(AllAppsSearchPlugin plugin) {
|
|
||||||
mPlugin = plugin;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AdapterItemWithPayload(T payload, int type, AllAppsSearchPlugin plugin) {
|
|
||||||
mPayload = payload;
|
|
||||||
viewType = type;
|
viewType = type;
|
||||||
mPlugin = plugin;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSelectionHandler(IntConsumer runnable) {
|
public SearchTarget getSearchTarget() {
|
||||||
mSelectionHandler = runnable;
|
return mSearchTarget;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSearchSessionId(String searchSessionId) {
|
|
||||||
mSearchSessionId = searchSessionId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getSearchSessionId() {
|
|
||||||
return mSearchSessionId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public IntConsumer getSelectionHandler() {
|
|
||||||
return mSelectionHandler;
|
|
||||||
}
|
|
||||||
|
|
||||||
public T getPayload() {
|
|
||||||
return mPayload;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -426,11 +388,8 @@ public class AllAppsGridAdapter extends
|
||||||
R.layout.all_apps_icon, parent, false);
|
R.layout.all_apps_icon, parent, false);
|
||||||
icon.setLongPressTimeoutFactor(1f);
|
icon.setLongPressTimeoutFactor(1f);
|
||||||
icon.setOnFocusChangeListener(mIconFocusListener);
|
icon.setOnFocusChangeListener(mIconFocusListener);
|
||||||
if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
|
|
||||||
icon.setOnClickListener(mOnIconClickListener);
|
icon.setOnClickListener(mOnIconClickListener);
|
||||||
icon.setOnLongClickListener(mOnIconLongClickListener);
|
icon.setOnLongClickListener(mOnIconLongClickListener);
|
||||||
}
|
|
||||||
|
|
||||||
// Ensure the all apps icon height matches the workspace icons in portrait mode.
|
// Ensure the all apps icon height matches the workspace icons in portrait mode.
|
||||||
icon.getLayoutParams().height = mLauncher.getDeviceProfile().allAppsCellHeightPx;
|
icon.getLayoutParams().height = mLauncher.getDeviceProfile().allAppsCellHeightPx;
|
||||||
return new ViewHolder(icon);
|
return new ViewHolder(icon);
|
||||||
|
@ -446,6 +405,9 @@ public class AllAppsGridAdapter extends
|
||||||
case VIEW_TYPE_ALL_APPS_DIVIDER:
|
case VIEW_TYPE_ALL_APPS_DIVIDER:
|
||||||
return new ViewHolder(mLayoutInflater.inflate(
|
return new ViewHolder(mLayoutInflater.inflate(
|
||||||
R.layout.all_apps_divider, parent, false));
|
R.layout.all_apps_divider, parent, false));
|
||||||
|
case VIEW_TYPE_SEARCH_ICON:
|
||||||
|
return new ViewHolder(mLayoutInflater.inflate(
|
||||||
|
R.layout.search_result_icon, parent, false));
|
||||||
case VIEW_TYPE_SEARCH_CORPUS_TITLE:
|
case VIEW_TYPE_SEARCH_CORPUS_TITLE:
|
||||||
return new ViewHolder(
|
return new ViewHolder(
|
||||||
mLayoutInflater.inflate(R.layout.search_section_title, parent, false));
|
mLayoutInflater.inflate(R.layout.search_section_title, parent, false));
|
||||||
|
@ -480,6 +442,10 @@ public class AllAppsGridAdapter extends
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(ViewHolder holder, int position) {
|
public void onBindViewHolder(ViewHolder holder, int position) {
|
||||||
|
if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()
|
||||||
|
&& holder.itemView instanceof AllAppsSectionDecorator.SelfDecoratingView) {
|
||||||
|
((AllAppsSectionDecorator.SelfDecoratingView) holder.itemView).removeDecoration();
|
||||||
|
}
|
||||||
switch (holder.getItemViewType()) {
|
switch (holder.getItemViewType()) {
|
||||||
case VIEW_TYPE_ICON:
|
case VIEW_TYPE_ICON:
|
||||||
AdapterItem adapterItem = mApps.getAdapterItems().get(position);
|
AdapterItem adapterItem = mApps.getAdapterItems().get(position);
|
||||||
|
@ -487,34 +453,6 @@ public class AllAppsGridAdapter extends
|
||||||
BubbleTextView icon = (BubbleTextView) holder.itemView;
|
BubbleTextView icon = (BubbleTextView) holder.itemView;
|
||||||
icon.reset();
|
icon.reset();
|
||||||
icon.applyFromApplicationInfo(info);
|
icon.applyFromApplicationInfo(info);
|
||||||
if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
//TODO: replace with custom TopHitBubbleTextView with support for both shortcut
|
|
||||||
// and apps
|
|
||||||
if (adapterItem instanceof AdapterItemWithPayload) {
|
|
||||||
AdapterItemWithPayload item = (AdapterItemWithPayload) adapterItem;
|
|
||||||
item.setSelectionHandler(type -> {
|
|
||||||
SearchTargetEvent e = new SearchTargetEvent(SearchTarget.ItemType.APP,
|
|
||||||
type, item.position, item.getSearchSessionId());
|
|
||||||
e.bundle = HeroSearchResultView.getAppBundle(info);
|
|
||||||
if (item.getPlugin() != null) {
|
|
||||||
item.getPlugin().notifySearchTargetEvent(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
icon.setOnClickListener(view -> {
|
|
||||||
item.getSelectionHandler().accept(SearchTargetEvent.SELECT);
|
|
||||||
mOnIconClickListener.onClick(view);
|
|
||||||
});
|
|
||||||
icon.setOnLongClickListener(view -> {
|
|
||||||
item.getSelectionHandler().accept(SearchTargetEvent.SELECT);
|
|
||||||
return mOnIconLongClickListener.onLongClick(view);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
icon.setOnClickListener(mOnIconClickListener);
|
|
||||||
icon.setOnLongClickListener(mOnIconLongClickListener);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case VIEW_TYPE_EMPTY_SEARCH:
|
case VIEW_TYPE_EMPTY_SEARCH:
|
||||||
TextView emptyViewText = (TextView) holder.itemView;
|
TextView emptyViewText = (TextView) holder.itemView;
|
||||||
|
@ -532,39 +470,25 @@ public class AllAppsGridAdapter extends
|
||||||
break;
|
break;
|
||||||
case VIEW_TYPE_SEARCH_SLICE:
|
case VIEW_TYPE_SEARCH_SLICE:
|
||||||
SliceView sliceView = (SliceView) holder.itemView;
|
SliceView sliceView = (SliceView) holder.itemView;
|
||||||
AdapterItemWithPayload<Uri> slicePayload =
|
SearchAdapterItem slicePayload = (SearchAdapterItem) mApps.getAdapterItems().get(
|
||||||
(AdapterItemWithPayload<Uri>) mApps.getAdapterItems().get(position);
|
position);
|
||||||
sliceView.setOnSliceActionListener((info1, s) -> {
|
SearchTarget searchTarget = slicePayload.getSearchTarget();
|
||||||
if (slicePayload.getPlugin() != null) {
|
sliceView.setTag(new SearchSliceWrapper(mLauncher, sliceView, searchTarget));
|
||||||
SearchTargetEvent searchTargetEvent = new SearchTargetEvent(
|
|
||||||
SearchTarget.ItemType.SETTINGS_SLICE,
|
|
||||||
SearchTargetEvent.CHILD_SELECT, slicePayload.position,
|
|
||||||
slicePayload.getSearchSessionId());
|
|
||||||
searchTargetEvent.bundle = new Bundle();
|
|
||||||
searchTargetEvent.bundle.putParcelable("uri", slicePayload.getPayload());
|
|
||||||
slicePayload.getPlugin().notifySearchTargetEvent(searchTargetEvent);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
try {
|
|
||||||
LiveData<Slice> liveData = SliceLiveData.fromUri(mLauncher,
|
|
||||||
slicePayload.getPayload());
|
|
||||||
liveData.observe((Launcher) mLauncher, sliceView);
|
|
||||||
sliceView.setTag(liveData);
|
|
||||||
} catch (Exception ignored) {
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case VIEW_TYPE_SEARCH_CORPUS_TITLE:
|
case VIEW_TYPE_SEARCH_CORPUS_TITLE:
|
||||||
case VIEW_TYPE_SEARCH_ROW_WITH_BUTTON:
|
case VIEW_TYPE_SEARCH_ROW_WITH_BUTTON:
|
||||||
case VIEW_TYPE_SEARCH_HERO_APP:
|
case VIEW_TYPE_SEARCH_HERO_APP:
|
||||||
case VIEW_TYPE_SEARCH_ROW:
|
case VIEW_TYPE_SEARCH_ROW:
|
||||||
|
case VIEW_TYPE_SEARCH_ICON:
|
||||||
case VIEW_TYPE_SEARCH_ICON_ROW:
|
case VIEW_TYPE_SEARCH_ICON_ROW:
|
||||||
case VIEW_TYPE_SEARCH_PEOPLE:
|
case VIEW_TYPE_SEARCH_PEOPLE:
|
||||||
case VIEW_TYPE_SEARCH_THUMBNAIL:
|
case VIEW_TYPE_SEARCH_THUMBNAIL:
|
||||||
case VIEW_TYPE_SEARCH_SUGGEST:
|
case VIEW_TYPE_SEARCH_SUGGEST:
|
||||||
AdapterItemWithPayload item =
|
SearchAdapterItem item =
|
||||||
(AdapterItemWithPayload) mApps.getAdapterItems().get(position);
|
(SearchAdapterItem) mApps.getAdapterItems().get(position);
|
||||||
PayloadResultHandler payloadResultView = (PayloadResultHandler) holder.itemView;
|
SearchTargetHandler payloadResultView = (SearchTargetHandler) holder.itemView;
|
||||||
payloadResultView.setup(item);
|
payloadResultView.applySearchTarget(item.getSearchTarget());
|
||||||
break;
|
break;
|
||||||
case VIEW_TYPE_ALL_APPS_DIVIDER:
|
case VIEW_TYPE_ALL_APPS_DIVIDER:
|
||||||
// nothing to do
|
// nothing to do
|
||||||
|
@ -576,17 +500,15 @@ public class AllAppsGridAdapter extends
|
||||||
public void onViewRecycled(@NonNull ViewHolder holder) {
|
public void onViewRecycled(@NonNull ViewHolder holder) {
|
||||||
super.onViewRecycled(holder);
|
super.onViewRecycled(holder);
|
||||||
if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()) return;
|
if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()) return;
|
||||||
if (holder.itemView instanceof BubbleTextView) {
|
if (holder.itemView instanceof AllAppsSectionDecorator.SelfDecoratingView) {
|
||||||
BubbleTextView icon = (BubbleTextView) holder.itemView;
|
((AllAppsSectionDecorator.SelfDecoratingView) holder.itemView).removeDecoration();
|
||||||
icon.setOnClickListener(null);
|
|
||||||
icon.setOnLongClickListener(null);
|
|
||||||
} else if (holder.itemView instanceof SliceView) {
|
|
||||||
SliceView sliceView = (SliceView) holder.itemView;
|
|
||||||
sliceView.setOnSliceActionListener(null);
|
|
||||||
if (sliceView.getTag() instanceof LiveData) {
|
|
||||||
LiveData sliceLiveData = (LiveData) sliceView.getTag();
|
|
||||||
sliceLiveData.removeObservers((Launcher) mLauncher);
|
|
||||||
}
|
}
|
||||||
|
if (holder.itemView instanceof SliceView) {
|
||||||
|
SliceView sliceView = (SliceView) holder.itemView;
|
||||||
|
if (sliceView.getTag() instanceof SearchSliceWrapper) {
|
||||||
|
((SearchSliceWrapper) sliceView.getTag()).destroy();
|
||||||
|
}
|
||||||
|
sliceView.setTag(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,12 +20,14 @@ import android.util.AttributeSet;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
|
|
||||||
import com.android.launcher3.PagedView;
|
import com.android.launcher3.PagedView;
|
||||||
|
import com.android.launcher3.R;
|
||||||
|
import com.android.launcher3.config.FeatureFlags;
|
||||||
|
|
||||||
public class AllAppsPagedView extends PagedView<PersonalWorkSlidingTabStrip> {
|
public class AllAppsPagedView extends PagedView<PersonalWorkSlidingTabStrip> {
|
||||||
|
|
||||||
final static float START_DAMPING_TOUCH_SLOP_ANGLE = (float) Math.PI / 6;
|
static final float START_DAMPING_TOUCH_SLOP_ANGLE = (float) Math.PI / 6;
|
||||||
final static float MAX_SWIPE_ANGLE = (float) Math.PI / 3;
|
static final float MAX_SWIPE_ANGLE = (float) Math.PI / 3;
|
||||||
final static float TOUCH_SLOP_DAMPING_FACTOR = 4;
|
static final float TOUCH_SLOP_DAMPING_FACTOR = 4;
|
||||||
|
|
||||||
public AllAppsPagedView(Context context) {
|
public AllAppsPagedView(Context context) {
|
||||||
this(context, null);
|
this(context, null);
|
||||||
|
@ -37,6 +39,10 @@ public class AllAppsPagedView extends PagedView<PersonalWorkSlidingTabStrip> {
|
||||||
|
|
||||||
public AllAppsPagedView(Context context, AttributeSet attrs, int defStyle) {
|
public AllAppsPagedView(Context context, AttributeSet attrs, int defStyle) {
|
||||||
super(context, attrs, defStyle);
|
super(context, attrs, defStyle);
|
||||||
|
int topPadding = FeatureFlags.ENABLE_DEVICE_SEARCH.get() ? 0
|
||||||
|
: context.getResources().getDimensionPixelOffset(
|
||||||
|
R.dimen.all_apps_header_top_padding);
|
||||||
|
setPadding(0, topPadding, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -24,6 +24,7 @@ import android.content.res.Resources;
|
||||||
import android.graphics.Canvas;
|
import android.graphics.Canvas;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
|
import android.util.Log;
|
||||||
import android.util.SparseIntArray;
|
import android.util.SparseIntArray;
|
||||||
import android.view.MotionEvent;
|
import android.view.MotionEvent;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
@ -45,6 +46,8 @@ import java.util.List;
|
||||||
* A RecyclerView with custom fast scroll support for the all apps view.
|
* A RecyclerView with custom fast scroll support for the all apps view.
|
||||||
*/
|
*/
|
||||||
public class AllAppsRecyclerView extends BaseRecyclerView {
|
public class AllAppsRecyclerView extends BaseRecyclerView {
|
||||||
|
private static final String TAG = "AllAppsContainerView";
|
||||||
|
private static final boolean DEBUG = true;
|
||||||
|
|
||||||
private AlphabeticalAppsList mApps;
|
private AlphabeticalAppsList mApps;
|
||||||
private final int mNumAppsPerRow;
|
private final int mNumAppsPerRow;
|
||||||
|
@ -131,7 +134,9 @@ public class AllAppsRecyclerView extends BaseRecyclerView {
|
||||||
if (mEmptySearchBackground != null && mEmptySearchBackground.getAlpha() > 0) {
|
if (mEmptySearchBackground != null && mEmptySearchBackground.getAlpha() > 0) {
|
||||||
mEmptySearchBackground.draw(c);
|
mEmptySearchBackground.draw(c);
|
||||||
}
|
}
|
||||||
|
if (DEBUG) {
|
||||||
|
Log.d(TAG, "onDraw at = " + System.currentTimeMillis());
|
||||||
|
}
|
||||||
super.onDraw(c);
|
super.onDraw(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ import androidx.core.graphics.ColorUtils;
|
||||||
import androidx.recyclerview.widget.RecyclerView;
|
import androidx.recyclerview.widget.RecyclerView;
|
||||||
|
|
||||||
import com.android.launcher3.R;
|
import com.android.launcher3.R;
|
||||||
|
import com.android.launcher3.allapps.AllAppsGridAdapter.AppsGridLayoutManager;
|
||||||
import com.android.launcher3.allapps.search.SearchSectionInfo;
|
import com.android.launcher3.allapps.search.SearchSectionInfo;
|
||||||
import com.android.launcher3.util.Themes;
|
import com.android.launcher3.util.Themes;
|
||||||
|
|
||||||
|
@ -53,6 +54,9 @@ public class AllAppsSectionDecorator extends RecyclerView.ItemDecoration {
|
||||||
int i = 0;
|
int i = 0;
|
||||||
while (i < itemCount) {
|
while (i < itemCount) {
|
||||||
View view = parent.getChildAt(i);
|
View view = parent.getChildAt(i);
|
||||||
|
if (view instanceof SelfDecoratingView) {
|
||||||
|
((SelfDecoratingView) view).removeDecoration();
|
||||||
|
}
|
||||||
int position = parent.getChildAdapterPosition(view);
|
int position = parent.getChildAdapterPosition(view);
|
||||||
AllAppsGridAdapter.AdapterItem adapterItem = adapterItems.get(position);
|
AllAppsGridAdapter.AdapterItem adapterItem = adapterItems.get(position);
|
||||||
if (adapterItem.searchSectionInfo != null) {
|
if (adapterItem.searchSectionInfo != null) {
|
||||||
|
@ -90,7 +94,10 @@ public class AllAppsSectionDecorator extends RecyclerView.ItemDecoration {
|
||||||
if (mAppsView.getFloatingHeaderView().getFocusedChild() == null
|
if (mAppsView.getFloatingHeaderView().getFocusedChild() == null
|
||||||
&& mAppsView.getApps().getFocusedChild() != null) {
|
&& mAppsView.getApps().getFocusedChild() != null) {
|
||||||
int index = mAppsView.getApps().getFocusedChildIndex();
|
int index = mAppsView.getApps().getFocusedChildIndex();
|
||||||
if (index >= 0 && index < parent.getChildCount()) {
|
AppsGridLayoutManager layoutManager = (AppsGridLayoutManager)
|
||||||
|
mAppsView.getActiveRecyclerView().getLayoutManager();
|
||||||
|
if (layoutManager.findFirstVisibleItemPosition() <= index
|
||||||
|
&& index < parent.getChildCount()) {
|
||||||
decorationHandler.onFocusDraw(c, parent.getChildAt(index));
|
decorationHandler.onFocusDraw(c, parent.getChildAt(index));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -101,8 +108,8 @@ public class AllAppsSectionDecorator extends RecyclerView.ItemDecoration {
|
||||||
* Handles grouping and drawing of items in the same all apps sections.
|
* Handles grouping and drawing of items in the same all apps sections.
|
||||||
*/
|
*/
|
||||||
public static class SectionDecorationHandler {
|
public static class SectionDecorationHandler {
|
||||||
private static final int FILL_ALPHA = (int) (.3f * 255);
|
private static final int FILL_ALPHA = 0;
|
||||||
private static final int FOCUS_ALPHA = (int) (.8f * 255);
|
private static final int FOCUS_ALPHA = (int) (.9f * 255);
|
||||||
|
|
||||||
protected RectF mBounds = new RectF();
|
protected RectF mBounds = new RectF();
|
||||||
private final boolean mIsFullWidth;
|
private final boolean mIsFullWidth;
|
||||||
|
@ -152,6 +159,10 @@ public class AllAppsSectionDecorator extends RecyclerView.ItemDecoration {
|
||||||
if (view == null) {
|
if (view == null) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if (view instanceof SelfDecoratingView) {
|
||||||
|
((SelfDecoratingView) view).decorate(mFocusColor);
|
||||||
|
return;
|
||||||
|
}
|
||||||
mPaint.setColor(mFocusColor);
|
mPaint.setColor(mFocusColor);
|
||||||
canvas.drawRoundRect(view.getLeft(), view.getTop(),
|
canvas.drawRoundRect(view.getLeft(), view.getTop(),
|
||||||
view.getRight(), view.getBottom(), mRadius, mRadius, mPaint);
|
view.getRight(), view.getBottom(), mRadius, mRadius, mPaint);
|
||||||
|
@ -165,4 +176,18 @@ public class AllAppsSectionDecorator extends RecyclerView.ItemDecoration {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An interface for a view to draw highlight indicator
|
||||||
|
*/
|
||||||
|
public interface SelfDecoratingView {
|
||||||
|
/**
|
||||||
|
* Removes decorations drawing if focus is acquired by another view
|
||||||
|
*/
|
||||||
|
void removeDecoration();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Draws highlight indicator on view.
|
||||||
|
*/
|
||||||
|
void decorate(int focusColor);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -133,7 +133,6 @@ public class AllAppsTransitionController implements StateHandler<LauncherState>,
|
||||||
* in xml-based animations which also handle updating the appropriate UI.
|
* in xml-based animations which also handle updating the appropriate UI.
|
||||||
*
|
*
|
||||||
* @param progress value between 0 and 1, 0 shows all apps and 1 shows workspace
|
* @param progress value between 0 and 1, 0 shows all apps and 1 shows workspace
|
||||||
*
|
|
||||||
* @see #setState(LauncherState)
|
* @see #setState(LauncherState)
|
||||||
* @see #setStateWithAnimation(LauncherState, StateAnimationConfig, PendingAnimation)
|
* @see #setStateWithAnimation(LauncherState, StateAnimationConfig, PendingAnimation)
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -178,16 +178,46 @@ public class AlphabeticalAppsList implements AllAppsStore.OnUpdateListener {
|
||||||
/**
|
/**
|
||||||
* Sets results list for search
|
* Sets results list for search
|
||||||
*/
|
*/
|
||||||
public boolean setSearchResults(ArrayList<AdapterItem> f) {
|
public boolean setSearchResults(ArrayList<AdapterItem> results) {
|
||||||
if (f == null || mSearchResults != f) {
|
if (results == null || mSearchResults != results) {
|
||||||
boolean same = mSearchResults != null && mSearchResults.equals(f);
|
boolean same = mSearchResults != null && mSearchResults.equals(results);
|
||||||
mSearchResults = f;
|
mSearchResults = results;
|
||||||
onAppsUpdated();
|
onAppsUpdated();
|
||||||
return !same;
|
return !same;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean appendSearchResults(ArrayList<AdapterItem> results) {
|
||||||
|
if (mSearchResults != null && results != null && results.size() > 0) {
|
||||||
|
updateSearchAdapterItems(results, mSearchResults.size());
|
||||||
|
refreshRecyclerView();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateSearchAdapterItems(ArrayList<AdapterItem> list, int offset) {
|
||||||
|
SearchSectionInfo lastSection = null;
|
||||||
|
for (int i = 0; i < list.size(); i++) {
|
||||||
|
AdapterItem adapterItem = list.get(i);
|
||||||
|
adapterItem.position = offset + i;
|
||||||
|
mAdapterItems.add(adapterItem);
|
||||||
|
if (adapterItem.searchSectionInfo != lastSection) {
|
||||||
|
if (adapterItem.searchSectionInfo != null) {
|
||||||
|
adapterItem.searchSectionInfo.setPosStart(adapterItem.position);
|
||||||
|
}
|
||||||
|
if (lastSection != null) {
|
||||||
|
lastSection.setPosEnd(adapterItem.position - 1);
|
||||||
|
}
|
||||||
|
lastSection = adapterItem.searchSectionInfo;
|
||||||
|
}
|
||||||
|
if (adapterItem.isCountedForAccessibility()) {
|
||||||
|
mAccessibilityResultsCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates internals when the set of apps are updated.
|
* Updates internals when the set of apps are updated.
|
||||||
*/
|
*/
|
||||||
|
@ -294,28 +324,7 @@ public class AlphabeticalAppsList implements AllAppsStore.OnUpdateListener {
|
||||||
}
|
}
|
||||||
appSection.setPosEnd(mApps.isEmpty() ? appSection.getPosStart() : position - 1);
|
appSection.setPosEnd(mApps.isEmpty() ? appSection.getPosStart() : position - 1);
|
||||||
} else {
|
} else {
|
||||||
List<AppInfo> appInfos = new ArrayList<>();
|
updateSearchAdapterItems(mSearchResults, 0);
|
||||||
SearchSectionInfo lastSection = null;
|
|
||||||
for (int i = 0; i < mSearchResults.size(); i++) {
|
|
||||||
AdapterItem adapterItem = mSearchResults.get(i);
|
|
||||||
adapterItem.position = i;
|
|
||||||
mAdapterItems.add(adapterItem);
|
|
||||||
if (adapterItem.searchSectionInfo != lastSection) {
|
|
||||||
if (adapterItem.searchSectionInfo != null) {
|
|
||||||
adapterItem.searchSectionInfo.setPosStart(i);
|
|
||||||
}
|
|
||||||
if (lastSection != null) {
|
|
||||||
lastSection.setPosEnd(i - 1);
|
|
||||||
}
|
|
||||||
lastSection = adapterItem.searchSectionInfo;
|
|
||||||
}
|
|
||||||
if (AllAppsGridAdapter.isIconViewType(adapterItem.viewType)) {
|
|
||||||
appInfos.add(adapterItem.appInfo);
|
|
||||||
}
|
|
||||||
if (adapterItem.isCountedForAccessibility()) {
|
|
||||||
mAccessibilityResultsCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
|
if (!FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
|
||||||
// Append the search market item
|
// Append the search market item
|
||||||
if (hasNoFilteredResults()) {
|
if (hasNoFilteredResults()) {
|
||||||
|
|
|
@ -111,8 +111,8 @@ public class FloatingHeaderView extends LinearLayout implements
|
||||||
|
|
||||||
public FloatingHeaderView(@NonNull Context context, @Nullable AttributeSet attrs) {
|
public FloatingHeaderView(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||||
super(context, attrs);
|
super(context, attrs);
|
||||||
mHeaderTopPadding = context.getResources()
|
mHeaderTopPadding = FeatureFlags.ENABLE_DEVICE_SEARCH.get() ? 0 :
|
||||||
.getDimensionPixelSize(R.dimen.all_apps_header_top_padding);
|
context.getResources().getDimensionPixelSize(R.dimen.all_apps_header_top_padding);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -130,6 +130,7 @@ public class FloatingHeaderView extends LinearLayout implements
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mFixedRows = rows.toArray(new FloatingHeaderRow[rows.size()]);
|
mFixedRows = rows.toArray(new FloatingHeaderRow[rows.size()]);
|
||||||
|
setPadding(0, mHeaderTopPadding, 0, 0);
|
||||||
mAllRows = mFixedRows;
|
mAllRows = mFixedRows;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -247,7 +248,9 @@ public class FloatingHeaderView extends LinearLayout implements
|
||||||
|
|
||||||
public int getMaxTranslation() {
|
public int getMaxTranslation() {
|
||||||
if (mMaxTranslation == 0 && mTabsHidden) {
|
if (mMaxTranslation == 0 && mTabsHidden) {
|
||||||
return getResources().getDimensionPixelSize(R.dimen.all_apps_search_bar_bottom_padding);
|
int paddingOffset = getResources().getDimensionPixelSize(
|
||||||
|
R.dimen.all_apps_search_bar_bottom_padding);
|
||||||
|
return FeatureFlags.ENABLE_DEVICE_SEARCH.get() ? 0 : paddingOffset;
|
||||||
} else if (mMaxTranslation > 0 && mTabsHidden) {
|
} else if (mMaxTranslation > 0 && mTabsHidden) {
|
||||||
return mMaxTranslation + getPaddingTop();
|
return mMaxTranslation + getPaddingTop();
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -30,13 +30,11 @@ import com.android.launcher3.BaseDraggingActivity;
|
||||||
import com.android.launcher3.ExtendedEditText;
|
import com.android.launcher3.ExtendedEditText;
|
||||||
import com.android.launcher3.Launcher;
|
import com.android.launcher3.Launcher;
|
||||||
import com.android.launcher3.Utilities;
|
import com.android.launcher3.Utilities;
|
||||||
import com.android.launcher3.allapps.AllAppsGridAdapter;
|
import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
|
||||||
import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload;
|
|
||||||
import com.android.launcher3.config.FeatureFlags;
|
import com.android.launcher3.config.FeatureFlags;
|
||||||
import com.android.launcher3.util.PackageManagerHelper;
|
import com.android.launcher3.util.PackageManagerHelper;
|
||||||
import com.android.systemui.plugins.AllAppsSearchPlugin;
|
import com.android.systemui.plugins.AllAppsSearchPlugin;
|
||||||
import com.android.systemui.plugins.shared.SearchTarget;
|
import com.android.systemui.plugins.shared.SearchTarget;
|
||||||
import com.android.systemui.plugins.shared.SearchTargetEvent;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
@ -114,7 +112,7 @@ public class AllAppsSearchBarController
|
||||||
@Override
|
@Override
|
||||||
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
|
||||||
if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
|
if (FeatureFlags.ENABLE_DEVICE_SEARCH.get()) {
|
||||||
if (actionId == EditorInfo.IME_ACTION_SEARCH) {
|
if (actionId == EditorInfo.IME_ACTION_SEARCH || actionId == EditorInfo.IME_ACTION_GO) {
|
||||||
// selectFocusedView should return SearchTargetEvent that is passed onto onClick
|
// selectFocusedView should return SearchTargetEvent that is passed onto onClick
|
||||||
if (Launcher.getLauncher(mLauncher).getAppsView().selectFocusedView(v)) {
|
if (Launcher.getLauncher(mLauncher).getAppsView().selectFocusedView(v)) {
|
||||||
return true;
|
return true;
|
||||||
|
@ -196,9 +194,16 @@ public class AllAppsSearchBarController
|
||||||
/**
|
/**
|
||||||
* Called when the search from primary source is complete.
|
* Called when the search from primary source is complete.
|
||||||
*
|
*
|
||||||
* @param items sorted list of search result adapter items.
|
* @param items sorted list of search result adapter items
|
||||||
*/
|
*/
|
||||||
void onSearchResult(String query, ArrayList<AllAppsGridAdapter.AdapterItem> items);
|
void onSearchResult(String query, ArrayList<AdapterItem> items);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the search from secondary source is complete.
|
||||||
|
*
|
||||||
|
* @param items sorted list of search result adapter items
|
||||||
|
*/
|
||||||
|
void onAppendSearchResult(String query, ArrayList<AdapterItem> items);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Called when the search results should be cleared.
|
* Called when the search results should be cleared.
|
||||||
|
@ -208,50 +213,20 @@ public class AllAppsSearchBarController
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* An interface for supporting dynamic search results
|
* An interface for supporting dynamic search results
|
||||||
*
|
|
||||||
* @param <T> Type of payload
|
|
||||||
*/
|
*/
|
||||||
public interface PayloadResultHandler<T> {
|
public interface SearchTargetHandler {
|
||||||
/**
|
|
||||||
* Updates View using Adapter's payload
|
|
||||||
*/
|
|
||||||
|
|
||||||
default void setup(AdapterItemWithPayload<T> adapterItemWithPayload) {
|
|
||||||
Object[] targetInfo = getTargetInfo();
|
|
||||||
if (targetInfo != null) {
|
|
||||||
targetInfo[0] = adapterItemWithPayload.getSearchSessionId();
|
|
||||||
targetInfo[1] = adapterItemWithPayload.position;
|
|
||||||
}
|
|
||||||
applyAdapterInfo(adapterItemWithPayload);
|
|
||||||
}
|
|
||||||
|
|
||||||
void applyAdapterInfo(AdapterItemWithPayload<T> adapterItemWithPayload);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets object created by {@link PayloadResultHandler#createTargetInfo()}
|
* Update view using values from {@link SearchTarget}
|
||||||
*/
|
*/
|
||||||
Object[] getTargetInfo();
|
void applySearchTarget(SearchTarget searchTarget);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a wrapper object to hold searchSessionId and item position
|
* Handles selection of SearchTarget
|
||||||
*/
|
*/
|
||||||
default Object[] createTargetInfo() {
|
default void handleSelection(int eventType) {
|
||||||
return new Object[2];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a SearchTargetEvent object for a PayloadHandlerView
|
|
||||||
*/
|
|
||||||
default SearchTargetEvent getSearchTargetEvent(SearchTarget.ItemType itemType,
|
|
||||||
int eventType) {
|
|
||||||
Object[] targetInfo = getTargetInfo();
|
|
||||||
if (targetInfo == null) return null;
|
|
||||||
|
|
||||||
String searchSessionId = (String) targetInfo[0];
|
|
||||||
int position = (int) targetInfo[1];
|
|
||||||
return new SearchTargetEvent(itemType, eventType,
|
|
||||||
position, searchSessionId);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ import com.android.launcher3.Insettable;
|
||||||
import com.android.launcher3.LauncherAppState;
|
import com.android.launcher3.LauncherAppState;
|
||||||
import com.android.launcher3.R;
|
import com.android.launcher3.R;
|
||||||
import com.android.launcher3.allapps.AllAppsContainerView;
|
import com.android.launcher3.allapps.AllAppsContainerView;
|
||||||
import com.android.launcher3.allapps.AllAppsGridAdapter;
|
import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
|
||||||
import com.android.launcher3.allapps.AllAppsStore;
|
import com.android.launcher3.allapps.AllAppsStore;
|
||||||
import com.android.launcher3.allapps.AlphabeticalAppsList;
|
import com.android.launcher3.allapps.AlphabeticalAppsList;
|
||||||
import com.android.launcher3.allapps.SearchUiManager;
|
import com.android.launcher3.allapps.SearchUiManager;
|
||||||
|
@ -173,7 +173,7 @@ public class AppsSearchContainerLayout extends ExtendedEditText
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onSearchResult(String query, ArrayList<AllAppsGridAdapter.AdapterItem> items) {
|
public void onSearchResult(String query, ArrayList<AdapterItem> items) {
|
||||||
if (items != null) {
|
if (items != null) {
|
||||||
mApps.setSearchResults(items);
|
mApps.setSearchResults(items);
|
||||||
notifyResultChanged();
|
notifyResultChanged();
|
||||||
|
@ -181,6 +181,14 @@ public class AppsSearchContainerLayout extends ExtendedEditText
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAppendSearchResult(String query, ArrayList<AdapterItem> items) {
|
||||||
|
if (items != null) {
|
||||||
|
mApps.appendSearchResults(items);
|
||||||
|
notifyResultChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void clearSearchResult() {
|
public void clearSearchResult() {
|
||||||
if (mApps.setSearchResults(null)) {
|
if (mApps.setSearchResults(null)) {
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
package com.android.launcher3.allapps.search;
|
package com.android.launcher3.allapps.search;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.os.CancellationSignal;
|
||||||
|
|
||||||
import com.android.launcher3.LauncherAppState;
|
import com.android.launcher3.LauncherAppState;
|
||||||
import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
|
import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItem;
|
||||||
|
@ -47,11 +48,12 @@ public class AppsSearchPipeline implements SearchPipeline {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void performSearch(String query, Consumer<ArrayList<AdapterItem>> callback) {
|
public void query(String input, Consumer<ArrayList<AdapterItem>> callback,
|
||||||
|
CancellationSignal cancellationSignal) {
|
||||||
mLauncherAppState.getModel().enqueueModelUpdateTask(new BaseModelUpdateTask() {
|
mLauncherAppState.getModel().enqueueModelUpdateTask(new BaseModelUpdateTask() {
|
||||||
@Override
|
@Override
|
||||||
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
|
public void execute(LauncherAppState app, BgDataModel dataModel, AllAppsList apps) {
|
||||||
List<AppInfo> matchingResults = getTitleMatchResult(apps.data, query);
|
List<AppInfo> matchingResults = getTitleMatchResult(apps.data, input);
|
||||||
callback.accept(getAdapterItems(matchingResults));
|
callback.accept(getAdapterItems(matchingResults));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
|
@ -46,8 +46,10 @@ public class DefaultAppSearchAlgorithm implements SearchAlgorithm {
|
||||||
@Override
|
@Override
|
||||||
public void doSearch(final String query,
|
public void doSearch(final String query,
|
||||||
final AllAppsSearchBarController.Callbacks callback) {
|
final AllAppsSearchBarController.Callbacks callback) {
|
||||||
mAppsSearchPipeline.performSearch(query,
|
mAppsSearchPipeline.query(query,
|
||||||
results -> mResultHandler.post(() -> callback.onSearchResult(query, results)));
|
results -> mResultHandler.post(
|
||||||
|
() -> callback.onSearchResult(query, results)),
|
||||||
|
null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean matches(AppInfo info, String query, StringMatcher matcher) {
|
public static boolean matches(AppInfo info, String query, StringMatcher matcher) {
|
||||||
|
|
|
@ -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;
|
package com.android.launcher3.allapps.search;
|
||||||
|
|
||||||
|
import android.os.CancellationSignal;
|
||||||
|
|
||||||
import com.android.launcher3.allapps.AllAppsGridAdapter;
|
import com.android.launcher3.allapps.AllAppsGridAdapter;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
@ -23,10 +25,13 @@ import java.util.function.Consumer;
|
||||||
/**
|
/**
|
||||||
* An interface for handling search within pipeline
|
* An interface for handling search within pipeline
|
||||||
*/
|
*/
|
||||||
|
// Remove when System Service API is added.
|
||||||
public interface SearchPipeline {
|
public interface SearchPipeline {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Perform query
|
* Perform query
|
||||||
*/
|
*/
|
||||||
void performSearch(String query, Consumer<ArrayList<AllAppsGridAdapter.AdapterItem>> cb);
|
void query(String input,
|
||||||
|
Consumer<ArrayList<AllAppsGridAdapter.AdapterItem>> callback,
|
||||||
|
CancellationSignal cancellationSignal);
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,7 @@ import com.android.launcher3.allapps.AllAppsSectionDecorator.SectionDecorationHa
|
||||||
*/
|
*/
|
||||||
public class SearchSectionInfo {
|
public class SearchSectionInfo {
|
||||||
|
|
||||||
private String mTitle;
|
private String mSectionId;
|
||||||
private SectionDecorationHandler mDecorationHandler;
|
private SectionDecorationHandler mDecorationHandler;
|
||||||
|
|
||||||
public int getPosStart() {
|
public int getPosStart() {
|
||||||
|
@ -48,8 +48,8 @@ public class SearchSectionInfo {
|
||||||
this(null);
|
this(null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SearchSectionInfo(String title) {
|
public SearchSectionInfo(String sectionId) {
|
||||||
mTitle = title;
|
mSectionId = sectionId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setDecorationHandler(SectionDecorationHandler sectionDecorationHandler) {
|
public void setDecorationHandler(SectionDecorationHandler sectionDecorationHandler) {
|
||||||
|
@ -62,9 +62,9 @@ public class SearchSectionInfo {
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the section's title
|
* Returns the section's ID
|
||||||
*/
|
*/
|
||||||
public String getTitle() {
|
public String getSectionId() {
|
||||||
return mTitle == null ? "" : mTitle;
|
return mSectionId == null ? "" : mSectionId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -156,10 +156,14 @@ public final class FeatureFlags {
|
||||||
"ENABLE_DATABASE_RESTORE", true,
|
"ENABLE_DATABASE_RESTORE", true,
|
||||||
"Enable database restore when new restore session is created");
|
"Enable database restore when new restore session is created");
|
||||||
|
|
||||||
public static final BooleanFlag ENABLE_UNIVERSAL_SMARTSPACE = getDebugFlag(
|
public static final BooleanFlag ENABLE_SMARTSPACE_UNIVERSAL = getDebugFlag(
|
||||||
"ENABLE_UNIVERSAL_SMARTSPACE", false,
|
"ENABLE_SMARTSPACE_UNIVERSAL", false,
|
||||||
"Replace Smartspace with a version rendered by System UI.");
|
"Replace Smartspace with a version rendered by System UI.");
|
||||||
|
|
||||||
|
public static final BooleanFlag ENABLE_SMARTSPACE_BLUECHIP = getDebugFlag(
|
||||||
|
"ENABLE_SMARTSPACE_BLUECHIP", false,
|
||||||
|
"Replace Smartspace with the Bluechip version. Ignored if ENABLE_SMARTSPACE_UNIVERSAL is enabled.");
|
||||||
|
|
||||||
public static final BooleanFlag ENABLE_SYSTEM_VELOCITY_PROVIDER = getDebugFlag(
|
public static final BooleanFlag ENABLE_SYSTEM_VELOCITY_PROVIDER = getDebugFlag(
|
||||||
"ENABLE_SYSTEM_VELOCITY_PROVIDER", true,
|
"ENABLE_SYSTEM_VELOCITY_PROVIDER", true,
|
||||||
"Use system VelocityTracker's algorithm for motion pause detection.");
|
"Use system VelocityTracker's algorithm for motion pause detection.");
|
||||||
|
@ -178,7 +182,7 @@ public final class FeatureFlags {
|
||||||
"Uses a separate recents activity instead of using the integrated recents+Launcher UI");
|
"Uses a separate recents activity instead of using the integrated recents+Launcher UI");
|
||||||
|
|
||||||
public static final BooleanFlag ENABLE_MINIMAL_DEVICE = getDebugFlag(
|
public static final BooleanFlag ENABLE_MINIMAL_DEVICE = getDebugFlag(
|
||||||
"ENABLE_MINIMAL_DEVICE", true,
|
"ENABLE_MINIMAL_DEVICE", false,
|
||||||
"Allow user to toggle minimal device mode in launcher.");
|
"Allow user to toggle minimal device mode in launcher.");
|
||||||
|
|
||||||
public static void initialize(Context context) {
|
public static void initialize(Context context) {
|
||||||
|
|
|
@ -53,8 +53,8 @@ import com.android.launcher3.R;
|
||||||
import com.android.launcher3.logging.StatsLogManager;
|
import com.android.launcher3.logging.StatsLogManager;
|
||||||
import com.android.launcher3.model.ItemInstallQueue;
|
import com.android.launcher3.model.ItemInstallQueue;
|
||||||
import com.android.launcher3.model.WidgetItem;
|
import com.android.launcher3.model.WidgetItem;
|
||||||
|
import com.android.launcher3.model.data.ItemInfo;
|
||||||
import com.android.launcher3.pm.PinRequestHelper;
|
import com.android.launcher3.pm.PinRequestHelper;
|
||||||
import com.android.launcher3.util.InstantAppResolver;
|
|
||||||
import com.android.launcher3.views.BaseDragLayer;
|
import com.android.launcher3.views.BaseDragLayer;
|
||||||
import com.android.launcher3.widget.PendingAddShortcutInfo;
|
import com.android.launcher3.widget.PendingAddShortcutInfo;
|
||||||
import com.android.launcher3.widget.PendingAddWidgetInfo;
|
import com.android.launcher3.widget.PendingAddWidgetInfo;
|
||||||
|
@ -87,7 +87,6 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener
|
||||||
private Bundle mWidgetOptions;
|
private Bundle mWidgetOptions;
|
||||||
|
|
||||||
private boolean mFinishOnPause = false;
|
private boolean mFinishOnPause = false;
|
||||||
private InstantAppResolver mInstantAppResolver;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle savedInstanceState) {
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
@ -101,7 +100,6 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener
|
||||||
|
|
||||||
mApp = LauncherAppState.getInstance(this);
|
mApp = LauncherAppState.getInstance(this);
|
||||||
mIdp = mApp.getInvariantDeviceProfile();
|
mIdp = mApp.getInvariantDeviceProfile();
|
||||||
mInstantAppResolver = InstantAppResolver.newInstance(this);
|
|
||||||
|
|
||||||
// Use the application context to get the device profile, as in multiwindow-mode, the
|
// Use the application context to get the device profile, as in multiwindow-mode, the
|
||||||
// confirmation activity might be rotated.
|
// confirmation activity might be rotated.
|
||||||
|
@ -322,6 +320,8 @@ public class AddItemActivity extends BaseActivity implements OnLongClickListener
|
||||||
}
|
}
|
||||||
|
|
||||||
private void logCommand(StatsLogManager.EventEnum command) {
|
private void logCommand(StatsLogManager.EventEnum command) {
|
||||||
getStatsLogManager().logger().log(command);
|
getStatsLogManager().logger()
|
||||||
|
.withItemInfo((ItemInfo) mWidgetCell.getWidgetView().getTag())
|
||||||
|
.log(command);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,6 +50,7 @@ import android.util.LongSparseArray;
|
||||||
import android.util.TimingLogger;
|
import android.util.TimingLogger;
|
||||||
|
|
||||||
import com.android.launcher3.LauncherAppState;
|
import com.android.launcher3.LauncherAppState;
|
||||||
|
import com.android.launcher3.LauncherAppWidgetProviderInfo;
|
||||||
import com.android.launcher3.LauncherModel;
|
import com.android.launcher3.LauncherModel;
|
||||||
import com.android.launcher3.LauncherSettings;
|
import com.android.launcher3.LauncherSettings;
|
||||||
import com.android.launcher3.Utilities;
|
import com.android.launcher3.Utilities;
|
||||||
|
@ -315,6 +316,7 @@ public class LoaderTask implements Runnable {
|
||||||
final PackageManagerHelper pmHelper = new PackageManagerHelper(context);
|
final PackageManagerHelper pmHelper = new PackageManagerHelper(context);
|
||||||
final boolean isSafeMode = pmHelper.isSafeMode();
|
final boolean isSafeMode = pmHelper.isSafeMode();
|
||||||
final boolean isSdCardReady = Utilities.isBootCompleted();
|
final boolean isSdCardReady = Utilities.isBootCompleted();
|
||||||
|
final WidgetManagerHelper widgetHelper = new WidgetManagerHelper(context);
|
||||||
|
|
||||||
boolean clearDb = false;
|
boolean clearDb = false;
|
||||||
try {
|
try {
|
||||||
|
@ -402,6 +404,7 @@ public class LoaderTask implements Runnable {
|
||||||
|
|
||||||
WorkspaceItemInfo info;
|
WorkspaceItemInfo info;
|
||||||
LauncherAppWidgetInfo appWidgetInfo;
|
LauncherAppWidgetInfo appWidgetInfo;
|
||||||
|
LauncherAppWidgetProviderInfo widgetProviderInfo;
|
||||||
Intent intent;
|
Intent intent;
|
||||||
String targetPkg;
|
String targetPkg;
|
||||||
|
|
||||||
|
@ -731,6 +734,19 @@ public class LoaderTask implements Runnable {
|
||||||
+ appWidgetInfo.spanX + "x" + appWidgetInfo.spanY);
|
+ appWidgetInfo.spanX + "x" + appWidgetInfo.spanY);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
widgetProviderInfo =
|
||||||
|
widgetHelper.getLauncherAppWidgetInfo(appWidgetId);
|
||||||
|
if (widgetProviderInfo != null
|
||||||
|
&& (appWidgetInfo.spanX < widgetProviderInfo.minSpanX
|
||||||
|
|| appWidgetInfo.spanY < widgetProviderInfo.minSpanY)) {
|
||||||
|
// This can happen when display size changes.
|
||||||
|
c.markDeleted("Widget removed, min sizes not met: "
|
||||||
|
+ "span=" + appWidgetInfo.spanX + "x"
|
||||||
|
+ appWidgetInfo.spanY + " minSpan="
|
||||||
|
+ widgetProviderInfo.minSpanX + "x"
|
||||||
|
+ widgetProviderInfo.minSpanY);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (!c.isOnWorkspaceOrHotseat()) {
|
if (!c.isOnWorkspaceOrHotseat()) {
|
||||||
c.markDeleted("Widget found where container != " +
|
c.markDeleted("Widget found where container != " +
|
||||||
"CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!");
|
"CONTAINER_DESKTOP nor CONTAINER_HOTSEAT - ignoring!");
|
||||||
|
|
|
@ -32,6 +32,7 @@ import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SH
|
||||||
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
|
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_SHORTCUT;
|
||||||
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_TASK;
|
import static com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_TASK;
|
||||||
import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.CONTAINER_NOT_SET;
|
import static com.android.launcher3.logger.LauncherAtom.ContainerInfo.ContainerCase.CONTAINER_NOT_SET;
|
||||||
|
import static com.android.launcher3.shortcuts.ShortcutKey.EXTRA_SHORTCUT_ID;
|
||||||
|
|
||||||
import android.content.ComponentName;
|
import android.content.ComponentName;
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
|
@ -50,10 +51,10 @@ import com.android.launcher3.logger.LauncherAtom.ContainerInfo;
|
||||||
import com.android.launcher3.logger.LauncherAtom.PredictionContainer;
|
import com.android.launcher3.logger.LauncherAtom.PredictionContainer;
|
||||||
import com.android.launcher3.logger.LauncherAtom.SearchResultContainer;
|
import com.android.launcher3.logger.LauncherAtom.SearchResultContainer;
|
||||||
import com.android.launcher3.logger.LauncherAtom.SettingsContainer;
|
import com.android.launcher3.logger.LauncherAtom.SettingsContainer;
|
||||||
|
import com.android.launcher3.logger.LauncherAtom.Shortcut;
|
||||||
import com.android.launcher3.logger.LauncherAtom.ShortcutsContainer;
|
import com.android.launcher3.logger.LauncherAtom.ShortcutsContainer;
|
||||||
import com.android.launcher3.logger.LauncherAtom.TaskSwitcherContainer;
|
import com.android.launcher3.logger.LauncherAtom.TaskSwitcherContainer;
|
||||||
import com.android.launcher3.model.ModelWriter;
|
import com.android.launcher3.model.ModelWriter;
|
||||||
import com.android.launcher3.shortcuts.ShortcutKey;
|
|
||||||
import com.android.launcher3.util.ContentWriter;
|
import com.android.launcher3.util.ContentWriter;
|
||||||
|
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
@ -282,9 +283,14 @@ public class ItemInfo {
|
||||||
case ITEM_TYPE_DEEP_SHORTCUT:
|
case ITEM_TYPE_DEEP_SHORTCUT:
|
||||||
itemBuilder
|
itemBuilder
|
||||||
.setShortcut(nullableComponent
|
.setShortcut(nullableComponent
|
||||||
.map(component -> LauncherAtom.Shortcut.newBuilder()
|
.map(component -> {
|
||||||
.setShortcutName(component.flattenToShortString())
|
Shortcut.Builder lsb = Shortcut.newBuilder()
|
||||||
.setShortcutId(ShortcutKey.fromItemInfo(this).getId()))
|
.setShortcutName(component.flattenToShortString());
|
||||||
|
Optional.ofNullable(getIntent())
|
||||||
|
.map(i -> i.getStringExtra(EXTRA_SHORTCUT_ID))
|
||||||
|
.ifPresent(lsb::setShortcutId);
|
||||||
|
return lsb;
|
||||||
|
})
|
||||||
.orElse(LauncherAtom.Shortcut.newBuilder()));
|
.orElse(LauncherAtom.Shortcut.newBuilder()));
|
||||||
break;
|
break;
|
||||||
case ITEM_TYPE_SHORTCUT:
|
case ITEM_TYPE_SHORTCUT:
|
||||||
|
|
|
@ -61,4 +61,8 @@ public class RemoteActionItemInfo extends ItemInfoWithIcon {
|
||||||
public boolean shouldStartInLauncher() {
|
public boolean shouldStartInLauncher() {
|
||||||
return mShouldStart;
|
return mShouldStart;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isEscapeHatch() {
|
||||||
|
return mToken.contains("item_type:[ESCAPE_HATCH]");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -275,7 +275,8 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat {
|
||||||
launchBackTutorialPreference.setSummary("Learn how to use the Back gesture");
|
launchBackTutorialPreference.setSummary("Learn how to use the Back gesture");
|
||||||
launchBackTutorialPreference.setOnPreferenceClickListener(preference -> {
|
launchBackTutorialPreference.setOnPreferenceClickListener(preference -> {
|
||||||
startActivity(launchSandboxIntent.putExtra(
|
startActivity(launchSandboxIntent.putExtra(
|
||||||
"tutorial_type", "RIGHT_EDGE_BACK_NAVIGATION"));
|
"tutorial_steps",
|
||||||
|
new String[] {"RIGHT_EDGE_BACK_NAVIGATION"}));
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
sandboxCategory.addPreference(launchBackTutorialPreference);
|
sandboxCategory.addPreference(launchBackTutorialPreference);
|
||||||
|
@ -284,7 +285,9 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat {
|
||||||
launchHomeTutorialPreference.setTitle("Launch Home Tutorial");
|
launchHomeTutorialPreference.setTitle("Launch Home Tutorial");
|
||||||
launchHomeTutorialPreference.setSummary("Learn how to use the Home gesture");
|
launchHomeTutorialPreference.setSummary("Learn how to use the Home gesture");
|
||||||
launchHomeTutorialPreference.setOnPreferenceClickListener(preference -> {
|
launchHomeTutorialPreference.setOnPreferenceClickListener(preference -> {
|
||||||
startActivity(launchSandboxIntent.putExtra("tutorial_type", "HOME_NAVIGATION"));
|
startActivity(launchSandboxIntent.putExtra(
|
||||||
|
"tutorial_steps",
|
||||||
|
new String[] {"HOME_NAVIGATION"}));
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
sandboxCategory.addPreference(launchHomeTutorialPreference);
|
sandboxCategory.addPreference(launchHomeTutorialPreference);
|
||||||
|
@ -293,7 +296,9 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat {
|
||||||
launchOverviewTutorialPreference.setTitle("Launch Overview Tutorial");
|
launchOverviewTutorialPreference.setTitle("Launch Overview Tutorial");
|
||||||
launchOverviewTutorialPreference.setSummary("Learn how to use the Overview gesture");
|
launchOverviewTutorialPreference.setSummary("Learn how to use the Overview gesture");
|
||||||
launchOverviewTutorialPreference.setOnPreferenceClickListener(preference -> {
|
launchOverviewTutorialPreference.setOnPreferenceClickListener(preference -> {
|
||||||
startActivity(launchSandboxIntent.putExtra("tutorial_type", "OVERVIEW_NAVIGATION"));
|
startActivity(launchSandboxIntent.putExtra(
|
||||||
|
"tutorial_steps",
|
||||||
|
new String[] {"OVERVIEW_NAVIGATION"}));
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
sandboxCategory.addPreference(launchOverviewTutorialPreference);
|
sandboxCategory.addPreference(launchOverviewTutorialPreference);
|
||||||
|
@ -302,7 +307,9 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat {
|
||||||
launchAssistantTutorialPreference.setTitle("Launch Assistant Tutorial");
|
launchAssistantTutorialPreference.setTitle("Launch Assistant Tutorial");
|
||||||
launchAssistantTutorialPreference.setSummary("Learn how to use the Assistant gesture");
|
launchAssistantTutorialPreference.setSummary("Learn how to use the Assistant gesture");
|
||||||
launchAssistantTutorialPreference.setOnPreferenceClickListener(preference -> {
|
launchAssistantTutorialPreference.setOnPreferenceClickListener(preference -> {
|
||||||
startActivity(launchSandboxIntent.putExtra("tutorial_type", "ASSISTANT"));
|
startActivity(launchSandboxIntent.putExtra(
|
||||||
|
"tutorial_steps",
|
||||||
|
new String[] {"ASSISTANT"}));
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
sandboxCategory.addPreference(launchAssistantTutorialPreference);
|
sandboxCategory.addPreference(launchAssistantTutorialPreference);
|
||||||
|
@ -311,7 +318,9 @@ public class DeveloperOptionsFragment extends PreferenceFragmentCompat {
|
||||||
launchSandboxModeTutorialPreference.setTitle("Launch Sandbox Mode");
|
launchSandboxModeTutorialPreference.setTitle("Launch Sandbox Mode");
|
||||||
launchSandboxModeTutorialPreference.setSummary("Practice navigation gestures");
|
launchSandboxModeTutorialPreference.setSummary("Practice navigation gestures");
|
||||||
launchSandboxModeTutorialPreference.setOnPreferenceClickListener(preference -> {
|
launchSandboxModeTutorialPreference.setOnPreferenceClickListener(preference -> {
|
||||||
startActivity(launchSandboxIntent.putExtra("tutorial_type", "SANDBOX_MODE"));
|
startActivity(launchSandboxIntent.putExtra(
|
||||||
|
"tutorial_steps",
|
||||||
|
new String[] {"SANDBOX_MODE"}));
|
||||||
return true;
|
return true;
|
||||||
});
|
});
|
||||||
sandboxCategory.addPreference(launchSandboxModeTutorialPreference);
|
sandboxCategory.addPreference(launchSandboxModeTutorialPreference);
|
||||||
|
|
|
@ -21,14 +21,9 @@ import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
|
||||||
import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE;
|
import static android.util.DisplayMetrics.DENSITY_DEVICE_STABLE;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.ContentResolver;
|
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.database.ContentObserver;
|
|
||||||
import android.os.Handler;
|
|
||||||
import android.provider.Settings;
|
|
||||||
import android.util.Log;
|
|
||||||
|
|
||||||
import com.android.launcher3.R;
|
import com.android.launcher3.R;
|
||||||
import com.android.launcher3.Utilities;
|
import com.android.launcher3.Utilities;
|
||||||
|
@ -43,16 +38,6 @@ public class RotationHelper implements OnSharedPreferenceChangeListener {
|
||||||
|
|
||||||
public static final String ALLOW_ROTATION_PREFERENCE_KEY = "pref_allowRotation";
|
public static final String ALLOW_ROTATION_PREFERENCE_KEY = "pref_allowRotation";
|
||||||
|
|
||||||
private final ContentResolver mContentResolver;
|
|
||||||
private boolean mSystemAutoRotateEnabled;
|
|
||||||
|
|
||||||
private ContentObserver mSystemAutoRotateObserver = new ContentObserver(new Handler()) {
|
|
||||||
@Override
|
|
||||||
public void onChange(boolean selfChange) {
|
|
||||||
updateAutoRotateSetting();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
public static boolean getAllowRotationDefaultValue() {
|
public static boolean getAllowRotationDefaultValue() {
|
||||||
// If the device's pixel density was scaled (usually via settings for A11y), use the
|
// If the device's pixel density was scaled (usually via settings for A11y), use the
|
||||||
// original dimensions to determine if rotation is allowed of not.
|
// original dimensions to determine if rotation is allowed of not.
|
||||||
|
@ -106,20 +91,6 @@ public class RotationHelper implements OnSharedPreferenceChangeListener {
|
||||||
} else {
|
} else {
|
||||||
mSharedPrefs = null;
|
mSharedPrefs = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
mContentResolver = activity.getContentResolver();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateAutoRotateSetting() {
|
|
||||||
int autoRotateEnabled = 0;
|
|
||||||
try {
|
|
||||||
autoRotateEnabled = Settings.System.getInt(mContentResolver,
|
|
||||||
Settings.System.ACCELEROMETER_ROTATION);
|
|
||||||
} catch (Settings.SettingNotFoundException e) {
|
|
||||||
Log.e(TAG, "autorotate setting not found", e);
|
|
||||||
}
|
|
||||||
|
|
||||||
mSystemAutoRotateEnabled = autoRotateEnabled == 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -129,7 +100,6 @@ public class RotationHelper implements OnSharedPreferenceChangeListener {
|
||||||
getAllowRotationDefaultValue());
|
getAllowRotationDefaultValue());
|
||||||
if (mHomeRotationEnabled != wasRotationEnabled) {
|
if (mHomeRotationEnabled != wasRotationEnabled) {
|
||||||
notifyChange();
|
notifyChange();
|
||||||
updateAutoRotateSetting();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,11 +135,6 @@ public class RotationHelper implements OnSharedPreferenceChangeListener {
|
||||||
if (!mInitialized) {
|
if (!mInitialized) {
|
||||||
mInitialized = true;
|
mInitialized = true;
|
||||||
notifyChange();
|
notifyChange();
|
||||||
|
|
||||||
mContentResolver.registerContentObserver(
|
|
||||||
Settings.System.getUriFor(Settings.System.ACCELEROMETER_ROTATION),
|
|
||||||
false, mSystemAutoRotateObserver);
|
|
||||||
updateAutoRotateSetting();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -179,7 +144,6 @@ public class RotationHelper implements OnSharedPreferenceChangeListener {
|
||||||
if (mSharedPrefs != null) {
|
if (mSharedPrefs != null) {
|
||||||
mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this);
|
mSharedPrefs.unregisterOnSharedPreferenceChangeListener(this);
|
||||||
}
|
}
|
||||||
mContentResolver.unregisterContentObserver(mSystemAutoRotateObserver);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,9 +189,8 @@ public class RotationHelper implements OnSharedPreferenceChangeListener {
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return String.format("[mStateHandlerRequest=%d, mCurrentStateRequest=%d,"
|
return String.format("[mStateHandlerRequest=%d, mCurrentStateRequest=%d,"
|
||||||
+ " mLastActivityFlags=%d, mIgnoreAutoRotateSettings=%b, mHomeRotationEnabled=%b,"
|
+ " mLastActivityFlags=%d, mIgnoreAutoRotateSettings=%b, mHomeRotationEnabled=%b]",
|
||||||
+ " mSystemAutoRotateEnabled=%b]",
|
|
||||||
mStateHandlerRequest, mCurrentStateRequest, mLastActivityFlags,
|
mStateHandlerRequest, mCurrentStateRequest, mLastActivityFlags,
|
||||||
mIgnoreAutoRotateSettings, mHomeRotationEnabled, mSystemAutoRotateEnabled);
|
mIgnoreAutoRotateSettings, mHomeRotationEnabled);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 androidx.annotation.VisibleForTesting;
|
||||||
|
|
||||||
|
import com.android.launcher3.Utilities;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -157,13 +159,13 @@ public class DisplayController implements DisplayListener {
|
||||||
private final ArrayList<DisplayInfoChangeListener> mListeners = new ArrayList<>();
|
private final ArrayList<DisplayInfoChangeListener> mListeners = new ArrayList<>();
|
||||||
private DisplayController.Info mInfo;
|
private DisplayController.Info mInfo;
|
||||||
|
|
||||||
private DisplayHolder(Context displayContext) {
|
private DisplayHolder(Context displayContext, Display display) {
|
||||||
mDisplayContext = displayContext;
|
mDisplayContext = displayContext;
|
||||||
// Note that the Display object must be obtained from DisplayManager which is
|
// Note that the Display object must be obtained from DisplayManager which is
|
||||||
// associated to the display context, so the Display is isolated from Activity and
|
// associated to the display context, so the Display is isolated from Activity and
|
||||||
// Application to provide the actual state of device that excludes the additional
|
// Application to provide the actual state of device that excludes the additional
|
||||||
// adjustment and override.
|
// adjustment and override.
|
||||||
mInfo = new DisplayController.Info(mDisplayContext);
|
mInfo = new DisplayController.Info(display);
|
||||||
mId = mInfo.id;
|
mId = mInfo.id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -180,22 +182,31 @@ public class DisplayController implements DisplayListener {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void handleOnChange() {
|
protected void handleOnChange() {
|
||||||
|
Display display = Utilities.ATLEAST_R
|
||||||
|
? mDisplayContext.getDisplay()
|
||||||
|
: mDisplayContext
|
||||||
|
.getSystemService(DisplayManager.class)
|
||||||
|
.getDisplay(mId);
|
||||||
|
if (display == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
Info oldInfo = mInfo;
|
Info oldInfo = mInfo;
|
||||||
Info info = new Info(mDisplayContext);
|
Info newInfo = new Info(display);
|
||||||
|
|
||||||
int change = 0;
|
int change = 0;
|
||||||
if (info.hasDifferentSize(oldInfo)) {
|
if (newInfo.hasDifferentSize(oldInfo)) {
|
||||||
change |= CHANGE_SIZE;
|
change |= CHANGE_SIZE;
|
||||||
}
|
}
|
||||||
if (oldInfo.rotation != info.rotation) {
|
if (newInfo.rotation != oldInfo.rotation) {
|
||||||
change |= CHANGE_ROTATION;
|
change |= CHANGE_ROTATION;
|
||||||
}
|
}
|
||||||
if (info.singleFrameMs != oldInfo.singleFrameMs) {
|
if (newInfo.singleFrameMs != oldInfo.singleFrameMs) {
|
||||||
change |= CHANGE_FRAME_DELAY;
|
change |= CHANGE_FRAME_DELAY;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (change != 0) {
|
if (change != 0) {
|
||||||
mInfo = info;
|
mInfo = newInfo;
|
||||||
final int flags = change;
|
final int flags = change;
|
||||||
MAIN_EXECUTOR.execute(() -> notifyChange(flags));
|
MAIN_EXECUTOR.execute(() -> notifyChange(flags));
|
||||||
}
|
}
|
||||||
|
@ -216,7 +227,7 @@ public class DisplayController implements DisplayListener {
|
||||||
// Use application context to create display context so that it can have its own
|
// Use application context to create display context so that it can have its own
|
||||||
// Resources.
|
// Resources.
|
||||||
Context displayContext = context.getApplicationContext().createDisplayContext(display);
|
Context displayContext = context.getApplicationContext().createDisplayContext(display);
|
||||||
return new DisplayHolder(displayContext);
|
return new DisplayHolder(displayContext, display);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,12 +255,7 @@ public class DisplayController implements DisplayListener {
|
||||||
this.metrics = metrics;
|
this.metrics = metrics;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Info(Context context) {
|
public Info(Display display) {
|
||||||
this(context, context.getSystemService(DisplayManager.class)
|
|
||||||
.getDisplay(DEFAULT_DISPLAY));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Info(Context context, Display display) {
|
|
||||||
id = display.getDisplayId();
|
id = display.getDisplayId();
|
||||||
rotation = display.getRotation();
|
rotation = display.getRotation();
|
||||||
|
|
||||||
|
@ -262,7 +268,8 @@ public class DisplayController implements DisplayListener {
|
||||||
display.getRealSize(realSize);
|
display.getRealSize(realSize);
|
||||||
display.getCurrentSizeRange(smallestSize, largestSize);
|
display.getCurrentSizeRange(smallestSize, largestSize);
|
||||||
|
|
||||||
metrics = context.getResources().getDisplayMetrics();
|
metrics = new DisplayMetrics();
|
||||||
|
display.getMetrics(metrics);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean hasDifferentSize(Info info) {
|
private boolean hasDifferentSize(Info info) {
|
||||||
|
|
|
@ -20,10 +20,8 @@ import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
||||||
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
|
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.pm.ShortcutInfo;
|
import android.content.pm.ShortcutInfo;
|
||||||
import android.graphics.Point;
|
import android.graphics.Point;
|
||||||
import android.os.Bundle;
|
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
@ -37,8 +35,9 @@ import com.android.launcher3.DropTarget;
|
||||||
import com.android.launcher3.Launcher;
|
import com.android.launcher3.Launcher;
|
||||||
import com.android.launcher3.LauncherAppState;
|
import com.android.launcher3.LauncherAppState;
|
||||||
import com.android.launcher3.R;
|
import com.android.launcher3.R;
|
||||||
import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload;
|
import com.android.launcher3.allapps.AllAppsStore;
|
||||||
import com.android.launcher3.allapps.search.AllAppsSearchBarController.PayloadResultHandler;
|
import com.android.launcher3.allapps.search.AllAppsSearchBarController.SearchTargetHandler;
|
||||||
|
import com.android.launcher3.allapps.search.SearchEventTracker;
|
||||||
import com.android.launcher3.dragndrop.DragOptions;
|
import com.android.launcher3.dragndrop.DragOptions;
|
||||||
import com.android.launcher3.dragndrop.DraggableView;
|
import com.android.launcher3.dragndrop.DraggableView;
|
||||||
import com.android.launcher3.graphics.DragPreviewProvider;
|
import com.android.launcher3.graphics.DragPreviewProvider;
|
||||||
|
@ -48,24 +47,28 @@ import com.android.launcher3.model.data.ItemInfoWithIcon;
|
||||||
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
||||||
import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
|
import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
|
||||||
import com.android.launcher3.touch.ItemLongClickListener;
|
import com.android.launcher3.touch.ItemLongClickListener;
|
||||||
import com.android.systemui.plugins.AllAppsSearchPlugin;
|
import com.android.launcher3.util.ComponentKey;
|
||||||
import com.android.systemui.plugins.shared.SearchTarget;
|
import com.android.systemui.plugins.shared.SearchTarget;
|
||||||
import com.android.systemui.plugins.shared.SearchTargetEvent;
|
import com.android.systemui.plugins.shared.SearchTargetEvent;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A view representing a high confidence app search result that includes shortcuts
|
* A view representing a high confidence app search result that includes shortcuts
|
||||||
|
* TODO (sfufa@) consolidate this with SearchResultIconRow
|
||||||
*/
|
*/
|
||||||
public class HeroSearchResultView extends LinearLayout implements DragSource,
|
public class HeroSearchResultView extends LinearLayout implements DragSource, SearchTargetHandler {
|
||||||
PayloadResultHandler<List<Pair<ShortcutInfo, ItemInfoWithIcon>>> {
|
|
||||||
|
public static final String TARGET_TYPE_HERO_APP = "hero_app";
|
||||||
|
|
||||||
public static final int MAX_SHORTCUTS_COUNT = 2;
|
public static final int MAX_SHORTCUTS_COUNT = 2;
|
||||||
private final Object[] mTargetInfo = createTargetInfo();
|
|
||||||
BubbleTextView mBubbleTextView;
|
private SearchTarget mSearchTarget;
|
||||||
View mIconView;
|
private BubbleTextView mBubbleTextView;
|
||||||
BubbleTextView[] mDeepShortcutTextViews = new BubbleTextView[2];
|
private View mIconView;
|
||||||
AllAppsSearchPlugin mPlugin;
|
private BubbleTextView[] mDeepShortcutTextViews = new BubbleTextView[2];
|
||||||
|
|
||||||
|
|
||||||
public HeroSearchResultView(Context context) {
|
public HeroSearchResultView(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
|
@ -96,8 +99,6 @@ public class HeroSearchResultView extends LinearLayout implements DragSource,
|
||||||
launcher.getItemOnClickListener().onClick(view);
|
launcher.getItemOnClickListener().onClick(view);
|
||||||
});
|
});
|
||||||
mBubbleTextView.setOnLongClickListener(new HeroItemDragHandler(getContext(), this));
|
mBubbleTextView.setOnLongClickListener(new HeroItemDragHandler(getContext(), this));
|
||||||
setLayoutParams(
|
|
||||||
new LinearLayout.LayoutParams(LayoutParams.MATCH_PARENT, grid.allAppsCellHeightPx));
|
|
||||||
|
|
||||||
|
|
||||||
mDeepShortcutTextViews[0] = findViewById(R.id.shortcut_0);
|
mDeepShortcutTextViews[0] = findViewById(R.id.shortcut_0);
|
||||||
|
@ -108,35 +109,39 @@ public class HeroSearchResultView extends LinearLayout implements DragSource,
|
||||||
grid.allAppsIconSizePx));
|
grid.allAppsIconSizePx));
|
||||||
bubbleTextView.setOnClickListener(view -> {
|
bubbleTextView.setOnClickListener(view -> {
|
||||||
WorkspaceItemInfo itemInfo = (WorkspaceItemInfo) bubbleTextView.getTag();
|
WorkspaceItemInfo itemInfo = (WorkspaceItemInfo) bubbleTextView.getTag();
|
||||||
SearchTargetEvent event = getSearchTargetEvent(
|
SearchTargetEvent event = new SearchTargetEvent.Builder(mSearchTarget,
|
||||||
SearchTarget.ItemType.APP_HERO,
|
SearchTargetEvent.CHILD_SELECT).setShortcutPosition(itemInfo.rank).build();
|
||||||
SearchTargetEvent.CHILD_SELECT);
|
SearchEventTracker.getInstance(getContext()).notifySearchTargetEvent(event);
|
||||||
event.bundle = getAppBundle(itemInfo);
|
|
||||||
event.bundle.putString("shortcut_id", itemInfo.getDeepShortcutId());
|
|
||||||
if (mPlugin != null) {
|
|
||||||
mPlugin.notifySearchTargetEvent(event);
|
|
||||||
}
|
|
||||||
launcher.getItemOnClickListener().onClick(view);
|
launcher.getItemOnClickListener().onClick(view);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Apply {@link ItemInfo} for appIcon and shortcut Icons
|
|
||||||
*/
|
|
||||||
@Override
|
@Override
|
||||||
public void applyAdapterInfo(
|
public void applySearchTarget(SearchTarget searchTarget) {
|
||||||
AdapterItemWithPayload<List<Pair<ShortcutInfo, ItemInfoWithIcon>>> adapterItem) {
|
mSearchTarget = searchTarget;
|
||||||
mBubbleTextView.applyFromApplicationInfo(adapterItem.appInfo);
|
AllAppsStore apps = Launcher.getLauncher(getContext()).getAppsView().getAppsStore();
|
||||||
|
AppInfo appInfo = apps.getApp(new ComponentKey(searchTarget.getComponentName(),
|
||||||
|
searchTarget.getUserHandle()));
|
||||||
|
List<ShortcutInfo> infos = mSearchTarget.getShortcutInfos();
|
||||||
|
|
||||||
|
ArrayList<Pair<ShortcutInfo, ItemInfoWithIcon>> shortcuts = new ArrayList<>();
|
||||||
|
for (int i = 0; infos != null && i < infos.size() && i < MAX_SHORTCUTS_COUNT; i++) {
|
||||||
|
ShortcutInfo shortcutInfo = infos.get(i);
|
||||||
|
ItemInfoWithIcon si = new WorkspaceItemInfo(shortcutInfo, getContext());
|
||||||
|
si.rank = i;
|
||||||
|
shortcuts.add(new Pair<>(shortcutInfo, si));
|
||||||
|
}
|
||||||
|
|
||||||
|
mBubbleTextView.applyFromApplicationInfo(appInfo);
|
||||||
mIconView.setBackground(mBubbleTextView.getIcon());
|
mIconView.setBackground(mBubbleTextView.getIcon());
|
||||||
mIconView.setTag(adapterItem.appInfo);
|
mIconView.setTag(appInfo);
|
||||||
List<Pair<ShortcutInfo, ItemInfoWithIcon>> shortcutDetails = adapterItem.getPayload();
|
|
||||||
LauncherAppState appState = LauncherAppState.getInstance(getContext());
|
LauncherAppState appState = LauncherAppState.getInstance(getContext());
|
||||||
for (int i = 0; i < mDeepShortcutTextViews.length; i++) {
|
for (int i = 0; i < mDeepShortcutTextViews.length; i++) {
|
||||||
BubbleTextView shortcutView = mDeepShortcutTextViews[i];
|
BubbleTextView shortcutView = mDeepShortcutTextViews[i];
|
||||||
mDeepShortcutTextViews[i].setVisibility(shortcutDetails.size() > i ? VISIBLE : GONE);
|
mDeepShortcutTextViews[i].setVisibility(shortcuts.size() > i ? VISIBLE : GONE);
|
||||||
if (i < shortcutDetails.size()) {
|
if (i < shortcuts.size()) {
|
||||||
Pair<ShortcutInfo, ItemInfoWithIcon> p = shortcutDetails.get(i);
|
Pair<ShortcutInfo, ItemInfoWithIcon> p = shortcuts.get(i);
|
||||||
//apply ItemInfo and prepare view
|
//apply ItemInfo and prepare view
|
||||||
shortcutView.applyFromWorkspaceItem((WorkspaceItemInfo) p.second);
|
shortcutView.applyFromWorkspaceItem((WorkspaceItemInfo) p.second);
|
||||||
MODEL_EXECUTOR.execute(() -> {
|
MODEL_EXECUTOR.execute(() -> {
|
||||||
|
@ -146,13 +151,7 @@ public class HeroSearchResultView extends LinearLayout implements DragSource,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
mPlugin = adapterItem.getPlugin();
|
SearchEventTracker.INSTANCE.get(getContext()).registerWeakHandler(searchTarget, this);
|
||||||
adapterItem.setSelectionHandler(this::handleSelection);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object[] getTargetInfo() {
|
|
||||||
return mTargetInfo;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -190,38 +189,21 @@ public class HeroSearchResultView extends LinearLayout implements DragSource,
|
||||||
mLauncher.getWorkspace().beginDragShared(mContainer.mBubbleTextView,
|
mLauncher.getWorkspace().beginDragShared(mContainer.mBubbleTextView,
|
||||||
draggableView, mContainer, itemInfo, previewProvider, new DragOptions());
|
draggableView, mContainer, itemInfo, previewProvider, new DragOptions());
|
||||||
|
|
||||||
SearchTargetEvent event = mContainer.getSearchTargetEvent(
|
SearchTargetEvent event = new SearchTargetEvent.Builder(mContainer.mSearchTarget,
|
||||||
SearchTarget.ItemType.APP_HERO, SearchTargetEvent.LONG_PRESS);
|
SearchTargetEvent.LONG_PRESS).build();
|
||||||
event.bundle = getAppBundle(itemInfo);
|
SearchEventTracker.INSTANCE.get(mLauncher).notifySearchTargetEvent(event);
|
||||||
if (mContainer.mPlugin != null) {
|
|
||||||
mContainer.mPlugin.notifySearchTargetEvent(event);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleSelection(int eventType) {
|
@Override
|
||||||
|
public void handleSelection(int eventType) {
|
||||||
ItemInfo itemInfo = (ItemInfo) mBubbleTextView.getTag();
|
ItemInfo itemInfo = (ItemInfo) mBubbleTextView.getTag();
|
||||||
if (itemInfo == null) return;
|
if (itemInfo == null) return;
|
||||||
Launcher launcher = Launcher.getLauncher(getContext());
|
Launcher launcher = Launcher.getLauncher(getContext());
|
||||||
launcher.startActivitySafely(this, itemInfo.getIntent(), itemInfo);
|
launcher.startActivitySafely(this, itemInfo.getIntent(), itemInfo);
|
||||||
|
|
||||||
SearchTargetEvent event = getSearchTargetEvent(
|
SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(
|
||||||
SearchTarget.ItemType.APP_HERO, eventType);
|
new SearchTargetEvent.Builder(mSearchTarget, eventType).build());
|
||||||
event.bundle = getAppBundle(itemInfo);
|
|
||||||
if (mPlugin != null) {
|
|
||||||
mPlugin.notifySearchTargetEvent(event);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Helper method to generate {@link SearchTargetEvent} bundle from {@link ItemInfo}
|
|
||||||
*/
|
|
||||||
public static Bundle getAppBundle(ItemInfo itemInfo) {
|
|
||||||
Bundle b = new Bundle();
|
|
||||||
b.putParcelable(Intent.EXTRA_COMPONENT_NAME, itemInfo.getTargetComponent());
|
|
||||||
b.putParcelable(Intent.EXTRA_USER, itemInfo.user);
|
|
||||||
return b;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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,134 +22,175 @@ import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
|
||||||
import android.app.RemoteAction;
|
import android.app.RemoteAction;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.pm.ShortcutInfo;
|
import android.content.pm.ShortcutInfo;
|
||||||
|
import android.content.res.TypedArray;
|
||||||
|
import android.graphics.Canvas;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
|
import android.widget.EditText;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.android.launcher3.Launcher;
|
import com.android.launcher3.Launcher;
|
||||||
import com.android.launcher3.LauncherAppState;
|
import com.android.launcher3.LauncherAppState;
|
||||||
import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload;
|
import com.android.launcher3.R;
|
||||||
import com.android.launcher3.allapps.search.AllAppsSearchBarController;
|
import com.android.launcher3.allapps.search.AllAppsSearchBarController;
|
||||||
|
import com.android.launcher3.allapps.search.SearchEventTracker;
|
||||||
|
import com.android.launcher3.icons.BitmapInfo;
|
||||||
import com.android.launcher3.icons.LauncherIcons;
|
import com.android.launcher3.icons.LauncherIcons;
|
||||||
import com.android.launcher3.model.data.ItemInfo;
|
import com.android.launcher3.model.data.ItemInfo;
|
||||||
import com.android.launcher3.model.data.ItemInfoWithIcon;
|
import com.android.launcher3.model.data.ItemInfoWithIcon;
|
||||||
import com.android.launcher3.model.data.RemoteActionItemInfo;
|
import com.android.launcher3.model.data.RemoteActionItemInfo;
|
||||||
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
||||||
import com.android.launcher3.touch.ItemClickHandler;
|
import com.android.launcher3.touch.ItemClickHandler;
|
||||||
import com.android.systemui.plugins.AllAppsSearchPlugin;
|
import com.android.launcher3.util.Themes;
|
||||||
import com.android.systemui.plugins.shared.SearchTarget;
|
import com.android.systemui.plugins.shared.SearchTarget;
|
||||||
import com.android.systemui.plugins.shared.SearchTarget.ItemType;
|
|
||||||
import com.android.systemui.plugins.shared.SearchTargetEvent;
|
import com.android.systemui.plugins.shared.SearchTargetEvent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A view representing a stand alone shortcut search result
|
* A view representing a stand alone shortcut search result
|
||||||
*/
|
*/
|
||||||
public class SearchResultIconRow extends DoubleShadowBubbleTextView implements
|
public class SearchResultIconRow extends DoubleShadowBubbleTextView implements
|
||||||
AllAppsSearchBarController.PayloadResultHandler<SearchTarget> {
|
AllAppsSearchBarController.SearchTargetHandler {
|
||||||
|
|
||||||
private final Object[] mTargetInfo = createTargetInfo();
|
|
||||||
private ShortcutInfo mShortcutInfo;
|
public static final String TARGET_TYPE_REMOTE_ACTION = "remote_action";
|
||||||
private AllAppsSearchPlugin mPlugin;
|
public static final String TARGET_TYPE_SUGGEST = "suggest";
|
||||||
private AdapterItemWithPayload<SearchTarget> mAdapterItem;
|
public static final String TARGET_TYPE_SHORTCUT = "shortcut";
|
||||||
|
|
||||||
|
|
||||||
|
public static final String REMOTE_ACTION_SHOULD_START = "should_start_for_result";
|
||||||
|
public static final String REMOTE_ACTION_TOKEN = "action_token";
|
||||||
|
|
||||||
|
private final int mCustomIconResId;
|
||||||
|
private final boolean mMatchesInset;
|
||||||
|
|
||||||
|
private SearchTarget mSearchTarget;
|
||||||
|
|
||||||
|
|
||||||
public SearchResultIconRow(@NonNull Context context) {
|
public SearchResultIconRow(@NonNull Context context) {
|
||||||
super(context);
|
this(context, null, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SearchResultIconRow(@NonNull Context context,
|
public SearchResultIconRow(@NonNull Context context,
|
||||||
@Nullable AttributeSet attrs) {
|
@Nullable AttributeSet attrs) {
|
||||||
super(context, attrs);
|
this(context, attrs, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SearchResultIconRow(@NonNull Context context, @Nullable AttributeSet attrs,
|
public SearchResultIconRow(@NonNull Context context, @Nullable AttributeSet attrs,
|
||||||
int defStyleAttr) {
|
int defStyleAttr) {
|
||||||
super(context, attrs, defStyleAttr);
|
super(context, attrs, defStyleAttr);
|
||||||
|
TypedArray a = context.obtainStyledAttributes(attrs,
|
||||||
|
R.styleable.SearchResultIconRow, defStyleAttr, 0);
|
||||||
|
mCustomIconResId = a.getResourceId(R.styleable.SearchResultIconRow_customIcon, 0);
|
||||||
|
mMatchesInset = a.getBoolean(R.styleable.SearchResultIconRow_matchTextInsetWithQuery,
|
||||||
|
false);
|
||||||
|
|
||||||
|
a.recycle();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onAttachedToWindow() {
|
||||||
|
super.onAttachedToWindow();
|
||||||
|
Launcher launcher = Launcher.getLauncher(getContext());
|
||||||
|
if (mMatchesInset && launcher.getAppsView() != null && getParent() != null) {
|
||||||
|
EditText editText = launcher.getAppsView().getSearchUiManager().getEditText();
|
||||||
|
if (editText != null) {
|
||||||
|
int counterOffset = getIconSize() + getCompoundDrawablePadding() / 2;
|
||||||
|
setPadding(editText.getLeft() - counterOffset, getPaddingTop(),
|
||||||
|
getPaddingRight(), getPaddingBottom());
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void applyAdapterInfo(AdapterItemWithPayload<SearchTarget> adapterItemWithPayload) {
|
protected void drawFocusHighlight(Canvas canvas) {
|
||||||
if (mAdapterItem != null) {
|
mHighlightPaint.setColor(mHighlightColor);
|
||||||
mAdapterItem.setSelectionHandler(null);
|
float r = Themes.getDialogCornerRadius(getContext());
|
||||||
|
canvas.drawRoundRect(0, 0, getWidth(), getHeight(), r, r, mHighlightPaint);
|
||||||
}
|
}
|
||||||
mAdapterItem = adapterItemWithPayload;
|
|
||||||
SearchTarget payload = adapterItemWithPayload.getPayload();
|
|
||||||
mPlugin = adapterItemWithPayload.getPlugin();
|
|
||||||
|
|
||||||
if (payload.mRemoteAction != null) {
|
|
||||||
prepareUsingRemoteAction(payload.mRemoteAction,
|
@Override
|
||||||
payload.bundle.getString(SearchTarget.REMOTE_ACTION_TOKEN),
|
public void applySearchTarget(SearchTarget searchTarget) {
|
||||||
payload.bundle.getBoolean(SearchTarget.REMOTE_ACTION_SHOULD_START));
|
mSearchTarget = searchTarget;
|
||||||
} else {
|
String type = searchTarget.getItemType();
|
||||||
prepareUsingShortcutInfo(payload.shortcuts.get(0));
|
if (type.equals(TARGET_TYPE_REMOTE_ACTION) || type.equals(TARGET_TYPE_SUGGEST)) {
|
||||||
|
prepareUsingRemoteAction(searchTarget.getRemoteAction(),
|
||||||
|
searchTarget.getExtras().getString(REMOTE_ACTION_TOKEN),
|
||||||
|
searchTarget.getExtras().getBoolean(REMOTE_ACTION_SHOULD_START),
|
||||||
|
type.equals(TARGET_TYPE_REMOTE_ACTION));
|
||||||
|
|
||||||
|
} else if (type.equals(TARGET_TYPE_SHORTCUT)) {
|
||||||
|
prepareUsingShortcutInfo(searchTarget.getShortcutInfos().get(0));
|
||||||
}
|
}
|
||||||
setOnClickListener(v -> handleSelection(SearchTargetEvent.SELECT));
|
setOnClickListener(v -> handleSelection(SearchTargetEvent.SELECT));
|
||||||
adapterItemWithPayload.setSelectionHandler(this::handleSelection);
|
SearchEventTracker.INSTANCE.get(getContext()).registerWeakHandler(searchTarget, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void prepareUsingShortcutInfo(ShortcutInfo shortcutInfo) {
|
private void prepareUsingShortcutInfo(ShortcutInfo shortcutInfo) {
|
||||||
mShortcutInfo = shortcutInfo;
|
WorkspaceItemInfo workspaceItemInfo = new WorkspaceItemInfo(shortcutInfo, getContext());
|
||||||
WorkspaceItemInfo workspaceItemInfo = new WorkspaceItemInfo(mShortcutInfo, getContext());
|
|
||||||
applyFromWorkspaceItem(workspaceItemInfo);
|
applyFromWorkspaceItem(workspaceItemInfo);
|
||||||
LauncherAppState launcherAppState = LauncherAppState.getInstance(getContext());
|
LauncherAppState launcherAppState = LauncherAppState.getInstance(getContext());
|
||||||
|
if (!loadIconFromResource()) {
|
||||||
MODEL_EXECUTOR.execute(() -> {
|
MODEL_EXECUTOR.execute(() -> {
|
||||||
launcherAppState.getIconCache().getShortcutIcon(workspaceItemInfo, mShortcutInfo);
|
launcherAppState.getIconCache().getShortcutIcon(workspaceItemInfo, shortcutInfo);
|
||||||
reapplyItemInfoAsync(workspaceItemInfo);
|
reapplyItemInfoAsync(workspaceItemInfo);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void prepareUsingRemoteAction(RemoteAction remoteAction, String token, boolean start) {
|
private void prepareUsingRemoteAction(RemoteAction remoteAction, String token, boolean start,
|
||||||
|
boolean useIconToBadge) {
|
||||||
RemoteActionItemInfo itemInfo = new RemoteActionItemInfo(remoteAction, token, start);
|
RemoteActionItemInfo itemInfo = new RemoteActionItemInfo(remoteAction, token, start);
|
||||||
|
|
||||||
applyFromRemoteActionInfo(itemInfo);
|
applyFromRemoteActionInfo(itemInfo);
|
||||||
|
if (itemInfo.isEscapeHatch() || !loadIconFromResource()) {
|
||||||
UI_HELPER_EXECUTOR.post(() -> {
|
UI_HELPER_EXECUTOR.post(() -> {
|
||||||
// If the Drawable from the remote action is not AdaptiveBitmap, styling will not work.
|
// If the Drawable from the remote action is not AdaptiveBitmap, styling will not
|
||||||
|
// work.
|
||||||
try (LauncherIcons li = LauncherIcons.obtain(getContext())) {
|
try (LauncherIcons li = LauncherIcons.obtain(getContext())) {
|
||||||
Drawable d = itemInfo.getRemoteAction().getIcon().loadDrawable(getContext());
|
Drawable d = itemInfo.getRemoteAction().getIcon().loadDrawable(getContext());
|
||||||
itemInfo.bitmap = li.createBadgedIconBitmap(d, itemInfo.user,
|
BitmapInfo bitmap = li.createBadgedIconBitmap(d, itemInfo.user,
|
||||||
Build.VERSION.SDK_INT);
|
Build.VERSION.SDK_INT);
|
||||||
|
|
||||||
|
if (useIconToBadge) {
|
||||||
|
BitmapInfo placeholder = li.createIconBitmap(
|
||||||
|
itemInfo.getRemoteAction().getTitle().toString().substring(0, 1),
|
||||||
|
bitmap.color);
|
||||||
|
itemInfo.bitmap = li.badgeBitmap(placeholder.icon, bitmap);
|
||||||
|
} else {
|
||||||
|
itemInfo.bitmap = bitmap;
|
||||||
|
}
|
||||||
reapplyItemInfoAsync(itemInfo);
|
reapplyItemInfoAsync(itemInfo);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean loadIconFromResource() {
|
||||||
|
if (mCustomIconResId == 0) return false;
|
||||||
|
setIcon(Launcher.getLauncher(getContext()).getDrawable(mCustomIconResId));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void reapplyItemInfoAsync(ItemInfoWithIcon itemInfoWithIcon) {
|
void reapplyItemInfoAsync(ItemInfoWithIcon itemInfoWithIcon) {
|
||||||
MAIN_EXECUTOR.post(() -> reapplyItemInfo(itemInfoWithIcon));
|
MAIN_EXECUTOR.post(() -> reapplyItemInfo(itemInfoWithIcon));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object[] getTargetInfo() {
|
public void handleSelection(int eventType) {
|
||||||
return mTargetInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleSelection(int eventType) {
|
|
||||||
ItemInfo itemInfo = (ItemInfo) getTag();
|
ItemInfo itemInfo = (ItemInfo) getTag();
|
||||||
Launcher launcher = Launcher.getLauncher(getContext());
|
Launcher launcher = Launcher.getLauncher(getContext());
|
||||||
final SearchTargetEvent searchTargetEvent;
|
|
||||||
if (itemInfo instanceof WorkspaceItemInfo) {
|
if (itemInfo instanceof WorkspaceItemInfo) {
|
||||||
ItemClickHandler.onClickAppShortcut(this, (WorkspaceItemInfo) itemInfo, launcher);
|
ItemClickHandler.onClickAppShortcut(this, (WorkspaceItemInfo) itemInfo, launcher);
|
||||||
searchTargetEvent = getSearchTargetEvent(SearchTarget.ItemType.SHORTCUT,
|
|
||||||
eventType);
|
|
||||||
searchTargetEvent.shortcut = mShortcutInfo;
|
|
||||||
} else {
|
} else {
|
||||||
RemoteActionItemInfo remoteItemInfo = (RemoteActionItemInfo) itemInfo;
|
ItemClickHandler.onClickRemoteAction(launcher, (RemoteActionItemInfo) itemInfo);
|
||||||
ItemClickHandler.onClickRemoteAction(launcher, remoteItemInfo);
|
|
||||||
searchTargetEvent = getSearchTargetEvent(ItemType.ACTION,
|
|
||||||
eventType);
|
|
||||||
searchTargetEvent.bundle = new Bundle();
|
|
||||||
searchTargetEvent.remoteAction = remoteItemInfo.getRemoteAction();
|
|
||||||
searchTargetEvent.bundle.putBoolean(SearchTarget.REMOTE_ACTION_SHOULD_START,
|
|
||||||
remoteItemInfo.shouldStartInLauncher());
|
|
||||||
searchTargetEvent.bundle.putString(SearchTarget.REMOTE_ACTION_TOKEN,
|
|
||||||
remoteItemInfo.getToken());
|
|
||||||
}
|
|
||||||
if (mPlugin != null) {
|
|
||||||
mPlugin.notifySearchTargetEvent(searchTargetEvent);
|
|
||||||
}
|
}
|
||||||
|
SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(
|
||||||
|
new SearchTargetEvent.Builder(mSearchTarget, eventType).build());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,9 +23,12 @@ import android.content.Intent;
|
||||||
import android.content.pm.ApplicationInfo;
|
import android.content.pm.ApplicationInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.Canvas;
|
||||||
import android.graphics.drawable.BitmapDrawable;
|
import android.graphics.drawable.BitmapDrawable;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.Process;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
|
@ -39,10 +42,10 @@ import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
|
||||||
import com.android.launcher3.DeviceProfile;
|
import com.android.launcher3.DeviceProfile;
|
||||||
import com.android.launcher3.Launcher;
|
import com.android.launcher3.Launcher;
|
||||||
import com.android.launcher3.R;
|
import com.android.launcher3.R;
|
||||||
import com.android.launcher3.allapps.AllAppsGridAdapter;
|
|
||||||
import com.android.launcher3.allapps.search.AllAppsSearchBarController;
|
import com.android.launcher3.allapps.search.AllAppsSearchBarController;
|
||||||
import com.android.launcher3.util.Themes;
|
import com.android.launcher3.allapps.search.SearchEventTracker;
|
||||||
import com.android.systemui.plugins.AllAppsSearchPlugin;
|
import com.android.launcher3.icons.BitmapInfo;
|
||||||
|
import com.android.launcher3.icons.LauncherIcons;
|
||||||
import com.android.systemui.plugins.shared.SearchTarget;
|
import com.android.systemui.plugins.shared.SearchTarget;
|
||||||
import com.android.systemui.plugins.shared.SearchTargetEvent;
|
import com.android.systemui.plugins.shared.SearchTargetEvent;
|
||||||
|
|
||||||
|
@ -52,7 +55,9 @@ import java.util.ArrayList;
|
||||||
* A view representing a single people search result in all apps
|
* A view representing a single people search result in all apps
|
||||||
*/
|
*/
|
||||||
public class SearchResultPeopleView extends LinearLayout implements
|
public class SearchResultPeopleView extends LinearLayout implements
|
||||||
AllAppsSearchBarController.PayloadResultHandler<Bundle> {
|
AllAppsSearchBarController.SearchTargetHandler {
|
||||||
|
|
||||||
|
public static final String TARGET_TYPE_PEOPLE = "people";
|
||||||
|
|
||||||
private final int mIconSize;
|
private final int mIconSize;
|
||||||
private final int mButtonSize;
|
private final int mButtonSize;
|
||||||
|
@ -60,9 +65,10 @@ public class SearchResultPeopleView extends LinearLayout implements
|
||||||
private View mIconView;
|
private View mIconView;
|
||||||
private TextView mTitleView;
|
private TextView mTitleView;
|
||||||
private ImageButton[] mProviderButtons = new ImageButton[3];
|
private ImageButton[] mProviderButtons = new ImageButton[3];
|
||||||
private AllAppsSearchPlugin mPlugin;
|
|
||||||
private Intent mIntent;
|
private Intent mIntent;
|
||||||
private final Object[] mTargetInfo = createTargetInfo();
|
|
||||||
|
|
||||||
|
private SearchTarget mSearchTarget;
|
||||||
|
|
||||||
public SearchResultPeopleView(Context context) {
|
public SearchResultPeopleView(Context context) {
|
||||||
this(context, null, 0);
|
this(context, null, 0);
|
||||||
|
@ -99,21 +105,19 @@ public class SearchResultPeopleView extends LinearLayout implements
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void applyAdapterInfo(
|
public void applySearchTarget(SearchTarget searchTarget) {
|
||||||
AllAppsGridAdapter.AdapterItemWithPayload<Bundle> adapterItemWithPayload) {
|
mSearchTarget = searchTarget;
|
||||||
Bundle payload = adapterItemWithPayload.getPayload();
|
Bundle payload = searchTarget.getExtras();
|
||||||
mPlugin = adapterItemWithPayload.getPlugin();
|
|
||||||
mTitleView.setText(payload.getString("title"));
|
mTitleView.setText(payload.getString("title"));
|
||||||
mIntent = payload.getParcelable("intent");
|
mIntent = payload.getParcelable("intent");
|
||||||
Bitmap icon = payload.getParcelable("icon");
|
Bitmap contactIcon = payload.getParcelable("icon");
|
||||||
if (icon != null) {
|
try (LauncherIcons li = LauncherIcons.obtain(getContext())) {
|
||||||
RoundedBitmapDrawable d = RoundedBitmapDrawableFactory.create(getResources(), icon);
|
BitmapInfo badgeInfo = li.createBadgedIconBitmap(
|
||||||
float radius = Themes.getDialogCornerRadius(getContext());
|
getAppIcon(mIntent.getPackage()), Process.myUserHandle(),
|
||||||
d.setCornerRadius(radius);
|
Build.VERSION.SDK_INT);
|
||||||
d.setBounds(0, 0, mIconSize, mIconSize);
|
setIcon(li.badgeBitmap(roundBitmap(contactIcon), badgeInfo).icon, false);
|
||||||
BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(),
|
} catch (Exception e) {
|
||||||
Bitmap.createScaledBitmap(icon, mIconSize, mIconSize, false));
|
setIcon(contactIcon, true);
|
||||||
mIconView.setBackground(d);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayList<Bundle> providers = payload.getParcelableArrayList("providers");
|
ArrayList<Bundle> providers = payload.getParcelableArrayList("providers");
|
||||||
|
@ -122,59 +126,80 @@ public class SearchResultPeopleView extends LinearLayout implements
|
||||||
if (providers != null && i < providers.size()) {
|
if (providers != null && i < providers.size()) {
|
||||||
Bundle provider = providers.get(i);
|
Bundle provider = providers.get(i);
|
||||||
Intent intent = provider.getParcelable("intent");
|
Intent intent = provider.getParcelable("intent");
|
||||||
setupProviderButton(button, provider, intent, adapterItemWithPayload);
|
setupProviderButton(button, provider, intent);
|
||||||
String pkg = provider.getString("package_name");
|
|
||||||
UI_HELPER_EXECUTOR.post(() -> {
|
UI_HELPER_EXECUTOR.post(() -> {
|
||||||
try {
|
String pkg = provider.getString("package_name");
|
||||||
ApplicationInfo applicationInfo = mPackageManager.getApplicationInfo(
|
Drawable appIcon = getAppIcon(pkg);
|
||||||
pkg, 0);
|
if (appIcon != null) {
|
||||||
Drawable appIcon = applicationInfo.loadIcon(mPackageManager);
|
|
||||||
MAIN_EXECUTOR.post(() -> button.setImageDrawable(appIcon));
|
MAIN_EXECUTOR.post(() -> button.setImageDrawable(appIcon));
|
||||||
} catch (PackageManager.NameNotFoundException ignored) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
|
button.setVisibility(VISIBLE);
|
||||||
} else {
|
} else {
|
||||||
button.setVisibility(GONE);
|
button.setVisibility(GONE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
adapterItemWithPayload.setSelectionHandler(this::handleSelection);
|
SearchEventTracker.INSTANCE.get(getContext()).registerWeakHandler(searchTarget, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
/**
|
||||||
public Object[] getTargetInfo() {
|
* Normalizes the bitmap to look like rounded App Icon
|
||||||
return mTargetInfo;
|
* TODO(b/170234747) to support styling, generate adaptive icon drawable and generate
|
||||||
|
* bitmap from it.
|
||||||
|
*/
|
||||||
|
private Bitmap roundBitmap(Bitmap icon) {
|
||||||
|
final RoundedBitmapDrawable d = RoundedBitmapDrawableFactory.create(getResources(), icon);
|
||||||
|
d.setCornerRadius(R.attr.folderIconRadius);
|
||||||
|
d.setBounds(0, 0, mIconSize, mIconSize);
|
||||||
|
final Bitmap bitmap = Bitmap.createBitmap(d.getBounds().width(), d.getBounds().height(),
|
||||||
|
Bitmap.Config.ARGB_8888);
|
||||||
|
Canvas canvas = new Canvas(bitmap);
|
||||||
|
d.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
|
||||||
|
d.draw(canvas);
|
||||||
|
return bitmap;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setupProviderButton(ImageButton button, Bundle provider, Intent intent,
|
private void setIcon(Bitmap icon, Boolean round) {
|
||||||
AllAppsGridAdapter.AdapterItem adapterItem) {
|
if (round) {
|
||||||
|
RoundedBitmapDrawable d = RoundedBitmapDrawableFactory.create(getResources(), icon);
|
||||||
|
d.setCornerRadius(R.attr.folderIconRadius);
|
||||||
|
d.setBounds(0, 0, mIconSize, mIconSize);
|
||||||
|
mIconView.setBackground(d);
|
||||||
|
} else {
|
||||||
|
mIconView.setBackground(new BitmapDrawable(getResources(), icon));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Drawable getAppIcon(String pkg) {
|
||||||
|
try {
|
||||||
|
ApplicationInfo applicationInfo = mPackageManager.getApplicationInfo(
|
||||||
|
pkg, 0);
|
||||||
|
return applicationInfo.loadIcon(mPackageManager);
|
||||||
|
} catch (PackageManager.NameNotFoundException ignored) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupProviderButton(ImageButton button, Bundle provider, Intent intent) {
|
||||||
Launcher launcher = Launcher.getLauncher(getContext());
|
Launcher launcher = Launcher.getLauncher(getContext());
|
||||||
button.setOnClickListener(b -> {
|
button.setOnClickListener(b -> {
|
||||||
launcher.startActivitySafely(b, intent, null);
|
launcher.startActivitySafely(b, intent, null);
|
||||||
SearchTargetEvent searchTargetEvent = getSearchTargetEvent(
|
Bundle bundle = new Bundle();
|
||||||
SearchTarget.ItemType.PEOPLE,
|
bundle.putBundle("provider", provider);
|
||||||
SearchTargetEvent.CHILD_SELECT);
|
SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(
|
||||||
searchTargetEvent.bundle = new Bundle();
|
new SearchTargetEvent.Builder(mSearchTarget,
|
||||||
searchTargetEvent.bundle.putParcelable("intent", mIntent);
|
SearchTargetEvent.CHILD_SELECT).setExtras(bundle).build());
|
||||||
searchTargetEvent.bundle.putBundle("provider", provider);
|
|
||||||
if (mPlugin != null) {
|
|
||||||
mPlugin.notifySearchTargetEvent(searchTargetEvent);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
private void handleSelection(int eventType) {
|
public void handleSelection(int eventType) {
|
||||||
if (mIntent != null) {
|
if (mIntent != null) {
|
||||||
Launcher launcher = Launcher.getLauncher(getContext());
|
Launcher launcher = Launcher.getLauncher(getContext());
|
||||||
launcher.startActivitySafely(this, mIntent, null);
|
launcher.startActivitySafely(this, mIntent, null);
|
||||||
SearchTargetEvent searchTargetEvent = getSearchTargetEvent(SearchTarget.ItemType.PEOPLE,
|
SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(
|
||||||
eventType);
|
new SearchTargetEvent.Builder(mSearchTarget, eventType).build());
|
||||||
searchTargetEvent.bundle = new Bundle();
|
|
||||||
searchTargetEvent.bundle.putParcelable("intent", mIntent);
|
|
||||||
if (mPlugin != null) {
|
|
||||||
mPlugin.notifySearchTargetEvent(searchTargetEvent);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,11 @@ import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.BitmapFactory;
|
import android.graphics.BitmapFactory;
|
||||||
|
import android.graphics.Paint;
|
||||||
|
import android.graphics.PorterDuff;
|
||||||
|
import android.graphics.PorterDuffXfermode;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.graphics.RectF;
|
||||||
import android.graphics.drawable.BitmapDrawable;
|
import android.graphics.drawable.BitmapDrawable;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
@ -36,20 +41,28 @@ import androidx.annotation.Nullable;
|
||||||
import com.android.launcher3.DeviceProfile;
|
import com.android.launcher3.DeviceProfile;
|
||||||
import com.android.launcher3.Launcher;
|
import com.android.launcher3.Launcher;
|
||||||
import com.android.launcher3.R;
|
import com.android.launcher3.R;
|
||||||
import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload;
|
|
||||||
import com.android.launcher3.allapps.search.AllAppsSearchBarController;
|
import com.android.launcher3.allapps.search.AllAppsSearchBarController;
|
||||||
import com.android.systemui.plugins.AllAppsSearchPlugin;
|
import com.android.launcher3.allapps.search.SearchEventTracker;
|
||||||
|
import com.android.launcher3.icons.BitmapRenderer;
|
||||||
|
import com.android.launcher3.util.Themes;
|
||||||
import com.android.systemui.plugins.shared.SearchTarget;
|
import com.android.systemui.plugins.shared.SearchTarget;
|
||||||
import com.android.systemui.plugins.shared.SearchTargetEvent;
|
import com.android.systemui.plugins.shared.SearchTargetEvent;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.net.URLConnection;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A View representing a PlayStore item.
|
* A View representing a PlayStore item.
|
||||||
*/
|
*/
|
||||||
public class SearchResultPlayItem extends LinearLayout implements
|
public class SearchResultPlayItem extends LinearLayout implements
|
||||||
AllAppsSearchBarController.PayloadResultHandler<Bundle> {
|
AllAppsSearchBarController.SearchTargetHandler {
|
||||||
|
|
||||||
|
public static final String TARGET_TYPE_PLAY = "play";
|
||||||
|
|
||||||
|
private static final int BITMAP_CROP_MASK_COLOR = 0xff424242;
|
||||||
|
final Paint mIconPaint = new Paint();
|
||||||
|
final Rect mTempRect = new Rect();
|
||||||
private final DeviceProfile mDeviceProfile;
|
private final DeviceProfile mDeviceProfile;
|
||||||
private View mIconView;
|
private View mIconView;
|
||||||
private TextView mTitleView;
|
private TextView mTitleView;
|
||||||
|
@ -57,8 +70,8 @@ public class SearchResultPlayItem extends LinearLayout implements
|
||||||
private Button mPreviewButton;
|
private Button mPreviewButton;
|
||||||
private String mPackageName;
|
private String mPackageName;
|
||||||
private boolean mIsInstantGame;
|
private boolean mIsInstantGame;
|
||||||
private AllAppsSearchPlugin mPlugin;
|
|
||||||
private final Object[] mTargetInfo = createTargetInfo();
|
private SearchTarget mSearchTarget;
|
||||||
|
|
||||||
|
|
||||||
public SearchResultPlayItem(Context context) {
|
public SearchResultPlayItem(Context context) {
|
||||||
|
@ -91,14 +104,35 @@ public class SearchResultPlayItem extends LinearLayout implements
|
||||||
iconParams.height = mDeviceProfile.allAppsIconSizePx;
|
iconParams.height = mDeviceProfile.allAppsIconSizePx;
|
||||||
iconParams.width = mDeviceProfile.allAppsIconSizePx;
|
iconParams.width = mDeviceProfile.allAppsIconSizePx;
|
||||||
setOnClickListener(view -> handleSelection(SearchTargetEvent.SELECT));
|
setOnClickListener(view -> handleSelection(SearchTargetEvent.SELECT));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Bitmap getRoundedBitmap(Bitmap bitmap) {
|
||||||
|
final int iconSize = bitmap.getWidth();
|
||||||
|
final float radius = Themes.getDialogCornerRadius(getContext());
|
||||||
|
|
||||||
|
Bitmap output = BitmapRenderer.createHardwareBitmap(iconSize, iconSize, (canvas) -> {
|
||||||
|
mTempRect.set(0, 0, iconSize, iconSize);
|
||||||
|
final RectF rectF = new RectF(mTempRect);
|
||||||
|
|
||||||
|
mIconPaint.setAntiAlias(true);
|
||||||
|
mIconPaint.reset();
|
||||||
|
canvas.drawARGB(0, 0, 0, 0);
|
||||||
|
mIconPaint.setColor(BITMAP_CROP_MASK_COLOR);
|
||||||
|
canvas.drawRoundRect(rectF, radius, radius, mIconPaint);
|
||||||
|
|
||||||
|
mIconPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
|
||||||
|
canvas.drawBitmap(bitmap, mTempRect, mTempRect, mIconPaint);
|
||||||
|
});
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void applyAdapterInfo(AdapterItemWithPayload<Bundle> adapterItemWithPayload) {
|
public void applySearchTarget(SearchTarget searchTarget) {
|
||||||
Bundle bundle = adapterItemWithPayload.getPayload();
|
mSearchTarget = searchTarget;
|
||||||
mPlugin = adapterItemWithPayload.getPlugin();
|
Bundle bundle = searchTarget.getExtras();
|
||||||
adapterItemWithPayload.setSelectionHandler(this::handleSelection);
|
SearchEventTracker.INSTANCE.get(getContext()).registerWeakHandler(searchTarget, this);
|
||||||
if (bundle.getString("package", "").equals(mPackageName)) {
|
if (bundle.getString("package", "").equals(mPackageName)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -109,17 +143,19 @@ public class SearchResultPlayItem extends LinearLayout implements
|
||||||
// TODO: Should use a generic type to get values b/165320033
|
// TODO: Should use a generic type to get values b/165320033
|
||||||
showIfNecessary(mDetailViews[0], bundle.getString("price"));
|
showIfNecessary(mDetailViews[0], bundle.getString("price"));
|
||||||
showIfNecessary(mDetailViews[1], bundle.getString("rating"));
|
showIfNecessary(mDetailViews[1], bundle.getString("rating"));
|
||||||
showIfNecessary(mDetailViews[2], bundle.getString("category"));
|
|
||||||
|
|
||||||
mIconView.setBackgroundResource(R.drawable.ic_deepshortcut_placeholder);
|
mIconView.setBackgroundResource(R.drawable.ic_deepshortcut_placeholder);
|
||||||
UI_HELPER_EXECUTOR.execute(() -> {
|
UI_HELPER_EXECUTOR.execute(() -> {
|
||||||
try {
|
try {
|
||||||
// TODO: Handle caching
|
|
||||||
URL url = new URL(bundle.getString("icon_url"));
|
URL url = new URL(bundle.getString("icon_url"));
|
||||||
Bitmap bitmap = BitmapFactory.decodeStream(url.openStream());
|
URLConnection con = url.openConnection();
|
||||||
BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(),
|
// TODO: monitor memory and investigate if it's better to use glide
|
||||||
|
con.addRequestProperty("Cache-Control", "max-age: 0");
|
||||||
|
con.setUseCaches(true);
|
||||||
|
Bitmap bitmap = BitmapFactory.decodeStream(con.getInputStream());
|
||||||
|
BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(), getRoundedBitmap(
|
||||||
Bitmap.createScaledBitmap(bitmap, mDeviceProfile.allAppsIconSizePx,
|
Bitmap.createScaledBitmap(bitmap, mDeviceProfile.allAppsIconSizePx,
|
||||||
mDeviceProfile.allAppsIconSizePx, false));
|
mDeviceProfile.allAppsIconSizePx, false)));
|
||||||
mIconView.post(() -> mIconView.setBackground(bitmapDrawable));
|
mIconView.post(() -> mIconView.setBackground(bitmapDrawable));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
@ -127,11 +163,6 @@ public class SearchResultPlayItem extends LinearLayout implements
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object[] getTargetInfo() {
|
|
||||||
return mTargetInfo;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showIfNecessary(TextView textView, @Nullable String string) {
|
private void showIfNecessary(TextView textView, @Nullable String string) {
|
||||||
if (string == null || string.isEmpty()) {
|
if (string == null || string.isEmpty()) {
|
||||||
textView.setVisibility(GONE);
|
textView.setVisibility(GONE);
|
||||||
|
@ -141,7 +172,8 @@ public class SearchResultPlayItem extends LinearLayout implements
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleSelection(int eventType) {
|
@Override
|
||||||
|
public void handleSelection(int eventType) {
|
||||||
if (mPackageName == null) return;
|
if (mPackageName == null) return;
|
||||||
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(
|
Intent i = new Intent(Intent.ACTION_VIEW, Uri.parse(
|
||||||
"https://play.google.com/store/apps/details?id="
|
"https://play.google.com/store/apps/details?id="
|
||||||
|
@ -167,12 +199,7 @@ public class SearchResultPlayItem extends LinearLayout implements
|
||||||
}
|
}
|
||||||
|
|
||||||
private void logSearchEvent(int eventType) {
|
private void logSearchEvent(int eventType) {
|
||||||
SearchTargetEvent searchTargetEvent = getSearchTargetEvent(
|
SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(
|
||||||
SearchTarget.ItemType.PLAY_RESULTS, eventType);
|
new SearchTargetEvent.Builder(mSearchTarget, eventType).build());
|
||||||
searchTargetEvent.bundle = new Bundle();
|
|
||||||
searchTargetEvent.bundle.putString("package_name", mPackageName);
|
|
||||||
if (mPlugin != null) {
|
|
||||||
mPlugin.notifySearchTargetEvent(searchTargetEvent);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.android.launcher3.allapps.AllAppsGridAdapter;
|
|
||||||
import com.android.launcher3.allapps.search.AllAppsSearchBarController;
|
import com.android.launcher3.allapps.search.AllAppsSearchBarController;
|
||||||
|
import com.android.systemui.plugins.shared.SearchTarget;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Header text view that shows a title for a given section in All apps search
|
* Header text view that shows a title for a given section in All apps search
|
||||||
*/
|
*/
|
||||||
public class SearchSectionHeaderView extends TextView implements
|
public class SearchSectionHeaderView extends TextView implements
|
||||||
AllAppsSearchBarController.PayloadResultHandler<String> {
|
AllAppsSearchBarController.SearchTargetHandler {
|
||||||
|
public static final String TARGET_TYPE_SECTION_HEADER = "section_header";
|
||||||
|
|
||||||
public SearchSectionHeaderView(Context context) {
|
public SearchSectionHeaderView(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
}
|
}
|
||||||
|
@ -43,8 +45,8 @@ public class SearchSectionHeaderView extends TextView implements
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void applyAdapterInfo(AllAppsGridAdapter.AdapterItemWithPayload<String> adapterItem) {
|
public void applySearchTarget(SearchTarget searchTarget) {
|
||||||
String title = adapterItem.getPayload();
|
String title = searchTarget.getExtras().getString("title");
|
||||||
if (title == null || !title.isEmpty()) {
|
if (title == null || !title.isEmpty()) {
|
||||||
setText(title);
|
setText(title);
|
||||||
setVisibility(VISIBLE);
|
setVisibility(VISIBLE);
|
||||||
|
@ -52,9 +54,4 @@ public class SearchSectionHeaderView extends TextView implements
|
||||||
setVisibility(INVISIBLE);
|
setVisibility(INVISIBLE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object[] getTargetInfo() {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -29,9 +29,8 @@ import androidx.annotation.Nullable;
|
||||||
|
|
||||||
import com.android.launcher3.Launcher;
|
import com.android.launcher3.Launcher;
|
||||||
import com.android.launcher3.R;
|
import com.android.launcher3.R;
|
||||||
import com.android.launcher3.allapps.AllAppsGridAdapter;
|
|
||||||
import com.android.launcher3.allapps.search.AllAppsSearchBarController;
|
import com.android.launcher3.allapps.search.AllAppsSearchBarController;
|
||||||
import com.android.systemui.plugins.AllAppsSearchPlugin;
|
import com.android.launcher3.allapps.search.SearchEventTracker;
|
||||||
import com.android.systemui.plugins.shared.SearchTarget;
|
import com.android.systemui.plugins.shared.SearchTarget;
|
||||||
import com.android.systemui.plugins.shared.SearchTargetEvent;
|
import com.android.systemui.plugins.shared.SearchTargetEvent;
|
||||||
|
|
||||||
|
@ -41,14 +40,16 @@ import java.util.ArrayList;
|
||||||
* A row of tappable TextViews with a breadcrumb for settings search.
|
* A row of tappable TextViews with a breadcrumb for settings search.
|
||||||
*/
|
*/
|
||||||
public class SearchSettingsRowView extends LinearLayout implements
|
public class SearchSettingsRowView extends LinearLayout implements
|
||||||
View.OnClickListener, AllAppsSearchBarController.PayloadResultHandler<Bundle> {
|
View.OnClickListener, AllAppsSearchBarController.SearchTargetHandler {
|
||||||
|
|
||||||
|
public static final String TARGET_TYPE_SETTINGS_ROW = "settings_row";
|
||||||
|
|
||||||
|
|
||||||
private TextView mTitleView;
|
private TextView mTitleView;
|
||||||
private TextView mDescriptionView;
|
private TextView mDescriptionView;
|
||||||
private TextView mBreadcrumbsView;
|
private TextView mBreadcrumbsView;
|
||||||
private Intent mIntent;
|
private Intent mIntent;
|
||||||
private AllAppsSearchPlugin mPlugin;
|
private SearchTarget mSearchTarget;
|
||||||
private final Object[] mTargetInfo = createTargetInfo();
|
|
||||||
|
|
||||||
|
|
||||||
public SearchSettingsRowView(@NonNull Context context) {
|
public SearchSettingsRowView(@NonNull Context context) {
|
||||||
|
@ -75,10 +76,9 @@ public class SearchSettingsRowView extends LinearLayout implements
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void applyAdapterInfo(
|
public void applySearchTarget(SearchTarget searchTarget) {
|
||||||
AllAppsGridAdapter.AdapterItemWithPayload<Bundle> adapterItemWithPayload) {
|
mSearchTarget = searchTarget;
|
||||||
Bundle bundle = adapterItemWithPayload.getPayload();
|
Bundle bundle = searchTarget.getExtras();
|
||||||
mPlugin = adapterItemWithPayload.getPlugin();
|
|
||||||
mIntent = bundle.getParcelable("intent");
|
mIntent = bundle.getParcelable("intent");
|
||||||
showIfAvailable(mTitleView, bundle.getString("title"));
|
showIfAvailable(mTitleView, bundle.getString("title"));
|
||||||
showIfAvailable(mDescriptionView, bundle.getString("description"));
|
showIfAvailable(mDescriptionView, bundle.getString("description"));
|
||||||
|
@ -86,12 +86,7 @@ public class SearchSettingsRowView extends LinearLayout implements
|
||||||
//TODO: implement RTL friendly breadcrumbs view
|
//TODO: implement RTL friendly breadcrumbs view
|
||||||
showIfAvailable(mBreadcrumbsView, breadcrumbs != null
|
showIfAvailable(mBreadcrumbsView, breadcrumbs != null
|
||||||
? String.join(" > ", breadcrumbs) : null);
|
? String.join(" > ", breadcrumbs) : null);
|
||||||
adapterItemWithPayload.setSelectionHandler(this::handleSelection);
|
SearchEventTracker.INSTANCE.get(getContext()).registerWeakHandler(searchTarget, this);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object[] getTargetInfo() {
|
|
||||||
return mTargetInfo;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void showIfAvailable(TextView view, @Nullable String string) {
|
private void showIfAvailable(TextView view, @Nullable String string) {
|
||||||
|
@ -108,19 +103,15 @@ public class SearchSettingsRowView extends LinearLayout implements
|
||||||
handleSelection(SearchTargetEvent.SELECT);
|
handleSelection(SearchTargetEvent.SELECT);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleSelection(int eventType) {
|
@Override
|
||||||
|
public void handleSelection(int eventType) {
|
||||||
if (mIntent == null) return;
|
if (mIntent == null) return;
|
||||||
// TODO: create ItemInfo object and then use it to call startActivityForResult for proper
|
// TODO: create ItemInfo object and then use it to call startActivityForResult for proper
|
||||||
// WW logging
|
// WW logging
|
||||||
Launcher launcher = Launcher.getLauncher(getContext());
|
Launcher launcher = Launcher.getLauncher(getContext());
|
||||||
launcher.startActivityForResult(mIntent, 0);
|
launcher.startActivityForResult(mIntent, 0);
|
||||||
|
|
||||||
SearchTargetEvent searchTargetEvent = getSearchTargetEvent(
|
SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(
|
||||||
SearchTarget.ItemType.SETTINGS_ROW, eventType);
|
new SearchTargetEvent.Builder(mSearchTarget, eventType).build());
|
||||||
searchTargetEvent.bundle = new Bundle();
|
|
||||||
searchTargetEvent.bundle.putParcelable("intent", mIntent);
|
|
||||||
if (mPlugin != null) {
|
|
||||||
mPlugin.notifySearchTargetEvent(searchTargetEvent);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
package com.android.launcher3.views;
|
||||||
|
|
||||||
|
import static com.android.launcher3.views.SearchResultIconRow.REMOTE_ACTION_SHOULD_START;
|
||||||
|
import static com.android.launcher3.views.SearchResultIconRow.REMOTE_ACTION_TOKEN;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
|
@ -26,14 +29,13 @@ import androidx.core.graphics.drawable.RoundedBitmapDrawable;
|
||||||
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
|
import androidx.core.graphics.drawable.RoundedBitmapDrawableFactory;
|
||||||
|
|
||||||
import com.android.launcher3.Launcher;
|
import com.android.launcher3.Launcher;
|
||||||
import com.android.launcher3.allapps.AllAppsGridAdapter.AdapterItemWithPayload;
|
|
||||||
import com.android.launcher3.allapps.search.AllAppsSearchBarController;
|
import com.android.launcher3.allapps.search.AllAppsSearchBarController;
|
||||||
|
import com.android.launcher3.allapps.search.SearchEventTracker;
|
||||||
import com.android.launcher3.model.data.ItemInfo;
|
import com.android.launcher3.model.data.ItemInfo;
|
||||||
import com.android.launcher3.model.data.RemoteActionItemInfo;
|
import com.android.launcher3.model.data.RemoteActionItemInfo;
|
||||||
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
import com.android.launcher3.model.data.WorkspaceItemInfo;
|
||||||
import com.android.launcher3.touch.ItemClickHandler;
|
import com.android.launcher3.touch.ItemClickHandler;
|
||||||
import com.android.launcher3.util.Themes;
|
import com.android.launcher3.util.Themes;
|
||||||
import com.android.systemui.plugins.AllAppsSearchPlugin;
|
|
||||||
import com.android.systemui.plugins.shared.SearchTarget;
|
import com.android.systemui.plugins.shared.SearchTarget;
|
||||||
import com.android.systemui.plugins.shared.SearchTargetEvent;
|
import com.android.systemui.plugins.shared.SearchTargetEvent;
|
||||||
|
|
||||||
|
@ -41,11 +43,12 @@ import com.android.systemui.plugins.shared.SearchTargetEvent;
|
||||||
* A view representing a high confidence app search result that includes shortcuts
|
* A view representing a high confidence app search result that includes shortcuts
|
||||||
*/
|
*/
|
||||||
public class ThumbnailSearchResultView extends androidx.appcompat.widget.AppCompatImageView
|
public class ThumbnailSearchResultView extends androidx.appcompat.widget.AppCompatImageView
|
||||||
implements AllAppsSearchBarController.PayloadResultHandler<SearchTarget> {
|
implements AllAppsSearchBarController.SearchTargetHandler {
|
||||||
|
|
||||||
private final Object[] mTargetInfo = createTargetInfo();
|
public static final String TARGET_TYPE_SCREENSHOT = "screenshot";
|
||||||
AllAppsSearchPlugin mPlugin;
|
public static final String TARGET_TYPE_SCREENSHOT_LEGACY = "screenshot_legacy";
|
||||||
int mPosition;
|
|
||||||
|
private SearchTarget mSearchTarget;
|
||||||
|
|
||||||
public ThumbnailSearchResultView(Context context) {
|
public ThumbnailSearchResultView(Context context) {
|
||||||
super(context);
|
super(context);
|
||||||
|
@ -59,7 +62,8 @@ public class ThumbnailSearchResultView extends androidx.appcompat.widget.AppComp
|
||||||
super(context, attrs, defStyleAttr);
|
super(context, attrs, defStyleAttr);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleSelection(int eventType) {
|
@Override
|
||||||
|
public void handleSelection(int eventType) {
|
||||||
Launcher launcher = Launcher.getLauncher(getContext());
|
Launcher launcher = Launcher.getLauncher(getContext());
|
||||||
ItemInfo itemInfo = (ItemInfo) getTag();
|
ItemInfo itemInfo = (ItemInfo) getTag();
|
||||||
if (itemInfo instanceof RemoteActionItemInfo) {
|
if (itemInfo instanceof RemoteActionItemInfo) {
|
||||||
|
@ -68,33 +72,29 @@ public class ThumbnailSearchResultView extends androidx.appcompat.widget.AppComp
|
||||||
} else {
|
} else {
|
||||||
ItemClickHandler.onClickAppShortcut(this, (WorkspaceItemInfo) itemInfo, launcher);
|
ItemClickHandler.onClickAppShortcut(this, (WorkspaceItemInfo) itemInfo, launcher);
|
||||||
}
|
}
|
||||||
if (mPlugin != null) {
|
SearchEventTracker.INSTANCE.get(getContext()).notifySearchTargetEvent(
|
||||||
SearchTargetEvent event = getSearchTargetEvent(
|
new SearchTargetEvent.Builder(mSearchTarget, eventType).build());
|
||||||
SearchTarget.ItemType.SCREENSHOT, eventType);
|
|
||||||
mPlugin.notifySearchTargetEvent(event);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void applyAdapterInfo(AdapterItemWithPayload<SearchTarget> adapterItem) {
|
public void applySearchTarget(SearchTarget target) {
|
||||||
Launcher launcher = Launcher.getLauncher(getContext());
|
mSearchTarget = target;
|
||||||
mPosition = adapterItem.position;
|
|
||||||
|
|
||||||
SearchTarget target = adapterItem.getPayload();
|
|
||||||
Bitmap bitmap;
|
Bitmap bitmap;
|
||||||
if (target.mRemoteAction != null) {
|
if (target.getRemoteAction() != null) {
|
||||||
RemoteActionItemInfo itemInfo = new RemoteActionItemInfo(target.mRemoteAction,
|
RemoteActionItemInfo itemInfo = new RemoteActionItemInfo(target.getRemoteAction(),
|
||||||
target.bundle.getString(SearchTarget.REMOTE_ACTION_TOKEN),
|
target.getExtras().getString(REMOTE_ACTION_TOKEN),
|
||||||
target.bundle.getBoolean(SearchTarget.REMOTE_ACTION_SHOULD_START));
|
target.getExtras().getBoolean(REMOTE_ACTION_SHOULD_START));
|
||||||
ItemClickHandler.onClickRemoteAction(launcher, itemInfo);
|
bitmap = ((BitmapDrawable) target.getRemoteAction().getIcon()
|
||||||
bitmap = ((BitmapDrawable) target.mRemoteAction.getIcon()
|
|
||||||
.loadDrawable(getContext())).getBitmap();
|
.loadDrawable(getContext())).getBitmap();
|
||||||
setTag(itemInfo);
|
// crop
|
||||||
|
bitmap = Bitmap.createBitmap(bitmap, 0,
|
||||||
|
bitmap.getHeight() / 2 - bitmap.getWidth() / 2,
|
||||||
|
bitmap.getWidth(), bitmap.getWidth());
|
||||||
} else {
|
} else {
|
||||||
bitmap = (Bitmap) target.bundle.getParcelable("bitmap");
|
bitmap = (Bitmap) target.getExtras().getParcelable("bitmap");
|
||||||
WorkspaceItemInfo itemInfo = new WorkspaceItemInfo();
|
WorkspaceItemInfo itemInfo = new WorkspaceItemInfo();
|
||||||
itemInfo.intent = new Intent(Intent.ACTION_VIEW)
|
itemInfo.intent = new Intent(Intent.ACTION_VIEW)
|
||||||
.setData(Uri.parse(target.bundle.getString("uri")))
|
.setData(Uri.parse(target.getExtras().getString("uri")))
|
||||||
.setType("image/*")
|
.setType("image/*")
|
||||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
setTag(itemInfo);
|
setTag(itemInfo);
|
||||||
|
@ -103,12 +103,6 @@ public class ThumbnailSearchResultView extends androidx.appcompat.widget.AppComp
|
||||||
drawable.setCornerRadius(Themes.getDialogCornerRadius(getContext()));
|
drawable.setCornerRadius(Themes.getDialogCornerRadius(getContext()));
|
||||||
setImageDrawable(drawable);
|
setImageDrawable(drawable);
|
||||||
setOnClickListener(v -> handleSelection(SearchTargetEvent.SELECT));
|
setOnClickListener(v -> handleSelection(SearchTargetEvent.SELECT));
|
||||||
mPlugin = adapterItem.getPlugin();
|
SearchEventTracker.INSTANCE.get(getContext()).registerWeakHandler(target, this);
|
||||||
adapterItem.setSelectionHandler(this::handleSelection);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Object[] getTargetInfo() {
|
|
||||||
return mTargetInfo;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,6 +17,8 @@
|
||||||
package com.android.systemui.plugins;
|
package com.android.systemui.plugins;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.os.CancellationSignal;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
import com.android.systemui.plugins.annotations.ProvidesInterface;
|
import com.android.systemui.plugins.annotations.ProvidesInterface;
|
||||||
|
@ -32,7 +34,7 @@ import java.util.function.Consumer;
|
||||||
@ProvidesInterface(action = AllAppsSearchPlugin.ACTION, version = AllAppsSearchPlugin.VERSION)
|
@ProvidesInterface(action = AllAppsSearchPlugin.ACTION, version = AllAppsSearchPlugin.VERSION)
|
||||||
public interface AllAppsSearchPlugin extends Plugin {
|
public interface AllAppsSearchPlugin extends Plugin {
|
||||||
String ACTION = "com.android.systemui.action.PLUGIN_ALL_APPS_SEARCH_ACTIONS";
|
String ACTION = "com.android.systemui.action.PLUGIN_ALL_APPS_SEARCH_ACTIONS";
|
||||||
int VERSION = 7;
|
int VERSION = 8;
|
||||||
|
|
||||||
void setup(Activity activity, View view);
|
void setup(Activity activity, View view);
|
||||||
|
|
||||||
|
@ -49,10 +51,21 @@ public interface AllAppsSearchPlugin extends Plugin {
|
||||||
void onWindowVisibilityChanged(int visibility);
|
void onWindowVisibilityChanged(int visibility);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send signal when user starts typing, perform search, when search ends
|
* Send signal when user starts typing, perform search, notify search target
|
||||||
|
* event when search ends.
|
||||||
*/
|
*/
|
||||||
void startedSearchSession();
|
void startedSearchSession();
|
||||||
void performSearch(String query, Consumer<List<SearchTarget>> results);
|
|
||||||
|
/**
|
||||||
|
* Main function that triggers search.
|
||||||
|
*
|
||||||
|
* @param input string that has been typed by a user
|
||||||
|
* @param inputArgs extra info that may be relevant for the input query
|
||||||
|
* @param results contains the result that will be rendered in all apps search surface
|
||||||
|
* @param cancellationSignal {@link CancellationSignal} can be used to share status of current
|
||||||
|
*/
|
||||||
|
void query(String input, Bundle inputArgs, Consumer<List<SearchTarget>> results,
|
||||||
|
CancellationSignal cancellationSignal);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Send over search target interaction events to Plugin
|
* Send over search target interaction events to Plugin
|
||||||
|
|
|
@ -16,8 +16,10 @@
|
||||||
package com.android.systemui.plugins.shared;
|
package com.android.systemui.plugins.shared;
|
||||||
|
|
||||||
import android.app.RemoteAction;
|
import android.app.RemoteAction;
|
||||||
|
import android.content.ComponentName;
|
||||||
import android.content.pm.ShortcutInfo;
|
import android.content.pm.ShortcutInfo;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.UserHandle;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
@ -26,139 +28,145 @@ import java.util.List;
|
||||||
*/
|
*/
|
||||||
public class SearchTarget implements Comparable<SearchTarget> {
|
public class SearchTarget implements Comparable<SearchTarget> {
|
||||||
|
|
||||||
|
private final String mItemId;
|
||||||
|
private final String mItemType;
|
||||||
|
private final float mScore;
|
||||||
|
|
||||||
/**
|
private final ComponentName mComponentName;
|
||||||
* A bundle key for boolean value of whether remote action should be started in launcher or not
|
private final UserHandle mUserHandle;
|
||||||
*/
|
private final List<ShortcutInfo> mShortcutInfos;
|
||||||
public static final String REMOTE_ACTION_SHOULD_START = "should_start_for_result";
|
//TODO: (sfufa) replace with a list of a custom type
|
||||||
public static final String REMOTE_ACTION_TOKEN = "action_token";
|
private final RemoteAction mRemoteAction;
|
||||||
|
private final Bundle mExtras;
|
||||||
|
|
||||||
|
private SearchTarget(String itemId, String itemType, float score,
|
||||||
public enum ViewType {
|
ComponentName componentName, UserHandle userHandle, List<ShortcutInfo> shortcutInfos,
|
||||||
|
RemoteAction remoteAction, Bundle extras) {
|
||||||
/**
|
mItemId = itemId;
|
||||||
* Consists of N number of icons. (N: launcher column count)
|
mItemType = itemType;
|
||||||
*/
|
mScore = score;
|
||||||
TOP_HIT(0),
|
mComponentName = componentName;
|
||||||
|
mUserHandle = userHandle;
|
||||||
/**
|
mShortcutInfos = shortcutInfos;
|
||||||
* Consists of 1 icon and two subsidiary icons.
|
mExtras = extras;
|
||||||
*/
|
mRemoteAction = remoteAction;
|
||||||
HERO(1),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Main/sub/breadcrumb texts are rendered.
|
|
||||||
*/
|
|
||||||
DETAIL(2),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Consists of an icon, three detail strings.
|
|
||||||
*/
|
|
||||||
ROW(3),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Consists of an icon, three detail strings and a button.
|
|
||||||
*/
|
|
||||||
ROW_WITH_BUTTON(4),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Consists of a single slice view
|
|
||||||
*/
|
|
||||||
SLICE(5),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Similar to hero section.
|
|
||||||
*/
|
|
||||||
SHORTCUT(6),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Person icon and handling app icons are rendered.
|
|
||||||
*/
|
|
||||||
PEOPLE(7),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* N number of 1x1 ratio thumbnail is rendered.
|
|
||||||
* (current N = 3)
|
|
||||||
*/
|
|
||||||
THUMBNAIL(8),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Fallback search icon and relevant text is rendered.
|
|
||||||
*/
|
|
||||||
SUGGEST(9);
|
|
||||||
|
|
||||||
private final int mId;
|
|
||||||
|
|
||||||
ViewType(int id) {
|
|
||||||
mId = id;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int get() {
|
public String getItemId() {
|
||||||
return mId;
|
return mItemId;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ItemType {
|
public String getItemType() {
|
||||||
PLAY_RESULTS(0, "Play Store", ViewType.DETAIL),
|
return mItemType;
|
||||||
SETTINGS_ROW(1, "Settings", ViewType.ROW),
|
|
||||||
SETTINGS_SLICE(2, "Settings", ViewType.SLICE),
|
|
||||||
APP(3, "", ViewType.TOP_HIT),
|
|
||||||
APP_HERO(4, "", ViewType.HERO),
|
|
||||||
SHORTCUT(5, "Shortcuts", ViewType.SHORTCUT),
|
|
||||||
PEOPLE(6, "People", ViewType.PEOPLE),
|
|
||||||
SCREENSHOT(7, "Screenshots", ViewType.THUMBNAIL),
|
|
||||||
ACTION(8, "Actions", ViewType.SHORTCUT),
|
|
||||||
SUGGEST(9, "Fallback Search", ViewType.SUGGEST),
|
|
||||||
CHROME_TAB(10, "Chrome Tab", ViewType.SHORTCUT);
|
|
||||||
|
|
||||||
private final int mId;
|
|
||||||
|
|
||||||
/** Used to render section title. */
|
|
||||||
private final String mTitle;
|
|
||||||
private final ViewType mViewType;
|
|
||||||
|
|
||||||
ItemType(int id, String title, ViewType type) {
|
|
||||||
mId = id;
|
|
||||||
mTitle = title;
|
|
||||||
mViewType = type;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ViewType getViewType() {
|
public ComponentName getComponentName() {
|
||||||
return mViewType;
|
return mComponentName;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getTitle() {
|
public UserHandle getUserHandle() {
|
||||||
return mTitle;
|
return mUserHandle;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getId() {
|
public float getScore() {
|
||||||
return mId;
|
return mScore;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ItemType type;
|
public List<ShortcutInfo> getShortcutInfos() {
|
||||||
public List<ShortcutInfo> shortcuts;
|
return mShortcutInfos;
|
||||||
public Bundle bundle;
|
}
|
||||||
public float score;
|
|
||||||
public String mSessionId;
|
|
||||||
public RemoteAction mRemoteAction;
|
|
||||||
|
|
||||||
/**
|
public Bundle getExtras() {
|
||||||
* Constructor to create the search target. Bundle is currently temporary to hold
|
return mExtras;
|
||||||
* search target primitives that cannot be expressed as java primitive objects
|
}
|
||||||
* or AOSP native objects.
|
|
||||||
*/
|
public RemoteAction getRemoteAction() {
|
||||||
public SearchTarget(ItemType itemType, List<ShortcutInfo> shortcuts,
|
return mRemoteAction;
|
||||||
Bundle bundle, float score, String sessionId) {
|
|
||||||
this.type = itemType;
|
|
||||||
this.shortcuts = shortcuts;
|
|
||||||
this.bundle = bundle;
|
|
||||||
this.score = score;
|
|
||||||
this.mSessionId = sessionId;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int compareTo(SearchTarget o) {
|
public int compareTo(SearchTarget o) {
|
||||||
return Float.compare(o.score, score);
|
return Float.compare(o.mScore, mScore);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A builder for {@link SearchTarget}
|
||||||
|
*/
|
||||||
|
public static final class Builder {
|
||||||
|
|
||||||
|
|
||||||
|
private String mItemId;
|
||||||
|
|
||||||
|
private final String mItemType;
|
||||||
|
private final float mScore;
|
||||||
|
|
||||||
|
|
||||||
|
private ComponentName mComponentName;
|
||||||
|
private UserHandle mUserHandle;
|
||||||
|
private List<ShortcutInfo> mShortcutInfos;
|
||||||
|
private Bundle mExtras;
|
||||||
|
private RemoteAction mRemoteAction;
|
||||||
|
|
||||||
|
public Builder(String itemType, float score) {
|
||||||
|
this(itemType, score, null, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder(String itemType, float score, ComponentName cn,
|
||||||
|
UserHandle user) {
|
||||||
|
mItemType = itemType;
|
||||||
|
mScore = score;
|
||||||
|
mComponentName = cn;
|
||||||
|
mUserHandle = user;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getItemId() {
|
||||||
|
return mItemId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float getScore() {
|
||||||
|
return mScore;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setItemId(String itemId) {
|
||||||
|
mItemId = itemId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setComponentName(ComponentName componentName) {
|
||||||
|
mComponentName = componentName;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setUserHandle(UserHandle userHandle) {
|
||||||
|
mUserHandle = userHandle;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setShortcutInfos(List<ShortcutInfo> shortcutInfos) {
|
||||||
|
mShortcutInfos = shortcutInfos;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setExtras(Bundle extras) {
|
||||||
|
mExtras = extras;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setRemoteAction(RemoteAction remoteAction) {
|
||||||
|
mRemoteAction = remoteAction;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a {@link SearchTarget}
|
||||||
|
*/
|
||||||
|
public SearchTarget build() {
|
||||||
|
if (mItemId == null) {
|
||||||
|
throw new IllegalStateException("Item ID is required for building SearchTarget");
|
||||||
|
}
|
||||||
|
return new SearchTarget(mItemId, mItemType, mScore, mComponentName, mUserHandle,
|
||||||
|
mShortcutInfos,
|
||||||
|
mRemoteAction, mExtras);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,32 +15,76 @@
|
||||||
*/
|
*/
|
||||||
package com.android.systemui.plugins.shared;
|
package com.android.systemui.plugins.shared;
|
||||||
|
|
||||||
import android.app.RemoteAction;
|
|
||||||
import android.content.pm.ShortcutInfo;
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Event used for the feedback loop to the plugin. (and future aiai)
|
* Event used for the feedback loop to the plugin. (and future aiai)
|
||||||
*/
|
*/
|
||||||
public class SearchTargetEvent {
|
public class SearchTargetEvent {
|
||||||
|
public static final int POSITION_NONE = -1;
|
||||||
|
|
||||||
public static final int SELECT = 0;
|
public static final int SELECT = 0;
|
||||||
public static final int QUICK_SELECT = 1;
|
public static final int QUICK_SELECT = 1;
|
||||||
public static final int LONG_PRESS = 2;
|
public static final int LONG_PRESS = 2;
|
||||||
public static final int CHILD_SELECT = 3;
|
public static final int CHILD_SELECT = 3;
|
||||||
|
|
||||||
public SearchTarget.ItemType type;
|
private final SearchTarget mSearchTarget;
|
||||||
public ShortcutInfo shortcut;
|
private final int mEventType;
|
||||||
public RemoteAction remoteAction;
|
private final int mShortcutPosition;
|
||||||
public int eventType;
|
private final Bundle mExtras;
|
||||||
public Bundle bundle;
|
|
||||||
public int index;
|
|
||||||
public String sessionIdentifier;
|
|
||||||
|
|
||||||
public SearchTargetEvent(SearchTarget.ItemType itemType, int eventType, int index,
|
public SearchTargetEvent(SearchTarget searchTarget, int eventType, int shortcutPosition,
|
||||||
String sessionId) {
|
Bundle extras) {
|
||||||
this.type = itemType;
|
mSearchTarget = searchTarget;
|
||||||
this.eventType = eventType;
|
mEventType = eventType;
|
||||||
this.index = index;
|
mShortcutPosition = shortcutPosition;
|
||||||
this.sessionIdentifier = sessionId;
|
mExtras = extras;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public SearchTarget getSearchTarget() {
|
||||||
|
return mSearchTarget;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getShortcutPosition() {
|
||||||
|
return mShortcutPosition;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getEventType() {
|
||||||
|
return mEventType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Bundle getExtras() {
|
||||||
|
return mExtras;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A builder for {@link SearchTarget}
|
||||||
|
*/
|
||||||
|
public static final class Builder {
|
||||||
|
private final SearchTarget mSearchTarget;
|
||||||
|
private final int mEventType;
|
||||||
|
private int mShortcutPosition = POSITION_NONE;
|
||||||
|
private Bundle mExtras;
|
||||||
|
|
||||||
|
public Builder(SearchTarget searchTarget, int eventType) {
|
||||||
|
mSearchTarget = searchTarget;
|
||||||
|
mEventType = eventType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setShortcutPosition(int shortcutPosition) {
|
||||||
|
mShortcutPosition = shortcutPosition;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Builder setExtras(Bundle extras) {
|
||||||
|
mExtras = extras;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public SearchTargetEvent build() {
|
||||||
|
return new SearchTargetEvent(mSearchTarget, mEventType, mShortcutPosition, mExtras);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<appwidget-provider
|
<appwidget-provider
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:minWidth="180dp"
|
android:minWidth="1dp"
|
||||||
android:minHeight="110dp"
|
android:minHeight="1dp"
|
||||||
android:updatePeriodMillis="86400000"
|
android:updatePeriodMillis="86400000"
|
||||||
android:initialLayout="@layout/test_layout_appwidget_blue"
|
android:initialLayout="@layout/test_layout_appwidget_blue"
|
||||||
android:resizeMode="horizontal|vertical"
|
android:resizeMode="horizontal|vertical"
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<appwidget-provider
|
<appwidget-provider
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:minWidth="180dp"
|
android:minWidth="1dp"
|
||||||
android:minHeight="110dp"
|
android:minHeight="1dp"
|
||||||
android:updatePeriodMillis="86400000"
|
android:updatePeriodMillis="86400000"
|
||||||
android:initialLayout="@layout/test_layout_appwidget_red"
|
android:initialLayout="@layout/test_layout_appwidget_red"
|
||||||
android:resizeMode="horizontal|vertical"
|
android:resizeMode="horizontal|vertical"
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<appwidget-provider
|
<appwidget-provider
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:minWidth="180dp"
|
android:minWidth="1dp"
|
||||||
android:minHeight="110dp"
|
android:minHeight="1dp"
|
||||||
android:updatePeriodMillis="86400000"
|
android:updatePeriodMillis="86400000"
|
||||||
android:initialLayout="@layout/test_layout_appwidget_blue"
|
android:initialLayout="@layout/test_layout_appwidget_blue"
|
||||||
android:configure="com.android.launcher3.testcomponent.WidgetConfigActivity"
|
android:configure="com.android.launcher3.testcomponent.WidgetConfigActivity"
|
||||||
|
|
|
@ -88,10 +88,6 @@ public class Background extends LauncherInstrumentation.VisibleContainer {
|
||||||
? LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE
|
? LauncherInstrumentation.GestureScope.INSIDE_TO_OUTSIDE
|
||||||
: LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER;
|
: LauncherInstrumentation.GestureScope.OUTSIDE_WITH_PILFER;
|
||||||
|
|
||||||
// b/156044202
|
|
||||||
mLauncher.log("Hierarchy before swiping up to overview:");
|
|
||||||
mLauncher.dumpViewHierarchy();
|
|
||||||
|
|
||||||
mLauncher.sendPointer(
|
mLauncher.sendPointer(
|
||||||
downTime, downTime, MotionEvent.ACTION_DOWN, start, gestureScope);
|
downTime, downTime, MotionEvent.ACTION_DOWN, start, gestureScope);
|
||||||
mLauncher.executeAndWaitForLauncherEvent(
|
mLauncher.executeAndWaitForLauncherEvent(
|
||||||
|
|
Loading…
Reference in New Issue