| /* |
| * Copyright 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.systemui.shared.rotation; |
| |
| import static android.content.pm.PackageManager.FEATURE_PC; |
| import static android.view.Display.DEFAULT_DISPLAY; |
| |
| import static com.android.internal.view.RotationPolicy.NATURAL_ROTATION; |
| import static com.android.systemui.shared.system.QuickStepContract.isGesturalMode; |
| |
| import android.animation.Animator; |
| import android.animation.AnimatorListenerAdapter; |
| import android.animation.ObjectAnimator; |
| import android.annotation.ColorInt; |
| import android.annotation.DrawableRes; |
| import android.annotation.SuppressLint; |
| import android.app.StatusBarManager; |
| import android.content.ContentResolver; |
| import android.content.Context; |
| import android.graphics.drawable.AnimatedVectorDrawable; |
| import android.graphics.drawable.Drawable; |
| import android.os.Handler; |
| import android.os.Looper; |
| import android.os.RemoteException; |
| import android.provider.Settings; |
| import android.util.Log; |
| import android.view.HapticFeedbackConstants; |
| import android.view.IRotationWatcher; |
| import android.view.MotionEvent; |
| import android.view.Surface; |
| import android.view.View; |
| import android.view.WindowInsetsController; |
| import android.view.WindowManagerGlobal; |
| import android.view.accessibility.AccessibilityManager; |
| import android.view.animation.Interpolator; |
| import android.view.animation.LinearInterpolator; |
| |
| import com.android.internal.logging.UiEvent; |
| import com.android.internal.logging.UiEventLogger; |
| import com.android.internal.logging.UiEventLoggerImpl; |
| import com.android.internal.view.RotationPolicy; |
| import com.android.systemui.shared.recents.utilities.Utilities; |
| import com.android.systemui.shared.recents.utilities.ViewRippler; |
| import com.android.systemui.shared.rotation.RotationButton.RotationButtonUpdatesCallback; |
| import com.android.systemui.shared.system.ActivityManagerWrapper; |
| import com.android.systemui.shared.system.TaskStackChangeListener; |
| import com.android.systemui.shared.system.TaskStackChangeListeners; |
| |
| import java.io.PrintWriter; |
| import java.util.Optional; |
| import java.util.function.Consumer; |
| import java.util.function.Supplier; |
| |
| /** |
| * Contains logic that deals with showing a rotate suggestion button with animation. |
| */ |
| public class RotationButtonController { |
| |
| private static final String TAG = "StatusBar/RotationButtonController"; |
| private static final int BUTTON_FADE_IN_OUT_DURATION_MS = 100; |
| private static final int NAVBAR_HIDDEN_PENDING_ICON_TIMEOUT_MS = 20000; |
| private static final Interpolator LINEAR_INTERPOLATOR = new LinearInterpolator(); |
| |
| private static final int NUM_ACCEPTED_ROTATION_SUGGESTIONS_FOR_INTRODUCTION = 3; |
| |
| private final Context mContext; |
| private final Handler mMainThreadHandler = new Handler(Looper.getMainLooper()); |
| private final UiEventLogger mUiEventLogger = new UiEventLoggerImpl(); |
| private final ViewRippler mViewRippler = new ViewRippler(); |
| private final Supplier<Integer> mWindowRotationProvider; |
| private RotationButton mRotationButton; |
| |
| private boolean mIsRecentsAnimationRunning; |
| private boolean mHomeRotationEnabled; |
| private int mLastRotationSuggestion; |
| private boolean mPendingRotationSuggestion; |
| private boolean mHoveringRotationSuggestion; |
| private final AccessibilityManager mAccessibilityManager; |
| private final TaskStackListenerImpl mTaskStackListener; |
| private Consumer<Integer> mRotWatcherListener; |
| |
| private boolean mListenersRegistered = false; |
| private boolean mIsNavigationBarShowing; |
| @SuppressLint("InlinedApi") |
| private @WindowInsetsController.Behavior |
| int mBehavior = WindowInsetsController.BEHAVIOR_DEFAULT; |
| private int mNavBarMode; |
| private boolean mSkipOverrideUserLockPrefsOnce; |
| private final int mLightIconColor; |
| private final int mDarkIconColor; |
| |
| @DrawableRes |
| private final int mIconCcwStart0ResId; |
| @DrawableRes |
| private final int mIconCcwStart90ResId; |
| @DrawableRes |
| private final int mIconCwStart0ResId; |
| @DrawableRes |
| private final int mIconCwStart90ResId; |
| |
| @DrawableRes |
| private int mIconResId; |
| |
| private final Runnable mRemoveRotationProposal = |
| () -> setRotateSuggestionButtonState(false /* visible */); |
| private final Runnable mCancelPendingRotationProposal = |
| () -> mPendingRotationSuggestion = false; |
| private Animator mRotateHideAnimator; |
| |
| |
| private final IRotationWatcher.Stub mRotationWatcher = new IRotationWatcher.Stub() { |
| @Override |
| public void onRotationChanged(final int rotation) { |
| // We need this to be scheduled as early as possible to beat the redrawing of |
| // window in response to the orientation change. |
| mMainThreadHandler.postAtFrontOfQueue(() -> { |
| // If the screen rotation changes while locked, potentially update lock to flow with |
| // new screen rotation and hide any showing suggestions. |
| if (isRotationLocked()) { |
| if (shouldOverrideUserLockPrefs(rotation)) { |
| setRotationLockedAtAngle(rotation); |
| } |
| setRotateSuggestionButtonState(false /* visible */, true /* forced */); |
| } |
| |
| if (mRotWatcherListener != null) { |
| mRotWatcherListener.accept(rotation); |
| } |
| }); |
| } |
| }; |
| |
| /** |
| * Determines if rotation suggestions disabled2 flag exists in flag |
| * |
| * @param disable2Flags see if rotation suggestion flag exists in this flag |
| * @return whether flag exists |
| */ |
| public static boolean hasDisable2RotateSuggestionFlag(int disable2Flags) { |
| return (disable2Flags & StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS) != 0; |
| } |
| |
| public RotationButtonController(Context context, |
| @ColorInt int lightIconColor, @ColorInt int darkIconColor, |
| @DrawableRes int iconCcwStart0ResId, |
| @DrawableRes int iconCcwStart90ResId, |
| @DrawableRes int iconCwStart0ResId, |
| @DrawableRes int iconCwStart90ResId, |
| Supplier<Integer> windowRotationProvider) { |
| |
| mContext = context; |
| mLightIconColor = lightIconColor; |
| mDarkIconColor = darkIconColor; |
| |
| mIconCcwStart0ResId = iconCcwStart0ResId; |
| mIconCcwStart90ResId = iconCcwStart90ResId; |
| mIconCwStart0ResId = iconCwStart0ResId; |
| mIconCwStart90ResId = iconCwStart90ResId; |
| mIconResId = mIconCcwStart90ResId; |
| |
| mAccessibilityManager = AccessibilityManager.getInstance(context); |
| mTaskStackListener = new TaskStackListenerImpl(); |
| mWindowRotationProvider = windowRotationProvider; |
| } |
| |
| public void setRotationButton(RotationButton rotationButton, |
| RotationButtonUpdatesCallback updatesCallback) { |
| mRotationButton = rotationButton; |
| mRotationButton.setRotationButtonController(this); |
| mRotationButton.setOnClickListener(this::onRotateSuggestionClick); |
| mRotationButton.setOnHoverListener(this::onRotateSuggestionHover); |
| mRotationButton.setUpdatesCallback(updatesCallback); |
| } |
| |
| public Context getContext() { |
| return mContext; |
| } |
| |
| public void init() { |
| registerListeners(); |
| if (mContext.getDisplay().getDisplayId() != DEFAULT_DISPLAY) { |
| // Currently there is no accelerometer sensor on non-default display, disable fixed |
| // rotation for non-default display |
| onDisable2FlagChanged(StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS); |
| } |
| } |
| |
| public void onDestroy() { |
| unregisterListeners(); |
| } |
| |
| public void registerListeners() { |
| if (mListenersRegistered || getContext().getPackageManager().hasSystemFeature(FEATURE_PC)) { |
| return; |
| } |
| |
| mListenersRegistered = true; |
| try { |
| WindowManagerGlobal.getWindowManagerService() |
| .watchRotation(mRotationWatcher, DEFAULT_DISPLAY); |
| } catch (IllegalArgumentException e) { |
| mListenersRegistered = false; |
| Log.w(TAG, "RegisterListeners for the display failed"); |
| } catch (RemoteException e) { |
| Log.e(TAG, "RegisterListeners caught a RemoteException", e); |
| return; |
| } |
| |
| TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener); |
| } |
| |
| public void unregisterListeners() { |
| if (!mListenersRegistered) { |
| return; |
| } |
| |
| mListenersRegistered = false; |
| try { |
| WindowManagerGlobal.getWindowManagerService().removeRotationWatcher(mRotationWatcher); |
| } catch (RemoteException e) { |
| Log.e(TAG, "UnregisterListeners caught a RemoteException", e); |
| return; |
| } |
| |
| TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener); |
| } |
| |
| public void setRotationCallback(Consumer<Integer> watcher) { |
| mRotWatcherListener = watcher; |
| } |
| |
| public void setRotationLockedAtAngle(int rotationSuggestion) { |
| RotationPolicy.setRotationLockAtAngle(mContext, true, rotationSuggestion); |
| } |
| |
| public boolean isRotationLocked() { |
| return RotationPolicy.isRotationLocked(mContext); |
| } |
| |
| public void setRotateSuggestionButtonState(boolean visible) { |
| setRotateSuggestionButtonState(visible, false /* force */); |
| } |
| |
| void setRotateSuggestionButtonState(final boolean visible, final boolean force) { |
| // At any point the button can become invisible because an a11y service became active. |
| // Similarly, a call to make the button visible may be rejected because an a11y service is |
| // active. Must account for this. |
| // Rerun a show animation to indicate change but don't rerun a hide animation |
| if (!visible && !mRotationButton.isVisible()) return; |
| |
| final View view = mRotationButton.getCurrentView(); |
| if (view == null) return; |
| |
| final Drawable currentDrawable = mRotationButton.getImageDrawable(); |
| if (currentDrawable == null) return; |
| |
| // Clear any pending suggestion flag as it has either been nullified or is being shown |
| mPendingRotationSuggestion = false; |
| mMainThreadHandler.removeCallbacks(mCancelPendingRotationProposal); |
| |
| // Handle the visibility change and animation |
| if (visible) { // Appear and change (cannot force) |
| // Stop and clear any currently running hide animations |
| if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) { |
| mRotateHideAnimator.cancel(); |
| } |
| mRotateHideAnimator = null; |
| |
| // Reset the alpha if any has changed due to hide animation |
| view.setAlpha(1f); |
| |
| // Run the rotate icon's animation if it has one |
| if (currentDrawable instanceof AnimatedVectorDrawable) { |
| ((AnimatedVectorDrawable) currentDrawable).reset(); |
| ((AnimatedVectorDrawable) currentDrawable).start(); |
| } |
| |
| // TODO(b/187754252): No idea why this doesn't work. If we remove the "false" |
| // we see the animation show the pressed state... but it only shows the first time. |
| if (!isRotateSuggestionIntroduced()) mViewRippler.start(view); |
| |
| // Set visibility unless a11y service is active. |
| mRotationButton.show(); |
| } else { // Hide |
| mViewRippler.stop(); // Prevent any pending ripples, force hide or not |
| |
| if (force) { |
| // If a hide animator is running stop it and make invisible |
| if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) { |
| mRotateHideAnimator.pause(); |
| } |
| mRotationButton.hide(); |
| return; |
| } |
| |
| // Don't start any new hide animations if one is running |
| if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) return; |
| |
| ObjectAnimator fadeOut = ObjectAnimator.ofFloat(view, "alpha", 0f); |
| fadeOut.setDuration(BUTTON_FADE_IN_OUT_DURATION_MS); |
| fadeOut.setInterpolator(LINEAR_INTERPOLATOR); |
| fadeOut.addListener(new AnimatorListenerAdapter() { |
| @Override |
| public void onAnimationEnd(Animator animation) { |
| mRotationButton.hide(); |
| } |
| }); |
| |
| mRotateHideAnimator = fadeOut; |
| fadeOut.start(); |
| } |
| } |
| |
| public void setDarkIntensity(float darkIntensity) { |
| mRotationButton.setDarkIntensity(darkIntensity); |
| } |
| |
| public void setRecentsAnimationRunning(boolean running) { |
| mIsRecentsAnimationRunning = running; |
| updateRotationButtonStateInOverview(); |
| } |
| |
| public void setHomeRotationEnabled(boolean enabled) { |
| mHomeRotationEnabled = enabled; |
| updateRotationButtonStateInOverview(); |
| } |
| |
| private void updateRotationButtonStateInOverview() { |
| if (mIsRecentsAnimationRunning && !mHomeRotationEnabled) { |
| setRotateSuggestionButtonState(false, true /* hideImmediately */); |
| } |
| } |
| |
| public void onRotationProposal(int rotation, boolean isValid) { |
| int windowRotation = mWindowRotationProvider.get(); |
| |
| if (!mRotationButton.acceptRotationProposal()) { |
| return; |
| } |
| |
| if (!mHomeRotationEnabled && mIsRecentsAnimationRunning) { |
| return; |
| } |
| |
| // This method will be called on rotation suggestion changes even if the proposed rotation |
| // is not valid for the top app. Use invalid rotation choices as a signal to remove the |
| // rotate button if shown. |
| if (!isValid) { |
| setRotateSuggestionButtonState(false /* visible */); |
| return; |
| } |
| |
| // If window rotation matches suggested rotation, remove any current suggestions |
| if (rotation == windowRotation) { |
| mMainThreadHandler.removeCallbacks(mRemoveRotationProposal); |
| setRotateSuggestionButtonState(false /* visible */); |
| return; |
| } |
| |
| // Prepare to show the navbar icon by updating the icon style to change anim params |
| mLastRotationSuggestion = rotation; // Remember rotation for click |
| final boolean rotationCCW = Utilities.isRotationAnimationCCW(windowRotation, rotation); |
| if (windowRotation == Surface.ROTATION_0 || windowRotation == Surface.ROTATION_180) { |
| mIconResId = rotationCCW ? mIconCcwStart0ResId : mIconCwStart0ResId; |
| } else { // 90 or 270 |
| mIconResId = rotationCCW ? mIconCcwStart90ResId : mIconCwStart90ResId; |
| } |
| mRotationButton.updateIcon(mLightIconColor, mDarkIconColor); |
| |
| if (canShowRotationButton()) { |
| // The navbar is visible / it's in visual immersive mode, so show the icon right away |
| showAndLogRotationSuggestion(); |
| } else { |
| // If the navbar isn't shown, flag the rotate icon to be shown should the navbar become |
| // visible given some time limit. |
| mPendingRotationSuggestion = true; |
| mMainThreadHandler.removeCallbacks(mCancelPendingRotationProposal); |
| mMainThreadHandler.postDelayed(mCancelPendingRotationProposal, |
| NAVBAR_HIDDEN_PENDING_ICON_TIMEOUT_MS); |
| } |
| } |
| |
| public void onDisable2FlagChanged(int state2) { |
| final boolean rotateSuggestionsDisabled = hasDisable2RotateSuggestionFlag(state2); |
| if (rotateSuggestionsDisabled) onRotationSuggestionsDisabled(); |
| } |
| |
| public void onNavigationModeChanged(int mode) { |
| mNavBarMode = mode; |
| } |
| |
| public void onBehaviorChanged(int displayId, @WindowInsetsController.Behavior int behavior) { |
| if (DEFAULT_DISPLAY != displayId) { |
| return; |
| } |
| |
| if (mBehavior != behavior) { |
| mBehavior = behavior; |
| showPendingRotationButtonIfNeeded(); |
| } |
| } |
| |
| public void onNavigationBarWindowVisibilityChange(boolean showing) { |
| if (mIsNavigationBarShowing != showing) { |
| mIsNavigationBarShowing = showing; |
| showPendingRotationButtonIfNeeded(); |
| } |
| } |
| |
| public void onTaskbarStateChange(boolean visible, boolean stashed) { |
| if (getRotationButton() == null) { |
| return; |
| } |
| getRotationButton().onTaskbarStateChanged(visible, stashed); |
| } |
| |
| private void showPendingRotationButtonIfNeeded() { |
| if (canShowRotationButton() && mPendingRotationSuggestion) { |
| showAndLogRotationSuggestion(); |
| } |
| } |
| |
| /** |
| * Return true when either the task bar is visible or it's in visual immersive mode. |
| */ |
| @SuppressLint("InlinedApi") |
| private boolean canShowRotationButton() { |
| return mIsNavigationBarShowing || mBehavior == WindowInsetsController.BEHAVIOR_DEFAULT |
| || isGesturalMode(mNavBarMode); |
| } |
| |
| @DrawableRes |
| public int getIconResId() { |
| return mIconResId; |
| } |
| |
| @ColorInt |
| public int getLightIconColor() { |
| return mLightIconColor; |
| } |
| |
| @ColorInt |
| public int getDarkIconColor() { |
| return mDarkIconColor; |
| } |
| |
| public void dumpLogs(String prefix, PrintWriter pw) { |
| pw.println(prefix + "RotationButtonController:"); |
| |
| pw.println(String.format( |
| "%s\tmIsRecentsAnimationRunning=%b", prefix, mIsRecentsAnimationRunning)); |
| pw.println(String.format("%s\tmHomeRotationEnabled=%b", prefix, mHomeRotationEnabled)); |
| pw.println(String.format( |
| "%s\tmLastRotationSuggestion=%d", prefix, mLastRotationSuggestion)); |
| pw.println(String.format( |
| "%s\tmPendingRotationSuggestion=%b", prefix, mPendingRotationSuggestion)); |
| pw.println(String.format( |
| "%s\tmHoveringRotationSuggestion=%b", prefix, mHoveringRotationSuggestion)); |
| pw.println(String.format("%s\tmListenersRegistered=%b", prefix, mListenersRegistered)); |
| pw.println(String.format( |
| "%s\tmIsNavigationBarShowing=%b", prefix, mIsNavigationBarShowing)); |
| pw.println(String.format("%s\tmBehavior=%d", prefix, mBehavior)); |
| pw.println(String.format( |
| "%s\tmSkipOverrideUserLockPrefsOnce=%b", prefix, mSkipOverrideUserLockPrefsOnce)); |
| pw.println(String.format( |
| "%s\tmLightIconColor=0x%s", prefix, Integer.toHexString(mLightIconColor))); |
| pw.println(String.format( |
| "%s\tmDarkIconColor=0x%s", prefix, Integer.toHexString(mDarkIconColor))); |
| } |
| |
| public RotationButton getRotationButton() { |
| return mRotationButton; |
| } |
| |
| private void onRotateSuggestionClick(View v) { |
| mUiEventLogger.log(RotationButtonEvent.ROTATION_SUGGESTION_ACCEPTED); |
| incrementNumAcceptedRotationSuggestionsIfNeeded(); |
| setRotationLockedAtAngle(mLastRotationSuggestion); |
| v.performHapticFeedback(HapticFeedbackConstants.VIRTUAL_KEY); |
| } |
| |
| private boolean onRotateSuggestionHover(View v, MotionEvent event) { |
| final int action = event.getActionMasked(); |
| mHoveringRotationSuggestion = (action == MotionEvent.ACTION_HOVER_ENTER) |
| || (action == MotionEvent.ACTION_HOVER_MOVE); |
| rescheduleRotationTimeout(true /* reasonHover */); |
| return false; // Must return false so a11y hover events are dispatched correctly. |
| } |
| |
| private void onRotationSuggestionsDisabled() { |
| // Immediately hide the rotate button and clear any planned removal |
| setRotateSuggestionButtonState(false /* visible */, true /* force */); |
| mMainThreadHandler.removeCallbacks(mRemoveRotationProposal); |
| } |
| |
| private void showAndLogRotationSuggestion() { |
| setRotateSuggestionButtonState(true /* visible */); |
| rescheduleRotationTimeout(false /* reasonHover */); |
| mUiEventLogger.log(RotationButtonEvent.ROTATION_SUGGESTION_SHOWN); |
| } |
| |
| /** |
| * Makes {@link #shouldOverrideUserLockPrefs} always return {@code false} once. It is used to |
| * avoid losing original user rotation when display rotation is changed by entering the fixed |
| * orientation overview. |
| */ |
| public void setSkipOverrideUserLockPrefsOnce() { |
| // If live-tile is enabled (recents animation keeps running in overview), there is no |
| // activity switch so the display rotation is not changed, then it is no need to skip. |
| mSkipOverrideUserLockPrefsOnce = !mIsRecentsAnimationRunning; |
| } |
| |
| private boolean shouldOverrideUserLockPrefs(final int rotation) { |
| if (mSkipOverrideUserLockPrefsOnce) { |
| mSkipOverrideUserLockPrefsOnce = false; |
| return false; |
| } |
| // Only override user prefs when returning to the natural rotation (normally portrait). |
| // Don't let apps that force landscape or 180 alter user lock. |
| return rotation == NATURAL_ROTATION; |
| } |
| |
| private void rescheduleRotationTimeout(final boolean reasonHover) { |
| // May be called due to a new rotation proposal or a change in hover state |
| if (reasonHover) { |
| // Don't reschedule if a hide animator is running |
| if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) return; |
| // Don't reschedule if not visible |
| if (!mRotationButton.isVisible()) return; |
| } |
| |
| // Stop any pending removal |
| mMainThreadHandler.removeCallbacks(mRemoveRotationProposal); |
| // Schedule timeout |
| mMainThreadHandler.postDelayed(mRemoveRotationProposal, |
| computeRotationProposalTimeout()); |
| } |
| |
| private int computeRotationProposalTimeout() { |
| return mAccessibilityManager.getRecommendedTimeoutMillis( |
| mHoveringRotationSuggestion ? 16000 : 5000, |
| AccessibilityManager.FLAG_CONTENT_CONTROLS); |
| } |
| |
| private boolean isRotateSuggestionIntroduced() { |
| ContentResolver cr = mContext.getContentResolver(); |
| return Settings.Secure.getInt(cr, Settings.Secure.NUM_ROTATION_SUGGESTIONS_ACCEPTED, 0) |
| >= NUM_ACCEPTED_ROTATION_SUGGESTIONS_FOR_INTRODUCTION; |
| } |
| |
| private void incrementNumAcceptedRotationSuggestionsIfNeeded() { |
| // Get the number of accepted suggestions |
| ContentResolver cr = mContext.getContentResolver(); |
| final int numSuggestions = Settings.Secure.getInt(cr, |
| Settings.Secure.NUM_ROTATION_SUGGESTIONS_ACCEPTED, 0); |
| |
| // Increment the number of accepted suggestions only if it would change intro mode |
| if (numSuggestions < NUM_ACCEPTED_ROTATION_SUGGESTIONS_FOR_INTRODUCTION) { |
| Settings.Secure.putInt(cr, Settings.Secure.NUM_ROTATION_SUGGESTIONS_ACCEPTED, |
| numSuggestions + 1); |
| } |
| } |
| |
| private class TaskStackListenerImpl implements TaskStackChangeListener { |
| // Invalidate any rotation suggestion on task change or activity orientation change |
| // Note: all callbacks happen on main thread |
| |
| @Override |
| public void onTaskStackChanged() { |
| setRotateSuggestionButtonState(false /* visible */); |
| } |
| |
| @Override |
| public void onTaskRemoved(int taskId) { |
| setRotateSuggestionButtonState(false /* visible */); |
| } |
| |
| @Override |
| public void onTaskMovedToFront(int taskId) { |
| setRotateSuggestionButtonState(false /* visible */); |
| } |
| |
| @Override |
| public void onActivityRequestedOrientationChanged(int taskId, int requestedOrientation) { |
| // Only hide the icon if the top task changes its requestedOrientation |
| // Launcher can alter its requestedOrientation while it's not on top, don't hide on this |
| Optional.ofNullable(ActivityManagerWrapper.getInstance()) |
| .map(ActivityManagerWrapper::getRunningTask) |
| .ifPresent(a -> { |
| if (a.id == taskId) setRotateSuggestionButtonState(false /* visible */); |
| }); |
| } |
| } |
| |
| enum RotationButtonEvent implements UiEventLogger.UiEventEnum { |
| @UiEvent(doc = "The rotation button was shown") |
| ROTATION_SUGGESTION_SHOWN(206), |
| @UiEvent(doc = "The rotation button was clicked") |
| ROTATION_SUGGESTION_ACCEPTED(207); |
| |
| private final int mId; |
| |
| RotationButtonEvent(int id) { |
| mId = id; |
| } |
| |
| @Override |
| public int getId() { |
| return mId; |
| } |
| } |
| } |
| |