Snap for 9227242 from 17137e8bce to tm-qpr2-release
Change-Id: I7ec485e36d710d095504fc3e4e290de65cb2cc10
This commit is contained in:
@@ -153,6 +153,7 @@ android_library {
|
||||
"androidx.cardview_cardview",
|
||||
"com.google.android.material_material",
|
||||
"iconloader_base",
|
||||
"view_capture"
|
||||
],
|
||||
manifest: "AndroidManifest-common.xml",
|
||||
sdk_version: "current",
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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";
|
||||
|
||||
package com.android.launcher3.view;
|
||||
|
||||
option java_outer_classname = "ViewCaptureData";
|
||||
|
||||
message ExportedData {
|
||||
|
||||
repeated FrameData frameData = 1;
|
||||
repeated string classname = 2;
|
||||
}
|
||||
|
||||
message FrameData {
|
||||
optional int64 timestamp = 1;
|
||||
optional ViewNode node = 2;
|
||||
}
|
||||
|
||||
message ViewNode {
|
||||
optional int32 classname_index = 1;
|
||||
optional int32 hashcode = 2;
|
||||
|
||||
repeated ViewNode children = 3;
|
||||
|
||||
optional string id = 4;
|
||||
optional int32 left = 5;
|
||||
optional int32 top = 6;
|
||||
optional int32 width = 7;
|
||||
optional int32 height = 8;
|
||||
optional int32 scrollX = 9;
|
||||
optional int32 scrollY = 10;
|
||||
|
||||
optional float translationX = 11;
|
||||
optional float translationY = 12;
|
||||
optional float scaleX = 13 [default = 1];
|
||||
optional float scaleY = 14 [default = 1];
|
||||
optional float alpha = 15 [default = 1];
|
||||
|
||||
optional bool willNotDraw = 16;
|
||||
optional bool clipChildren = 17;
|
||||
optional int32 visibility = 18;
|
||||
|
||||
optional float elevation = 19;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2019 The Android Open Source Project
|
||||
* Copyright (C) 2022 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.
|
||||
@@ -58,6 +58,7 @@ import android.content.SharedPreferences;
|
||||
import android.content.res.Configuration;
|
||||
import android.hardware.SensorManager;
|
||||
import android.hardware.devicestate.DeviceStateManager;
|
||||
import android.media.permission.SafeCloseable;
|
||||
import android.os.Bundle;
|
||||
import android.os.CancellationSignal;
|
||||
import android.os.IBinder;
|
||||
@@ -71,10 +72,12 @@ import android.window.SplashScreen;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.app.viewcapture.ViewCapture;
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.Launcher;
|
||||
import com.android.launcher3.LauncherSettings.Favorites;
|
||||
import com.android.launcher3.LauncherState;
|
||||
import com.android.launcher3.LauncherWidgetHolder;
|
||||
import com.android.launcher3.QuickstepAccessibilityDelegate;
|
||||
import com.android.launcher3.QuickstepTransitionManager;
|
||||
import com.android.launcher3.R;
|
||||
@@ -119,10 +122,8 @@ import com.android.launcher3.util.ObjectWrapper;
|
||||
import com.android.launcher3.util.PendingRequestArgs;
|
||||
import com.android.launcher3.util.PendingSplitSelectInfo;
|
||||
import com.android.launcher3.util.RunnableList;
|
||||
import com.android.launcher3.util.SafeCloseable;
|
||||
import com.android.launcher3.util.SplitConfigurationOptions.SplitPositionOption;
|
||||
import com.android.launcher3.util.TouchController;
|
||||
import com.android.launcher3.widget.LauncherAppWidgetHost;
|
||||
import com.android.quickstep.OverviewCommandHelper;
|
||||
import com.android.quickstep.RecentsModel;
|
||||
import com.android.quickstep.SystemUiProxy;
|
||||
@@ -135,7 +136,6 @@ import com.android.quickstep.util.RemoteAnimationProvider;
|
||||
import com.android.quickstep.util.RemoteFadeOutAnimationListener;
|
||||
import com.android.quickstep.util.SplitSelectStateController;
|
||||
import com.android.quickstep.util.TISBindHelper;
|
||||
import com.android.quickstep.util.ViewCapture;
|
||||
import com.android.quickstep.views.OverviewActionsView;
|
||||
import com.android.quickstep.views.RecentsView;
|
||||
import com.android.quickstep.views.TaskView;
|
||||
@@ -402,7 +402,7 @@ public class QuickstepLauncher extends Launcher {
|
||||
|
||||
super.onDestroy();
|
||||
mHotseatPredictionController.destroy();
|
||||
mViewCapture.close();
|
||||
if (mViewCapture != null) mViewCapture.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -487,11 +487,11 @@ public class QuickstepLauncher extends Launcher {
|
||||
return new QuickstepAtomicAnimationFactory(this);
|
||||
}
|
||||
|
||||
protected LauncherAppWidgetHost createAppWidgetHost() {
|
||||
LauncherAppWidgetHost appWidgetHost = super.createAppWidgetHost();
|
||||
ApiWrapper.setHostInteractionHandler(appWidgetHost,
|
||||
new QuickstepInteractionHandler(this));
|
||||
return appWidgetHost;
|
||||
@Override
|
||||
protected LauncherWidgetHolder createAppWidgetHolder() {
|
||||
LauncherWidgetHolder appWidgetHolder = super.createAppWidgetHolder();
|
||||
appWidgetHolder.setInteractionHandler(new QuickstepInteractionHandler(this));
|
||||
return appWidgetHolder;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -503,7 +503,9 @@ public class QuickstepLauncher extends Launcher {
|
||||
}
|
||||
addMultiWindowModeChangedListener(mDepthController);
|
||||
initUnfoldTransitionProgressProvider();
|
||||
mViewCapture = ViewCapture.INSTANCE.get(this).startCapture(getWindow());
|
||||
if (FeatureFlags.CONTINUOUS_VIEW_TREE_CAPTURE.get()) {
|
||||
mViewCapture = ViewCapture.getInstance().startCapture(getWindow());
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2017 The Android Open Source Project
|
||||
* Copyright (C) 2022 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.
|
||||
@@ -67,6 +67,7 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.UiThread;
|
||||
|
||||
import com.android.app.viewcapture.ViewCapture;
|
||||
import com.android.launcher3.BaseDraggingActivity;
|
||||
import com.android.launcher3.R;
|
||||
import com.android.launcher3.Utilities;
|
||||
@@ -103,7 +104,6 @@ import com.android.quickstep.util.ActiveGestureLog.CompoundString;
|
||||
import com.android.quickstep.util.ProtoTracer;
|
||||
import com.android.quickstep.util.ProxyScreenStatusProvider;
|
||||
import com.android.quickstep.util.SplitScreenBounds;
|
||||
import com.android.quickstep.util.ViewCapture;
|
||||
import com.android.systemui.shared.recents.IOverviewProxy;
|
||||
import com.android.systemui.shared.recents.ISystemUiProxy;
|
||||
import com.android.systemui.shared.system.ActivityManagerWrapper;
|
||||
@@ -1238,7 +1238,9 @@ public class TouchInteractionService extends Service
|
||||
}
|
||||
mTaskbarManager.dumpLogs("", pw);
|
||||
|
||||
ViewCapture.INSTANCE.get(this).dump(pw, fd);
|
||||
if (FeatureFlags.CONTINUOUS_VIEW_TREE_CAPTURE.get()) {
|
||||
ViewCapture.getInstance().dump(pw, fd, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,541 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2022 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.quickstep.util;
|
||||
|
||||
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
||||
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
|
||||
import static com.android.launcher3.util.Executors.createAndStartNewLooper;
|
||||
|
||||
import static java.util.stream.Collectors.toList;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.os.Looper;
|
||||
import android.os.Process;
|
||||
import android.os.SystemClock;
|
||||
import android.os.Trace;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Base64;
|
||||
import android.util.Base64OutputStream;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.util.SparseArray;
|
||||
import android.view.View;
|
||||
import android.view.View.OnAttachStateChangeListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewTreeObserver.OnDrawListener;
|
||||
import android.view.Window;
|
||||
|
||||
import androidx.annotation.UiThread;
|
||||
import androidx.annotation.WorkerThread;
|
||||
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.util.LooperExecutor;
|
||||
import com.android.launcher3.util.MainThreadInitializedObject;
|
||||
import com.android.launcher3.util.SafeCloseable;
|
||||
import com.android.launcher3.view.ViewCaptureData.ExportedData;
|
||||
import com.android.launcher3.view.ViewCaptureData.FrameData;
|
||||
import com.android.launcher3.view.ViewCaptureData.ViewNode;
|
||||
|
||||
import java.io.FileDescriptor;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.FutureTask;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.zip.GZIPOutputStream;
|
||||
|
||||
/**
|
||||
* Utility class for capturing view data every frame
|
||||
*/
|
||||
public class ViewCapture {
|
||||
|
||||
private static final String TAG = "ViewCapture";
|
||||
|
||||
// These flags are copies of two private flags in the View class.
|
||||
private static final int PFLAG_INVALIDATED = 0x80000000;
|
||||
private static final int PFLAG_DIRTY_MASK = 0x00200000;
|
||||
|
||||
// Number of frames to keep in memory
|
||||
private static final int MEMORY_SIZE = 2000;
|
||||
// Initial size of the reference pool. This is at least be 5 * total number of views in
|
||||
// Launcher. This allows the first free frames avoid object allocation during view capture.
|
||||
private static final int INIT_POOL_SIZE = 300;
|
||||
|
||||
public static final MainThreadInitializedObject<ViewCapture> INSTANCE =
|
||||
new MainThreadInitializedObject<>(ViewCapture::new);
|
||||
|
||||
private final List<WindowListener> mListeners = new ArrayList<>();
|
||||
|
||||
private final Context mContext;
|
||||
private final Executor mExecutor;
|
||||
|
||||
// Pool used for capturing view tree on the UI thread.
|
||||
private ViewRef mPool = new ViewRef();
|
||||
|
||||
private ViewCapture(Context context) {
|
||||
mContext = context;
|
||||
if (FeatureFlags.CONTINUOUS_VIEW_TREE_CAPTURE.get()) {
|
||||
Looper looper = createAndStartNewLooper("ViewCapture",
|
||||
Process.THREAD_PRIORITY_FOREGROUND);
|
||||
mExecutor = new LooperExecutor(looper);
|
||||
mExecutor.execute(this::initPool);
|
||||
} else {
|
||||
mExecutor = UI_HELPER_EXECUTOR;
|
||||
}
|
||||
}
|
||||
|
||||
@UiThread
|
||||
private void addToPool(ViewRef start, ViewRef end) {
|
||||
end.next = mPool;
|
||||
mPool = start;
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private void initPool() {
|
||||
ViewRef start = new ViewRef();
|
||||
ViewRef current = start;
|
||||
|
||||
for (int i = 0; i < INIT_POOL_SIZE; i++) {
|
||||
current.next = new ViewRef();
|
||||
current = current.next;
|
||||
}
|
||||
|
||||
ViewRef finalCurrent = current;
|
||||
MAIN_EXECUTOR.execute(() -> addToPool(start, finalCurrent));
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches the ViewCapture to the provided window and returns a handle to detach the listener
|
||||
*/
|
||||
public SafeCloseable startCapture(Window window) {
|
||||
String title = window.getAttributes().getTitle().toString();
|
||||
String name = TextUtils.isEmpty(title) ? window.toString() : title;
|
||||
return startCapture(window.getDecorView(), name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Attaches the ViewCapture to the provided window and returns a handle to detach the listener
|
||||
*/
|
||||
public SafeCloseable startCapture(View view, String name) {
|
||||
if (!FeatureFlags.CONTINUOUS_VIEW_TREE_CAPTURE.get()) {
|
||||
return () -> { };
|
||||
}
|
||||
|
||||
WindowListener listener = new WindowListener(view, name);
|
||||
mExecutor.execute(() -> MAIN_EXECUTOR.execute(listener::attachToRoot));
|
||||
mListeners.add(listener);
|
||||
return () -> {
|
||||
mListeners.remove(listener);
|
||||
listener.destroy();
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Dumps all the active view captures
|
||||
*/
|
||||
public void dump(PrintWriter writer, FileDescriptor out) {
|
||||
if (!FeatureFlags.CONTINUOUS_VIEW_TREE_CAPTURE.get()) {
|
||||
return;
|
||||
}
|
||||
ViewIdProvider idProvider = new ViewIdProvider(mContext.getResources());
|
||||
|
||||
// Collect all the tasks first so that all the tasks are posted on the executor
|
||||
List<Pair<String, FutureTask<ExportedData>>> tasks = mListeners.stream()
|
||||
.map(l -> {
|
||||
FutureTask<ExportedData> task =
|
||||
new FutureTask<ExportedData>(() -> l.dumpToProto(idProvider));
|
||||
mExecutor.execute(task);
|
||||
return Pair.create(l.name, task);
|
||||
})
|
||||
.collect(toList());
|
||||
|
||||
tasks.forEach(pair -> {
|
||||
writer.println();
|
||||
writer.println(" ContinuousViewCapture:");
|
||||
writer.println(" window " + pair.first + ":");
|
||||
writer.println(" pkg:" + mContext.getPackageName());
|
||||
writer.print(" data:");
|
||||
writer.flush();
|
||||
try (OutputStream os = new FileOutputStream(out)) {
|
||||
ExportedData data = pair.second.get();
|
||||
OutputStream encodedOS = new GZIPOutputStream(new Base64OutputStream(os,
|
||||
Base64.NO_CLOSE | Base64.NO_PADDING | Base64.NO_WRAP));
|
||||
data.writeTo(encodedOS);
|
||||
encodedOS.close();
|
||||
os.flush();
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Error capturing proto", e);
|
||||
}
|
||||
writer.println();
|
||||
writer.println("--end--");
|
||||
});
|
||||
}
|
||||
|
||||
private class WindowListener implements OnDrawListener {
|
||||
|
||||
private final View mRoot;
|
||||
public final String name;
|
||||
|
||||
private final ViewRef mViewRef = new ViewRef();
|
||||
|
||||
private int mFrameIndexBg = -1;
|
||||
private boolean mIsFirstFrame = true;
|
||||
private final long[] mFrameTimesBg = new long[MEMORY_SIZE];
|
||||
private final ViewPropertyRef[] mNodesBg = new ViewPropertyRef[MEMORY_SIZE];
|
||||
|
||||
private boolean mDestroyed = false;
|
||||
private final Consumer<ViewRef> mCaptureCallback = this::captureViewPropertiesBg;
|
||||
|
||||
WindowListener(View view, String name) {
|
||||
mRoot = view;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDraw() {
|
||||
Trace.beginSection("view_capture");
|
||||
captureViewTree(mRoot, mViewRef);
|
||||
ViewRef captured = mViewRef.next;
|
||||
if (captured != null) {
|
||||
captured.callback = mCaptureCallback;
|
||||
captured.creationTime = SystemClock.uptimeMillis();
|
||||
mExecutor.execute(captured);
|
||||
}
|
||||
mIsFirstFrame = false;
|
||||
Trace.endSection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Captures the View property on the background thread, and transfer all the ViewRef objects
|
||||
* back to the pool
|
||||
*/
|
||||
@WorkerThread
|
||||
private void captureViewPropertiesBg(ViewRef viewRefStart) {
|
||||
long time = viewRefStart.creationTime;
|
||||
mFrameIndexBg++;
|
||||
if (mFrameIndexBg >= MEMORY_SIZE) {
|
||||
mFrameIndexBg = 0;
|
||||
}
|
||||
mFrameTimesBg[mFrameIndexBg] = time;
|
||||
|
||||
ViewPropertyRef recycle = mNodesBg[mFrameIndexBg];
|
||||
|
||||
ViewPropertyRef resultStart = null;
|
||||
ViewPropertyRef resultEnd = null;
|
||||
|
||||
ViewRef viewRefEnd = viewRefStart;
|
||||
while (viewRefEnd != null) {
|
||||
ViewPropertyRef propertyRef = recycle;
|
||||
if (propertyRef == null) {
|
||||
propertyRef = new ViewPropertyRef();
|
||||
} else {
|
||||
recycle = recycle.next;
|
||||
propertyRef.next = null;
|
||||
}
|
||||
|
||||
ViewPropertyRef copy = null;
|
||||
if (viewRefEnd.childCount < 0) {
|
||||
copy = findInLastFrame(viewRefEnd.view.hashCode());
|
||||
viewRefEnd.childCount = (copy != null) ? copy.childCount : 0;
|
||||
}
|
||||
viewRefEnd.transferTo(propertyRef);
|
||||
|
||||
if (resultStart == null) {
|
||||
resultStart = propertyRef;
|
||||
resultEnd = resultStart;
|
||||
} else {
|
||||
resultEnd.next = propertyRef;
|
||||
resultEnd = resultEnd.next;
|
||||
}
|
||||
|
||||
if (copy != null) {
|
||||
int pending = copy.childCount;
|
||||
while (pending > 0) {
|
||||
copy = copy.next;
|
||||
pending = pending - 1 + copy.childCount;
|
||||
|
||||
propertyRef = recycle;
|
||||
if (propertyRef == null) {
|
||||
propertyRef = new ViewPropertyRef();
|
||||
} else {
|
||||
recycle = recycle.next;
|
||||
propertyRef.next = null;
|
||||
}
|
||||
|
||||
copy.transferTo(propertyRef);
|
||||
|
||||
resultEnd.next = propertyRef;
|
||||
resultEnd = resultEnd.next;
|
||||
}
|
||||
}
|
||||
|
||||
if (viewRefEnd.next == null) {
|
||||
// The compiler will complain about using a non-final variable from
|
||||
// an outer class in a lambda if we pass in viewRefEnd directly.
|
||||
final ViewRef finalViewRefEnd = viewRefEnd;
|
||||
MAIN_EXECUTOR.execute(() -> addToPool(viewRefStart, finalViewRefEnd));
|
||||
break;
|
||||
}
|
||||
viewRefEnd = viewRefEnd.next;
|
||||
}
|
||||
mNodesBg[mFrameIndexBg] = resultStart;
|
||||
}
|
||||
|
||||
private ViewPropertyRef findInLastFrame(int hashCode) {
|
||||
int lastFrameIndex = (mFrameIndexBg == 0) ? MEMORY_SIZE - 1 : mFrameIndexBg - 1;
|
||||
ViewPropertyRef viewPropertyRef = mNodesBg[lastFrameIndex];
|
||||
while (viewPropertyRef != null && viewPropertyRef.hashCode != hashCode) {
|
||||
viewPropertyRef = viewPropertyRef.next;
|
||||
}
|
||||
return viewPropertyRef;
|
||||
}
|
||||
|
||||
void attachToRoot() {
|
||||
if (mRoot.isAttachedToWindow()) {
|
||||
mRoot.getViewTreeObserver().addOnDrawListener(this);
|
||||
} else {
|
||||
mRoot.addOnAttachStateChangeListener(new OnAttachStateChangeListener() {
|
||||
@Override
|
||||
public void onViewAttachedToWindow(View v) {
|
||||
if (!mDestroyed) {
|
||||
mRoot.getViewTreeObserver().addOnDrawListener(WindowListener.this);
|
||||
}
|
||||
mRoot.removeOnAttachStateChangeListener(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewDetachedFromWindow(View v) { }
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
void destroy() {
|
||||
mRoot.getViewTreeObserver().removeOnDrawListener(this);
|
||||
mDestroyed = true;
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
private ExportedData dumpToProto(ViewIdProvider idProvider) {
|
||||
ExportedData.Builder dataBuilder = ExportedData.newBuilder();
|
||||
ArrayList<Class> classList = new ArrayList<>();
|
||||
|
||||
int size = (mNodesBg[MEMORY_SIZE - 1] == null) ? mFrameIndexBg + 1 : MEMORY_SIZE;
|
||||
for (int i = size - 1; i >= 0; i--) {
|
||||
int index = (MEMORY_SIZE + mFrameIndexBg - i) % MEMORY_SIZE;
|
||||
ViewNode.Builder nodeBuilder = ViewNode.newBuilder();
|
||||
mNodesBg[index].toProto(idProvider, classList, nodeBuilder);
|
||||
dataBuilder.addFrameData(FrameData.newBuilder()
|
||||
.setNode(nodeBuilder)
|
||||
.setTimestamp(mFrameTimesBg[index]));
|
||||
}
|
||||
return dataBuilder
|
||||
.addAllClassname(classList.stream().map(Class::getName).collect(toList()))
|
||||
.build();
|
||||
}
|
||||
|
||||
private ViewRef captureViewTree(View view, ViewRef start) {
|
||||
ViewRef ref;
|
||||
if (mPool != null) {
|
||||
ref = mPool;
|
||||
mPool = mPool.next;
|
||||
ref.next = null;
|
||||
} else {
|
||||
ref = new ViewRef();
|
||||
}
|
||||
ref.view = view;
|
||||
start.next = ref;
|
||||
if (view instanceof ViewGroup) {
|
||||
ViewGroup parent = (ViewGroup) view;
|
||||
// If a view has not changed since the last frame, we will copy
|
||||
// its children from the last processed frame's data.
|
||||
if ((view.mPrivateFlags & (PFLAG_INVALIDATED | PFLAG_DIRTY_MASK)) == 0
|
||||
&& !mIsFirstFrame) {
|
||||
// A negative child count is the signal to copy this view from the last frame.
|
||||
ref.childCount = -parent.getChildCount();
|
||||
return ref;
|
||||
}
|
||||
ViewRef result = ref;
|
||||
int childCount = ref.childCount = parent.getChildCount();
|
||||
for (int i = 0; i < childCount; i++) {
|
||||
result = captureViewTree(parent.getChildAt(i), result);
|
||||
}
|
||||
return result;
|
||||
} else {
|
||||
ref.childCount = 0;
|
||||
return ref;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static class ViewPropertyRef {
|
||||
// We store reference in memory to avoid generating and storing too many strings
|
||||
public Class clazz;
|
||||
public int hashCode;
|
||||
public int childCount = 0;
|
||||
|
||||
public int id;
|
||||
public int left, top, right, bottom;
|
||||
public int scrollX, scrollY;
|
||||
|
||||
public float translateX, translateY;
|
||||
public float scaleX, scaleY;
|
||||
public float alpha;
|
||||
public float elevation;
|
||||
|
||||
public int visibility;
|
||||
public boolean willNotDraw;
|
||||
public boolean clipChildren;
|
||||
|
||||
public ViewPropertyRef next;
|
||||
|
||||
public void transferTo(ViewPropertyRef out) {
|
||||
out.clazz = this.clazz;
|
||||
out.hashCode = this.hashCode;
|
||||
out.childCount = this.childCount;
|
||||
out.id = this.id;
|
||||
out.left = this.left;
|
||||
out.top = this.top;
|
||||
out.right = this.right;
|
||||
out.bottom = this.bottom;
|
||||
out.scrollX = this.scrollX;
|
||||
out.scrollY = this.scrollY;
|
||||
out.scaleX = this.scaleX;
|
||||
out.scaleY = this.scaleY;
|
||||
out.translateX = this.translateX;
|
||||
out.translateY = this.translateY;
|
||||
out.alpha = this.alpha;
|
||||
out.visibility = this.visibility;
|
||||
out.willNotDraw = this.willNotDraw;
|
||||
out.clipChildren = this.clipChildren;
|
||||
out.elevation = this.elevation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts the data to the proto representation and returns the next property ref
|
||||
* at the end of the iteration.
|
||||
* @return
|
||||
*/
|
||||
public ViewPropertyRef toProto(ViewIdProvider idProvider, ArrayList<Class> classList,
|
||||
ViewNode.Builder outBuilder) {
|
||||
int classnameIndex = classList.indexOf(clazz);
|
||||
if (classnameIndex < 0) {
|
||||
classnameIndex = classList.size();
|
||||
classList.add(clazz);
|
||||
}
|
||||
outBuilder
|
||||
.setClassnameIndex(classnameIndex)
|
||||
.setHashcode(hashCode)
|
||||
.setId(idProvider.getName(id))
|
||||
.setLeft(left)
|
||||
.setTop(top)
|
||||
.setWidth(right - left)
|
||||
.setHeight(bottom - top)
|
||||
.setTranslationX(translateX)
|
||||
.setTranslationY(translateY)
|
||||
.setScaleX(scaleX)
|
||||
.setScaleY(scaleY)
|
||||
.setAlpha(alpha)
|
||||
.setVisibility(visibility)
|
||||
.setWillNotDraw(willNotDraw)
|
||||
.setElevation(elevation)
|
||||
.setClipChildren(clipChildren);
|
||||
|
||||
ViewPropertyRef result = next;
|
||||
for (int i = 0; (i < childCount) && (result != null); i++) {
|
||||
ViewNode.Builder childBuilder = ViewNode.newBuilder();
|
||||
result = result.toProto(idProvider, classList, childBuilder);
|
||||
outBuilder.addChildren(childBuilder);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
private static class ViewRef implements Runnable {
|
||||
public View view;
|
||||
public int childCount = 0;
|
||||
public ViewRef next;
|
||||
|
||||
public Consumer<ViewRef> callback = null;
|
||||
public long creationTime = 0;
|
||||
|
||||
public void transferTo(ViewPropertyRef out) {
|
||||
out.childCount = this.childCount;
|
||||
|
||||
View view = this.view;
|
||||
this.view = null;
|
||||
|
||||
out.clazz = view.getClass();
|
||||
out.hashCode = view.hashCode();
|
||||
out.id = view.getId();
|
||||
out.left = view.getLeft();
|
||||
out.top = view.getTop();
|
||||
out.right = view.getRight();
|
||||
out.bottom = view.getBottom();
|
||||
out.scrollX = view.getScrollX();
|
||||
out.scrollY = view.getScrollY();
|
||||
|
||||
out.translateX = view.getTranslationX();
|
||||
out.translateY = view.getTranslationY();
|
||||
out.scaleX = view.getScaleX();
|
||||
out.scaleY = view.getScaleY();
|
||||
out.alpha = view.getAlpha();
|
||||
out.elevation = view.getElevation();
|
||||
|
||||
out.visibility = view.getVisibility();
|
||||
out.willNotDraw = view.willNotDraw();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Consumer<ViewRef> oldCallback = callback;
|
||||
callback = null;
|
||||
if (oldCallback != null) {
|
||||
oldCallback.accept(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final class ViewIdProvider {
|
||||
|
||||
private final SparseArray<String> mNames = new SparseArray<>();
|
||||
private final Resources mRes;
|
||||
|
||||
ViewIdProvider(Resources res) {
|
||||
mRes = res;
|
||||
}
|
||||
|
||||
String getName(int id) {
|
||||
String name = mNames.get(id);
|
||||
if (name == null) {
|
||||
if (id >= 0) {
|
||||
try {
|
||||
name = mRes.getResourceTypeName(id) + '/' + mRes.getResourceEntryName(id);
|
||||
} catch (Resources.NotFoundException e) {
|
||||
name = "id/" + "0x" + Integer.toHexString(id).toUpperCase();
|
||||
}
|
||||
} else {
|
||||
name = "NO_ID";
|
||||
}
|
||||
mNames.put(id, name);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -249,11 +249,11 @@ public class AppWidgetResizeFrame extends AbstractFloatingView implements View.O
|
||||
/* widgetHandler= */ null,
|
||||
(ItemInfo) mWidgetView.getTag()));
|
||||
mLauncher
|
||||
.getAppWidgetHost()
|
||||
.startConfigActivity(
|
||||
mLauncher,
|
||||
mWidgetView.getAppWidgetId(),
|
||||
Launcher.REQUEST_RECONFIGURE_APPWIDGET);
|
||||
.getAppWidgetHolder()
|
||||
.startConfigActivity(
|
||||
mLauncher,
|
||||
mWidgetView.getAppWidgetId(),
|
||||
Launcher.REQUEST_RECONFIGURE_APPWIDGET);
|
||||
});
|
||||
if (!hasSeenReconfigurableWidgetEducationTip()) {
|
||||
post(() -> {
|
||||
|
||||
@@ -131,6 +131,7 @@ import com.android.launcher3.allapps.AllAppsRecyclerView;
|
||||
import com.android.launcher3.allapps.AllAppsStore;
|
||||
import com.android.launcher3.allapps.AllAppsTransitionController;
|
||||
import com.android.launcher3.allapps.BaseAllAppsContainerView;
|
||||
import com.android.launcher3.allapps.BaseSearchConfig;
|
||||
import com.android.launcher3.allapps.DiscoveryBounce;
|
||||
import com.android.launcher3.anim.PropertyListBuilder;
|
||||
import com.android.launcher3.compat.AccessibilityManagerCompat;
|
||||
@@ -204,7 +205,6 @@ import com.android.launcher3.views.FloatingIconView;
|
||||
import com.android.launcher3.views.FloatingSurfaceView;
|
||||
import com.android.launcher3.views.OptionsPopupView;
|
||||
import com.android.launcher3.views.ScrimView;
|
||||
import com.android.launcher3.widget.LauncherAppWidgetHost;
|
||||
import com.android.launcher3.widget.LauncherAppWidgetHostView;
|
||||
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
|
||||
import com.android.launcher3.widget.PendingAddShortcutInfo;
|
||||
@@ -316,7 +316,7 @@ public class Launcher extends StatefulActivity<LauncherState>
|
||||
DragLayer mDragLayer;
|
||||
|
||||
private WidgetManagerHelper mAppWidgetManager;
|
||||
private LauncherAppWidgetHost mAppWidgetHost;
|
||||
private LauncherWidgetHolder mAppWidgetHolder;
|
||||
|
||||
private final int[] mTmpAddItemCellCoordinates = new int[2];
|
||||
|
||||
@@ -395,6 +395,7 @@ public class Launcher extends StatefulActivity<LauncherState>
|
||||
private LauncherState mPrevLauncherState;
|
||||
|
||||
private StringCache mStringCache;
|
||||
private BaseSearchConfig mBaseSearchConfig;
|
||||
|
||||
@Override
|
||||
@TargetApi(Build.VERSION_CODES.S)
|
||||
@@ -480,8 +481,8 @@ public class Launcher extends StatefulActivity<LauncherState>
|
||||
mOnboardingPrefs = createOnboardingPrefs(mSharedPrefs);
|
||||
|
||||
mAppWidgetManager = new WidgetManagerHelper(this);
|
||||
mAppWidgetHost = createAppWidgetHost();
|
||||
mAppWidgetHost.startListening();
|
||||
mAppWidgetHolder = createAppWidgetHolder();
|
||||
mAppWidgetHolder.startListening();
|
||||
|
||||
setupViews();
|
||||
crossFadeWithPreviousAppearance();
|
||||
@@ -545,6 +546,9 @@ public class Launcher extends StatefulActivity<LauncherState>
|
||||
getWindow().setSoftInputMode(LayoutParams.SOFT_INPUT_ADJUST_NOTHING);
|
||||
}
|
||||
setTitle(R.string.home_screen);
|
||||
|
||||
// TODO: move the SearchConfig to SearchState when new LauncherState is created.
|
||||
mBaseSearchConfig = new BaseSearchConfig();
|
||||
}
|
||||
|
||||
protected LauncherOverlayManager getDefaultOverlay() {
|
||||
@@ -958,7 +962,7 @@ public class Launcher extends StatefulActivity<LauncherState>
|
||||
AppWidgetHostView boundWidget = null;
|
||||
if (resultCode == RESULT_OK) {
|
||||
animationType = Workspace.COMPLETE_TWO_STAGE_WIDGET_DROP_ANIMATION;
|
||||
final AppWidgetHostView layout = mAppWidgetHost.createView(this, appWidgetId,
|
||||
final AppWidgetHostView layout = mAppWidgetHolder.createView(this, appWidgetId,
|
||||
requestArgs.getWidgetHandler().getProviderInfo(this));
|
||||
boundWidget = layout;
|
||||
onCompleteRunnable = new Runnable() {
|
||||
@@ -969,7 +973,7 @@ public class Launcher extends StatefulActivity<LauncherState>
|
||||
}
|
||||
};
|
||||
} else if (resultCode == RESULT_CANCELED) {
|
||||
mAppWidgetHost.deleteAppWidgetId(appWidgetId);
|
||||
mAppWidgetHolder.deleteAppWidgetId(appWidgetId);
|
||||
animationType = Workspace.CANCEL_TWO_STAGE_WIDGET_DROP_ANIMATION;
|
||||
}
|
||||
if (mDragLayer.getAnimatedView() != null) {
|
||||
@@ -992,7 +996,7 @@ public class Launcher extends StatefulActivity<LauncherState>
|
||||
}
|
||||
hideKeyboard();
|
||||
logStopAndResume(false /* isResume */);
|
||||
mAppWidgetHost.setActivityStarted(false);
|
||||
mAppWidgetHolder.setActivityStarted(false);
|
||||
NotificationListener.removeNotificationsChangedListener(getPopupDataProvider());
|
||||
}
|
||||
|
||||
@@ -1005,7 +1009,7 @@ public class Launcher extends StatefulActivity<LauncherState>
|
||||
mOverlayManager.onActivityStarted(this);
|
||||
}
|
||||
|
||||
mAppWidgetHost.setActivityStarted(true);
|
||||
mAppWidgetHolder.setActivityStarted(true);
|
||||
TraceHelper.INSTANCE.endSection(traceToken);
|
||||
}
|
||||
|
||||
@@ -1025,7 +1029,7 @@ public class Launcher extends StatefulActivity<LauncherState>
|
||||
NotificationListener.addNotificationsChangedListener(mPopupDataProvider);
|
||||
|
||||
DiscoveryBounce.showForHomeIfNeeded(this);
|
||||
mAppWidgetHost.setActivityResumed(true);
|
||||
mAppWidgetHolder.setActivityResumed(true);
|
||||
}
|
||||
|
||||
private void logStopAndResume(boolean isResume) {
|
||||
@@ -1140,7 +1144,7 @@ public class Launcher extends StatefulActivity<LauncherState>
|
||||
@Override
|
||||
public void onStateSetEnd(LauncherState state) {
|
||||
super.onStateSetEnd(state);
|
||||
getAppWidgetHost().setStateIsNormal(state == LauncherState.NORMAL);
|
||||
getAppWidgetHolder().setStateIsNormal(state == LauncherState.NORMAL);
|
||||
getWorkspace().setClipChildren(!state.hasFlag(FLAG_MULTI_PAGE));
|
||||
|
||||
finishAutoCancelActionMode();
|
||||
@@ -1205,7 +1209,7 @@ public class Launcher extends StatefulActivity<LauncherState>
|
||||
if (!mDeferOverlayCallbacks) {
|
||||
mOverlayManager.onActivityPaused(this);
|
||||
}
|
||||
mAppWidgetHost.setActivityResumed(false);
|
||||
mAppWidgetHolder.setActivityResumed(false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1430,7 +1434,7 @@ public class Launcher extends StatefulActivity<LauncherState>
|
||||
|
||||
if (hostView == null) {
|
||||
// Perform actual inflation because we're live
|
||||
hostView = mAppWidgetHost.createView(this, appWidgetId, appWidgetInfo);
|
||||
hostView = mAppWidgetHolder.createView(this, appWidgetId, appWidgetInfo);
|
||||
}
|
||||
|
||||
LauncherAppWidgetInfo launcherInfo;
|
||||
@@ -1561,12 +1565,12 @@ public class Launcher extends StatefulActivity<LauncherState>
|
||||
return mScrimView;
|
||||
}
|
||||
|
||||
public LauncherAppWidgetHost getAppWidgetHost() {
|
||||
return mAppWidgetHost;
|
||||
public LauncherWidgetHolder getAppWidgetHolder() {
|
||||
return mAppWidgetHolder;
|
||||
}
|
||||
|
||||
protected LauncherAppWidgetHost createAppWidgetHost() {
|
||||
return new LauncherAppWidgetHost(this,
|
||||
protected LauncherWidgetHolder createAppWidgetHolder() {
|
||||
return new LauncherWidgetHolder(this,
|
||||
appWidgetId -> getWorkspace().removeWidget(appWidgetId));
|
||||
}
|
||||
|
||||
@@ -1592,12 +1596,8 @@ public class Launcher extends StatefulActivity<LauncherState>
|
||||
return mOldConfig.orientation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether keyboard sync is enabled for transitions between Home and All Apps.
|
||||
* TODO(b/251387263): move this method inside an All Apps specific config class.
|
||||
*/
|
||||
public boolean isKeyboardSyncEnabled() {
|
||||
return false;
|
||||
public BaseSearchConfig getSearchConfig() {
|
||||
return mBaseSearchConfig;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1732,7 +1732,7 @@ public class Launcher extends StatefulActivity<LauncherState>
|
||||
mRotationHelper.destroy();
|
||||
|
||||
try {
|
||||
mAppWidgetHost.stopListening();
|
||||
mAppWidgetHolder.stopListening();
|
||||
} catch (NullPointerException ex) {
|
||||
Log.w(TAG, "problem while stopping AppWidgetHost during Launcher destruction", ex);
|
||||
}
|
||||
@@ -1907,7 +1907,7 @@ public class Launcher extends StatefulActivity<LauncherState>
|
||||
appWidgetId = CustomWidgetManager.INSTANCE.get(this).getWidgetIdForCustomProvider(
|
||||
info.componentName);
|
||||
} else {
|
||||
appWidgetId = getAppWidgetHost().allocateAppWidgetId();
|
||||
appWidgetId = getAppWidgetHolder().allocateAppWidgetId();
|
||||
}
|
||||
Bundle options = info.bindOptions;
|
||||
|
||||
@@ -2021,7 +2021,7 @@ public class Launcher extends StatefulActivity<LauncherState>
|
||||
final LauncherAppWidgetInfo widgetInfo = (LauncherAppWidgetInfo) itemInfo;
|
||||
mWorkspace.removeWorkspaceItem(v);
|
||||
if (deleteFromDb) {
|
||||
getModelWriter().deleteWidgetInfo(widgetInfo, getAppWidgetHost(), reason);
|
||||
getModelWriter().deleteWidgetInfo(widgetInfo, getAppWidgetHolder(), reason);
|
||||
}
|
||||
} else {
|
||||
return false;
|
||||
@@ -2279,7 +2279,7 @@ public class Launcher extends StatefulActivity<LauncherState>
|
||||
|
||||
mWorkspace.clearDropTargets();
|
||||
mWorkspace.removeAllWorkspaceScreens();
|
||||
mAppWidgetHost.clearViews();
|
||||
mAppWidgetHolder.clearViews();
|
||||
|
||||
if (mHotseat != null) {
|
||||
mHotseat.resetLayout(getDeviceProfile().isVerticalBarLayout());
|
||||
@@ -2586,7 +2586,7 @@ public class Launcher extends StatefulActivity<LauncherState>
|
||||
if (item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)) {
|
||||
if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_ALLOCATED)) {
|
||||
// Id has not been allocated yet. Allocate a new id.
|
||||
item.appWidgetId = mAppWidgetHost.allocateAppWidgetId();
|
||||
item.appWidgetId = mAppWidgetHolder.allocateAppWidgetId();
|
||||
item.restoreStatus |= LauncherAppWidgetInfo.FLAG_ID_ALLOCATED;
|
||||
|
||||
// Also try to bind the widget. If the bind fails, the user will be shown
|
||||
@@ -2648,18 +2648,18 @@ public class Launcher extends StatefulActivity<LauncherState>
|
||||
// Verify that we own the widget
|
||||
if (appWidgetInfo == null) {
|
||||
FileLog.e(TAG, "Removing invalid widget: id=" + item.appWidgetId);
|
||||
getModelWriter().deleteWidgetInfo(item, getAppWidgetHost(), removalReason);
|
||||
getModelWriter().deleteWidgetInfo(item, getAppWidgetHolder(), removalReason);
|
||||
return null;
|
||||
}
|
||||
|
||||
item.minSpanX = appWidgetInfo.minSpanX;
|
||||
item.minSpanY = appWidgetInfo.minSpanY;
|
||||
view = mAppWidgetHost.createView(this, item.appWidgetId, appWidgetInfo);
|
||||
view = mAppWidgetHolder.createView(this, item.appWidgetId, appWidgetInfo);
|
||||
} else if (!item.hasRestoreFlag(LauncherAppWidgetInfo.FLAG_ID_NOT_VALID)
|
||||
&& appWidgetInfo != null) {
|
||||
mAppWidgetHost.addPendingView(item.appWidgetId,
|
||||
mAppWidgetHolder.addPendingView(item.appWidgetId,
|
||||
new PendingAppWidgetHostView(this, item, mIconCache, false));
|
||||
view = mAppWidgetHost.createView(this, item.appWidgetId, appWidgetInfo);
|
||||
view = mAppWidgetHolder.createView(this, item.appWidgetId, appWidgetInfo);
|
||||
} else {
|
||||
view = new PendingAppWidgetHostView(this, item, mIconCache, false);
|
||||
}
|
||||
@@ -3017,7 +3017,8 @@ public class Launcher extends StatefulActivity<LauncherState>
|
||||
writer.println(prefix + "\tmPendingRequestArgs=" + mPendingRequestArgs
|
||||
+ " mPendingActivityResult=" + mPendingActivityResult);
|
||||
writer.println(prefix + "\tmRotationHelper: " + mRotationHelper);
|
||||
writer.println(prefix + "\tmAppWidgetHost.isListening: " + mAppWidgetHost.isListening());
|
||||
writer.println(prefix + "\tmAppWidgetHolder.isListening: "
|
||||
+ mAppWidgetHolder.isListening());
|
||||
|
||||
// Extra logging for general debugging
|
||||
mDragLayer.dump(prefix, writer);
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
/**
|
||||
* Copyright (C) 2022 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;
|
||||
|
||||
import android.appwidget.AppWidgetHostView;
|
||||
import android.appwidget.AppWidgetProviderInfo;
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.launcher3.uioverrides.ApiWrapper;
|
||||
import com.android.launcher3.widget.LauncherAppWidgetHost;
|
||||
import com.android.launcher3.widget.LauncherAppWidgetProviderInfo;
|
||||
import com.android.launcher3.widget.PendingAppWidgetHostView;
|
||||
|
||||
import java.util.function.IntConsumer;
|
||||
|
||||
/**
|
||||
* A wrapper for LauncherAppWidgetHost. This class is created so the AppWidgetHost could run in
|
||||
* background.
|
||||
*/
|
||||
public class LauncherWidgetHolder {
|
||||
@NonNull
|
||||
private final LauncherAppWidgetHost mWidgetHost;
|
||||
|
||||
public LauncherWidgetHolder(@NonNull Context context) {
|
||||
this(context, null);
|
||||
}
|
||||
|
||||
public LauncherWidgetHolder(@NonNull Context context,
|
||||
@Nullable IntConsumer appWidgetRemovedCallback) {
|
||||
mWidgetHost = new LauncherAppWidgetHost(context, appWidgetRemovedCallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts listening to the widget updates from the server side
|
||||
*/
|
||||
public void startListening() {
|
||||
mWidgetHost.startListening();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the STARTED state of the widget host
|
||||
* @param isStarted True if setting the host as started, false otherwise
|
||||
*/
|
||||
public void setActivityStarted(boolean isStarted) {
|
||||
mWidgetHost.setActivityStarted(isStarted);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the RESUMED state of the widget host
|
||||
* @param isResumed True if setting the host as resumed, false otherwise
|
||||
*/
|
||||
public void setActivityResumed(boolean isResumed) {
|
||||
mWidgetHost.setActivityResumed(isResumed);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the NORMAL state of the widget host
|
||||
* @param isNormal True if setting the host to be in normal state, false otherwise
|
||||
*/
|
||||
public void setStateIsNormal(boolean isNormal) {
|
||||
mWidgetHost.setStateIsNormal(isNormal);
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the specified app widget from the host
|
||||
* @param appWidgetId The ID of the app widget to be deleted
|
||||
*/
|
||||
public void deleteAppWidgetId(int appWidgetId) {
|
||||
mWidgetHost.deleteAppWidgetId(appWidgetId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add the pending view to the host for complete configuration in further steps
|
||||
* @param appWidgetId The ID of the specified app widget
|
||||
* @param view The {@link PendingAppWidgetHostView} of the app widget
|
||||
*/
|
||||
public void addPendingView(int appWidgetId, @NonNull PendingAppWidgetHostView view) {
|
||||
mWidgetHost.addPendingView(appWidgetId, view);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return True if the host is listening to the widget updates, false otherwise
|
||||
*/
|
||||
public boolean isListening() {
|
||||
return mWidgetHost.isListening();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The allocated app widget id if allocation is successful, returns -1 otherwise
|
||||
*/
|
||||
public int allocateAppWidgetId() {
|
||||
return mWidgetHost.allocateAppWidgetId();
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a listener that is triggered when the providers of the widgets are changed
|
||||
* @param listener The listener that notifies when the providers changed
|
||||
*/
|
||||
public void addProviderChangeListener(
|
||||
@NonNull LauncherAppWidgetHost.ProviderChangedListener listener) {
|
||||
mWidgetHost.addProviderChangeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified listener from the host
|
||||
* @param listener The listener that is to be removed from the host
|
||||
*/
|
||||
public void removeProviderChangeListener(
|
||||
LauncherAppWidgetHost.ProviderChangedListener listener) {
|
||||
mWidgetHost.removeProviderChangeListener(listener);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the configuration activity for the widget
|
||||
* @param activity The activity in which to start the configuration page
|
||||
* @param widgetId The ID of the widget
|
||||
* @param requestCode The request code
|
||||
*/
|
||||
public void startConfigActivity(@NonNull BaseDraggingActivity activity, int widgetId,
|
||||
int requestCode) {
|
||||
mWidgetHost.startConfigActivity(activity, widgetId, requestCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Starts the binding flow for the widget
|
||||
* @param activity The activity for which to bind the widget
|
||||
* @param appWidgetId The ID of the widget
|
||||
* @param info The {@link AppWidgetProviderInfo} of the widget
|
||||
* @param requestCode The request code
|
||||
*/
|
||||
public void startBindFlow(@NonNull BaseActivity activity,
|
||||
int appWidgetId, @NonNull AppWidgetProviderInfo info, int requestCode) {
|
||||
mWidgetHost.startBindFlow(activity, appWidgetId, info, requestCode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stop the host from listening to the widget updates
|
||||
*/
|
||||
public void stopListening() {
|
||||
mWidgetHost.stopListening();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a view for the specified app widget
|
||||
* @param context The activity context for which the view is created
|
||||
* @param appWidgetId The ID of the widget
|
||||
* @param info The {@link LauncherAppWidgetProviderInfo} of the widget
|
||||
* @return A view for the widget
|
||||
*/
|
||||
@NonNull
|
||||
public AppWidgetHostView createView(@NonNull Context context, int appWidgetId,
|
||||
@NonNull LauncherAppWidgetProviderInfo info) {
|
||||
return mWidgetHost.createView(context, appWidgetId, info);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the interaction handler for the widget host
|
||||
* @param handler The interaction handler
|
||||
*/
|
||||
public void setInteractionHandler(
|
||||
@Nullable LauncherAppWidgetHost.LauncherWidgetInteractionHandler handler) {
|
||||
ApiWrapper.setHostInteractionHandler(mWidgetHost, handler);
|
||||
}
|
||||
|
||||
/**
|
||||
* Clears all the views from the host
|
||||
*/
|
||||
public void clearViews() {
|
||||
mWidgetHost.clearViews();
|
||||
}
|
||||
}
|
||||
@@ -288,7 +288,7 @@ public class SecondaryDropTarget extends ButtonDropTarget implements OnAlarmList
|
||||
if (widgetId != INVALID_APPWIDGET_ID) {
|
||||
mLauncher.setWaitingForResult(
|
||||
PendingRequestArgs.forWidgetInfo(widgetId, null, info));
|
||||
mLauncher.getAppWidgetHost().startConfigActivity(mLauncher, widgetId,
|
||||
mLauncher.getAppWidgetHolder().startConfigActivity(mLauncher, widgetId,
|
||||
REQUEST_RECONFIGURE_APPWIDGET);
|
||||
}
|
||||
return null;
|
||||
|
||||
@@ -109,7 +109,6 @@ import com.android.launcher3.util.PackageUserKey;
|
||||
import com.android.launcher3.util.RunnableList;
|
||||
import com.android.launcher3.util.Thunk;
|
||||
import com.android.launcher3.util.WallpaperOffsetInterpolator;
|
||||
import com.android.launcher3.widget.LauncherAppWidgetHost;
|
||||
import com.android.launcher3.widget.LauncherAppWidgetHost.ProviderChangedListener;
|
||||
import com.android.launcher3.widget.LauncherAppWidgetHostView;
|
||||
import com.android.launcher3.widget.NavigableAppWidgetHostView;
|
||||
@@ -3391,7 +3390,7 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T>
|
||||
public void widgetsRestored(final ArrayList<LauncherAppWidgetInfo> changedInfo) {
|
||||
if (!changedInfo.isEmpty()) {
|
||||
DeferredWidgetRefresh widgetRefresh = new DeferredWidgetRefresh(changedInfo,
|
||||
mLauncher.getAppWidgetHost());
|
||||
mLauncher.getAppWidgetHolder());
|
||||
|
||||
LauncherAppWidgetInfo item = changedInfo.get(0);
|
||||
final AppWidgetProviderInfo widgetInfo;
|
||||
@@ -3517,19 +3516,19 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T>
|
||||
*/
|
||||
private class DeferredWidgetRefresh implements Runnable, ProviderChangedListener {
|
||||
private final ArrayList<LauncherAppWidgetInfo> mInfos;
|
||||
private final LauncherAppWidgetHost mHost;
|
||||
private final LauncherWidgetHolder mWidgetHolder;
|
||||
private final Handler mHandler;
|
||||
|
||||
private boolean mRefreshPending;
|
||||
|
||||
DeferredWidgetRefresh(ArrayList<LauncherAppWidgetInfo> infos,
|
||||
LauncherAppWidgetHost host) {
|
||||
LauncherWidgetHolder holder) {
|
||||
mInfos = infos;
|
||||
mHost = host;
|
||||
mWidgetHolder = holder;
|
||||
mHandler = mLauncher.mHandler;
|
||||
mRefreshPending = true;
|
||||
|
||||
mHost.addProviderChangeListener(this);
|
||||
mWidgetHolder.addProviderChangeListener(this);
|
||||
// Force refresh after 10 seconds, if we don't get the provider changed event.
|
||||
// This could happen when the provider is no longer available in the app.
|
||||
Message msg = Message.obtain(mHandler, this);
|
||||
@@ -3539,7 +3538,7 @@ public class Workspace<T extends View & PageIndicator> extends PagedView<T>
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
mHost.removeProviderChangeListener(this);
|
||||
mWidgetHolder.removeProviderChangeListener(this);
|
||||
mHandler.removeCallbacks(this);
|
||||
|
||||
if (!mRefreshPending) {
|
||||
|
||||
@@ -76,6 +76,8 @@ public class AllAppsTransitionController
|
||||
}
|
||||
};
|
||||
|
||||
private static final float ALL_APPS_PULL_BACK_TRANSLATION_DEFAULT = 0f;
|
||||
|
||||
public static final FloatProperty<AllAppsTransitionController> ALL_APPS_PULL_BACK_TRANSLATION =
|
||||
new FloatProperty<AllAppsTransitionController>("allAppsPullBackTranslation") {
|
||||
|
||||
@@ -92,12 +94,18 @@ public class AllAppsTransitionController
|
||||
public void setValue(AllAppsTransitionController controller, float translation) {
|
||||
if (controller.mIsTablet) {
|
||||
controller.mAppsView.getActiveRecyclerView().setTranslationY(translation);
|
||||
controller.getAppsViewPullbackTranslationY().setValue(
|
||||
ALL_APPS_PULL_BACK_TRANSLATION_DEFAULT);
|
||||
} else {
|
||||
controller.getAppsViewPullbackTranslationY().setValue(translation);
|
||||
controller.mAppsView.getActiveRecyclerView().setTranslationY(
|
||||
ALL_APPS_PULL_BACK_TRANSLATION_DEFAULT);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
private static final float ALL_APPS_PULL_BACK_ALPHA_DEFAULT = 1f;
|
||||
|
||||
public static final FloatProperty<AllAppsTransitionController> ALL_APPS_PULL_BACK_ALPHA =
|
||||
new FloatProperty<AllAppsTransitionController>("allAppsPullBackAlpha") {
|
||||
|
||||
@@ -114,8 +122,12 @@ public class AllAppsTransitionController
|
||||
public void setValue(AllAppsTransitionController controller, float alpha) {
|
||||
if (controller.mIsTablet) {
|
||||
controller.mAppsView.getActiveRecyclerView().setAlpha(alpha);
|
||||
controller.getAppsViewPullbackAlpha().setValue(
|
||||
ALL_APPS_PULL_BACK_ALPHA_DEFAULT);
|
||||
} else {
|
||||
controller.getAppsViewPullbackAlpha().setValue(alpha);
|
||||
controller.mAppsView.getActiveRecyclerView().setAlpha(
|
||||
ALL_APPS_PULL_BACK_ALPHA_DEFAULT);
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -226,14 +238,14 @@ public class AllAppsTransitionController
|
||||
StateAnimationConfig config, PendingAnimation builder) {
|
||||
if (mLauncher.isInState(ALL_APPS) && !ALL_APPS.equals(toState)) {
|
||||
// For atomic animations, we close the keyboard immediately.
|
||||
if (!config.userControlled && !mLauncher.isKeyboardSyncEnabled()) {
|
||||
if (!config.userControlled && !mLauncher.getSearchConfig().isKeyboardSyncEnabled()) {
|
||||
mLauncher.getAppsView().getSearchUiManager().getEditText().hideKeyboard();
|
||||
}
|
||||
|
||||
builder.addEndListener(success -> {
|
||||
// Reset pull back progress and alpha after switching states.
|
||||
ALL_APPS_PULL_BACK_TRANSLATION.set(this, 0f);
|
||||
ALL_APPS_PULL_BACK_ALPHA.set(this, 1f);
|
||||
ALL_APPS_PULL_BACK_TRANSLATION.set(this, ALL_APPS_PULL_BACK_TRANSLATION_DEFAULT);
|
||||
ALL_APPS_PULL_BACK_ALPHA.set(this, ALL_APPS_PULL_BACK_ALPHA_DEFAULT);
|
||||
|
||||
// We only want to close the keyboard if the animation has completed successfully.
|
||||
// The reason is that with keyboard sync, if the user swipes down from All Apps with
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright (C) 2022 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;
|
||||
|
||||
/** Base config values for search. */
|
||||
public class BaseSearchConfig {
|
||||
public BaseSearchConfig() {}
|
||||
|
||||
/**
|
||||
* Returns whether to enable the synchronized keyboard transition between Home and All Apps.
|
||||
*/
|
||||
public boolean isKeyboardSyncEnabled() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,7 @@ import com.android.launcher3.LauncherProvider;
|
||||
import com.android.launcher3.LauncherSettings;
|
||||
import com.android.launcher3.LauncherSettings.Favorites;
|
||||
import com.android.launcher3.LauncherSettings.Settings;
|
||||
import com.android.launcher3.LauncherWidgetHolder;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.config.FeatureFlags;
|
||||
import com.android.launcher3.logging.FileLog;
|
||||
@@ -48,7 +49,6 @@ import com.android.launcher3.util.ContentWriter;
|
||||
import com.android.launcher3.util.Executors;
|
||||
import com.android.launcher3.util.ItemInfoMatcher;
|
||||
import com.android.launcher3.util.LooperExecutor;
|
||||
import com.android.launcher3.widget.LauncherAppWidgetHost;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
@@ -333,13 +333,13 @@ public class ModelWriter {
|
||||
/**
|
||||
* Deletes the widget info and the widget id.
|
||||
*/
|
||||
public void deleteWidgetInfo(final LauncherAppWidgetInfo info, LauncherAppWidgetHost host,
|
||||
public void deleteWidgetInfo(final LauncherAppWidgetInfo info, LauncherWidgetHolder holder,
|
||||
@Nullable final String reason) {
|
||||
notifyDelete(Collections.singleton(info));
|
||||
if (host != null && !info.isCustomWidget() && info.isWidgetIdAllocated()) {
|
||||
if (holder != null && !info.isCustomWidget() && info.isWidgetIdAllocated()) {
|
||||
// Deleting an app widget ID is a void call but writes to disk before returning
|
||||
// to the caller...
|
||||
enqueueDeleteRunnable(() -> host.deleteAppWidgetId(info.appWidgetId));
|
||||
enqueueDeleteRunnable(() -> holder.deleteAppWidgetId(info.appWidgetId));
|
||||
}
|
||||
deleteItemFromDatabase(info, reason);
|
||||
}
|
||||
|
||||
@@ -55,7 +55,7 @@ public class WidgetAddFlowHandler implements Parcelable {
|
||||
|
||||
public void startBindFlow(Launcher launcher, int appWidgetId, ItemInfo info, int requestCode) {
|
||||
launcher.setWaitingForResult(PendingRequestArgs.forWidgetInfo(appWidgetId, this, info));
|
||||
launcher.getAppWidgetHost()
|
||||
launcher.getAppWidgetHolder()
|
||||
.startBindFlow(launcher, appWidgetId, mProviderInfo, requestCode);
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ public class WidgetAddFlowHandler implements Parcelable {
|
||||
return false;
|
||||
}
|
||||
launcher.setWaitingForResult(PendingRequestArgs.forWidgetInfo(appWidgetId, this, info));
|
||||
launcher.getAppWidgetHost().startConfigActivity(launcher, appWidgetId, requestCode);
|
||||
launcher.getAppWidgetHolder().startConfigActivity(launcher, appWidgetId, requestCode);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -59,7 +59,7 @@ public class WidgetHostViewLoader implements DragController.DragListener {
|
||||
|
||||
// Cleanup widget id
|
||||
if (mWidgetLoadingId != -1) {
|
||||
mLauncher.getAppWidgetHost().deleteAppWidgetId(mWidgetLoadingId);
|
||||
mLauncher.getAppWidgetHolder().deleteAppWidgetId(mWidgetLoadingId);
|
||||
mWidgetLoadingId = -1;
|
||||
}
|
||||
|
||||
@@ -69,7 +69,7 @@ public class WidgetHostViewLoader implements DragController.DragListener {
|
||||
Log.d(TAG, "...removing widget from drag layer");
|
||||
}
|
||||
mLauncher.getDragLayer().removeView(mInfo.boundWidget);
|
||||
mLauncher.getAppWidgetHost().deleteAppWidgetId(mInfo.boundWidget.getAppWidgetId());
|
||||
mLauncher.getAppWidgetHolder().deleteAppWidgetId(mInfo.boundWidget.getAppWidgetId());
|
||||
mInfo.boundWidget = null;
|
||||
}
|
||||
}
|
||||
@@ -94,7 +94,7 @@ public class WidgetHostViewLoader implements DragController.DragListener {
|
||||
mBindWidgetRunnable = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mWidgetLoadingId = mLauncher.getAppWidgetHost().allocateAppWidgetId();
|
||||
mWidgetLoadingId = mLauncher.getAppWidgetHolder().allocateAppWidgetId();
|
||||
if (LOGD) {
|
||||
Log.d(TAG, "Binding widget, id: " + mWidgetLoadingId);
|
||||
}
|
||||
@@ -116,7 +116,7 @@ public class WidgetHostViewLoader implements DragController.DragListener {
|
||||
if (mWidgetLoadingId == -1) {
|
||||
return;
|
||||
}
|
||||
AppWidgetHostView hostView = mLauncher.getAppWidgetHost().createView(
|
||||
AppWidgetHostView hostView = mLauncher.getAppWidgetHolder().createView(
|
||||
(Context) mLauncher, mWidgetLoadingId, pInfo);
|
||||
mInfo.boundWidget = hostView;
|
||||
|
||||
|
||||
@@ -321,7 +321,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
||||
@Override
|
||||
protected void onAttachedToWindow() {
|
||||
super.onAttachedToWindow();
|
||||
mActivityContext.getAppWidgetHost().addProviderChangeListener(this);
|
||||
mActivityContext.getAppWidgetHolder().addProviderChangeListener(this);
|
||||
notifyWidgetProvidersChanged();
|
||||
onRecommendedWidgetsBound();
|
||||
}
|
||||
@@ -329,7 +329,7 @@ public class WidgetsFullSheet extends BaseWidgetSheet
|
||||
@Override
|
||||
protected void onDetachedFromWindow() {
|
||||
super.onDetachedFromWindow();
|
||||
mActivityContext.getAppWidgetHost().removeProviderChangeListener(this);
|
||||
mActivityContext.getAppWidgetHolder().removeProviderChangeListener(this);
|
||||
mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView
|
||||
.removeOnAttachStateChangeListener(mBindScrollbarInSearchMode);
|
||||
if (mHasWorkProfile) {
|
||||
|
||||
@@ -190,7 +190,7 @@ public class BindWidgetTest extends AbstractLauncherUiTest {
|
||||
waitForLauncherCondition("App widget options did not update",
|
||||
l -> appWidgetManager.getAppWidgetOptions(appWidgetId).getBoolean(
|
||||
WidgetManagerHelper.WIDGET_OPTION_RESTORE_COMPLETED));
|
||||
executeOnLauncher(l -> l.getAppWidgetHost().startListening());
|
||||
executeOnLauncher(l -> l.getAppWidgetHolder().startListening());
|
||||
verifyWidgetPresent(info);
|
||||
assertNull(mLauncher.getWorkspace().tryGetPendingWidget(100));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user