From 4ed3dfc28a43ead701f5756e5e29e30028b32203 Mon Sep 17 00:00:00 2001 From: Becky Qiu Date: Wed, 19 Oct 2022 16:58:22 -0700 Subject: [PATCH 1/6] [Toast] Use a BaseSearchConfig for launcher3 to read allapps device config. Bug: 251387263 Test: local Change-Id: I92c52caff377e627305bc7999ae0205fe6269968 --- src/com/android/launcher3/Launcher.java | 13 +++++---- .../allapps/AllAppsTransitionController.java | 2 +- .../launcher3/allapps/BaseSearchConfig.java | 28 +++++++++++++++++++ 3 files changed, 36 insertions(+), 7 deletions(-) create mode 100644 src/com/android/launcher3/allapps/BaseSearchConfig.java diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 3abefe0cad..a2cfff3080 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -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; @@ -395,6 +396,7 @@ public class Launcher extends StatefulActivity private LauncherState mPrevLauncherState; private StringCache mStringCache; + private BaseSearchConfig mBaseSearchConfig; @Override @TargetApi(Build.VERSION_CODES.S) @@ -545,6 +547,9 @@ public class Launcher extends StatefulActivity 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() { @@ -1592,12 +1597,8 @@ public class Launcher extends StatefulActivity 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 diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index 001b494439..25225ed940 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -228,7 +228,7 @@ 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(); } diff --git a/src/com/android/launcher3/allapps/BaseSearchConfig.java b/src/com/android/launcher3/allapps/BaseSearchConfig.java new file mode 100644 index 0000000000..9f47e8d82f --- /dev/null +++ b/src/com/android/launcher3/allapps/BaseSearchConfig.java @@ -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; + } +} From 96cad794d75d284930391084d8f47c68e861c95b Mon Sep 17 00:00:00 2001 From: Saumya Prakash Date: Tue, 11 Oct 2022 00:25:59 +0000 Subject: [PATCH 2/6] Removed disabled overview actions at end of scroll Updated visibility of the overview action bar so that when viewing the clear all button at the end of the tasks it becomes hidden. Previously, the overview actions were disabled so they were not usable anyways. Updated the corresponding tests because expected behavior was changed. Test: Manual Fix: 230508197 Change-Id: I17628f1fa3869b575ec404a2a630a6927e3c1d14 --- .../quickstep/views/ClearAllButton.java | 4 +++ .../quickstep/views/OverviewActionsView.java | 8 ++++- .../android/quickstep/views/RecentsView.java | 3 ++ .../android/launcher3/tapl/BaseOverview.java | 32 ++++++++----------- 4 files changed, 28 insertions(+), 19 deletions(-) diff --git a/quickstep/src/com/android/quickstep/views/ClearAllButton.java b/quickstep/src/com/android/quickstep/views/ClearAllButton.java index 50be5ea565..d098ffc2fb 100644 --- a/quickstep/src/com/android/quickstep/views/ClearAllButton.java +++ b/quickstep/src/com/android/quickstep/views/ClearAllButton.java @@ -99,6 +99,10 @@ public class ClearAllButton extends Button { return false; } + public float getScrollAlpha() { + return mScrollAlpha; + } + public void setContentAlpha(float alpha) { if (mContentAlpha != alpha) { mContentAlpha = alpha; diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java index abace7cd7d..997200b746 100644 --- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java +++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java @@ -81,6 +81,8 @@ public class OverviewActionsView extends FrameLayo private static final int INDEX_FULLSCREEN_ALPHA = 2; private static final int INDEX_HIDDEN_FLAGS_ALPHA = 3; private static final int INDEX_SHARE_TARGET_ALPHA = 4; + private static final int INDEX_SCROLL_ALPHA = 5; + private static final int NUM_ALPHAS = 6; public @interface SplitButtonHiddenFlags { } public static final int FLAG_IS_NOT_TABLET = 1 << 0; @@ -125,7 +127,7 @@ public class OverviewActionsView extends FrameLayo @Override protected void onFinishInflate() { super.onFinishInflate(); - mMultiValueAlpha = new MultiValueAlpha(findViewById(R.id.action_buttons), 5); + mMultiValueAlpha = new MultiValueAlpha(findViewById(R.id.action_buttons), NUM_ALPHAS); mMultiValueAlpha.setUpdateVisibility(true); findViewById(R.id.action_screenshot).setOnClickListener(this); @@ -247,6 +249,10 @@ public class OverviewActionsView extends FrameLayo return mMultiValueAlpha.getProperty(INDEX_SHARE_TARGET_ALPHA); } + public AlphaProperty getIndexScrollAlpha() { + return mMultiValueAlpha.getProperty(INDEX_SCROLL_ALPHA); + } + /** * Offsets OverviewActionsView horizontal position based on 3 button nav container in taskbar. */ diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index 68b80c26f6..64e8295237 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -1890,6 +1890,9 @@ public abstract class RecentsView= 1) { + return false; + } + // Overview actions aren't visible for split screen tasks. return !task.isTaskSplit(); } private void verifyActionsViewVisibility() { - if (!hasTasks() || !isActionsViewVisible()) { - return; - } try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer( "want to assert overview actions view visibility")) { - if (mLauncher.isTablet() && !isOverviewSnappedToFocusedTaskForTablet()) { - mLauncher.waitUntilOverviewObjectGone("action_buttons"); - } else { + if (isActionsViewVisible()) { mLauncher.waitForOverviewObject("action_buttons"); + } else { + mLauncher.waitUntilOverviewObjectGone("action_buttons"); } } } - /** - * Returns if focused task is currently snapped task in tablet grid overview. - */ - private boolean isOverviewSnappedToFocusedTaskForTablet() { - OverviewTask focusedTask = getFocusedTaskForTablet(); - if (focusedTask == null) { - return false; - } - return Math.abs(focusedTask.getExactCenterX() - mLauncher.getExactScreenCenterX()) < 1; - } - /** * Returns Overview focused task if it exists. * From 6518efce73dd5d183ed444d4ef056d1e3d980111 Mon Sep 17 00:00:00 2001 From: Johannes Gallmann Date: Tue, 4 Oct 2022 11:57:37 +0000 Subject: [PATCH 3/6] Extracting ViewCapture into common Library The ViewCapture functionality is extracted into a common Library accessible to the Launcher and SystemUi modules. Test: None yet Change-Id: I4be394ed8dc86e30f6b8c69c438b8bc943e7907c --- Android.bp | 1 + protos/view_capture.proto | 59 -- .../uioverrides/QuickstepLauncher.java | 12 +- .../quickstep/TouchInteractionService.java | 8 +- .../android/quickstep/util/ViewCapture.java | 541 ------------------ 5 files changed, 13 insertions(+), 608 deletions(-) delete mode 100644 protos/view_capture.proto delete mode 100644 quickstep/src/com/android/quickstep/util/ViewCapture.java diff --git a/Android.bp b/Android.bp index 6267e9fa27..0bbb3d2c1e 100644 --- a/Android.bp +++ b/Android.bp @@ -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", diff --git a/protos/view_capture.proto b/protos/view_capture.proto deleted file mode 100644 index f363f36820..0000000000 --- a/protos/view_capture.proto +++ /dev/null @@ -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; -} diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index df391117fb..98323522b2 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -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,6 +72,7 @@ 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; @@ -119,7 +121,6 @@ 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; @@ -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 @@ -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 diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java index 80db362b09..e9f9d80e01 100644 --- a/quickstep/src/com/android/quickstep/TouchInteractionService.java +++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java @@ -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); + } } } diff --git a/quickstep/src/com/android/quickstep/util/ViewCapture.java b/quickstep/src/com/android/quickstep/util/ViewCapture.java deleted file mode 100644 index ba7d7f557c..0000000000 --- a/quickstep/src/com/android/quickstep/util/ViewCapture.java +++ /dev/null @@ -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 INSTANCE = - new MainThreadInitializedObject<>(ViewCapture::new); - - private final List 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>> tasks = mListeners.stream() - .map(l -> { - FutureTask task = - new FutureTask(() -> 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 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 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 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 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 oldCallback = callback; - callback = null; - if (oldCallback != null) { - oldCallback.accept(this); - } - } - } - - private static final class ViewIdProvider { - - private final SparseArray 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; - } - } -} From ee6a99b798dff4fc02fdd59692d2a5b02027253e Mon Sep 17 00:00:00 2001 From: Alex Chau Date: Tue, 25 Oct 2022 19:25:47 +0100 Subject: [PATCH 4/6] Update pullback animation to use reset FloatProperty for non-active state Fix: 246733229 Test: Change display between phone and tablet, use home gesture in AllApps Change-Id: I20bdef9dcf9a60475f482261501ad39106b8b107 --- .../allapps/AllAppsTransitionController.java | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/src/com/android/launcher3/allapps/AllAppsTransitionController.java b/src/com/android/launcher3/allapps/AllAppsTransitionController.java index 624cfc2215..05c9905f5a 100644 --- a/src/com/android/launcher3/allapps/AllAppsTransitionController.java +++ b/src/com/android/launcher3/allapps/AllAppsTransitionController.java @@ -76,6 +76,8 @@ public class AllAppsTransitionController } }; + private static final float ALL_APPS_PULL_BACK_TRANSLATION_DEFAULT = 0f; + public static final FloatProperty ALL_APPS_PULL_BACK_TRANSLATION = new FloatProperty("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 ALL_APPS_PULL_BACK_ALPHA = new FloatProperty("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); } } }; @@ -232,8 +244,8 @@ public class AllAppsTransitionController 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 From 0593a0d3ec7dc87c1053ff8ce97081c1e747de4a Mon Sep 17 00:00:00 2001 From: Sihua Ma Date: Mon, 3 Oct 2022 16:01:48 -0700 Subject: [PATCH 5/6] Created LauncherWidgetHolder as a wrapper for LauncherAppWidgetHost The wrapper class is created for further changes that will run the wrapped host in the background. Test: N/A Bug: 235358918 Change-Id: I9bd6fc2749c5d4a4d3391fb75d0c3ff1e13d17bf --- .../uioverrides/QuickstepLauncher.java | 12 +- .../launcher3/AppWidgetResizeFrame.java | 10 +- src/com/android/launcher3/Launcher.java | 52 ++--- .../launcher3/LauncherWidgetHolder.java | 187 ++++++++++++++++++ .../launcher3/SecondaryDropTarget.java | 2 +- src/com/android/launcher3/Workspace.java | 13 +- .../android/launcher3/model/ModelWriter.java | 8 +- .../widget/WidgetAddFlowHandler.java | 4 +- .../widget/WidgetHostViewLoader.java | 8 +- .../widget/picker/WidgetsFullSheet.java | 4 +- .../launcher3/ui/widget/BindWidgetTest.java | 2 +- 11 files changed, 244 insertions(+), 58 deletions(-) create mode 100644 src/com/android/launcher3/LauncherWidgetHolder.java diff --git a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java index 23794b0e20..77c15185d5 100644 --- a/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java +++ b/quickstep/src/com/android/launcher3/uioverrides/QuickstepLauncher.java @@ -76,6 +76,7 @@ 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; @@ -123,7 +124,6 @@ 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; @@ -488,11 +488,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 diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java index 555fbb412b..76a91c0029 100644 --- a/src/com/android/launcher3/AppWidgetResizeFrame.java +++ b/src/com/android/launcher3/AppWidgetResizeFrame.java @@ -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(() -> { diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java index 07d0f55321..7cf9296187 100644 --- a/src/com/android/launcher3/Launcher.java +++ b/src/com/android/launcher3/Launcher.java @@ -204,7 +204,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 +315,7 @@ public class Launcher extends StatefulActivity DragLayer mDragLayer; private WidgetManagerHelper mAppWidgetManager; - private LauncherAppWidgetHost mAppWidgetHost; + private LauncherWidgetHolder mAppWidgetHolder; private final int[] mTmpAddItemCellCoordinates = new int[2]; @@ -480,8 +479,8 @@ public class Launcher extends StatefulActivity mOnboardingPrefs = createOnboardingPrefs(mSharedPrefs); mAppWidgetManager = new WidgetManagerHelper(this); - mAppWidgetHost = createAppWidgetHost(); - mAppWidgetHost.startListening(); + mAppWidgetHolder = createAppWidgetHolder(); + mAppWidgetHolder.startListening(); setupViews(); crossFadeWithPreviousAppearance(); @@ -958,7 +957,7 @@ public class Launcher extends StatefulActivity 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 +968,7 @@ public class Launcher extends StatefulActivity } }; } 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 +991,7 @@ public class Launcher extends StatefulActivity } hideKeyboard(); logStopAndResume(false /* isResume */); - mAppWidgetHost.setActivityStarted(false); + mAppWidgetHolder.setActivityStarted(false); NotificationListener.removeNotificationsChangedListener(getPopupDataProvider()); } @@ -1005,7 +1004,7 @@ public class Launcher extends StatefulActivity mOverlayManager.onActivityStarted(this); } - mAppWidgetHost.setActivityStarted(true); + mAppWidgetHolder.setActivityStarted(true); TraceHelper.INSTANCE.endSection(traceToken); } @@ -1025,7 +1024,7 @@ public class Launcher extends StatefulActivity NotificationListener.addNotificationsChangedListener(mPopupDataProvider); DiscoveryBounce.showForHomeIfNeeded(this); - mAppWidgetHost.setActivityResumed(true); + mAppWidgetHolder.setActivityResumed(true); } private void logStopAndResume(boolean isResume) { @@ -1140,7 +1139,7 @@ public class Launcher extends StatefulActivity @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 +1204,7 @@ public class Launcher extends StatefulActivity if (!mDeferOverlayCallbacks) { mOverlayManager.onActivityPaused(this); } - mAppWidgetHost.setActivityResumed(false); + mAppWidgetHolder.setActivityResumed(false); } /** @@ -1430,7 +1429,7 @@ public class Launcher extends StatefulActivity 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 +1560,12 @@ public class Launcher extends StatefulActivity 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)); } @@ -1732,7 +1731,7 @@ public class Launcher extends StatefulActivity mRotationHelper.destroy(); try { - mAppWidgetHost.stopListening(); + mAppWidgetHolder.stopListening(); } catch (NullPointerException ex) { Log.w(TAG, "problem while stopping AppWidgetHost during Launcher destruction", ex); } @@ -1907,7 +1906,7 @@ public class Launcher extends StatefulActivity appWidgetId = CustomWidgetManager.INSTANCE.get(this).getWidgetIdForCustomProvider( info.componentName); } else { - appWidgetId = getAppWidgetHost().allocateAppWidgetId(); + appWidgetId = getAppWidgetHolder().allocateAppWidgetId(); } Bundle options = info.bindOptions; @@ -2021,7 +2020,7 @@ public class Launcher extends StatefulActivity 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 +2278,7 @@ public class Launcher extends StatefulActivity mWorkspace.clearDropTargets(); mWorkspace.removeAllWorkspaceScreens(); - mAppWidgetHost.clearViews(); + mAppWidgetHolder.clearViews(); if (mHotseat != null) { mHotseat.resetLayout(getDeviceProfile().isVerticalBarLayout()); @@ -2586,7 +2585,7 @@ public class Launcher extends StatefulActivity 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 +2647,18 @@ public class Launcher extends StatefulActivity // 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 +3016,8 @@ public class Launcher extends StatefulActivity 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); diff --git a/src/com/android/launcher3/LauncherWidgetHolder.java b/src/com/android/launcher3/LauncherWidgetHolder.java new file mode 100644 index 0000000000..5fcd46fe4d --- /dev/null +++ b/src/com/android/launcher3/LauncherWidgetHolder.java @@ -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(); + } +} diff --git a/src/com/android/launcher3/SecondaryDropTarget.java b/src/com/android/launcher3/SecondaryDropTarget.java index 0ee7aae088..791cfff9b0 100644 --- a/src/com/android/launcher3/SecondaryDropTarget.java +++ b/src/com/android/launcher3/SecondaryDropTarget.java @@ -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; diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java index fe8b3641e3..f834cce64f 100644 --- a/src/com/android/launcher3/Workspace.java +++ b/src/com/android/launcher3/Workspace.java @@ -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 extends PagedView public void widgetsRestored(final ArrayList 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 extends PagedView */ private class DeferredWidgetRefresh implements Runnable, ProviderChangedListener { private final ArrayList mInfos; - private final LauncherAppWidgetHost mHost; + private final LauncherWidgetHolder mWidgetHolder; private final Handler mHandler; private boolean mRefreshPending; DeferredWidgetRefresh(ArrayList 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 extends PagedView @Override public void run() { - mHost.removeProviderChangeListener(this); + mWidgetHolder.removeProviderChangeListener(this); mHandler.removeCallbacks(this); if (!mRefreshPending) { diff --git a/src/com/android/launcher3/model/ModelWriter.java b/src/com/android/launcher3/model/ModelWriter.java index 0a68d4aa34..514e7b22d8 100644 --- a/src/com/android/launcher3/model/ModelWriter.java +++ b/src/com/android/launcher3/model/ModelWriter.java @@ -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); } diff --git a/src/com/android/launcher3/widget/WidgetAddFlowHandler.java b/src/com/android/launcher3/widget/WidgetAddFlowHandler.java index 93132664ef..9319a9c2c4 100644 --- a/src/com/android/launcher3/widget/WidgetAddFlowHandler.java +++ b/src/com/android/launcher3/widget/WidgetAddFlowHandler.java @@ -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; } diff --git a/src/com/android/launcher3/widget/WidgetHostViewLoader.java b/src/com/android/launcher3/widget/WidgetHostViewLoader.java index 46141e0bc3..b18cd471cb 100644 --- a/src/com/android/launcher3/widget/WidgetHostViewLoader.java +++ b/src/com/android/launcher3/widget/WidgetHostViewLoader.java @@ -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; diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java index da8e25c1f6..21b2647d0c 100644 --- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java +++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java @@ -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) { diff --git a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java index fa39ce0604..0f861eb1f3 100644 --- a/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java +++ b/tests/src/com/android/launcher3/ui/widget/BindWidgetTest.java @@ -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)); } From 19250d69f7acf7843de5427fa7bcf8b8dbded423 Mon Sep 17 00:00:00 2001 From: Saumya Prakash Date: Wed, 26 Oct 2022 20:59:30 +0000 Subject: [PATCH 6/6] Revert "Removed disabled overview actions at end of scroll" This reverts commit 96cad794d75d284930391084d8f47c68e861c95b. Reason for revert: Breaks build test_suites_x86_64_coverage (linux) build 9222442 in git_tm-qpr-dev. Change-Id: I482f526da169505cf5d3f42ef999303dcc162a1f --- .../quickstep/views/ClearAllButton.java | 4 --- .../quickstep/views/OverviewActionsView.java | 8 +---- .../android/quickstep/views/RecentsView.java | 3 -- .../android/launcher3/tapl/BaseOverview.java | 32 +++++++++++-------- 4 files changed, 19 insertions(+), 28 deletions(-) diff --git a/quickstep/src/com/android/quickstep/views/ClearAllButton.java b/quickstep/src/com/android/quickstep/views/ClearAllButton.java index d098ffc2fb..50be5ea565 100644 --- a/quickstep/src/com/android/quickstep/views/ClearAllButton.java +++ b/quickstep/src/com/android/quickstep/views/ClearAllButton.java @@ -99,10 +99,6 @@ public class ClearAllButton extends Button { return false; } - public float getScrollAlpha() { - return mScrollAlpha; - } - public void setContentAlpha(float alpha) { if (mContentAlpha != alpha) { mContentAlpha = alpha; diff --git a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java index 997200b746..abace7cd7d 100644 --- a/quickstep/src/com/android/quickstep/views/OverviewActionsView.java +++ b/quickstep/src/com/android/quickstep/views/OverviewActionsView.java @@ -81,8 +81,6 @@ public class OverviewActionsView extends FrameLayo private static final int INDEX_FULLSCREEN_ALPHA = 2; private static final int INDEX_HIDDEN_FLAGS_ALPHA = 3; private static final int INDEX_SHARE_TARGET_ALPHA = 4; - private static final int INDEX_SCROLL_ALPHA = 5; - private static final int NUM_ALPHAS = 6; public @interface SplitButtonHiddenFlags { } public static final int FLAG_IS_NOT_TABLET = 1 << 0; @@ -127,7 +125,7 @@ public class OverviewActionsView extends FrameLayo @Override protected void onFinishInflate() { super.onFinishInflate(); - mMultiValueAlpha = new MultiValueAlpha(findViewById(R.id.action_buttons), NUM_ALPHAS); + mMultiValueAlpha = new MultiValueAlpha(findViewById(R.id.action_buttons), 5); mMultiValueAlpha.setUpdateVisibility(true); findViewById(R.id.action_screenshot).setOnClickListener(this); @@ -249,10 +247,6 @@ public class OverviewActionsView extends FrameLayo return mMultiValueAlpha.getProperty(INDEX_SHARE_TARGET_ALPHA); } - public AlphaProperty getIndexScrollAlpha() { - return mMultiValueAlpha.getProperty(INDEX_SCROLL_ALPHA); - } - /** * Offsets OverviewActionsView horizontal position based on 3 button nav container in taskbar. */ diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java index 64e8295237..68b80c26f6 100644 --- a/quickstep/src/com/android/quickstep/views/RecentsView.java +++ b/quickstep/src/com/android/quickstep/views/RecentsView.java @@ -1890,9 +1890,6 @@ public abstract class RecentsView= 1) { - return false; - } - // Overview actions aren't visible for split screen tasks. return !task.isTaskSplit(); } private void verifyActionsViewVisibility() { + if (!hasTasks() || !isActionsViewVisible()) { + return; + } try (LauncherInstrumentation.Closable c = mLauncher.addContextLayer( "want to assert overview actions view visibility")) { - if (isActionsViewVisible()) { - mLauncher.waitForOverviewObject("action_buttons"); - } else { + if (mLauncher.isTablet() && !isOverviewSnappedToFocusedTaskForTablet()) { mLauncher.waitUntilOverviewObjectGone("action_buttons"); + } else { + mLauncher.waitForOverviewObject("action_buttons"); } } } + /** + * Returns if focused task is currently snapped task in tablet grid overview. + */ + private boolean isOverviewSnappedToFocusedTaskForTablet() { + OverviewTask focusedTask = getFocusedTaskForTablet(); + if (focusedTask == null) { + return false; + } + return Math.abs(focusedTask.getExactCenterX() - mLauncher.getExactScreenCenterX()) < 1; + } + /** * Returns Overview focused task if it exists. *