Merge "Update rotation suggestion button position to align it with taskbar" into sc-v2-dev am: ecff0245a7 am: 1996fcf26b
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/base/+/15095931 Change-Id: Ic93ee74fa8bb4c37c9e7d7854e536089927171c0
This commit is contained in:
@@ -14,16 +14,19 @@
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License
|
||||
-->
|
||||
|
||||
<com.android.systemui.navigationbar.buttons.KeyButtonView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/rotate_suggestion"
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="0"
|
||||
android:scaleType="center"
|
||||
android:visibility="invisible"
|
||||
android:contentDescription="@string/accessibility_rotate_button"
|
||||
android:paddingStart="@dimen/navigation_key_padding"
|
||||
android:paddingEnd="@dimen/navigation_key_padding"
|
||||
/>
|
||||
>
|
||||
|
||||
<com.android.systemui.navigationbar.buttons.KeyButtonView
|
||||
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>
|
||||
@@ -62,7 +62,9 @@
|
||||
<item name="navigation_luminance_change_threshold" type="dimen" format="float">0.05</item>
|
||||
|
||||
<dimen name="floating_rotation_button_diameter">40dp</dimen>
|
||||
<dimen name="floating_rotation_button_min_margin">4dp</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>
|
||||
|
||||
@@ -140,5 +140,8 @@ interface ISystemUiProxy {
|
||||
/** Notifies that a swipe-up gesture has started */
|
||||
oneway void notifySwipeUpGestureStarted() = 46;
|
||||
|
||||
// Next id = 47
|
||||
/** Notifies when taskbar status updated */
|
||||
oneway void notifyTaskbarStatus(boolean visible, boolean stashed) = 47;
|
||||
|
||||
// Next id = 48
|
||||
}
|
||||
|
||||
@@ -383,6 +383,13 @@ public class NavigationBar implements View.OnAttachStateChangeListener,
|
||||
mNavigationBarView.getRotationButtonController().setSkipOverrideUserLockPrefsOnce();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskbarStatusUpdated(boolean visible, boolean stashed) {
|
||||
mNavigationBarView
|
||||
.getFloatingRotationButton()
|
||||
.onTaskbarStateChanged(visible, stashed);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onToggleRecentApps() {
|
||||
// The same case as onOverviewShown but only for 3-button navigation.
|
||||
|
||||
@@ -69,6 +69,7 @@ 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;
|
||||
@@ -275,14 +276,23 @@ public class NavigationBarView extends FrameLayout implements
|
||||
false /* inScreen */, false /* useNearestRegion */));
|
||||
};
|
||||
|
||||
private final Consumer<Boolean> mRotationButtonListener = (visible) -> {
|
||||
if (visible) {
|
||||
// If the button will actually become visible and the navbar is about to hide,
|
||||
// tell the statusbar to keep it around for longer
|
||||
mAutoHideController.touchAutoHide();
|
||||
}
|
||||
notifyActiveTouchRegions();
|
||||
};
|
||||
private final RotationButtonUpdatesCallback mRotationButtonListener =
|
||||
new RotationButtonUpdatesCallback() {
|
||||
@Override
|
||||
public void onVisibilityChanged(boolean visible) {
|
||||
if (visible) {
|
||||
// If the button will actually become visible and the navbar is about
|
||||
// to hide, tell the statusbar to keep it around for longer
|
||||
mAutoHideController.touchAutoHide();
|
||||
}
|
||||
notifyActiveTouchRegions();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPositionChanged() {
|
||||
notifyActiveTouchRegions();
|
||||
}
|
||||
};
|
||||
|
||||
private final Consumer<Boolean> mNavbarOverlayVisibilityChangeCallback = (visible) -> {
|
||||
if (visible) {
|
||||
|
||||
@@ -20,12 +20,10 @@ import android.view.View;
|
||||
|
||||
import com.android.systemui.navigationbar.buttons.KeyButtonDrawable;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/** Interface of a rotation button that interacts {@link RotationButtonController}. */
|
||||
public interface RotationButton {
|
||||
void setRotationButtonController(RotationButtonController rotationButtonController);
|
||||
void setVisibilityChangedCallback(Consumer<Boolean> visibilityChangedCallback);
|
||||
void setUpdatesCallback(RotationButtonUpdatesCallback updatesCallback);
|
||||
View getCurrentView();
|
||||
boolean show();
|
||||
boolean hide();
|
||||
@@ -39,4 +37,12 @@ public interface RotationButton {
|
||||
default boolean acceptRotationProposal() {
|
||||
return getCurrentView() != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Callback for updates provided by a rotation button
|
||||
*/
|
||||
interface RotationButtonUpdatesCallback {
|
||||
void onVisibilityChanged(boolean isVisible);
|
||||
void onPositionChanged();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,6 +46,7 @@ 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.systemui.shared.recents.utilities.Utilities;
|
||||
import com.android.systemui.shared.recents.utilities.ViewRippler;
|
||||
@@ -140,12 +141,12 @@ public class RotationButtonController {
|
||||
}
|
||||
|
||||
void setRotationButton(RotationButton rotationButton,
|
||||
Consumer<Boolean> visibilityChangedCallback) {
|
||||
RotationButtonUpdatesCallback updatesCallback) {
|
||||
mRotationButton = rotationButton;
|
||||
mRotationButton.setRotationButtonController(this);
|
||||
mRotationButton.setOnClickListener(this::onRotateSuggestionClick);
|
||||
mRotationButton.setOnHoverListener(this::onRotateSuggestionHover);
|
||||
mRotationButton.setVisibilityChangedCallback(visibilityChangedCallback);
|
||||
mRotationButton.setUpdatesCallback(updatesCallback);
|
||||
}
|
||||
|
||||
void registerListeners() {
|
||||
|
||||
@@ -23,10 +23,6 @@ import android.view.View;
|
||||
|
||||
import com.android.systemui.navigationbar.RotationButton;
|
||||
import com.android.systemui.navigationbar.RotationButtonController;
|
||||
import com.android.systemui.navigationbar.buttons.ContextualButton;
|
||||
import com.android.systemui.navigationbar.buttons.KeyButtonDrawable;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/** Containing logic for the rotation button in nav bar. */
|
||||
public class RotationContextButton extends ContextualButton implements RotationButton {
|
||||
@@ -48,13 +44,10 @@ public class RotationContextButton extends ContextualButton implements RotationB
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisibilityChangedCallback(Consumer<Boolean> visibilityChangedCallback) {
|
||||
setListener(new ContextButtonListener() {
|
||||
@Override
|
||||
public void onVisibilityChanged(ContextualButton button, boolean visible) {
|
||||
if (visibilityChangedCallback != null) {
|
||||
visibilityChangedCallback.accept(visible);
|
||||
}
|
||||
public void setUpdatesCallback(RotationButtonUpdatesCallback updatesCallback) {
|
||||
setListener((button, visible) -> {
|
||||
if (updatesCallback != null) {
|
||||
updatesCallback.onVisibilityChanged(visible);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -20,48 +20,72 @@ import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.view.Gravity;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Surface;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
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 java.util.function.Consumer;
|
||||
|
||||
/** Containing logic for the rotation button on the physical left bottom corner of the screen. */
|
||||
/**
|
||||
* 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 Context mContext;
|
||||
private final WindowManager mWindowManager;
|
||||
private final ViewGroup mKeyButtonContainer;
|
||||
private final KeyButtonView mKeyButtonView;
|
||||
private final int mDiameter;
|
||||
private final int mMargin;
|
||||
|
||||
private final int mContainerSize;
|
||||
|
||||
private KeyButtonDrawable mKeyButtonDrawable;
|
||||
private boolean mIsShowing;
|
||||
private boolean mCanShow = true;
|
||||
private int mDisplayRotation;
|
||||
|
||||
private boolean mIsTaskbarVisible = false;
|
||||
private boolean mIsTaskbarStashed = false;
|
||||
|
||||
private final FloatingRotationButtonPositionCalculator mPositionCalculator;
|
||||
|
||||
private RotationButtonController mRotationButtonController;
|
||||
private Consumer<Boolean> mVisibilityChangedCallback;
|
||||
private RotationButtonUpdatesCallback mUpdatesCallback;
|
||||
private Position mPosition;
|
||||
|
||||
public FloatingRotationButton(Context context) {
|
||||
mContext = context;
|
||||
mWindowManager = (WindowManager) mContext.getSystemService(Context.WINDOW_SERVICE);
|
||||
mKeyButtonView = (KeyButtonView) LayoutInflater.from(mContext).inflate(
|
||||
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);
|
||||
|
||||
Resources res = mContext.getResources();
|
||||
mDiameter = res.getDimensionPixelSize(R.dimen.floating_rotation_button_diameter);
|
||||
mMargin = Math.max(res.getDimensionPixelSize(R.dimen.floating_rotation_button_min_margin),
|
||||
Resources res = context.getResources();
|
||||
|
||||
int defaultMargin = Math.max(
|
||||
res.getDimensionPixelSize(R.dimen.floating_rotation_button_min_margin),
|
||||
res.getDimensionPixelSize(R.dimen.rounded_corner_content_padding));
|
||||
|
||||
int taskbarMarginLeft =
|
||||
res.getDimensionPixelSize(R.dimen.floating_rotation_button_taskbar_left_margin);
|
||||
int taskbarMarginBottom =
|
||||
res.getDimensionPixelSize(R.dimen.floating_rotation_button_taskbar_bottom_margin);
|
||||
|
||||
mPositionCalculator = new FloatingRotationButtonPositionCalculator(defaultMargin,
|
||||
taskbarMarginLeft, taskbarMarginBottom);
|
||||
|
||||
final int diameter = res.getDimensionPixelSize(R.dimen.floating_rotation_button_diameter);
|
||||
mContainerSize = diameter + Math.max(defaultMargin, Math.max(taskbarMarginLeft,
|
||||
taskbarMarginBottom));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -72,8 +96,8 @@ public class FloatingRotationButton implements RotationButton {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setVisibilityChangedCallback(Consumer<Boolean> visibilityChangedCallback) {
|
||||
mVisibilityChangedCallback = visibilityChangedCallback;
|
||||
public void setUpdatesCallback(RotationButtonUpdatesCallback updatesCallback) {
|
||||
mUpdatesCallback = updatesCallback;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -86,45 +110,39 @@ public class FloatingRotationButton implements RotationButton {
|
||||
if (!mCanShow || mIsShowing) {
|
||||
return false;
|
||||
}
|
||||
|
||||
mIsShowing = true;
|
||||
int flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
|
||||
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(mDiameter, mDiameter,
|
||||
mMargin, mMargin, WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, flags,
|
||||
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
|
||||
mContainerSize,
|
||||
mContainerSize,
|
||||
0, 0, WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL, flags,
|
||||
PixelFormat.TRANSLUCENT);
|
||||
|
||||
lp.privateFlags |= WindowManager.LayoutParams.SYSTEM_FLAG_SHOW_FOR_ALL_USERS;
|
||||
lp.setTitle("FloatingRotationButton");
|
||||
lp.setFitInsetsTypes(0 /*types */);
|
||||
switch (mWindowManager.getDefaultDisplay().getRotation()) {
|
||||
case Surface.ROTATION_0:
|
||||
lp.gravity = Gravity.BOTTOM | Gravity.LEFT;
|
||||
break;
|
||||
case Surface.ROTATION_90:
|
||||
lp.gravity = Gravity.BOTTOM | Gravity.RIGHT;
|
||||
break;
|
||||
case Surface.ROTATION_180:
|
||||
lp.gravity = Gravity.TOP | Gravity.RIGHT;
|
||||
break;
|
||||
case Surface.ROTATION_270:
|
||||
lp.gravity = Gravity.TOP | Gravity.LEFT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
mWindowManager.addView(mKeyButtonView, lp);
|
||||
|
||||
mDisplayRotation = mWindowManager.getDefaultDisplay().getRotation();
|
||||
mPosition = mPositionCalculator
|
||||
.calculatePosition(mDisplayRotation, mIsTaskbarVisible, mIsTaskbarStashed);
|
||||
|
||||
lp.gravity = mPosition.getGravity();
|
||||
((FrameLayout.LayoutParams) mKeyButtonView.getLayoutParams()).gravity =
|
||||
mPosition.getGravity();
|
||||
|
||||
updateTranslation(mPosition, /* animate */ false);
|
||||
|
||||
mWindowManager.addView(mKeyButtonContainer, lp);
|
||||
if (mKeyButtonDrawable != null && mKeyButtonDrawable.canAnimate()) {
|
||||
mKeyButtonDrawable.resetAnimation();
|
||||
mKeyButtonDrawable.startAnimation();
|
||||
}
|
||||
mKeyButtonView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
|
||||
@Override
|
||||
public void onLayoutChange(View view, int i, int i1, int i2, int i3, int i4, int i5,
|
||||
int i6, int i7) {
|
||||
if (mIsShowing && mVisibilityChangedCallback != null) {
|
||||
mVisibilityChangedCallback.accept(true);
|
||||
}
|
||||
mKeyButtonView.removeOnLayoutChangeListener(this);
|
||||
}
|
||||
});
|
||||
|
||||
if (mUpdatesCallback != null) {
|
||||
mUpdatesCallback.onVisibilityChanged(true);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -133,10 +151,10 @@ public class FloatingRotationButton implements RotationButton {
|
||||
if (!mIsShowing) {
|
||||
return false;
|
||||
}
|
||||
mWindowManager.removeViewImmediate(mKeyButtonView);
|
||||
mWindowManager.removeViewImmediate(mKeyButtonContainer);
|
||||
mIsShowing = false;
|
||||
if (mVisibilityChangedCallback != null) {
|
||||
mVisibilityChangedCallback.accept(false);
|
||||
if (mUpdatesCallback != null) {
|
||||
mUpdatesCallback.onVisibilityChanged(false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@@ -183,4 +201,43 @@ public class FloatingRotationButton implements RotationButton {
|
||||
hide();
|
||||
}
|
||||
}
|
||||
|
||||
public void onTaskbarStateChanged(boolean taskbarVisible, boolean taskbarStashed) {
|
||||
mIsTaskbarVisible = taskbarVisible;
|
||||
mIsTaskbarStashed = taskbarStashed;
|
||||
|
||||
if (!mIsShowing) return;
|
||||
|
||||
final Position newPosition = mPositionCalculator
|
||||
.calculatePosition(mDisplayRotation, mIsTaskbarVisible, mIsTaskbarStashed);
|
||||
|
||||
if (newPosition.getTranslationX() != mPosition.getTranslationX()
|
||||
|| newPosition.getTranslationY() != mPosition.getTranslationY()) {
|
||||
updateTranslation(newPosition, /* animate */ true);
|
||||
mPosition = newPosition;
|
||||
}
|
||||
}
|
||||
|
||||
private void updateTranslation(Position position, boolean animate) {
|
||||
final int translationX = position.getTranslationX();
|
||||
final int translationY = position.getTranslationY();
|
||||
|
||||
if (animate) {
|
||||
mKeyButtonView
|
||||
.animate()
|
||||
.translationX(translationX)
|
||||
.translationY(translationY)
|
||||
.setDuration(MARGIN_ANIMATION_DURATION_MILLIS)
|
||||
.setInterpolator(new AccelerateDecelerateInterpolator())
|
||||
.withEndAction(() -> {
|
||||
if (mUpdatesCallback != null && mIsShowing) {
|
||||
mUpdatesCallback.onPositionChanged();
|
||||
}
|
||||
})
|
||||
.start();
|
||||
} else {
|
||||
mKeyButtonView.setTranslationX(translationX);
|
||||
mKeyButtonView.setTranslationY(translationY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
package com.android.systemui.navigationbar.gestural
|
||||
|
||||
import android.view.Gravity
|
||||
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(
|
||||
private val defaultMargin: Int,
|
||||
private val taskbarMarginLeft: Int,
|
||||
private val taskbarMarginBottom: Int
|
||||
) {
|
||||
|
||||
fun calculatePosition(
|
||||
currentRotation: Int,
|
||||
taskbarVisible: Boolean,
|
||||
taskbarStashed: Boolean
|
||||
): Position {
|
||||
|
||||
val isTaskbarSide = currentRotation == Surface.ROTATION_0
|
||||
|| currentRotation == Surface.ROTATION_90
|
||||
val useTaskbarMargin = isTaskbarSide && taskbarVisible && !taskbarStashed
|
||||
|
||||
val gravity = resolveGravity(currentRotation)
|
||||
|
||||
val marginLeft = if (useTaskbarMargin) taskbarMarginLeft else defaultMargin
|
||||
val marginBottom = if (useTaskbarMargin) taskbarMarginBottom else defaultMargin
|
||||
|
||||
val translationX =
|
||||
if (gravity and Gravity.RIGHT == Gravity.RIGHT) {
|
||||
-marginLeft
|
||||
} else {
|
||||
marginLeft
|
||||
}
|
||||
val translationY =
|
||||
if (gravity and Gravity.BOTTOM == Gravity.BOTTOM) {
|
||||
-marginBottom
|
||||
} else {
|
||||
marginBottom
|
||||
}
|
||||
|
||||
return Position(
|
||||
gravity = gravity,
|
||||
translationX = translationX,
|
||||
translationY = translationY
|
||||
)
|
||||
}
|
||||
|
||||
data class Position(
|
||||
val gravity: Int,
|
||||
val translationX: Int,
|
||||
val translationY: Int
|
||||
)
|
||||
|
||||
private fun resolveGravity(rotation: Int): Int =
|
||||
when (rotation) {
|
||||
Surface.ROTATION_0 -> Gravity.BOTTOM or Gravity.LEFT
|
||||
Surface.ROTATION_90 -> Gravity.BOTTOM or Gravity.RIGHT
|
||||
Surface.ROTATION_180 -> Gravity.TOP or Gravity.RIGHT
|
||||
Surface.ROTATION_270 -> Gravity.TOP or Gravity.LEFT
|
||||
else -> throw IllegalArgumentException("Invalid rotation $rotation")
|
||||
}
|
||||
}
|
||||
@@ -110,6 +110,7 @@ import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
@@ -172,55 +173,34 @@ public class OverviewProxyService extends CurrentUserTracker implements
|
||||
public ISystemUiProxy mSysUiProxy = new ISystemUiProxy.Stub() {
|
||||
@Override
|
||||
public void startScreenPinning(int taskId) {
|
||||
if (!verifyCaller("startScreenPinning")) {
|
||||
return;
|
||||
}
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mHandler.post(() -> {
|
||||
verifyCallerAndClearCallingIdentityPostMain("startScreenPinning", () ->
|
||||
mStatusBarOptionalLazy.ifPresent(
|
||||
statusBarLazy -> statusBarLazy.get().showScreenPinningRequest(taskId,
|
||||
false /* allowCancel */));
|
||||
});
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
false /* allowCancel */)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stopScreenPinning() {
|
||||
if (!verifyCaller("stopScreenPinning")) {
|
||||
return;
|
||||
}
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mHandler.post(() -> {
|
||||
try {
|
||||
ActivityTaskManager.getService().stopSystemLockTaskMode();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG_OPS, "Failed to stop screen pinning");
|
||||
}
|
||||
});
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
verifyCallerAndClearCallingIdentityPostMain("stopScreenPinning", () -> {
|
||||
try {
|
||||
ActivityTaskManager.getService().stopSystemLockTaskMode();
|
||||
} catch (RemoteException e) {
|
||||
Log.e(TAG_OPS, "Failed to stop screen pinning");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// TODO: change the method signature to use (boolean inputFocusTransferStarted)
|
||||
@Override
|
||||
public void onStatusBarMotionEvent(MotionEvent event) {
|
||||
if (!verifyCaller("onStatusBarMotionEvent")) {
|
||||
return;
|
||||
}
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
verifyCallerAndClearCallingIdentity("onStatusBarMotionEvent", () -> {
|
||||
// TODO move this logic to message queue
|
||||
mStatusBarOptionalLazy.ifPresent(statusBarLazy -> {
|
||||
StatusBar statusBar = statusBarLazy.get();
|
||||
if (event.getActionMasked() == ACTION_DOWN) {
|
||||
statusBar.getPanelController().startExpandLatencyTracking();
|
||||
}
|
||||
mHandler.post(()-> {
|
||||
mHandler.post(() -> {
|
||||
int action = event.getActionMasked();
|
||||
if (action == ACTION_DOWN) {
|
||||
mInputFocusTransferStarted = true;
|
||||
@@ -232,50 +212,38 @@ public class OverviewProxyService extends CurrentUserTracker implements
|
||||
}
|
||||
if (action == ACTION_UP || action == ACTION_CANCEL) {
|
||||
mInputFocusTransferStarted = false;
|
||||
float velocity = (event.getY() - mInputFocusTransferStartY)
|
||||
/ (event.getEventTime() - mInputFocusTransferStartMillis);
|
||||
statusBar.onInputFocusTransfer(mInputFocusTransferStarted,
|
||||
action == ACTION_CANCEL,
|
||||
(event.getY() - mInputFocusTransferStartY)
|
||||
/ (event.getEventTime() - mInputFocusTransferStartMillis));
|
||||
velocity);
|
||||
}
|
||||
event.recycle();
|
||||
});
|
||||
});
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBackPressed() throws RemoteException {
|
||||
if (!verifyCaller("onBackPressed")) {
|
||||
return;
|
||||
}
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mHandler.post(() -> {
|
||||
sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK);
|
||||
sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK);
|
||||
verifyCallerAndClearCallingIdentityPostMain("onBackPressed", () -> {
|
||||
sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK);
|
||||
sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK);
|
||||
|
||||
notifyBackAction(true, -1, -1, true, false);
|
||||
});
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
notifyBackAction(true, -1, -1, true, false);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setHomeRotationEnabled(boolean enabled) {
|
||||
if (!verifyCaller("setHomeRotationEnabled")) {
|
||||
return;
|
||||
}
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mHandler.post(() -> {
|
||||
mHandler.post(() -> notifyHomeRotationEnabled(enabled));
|
||||
});
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
verifyCallerAndClearCallingIdentityPostMain("setHomeRotationEnabled", () ->
|
||||
mHandler.post(() -> notifyHomeRotationEnabled(enabled)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyTaskbarStatus(boolean visible, boolean stashed) {
|
||||
verifyCallerAndClearCallingIdentityPostMain("notifyTaskbarStatus", () ->
|
||||
onTaskbarStatusUpdated(visible, stashed));
|
||||
}
|
||||
|
||||
private boolean sendEvent(int action, int code) {
|
||||
@@ -292,124 +260,74 @@ public class OverviewProxyService extends CurrentUserTracker implements
|
||||
|
||||
@Override
|
||||
public void onOverviewShown(boolean fromHome) {
|
||||
if (!verifyCaller("onOverviewShown")) {
|
||||
return;
|
||||
}
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mHandler.post(() -> {
|
||||
for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
|
||||
mConnectionCallbacks.get(i).onOverviewShown(fromHome);
|
||||
}
|
||||
});
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
verifyCallerAndClearCallingIdentityPostMain("onOverviewShown", () -> {
|
||||
for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
|
||||
mConnectionCallbacks.get(i).onOverviewShown(fromHome);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public Rect getNonMinimizedSplitScreenSecondaryBounds() {
|
||||
if (!verifyCaller("getNonMinimizedSplitScreenSecondaryBounds")) {
|
||||
return null;
|
||||
}
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
return mLegacySplitScreenOptional.map(splitScreen ->
|
||||
splitScreen.getDividerView().getNonMinimizedSplitScreenSecondaryBounds())
|
||||
.orElse(null);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
return verifyCallerAndClearCallingIdentity(
|
||||
"getNonMinimizedSplitScreenSecondaryBounds",
|
||||
() -> mLegacySplitScreenOptional.map(splitScreen ->
|
||||
splitScreen
|
||||
.getDividerView()
|
||||
.getNonMinimizedSplitScreenSecondaryBounds())
|
||||
.orElse(null)
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNavBarButtonAlpha(float alpha, boolean animate) {
|
||||
if (!verifyCaller("setNavBarButtonAlpha")) {
|
||||
return;
|
||||
}
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mNavBarButtonAlpha = alpha;
|
||||
mHandler.post(() -> notifyNavBarButtonAlphaChanged(alpha, animate));
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
verifyCallerAndClearCallingIdentityPostMain("setNavBarButtonAlpha", () ->
|
||||
notifyNavBarButtonAlphaChanged(alpha, animate));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAssistantProgress(@FloatRange(from = 0.0, to = 1.0) float progress) {
|
||||
if (!verifyCaller("onAssistantProgress")) {
|
||||
return;
|
||||
}
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mHandler.post(() -> notifyAssistantProgress(progress));
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
verifyCallerAndClearCallingIdentityPostMain("onAssistantProgress", () ->
|
||||
notifyAssistantProgress(progress));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAssistantGestureCompletion(float velocity) {
|
||||
if (!verifyCaller("onAssistantGestureCompletion")) {
|
||||
return;
|
||||
}
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mHandler.post(() -> notifyAssistantGestureCompletion(velocity));
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
verifyCallerAndClearCallingIdentityPostMain("onAssistantGestureCompletion", () ->
|
||||
notifyAssistantGestureCompletion(velocity));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void startAssistant(Bundle bundle) {
|
||||
if (!verifyCaller("startAssistant")) {
|
||||
return;
|
||||
}
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mHandler.post(() -> notifyStartAssistant(bundle));
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
verifyCallerAndClearCallingIdentityPostMain("startAssistant", () ->
|
||||
notifyStartAssistant(bundle));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyAccessibilityButtonClicked(int displayId) {
|
||||
if (!verifyCaller("notifyAccessibilityButtonClicked")) {
|
||||
return;
|
||||
}
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
AccessibilityManager.getInstance(mContext)
|
||||
.notifyAccessibilityButtonClicked(displayId);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
verifyCallerAndClearCallingIdentity("notifyAccessibilityButtonClicked", () ->
|
||||
AccessibilityManager.getInstance(mContext)
|
||||
.notifyAccessibilityButtonClicked(displayId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyAccessibilityButtonLongClicked() {
|
||||
if (!verifyCaller("notifyAccessibilityButtonLongClicked")) {
|
||||
return;
|
||||
}
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
final Intent intent =
|
||||
new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON);
|
||||
final String chooserClassName = AccessibilityButtonChooserActivity.class.getName();
|
||||
intent.setClassName(CHOOSER_PACKAGE_NAME, chooserClassName);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
mContext.startActivityAsUser(intent, UserHandle.CURRENT);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
verifyCallerAndClearCallingIdentity("notifyAccessibilityButtonLongClicked",
|
||||
() -> {
|
||||
final Intent intent =
|
||||
new Intent(AccessibilityManager.ACTION_CHOOSE_ACCESSIBILITY_BUTTON);
|
||||
final String chooserClassName = AccessibilityButtonChooserActivity
|
||||
.class.getName();
|
||||
intent.setClassName(CHOOSER_PACKAGE_NAME, chooserClassName);
|
||||
intent.addFlags(
|
||||
Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TASK);
|
||||
mContext.startActivityAsUser(intent, UserHandle.CURRENT);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleImageAsScreenshot(Bitmap screenImage, Rect locationInScreen,
|
||||
Insets visibleInsets, int taskId) {
|
||||
Insets visibleInsets, int taskId) {
|
||||
// Deprecated
|
||||
}
|
||||
|
||||
@@ -421,43 +339,22 @@ public class OverviewProxyService extends CurrentUserTracker implements
|
||||
|
||||
@Override
|
||||
public void notifySwipeToHomeFinished() {
|
||||
if (!verifyCaller("notifySwipeToHomeFinished")) {
|
||||
return;
|
||||
}
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mPipOptional.ifPresent(
|
||||
pip -> pip.setPinnedStackAnimationType(
|
||||
PipAnimationController.ANIM_TYPE_ALPHA));
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
verifyCallerAndClearCallingIdentity("notifySwipeToHomeFinished", () ->
|
||||
mPipOptional.ifPresent(
|
||||
pip -> pip.setPinnedStackAnimationType(
|
||||
PipAnimationController.ANIM_TYPE_ALPHA)));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifySwipeUpGestureStarted() {
|
||||
if (!verifyCaller("notifySwipeUpGestureStarted")) {
|
||||
return;
|
||||
}
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mHandler.post(() -> notifySwipeUpGestureStartedInternal());
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
verifyCallerAndClearCallingIdentityPostMain("notifySwipeUpGestureStarted", () ->
|
||||
notifySwipeUpGestureStartedInternal());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void notifyPrioritizedRotation(@Surface.Rotation int rotation) {
|
||||
if (!verifyCaller("notifyPrioritizedRotation")) {
|
||||
return;
|
||||
}
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mHandler.post(() -> notifyPrioritizedRotationInternal(rotation));
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
verifyCallerAndClearCallingIdentityPostMain("notifyPrioritizedRotation", () ->
|
||||
notifyPrioritizedRotationInternal(rotation));
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -477,15 +374,8 @@ public class OverviewProxyService extends CurrentUserTracker implements
|
||||
|
||||
@Override
|
||||
public void expandNotificationPanel() {
|
||||
if (!verifyCaller("expandNotificationPanel")) {
|
||||
return;
|
||||
}
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
mCommandQueue.handleSystemKey(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN);
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
verifyCallerAndClearCallingIdentity("expandNotificationPanel",
|
||||
() -> mCommandQueue.handleSystemKey(KeyEvent.KEYCODE_SYSTEM_NAVIGATION_DOWN));
|
||||
}
|
||||
|
||||
private boolean verifyCaller(String reason) {
|
||||
@@ -497,6 +387,29 @@ public class OverviewProxyService extends CurrentUserTracker implements
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private <T> T verifyCallerAndClearCallingIdentity(String reason, Supplier<T> supplier) {
|
||||
if (!verifyCaller(reason)) {
|
||||
return null;
|
||||
}
|
||||
final long token = Binder.clearCallingIdentity();
|
||||
try {
|
||||
return supplier.get();
|
||||
} finally {
|
||||
Binder.restoreCallingIdentity(token);
|
||||
}
|
||||
}
|
||||
|
||||
private void verifyCallerAndClearCallingIdentity(String reason, Runnable runnable) {
|
||||
verifyCallerAndClearCallingIdentity(reason, () -> {
|
||||
runnable.run();
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
private void verifyCallerAndClearCallingIdentityPostMain(String reason, Runnable runnable) {
|
||||
verifyCallerAndClearCallingIdentity(reason, () -> mHandler.post(runnable));
|
||||
}
|
||||
};
|
||||
|
||||
private final Runnable mDeferredConnectionCallback = () -> {
|
||||
@@ -883,6 +796,12 @@ public class OverviewProxyService extends CurrentUserTracker implements
|
||||
}
|
||||
}
|
||||
|
||||
private void onTaskbarStatusUpdated(boolean visible, boolean stashed) {
|
||||
for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
|
||||
mConnectionCallbacks.get(i).onTaskbarStatusUpdated(visible, stashed);
|
||||
}
|
||||
}
|
||||
|
||||
private void notifyConnectionChanged() {
|
||||
for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
|
||||
mConnectionCallbacks.get(i).onConnectionChanged(mOverviewProxy != null);
|
||||
@@ -1049,6 +968,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
|
||||
/** Notify changes in the nav bar button alpha */
|
||||
default void onNavBarButtonAlphaChanged(float alpha, boolean animate) {}
|
||||
default void onHomeRotationEnabled(boolean enabled) {}
|
||||
default void onTaskbarStatusUpdated(boolean visible, boolean stashed) {}
|
||||
default void onSystemUiStateChanged(int sysuiStateFlags) {}
|
||||
default void onAssistantProgress(@FloatRange(from = 0.0, to = 1.0) float progress) {}
|
||||
default void onAssistantGestureCompletion(float velocity) {}
|
||||
|
||||
@@ -59,7 +59,16 @@ public class NavigationBarRotationContextTest extends SysuiTestCase {
|
||||
final View view = new View(mContext);
|
||||
mRotationButton = mock(RotationButton.class);
|
||||
mRotationButtonController = new RotationButtonController(mContext, 0, 0);
|
||||
mRotationButtonController.setRotationButton(mRotationButton, (visibility) -> {});
|
||||
mRotationButtonController.setRotationButton(mRotationButton,
|
||||
new RotationButton.RotationButtonUpdatesCallback() {
|
||||
@Override
|
||||
public void onVisibilityChanged(boolean isVisible) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPositionChanged() {
|
||||
}
|
||||
});
|
||||
// Due to a mockito issue, only spy the object after setting the initial state
|
||||
mRotationButtonController = spy(mRotationButtonController);
|
||||
doReturn(view).when(mRotationButton).getCurrentView();
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
package com.android.systemui.navigationbar.gestural
|
||||
|
||||
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.google.common.truth.Truth.assertThat
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.junit.runners.Parameterized
|
||||
|
||||
@RunWith(Parameterized::class)
|
||||
@SmallTest
|
||||
internal class FloatingRotationButtonPositionCalculatorTest(private val testCase: TestCase)
|
||||
: SysuiTestCase() {
|
||||
|
||||
private val calculator = FloatingRotationButtonPositionCalculator(
|
||||
MARGIN_DEFAULT, MARGIN_TASKBAR_LEFT, MARGIN_TASKBAR_BOTTOM
|
||||
)
|
||||
|
||||
@Test
|
||||
fun calculatePosition() {
|
||||
val position = calculator.calculatePosition(
|
||||
testCase.rotation,
|
||||
testCase.taskbarVisible,
|
||||
testCase.taskbarStashed
|
||||
)
|
||||
|
||||
assertThat(position).isEqualTo(testCase.expectedPosition)
|
||||
}
|
||||
|
||||
internal class TestCase(
|
||||
val rotation: Int,
|
||||
val taskbarVisible: Boolean,
|
||||
val taskbarStashed: Boolean,
|
||||
val expectedPosition: Position
|
||||
) {
|
||||
override fun toString(): String =
|
||||
"when rotation = $rotation, " +
|
||||
"taskbarVisible = $taskbarVisible, " +
|
||||
"taskbarStashed = $taskbarStashed - " +
|
||||
"expected $expectedPosition"
|
||||
}
|
||||
|
||||
companion object {
|
||||
@Parameterized.Parameters(name = "{0}")
|
||||
@JvmStatic
|
||||
fun getParams(): Collection<TestCase> =
|
||||
listOf(
|
||||
TestCase(
|
||||
rotation = Surface.ROTATION_0,
|
||||
taskbarVisible = false,
|
||||
taskbarStashed = false,
|
||||
expectedPosition = Position(
|
||||
gravity = Gravity.BOTTOM or Gravity.LEFT,
|
||||
translationX = MARGIN_DEFAULT,
|
||||
translationY = -MARGIN_DEFAULT
|
||||
)
|
||||
),
|
||||
TestCase(
|
||||
rotation = Surface.ROTATION_90,
|
||||
taskbarVisible = false,
|
||||
taskbarStashed = false,
|
||||
expectedPosition = Position(
|
||||
gravity = Gravity.BOTTOM or Gravity.RIGHT,
|
||||
translationX = -MARGIN_DEFAULT,
|
||||
translationY = -MARGIN_DEFAULT
|
||||
)
|
||||
),
|
||||
TestCase(
|
||||
rotation = Surface.ROTATION_180,
|
||||
taskbarVisible = false,
|
||||
taskbarStashed = false,
|
||||
expectedPosition = Position(
|
||||
gravity = Gravity.TOP or Gravity.RIGHT,
|
||||
translationX = -MARGIN_DEFAULT,
|
||||
translationY = MARGIN_DEFAULT
|
||||
)
|
||||
),
|
||||
TestCase(
|
||||
rotation = Surface.ROTATION_270,
|
||||
taskbarVisible = false,
|
||||
taskbarStashed = false,
|
||||
expectedPosition = Position(
|
||||
gravity = Gravity.TOP or Gravity.LEFT,
|
||||
translationX = MARGIN_DEFAULT,
|
||||
translationY = MARGIN_DEFAULT
|
||||
)
|
||||
),
|
||||
TestCase(
|
||||
rotation = Surface.ROTATION_0,
|
||||
taskbarVisible = true,
|
||||
taskbarStashed = false,
|
||||
expectedPosition = Position(
|
||||
gravity = Gravity.BOTTOM or Gravity.LEFT,
|
||||
translationX = MARGIN_TASKBAR_LEFT,
|
||||
translationY = -MARGIN_TASKBAR_BOTTOM
|
||||
)
|
||||
),
|
||||
TestCase(
|
||||
rotation = Surface.ROTATION_0,
|
||||
taskbarVisible = true,
|
||||
taskbarStashed = true,
|
||||
expectedPosition = Position(
|
||||
gravity = Gravity.BOTTOM or Gravity.LEFT,
|
||||
translationX = MARGIN_DEFAULT,
|
||||
translationY = -MARGIN_DEFAULT
|
||||
)
|
||||
),
|
||||
TestCase(
|
||||
rotation = Surface.ROTATION_90,
|
||||
taskbarVisible = true,
|
||||
taskbarStashed = false,
|
||||
expectedPosition = Position(
|
||||
gravity = Gravity.BOTTOM or Gravity.RIGHT,
|
||||
translationX = -MARGIN_TASKBAR_LEFT,
|
||||
translationY = -MARGIN_TASKBAR_BOTTOM
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
private const val MARGIN_DEFAULT = 10
|
||||
private const val MARGIN_TASKBAR_LEFT = 20
|
||||
private const val MARGIN_TASKBAR_BOTTOM = 30
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user