Move floating rotation button handling to Launcher
Moves handling of floating rotation button when navigation bar is not created to the launcher. This button was not showing when taskbar is visible as it was initialized in navigation bar (which is not created for large screens). Bug: 200103245 Test: rotate phone when autorotate disabled on inner screen Test: showing rotate suggestion when gesture nav enabled/disabled Change-Id: I0619acd9d24eb4ba36bdb601517f9a8370ee999a
This commit is contained in:
@@ -81,7 +81,6 @@
|
||||
<dimen name="fab_margin">24dp</dimen>
|
||||
|
||||
<dimen name="navigation_key_width">128dp</dimen>
|
||||
<dimen name="navigation_key_padding">25dp</dimen>
|
||||
|
||||
<!-- Keyboard shortcuts helper -->
|
||||
<dimen name="ksh_layout_width">488dp</dimen>
|
||||
|
||||
@@ -21,11 +21,4 @@
|
||||
<dimen name="navigation_side_padding">@dimen/button_size</dimen>
|
||||
<dimen name="navigation_key_width">@dimen/button_size</dimen>
|
||||
<dimen name="navigation_extra_key_width">@dimen/button_size</dimen>
|
||||
|
||||
<!-- The maximum width of the navigation bar ripples. -->
|
||||
<dimen name="key_button_ripple_max_width">76dp</dimen>
|
||||
|
||||
<!-- The padding around the navigation buttons -->
|
||||
<dimen name="navigation_key_padding">0dp</dimen>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -56,11 +56,6 @@
|
||||
<!-- The amount by which the arrow is shifted to avoid the finger-->
|
||||
<dimen name="navigation_edge_finger_offset">48dp</dimen>
|
||||
|
||||
<dimen name="floating_rotation_button_diameter">40dp</dimen>
|
||||
<dimen name="floating_rotation_button_min_margin">20dp</dimen>
|
||||
<dimen name="floating_rotation_button_taskbar_left_margin">20dp</dimen>
|
||||
<dimen name="floating_rotation_button_taskbar_bottom_margin">10dp</dimen>
|
||||
|
||||
<!-- Height of notification icons in the status bar -->
|
||||
<dimen name="status_bar_icon_size">@*android:dimen/status_bar_icon_size</dimen>
|
||||
|
||||
@@ -358,8 +353,6 @@
|
||||
<!-- The width/height of the icon of a navigation button -->
|
||||
<dimen name="navigation_icon_size">32dp</dimen>
|
||||
|
||||
<dimen name="navigation_key_padding">0dp</dimen>
|
||||
|
||||
<!-- The width of the view containing the menu/ime navigation bar icons -->
|
||||
<dimen name="navigation_extra_key_width">36dp</dimen>
|
||||
|
||||
@@ -971,9 +964,6 @@
|
||||
|
||||
<dimen name="signal_indicator_to_icon_frame_spacing">3dp</dimen>
|
||||
|
||||
<!-- The maximum width of the navigation bar ripples. -->
|
||||
<dimen name="key_button_ripple_max_width">95dp</dimen>
|
||||
|
||||
<!-- Inset shadow for FakeShadowDrawable. It is used to avoid gaps between the card
|
||||
and the shadow. -->
|
||||
<dimen name="fake_shadow_inset">1dp</dimen>
|
||||
@@ -1163,7 +1153,6 @@
|
||||
|
||||
<!-- The absolute side margins of quick settings -->
|
||||
<dimen name="quick_settings_bottom_margin_media">8dp</dimen>
|
||||
<dimen name="rounded_corner_content_padding">0dp</dimen>
|
||||
<dimen name="nav_content_padding">0dp</dimen>
|
||||
<dimen name="nav_quick_scrub_track_edge_padding">24dp</dimen>
|
||||
<dimen name="nav_quick_scrub_track_thickness">10dp</dimen>
|
||||
|
||||
@@ -45,7 +45,9 @@ android_library {
|
||||
":wm_shell-aidls",
|
||||
":wm_shell_util-sources",
|
||||
],
|
||||
|
||||
resource_dirs: [
|
||||
"res",
|
||||
],
|
||||
static_libs: [
|
||||
"PluginCoreLib",
|
||||
"androidx.dynamicanimation_dynamicanimation",
|
||||
|
||||
@@ -18,15 +18,13 @@
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
>
|
||||
|
||||
<com.android.systemui.navigationbar.buttons.KeyButtonView
|
||||
<com.android.systemui.shared.rotation.FloatingRotationButtonView
|
||||
android:id="@+id/rotate_suggestion"
|
||||
android:layout_width="@dimen/floating_rotation_button_diameter"
|
||||
android:layout_height="@dimen/floating_rotation_button_diameter"
|
||||
android:contentDescription="@string/accessibility_rotate_button"
|
||||
android:paddingStart="@dimen/navigation_key_padding"
|
||||
android:paddingEnd="@dimen/navigation_key_padding"
|
||||
android:layout_gravity="bottom|left"
|
||||
android:scaleType="center"
|
||||
android:visibility="invisible" />
|
||||
</FrameLayout>
|
||||
</FrameLayout>
|
||||
19
packages/SystemUI/shared/res/values-sw600dp/dimens.xml
Normal file
19
packages/SystemUI/shared/res/values-sw600dp/dimens.xml
Normal file
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
* 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.
|
||||
*/
|
||||
-->
|
||||
<resources>
|
||||
<dimen name="navigation_key_padding">25dp</dimen>
|
||||
</resources>
|
||||
23
packages/SystemUI/shared/res/values-sw900dp/dimens.xml
Normal file
23
packages/SystemUI/shared/res/values-sw900dp/dimens.xml
Normal file
@@ -0,0 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
* 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.
|
||||
*/
|
||||
-->
|
||||
<resources>
|
||||
<!-- The maximum width of the navigation bar ripples. -->
|
||||
<dimen name="key_button_ripple_max_width">76dp</dimen>
|
||||
|
||||
<!-- The padding around the navigation buttons -->
|
||||
<dimen name="navigation_key_padding">0dp</dimen>
|
||||
</resources>
|
||||
30
packages/SystemUI/shared/res/values/dimens.xml
Normal file
30
packages/SystemUI/shared/res/values/dimens.xml
Normal file
@@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
* 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.
|
||||
*/
|
||||
-->
|
||||
<resources>
|
||||
<!-- The maximum width of the navigation bar ripples. -->
|
||||
<dimen name="key_button_ripple_max_width">95dp</dimen>
|
||||
|
||||
<dimen name="rounded_corner_content_padding">0dp</dimen>
|
||||
|
||||
<dimen name="navigation_key_padding">0dp</dimen>
|
||||
|
||||
<!-- Floating rotation button -->
|
||||
<dimen name="floating_rotation_button_diameter">40dp</dimen>
|
||||
<dimen name="floating_rotation_button_min_margin">20dp</dimen>
|
||||
<dimen name="floating_rotation_button_taskbar_left_margin">20dp</dimen>
|
||||
<dimen name="floating_rotation_button_taskbar_bottom_margin">10dp</dimen>
|
||||
</resources>
|
||||
@@ -33,13 +33,11 @@ import android.view.RenderNodeAnimator;
|
||||
import android.view.View;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.view.animation.Interpolator;
|
||||
import android.view.animation.PathInterpolator;
|
||||
|
||||
import androidx.annotation.Keep;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.animation.Interpolators;
|
||||
|
||||
import androidx.annotation.Keep;
|
||||
import com.android.systemui.shared.R;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
@@ -51,6 +49,8 @@ public class KeyButtonRipple extends Drawable {
|
||||
private static final float GLOW_MAX_ALPHA_DARK = 0.1f;
|
||||
private static final int ANIMATION_DURATION_SCALE = 350;
|
||||
private static final int ANIMATION_DURATION_FADE = 450;
|
||||
private static final Interpolator ALPHA_OUT_INTERPOLATOR =
|
||||
new PathInterpolator(0f, 0f, 0.8f, 1f);
|
||||
|
||||
private Paint mRipplePaint;
|
||||
private CanvasProperty<Float> mLeftProp;
|
||||
@@ -338,7 +338,7 @@ public class KeyButtonRipple extends Drawable {
|
||||
|
||||
private void exitSoftware() {
|
||||
ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(this, "glowAlpha", mGlowAlpha, 0f);
|
||||
alphaAnimator.setInterpolator(Interpolators.ALPHA_OUT);
|
||||
alphaAnimator.setInterpolator(ALPHA_OUT_INTERPOLATOR);
|
||||
alphaAnimator.setDuration(ANIMATION_DURATION_FADE);
|
||||
alphaAnimator.addListener(mAnimatorListener);
|
||||
alphaAnimator.start();
|
||||
@@ -461,7 +461,7 @@ public class KeyButtonRipple extends Drawable {
|
||||
final RenderNodeAnimator opacityAnim = new RenderNodeAnimator(mPaintProp,
|
||||
RenderNodeAnimator.PAINT_ALPHA, 0);
|
||||
opacityAnim.setDuration(ANIMATION_DURATION_FADE);
|
||||
opacityAnim.setInterpolator(Interpolators.ALPHA_OUT);
|
||||
opacityAnim.setInterpolator(ALPHA_OUT_INTERPOLATOR);
|
||||
opacityAnim.addListener(mAnimatorListener);
|
||||
opacityAnim.addListener(mExitHwTraceAnimator);
|
||||
opacityAnim.setTarget(mTargetView);
|
||||
@@ -14,12 +14,14 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.navigationbar.gestural;
|
||||
package com.android.systemui.shared.rotation;
|
||||
|
||||
import android.annotation.StringRes;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.drawable.AnimatedVectorDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -27,28 +29,25 @@ import android.view.WindowManager;
|
||||
import android.view.animation.AccelerateDecelerateInterpolator;
|
||||
import android.widget.FrameLayout;
|
||||
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.navigationbar.RotationButton;
|
||||
import com.android.systemui.navigationbar.RotationButtonController;
|
||||
import com.android.systemui.navigationbar.buttons.KeyButtonDrawable;
|
||||
import com.android.systemui.navigationbar.buttons.KeyButtonView;
|
||||
import com.android.systemui.navigationbar.gestural.FloatingRotationButtonPositionCalculator.Position;
|
||||
import androidx.core.view.OneShotPreDrawListener;
|
||||
|
||||
import com.android.systemui.shared.R;
|
||||
import com.android.systemui.shared.rotation.FloatingRotationButtonPositionCalculator.Position;
|
||||
|
||||
/**
|
||||
* Containing logic for the rotation button on the physical left bottom corner of the screen.
|
||||
*/
|
||||
public class FloatingRotationButton implements RotationButton {
|
||||
|
||||
private static final float BACKGROUND_ALPHA = 0.92f;
|
||||
private static final int MARGIN_ANIMATION_DURATION_MILLIS = 300;
|
||||
|
||||
private final WindowManager mWindowManager;
|
||||
private final ViewGroup mKeyButtonContainer;
|
||||
private final KeyButtonView mKeyButtonView;
|
||||
private final FloatingRotationButtonView mKeyButtonView;
|
||||
|
||||
private final int mContainerSize;
|
||||
|
||||
private KeyButtonDrawable mKeyButtonDrawable;
|
||||
private AnimatedVectorDrawable mAnimatedDrawable;
|
||||
private boolean mIsShowing;
|
||||
private boolean mCanShow = true;
|
||||
private int mDisplayRotation;
|
||||
@@ -62,12 +61,13 @@ public class FloatingRotationButton implements RotationButton {
|
||||
private RotationButtonUpdatesCallback mUpdatesCallback;
|
||||
private Position mPosition;
|
||||
|
||||
public FloatingRotationButton(Context context) {
|
||||
public FloatingRotationButton(Context context, @StringRes int contentDescription) {
|
||||
mWindowManager = context.getSystemService(WindowManager.class);
|
||||
mKeyButtonContainer = (ViewGroup) LayoutInflater.from(context).inflate(
|
||||
R.layout.rotate_suggestion, null);
|
||||
mKeyButtonView = mKeyButtonContainer.findViewById(R.id.rotate_suggestion);
|
||||
mKeyButtonView.setVisibility(View.VISIBLE);
|
||||
mKeyButtonView.setContentDescription(context.getString(contentDescription));
|
||||
|
||||
Resources res = context.getResources();
|
||||
|
||||
@@ -113,6 +113,10 @@ public class FloatingRotationButton implements RotationButton {
|
||||
|
||||
mIsShowing = true;
|
||||
int flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
|
||||
|
||||
// TODO(b/200103245): add new window type that has z-index above
|
||||
// TYPE_NAVIGATION_BAR_PANEL as currently it could be below the taskbar which has
|
||||
// the same window type
|
||||
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
|
||||
mContainerSize,
|
||||
mContainerSize,
|
||||
@@ -134,14 +138,18 @@ public class FloatingRotationButton implements RotationButton {
|
||||
updateTranslation(mPosition, /* animate */ false);
|
||||
|
||||
mWindowManager.addView(mKeyButtonContainer, lp);
|
||||
if (mKeyButtonDrawable != null && mKeyButtonDrawable.canAnimate()) {
|
||||
mKeyButtonDrawable.resetAnimation();
|
||||
mKeyButtonDrawable.startAnimation();
|
||||
if (mAnimatedDrawable != null) {
|
||||
mAnimatedDrawable.reset();
|
||||
mAnimatedDrawable.start();
|
||||
}
|
||||
|
||||
if (mUpdatesCallback != null) {
|
||||
mUpdatesCallback.onVisibilityChanged(true);
|
||||
}
|
||||
// Notify about visibility only after first traversal so we can properly calculate
|
||||
// the touch region for the button
|
||||
OneShotPreDrawListener.add(mKeyButtonView, () -> {
|
||||
if (mIsShowing && mUpdatesCallback != null) {
|
||||
mUpdatesCallback.onVisibilityChanged(true);
|
||||
}
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -166,12 +174,10 @@ public class FloatingRotationButton implements RotationButton {
|
||||
|
||||
@Override
|
||||
public void updateIcon(int lightIconColor, int darkIconColor) {
|
||||
Color ovalBackgroundColor = Color.valueOf(Color.red(darkIconColor),
|
||||
Color.green(darkIconColor), Color.blue(darkIconColor), BACKGROUND_ALPHA);
|
||||
mKeyButtonDrawable = KeyButtonDrawable.create(mRotationButtonController.getContext(),
|
||||
lightIconColor, darkIconColor, mRotationButtonController.getIconResId(),
|
||||
false /* shadow */, ovalBackgroundColor);
|
||||
mKeyButtonView.setImageDrawable(mKeyButtonDrawable);
|
||||
mAnimatedDrawable = (AnimatedVectorDrawable) mKeyButtonView.getContext()
|
||||
.getDrawable(mRotationButtonController.getIconResId());
|
||||
mKeyButtonView.setImageDrawable(mAnimatedDrawable);
|
||||
mKeyButtonView.setColors(lightIconColor, darkIconColor);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -185,8 +191,8 @@ public class FloatingRotationButton implements RotationButton {
|
||||
}
|
||||
|
||||
@Override
|
||||
public KeyButtonDrawable getImageDrawable() {
|
||||
return mKeyButtonDrawable;
|
||||
public Drawable getImageDrawable() {
|
||||
return mAnimatedDrawable;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -202,6 +208,7 @@ public class FloatingRotationButton implements RotationButton {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskbarStateChanged(boolean taskbarVisible, boolean taskbarStashed) {
|
||||
mIsTaskbarVisible = taskbarVisible;
|
||||
mIsTaskbarStashed = taskbarStashed;
|
||||
@@ -1,4 +1,4 @@
|
||||
package com.android.systemui.navigationbar.gestural
|
||||
package com.android.systemui.shared.rotation
|
||||
|
||||
import android.view.Gravity
|
||||
import android.view.Surface
|
||||
@@ -7,7 +7,7 @@ import android.view.Surface
|
||||
* Calculates gravity and translation that is necessary to display
|
||||
* the button in the correct position based on the current state
|
||||
*/
|
||||
internal class FloatingRotationButtonPositionCalculator(
|
||||
class FloatingRotationButtonPositionCalculator(
|
||||
private val defaultMargin: Int,
|
||||
private val taskbarMarginLeft: Int,
|
||||
private val taskbarMarginBottom: Int
|
||||
@@ -0,0 +1,81 @@
|
||||
/*
|
||||
* 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.systemui.shared.rotation;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PorterDuff;
|
||||
import android.graphics.PorterDuffColorFilter;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
|
||||
import com.android.systemui.navigationbar.buttons.KeyButtonRipple;
|
||||
|
||||
public class FloatingRotationButtonView extends ImageView {
|
||||
|
||||
private static final float BACKGROUND_ALPHA = 0.92f;
|
||||
|
||||
private final KeyButtonRipple mRipple;
|
||||
private final Paint mOvalBgPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
|
||||
|
||||
public FloatingRotationButtonView(Context context, AttributeSet attrs) {
|
||||
this(context, attrs, 0);
|
||||
}
|
||||
|
||||
public FloatingRotationButtonView(Context context, AttributeSet attrs, int defStyle) {
|
||||
super(context, attrs, defStyle);
|
||||
|
||||
setClickable(true);
|
||||
|
||||
mRipple = new KeyButtonRipple(context, this);
|
||||
setBackground(mRipple);
|
||||
setWillNotDraw(false);
|
||||
forceHasOverlappingRendering(false);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onWindowVisibilityChanged(int visibility) {
|
||||
super.onWindowVisibilityChanged(visibility);
|
||||
if (visibility != View.VISIBLE) {
|
||||
jumpDrawablesToCurrentState();
|
||||
}
|
||||
}
|
||||
|
||||
public void setColors(int lightColor, int darkColor) {
|
||||
getDrawable().setColorFilter(new PorterDuffColorFilter(lightColor, PorterDuff.Mode.SRC_IN));
|
||||
|
||||
final int ovalBackgroundColor = Color.valueOf(Color.red(darkColor),
|
||||
Color.green(darkColor), Color.blue(darkColor), BACKGROUND_ALPHA).toArgb();
|
||||
|
||||
mOvalBgPaint.setColor(ovalBackgroundColor);
|
||||
mRipple.setType(KeyButtonRipple.Type.OVAL);
|
||||
}
|
||||
|
||||
public void setDarkIntensity(float darkIntensity) {
|
||||
mRipple.setDarkIntensity(darkIntensity);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(Canvas canvas) {
|
||||
int d = Math.min(getWidth(), getHeight());
|
||||
canvas.drawOval(0, 0, d, d, mOvalBgPaint);
|
||||
super.draw(canvas);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
/*
|
||||
* 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.systemui.shared.rotation;
|
||||
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.view.View;
|
||||
|
||||
/**
|
||||
* Interface of a rotation button that interacts {@link RotationButtonController}.
|
||||
* This interface exists because of the two different styles of rotation button in Sysui,
|
||||
* one in contextual for 3 button nav and a floating rotation button for gestural.
|
||||
*/
|
||||
public interface RotationButton {
|
||||
default void setRotationButtonController(RotationButtonController rotationButtonController) { }
|
||||
default void setUpdatesCallback(RotationButtonUpdatesCallback updatesCallback) { }
|
||||
|
||||
default View getCurrentView() {
|
||||
return null;
|
||||
}
|
||||
default boolean show() { return false; }
|
||||
default boolean hide() { return false; }
|
||||
default boolean isVisible() {
|
||||
return false;
|
||||
}
|
||||
default void setCanShowRotationButton(boolean canShow) {}
|
||||
default void onTaskbarStateChanged(boolean taskbarVisible, boolean taskbarStashed) {}
|
||||
default void updateIcon(int lightIconColor, int darkIconColor) { }
|
||||
default void setOnClickListener(View.OnClickListener onClickListener) { }
|
||||
default void setOnHoverListener(View.OnHoverListener onHoverListener) { }
|
||||
default Drawable getImageDrawable() {
|
||||
return null;
|
||||
}
|
||||
default void setDarkIntensity(float darkIntensity) { }
|
||||
default boolean acceptRotationProposal() {
|
||||
return getCurrentView() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for updates provided by a rotation button
|
||||
*/
|
||||
interface RotationButtonUpdatesCallback {
|
||||
default void onVisibilityChanged(boolean isVisible) {};
|
||||
default void onPositionChanged() {};
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
* 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.
|
||||
@@ -14,7 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.navigationbar;
|
||||
package com.android.systemui.shared.rotation;
|
||||
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
|
||||
import static com.android.internal.view.RotationPolicy.NATURAL_ROTATION;
|
||||
|
||||
@@ -23,48 +25,51 @@ 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.IRotationWatcher.Stub;
|
||||
import android.view.IRotationWatcher;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.Surface;
|
||||
import android.view.View;
|
||||
import android.view.WindowInsetsController;
|
||||
import android.view.WindowInsetsController.Behavior;
|
||||
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.systemui.Dependency;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.animation.Interpolators;
|
||||
import com.android.systemui.navigationbar.RotationButton.RotationButtonUpdatesCallback;
|
||||
import com.android.systemui.navigationbar.buttons.KeyButtonDrawable;
|
||||
import com.android.internal.view.RotationPolicy;
|
||||
import com.android.systemui.shared.rotation.RotationButton.RotationButtonUpdatesCallback;
|
||||
import com.android.systemui.shared.recents.utilities.Utilities;
|
||||
import com.android.systemui.shared.recents.utilities.ViewRippler;
|
||||
import com.android.systemui.shared.system.ActivityManagerWrapper;
|
||||
import com.android.systemui.shared.system.TaskStackChangeListener;
|
||||
import com.android.systemui.shared.system.TaskStackChangeListeners;
|
||||
import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
|
||||
import com.android.systemui.statusbar.policy.RotationLockController;
|
||||
|
||||
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. */
|
||||
/**
|
||||
* 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;
|
||||
|
||||
@@ -72,6 +77,7 @@ public class RotationButtonController {
|
||||
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;
|
||||
@@ -79,17 +85,30 @@ public class RotationButtonController {
|
||||
private int mLastRotationSuggestion;
|
||||
private boolean mPendingRotationSuggestion;
|
||||
private boolean mHoveringRotationSuggestion;
|
||||
private RotationLockController mRotationLockController;
|
||||
private AccessibilityManagerWrapper mAccessibilityManagerWrapper;
|
||||
private TaskStackListenerImpl mTaskStackListener;
|
||||
private final AccessibilityManager mAccessibilityManager;
|
||||
private final TaskStackListenerImpl mTaskStackListener;
|
||||
private Consumer<Integer> mRotWatcherListener;
|
||||
|
||||
private boolean mListenersRegistered = false;
|
||||
private boolean mIsNavigationBarShowing;
|
||||
private @Behavior int mBehavior = WindowInsetsController.BEHAVIOR_DEFAULT;
|
||||
@SuppressLint("InlinedApi")
|
||||
private @WindowInsetsController.Behavior
|
||||
int mBehavior = WindowInsetsController.BEHAVIOR_DEFAULT;
|
||||
private boolean mSkipOverrideUserLockPrefsOnce;
|
||||
private int mLightIconColor;
|
||||
private int mDarkIconColor;
|
||||
private int mIconResId = R.drawable.ic_sysbar_rotate_button_ccw_start_90;
|
||||
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 */);
|
||||
@@ -97,19 +116,20 @@ public class RotationButtonController {
|
||||
() -> mPendingRotationSuggestion = false;
|
||||
private Animator mRotateHideAnimator;
|
||||
|
||||
private final Stub mRotationWatcher = new Stub() {
|
||||
|
||||
private final IRotationWatcher.Stub mRotationWatcher = new IRotationWatcher.Stub() {
|
||||
@Override
|
||||
public void onRotationChanged(final int rotation) throws RemoteException {
|
||||
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 (mRotationLockController.isRotationLocked()) {
|
||||
if (isRotationLocked()) {
|
||||
if (shouldOverrideUserLockPrefs(rotation)) {
|
||||
setRotationLockedAtAngle(rotation);
|
||||
}
|
||||
setRotateSuggestionButtonState(false /* visible */, true /* hideImmediately */);
|
||||
setRotateSuggestionButtonState(false /* visible */, true /* forced */);
|
||||
}
|
||||
|
||||
if (mRotWatcherListener != null) {
|
||||
@@ -121,27 +141,39 @@ public class RotationButtonController {
|
||||
|
||||
/**
|
||||
* Determines if rotation suggestions disabled2 flag exists in flag
|
||||
*
|
||||
* @param disable2Flags see if rotation suggestion flag exists in this flag
|
||||
* @return whether flag exists
|
||||
*/
|
||||
static boolean hasDisable2RotateSuggestionFlag(int disable2Flags) {
|
||||
public static boolean hasDisable2RotateSuggestionFlag(int disable2Flags) {
|
||||
return (disable2Flags & StatusBarManager.DISABLE2_ROTATE_SUGGESTIONS) != 0;
|
||||
}
|
||||
|
||||
RotationButtonController(Context context, @ColorInt int lightIconColor,
|
||||
@ColorInt int darkIconColor) {
|
||||
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;
|
||||
|
||||
mIsNavigationBarShowing = true;
|
||||
mRotationLockController = Dependency.get(RotationLockController.class);
|
||||
mAccessibilityManagerWrapper = Dependency.get(AccessibilityManagerWrapper.class);
|
||||
mIconCcwStart0ResId = iconCcwStart0ResId;
|
||||
mIconCcwStart90ResId = iconCcwStart90ResId;
|
||||
mIconCwStart0ResId = iconCwStart0ResId;
|
||||
mIconCwStart90ResId = iconCwStart90ResId;
|
||||
mIconResId = mIconCcwStart90ResId;
|
||||
|
||||
mAccessibilityManager = AccessibilityManager.getInstance(context);
|
||||
mTaskStackListener = new TaskStackListenerImpl();
|
||||
mWindowRotationProvider = windowRotationProvider;
|
||||
}
|
||||
|
||||
void setRotationButton(RotationButton rotationButton,
|
||||
RotationButtonUpdatesCallback updatesCallback) {
|
||||
public void setRotationButton(RotationButton rotationButton,
|
||||
RotationButtonUpdatesCallback updatesCallback) {
|
||||
mRotationButton = rotationButton;
|
||||
mRotationButton.setRotationButtonController(this);
|
||||
mRotationButton.setOnClickListener(this::onRotateSuggestionClick);
|
||||
@@ -149,7 +181,24 @@ public class RotationButtonController {
|
||||
mRotationButton.setUpdatesCallback(updatesCallback);
|
||||
}
|
||||
|
||||
void registerListeners() {
|
||||
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) {
|
||||
return;
|
||||
}
|
||||
@@ -157,18 +206,19 @@ public class RotationButtonController {
|
||||
mListenersRegistered = true;
|
||||
try {
|
||||
WindowManagerGlobal.getWindowManagerService()
|
||||
.watchRotation(mRotationWatcher, mContext.getDisplay().getDisplayId());
|
||||
.watchRotation(mRotationWatcher, DEFAULT_DISPLAY);
|
||||
} catch (IllegalArgumentException e) {
|
||||
mListenersRegistered = false;
|
||||
Log.w(TAG, "RegisterListeners for the display failed");
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
Log.e(TAG, "RegisterListeners caught a RemoteException", e);
|
||||
return;
|
||||
}
|
||||
|
||||
TaskStackChangeListeners.getInstance().registerTaskStackListener(mTaskStackListener);
|
||||
}
|
||||
|
||||
void unregisterListeners() {
|
||||
public void unregisterListeners() {
|
||||
if (!mListenersRegistered) {
|
||||
return;
|
||||
}
|
||||
@@ -177,34 +227,31 @@ public class RotationButtonController {
|
||||
try {
|
||||
WindowManagerGlobal.getWindowManagerService().removeRotationWatcher(mRotationWatcher);
|
||||
} catch (RemoteException e) {
|
||||
throw e.rethrowFromSystemServer();
|
||||
Log.e(TAG, "UnregisterListeners caught a RemoteException", e);
|
||||
return;
|
||||
}
|
||||
|
||||
TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
|
||||
}
|
||||
|
||||
void setRotationCallback(Consumer<Integer> watcher) {
|
||||
public void setRotationCallback(Consumer<Integer> watcher) {
|
||||
mRotWatcherListener = watcher;
|
||||
}
|
||||
|
||||
void setRotationLockedAtAngle(int rotationSuggestion) {
|
||||
mRotationLockController.setRotationLockedAtAngle(true /* locked */, rotationSuggestion);
|
||||
public void setRotationLockedAtAngle(int rotationSuggestion) {
|
||||
RotationPolicy.setRotationLockAtAngle(mContext, true, rotationSuggestion);
|
||||
}
|
||||
|
||||
public boolean isRotationLocked() {
|
||||
return mRotationLockController.isRotationLocked();
|
||||
return RotationPolicy.isRotationLocked(mContext);
|
||||
}
|
||||
|
||||
void setRotateSuggestionButtonState(boolean visible) {
|
||||
setRotateSuggestionButtonState(visible, false /* hideImmediately */);
|
||||
public void setRotateSuggestionButtonState(boolean visible) {
|
||||
setRotateSuggestionButtonState(visible, false /* force */);
|
||||
}
|
||||
|
||||
/**
|
||||
* Change the visibility of rotate suggestion button. If {@code hideImmediately} is true,
|
||||
* it doesn't wait until the completion of the running animation.
|
||||
*/
|
||||
void setRotateSuggestionButtonState(final boolean visible, final boolean hideImmediately) {
|
||||
// At any point the the button can become invisible because an a11y service became active.
|
||||
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
|
||||
@@ -213,7 +260,7 @@ public class RotationButtonController {
|
||||
final View view = mRotationButton.getCurrentView();
|
||||
if (view == null) return;
|
||||
|
||||
final KeyButtonDrawable currentDrawable = mRotationButton.getImageDrawable();
|
||||
final Drawable currentDrawable = mRotationButton.getImageDrawable();
|
||||
if (currentDrawable == null) return;
|
||||
|
||||
// Clear any pending suggestion flag as it has either been nullified or is being shown
|
||||
@@ -232,11 +279,13 @@ public class RotationButtonController {
|
||||
view.setAlpha(1f);
|
||||
|
||||
// Run the rotate icon's animation if it has one
|
||||
if (currentDrawable.canAnimate()) {
|
||||
currentDrawable.resetAnimation();
|
||||
currentDrawable.startAnimation();
|
||||
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.
|
||||
@@ -244,7 +293,7 @@ public class RotationButtonController {
|
||||
} else { // Hide
|
||||
mViewRippler.stop(); // Prevent any pending ripples, force hide or not
|
||||
|
||||
if (hideImmediately) {
|
||||
if (force) {
|
||||
// If a hide animator is running stop it and make invisible
|
||||
if (mRotateHideAnimator != null && mRotateHideAnimator.isRunning()) {
|
||||
mRotateHideAnimator.pause();
|
||||
@@ -258,7 +307,7 @@ public class RotationButtonController {
|
||||
|
||||
ObjectAnimator fadeOut = ObjectAnimator.ofFloat(view, "alpha", 0f);
|
||||
fadeOut.setDuration(BUTTON_FADE_IN_OUT_DURATION_MS);
|
||||
fadeOut.setInterpolator(Interpolators.LINEAR);
|
||||
fadeOut.setInterpolator(LINEAR_INTERPOLATOR);
|
||||
fadeOut.addListener(new AnimatorListenerAdapter() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animation) {
|
||||
@@ -271,29 +320,34 @@ public class RotationButtonController {
|
||||
}
|
||||
}
|
||||
|
||||
void setRecentsAnimationRunning(boolean running) {
|
||||
public void setDarkIntensity(float darkIntensity) {
|
||||
mRotationButton.setDarkIntensity(darkIntensity);
|
||||
}
|
||||
|
||||
public void setRecentsAnimationRunning(boolean running) {
|
||||
mIsRecentsAnimationRunning = running;
|
||||
updateRotationButtonStateInOverview();
|
||||
}
|
||||
|
||||
void setHomeRotationEnabled(boolean enabled) {
|
||||
public void setHomeRotationEnabled(boolean enabled) {
|
||||
mHomeRotationEnabled = enabled;
|
||||
updateRotationButtonStateInOverview();
|
||||
}
|
||||
|
||||
private void updateRotationButtonStateInOverview() {
|
||||
if (mIsRecentsAnimationRunning && !mHomeRotationEnabled) {
|
||||
setRotateSuggestionButtonState(false, true /* hideImmediately */ );
|
||||
setRotateSuggestionButtonState(false, true /* hideImmediately */);
|
||||
}
|
||||
}
|
||||
|
||||
void setDarkIntensity(float darkIntensity) {
|
||||
mRotationButton.setDarkIntensity(darkIntensity);
|
||||
}
|
||||
public void onRotationProposal(int rotation, boolean isValid) {
|
||||
int windowRotation = mWindowRotationProvider.get();
|
||||
|
||||
void onRotationProposal(int rotation, int windowRotation, boolean isValid) {
|
||||
if (!mRotationButton.acceptRotationProposal() || (!mHomeRotationEnabled
|
||||
&& mIsRecentsAnimationRunning)) {
|
||||
if (!mRotationButton.acceptRotationProposal()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mHomeRotationEnabled && mIsRecentsAnimationRunning) {
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -316,13 +370,9 @@ public class RotationButtonController {
|
||||
mLastRotationSuggestion = rotation; // Remember rotation for click
|
||||
final boolean rotationCCW = Utilities.isRotationAnimationCCW(windowRotation, rotation);
|
||||
if (windowRotation == Surface.ROTATION_0 || windowRotation == Surface.ROTATION_180) {
|
||||
mIconResId = rotationCCW
|
||||
? R.drawable.ic_sysbar_rotate_button_ccw_start_90
|
||||
: R.drawable.ic_sysbar_rotate_button_cw_start_90;
|
||||
mIconResId = rotationCCW ? mIconCcwStart0ResId : mIconCwStart0ResId;
|
||||
} else { // 90 or 270
|
||||
mIconResId = rotationCCW
|
||||
? R.drawable.ic_sysbar_rotate_button_ccw_start_0
|
||||
: R.drawable.ic_sysbar_rotate_button_ccw_start_0;
|
||||
mIconResId = rotationCCW ? mIconCcwStart90ResId : mIconCwStart90ResId;
|
||||
}
|
||||
mRotationButton.updateIcon(mLightIconColor, mDarkIconColor);
|
||||
|
||||
@@ -339,23 +389,31 @@ public class RotationButtonController {
|
||||
}
|
||||
}
|
||||
|
||||
void onDisable2FlagChanged(int state2) {
|
||||
public void onDisable2FlagChanged(int state2) {
|
||||
final boolean rotateSuggestionsDisabled = hasDisable2RotateSuggestionFlag(state2);
|
||||
if (rotateSuggestionsDisabled) onRotationSuggestionsDisabled();
|
||||
}
|
||||
|
||||
void onNavigationBarWindowVisibilityChange(boolean showing) {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
void onBehaviorChanged(@Behavior int behavior) {
|
||||
if (mBehavior != behavior) {
|
||||
mBehavior = behavior;
|
||||
showPendingRotationButtonIfNeeded();
|
||||
}
|
||||
public void onTaskbarStateChange(boolean visible, boolean stashed) {
|
||||
getRotationButton().onTaskbarStateChanged(visible, stashed);
|
||||
}
|
||||
|
||||
private void showPendingRotationButtonIfNeeded() {
|
||||
@@ -364,31 +422,33 @@ public class RotationButtonController {
|
||||
}
|
||||
}
|
||||
|
||||
/** Return true when either the nav bar is visible or it's in visual immersive mode. */
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
public Context getContext() {
|
||||
return mContext;
|
||||
}
|
||||
|
||||
RotationButton getRotationButton() {
|
||||
return mRotationButton;
|
||||
}
|
||||
|
||||
public @DrawableRes int getIconResId() {
|
||||
@DrawableRes
|
||||
public int getIconResId() {
|
||||
return mIconResId;
|
||||
}
|
||||
|
||||
public @ColorInt int getLightIconColor() {
|
||||
@ColorInt
|
||||
public int getLightIconColor() {
|
||||
return mLightIconColor;
|
||||
}
|
||||
|
||||
public @ColorInt int getDarkIconColor() {
|
||||
@ColorInt
|
||||
public int getDarkIconColor() {
|
||||
return mDarkIconColor;
|
||||
}
|
||||
|
||||
public RotationButton getRotationButton() {
|
||||
return mRotationButton;
|
||||
}
|
||||
|
||||
private void onRotateSuggestionClick(View v) {
|
||||
mUiEventLogger.log(RotationButtonEvent.ROTATION_SUGGESTION_ACCEPTED);
|
||||
incrementNumAcceptedRotationSuggestionsIfNeeded();
|
||||
@@ -420,7 +480,7 @@ public class RotationButtonController {
|
||||
* avoid losing original user rotation when display rotation is changed by entering the fixed
|
||||
* orientation overview.
|
||||
*/
|
||||
void setSkipOverrideUserLockPrefsOnce() {
|
||||
public void setSkipOverrideUserLockPrefsOnce() {
|
||||
mSkipOverrideUserLockPrefsOnce = true;
|
||||
}
|
||||
|
||||
@@ -451,7 +511,7 @@ public class RotationButtonController {
|
||||
}
|
||||
|
||||
private int computeRotationProposalTimeout() {
|
||||
return mAccessibilityManagerWrapper.getRecommendedTimeoutMillis(
|
||||
return mAccessibilityManager.getRecommendedTimeoutMillis(
|
||||
mHoveringRotationSuggestion ? 16000 : 5000,
|
||||
AccessibilityManager.FLAG_CONTENT_CONTROLS);
|
||||
}
|
||||
@@ -513,11 +573,15 @@ public class RotationButtonController {
|
||||
ROTATION_SUGGESTION_ACCEPTED(207);
|
||||
|
||||
private final int mId;
|
||||
|
||||
RotationButtonEvent(int id) {
|
||||
mId = id;
|
||||
}
|
||||
@Override public int getId() {
|
||||
|
||||
@Override
|
||||
public int getId() {
|
||||
return mId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,6 +131,8 @@ import com.android.systemui.navigationbar.gestural.QuickswitchOrientedNavHandle;
|
||||
import com.android.systemui.plugins.statusbar.StatusBarStateController;
|
||||
import com.android.systemui.recents.OverviewProxyService;
|
||||
import com.android.systemui.recents.Recents;
|
||||
import com.android.systemui.shared.rotation.RotationButton;
|
||||
import com.android.systemui.shared.rotation.RotationButtonController;
|
||||
import com.android.systemui.settings.UserTracker;
|
||||
import com.android.systemui.shared.recents.utilities.Utilities;
|
||||
import com.android.systemui.shared.system.ActivityManagerWrapper;
|
||||
@@ -944,7 +946,6 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
|
||||
// not valid. Just ignore the rotation in this case.
|
||||
if (!mNavigationBarView.isAttachedToWindow()) return;
|
||||
|
||||
final int winRotation = mNavigationBarView.getDisplay().getRotation();
|
||||
final boolean rotateSuggestionsDisabled = RotationButtonController
|
||||
.hasDisable2RotateSuggestionFlag(mDisabledFlags2);
|
||||
final RotationButtonController rotationButtonController =
|
||||
@@ -953,7 +954,6 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
|
||||
|
||||
if (RotationContextButton.DEBUG_ROTATION) {
|
||||
Log.v(TAG, "onRotationProposal proposedRotation=" + Surface.rotationToString(rotation)
|
||||
+ ", winRotation=" + Surface.rotationToString(winRotation)
|
||||
+ ", isValid=" + isValid + ", mNavBarWindowState="
|
||||
+ StatusBarManager.windowStateToString(mNavigationBarWindowState)
|
||||
+ ", rotateSuggestionsDisabled=" + rotateSuggestionsDisabled
|
||||
@@ -963,7 +963,7 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
|
||||
// Respect the disabled flag, no need for action as flag change callback will handle hiding
|
||||
if (rotateSuggestionsDisabled) return;
|
||||
|
||||
rotationButtonController.onRotationProposal(rotation, winRotation, isValid);
|
||||
rotationButtonController.onRotationProposal(rotation, isValid);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -67,7 +67,6 @@ import com.android.systemui.Dependency;
|
||||
import com.android.systemui.R;
|
||||
import com.android.systemui.animation.Interpolators;
|
||||
import com.android.systemui.model.SysUiState;
|
||||
import com.android.systemui.navigationbar.RotationButton.RotationButtonUpdatesCallback;
|
||||
import com.android.systemui.navigationbar.buttons.ButtonDispatcher;
|
||||
import com.android.systemui.navigationbar.buttons.ContextualButton;
|
||||
import com.android.systemui.navigationbar.buttons.ContextualButtonGroup;
|
||||
@@ -76,9 +75,11 @@ import com.android.systemui.navigationbar.buttons.KeyButtonDrawable;
|
||||
import com.android.systemui.navigationbar.buttons.NearestTouchFrame;
|
||||
import com.android.systemui.navigationbar.buttons.RotationContextButton;
|
||||
import com.android.systemui.navigationbar.gestural.EdgeBackGestureHandler;
|
||||
import com.android.systemui.navigationbar.gestural.FloatingRotationButton;
|
||||
import com.android.systemui.shared.rotation.FloatingRotationButton;
|
||||
import com.android.systemui.recents.OverviewProxyService;
|
||||
import com.android.systemui.recents.Recents;
|
||||
import com.android.systemui.shared.rotation.RotationButton.RotationButtonUpdatesCallback;
|
||||
import com.android.systemui.shared.rotation.RotationButtonController;
|
||||
import com.android.systemui.shared.navigationbar.RegionSamplingHelper;
|
||||
import com.android.systemui.shared.system.ActivityManagerWrapper;
|
||||
import com.android.systemui.shared.system.QuickStepContract;
|
||||
@@ -322,9 +323,15 @@ public class NavigationBarView extends FrameLayout implements
|
||||
mContextualButtonGroup.addButton(accessibilityButton);
|
||||
mRotationContextButton = new RotationContextButton(R.id.rotate_suggestion,
|
||||
mLightContext, R.drawable.ic_sysbar_rotate_button_ccw_start_0);
|
||||
mFloatingRotationButton = new FloatingRotationButton(context);
|
||||
mRotationButtonController = new RotationButtonController(mLightContext,
|
||||
mLightIconColor, mDarkIconColor);
|
||||
mFloatingRotationButton = new FloatingRotationButton(context,
|
||||
R.string.accessibility_rotate_button);
|
||||
mRotationButtonController = new RotationButtonController(mLightContext, mLightIconColor,
|
||||
mDarkIconColor, R.drawable.ic_sysbar_rotate_button_ccw_start_0,
|
||||
R.drawable.ic_sysbar_rotate_button_ccw_start_90,
|
||||
R.drawable.ic_sysbar_rotate_button_cw_start_0,
|
||||
R.drawable.ic_sysbar_rotate_button_cw_start_90,
|
||||
() -> getDisplay().getRotation());
|
||||
|
||||
updateRotationButton();
|
||||
|
||||
mOverviewProxyService = Dependency.get(OverviewProxyService.class);
|
||||
@@ -661,7 +668,7 @@ public class NavigationBarView extends FrameLayout implements
|
||||
}
|
||||
|
||||
public void setBehavior(@Behavior int behavior) {
|
||||
mRotationButtonController.onBehaviorChanged(behavior);
|
||||
mRotationButtonController.onBehaviorChanged(Display.DEFAULT_DISPLAY, behavior);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1277,6 +1284,7 @@ public class NavigationBarView extends FrameLayout implements
|
||||
mButtonDispatchers.valueAt(i).onDestroy();
|
||||
}
|
||||
if (mRotationButtonController != null) {
|
||||
mFloatingRotationButton.hide();
|
||||
mRotationButtonController.unregisterListeners();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* Copyright (C) 2020 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.android.systemui.navigationbar;
|
||||
|
||||
import android.view.View;
|
||||
|
||||
import com.android.systemui.navigationbar.buttons.KeyButtonDrawable;
|
||||
|
||||
/** Interface of a rotation button that interacts {@link RotationButtonController}. */
|
||||
public interface RotationButton {
|
||||
void setRotationButtonController(RotationButtonController rotationButtonController);
|
||||
void setUpdatesCallback(RotationButtonUpdatesCallback updatesCallback);
|
||||
View getCurrentView();
|
||||
boolean show();
|
||||
boolean hide();
|
||||
boolean isVisible();
|
||||
void updateIcon(int lightIconColor, int darkIconColor);
|
||||
void setOnClickListener(View.OnClickListener onClickListener);
|
||||
void setOnHoverListener(View.OnHoverListener onHoverListener);
|
||||
KeyButtonDrawable getImageDrawable();
|
||||
void setDarkIntensity(float darkIntensity);
|
||||
default void setCanShowRotationButton(boolean canShow) {}
|
||||
default boolean acceptRotationProposal() {
|
||||
return getCurrentView() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for updates provided by a rotation button
|
||||
*/
|
||||
interface RotationButtonUpdatesCallback {
|
||||
void onVisibilityChanged(boolean isVisible);
|
||||
void onPositionChanged();
|
||||
}
|
||||
}
|
||||
@@ -21,8 +21,8 @@ import android.annotation.IdRes;
|
||||
import android.content.Context;
|
||||
import android.view.View;
|
||||
|
||||
import com.android.systemui.navigationbar.RotationButton;
|
||||
import com.android.systemui.navigationbar.RotationButtonController;
|
||||
import com.android.systemui.shared.rotation.RotationButton;
|
||||
import com.android.systemui.shared.rotation.RotationButtonController;
|
||||
|
||||
/** Containing logic for the rotation button in nav bar. */
|
||||
public class RotationContextButton extends ContextualButton implements RotationButton {
|
||||
|
||||
@@ -22,6 +22,7 @@ import static org.mockito.Mockito.spy;
|
||||
import static org.mockito.Mockito.times;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
import android.view.Display;
|
||||
import android.view.View;
|
||||
import android.view.WindowInsetsController;
|
||||
|
||||
@@ -31,6 +32,8 @@ import androidx.test.runner.AndroidJUnit4;
|
||||
|
||||
import com.android.systemui.SysuiTestCase;
|
||||
import com.android.systemui.SysuiTestableContext;
|
||||
import com.android.systemui.shared.rotation.RotationButton;
|
||||
import com.android.systemui.shared.rotation.RotationButtonController;
|
||||
import com.android.systemui.statusbar.policy.RotationLockController;
|
||||
|
||||
import org.junit.Before;
|
||||
@@ -39,6 +42,8 @@ import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.MockitoAnnotations;
|
||||
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/** atest NavigationBarRotationContextTest */
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@@ -50,6 +55,8 @@ public class NavigationBarRotationContextTest extends SysuiTestCase {
|
||||
InstrumentationRegistry.getContext(), getLeakCheck());
|
||||
private RotationButtonController mRotationButtonController;
|
||||
private RotationButton mRotationButton;
|
||||
private int mWindowRotation = DEFAULT_ROTATE;
|
||||
private Supplier<Integer> mWindowRotationSupplier = () -> mWindowRotation;
|
||||
|
||||
@Before
|
||||
public void setup() {
|
||||
@@ -58,7 +65,15 @@ public class NavigationBarRotationContextTest extends SysuiTestCase {
|
||||
|
||||
final View view = new View(mContext);
|
||||
mRotationButton = mock(RotationButton.class);
|
||||
mRotationButtonController = new RotationButtonController(mContext, 0, 0);
|
||||
mRotationButtonController = new RotationButtonController(mContext,
|
||||
/* lightIconColor */ 0,
|
||||
/* darkIconColor */ 0,
|
||||
/* iconCcwStart0 */ 0,
|
||||
/* iconCcwStart90 */ 0,
|
||||
/* iconCwStart0 */ 0,
|
||||
/* iconCwStart90 */ 0,
|
||||
mWindowRotationSupplier
|
||||
);
|
||||
mRotationButtonController.setRotationButton(mRotationButton,
|
||||
new RotationButton.RotationButtonUpdatesCallback() {
|
||||
@Override
|
||||
@@ -77,16 +92,16 @@ public class NavigationBarRotationContextTest extends SysuiTestCase {
|
||||
|
||||
@Test
|
||||
public void testOnInvalidRotationProposal() {
|
||||
mRotationButtonController.onRotationProposal(DEFAULT_ROTATE, DEFAULT_ROTATE + 1,
|
||||
false /* isValid */);
|
||||
mWindowRotation = DEFAULT_ROTATE + 1;
|
||||
mRotationButtonController.onRotationProposal(DEFAULT_ROTATE, false /* isValid */);
|
||||
verify(mRotationButtonController, times(1)).setRotateSuggestionButtonState(
|
||||
false /* visible */);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOnSameRotationProposal() {
|
||||
mRotationButtonController.onRotationProposal(DEFAULT_ROTATE, DEFAULT_ROTATE,
|
||||
true /* isValid */);
|
||||
mWindowRotation = DEFAULT_ROTATE;
|
||||
mRotationButtonController.onRotationProposal(DEFAULT_ROTATE, true /* isValid */);
|
||||
verify(mRotationButtonController, times(1)).setRotateSuggestionButtonState(
|
||||
false /* visible */);
|
||||
}
|
||||
@@ -94,17 +109,17 @@ public class NavigationBarRotationContextTest extends SysuiTestCase {
|
||||
@Test
|
||||
public void testOnRotationProposalShowButtonShowNav() {
|
||||
// No navigation bar should not call to set visibility state
|
||||
mRotationButtonController.onBehaviorChanged(
|
||||
mRotationButtonController.onBehaviorChanged(Display.DEFAULT_DISPLAY,
|
||||
WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
|
||||
mRotationButtonController.onNavigationBarWindowVisibilityChange(false /* showing */);
|
||||
verify(mRotationButtonController, times(0)).setRotateSuggestionButtonState(
|
||||
false /* visible */);
|
||||
verify(mRotationButtonController, times(0)).setRotateSuggestionButtonState(
|
||||
true /* visible */);
|
||||
mWindowRotation = DEFAULT_ROTATE + 1;
|
||||
|
||||
// No navigation bar with rotation change should not call to set visibility state
|
||||
mRotationButtonController.onRotationProposal(DEFAULT_ROTATE, DEFAULT_ROTATE + 1,
|
||||
true /* isValid */);
|
||||
mRotationButtonController.onRotationProposal(DEFAULT_ROTATE, true /* isValid */);
|
||||
verify(mRotationButtonController, times(0)).setRotateSuggestionButtonState(
|
||||
false /* visible */);
|
||||
verify(mRotationButtonController, times(0)).setRotateSuggestionButtonState(
|
||||
@@ -124,10 +139,10 @@ public class NavigationBarRotationContextTest extends SysuiTestCase {
|
||||
false /* visible */);
|
||||
verify(mRotationButtonController, times(0)).setRotateSuggestionButtonState(
|
||||
true /* visible */);
|
||||
mWindowRotation = DEFAULT_ROTATE + 1;
|
||||
|
||||
// Navigation bar is visible and rotation requested
|
||||
mRotationButtonController.onRotationProposal(DEFAULT_ROTATE, DEFAULT_ROTATE + 1,
|
||||
true /* isValid */);
|
||||
mRotationButtonController.onRotationProposal(DEFAULT_ROTATE, true /* isValid */);
|
||||
verify(mRotationButtonController, times(1)).setRotateSuggestionButtonState(
|
||||
true /* visible */);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,8 @@ import android.view.Gravity
|
||||
import android.view.Surface
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.systemui.SysuiTestCase
|
||||
import com.android.systemui.navigationbar.gestural.FloatingRotationButtonPositionCalculator.Position
|
||||
import com.android.systemui.shared.rotation.FloatingRotationButtonPositionCalculator
|
||||
import com.android.systemui.shared.rotation.FloatingRotationButtonPositionCalculator.Position
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
Reference in New Issue
Block a user