diff options
Diffstat (limited to 'libs')
28 files changed, 508 insertions, 80 deletions
diff --git a/libs/WindowManager/Shell/res/values/config.xml b/libs/WindowManager/Shell/res/values/config.xml index 4c2863e4f594..8cea869aea34 100644 --- a/libs/WindowManager/Shell/res/values/config.xml +++ b/libs/WindowManager/Shell/res/values/config.xml @@ -40,7 +40,7 @@ <integer name="long_press_dock_anim_duration">250</integer> <!-- Animation duration for translating of one handed when trigger / dismiss. --> - <integer name="config_one_handed_translate_animation_duration">800</integer> + <integer name="config_one_handed_translate_animation_duration">600</integer> <!-- One handed mode default offset % of display size --> <fraction name="config_one_handed_offset">40%</fraction> diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java index f7fb63d9ab98..4b1955e56a6c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/ShellTaskOrganizer.java @@ -325,6 +325,13 @@ public class ShellTaskOrganizer extends TaskOrganizer { } @Override + public void onAppSplashScreenViewRemoved(int taskId) { + if (mStartingWindow != null) { + mStartingWindow.onAppSplashScreenViewRemoved(taskId); + } + } + + @Override public void onTaskAppeared(RunningTaskInfo taskInfo, SurfaceControl leash) { synchronized (mLock) { onTaskAppeared(new TaskAppearedInfo(taskInfo, leash)); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java index afb40d1ff95c..9d65d28b21b4 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/Bubble.java @@ -284,6 +284,15 @@ public class Bubble implements BubbleViewProvider { return mTitle; } + /** + * @return the ShortcutInfo id if it exists, or the metadata shortcut id otherwise. + */ + String getShortcutId() { + return getShortcutInfo() != null + ? getShortcutInfo().getId() + : getMetadataShortcutId(); + } + String getMetadataShortcutId() { return mMetadataShortcutId; } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java index dfd878f63283..202d9f013cc0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleController.java @@ -93,6 +93,7 @@ import java.io.FileDescriptor; import java.io.PrintWriter; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Objects; import java.util.concurrent.Executor; @@ -265,15 +266,7 @@ public class BubbleController { public void initialize() { mBubbleData.setListener(mBubbleDataListener); - mBubbleData.setSuppressionChangedListener(bubble -> { - // Make sure NoMan knows suppression state so that anyone querying it can tell. - try { - mBarService.onBubbleNotificationSuppressionChanged(bubble.getKey(), - !bubble.showInShade(), bubble.isSuppressed()); - } catch (RemoteException e) { - // Bad things have happened - } - }); + mBubbleData.setSuppressionChangedListener(this::onBubbleNotificationSuppressionChanged); mBubbleData.setPendingIntentCancelledListener(bubble -> { if (bubble.getBubbleIntent() == null) { @@ -401,6 +394,11 @@ public class BubbleController { return mImpl; } + @VisibleForTesting + public BubblesImpl.CachedState getImplCachedState() { + return mImpl.mCachedState; + } + public ShellExecutor getMainExecutor() { return mMainExecutor; } @@ -500,6 +498,18 @@ public class BubbleController { updateStack(); } + @VisibleForTesting + public void onBubbleNotificationSuppressionChanged(Bubble bubble) { + // Make sure NoMan knows suppression state so that anyone querying it can tell. + try { + mBarService.onBubbleNotificationSuppressionChanged(bubble.getKey(), + !bubble.showInShade(), bubble.isSuppressed()); + } catch (RemoteException e) { + // Bad things have happened + } + mImpl.mCachedState.updateBubbleSuppressedState(bubble); + } + /** Called when the current user changes. */ @VisibleForTesting public void onUserChanged(int newUserId) { @@ -808,11 +818,6 @@ public class BubbleController { } } - private boolean isBubbleExpanded(String key) { - return isStackExpanded() && mBubbleData != null && mBubbleData.getSelectedBubble() != null - && mBubbleData.getSelectedBubble().getKey().equals(key); - } - /** Promote the provided bubble from the overflow view. */ public void promoteBubbleFromOverflow(Bubble bubble) { mLogger.log(bubble, BubbleLogger.Event.BUBBLE_OVERFLOW_REMOVE_BACK_TO_STACK); @@ -1191,6 +1196,9 @@ public class BubbleController { mSysuiProxy.notifyInvalidateNotifications("BubbleData.Listener.applyUpdate"); updateStack(); + + // Update the cached state for queries from SysUI + mImpl.mCachedState.update(update); } }; @@ -1364,25 +1372,124 @@ public class BubbleController { } private class BubblesImpl implements Bubbles { + // Up-to-date cached state of bubbles data for SysUI to query from the calling thread + @VisibleForTesting + public class CachedState { + private boolean mIsStackExpanded; + private String mSelectedBubbleKey; + private HashSet<String> mSuppressedBubbleKeys = new HashSet<>(); + private HashMap<String, String> mSuppressedGroupToNotifKeys = new HashMap<>(); + private HashMap<String, Bubble> mShortcutIdToBubble = new HashMap<>(); + + private ArrayList<Bubble> mTmpBubbles = new ArrayList<>(); + + /** + * Updates the cached state based on the last full BubbleData change. + */ + synchronized void update(BubbleData.Update update) { + if (update.selectionChanged) { + mSelectedBubbleKey = update.selectedBubble != null + ? update.selectedBubble.getKey() + : null; + } + if (update.expandedChanged) { + mIsStackExpanded = update.expanded; + } + if (update.suppressedSummaryChanged) { + String summaryKey = + mBubbleData.getSummaryKey(update.suppressedSummaryGroup); + if (summaryKey != null) { + mSuppressedGroupToNotifKeys.put(update.suppressedSummaryGroup, summaryKey); + } else { + mSuppressedGroupToNotifKeys.remove(update.suppressedSummaryGroup); + } + } + + mTmpBubbles.clear(); + mTmpBubbles.addAll(update.bubbles); + mTmpBubbles.addAll(update.overflowBubbles); + + mSuppressedBubbleKeys.clear(); + mShortcutIdToBubble.clear(); + for (Bubble b : mTmpBubbles) { + mShortcutIdToBubble.put(b.getShortcutId(), b); + updateBubbleSuppressedState(b); + } + } + + /** + * Updates a specific bubble suppressed state. This is used mainly because notification + * suppression changes don't go through the same BubbleData update mechanism. + */ + synchronized void updateBubbleSuppressedState(Bubble b) { + if (!b.showInShade()) { + mSuppressedBubbleKeys.add(b.getKey()); + } else { + mSuppressedBubbleKeys.remove(b.getKey()); + } + } + + public synchronized boolean isStackExpanded() { + return mIsStackExpanded; + } + + public synchronized boolean isBubbleExpanded(String key) { + return mIsStackExpanded && key.equals(mSelectedBubbleKey); + } + + public synchronized boolean isBubbleNotificationSuppressedFromShade(String key, + String groupKey) { + return mSuppressedBubbleKeys.contains(key) + || (mSuppressedGroupToNotifKeys.containsKey(groupKey) + && key.equals(mSuppressedGroupToNotifKeys.get(groupKey))); + } + + @Nullable + public synchronized Bubble getBubbleWithShortcutId(String id) { + return mShortcutIdToBubble.get(id); + } + + synchronized void dump(PrintWriter pw) { + pw.println("BubbleImpl.CachedState state:"); + + pw.println("mIsStackExpanded: " + mIsStackExpanded); + pw.println("mSelectedBubbleKey: " + mSelectedBubbleKey); + + pw.print("mSuppressedBubbleKeys: "); + pw.println(mSuppressedBubbleKeys.size()); + for (String key : mSuppressedBubbleKeys) { + pw.println(" suppressing: " + key); + } + + pw.print("mSuppressedGroupToNotifKeys: "); + pw.println(mSuppressedGroupToNotifKeys.size()); + for (String key : mSuppressedGroupToNotifKeys.keySet()) { + pw.println(" suppressing: " + key); + } + } + } + + private CachedState mCachedState = new CachedState(); + @Override public boolean isBubbleNotificationSuppressedFromShade(String key, String groupKey) { - return mMainExecutor.executeBlockingForResult(() -> { - return BubbleController.this.isBubbleNotificationSuppressedFromShade(key, groupKey); - }, Boolean.class); + return mCachedState.isBubbleNotificationSuppressedFromShade(key, groupKey); } @Override public boolean isBubbleExpanded(String key) { - return mMainExecutor.executeBlockingForResult(() -> { - return BubbleController.this.isBubbleExpanded(key); - }, Boolean.class); + return mCachedState.isBubbleExpanded(key); } @Override public boolean isStackExpanded() { - return mMainExecutor.executeBlockingForResult(() -> { - return BubbleController.this.isStackExpanded(); - }, Boolean.class); + return mCachedState.isStackExpanded(); + } + + @Override + @Nullable + public Bubble getBubbleWithShortcutId(String shortcutId) { + return mCachedState.getBubbleWithShortcutId(shortcutId); } @Override @@ -1425,14 +1532,6 @@ public class BubbleController { } @Override - @Nullable - public Bubble getBubbleWithShortcutId(String shortcutId) { - return mMainExecutor.executeBlockingForResult(() -> { - return BubbleController.this.mBubbleData.getAnyBubbleWithShortcutId(shortcutId); - }, Bubble.class); - } - - @Override public void onTaskbarChanged(Bundle b) { mMainExecutor.execute(() -> { BubbleController.this.onTaskbarChanged(b); @@ -1555,6 +1654,7 @@ public class BubbleController { try { mMainExecutor.executeBlocking(() -> { BubbleController.this.dump(fd, pw, args); + mCachedState.dump(pw); }); } catch (InterruptedException e) { Slog.e(TAG, "Failed to dump BubbleController in 2s"); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java index 6f5cfd114688..d73ce6951e6d 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/bubbles/BubbleData.java @@ -73,6 +73,7 @@ public class BubbleData { boolean expandedChanged; boolean selectionChanged; boolean orderChanged; + boolean suppressedSummaryChanged; boolean expanded; @Nullable BubbleViewProvider selectedBubble; @Nullable Bubble addedBubble; @@ -81,6 +82,7 @@ public class BubbleData { @Nullable Bubble removedOverflowBubble; @Nullable Bubble suppressedBubble; @Nullable Bubble unsuppressedBubble; + @Nullable String suppressedSummaryGroup; // Pair with Bubble and @DismissReason Integer final List<Pair<Bubble, Integer>> removedBubbles = new ArrayList<>(); @@ -103,7 +105,9 @@ public class BubbleData { || removedOverflowBubble != null || orderChanged || suppressedBubble != null - || unsuppressedBubble != null; + || unsuppressedBubble != null + || suppressedSummaryChanged + || suppressedSummaryGroup != null; } void bubbleRemoved(Bubble bubbleToRemove, @DismissReason int reason) { @@ -380,6 +384,9 @@ public class BubbleData { */ void addSummaryToSuppress(String groupKey, String notifKey) { mSuppressedGroupKeys.put(groupKey, notifKey); + mStateChange.suppressedSummaryChanged = true; + mStateChange.suppressedSummaryGroup = groupKey; + dispatchPendingChanges(); } /** @@ -397,6 +404,9 @@ public class BubbleData { */ void removeSuppressedSummary(String groupKey) { mSuppressedGroupKeys.remove(groupKey); + mStateChange.suppressedSummaryChanged = true; + mStateChange.suppressedSummaryGroup = groupKey; + dispatchPendingChanges(); } /** diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java index 8dc05de9bb8f..a525c2c0219c 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHanded.java @@ -68,6 +68,11 @@ public interface OneHanded { void setLockedDisabled(boolean locked, boolean enabled); /** + * Registers callback to notify WMShell when user tap shortcut to expand notification. + */ + void registerEventCallback(OneHandedEventCallback callback); + + /** * Registers callback to be notified after {@link OneHandedDisplayAreaOrganizer} * transition start or finish */ diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java index c275d50a5d56..b43daa0da2c0 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedController.java @@ -43,6 +43,7 @@ import android.util.Slog; import android.view.Surface; import android.view.WindowManager; import android.view.accessibility.AccessibilityManager; +import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -72,6 +73,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController> private static final String ONE_HANDED_MODE_GESTURAL_OVERLAY = "com.android.internal.systemui.onehanded.gestural"; private static final int OVERLAY_ENABLED_DELAY_MS = 250; + private static final int DISPLAY_AREA_READY_RETRY_MS = 10; static final String SUPPORT_ONE_HANDED_MODE = "ro.support_one_handed_mode"; @@ -99,6 +101,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController> private final Handler mMainHandler; private final OneHandedImpl mImpl = new OneHandedImpl(); + private OneHandedEventCallback mEventCallback; private OneHandedDisplayAreaOrganizer mDisplayAreaOrganizer; private OneHandedBackgroundPanelOrganizer mBackgroundPanelOrganizer; @@ -288,7 +291,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController> mTimeoutObserver = getObserver(this::onTimeoutSettingChanged); mTaskChangeExitObserver = getObserver(this::onTaskChangeExitSettingChanged); mSwipeToNotificationEnabledObserver = - getObserver(this::onSwipeToNotificationEnabledSettingChanged); + getObserver(this::onSwipeToNotificationEnabledChanged); mDisplayController.addDisplayChangingController(mRotationController); setupCallback(); @@ -358,14 +361,23 @@ public class OneHandedController implements RemoteCallable<OneHandedController> Slog.d(TAG, "Temporary lock disabled"); return; } + + if (!mDisplayAreaOrganizer.isReady()) { + // Must wait until DisplayAreaOrganizer is ready for transitioning. + mMainExecutor.executeDelayed(this::startOneHanded, DISPLAY_AREA_READY_RETRY_MS); + return; + } + if (mState.isTransitioning() || mState.isInOneHanded()) { return; } + final int currentRotation = mDisplayAreaOrganizer.getDisplayLayout().rotation(); if (currentRotation != Surface.ROTATION_0 && currentRotation != Surface.ROTATION_180) { Slog.w(TAG, "One handed mode only support portrait mode"); return; } + mState.setState(STATE_ENTERING); final int yOffSet = Math.round( mDisplayAreaOrganizer.getDisplayLayout().height() * mOffSetFraction); @@ -394,6 +406,10 @@ public class OneHandedController implements RemoteCallable<OneHandedController> mOneHandedUiEventLogger.writeEvent(uiEvent); } + void registerEventCallback(OneHandedEventCallback callback) { + mEventCallback = callback; + } + @VisibleForTesting void registerTransitionCallback(OneHandedTransitionCallback callback) { mDisplayAreaOrganizer.registerTransitionCallback(callback); @@ -464,8 +480,29 @@ public class OneHandedController implements RemoteCallable<OneHandedController> } @VisibleForTesting + void notifyExpandNotification() { + mMainExecutor.execute(() -> mEventCallback.notifyExpandNotification()); + } + + @VisibleForTesting + void notifyUserConfigChanged(boolean success) { + if (!success) { + return; + } + // TODO Check UX if popup Toast to notify user when auto-enabled one-handed is good option. + Toast.makeText(mContext, R.string.one_handed_tutorial_title, Toast.LENGTH_LONG).show(); + } + + @VisibleForTesting void onActivatedActionChanged() { - if (mState.isTransitioning() || !isOneHandedEnabled()) { + if (!isOneHandedEnabled()) { + final boolean success = mOneHandedSettingsUtil.setOneHandedModeEnabled( + mContext.getContentResolver(), 1 /* Enabled for shortcut */, mUserId); + notifyUserConfigChanged(success); + } + + if (isSwipeToNotificationEnabled()) { + notifyExpandNotification(); return; } @@ -494,11 +531,9 @@ public class OneHandedController implements RemoteCallable<OneHandedController> setOneHandedEnabled(enabled); // Also checks swipe to notification settings since they all need gesture overlay. - // Enabled overlay package may affect the current animation(e.g:Settings switch), - // so we delay 250ms to enabled overlay after switch animation finish - mMainExecutor.executeDelayed(() -> setEnabledGesturalOverlay( + setEnabledGesturalOverlay( enabled || mOneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( - mContext.getContentResolver(), mUserId)), OVERLAY_ENABLED_DELAY_MS); + mContext.getContentResolver(), mUserId), true /* DelayExecute */); } @VisibleForTesting @@ -542,7 +577,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController> } @VisibleForTesting - void onSwipeToNotificationEnabledSettingChanged() { + void onSwipeToNotificationEnabledChanged() { final boolean enabled = mOneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( mContext.getContentResolver(), mUserId); @@ -551,7 +586,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController> // Also checks one handed mode settings since they all need gesture overlay. setEnabledGesturalOverlay( enabled || mOneHandedSettingsUtil.getSettingsOneHandedModeEnabled( - mContext.getContentResolver(), mUserId)); + mContext.getContentResolver(), mUserId), true /* DelayExecute */); } private void setupTimeoutListener() { @@ -569,11 +604,19 @@ public class OneHandedController implements RemoteCallable<OneHandedController> return mIsOneHandedEnabled; } + @VisibleForTesting + boolean isSwipeToNotificationEnabled() { + return mIsSwipeToNotificationEnabled; + } + private void updateOneHandedEnabled() { if (mState.getState() == STATE_ENTERING || mState.getState() == STATE_ACTIVE) { mMainExecutor.execute(() -> stopOneHanded()); } + // Reset and align shortcut one_handed_mode_activated status with current mState + notifyShortcutState(mState.getState()); + mTouchHandler.onOneHandedEnabled(mIsOneHandedEnabled); if (!mIsOneHandedEnabled) { @@ -608,12 +651,19 @@ public class OneHandedController implements RemoteCallable<OneHandedController> if (info != null && !info.isEnabled()) { // Enable the default gestural one handed overlay. - setEnabledGesturalOverlay(true); + setEnabledGesturalOverlay(true /* enabled */, false /* delayExecute */); } } @VisibleForTesting - private void setEnabledGesturalOverlay(boolean enabled) { + private void setEnabledGesturalOverlay(boolean enabled, boolean delayExecute) { + if (mState.isTransitioning() || delayExecute) { + // Enabled overlay package may affect the current animation(e.g:Settings switch), + // so we delay 250ms to enabled overlay after switch animation finish, only delay once. + mMainExecutor.executeDelayed(() -> setEnabledGesturalOverlay(enabled, false), + OVERLAY_ENABLED_DELAY_MS); + return; + } try { mOverlayManager.setEnabled(ONE_HANDED_MODE_GESTURAL_OVERLAY, enabled, USER_CURRENT); } catch (RemoteException e) { @@ -628,6 +678,7 @@ public class OneHandedController implements RemoteCallable<OneHandedController> if (enabled == isFeatureEnabled) { return; } + mLockedDisabled = locked && !enabled; } @@ -761,6 +812,13 @@ public class OneHandedController implements RemoteCallable<OneHandedController> } @Override + public void registerEventCallback(OneHandedEventCallback callback) { + mMainExecutor.execute(() -> { + OneHandedController.this.registerEventCallback(callback); + }); + } + + @Override public void registerTransitionCallback(OneHandedTransitionCallback callback) { mMainExecutor.execute(() -> { OneHandedController.this.registerTransitionCallback(callback); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java index b8da37fd0c25..03a90c6d4677 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizer.java @@ -61,11 +61,12 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { private DisplayLayout mDisplayLayout = new DisplayLayout(); - private float mLastVisualOffset = 0; private final Rect mLastVisualDisplayBounds = new Rect(); private final Rect mDefaultDisplayBounds = new Rect(); private final OneHandedSettingsUtil mOneHandedSettingsUtil; + private boolean mIsReady; + private float mLastVisualOffset = 0; private int mEnterExitAnimationDurationMs; private ArrayMap<WindowContainerToken, SurfaceControl> mDisplayAreaTokenMap = new ArrayMap(); @@ -157,6 +158,7 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { final DisplayAreaAppearedInfo info = displayAreaInfos.get(i); onDisplayAreaAppeared(info.getDisplayAreaInfo(), info.getLeash()); } + mIsReady = true; updateDisplayBounds(); return displayAreaInfos; } @@ -164,9 +166,14 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { @Override public void unregisterOrganizer() { super.unregisterOrganizer(); + mIsReady = false; resetWindowsOffset(); } + boolean isReady() { + return mIsReady; + } + /** * Handler for display rotation changes by {@link DisplayLayout} * @@ -184,8 +191,15 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { myUserId())) { return; } + mDisplayLayout.rotateTo(context.getResources(), toRotation); updateDisplayBounds(); + + if (mOneHandedSettingsUtil.getSettingsSwipeToNotificationEnabled( + context.getContentResolver(), myUserId())) { + // If current settings is swipe notification, skip finishOffset. + return; + } finishOffset(0, TRANSITION_DIRECTION_EXIT); } @@ -312,6 +326,8 @@ public class OneHandedDisplayAreaOrganizer extends DisplayAreaOrganizer { pw.println(mDisplayAreaTokenMap); pw.print(innerPrefix + "mDefaultDisplayBounds="); pw.println(mDefaultDisplayBounds); + pw.print(innerPrefix + "mIsReady="); + pw.println(mIsReady); pw.print(innerPrefix + "mLastVisualDisplayBounds="); pw.println(mLastVisualDisplayBounds); pw.print(innerPrefix + "mLastVisualOffset="); diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedEventCallback.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedEventCallback.java new file mode 100644 index 000000000000..d07eea271eac --- /dev/null +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedEventCallback.java @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2021 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.wm.shell.onehanded; + +/** + * Additional callback interface for OneHanded events. + */ +public interface OneHandedEventCallback { + /** + * Called to notify expand notification shade. + */ + default void notifyExpandNotification() { + } +} diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java index 90fc823fb574..da53b359a304 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/onehanded/OneHandedSettingsUtil.java @@ -105,6 +105,17 @@ public final class OneHandedSettingsUtil { } /** + * Sets one handed enable or disable flag from Settings provider. + * + * @return true if the value was set, false on database errors + */ + public boolean setOneHandedModeEnabled(ContentResolver resolver, int enabled, int userId) { + return Settings.Secure.putIntForUser(resolver, + Settings.Secure.ONE_HANDED_MODE_ENABLED, enabled, userId); + } + + + /** * Queries taps app to exit config from Settings provider. * * @return enable or disable taps app exit. diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java index f6b5889dda28..bc8e1e72b830 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java @@ -278,6 +278,10 @@ public class PhonePipMenuController implements PipMenuController { return; } + // Sync the menu bounds before showing it in case it is out of sync. + movePipMenu(null /* pipLeash */, null /* transaction */, stackBounds); + updateMenuBounds(stackBounds); + mPipMenuView.showMenu(menuState, stackBounds, allowMenuTimeout, willResizeMenu, withDelay, showResizeHandle); } diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java index 841edef9172f..f0bd8a2846ed 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipResizeGestureHandler.java @@ -235,15 +235,20 @@ public class PipResizeGestureHandler { @VisibleForTesting void onInputEvent(InputEvent ev) { + if (!mEnableDragCornerResize && !mEnablePinchResize) { + // No need to handle anything if neither form of resizing is enabled. + return; + } + // Don't allow resize when PiP is stashed. if (mPipBoundsState.isStashed()) { return; } if (ev instanceof MotionEvent) { - if (mOngoingPinchToResize) { + if (mEnablePinchResize && mOngoingPinchToResize) { onPinchResize((MotionEvent) ev); - } else { + } else if (mEnableDragCornerResize) { onDragCornerResize((MotionEvent) ev); } } @@ -318,8 +323,8 @@ public class PipResizeGestureHandler { case MotionEvent.ACTION_POINTER_DOWN: if (mEnablePinchResize && ev.getPointerCount() == 2) { onPinchResize(ev); - mOngoingPinchToResize = true; - return true; + mOngoingPinchToResize = mAllowGesture; + return mAllowGesture; } break; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java index 4d33cb0452dc..46db35a6e29f 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingSurfaceDrawer.java @@ -35,6 +35,7 @@ import android.graphics.Rect; import android.graphics.drawable.ColorDrawable; import android.hardware.display.DisplayManager; import android.os.IBinder; +import android.os.RemoteCallback; import android.os.Trace; import android.os.UserHandle; import android.util.Slog; @@ -42,6 +43,7 @@ import android.util.SparseArray; import android.view.Choreographer; import android.view.Display; import android.view.SurfaceControl; +import android.view.SurfaceControlViewHost; import android.view.View; import android.view.WindowManager; import android.widget.FrameLayout; @@ -121,6 +123,13 @@ public class StartingSurfaceDrawer { private final SparseArray<StartingWindowRecord> mStartingWindowRecords = new SparseArray<>(); + /** + * Records of {@link SurfaceControlViewHost} where the splash screen icon animation is + * rendered and that have not yet been removed by their client. + */ + private final SparseArray<SurfaceControlViewHost> mAnimatedSplashScreenSurfaceHosts = + new SparseArray<>(1); + /** Obtain proper context for showing splash screen on the provided display. */ private Context getDisplayContext(Context context, int displayId) { if (displayId == DEFAULT_DISPLAY) { @@ -386,25 +395,58 @@ public class StartingSurfaceDrawer { /** * Called when the Task wants to copy the splash screen. - * @param taskId */ public void copySplashScreenView(int taskId) { final StartingWindowRecord preView = mStartingWindowRecords.get(taskId); SplashScreenViewParcelable parcelable; - if (preView != null && preView.mContentView != null - && preView.mContentView.isCopyable()) { - parcelable = new SplashScreenViewParcelable(preView.mContentView); - preView.mContentView.onCopied(); + SplashScreenView splashScreenView = preView != null ? preView.mContentView : null; + if (splashScreenView != null && splashScreenView.isCopyable()) { + parcelable = new SplashScreenViewParcelable(splashScreenView); + parcelable.setClientCallback( + new RemoteCallback((bundle) -> mSplashScreenExecutor.execute( + () -> onAppSplashScreenViewRemoved(taskId, false)))); + splashScreenView.onCopied(); + mAnimatedSplashScreenSurfaceHosts.append(taskId, splashScreenView.getSurfaceHost()); } else { parcelable = null; } if (DEBUG_SPLASH_SCREEN) { Slog.v(TAG, "Copying splash screen window view for task: " + taskId - + " parcelable? " + parcelable); + + " parcelable: " + parcelable); } ActivityTaskManager.getInstance().onSplashScreenViewCopyFinished(taskId, parcelable); } + /** + * Called when the {@link SplashScreenView} is removed from the client Activity view's hierarchy + * or when the Activity is clean up. + * + * @param taskId The Task id on which the splash screen was attached + */ + public void onAppSplashScreenViewRemoved(int taskId) { + onAppSplashScreenViewRemoved(taskId, true /* fromServer */); + } + + /** + * @param fromServer If true, this means the removal was notified by the server. This is only + * used for debugging purposes. + * @see #onAppSplashScreenViewRemoved(int) + */ + private void onAppSplashScreenViewRemoved(int taskId, boolean fromServer) { + SurfaceControlViewHost viewHost = + mAnimatedSplashScreenSurfaceHosts.get(taskId); + if (viewHost == null) { + return; + } + mAnimatedSplashScreenSurfaceHosts.remove(taskId); + if (DEBUG_SPLASH_SCREEN) { + String reason = fromServer ? "Server cleaned up" : "App removed"; + Slog.v(TAG, reason + "the splash screen. Releasing SurfaceControlViewHost for task:" + + taskId); + } + viewHost.getView().post(viewHost::release); + } + protected boolean addWindow(int taskId, IBinder appToken, View view, WindowManager wm, WindowManager.LayoutParams params) { boolean shouldSaveView = true; diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java index cffc789106cb..9c1dde925762 100644 --- a/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java +++ b/libs/WindowManager/Shell/src/com/android/wm/shell/startingsurface/StartingWindowController.java @@ -150,6 +150,14 @@ public class StartingWindowController implements RemoteCallable<StartingWindowCo } /** + * @see StartingSurfaceDrawer#onAppSplashScreenViewRemoved(int) + */ + public void onAppSplashScreenViewRemoved(int taskId) { + mSplashScreenExecutor.execute( + () -> mStartingSurfaceDrawer.onAppSplashScreenViewRemoved(taskId)); + } + + /** * Called when the content of a task is ready to show, starting window can be removed. */ public void removeStartingWindow(int taskId, SurfaceControl leash, Rect frame, diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java index 1852279ee96c..47789b7490ee 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedControllerTest.java @@ -74,6 +74,8 @@ public class OneHandedControllerTest extends OneHandedTestCase { @Mock OneHandedDisplayAreaOrganizer mMockDisplayAreaOrganizer; @Mock + OneHandedEventCallback mMockEventCallback; + @Mock OneHandedTouchHandler mMockTouchHandler; @Mock OneHandedTutorialHandler mMockTutorialHandler; @@ -106,6 +108,7 @@ public class OneHandedControllerTest extends OneHandedTestCase { when(mMockDisplayController.getDisplay(anyInt())).thenReturn(mDisplay); when(mMockDisplayAreaOrganizer.getDisplayAreaTokenMap()).thenReturn(new ArrayMap<>()); + when(mMockDisplayAreaOrganizer.isReady()).thenReturn(true); when(mMockBackgroundOrganizer.getBackgroundSurface()).thenReturn(mMockLeash); when(mMockSettingsUitl.getSettingsOneHandedModeEnabled(any(), anyInt())).thenReturn( mDefaultEnabled); @@ -241,7 +244,7 @@ public class OneHandedControllerTest extends OneHandedTestCase { @Test public void testSettingsObserverUpdateSwipeToNotification() { - mSpiedOneHandedController.onSwipeToNotificationEnabledSettingChanged(); + mSpiedOneHandedController.onSwipeToNotificationEnabledChanged(); verify(mSpiedOneHandedController).setSwipeToNotificationEnabled(anyBoolean()); } @@ -311,6 +314,7 @@ public class OneHandedControllerTest extends OneHandedTestCase { final DisplayLayout testDisplayLayout = new DisplayLayout(mDisplayLayout); testDisplayLayout.rotateTo(mContext.getResources(), Surface.ROTATION_180); mSpiedTransitionState.setState(STATE_NONE); + when(mMockDisplayAreaOrganizer.isReady()).thenReturn(true); when(mMockDisplayAreaOrganizer.getDisplayLayout()).thenReturn(testDisplayLayout); mSpiedOneHandedController.setOneHandedEnabled(true); mSpiedOneHandedController.setLockedDisabled(false /* locked */, false /* enabled */); @@ -372,8 +376,7 @@ public class OneHandedControllerTest extends OneHandedTestCase { when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(true); mSpiedOneHandedController.onActivatedActionChanged(); - verify(mSpiedOneHandedController, never()).startOneHanded(); - verify(mSpiedOneHandedController, never()).stopOneHanded(); + verify(mSpiedTransitionState, never()).setState(STATE_EXITING); } @Test @@ -383,20 +386,20 @@ public class OneHandedControllerTest extends OneHandedTestCase { when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(true); mSpiedOneHandedController.onActivatedActionChanged(); - verify(mSpiedOneHandedController, never()).startOneHanded(); - verify(mSpiedOneHandedController, never()).stopOneHanded(); + verify(mSpiedTransitionState, never()).setState(STATE_ENTERING); } @Test - public void testOneHandedDisabled_shortcutEnabled_skipActions() { + public void testOneHandedDisabled_shortcutTrigger_thenAutoEnabled() { when(mSpiedOneHandedController.isOneHandedEnabled()).thenReturn(false); when(mSpiedTransitionState.getState()).thenReturn(STATE_NONE); when(mSpiedTransitionState.isTransitioning()).thenReturn(false); - when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(true); + when(mMockSettingsUitl.getOneHandedModeActivated(any(), anyInt())).thenReturn(false); + when(mMockSettingsUitl.setOneHandedModeEnabled(any(), anyInt(), anyInt())).thenReturn( + false); mSpiedOneHandedController.onActivatedActionChanged(); - verify(mSpiedOneHandedController, never()).startOneHanded(); - verify(mSpiedOneHandedController, never()).stopOneHanded(); + verify(mSpiedOneHandedController).notifyUserConfigChanged(anyBoolean()); } @Test @@ -408,4 +411,28 @@ public class OneHandedControllerTest extends OneHandedTestCase { verify(mSpiedTransitionState).addSListeners(mMockTutorialHandler); } + + @Test + public void testNotifyEventCallbackWithMainExecutor() { + when(mSpiedOneHandedController.isOneHandedEnabled()).thenReturn(true); + when(mSpiedTransitionState.getState()).thenReturn(STATE_NONE); + when(mSpiedTransitionState.isTransitioning()).thenReturn(false); + when(mSpiedOneHandedController.isSwipeToNotificationEnabled()).thenReturn(true); + mSpiedOneHandedController.registerEventCallback(mMockEventCallback); + mSpiedOneHandedController.onActivatedActionChanged(); + + verify(mMockShellMainExecutor).execute(any()); + } + + @Test + public void testNotifyShortcutState_whenUpdateOneHandedEnabled() { + when(mSpiedOneHandedController.isOneHandedEnabled()).thenReturn(false); + when(mSpiedTransitionState.getState()).thenReturn(STATE_NONE); + when(mSpiedTransitionState.isTransitioning()).thenReturn(false); + when(mSpiedOneHandedController.isSwipeToNotificationEnabled()).thenReturn(true); + mSpiedOneHandedController.registerEventCallback(mMockEventCallback); + mSpiedOneHandedController.setOneHandedEnabled(true); + + verify(mSpiedOneHandedController).notifyShortcutState(anyInt()); + } } diff --git a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java index a27ed114de70..ef16fd391235 100644 --- a/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java +++ b/libs/WindowManager/Shell/tests/unittest/src/com/android/wm/shell/onehanded/OneHandedDisplayAreaOrganizerTest.java @@ -418,4 +418,18 @@ public class OneHandedDisplayAreaOrganizerTest extends OneHandedTestCase { verify(mSpiedDisplayAreaOrganizer, never()).resetWindowsOffset(); } + + @Test + public void testDisplayArea_notReadyForTransition() { + OneHandedDisplayAreaOrganizer testSpiedDisplayAreaOrganizer = spy( + new OneHandedDisplayAreaOrganizer(mContext, + mDisplayLayout, + mMockSettingsUitl, + mMockAnimationController, + mTutorialHandler, + mMockBackgroundOrganizer, + mMockShellMainExecutor)); + + assertThat(testSpiedDisplayAreaOrganizer.isReady()).isFalse(); + } } diff --git a/libs/hwui/RenderNode.cpp b/libs/hwui/RenderNode.cpp index 332f7e6f0eac..44c335f6adb3 100644 --- a/libs/hwui/RenderNode.cpp +++ b/libs/hwui/RenderNode.cpp @@ -455,8 +455,7 @@ void RenderNode::destroyLayers() { if (hasLayer()) { this->setLayerSurface(nullptr); } - mSnapshotResult.snapshot = nullptr; - mTargetImageFilter = nullptr; + if (mDisplayList) { mDisplayList.updateChildren([](RenderNode* child) { child->destroyLayers(); }); } diff --git a/libs/hwui/RenderNode.h b/libs/hwui/RenderNode.h index 8595b6e5f78a..c770150650e2 100644 --- a/libs/hwui/RenderNode.h +++ b/libs/hwui/RenderNode.h @@ -330,6 +330,11 @@ public: } else { mSkiaLayer.reset(); } + + // Clear out the previous snapshot and the image filter the previous + // snapshot was created with whenever the layer changes. + mSnapshotResult.snapshot = nullptr; + mTargetImageFilter = nullptr; } /** diff --git a/libs/hwui/WebViewFunctorManager.cpp b/libs/hwui/WebViewFunctorManager.cpp index d9b9e2456fff..92e20c477669 100644 --- a/libs/hwui/WebViewFunctorManager.cpp +++ b/libs/hwui/WebViewFunctorManager.cpp @@ -197,6 +197,8 @@ ASurfaceControl* WebViewFunctor::getSurfaceControl() { auto funcs = renderthread::RenderThread::getInstance().getASurfaceControlFunctions(); mSurfaceControl = funcs.createFunc(rootSurfaceControl, "Webview Overlay SurfaceControl"); ASurfaceTransaction* transaction = funcs.transactionCreateFunc(); + activeContext->prepareSurfaceControlForWebview(); + funcs.transactionSetZOrderFunc(transaction, mSurfaceControl, -1); funcs.transactionSetVisibilityFunc(transaction, mSurfaceControl, ASURFACE_TRANSACTION_VISIBILITY_SHOW); funcs.transactionApplyFunc(transaction); diff --git a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp index 602c32a966d3..9ff2f461d40d 100644 --- a/libs/hwui/jni/android_graphics_HardwareRenderer.cpp +++ b/libs/hwui/jni/android_graphics_HardwareRenderer.cpp @@ -71,6 +71,10 @@ struct { } gASurfaceTransactionCallback; struct { + jmethodID prepare; +} gPrepareSurfaceControlForWebviewCallback; + +struct { jmethodID onFrameDraw; } gFrameDrawingCallback; @@ -500,6 +504,28 @@ private: jobject mObject; }; +class JWeakGlobalRefHolder { +public: + JWeakGlobalRefHolder(JavaVM* vm, jobject object) : mVm(vm) { + mWeakRef = getenv(vm)->NewWeakGlobalRef(object); + } + + virtual ~JWeakGlobalRefHolder() { + if (mWeakRef != nullptr) getenv(mVm)->DeleteWeakGlobalRef(mWeakRef); + mWeakRef = nullptr; + } + + jobject ref() { return mWeakRef; } + JavaVM* vm() { return mVm; } + +private: + JWeakGlobalRefHolder(const JWeakGlobalRefHolder&) = delete; + void operator=(const JWeakGlobalRefHolder&) = delete; + + JavaVM* mVm; + jobject mWeakRef; +}; + using TextureMap = std::unordered_map<uint32_t, sk_sp<SkImage>>; struct PictureCaptureState { @@ -633,19 +659,45 @@ static void android_view_ThreadedRenderer_setASurfaceTransactionCallback( } else { JavaVM* vm = nullptr; LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM"); - auto globalCallbackRef = std::make_shared<JGlobalRefHolder>( - vm, env->NewGlobalRef(aSurfaceTransactionCallback)); + auto globalCallbackRef = + std::make_shared<JWeakGlobalRefHolder>(vm, aSurfaceTransactionCallback); proxy->setASurfaceTransactionCallback( [globalCallbackRef](int64_t transObj, int64_t scObj, int64_t frameNr) { JNIEnv* env = getenv(globalCallbackRef->vm()); - env->CallVoidMethod(globalCallbackRef->object(), - gASurfaceTransactionCallback.onMergeTransaction, + jobject localref = env->NewLocalRef(globalCallbackRef->ref()); + if (CC_UNLIKELY(!localref)) { + return; + } + env->CallVoidMethod(localref, gASurfaceTransactionCallback.onMergeTransaction, static_cast<jlong>(transObj), static_cast<jlong>(scObj), static_cast<jlong>(frameNr)); + env->DeleteLocalRef(localref); }); } } +static void android_view_ThreadedRenderer_setPrepareSurfaceControlForWebviewCallback( + JNIEnv* env, jobject clazz, jlong proxyPtr, jobject callback) { + RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); + if (!callback) { + proxy->setPrepareSurfaceControlForWebviewCallback(nullptr); + } else { + JavaVM* vm = nullptr; + LOG_ALWAYS_FATAL_IF(env->GetJavaVM(&vm) != JNI_OK, "Unable to get Java VM"); + auto globalCallbackRef = + std::make_shared<JWeakGlobalRefHolder>(vm, callback); + proxy->setPrepareSurfaceControlForWebviewCallback([globalCallbackRef]() { + JNIEnv* env = getenv(globalCallbackRef->vm()); + jobject localref = env->NewLocalRef(globalCallbackRef->ref()); + if (CC_UNLIKELY(!localref)) { + return; + } + env->CallVoidMethod(localref, gPrepareSurfaceControlForWebviewCallback.prepare); + env->DeleteLocalRef(localref); + }); + } +} + static void android_view_ThreadedRenderer_setFrameCallback(JNIEnv* env, jobject clazz, jlong proxyPtr, jobject frameCallback) { RenderProxy* proxy = reinterpret_cast<RenderProxy*>(proxyPtr); @@ -944,6 +996,9 @@ static const JNINativeMethod gMethods[] = { {"nSetASurfaceTransactionCallback", "(JLandroid/graphics/HardwareRenderer$ASurfaceTransactionCallback;)V", (void*)android_view_ThreadedRenderer_setASurfaceTransactionCallback}, + {"nSetPrepareSurfaceControlForWebviewCallback", + "(JLandroid/graphics/HardwareRenderer$PrepareSurfaceControlForWebviewCallback;)V", + (void*)android_view_ThreadedRenderer_setPrepareSurfaceControlForWebviewCallback}, {"nSetFrameCallback", "(JLandroid/graphics/HardwareRenderer$FrameDrawingCallback;)V", (void*)android_view_ThreadedRenderer_setFrameCallback}, {"nSetFrameCompleteCallback", @@ -1011,6 +1066,11 @@ int register_android_view_ThreadedRenderer(JNIEnv* env) { gASurfaceTransactionCallback.onMergeTransaction = GetMethodIDOrDie(env, aSurfaceTransactionCallbackClass, "onMergeTransaction", "(JJJ)V"); + jclass prepareSurfaceControlForWebviewCallbackClass = FindClassOrDie( + env, "android/graphics/HardwareRenderer$PrepareSurfaceControlForWebviewCallback"); + gPrepareSurfaceControlForWebviewCallback.prepare = + GetMethodIDOrDie(env, prepareSurfaceControlForWebviewCallbackClass, "prepare", "()V"); + jclass frameCallbackClass = FindClassOrDie(env, "android/graphics/HardwareRenderer$FrameDrawingCallback"); gFrameDrawingCallback.onFrameDraw = GetMethodIDOrDie(env, frameCallbackClass, diff --git a/libs/hwui/renderthread/CanvasContext.cpp b/libs/hwui/renderthread/CanvasContext.cpp index 8bfc2c14ad7c..44d0038ad47e 100644 --- a/libs/hwui/renderthread/CanvasContext.cpp +++ b/libs/hwui/renderthread/CanvasContext.cpp @@ -174,16 +174,12 @@ void CanvasContext::setSurface(ANativeWindow* window, bool enableTimeout) { ATRACE_CALL(); if (window) { - int extraBuffers = 0; - native_window_get_extra_buffer_count(window, &extraBuffers); - mNativeSurface = std::make_unique<ReliableSurface>(window); mNativeSurface->init(); if (enableTimeout) { // TODO: Fix error handling & re-shorten timeout ANativeWindow_setDequeueTimeout(window, 4000_ms); } - mNativeSurface->setExtraBufferCount(extraBuffers); } else { mNativeSurface = nullptr; } @@ -197,6 +193,7 @@ void CanvasContext::setSurfaceControl(ASurfaceControl* surfaceControl) { if (surfaceControl == nullptr) { setASurfaceTransactionCallback(nullptr); + setPrepareSurfaceControlForWebviewCallback(nullptr); } if (mSurfaceControl != nullptr) { @@ -918,6 +915,12 @@ bool CanvasContext::mergeTransaction(ASurfaceTransaction* transaction, ASurfaceC return true; } +void CanvasContext::prepareSurfaceControlForWebview() { + if (mPrepareSurfaceControlForWebviewCallback) { + std::invoke(mPrepareSurfaceControlForWebviewCallback); + } +} + } /* namespace renderthread */ } /* namespace uirenderer */ } /* namespace android */ diff --git a/libs/hwui/renderthread/CanvasContext.h b/libs/hwui/renderthread/CanvasContext.h index 4bdc2514db8c..a61c2bfd5c01 100644 --- a/libs/hwui/renderthread/CanvasContext.h +++ b/libs/hwui/renderthread/CanvasContext.h @@ -212,6 +212,12 @@ public: bool mergeTransaction(ASurfaceTransaction* transaction, ASurfaceControl* control); + void setPrepareSurfaceControlForWebviewCallback(const std::function<void()>& callback) { + mPrepareSurfaceControlForWebviewCallback = callback; + } + + void prepareSurfaceControlForWebview(); + static CanvasContext* getActiveContext(); private: @@ -312,6 +318,8 @@ private: bool mExpectSurfaceStats = false; std::function<void(int64_t, int64_t, int64_t)> mASurfaceTransactionCallback; + std::function<void()> mPrepareSurfaceControlForWebviewCallback; + void cleanupResources(); }; diff --git a/libs/hwui/renderthread/ReliableSurface.cpp b/libs/hwui/renderthread/ReliableSurface.cpp index c29cc11fa7ea..6df34bee2224 100644 --- a/libs/hwui/renderthread/ReliableSurface.cpp +++ b/libs/hwui/renderthread/ReliableSurface.cpp @@ -278,7 +278,6 @@ int ReliableSurface::hook_query(const ANativeWindow *window, ANativeWindow_query int result = query(window, what, value); if (what == ANATIVEWINDOW_QUERY_MIN_UNDEQUEUED_BUFFERS && result == OK) { std::lock_guard _lock{rs->mMutex}; - *value += rs->mExtraBuffers; rs->mExpectedBufferCount = *value + 2; } return result; diff --git a/libs/hwui/renderthread/ReliableSurface.h b/libs/hwui/renderthread/ReliableSurface.h index 41969e776fc8..595964741049 100644 --- a/libs/hwui/renderthread/ReliableSurface.h +++ b/libs/hwui/renderthread/ReliableSurface.h @@ -51,11 +51,6 @@ public: return ret; } - void setExtraBufferCount(size_t extraBuffers) { - std::lock_guard _lock{mMutex}; - mExtraBuffers = extraBuffers; - } - bool didSetExtraBuffers() const { std::lock_guard _lock{mMutex}; return mDidSetExtraBuffers; @@ -73,7 +68,6 @@ private: base::unique_fd mReservedFenceFd; bool mHasDequeuedBuffer = false; int mBufferQueueState = OK; - size_t mExtraBuffers = 0; size_t mExpectedBufferCount = 0; bool mDidSetExtraBuffers = false; diff --git a/libs/hwui/renderthread/RenderProxy.cpp b/libs/hwui/renderthread/RenderProxy.cpp index 6fd644bfa28e..c47050c31e9a 100644 --- a/libs/hwui/renderthread/RenderProxy.cpp +++ b/libs/hwui/renderthread/RenderProxy.cpp @@ -319,6 +319,12 @@ void RenderProxy::setASurfaceTransactionCallback( [this, cb = callback]() { mContext->setASurfaceTransactionCallback(cb); }); } +void RenderProxy::setPrepareSurfaceControlForWebviewCallback( + const std::function<void()>& callback) { + mRenderThread.queue().post( + [this, cb = callback]() { mContext->setPrepareSurfaceControlForWebviewCallback(cb); }); +} + void RenderProxy::setFrameCallback(std::function<void(int64_t)>&& callback) { mDrawFrameTask.setFrameCallback(std::move(callback)); } diff --git a/libs/hwui/renderthread/RenderProxy.h b/libs/hwui/renderthread/RenderProxy.h index 6d80949a4eba..d575aa77e4ab 100644 --- a/libs/hwui/renderthread/RenderProxy.h +++ b/libs/hwui/renderthread/RenderProxy.h @@ -124,6 +124,7 @@ public: void setPictureCapturedCallback(const std::function<void(sk_sp<SkPicture>&&)>& callback); void setASurfaceTransactionCallback( const std::function<void(int64_t, int64_t, int64_t)>& callback); + void setPrepareSurfaceControlForWebviewCallback(const std::function<void()>& callback); void setFrameCallback(std::function<void(int64_t)>&& callback); void setFrameCompleteCallback(std::function<void(int64_t)>&& callback); diff --git a/libs/hwui/renderthread/RenderThread.cpp b/libs/hwui/renderthread/RenderThread.cpp index 4ba774801bba..524407d2b9b0 100644 --- a/libs/hwui/renderthread/RenderThread.cpp +++ b/libs/hwui/renderthread/RenderThread.cpp @@ -102,6 +102,10 @@ ASurfaceControlFunctions::ASurfaceControlFunctions() { (AST_setVisibility)dlsym(handle_, "ASurfaceTransaction_setVisibility"); LOG_ALWAYS_FATAL_IF(transactionSetVisibilityFunc == nullptr, "Failed to find required symbol ASurfaceTransaction_setVisibility!"); + + transactionSetZOrderFunc = (AST_setZOrder)dlsym(handle_, "ASurfaceTransaction_setZOrder"); + LOG_ALWAYS_FATAL_IF(transactionSetZOrderFunc == nullptr, + "Failed to find required symbol ASurfaceTransaction_setZOrder!"); } void RenderThread::frameCallback(int64_t frameTimeNanos, void* data) { diff --git a/libs/hwui/renderthread/RenderThread.h b/libs/hwui/renderthread/RenderThread.h index dd060097ddf7..c5e3746587b8 100644 --- a/libs/hwui/renderthread/RenderThread.h +++ b/libs/hwui/renderthread/RenderThread.h @@ -96,6 +96,8 @@ typedef void (*AST_delete)(ASurfaceTransaction* transaction); typedef void (*AST_apply)(ASurfaceTransaction* transaction); typedef void (*AST_setVisibility)(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, int8_t visibility); +typedef void (*AST_setZOrder)(ASurfaceTransaction* transaction, ASurfaceControl* surface_control, + int32_t z_order); struct ASurfaceControlFunctions { ASurfaceControlFunctions(); @@ -112,6 +114,7 @@ struct ASurfaceControlFunctions { AST_delete transactionDeleteFunc; AST_apply transactionApplyFunc; AST_setVisibility transactionSetVisibilityFunc; + AST_setZOrder transactionSetZOrderFunc; }; class ChoreographerSource; |