Merge "Exposing functionality to pin Taskbar from TaskbarDividerPopupView." into udc-dev
This commit is contained in:
@@ -13,16 +13,16 @@
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/taskbar_divider_container"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
<FrameLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="@dimen/taskbar_icon_min_touch_size"
|
||||
android:layout_height="@dimen/taskbar_icon_min_touch_size"
|
||||
android:contentDescription="@string/taskbar_divider_a11y_title"
|
||||
android:backgroundTint="@android:color/transparent">
|
||||
|
||||
<View
|
||||
android:id="@+id/taskbar_divider_bar"
|
||||
android:layout_height="32dp"
|
||||
android:layout_width="2dp"
|
||||
android:layout_gravity="center"
|
||||
android:background="@drawable/bg_rounded_corner_bottom_sheet_handle" />
|
||||
<!-- TODO(b/265347148): Create separate drawable -->
|
||||
</FrameLayout>
|
||||
android:background="@drawable/taskbar_divider_bg" />
|
||||
</FrameLayout>
|
||||
@@ -0,0 +1,98 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
Copyright (C) 2023 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.
|
||||
-->
|
||||
<com.android.launcher3.taskbar.TaskbarDividerPopupView
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="@dimen/taskbar_pinning_popup_menu_width"
|
||||
android:layout_height="wrap_content"
|
||||
android:focusable="true"
|
||||
android:background="@drawable/popup_background_material_u"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/taskbar_switch_option"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="52dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:elevation="2dp"
|
||||
android:focusable="true"
|
||||
android:clickable="true"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:background="@drawable/top_rounded_popup_ripple"
|
||||
android:paddingEnd="10dp"
|
||||
android:paddingStart="10dp"
|
||||
android:theme="@style/PopupItem">
|
||||
|
||||
<View
|
||||
android:layout_margin="6dp"
|
||||
android:layout_width="20dp"
|
||||
android:layout_height="20dp"
|
||||
android:background="@drawable/ic_visibility"
|
||||
android:backgroundTint="?android:attr/textColorPrimary" />
|
||||
|
||||
<Switch
|
||||
style="@style/BaseIcon"
|
||||
android:id="@+id/taskbar_pinning_switch"
|
||||
android:background="@null"
|
||||
android:clickable="false"
|
||||
android:gravity="start|center_vertical"
|
||||
android:textAlignment="viewStart"
|
||||
android:paddingStart="12dp"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="end"
|
||||
android:textSize="14sp"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:text="@string/always_show_taskbar" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/navigation_mode_switch_option"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="52dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:elevation="2dp"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:background="@drawable/bottom_rounded_popup_ripple"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingEnd="10dp"
|
||||
android:paddingStart="10dp"
|
||||
android:theme="@style/PopupItem">
|
||||
|
||||
<View
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_margin="4dp"
|
||||
android:background="@drawable/ic_touch"
|
||||
android:backgroundTint="?android:attr/textColorPrimary" />
|
||||
|
||||
<com.android.launcher3.BubbleTextView
|
||||
style="@style/BaseIcon"
|
||||
android:id="@+id/change_navigation_mode_text"
|
||||
android:gravity="start|center_vertical"
|
||||
android:textAlignment="viewStart"
|
||||
android:paddingStart="12dp"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="end"
|
||||
android:textSize="14sp"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:text="@string/change_navigation_mode" />
|
||||
|
||||
</LinearLayout>
|
||||
</com.android.launcher3.taskbar.TaskbarDividerPopupView>
|
||||
@@ -341,6 +341,9 @@
|
||||
<dimen name="taskbar_edu_features_lottie_height">106dp</dimen>
|
||||
<dimen name="taskbar_edu_features_horizontal_spacing">24dp</dimen>
|
||||
|
||||
<!--- Taskbar Pinning -->
|
||||
<dimen name="taskbar_pinning_popup_menu_width">300dp</dimen>
|
||||
|
||||
<!-- Recents overview -->
|
||||
<dimen name="recents_filter_icon_size">30dp</dimen>
|
||||
|
||||
|
||||
@@ -277,6 +277,13 @@
|
||||
<string name="taskbar_a11y_hidden_title">Taskbar hidden</string>
|
||||
<!-- Accessibility title for the Taskbar window on phones. [CHAR_LIMIT=NONE] -->
|
||||
<string name="taskbar_phone_a11y_title">Navigation bar</string>
|
||||
<!-- Text in popup dialog for user to switch between always showing Taskbar or not. [CHAR LIMIT=30] -->
|
||||
<string name="always_show_taskbar">Always show Taskbar</string>
|
||||
<!-- Text in popup dialog for user to switch between system navigation modes. [CHAR LIMIT=30] -->
|
||||
<string name="change_navigation_mode">Change navigation mode</string>
|
||||
<!-- Accessibility title for the Taskbar vertical divider icon. [CHAR_LIMIT=NONE] -->
|
||||
<string name="taskbar_divider_a11y_title">Taskbar Divider</string>
|
||||
|
||||
|
||||
<!-- Label for moving drop target to the top or left side of the screen, depending on orientation (from the Taskbar only). -->
|
||||
<string name="move_drop_target_top_or_left">Move to top/left</string>
|
||||
|
||||
@@ -238,7 +238,8 @@ public class TaskbarActivityContext extends BaseTaskbarContext {
|
||||
? new DesktopTaskbarRecentAppsController(this)
|
||||
: TaskbarRecentAppsController.DEFAULT,
|
||||
new TaskbarEduTooltipController(this),
|
||||
new KeyboardQuickSwitchController());
|
||||
new KeyboardQuickSwitchController(),
|
||||
new TaskbarDividerPopupController(this));
|
||||
}
|
||||
|
||||
public void init(@NonNull TaskbarSharedState sharedState) {
|
||||
|
||||
@@ -60,6 +60,7 @@ public class TaskbarControllers {
|
||||
public final TaskbarOverlayController taskbarOverlayController;
|
||||
public final TaskbarEduTooltipController taskbarEduTooltipController;
|
||||
public final KeyboardQuickSwitchController keyboardQuickSwitchController;
|
||||
public final TaskbarDividerPopupController taskbarPinningController;
|
||||
|
||||
@Nullable private LoggableTaskbarController[] mControllersToLog = null;
|
||||
@Nullable private BackgroundRendererController[] mBackgroundRendererControllers = null;
|
||||
@@ -105,7 +106,8 @@ public class TaskbarControllers {
|
||||
TaskbarSpringOnStashController taskbarSpringOnStashController,
|
||||
TaskbarRecentAppsController taskbarRecentAppsController,
|
||||
TaskbarEduTooltipController taskbarEduTooltipController,
|
||||
KeyboardQuickSwitchController keyboardQuickSwitchController) {
|
||||
KeyboardQuickSwitchController keyboardQuickSwitchController,
|
||||
TaskbarDividerPopupController taskbarPinningController) {
|
||||
this.taskbarActivityContext = taskbarActivityContext;
|
||||
this.taskbarDragController = taskbarDragController;
|
||||
this.navButtonController = navButtonController;
|
||||
@@ -130,6 +132,7 @@ public class TaskbarControllers {
|
||||
this.taskbarRecentAppsController = taskbarRecentAppsController;
|
||||
this.taskbarEduTooltipController = taskbarEduTooltipController;
|
||||
this.keyboardQuickSwitchController = keyboardQuickSwitchController;
|
||||
this.taskbarPinningController = taskbarPinningController;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -163,6 +166,7 @@ public class TaskbarControllers {
|
||||
taskbarTranslationController.init(this);
|
||||
taskbarEduTooltipController.init(this);
|
||||
keyboardQuickSwitchController.init(this);
|
||||
taskbarPinningController.init(this);
|
||||
|
||||
mControllersToLog = new LoggableTaskbarController[] {
|
||||
taskbarDragController, navButtonController, navbarButtonsViewController,
|
||||
@@ -171,7 +175,7 @@ public class TaskbarControllers {
|
||||
stashedHandleViewController, taskbarStashController,
|
||||
taskbarAutohideSuspendController, taskbarPopupController, taskbarInsetsController,
|
||||
voiceInteractionWindowController, taskbarTranslationController,
|
||||
taskbarEduTooltipController, keyboardQuickSwitchController
|
||||
taskbarEduTooltipController, keyboardQuickSwitchController, taskbarPinningController
|
||||
};
|
||||
mBackgroundRendererControllers = new BackgroundRendererController[] {
|
||||
taskbarDragLayerController, taskbarScrimViewController,
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* Copyright (C) 2023 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.launcher3.taskbar
|
||||
|
||||
import android.view.View
|
||||
import com.android.launcher3.LauncherPrefs
|
||||
import com.android.launcher3.LauncherPrefs.Companion.TASKBAR_PINNING
|
||||
import com.android.launcher3.taskbar.TaskbarDividerPopupView.Companion.createAndPopulate
|
||||
import java.io.PrintWriter
|
||||
|
||||
/** Controls taskbar pinning through a popup view. */
|
||||
class TaskbarDividerPopupController(private val context: TaskbarActivityContext) :
|
||||
TaskbarControllers.LoggableTaskbarController {
|
||||
|
||||
private lateinit var controllers: TaskbarControllers
|
||||
private val launcherPrefs = LauncherPrefs.get(context)
|
||||
|
||||
fun init(taskbarControllers: TaskbarControllers) {
|
||||
controllers = taskbarControllers
|
||||
}
|
||||
|
||||
fun showPinningView(view: View) {
|
||||
context.isTaskbarWindowFullscreen = true
|
||||
|
||||
view.post {
|
||||
val popupView = createAndPopulate(view, context)
|
||||
popupView.requestFocus()
|
||||
popupView.onCloseCallback = {
|
||||
context.onPopupVisibilityChanged(false)
|
||||
if (launcherPrefs.get(TASKBAR_PINNING)) {
|
||||
animateTransientToPersistentTaskBar()
|
||||
} else {
|
||||
animatePersistentToTransientTaskbar()
|
||||
}
|
||||
}
|
||||
popupView.changePreference = {
|
||||
launcherPrefs.put(TASKBAR_PINNING, !launcherPrefs.get(TASKBAR_PINNING))
|
||||
}
|
||||
context.onPopupVisibilityChanged(true)
|
||||
popupView.show()
|
||||
}
|
||||
}
|
||||
|
||||
// TODO(b/265436799): provide animation/transition from transient taskbar to persistent one
|
||||
private fun animateTransientToPersistentTaskBar() {}
|
||||
|
||||
// TODO(b/265436799): provide animation/transition from persistent taskbar to transient one
|
||||
private fun animatePersistentToTransientTaskbar() {}
|
||||
|
||||
override fun dumpLogs(prefix: String, pw: PrintWriter) {
|
||||
pw.println(prefix + "TaskbarPinningController:")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,171 @@
|
||||
/*
|
||||
* Copyright (C) 2023 The Android Open Source Project
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.android.launcher3.taskbar
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.Rect
|
||||
import android.util.AttributeSet
|
||||
import android.view.Gravity
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.Switch
|
||||
import androidx.core.view.postDelayed
|
||||
import com.android.launcher3.R
|
||||
import com.android.launcher3.popup.ArrowPopup
|
||||
import com.android.launcher3.popup.RoundedArrowDrawable
|
||||
import com.android.launcher3.util.DisplayController
|
||||
import com.android.launcher3.util.Themes
|
||||
|
||||
/** Popup view with arrow for taskbar pinning */
|
||||
class TaskbarDividerPopupView<T : TaskbarActivityContext>
|
||||
@JvmOverloads
|
||||
constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0,
|
||||
) : ArrowPopup<T>(context, attrs, defStyleAttr) {
|
||||
companion object {
|
||||
private const val TAG = "TaskbarDividerPopupView"
|
||||
private const val DIVIDER_POPUP_CLOSING_DELAY = 500L
|
||||
|
||||
@JvmStatic
|
||||
fun createAndPopulate(
|
||||
view: View,
|
||||
taskbarActivityContext: TaskbarActivityContext,
|
||||
): TaskbarDividerPopupView<*> {
|
||||
val taskMenuViewWithArrow =
|
||||
taskbarActivityContext.layoutInflater.inflate(
|
||||
R.layout.taskbar_divider_popup_menu,
|
||||
taskbarActivityContext.dragLayer,
|
||||
false
|
||||
) as TaskbarDividerPopupView<*>
|
||||
|
||||
return taskMenuViewWithArrow.populateForView(view)
|
||||
}
|
||||
}
|
||||
private lateinit var dividerView: View
|
||||
|
||||
private val menuWidth =
|
||||
context.resources.getDimensionPixelSize(R.dimen.taskbar_pinning_popup_menu_width)
|
||||
private val popupCornerRadius = Themes.getDialogCornerRadius(context)
|
||||
private val arrowWidth = resources.getDimension(R.dimen.popup_arrow_width)
|
||||
private val arrowHeight = resources.getDimension(R.dimen.popup_arrow_height)
|
||||
private val arrowPointRadius = resources.getDimension(R.dimen.popup_arrow_corner_radius)
|
||||
|
||||
private var alwaysShowTaskbarOn = !DisplayController.isTransientTaskbar(context)
|
||||
private var didPreferenceChange = false
|
||||
|
||||
/** Callback invoked when the pinning popup view is closing. */
|
||||
var onCloseCallback: () -> Unit = {}
|
||||
|
||||
/**
|
||||
* Callback invoked when the user preference changes in popup view. Preference change will be
|
||||
* based upon current value stored in [LauncherPrefs] for `TASKBAR_PINNING`
|
||||
*/
|
||||
var changePreference: () -> Unit = {}
|
||||
|
||||
init {
|
||||
// This synchronizes the arrow and menu to open at the same time
|
||||
mOpenChildFadeStartDelay = mOpenFadeStartDelay
|
||||
mOpenChildFadeDuration = mOpenFadeDuration
|
||||
mCloseFadeStartDelay = mCloseChildFadeStartDelay
|
||||
mCloseFadeDuration = mCloseChildFadeDuration
|
||||
}
|
||||
|
||||
override fun isOfType(type: Int): Boolean = type and TYPE_TASKBAR_PINNING_POPUP != 0
|
||||
|
||||
override fun getTargetObjectLocation(outPos: Rect) {
|
||||
popupContainer.getDescendantRectRelativeToSelf(dividerView, outPos)
|
||||
}
|
||||
|
||||
@SuppressLint("UseSwitchCompatOrMaterialCode")
|
||||
override fun onFinishInflate() {
|
||||
super.onFinishInflate()
|
||||
val taskbarSwitchOption = findViewById<LinearLayout>(R.id.taskbar_switch_option)
|
||||
val alwaysShowTaskbarSwitch = findViewById<Switch>(R.id.taskbar_pinning_switch)
|
||||
alwaysShowTaskbarSwitch.isChecked = alwaysShowTaskbarOn
|
||||
taskbarSwitchOption.setOnClickListener {
|
||||
alwaysShowTaskbarSwitch.isClickable = true
|
||||
alwaysShowTaskbarSwitch.isChecked = !alwaysShowTaskbarOn
|
||||
onClickAlwaysShowTaskbarSwitchOption()
|
||||
}
|
||||
}
|
||||
|
||||
/** Orient object as usual and then center object horizontally. */
|
||||
override fun orientAboutObject() {
|
||||
super.orientAboutObject()
|
||||
x = mTempRect.centerX() - menuWidth / 2f
|
||||
}
|
||||
|
||||
override fun onControllerInterceptTouchEvent(ev: MotionEvent?): Boolean {
|
||||
if (ev?.action == MotionEvent.ACTION_DOWN) {
|
||||
if (!popupContainer.isEventOverView(this, ev)) {
|
||||
close(true)
|
||||
}
|
||||
} else if (popupContainer.isEventOverView(dividerView, ev)) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun populateForView(view: View): TaskbarDividerPopupView<*> {
|
||||
dividerView = view
|
||||
return this
|
||||
}
|
||||
|
||||
override fun addArrow() {
|
||||
super.addArrow()
|
||||
// Change arrow location to the middle of popup.
|
||||
mArrow.x = (dividerView.x + dividerView.width / 2) - (mArrowWidth / 2)
|
||||
}
|
||||
|
||||
override fun updateArrowColor() {
|
||||
if (!Gravity.isVertical(mGravity)) {
|
||||
mArrow.background =
|
||||
RoundedArrowDrawable(
|
||||
arrowWidth,
|
||||
arrowHeight,
|
||||
arrowPointRadius,
|
||||
popupCornerRadius,
|
||||
measuredWidth.toFloat(),
|
||||
measuredHeight.toFloat(),
|
||||
(measuredWidth - arrowWidth) / 2, // arrowOffsetX
|
||||
0f, // arrowOffsetY
|
||||
false, // isPointingUp
|
||||
true, // leftAligned
|
||||
Themes.getAttrColor(context, R.attr.popupColorPrimary),
|
||||
)
|
||||
elevation = mElevation
|
||||
mArrow.elevation = mElevation
|
||||
}
|
||||
}
|
||||
|
||||
override fun closeComplete() {
|
||||
if (didPreferenceChange) {
|
||||
onCloseCallback()
|
||||
}
|
||||
super.closeComplete()
|
||||
}
|
||||
|
||||
private fun onClickAlwaysShowTaskbarSwitchOption() {
|
||||
didPreferenceChange = true
|
||||
changePreference()
|
||||
// Allow switch animation to finish and then close the popup.
|
||||
postDelayed(DIVIDER_POPUP_CLOSING_DELAY) { close(true) }
|
||||
}
|
||||
}
|
||||
@@ -20,6 +20,8 @@ import static android.content.pm.PackageManager.FEATURE_PC;
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
|
||||
|
||||
import static com.android.launcher3.LauncherPrefs.TASKBAR_PINNING;
|
||||
import static com.android.launcher3.LauncherPrefs.TASKBAR_PINNING_KEY;
|
||||
import static com.android.launcher3.util.DisplayController.CHANGE_DENSITY;
|
||||
import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
|
||||
import static com.android.launcher3.util.DisplayController.TASKBAR_NOT_DESTROYED_TAG;
|
||||
@@ -32,6 +34,7 @@ import android.content.ComponentCallbacks;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.ActivityInfo;
|
||||
import android.content.res.Configuration;
|
||||
import android.hardware.display.DisplayManager;
|
||||
@@ -48,6 +51,7 @@ import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.launcher3.DeviceProfile;
|
||||
import com.android.launcher3.LauncherAppState;
|
||||
import com.android.launcher3.LauncherPrefs;
|
||||
import com.android.launcher3.anim.AnimatorPlaybackController;
|
||||
import com.android.launcher3.statemanager.StatefulActivity;
|
||||
import com.android.launcher3.taskbar.unfold.NonDestroyableScopedUnfoldTransitionProgressProvider;
|
||||
@@ -133,6 +137,13 @@ public class TaskbarManager {
|
||||
private final SimpleBroadcastReceiver mTaskbarBroadcastReceiver =
|
||||
new SimpleBroadcastReceiver(this::showTaskbarFromBroadcast);
|
||||
|
||||
private final SharedPreferences.OnSharedPreferenceChangeListener
|
||||
mTaskbarPinningPreferenceChangeListener = (sharedPreferences, key) -> {
|
||||
if (TASKBAR_PINNING_KEY.equals(key)) {
|
||||
recreateTaskbar();
|
||||
}
|
||||
};
|
||||
|
||||
@SuppressLint("WrongConstant")
|
||||
public TaskbarManager(TouchInteractionService service) {
|
||||
mDisplayController = DisplayController.INSTANCE.get(service);
|
||||
@@ -249,6 +260,8 @@ public class TaskbarManager {
|
||||
private void destroyExistingTaskbar() {
|
||||
debugWhyTaskbarNotDestroyed("destroyExistingTaskbar: " + mTaskbarActivityContext);
|
||||
if (mTaskbarActivityContext != null) {
|
||||
LauncherPrefs.get(mContext).removeListener(mTaskbarPinningPreferenceChangeListener,
|
||||
TASKBAR_PINNING);
|
||||
mTaskbarActivityContext.onDestroy();
|
||||
if (!FLAG_HIDE_NAVBAR_WINDOW) {
|
||||
mTaskbarActivityContext = null;
|
||||
@@ -385,6 +398,10 @@ public class TaskbarManager {
|
||||
mTaskbarActivityContext.setUIController(
|
||||
createTaskbarUIControllerForActivity(mActivity));
|
||||
}
|
||||
|
||||
// We to wait until user unlocks the device to attach listener.
|
||||
LauncherPrefs.get(mContext).addListener(mTaskbarPinningPreferenceChangeListener,
|
||||
TASKBAR_PINNING);
|
||||
}
|
||||
|
||||
public void onSystemUiFlagsChanged(int systemUiStateFlags) {
|
||||
|
||||
@@ -18,11 +18,12 @@ package com.android.launcher3.taskbar;
|
||||
import static android.view.HapticFeedbackConstants.LONG_PRESS;
|
||||
import static android.view.accessibility.AccessibilityManager.FLAG_CONTENT_CONTROLS;
|
||||
|
||||
import static com.android.launcher3.LauncherPrefs.TASKBAR_PINNING_KEY;
|
||||
import static com.android.launcher3.anim.Interpolators.EMPHASIZED;
|
||||
import static com.android.launcher3.anim.Interpolators.FINAL_FRAME;
|
||||
import static com.android.launcher3.anim.Interpolators.INSTANT;
|
||||
import static com.android.launcher3.anim.Interpolators.LINEAR;
|
||||
import static com.android.launcher3.config.FeatureFlags.FORCE_PERSISTENT_TASKBAR;
|
||||
import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_PINNING;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_LONGPRESS_HIDE;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASKBAR_LONGPRESS_SHOW;
|
||||
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TRANSIENT_TASKBAR_HIDE;
|
||||
@@ -324,7 +325,7 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
|
||||
// that taskbar unstashes when going to 3 button mode (supportsVisualStashing() false).
|
||||
boolean isManuallyStashedInApp = supportsVisualStashing()
|
||||
&& !isTransientTaskbar
|
||||
&& !FORCE_PERSISTENT_TASKBAR.get()
|
||||
&& !ENABLE_TASKBAR_PINNING.get()
|
||||
&& mPrefs.getBoolean(SHARED_PREFS_STASHED_KEY, DEFAULT_STASHED_PREF);
|
||||
boolean isInSetup = !mActivity.isUserSetupComplete() || setupUIVisible;
|
||||
updateStateForFlag(FLAG_STASHED_IN_APP_MANUAL, isManuallyStashedInApp);
|
||||
@@ -353,7 +354,7 @@ public class TaskbarStashController implements TaskbarControllers.LoggableTaskba
|
||||
* Returns whether the user can manually stash the taskbar based on the current device state.
|
||||
*/
|
||||
protected boolean supportsManualStashing() {
|
||||
if (FORCE_PERSISTENT_TASKBAR.get()) {
|
||||
if (ENABLE_TASKBAR_PINNING.get() && mPrefs.getBoolean(TASKBAR_PINNING_KEY, false)) {
|
||||
return false;
|
||||
}
|
||||
return supportsVisualStashing()
|
||||
|
||||
@@ -218,7 +218,8 @@ public class TaskbarView extends FrameLayout implements FolderIcon.FolderIconPar
|
||||
mAllAppsButton.setOnClickListener(mControllerCallbacks.getAllAppsButtonClickListener());
|
||||
}
|
||||
if (mTaskbarDivider != null) {
|
||||
//TODO(b/265434705): set long press listener
|
||||
mTaskbarDivider.setOnLongClickListener(
|
||||
mControllerCallbacks.getTaskbarDividerLongClickListener());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -641,6 +641,13 @@ public class TaskbarViewController implements TaskbarControllers.LoggableTaskbar
|
||||
};
|
||||
}
|
||||
|
||||
public View.OnLongClickListener getTaskbarDividerLongClickListener() {
|
||||
return v -> {
|
||||
mControllers.taskbarPinningController.showPinningView(v);
|
||||
return true;
|
||||
};
|
||||
}
|
||||
|
||||
public View.OnLongClickListener getIconOnLongClickListener() {
|
||||
return mControllers.taskbarDragController::startDragOnLongClick;
|
||||
}
|
||||
|
||||
@@ -53,6 +53,7 @@ abstract class TaskbarBaseTestCase {
|
||||
@Mock lateinit var taskbarOverlayController: TaskbarOverlayController
|
||||
@Mock lateinit var taskbarEduTooltipController: TaskbarEduTooltipController
|
||||
@Mock lateinit var keyboardQuickSwitchController: KeyboardQuickSwitchController
|
||||
@Mock lateinit var taskbarPinningController: TaskbarDividerPopupController
|
||||
|
||||
lateinit var taskbarControllers: TaskbarControllers
|
||||
|
||||
@@ -91,7 +92,8 @@ abstract class TaskbarBaseTestCase {
|
||||
taskbarSpringOnStashController,
|
||||
taskbarRecentAppsController,
|
||||
taskbarEduTooltipController,
|
||||
keyboardQuickSwitchController
|
||||
keyboardQuickSwitchController,
|
||||
taskbarPinningController,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2023 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.
|
||||
-->
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="?android:attr/colorControlHighlight">
|
||||
<item android:id="@android:id/mask">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#FFFFFFFF"/>
|
||||
<corners android:bottomLeftRadius="@dimen/dialogCornerRadius"
|
||||
android:bottomRightRadius="@dimen/dialogCornerRadius"
|
||||
android:topLeftRadius="0dp"
|
||||
android:topRightRadius="0dp"/>
|
||||
</shape>
|
||||
</item>
|
||||
</ripple>
|
||||
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2023 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.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="?attr/colorControlNormal">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M18.19,12.44l-3.24,-1.62c1.29,-1 2.12,-2.56 2.12,-4.32c0,-3.03 -2.47,-5.5 -5.5,-5.5s-5.5,2.47 -5.5,5.5c0,2.13 1.22,3.98 3,4.89v3.26c-2.11,-0.45 -2.01,-0.44 -2.26,-0.44c-0.53,0 -1.03,0.21 -1.41,0.59L4,16.22l5.09,5.09C9.52,21.75 10.12,22 10.74,22h6.3c0.98,0 1.81,-0.7 1.97,-1.67l0.8,-4.71C20.03,14.32 19.38,13.04 18.19,12.44zM17.84,15.29L17.04,20h-6.3c-0.09,0 -0.17,-0.04 -0.24,-0.1l-3.68,-3.68l4.25,0.89V6.5c0,-0.28 0.22,-0.5 0.5,-0.5c0.28,0 0.5,0.22 0.5,0.5v6h1.76l3.46,1.73C17.69,14.43 17.91,14.86 17.84,15.29zM8.07,6.5c0,-1.93 1.57,-3.5 3.5,-3.5s3.5,1.57 3.5,3.5c0,0.95 -0.38,1.81 -1,2.44V6.5c0,-1.38 -1.12,-2.5 -2.5,-2.5c-1.38,0 -2.5,1.12 -2.5,2.5v2.44C8.45,8.31 8.07,7.45 8.07,6.5z"/>
|
||||
</vector>
|
||||
@@ -0,0 +1,29 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2023 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.
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="20dp"
|
||||
android:height="20dp"
|
||||
android:viewportWidth="20"
|
||||
android:viewportHeight="20">
|
||||
<group>
|
||||
<path
|
||||
android:pathData="M10,5.833C7.933,5.833 6.25,7.517 6.25,9.583C6.25,11.65 7.933,13.333 10,13.333C12.067,13.333 13.75,11.65 13.75,9.583C13.75,7.517 12.067,5.833 10,5.833ZM10,11.833C8.758,11.833 7.75,10.825 7.75,9.583C7.75,8.342 8.758,7.333 10,7.333C11.242,7.333 12.25,8.342 12.25,9.583C12.25,10.825 11.242,11.833 10,11.833Z"
|
||||
android:fillColor="#191C1D"/>
|
||||
<path
|
||||
android:pathData="M10.001,3.333C5.834,3.333 2.276,5.925 0.834,9.583C2.276,13.242 5.834,15.833 10.001,15.833C14.167,15.833 17.726,13.242 19.167,9.583C17.726,5.925 14.167,3.333 10.001,3.333ZM10.001,14.167C6.842,14.167 4.026,12.392 2.651,9.583C4.026,6.775 6.842,5 10.001,5C13.159,5 15.976,6.775 17.351,9.583C15.976,12.392 13.159,14.167 10.001,14.167Z"
|
||||
android:fillColor="#191C1D"/>
|
||||
</group>
|
||||
</vector>
|
||||
@@ -0,0 +1,21 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2023 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.
|
||||
-->
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
|
||||
android:shape="rectangle" >
|
||||
<solid android:color="?androidprv:attr/colorSurfaceVariant"/>
|
||||
<corners android:radius="1dp" />
|
||||
</shape>
|
||||
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Copyright (C) 2023 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.
|
||||
-->
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="?android:attr/colorControlHighlight">
|
||||
<item android:id="@android:id/mask">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#FFFFFFFF"/>
|
||||
<corners android:bottomLeftRadius="0dp"
|
||||
android:bottomRightRadius="0dp"
|
||||
android:topLeftRadius="@dimen/dialogCornerRadius"
|
||||
android:topRightRadius="@dimen/dialogCornerRadius"/>
|
||||
</shape>
|
||||
</item>
|
||||
</ripple>
|
||||
@@ -24,7 +24,7 @@
|
||||
<item name="android:listPreferredItemPaddingStart">24dp</item>
|
||||
<item name="android:navigationBarColor">@android:color/transparent</item>
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
<item name="android:switchStyle">@style/HomeSettings.SwitchStyle</item>
|
||||
<item name="android:switchStyle">@style/SwitchStyle</item>
|
||||
<item name="android:textAppearanceListItem">@style/HomeSettings.PreferenceTitle</item>
|
||||
<item name="android:windowActionBar">false</item>
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
@@ -61,13 +61,6 @@
|
||||
<item name="iconSpaceReserved">@bool/home_settings_icon_space_reserved</item>
|
||||
</style>
|
||||
|
||||
<style name="HomeSettings.SwitchStyle"
|
||||
parent="@android:style/Widget.Material.CompoundButton.Switch">
|
||||
<item name="android:switchMinWidth">52dp</item>
|
||||
<item name="android:thumb">@drawable/home_settings_switch_thumb</item>
|
||||
<item name="android:track">@drawable/home_settings_switch_track</item>
|
||||
</style>
|
||||
|
||||
<style name="HomeSettings.PreferenceTitle"
|
||||
parent="@android:style/TextAppearance.Material.Subhead">
|
||||
<item name="android:fontFamily">google-sans</item>
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
<item name="android:listPreferredItemPaddingStart">24dp</item>
|
||||
<item name="android:navigationBarColor">@android:color/transparent</item>
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
<item name="android:switchStyle">@style/HomeSettings.SwitchStyle</item>
|
||||
<item name="android:switchStyle">@style/SwitchStyle</item>
|
||||
<item name="android:textAppearanceListItem">@style/HomeSettings.PreferenceTitle</item>
|
||||
<item name="android:windowActionBar">false</item>
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
|
||||
@@ -65,12 +65,19 @@
|
||||
<item name="overviewScrimColor">@color/overview_scrim</item>
|
||||
<item name="preloadIconAccentColor">@color/preload_icon_accent_color_light</item>
|
||||
<item name="preloadIconBackgroundColor">@color/preload_icon_background_color_light</item>
|
||||
|
||||
<item name="android:windowTranslucentStatus">false</item>
|
||||
<item name="android:windowTranslucentNavigation">false</item>
|
||||
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
|
||||
<item name="android:statusBarColor">#00000000</item>
|
||||
<item name="android:navigationBarColor">#00000000</item>
|
||||
<item name="android:switchStyle">@style/SwitchStyle</item>
|
||||
</style>
|
||||
|
||||
<style name="SwitchStyle"
|
||||
parent="@android:style/Widget.Material.CompoundButton.Switch">
|
||||
<item name="android:switchMinWidth">52dp</item>
|
||||
<item name="android:thumb">@drawable/home_settings_switch_thumb</item>
|
||||
<item name="android:track">@drawable/home_settings_switch_track</item>
|
||||
</style>
|
||||
|
||||
<style name="LauncherTheme.DarkMainColor" parent="@style/LauncherTheme" />
|
||||
|
||||
@@ -74,7 +74,8 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch
|
||||
TYPE_TASKBAR_EDUCATION_DIALOG,
|
||||
TYPE_TASKBAR_ALL_APPS,
|
||||
TYPE_ADD_TO_HOME_CONFIRMATION,
|
||||
TYPE_TASKBAR_OVERLAY_PROXY
|
||||
TYPE_TASKBAR_OVERLAY_PROXY,
|
||||
TYPE_TASKBAR_PINNING_POPUP
|
||||
})
|
||||
@Retention(RetentionPolicy.SOURCE)
|
||||
public @interface FloatingViewType {}
|
||||
@@ -102,6 +103,7 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch
|
||||
public static final int TYPE_TASKBAR_ALL_APPS = 1 << 18;
|
||||
public static final int TYPE_ADD_TO_HOME_CONFIRMATION = 1 << 19;
|
||||
public static final int TYPE_TASKBAR_OVERLAY_PROXY = 1 << 20;
|
||||
public static final int TYPE_TASKBAR_PINNING_POPUP = 1 << 21;
|
||||
|
||||
public static final int TYPE_ALL = TYPE_FOLDER | TYPE_ACTION_POPUP
|
||||
| TYPE_WIDGETS_BOTTOM_SHEET | TYPE_WIDGET_RESIZE_FRAME | TYPE_WIDGETS_FULL_SHEET
|
||||
@@ -110,7 +112,7 @@ public abstract class AbstractFloatingView extends LinearLayout implements Touch
|
||||
| TYPE_ICON_SURFACE | TYPE_DRAG_DROP_POPUP | TYPE_PIN_WIDGET_FROM_EXTERNAL_POPUP
|
||||
| TYPE_WIDGETS_EDUCATION_DIALOG | TYPE_TASKBAR_EDUCATION_DIALOG | TYPE_TASKBAR_ALL_APPS
|
||||
| TYPE_OPTIONS_POPUP_DIALOG | TYPE_ADD_TO_HOME_CONFIRMATION
|
||||
| TYPE_TASKBAR_OVERLAY_PROXY;
|
||||
| TYPE_TASKBAR_OVERLAY_PROXY | TYPE_TASKBAR_PINNING_POPUP;
|
||||
|
||||
// Type of popups which should be kept open during launcher rebind
|
||||
public static final int TYPE_REBIND_SAFE = TYPE_WIDGETS_FULL_SHEET
|
||||
|
||||
@@ -273,12 +273,15 @@ class LauncherPrefs(private val encryptedContext: Context) {
|
||||
|
||||
@JvmStatic fun get(context: Context): LauncherPrefs = INSTANCE.get(context)
|
||||
|
||||
const val TASKBAR_PINNING_KEY = "TASKBAR_PINNING_KEY"
|
||||
@JvmField val ICON_STATE = nonRestorableItem(LauncherAppState.KEY_ICON_STATE, "", true)
|
||||
@JvmField val THEMED_ICONS = backedUpItem(Themes.KEY_THEMED_ICONS, false, true)
|
||||
@JvmField val PROMISE_ICON_IDS = backedUpItem(InstallSessionHelper.PROMISE_ICON_IDS, "")
|
||||
@JvmField val WORK_EDU_STEP = backedUpItem(WorkProfileManager.KEY_WORK_EDU_STEP, 0)
|
||||
@JvmField val WORKSPACE_SIZE = backedUpItem(DeviceGridState.KEY_WORKSPACE_SIZE, "", true)
|
||||
@JvmField val HOTSEAT_COUNT = backedUpItem(DeviceGridState.KEY_HOTSEAT_COUNT, -1, true)
|
||||
@JvmField val TASKBAR_PINNING = backedUpItem(TASKBAR_PINNING_KEY, false)
|
||||
|
||||
@JvmField
|
||||
val DEVICE_TYPE =
|
||||
backedUpItem(DeviceGridState.KEY_DEVICE_TYPE, InvariantDeviceProfile.TYPE_PHONE, true)
|
||||
|
||||
@@ -19,9 +19,10 @@ import static android.content.Intent.ACTION_CONFIGURATION_CHANGED;
|
||||
import static android.view.Display.DEFAULT_DISPLAY;
|
||||
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
|
||||
|
||||
import static com.android.launcher3.LauncherPrefs.TASKBAR_PINNING;
|
||||
import static com.android.launcher3.Utilities.dpiFromPx;
|
||||
import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_PINNING;
|
||||
import static com.android.launcher3.config.FeatureFlags.ENABLE_TRANSIENT_TASKBAR;
|
||||
import static com.android.launcher3.config.FeatureFlags.FORCE_PERSISTENT_TASKBAR;
|
||||
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
||||
import static com.android.launcher3.util.FlagDebugUtils.appendFlag;
|
||||
import static com.android.launcher3.util.window.WindowManagerProxy.MIN_TABLET_WIDTH;
|
||||
@@ -45,6 +46,7 @@ import androidx.annotation.AnyThread;
|
||||
import androidx.annotation.UiThread;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.launcher3.LauncherPrefs;
|
||||
import com.android.launcher3.Utilities;
|
||||
import com.android.launcher3.util.window.CachedDisplayInfo;
|
||||
import com.android.launcher3.util.window.WindowManagerProxy;
|
||||
@@ -101,9 +103,12 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable {
|
||||
private Info mInfo;
|
||||
private boolean mDestroyed = false;
|
||||
|
||||
private final LauncherPrefs mPrefs;
|
||||
|
||||
private DisplayController(Context context) {
|
||||
mContext = context;
|
||||
mDM = context.getSystemService(DisplayManager.class);
|
||||
mPrefs = LauncherPrefs.get(context);
|
||||
|
||||
Display display = mDM.getDisplay(DEFAULT_DISPLAY);
|
||||
if (Utilities.ATLEAST_S) {
|
||||
@@ -144,7 +149,9 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable {
|
||||
// TODO(b/258604917): When running in test harness, use !sTransientTaskbarStatusForTests
|
||||
// once tests are updated to expect new persistent behavior such as not allowing long press
|
||||
// to stash.
|
||||
if (!Utilities.isRunningInTestHarness() && FORCE_PERSISTENT_TASKBAR.get()) {
|
||||
if (!Utilities.isRunningInTestHarness()
|
||||
&& ENABLE_TASKBAR_PINNING.get()
|
||||
&& mPrefs.get(TASKBAR_PINNING)) {
|
||||
return false;
|
||||
}
|
||||
return getInfo().navigationMode == NavigationMode.NO_BUTTON
|
||||
|
||||
Reference in New Issue
Block a user