From bdd96de69a92fed9360ba8521d618b6274553447 Mon Sep 17 00:00:00 2001 From: Liran Binyamin Date: Wed, 3 Jul 2024 17:07:09 -0400 Subject: [PATCH 01/13] Send the bubble removal timestamp to wm shell The removal and update of bubbles happen in different processes, so it's possible for race conditions to allow users to remove bubbles without knowing that an update exists. Passing the removal timestamp allows wm shell to determine whether the removal happened before or after the latest update was received for the bubble. This largely mitigates the race but does not solve it completely. Will file a separate bug to track that. Flag: com.android.wm.shell.enable_bubble_bar Bug: 351026092 Test: Manual - Note the issue here is a race condition so requires code changes to force repro it - Locally I added an artificial delay between the time the - bubble is removed and until the removal is sent to wmshell - Create bubbles and expand the bubble bar - Dismiss bubble by dragging it to dismiss - Send an update to the same bubble before removal is processed in wmshell - Verify bubble is added back - Verify bubble is not removed when the removal is processed in wmshell Change-Id: I05ddc692fe30709125e380351b223d20a0778264 --- .../launcher3/taskbar/bubbles/BubbleBarController.java | 7 +++++++ .../taskbar/bubbles/BubbleBarViewController.java | 9 ++++++++- quickstep/src/com/android/quickstep/SystemUiProxy.java | 6 ++++-- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java index a3832cdf8b..7426dc7259 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarController.java @@ -356,6 +356,13 @@ public class BubbleBarController extends IBubblesListener.Stub { } } + // if a bubble was updated upstream, but removed before the update was received, add it back + if (update.updatedBubble != null && !mBubbles.containsKey(update.updatedBubble.getKey())) { + mBubbles.put(update.updatedBubble.getKey(), update.updatedBubble); + mBubbleBarViewController.addBubble( + update.updatedBubble, isExpanding, suppressAnimation); + } + if (update.addedBubble != null && isCollapsed) { // If we're collapsed, the most recently added bubble will be selected. bubbleToSelect = update.addedBubble; diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java index ad815094bb..23c747eda0 100644 --- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java +++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java @@ -89,6 +89,8 @@ public class BubbleBarViewController { private BubbleBarViewAnimator mBubbleBarViewAnimator; + private TimeSource mTimeSource = System::currentTimeMillis; + @Nullable private BubbleBarBoundsChangeListener mBoundsChangeListener; @@ -576,7 +578,7 @@ public class BubbleBarViewController { * @param bubble dismissed bubble item */ public void onDismissBubbleWhileDragging(@NonNull BubbleBarItem bubble) { - mSystemUiProxy.dragBubbleToDismiss(bubble.getKey()); + mSystemUiProxy.dragBubbleToDismiss(bubble.getKey(), mTimeSource.currentTimeMillis()); } /** @@ -601,6 +603,11 @@ public class BubbleBarViewController { void onBoundsChanged(); } + /** Interface for getting the current timestamp. */ + interface TimeSource { + long currentTimeMillis(); + } + /** Dumps the state of BubbleBarViewController. */ public void dump(PrintWriter pw) { pw.println("Bubble bar view controller state:"); diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.java b/quickstep/src/com/android/quickstep/SystemUiProxy.java index 433baa9581..ec035a9145 100644 --- a/quickstep/src/com/android/quickstep/SystemUiProxy.java +++ b/quickstep/src/com/android/quickstep/SystemUiProxy.java @@ -831,12 +831,14 @@ public class SystemUiProxy implements ISystemUiProxy, NavHandle, SafeCloseable { /** * Tells SysUI to dismiss the bubble with the provided key. + * * @param key the key of the bubble to dismiss. + * @param timestamp the timestamp when the removal happened. */ - public void dragBubbleToDismiss(String key) { + public void dragBubbleToDismiss(String key, long timestamp) { if (mBubbles == null) return; try { - mBubbles.dragBubbleToDismiss(key); + mBubbles.dragBubbleToDismiss(key, timestamp); } catch (RemoteException e) { Log.w(TAG, "Failed call dragBubbleToDismiss"); } From cbc84d96ef5c9bbcae8184f9795ebc2348b940e2 Mon Sep 17 00:00:00 2001 From: Riddle Hsu Date: Mon, 15 Jul 2024 15:43:12 +0800 Subject: [PATCH 02/13] Avoid changing transform when touching in progress After swiping from a landscape app to portrait home, the touch orientation may be changed if there the touch event comes in a short time (e.g. click app icon immediately): DOWN MOVE... SimpleOrientationTouchTransformer#onDisplayInfoChanged MOVE... (start to transform with inconsistent orientation) UP That could disturb the gesture detection: OverviewInputConsumer#onMotionEvent > BaseDragLayer#proxyTouchEvent onInterceptTouchEvent, findControllerToHandleTouch > PortraitStatesTouchController#onControllerInterceptTouchEvent > BaseSwipeDetector#onTouchEvent which may show the app drawer unexpectedly. http://recall/-/b2qm27pJZxFIWQccA9qE9Q/ggLXlE5kf7AWMOjUc0FCUU Bug: 351755391 Flag: EXEMPT bugfix Test: atest NexusLauncherTests: \ com.android.quickstep.OrientationTouchTransformerTest# \ testSimpleOrientationTouchTransformer Change-Id: Ic0e9d8292606837feb4775663abb60229c90e9c5 --- .../android/quickstep/OrientationRectF.java | 4 ++- .../SimpleOrientationTouchTransformer.java | 34 +++++++++++++++++-- .../quickstep/util/InputConsumerProxy.java | 13 +++++-- .../OrientationTouchTransformerTest.java | 29 ++++++++++++++++ 4 files changed, 74 insertions(+), 6 deletions(-) diff --git a/quickstep/src/com/android/quickstep/OrientationRectF.java b/quickstep/src/com/android/quickstep/OrientationRectF.java index aa01b05b47..2b7ecb26e4 100644 --- a/quickstep/src/com/android/quickstep/OrientationRectF.java +++ b/quickstep/src/com/android/quickstep/OrientationRectF.java @@ -67,13 +67,15 @@ public class OrientationRectF extends RectF { } public boolean applyTransform(MotionEvent event, int deltaRotation, boolean forceTransform) { + if (deltaRotation == 0) { + return contains(event.getX(), event.getY()); + } mTmpMatrix.reset(); postDisplayRotation(deltaRotation, mHeight, mWidth, mTmpMatrix); if (forceTransform) { if (DEBUG) { Log.d(TAG, "Transforming rotation due to forceTransform, " + "deltaRotation: " + deltaRotation - + "mRotation: " + mRotation + " this: " + this); } event.applyTransform(mTmpMatrix); diff --git a/quickstep/src/com/android/quickstep/SimpleOrientationTouchTransformer.java b/quickstep/src/com/android/quickstep/SimpleOrientationTouchTransformer.java index 29a57fcfc1..526464330d 100644 --- a/quickstep/src/com/android/quickstep/SimpleOrientationTouchTransformer.java +++ b/quickstep/src/com/android/quickstep/SimpleOrientationTouchTransformer.java @@ -34,12 +34,18 @@ public class SimpleOrientationTouchTransformer implements private final Context mContext; private OrientationRectF mOrientationRectF; + private OrientationRectF mTouchingOrientationRectF; + private int mViewRotation; public SimpleOrientationTouchTransformer(Context context) { + this(context, DisplayController.INSTANCE.get(context)); + } + + @androidx.annotation.VisibleForTesting + public SimpleOrientationTouchTransformer(Context context, DisplayController displayController) { mContext = context; - DisplayController.INSTANCE.get(context).addChangeListener(this); - onDisplayInfoChanged(context, DisplayController.INSTANCE.get(context).getInfo(), - CHANGE_ALL); + displayController.addChangeListener(this); + onDisplayInfoChanged(context, displayController.getInfo(), CHANGE_ALL); } @Override @@ -56,7 +62,29 @@ public class SimpleOrientationTouchTransformer implements info.rotation); } + /** + * Called when the touch is started. This preserves the touching orientation until the touch is + * done (i.e. ACTION_CANCEL or ACTION_UP). So the transform won't produce inconsistent position + * if display is changed during the touch. + */ + public void updateTouchingOrientation(int viewRotation) { + mViewRotation = viewRotation; + mTouchingOrientationRectF = new OrientationRectF(mOrientationRectF.left, + mOrientationRectF.top, mOrientationRectF.right, mOrientationRectF.bottom, + mOrientationRectF.getRotation()); + } + + /** Called when the touch is finished. */ + public void clearTouchingOrientation() { + mTouchingOrientationRectF = null; + } + public void transform(MotionEvent ev, int rotation) { + if (mTouchingOrientationRectF != null) { + mTouchingOrientationRectF.applyTransformToRotation(ev, mViewRotation, + true /* forceTransform */); + return; + } mOrientationRectF.applyTransformToRotation(ev, rotation, true /* forceTransform */); } } diff --git a/quickstep/src/com/android/quickstep/util/InputConsumerProxy.java b/quickstep/src/com/android/quickstep/util/InputConsumerProxy.java index cb44a1a0d8..fcf9ab1ad4 100644 --- a/quickstep/src/com/android/quickstep/util/InputConsumerProxy.java +++ b/quickstep/src/com/android/quickstep/util/InputConsumerProxy.java @@ -108,19 +108,28 @@ public class InputConsumerProxy { return false; } + final SimpleOrientationTouchTransformer touchTransformer = + SimpleOrientationTouchTransformer.INSTANCE.get(mContext); + final int viewRotation = mRotationSupplier.get(); + final boolean needTransform = viewRotation != ev.getSurfaceRotation(); if (action == ACTION_DOWN) { mTouchInProgress = true; + if (needTransform) { + touchTransformer.updateTouchingOrientation(viewRotation); + } initInputConsumerIfNeeded(/* isFromTouchDown= */ true); } else if (action == ACTION_CANCEL || action == ACTION_UP) { // Finish any pending actions mTouchInProgress = false; + touchTransformer.clearTouchingOrientation(); if (mDestroyPending) { destroy(); } } if (mInputConsumer != null) { - SimpleOrientationTouchTransformer.INSTANCE.get(mContext).transform(ev, - mRotationSupplier.get()); + if (needTransform) { + touchTransformer.transform(ev, viewRotation); + } mInputConsumer.onMotionEvent(ev); } diff --git a/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java b/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java index 298dd6cae0..f5d082dd5c 100644 --- a/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java +++ b/quickstep/tests/src/com/android/quickstep/OrientationTouchTransformerTest.java @@ -21,6 +21,7 @@ import static androidx.test.core.app.ApplicationProvider.getApplicationContext; import static com.android.launcher3.util.NavigationMode.NO_BUTTON; +import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; import static org.mockito.ArgumentMatchers.any; @@ -288,6 +289,34 @@ public class OrientationTouchTransformerTest { assertTrue(mTouchTransformer.touchInValidSwipeRegions(inRegion2.getX(), inRegion2.getY())); } + @Test + public void testSimpleOrientationTouchTransformer() { + final DisplayController displayController = mock(DisplayController.class); + doReturn(mInfo).when(displayController).getInfo(); + final SimpleOrientationTouchTransformer transformer = + new SimpleOrientationTouchTransformer(getApplicationContext(), displayController); + final MotionEvent move1 = generateMotionEvent(MotionEvent.ACTION_MOVE, 100, 10); + transformer.transform(move1, Surface.ROTATION_90); + // The position is transformed to 90 degree. + assertEquals(10, move1.getX(), 0f /* delta */); + assertEquals(NORMAL_SCREEN_SIZE.getWidth() - 100, move1.getY(), 0f /* delta */); + + // If the touching state is specified, the position is still transformed to 90 degree even + // if the given rotation is changed. + final MotionEvent move2 = generateMotionEvent(MotionEvent.ACTION_MOVE, 100, 10); + transformer.updateTouchingOrientation(Surface.ROTATION_90); + transformer.transform(move2, Surface.ROTATION_0); + assertEquals(move1.getX(), move2.getX(), 0f /* delta */); + assertEquals(move1.getY(), move2.getY(), 0f /* delta */); + + // If the touching state is cleared, it restores to use the given rotation. + final MotionEvent move3 = generateMotionEvent(MotionEvent.ACTION_MOVE, 100, 10); + transformer.clearTouchingOrientation(); + transformer.transform(move3, Surface.ROTATION_0); + assertEquals(100, move3.getX(), 0f /* delta */); + assertEquals(10, move3.getY(), 0f /* delta */); + } + private DisplayController.Info createDisplayInfo(Size screenSize, int rotation) { Point displaySize = new Point(screenSize.getWidth(), screenSize.getHeight()); RotationUtils.rotateSize(displaySize, rotation); From dafd7f55edb482d40d3844c24cfbcb5dce5a4191 Mon Sep 17 00:00:00 2001 From: Jagrut Desai Date: Thu, 27 Jun 2024 14:39:15 -0700 Subject: [PATCH 03/13] Adding Skeleton Code for Customizable Taksbar This cl includes - new skeleton xml view customizable taskbar view - new skeleton xml view for customizable taksbar container and a view class. - new taskbar all apps button view container class - pxFromDp util method introduction to launcher utilities Test: Presubmit Bug: 349885828 Bug: 349886203 Flag: com.android.launcher3.enable_taskbar_customization Change-Id: Id83906ed0717da495c33275e52261153a1598074 --- quickstep/res/layout/customizable_taskbar.xml | 121 ++++++++++++++++++ .../res/layout/customizable_taskbar_view.xml | 56 ++++++++ .../customization/CustomizableTaskbarView.kt | 40 ++++++ .../TaskbarAllAppsButtonContainer.kt | 97 ++++++++++++++ 4 files changed, 314 insertions(+) create mode 100644 quickstep/res/layout/customizable_taskbar.xml create mode 100644 quickstep/res/layout/customizable_taskbar_view.xml create mode 100644 quickstep/src/com/android/launcher3/taskbar/customization/CustomizableTaskbarView.kt create mode 100644 quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt diff --git a/quickstep/res/layout/customizable_taskbar.xml b/quickstep/res/layout/customizable_taskbar.xml new file mode 100644 index 0000000000..e1a80aee30 --- /dev/null +++ b/quickstep/res/layout/customizable_taskbar.xml @@ -0,0 +1,121 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/quickstep/res/layout/customizable_taskbar_view.xml b/quickstep/res/layout/customizable_taskbar_view.xml new file mode 100644 index 0000000000..f55e1d050d --- /dev/null +++ b/quickstep/res/layout/customizable_taskbar_view.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/CustomizableTaskbarView.kt b/quickstep/src/com/android/launcher3/taskbar/customization/CustomizableTaskbarView.kt new file mode 100644 index 0000000000..e384586b45 --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/customization/CustomizableTaskbarView.kt @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2024 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.customization + +import android.content.Context +import android.graphics.Rect +import android.util.AttributeSet +import androidx.constraintlayout.widget.ConstraintLayout +import com.android.launcher3.Insettable +import com.android.launcher3.R +import com.android.launcher3.taskbar.TaskbarActivityContext +import com.android.launcher3.views.ActivityContext + +/** TaskbarView that is customizeable via Taskbar containers. */ +class CustomizableTaskbarView(context: Context, attrs: AttributeSet? = null) : + ConstraintLayout(context, attrs), Insettable { + private val activityContext: TaskbarActivityContext = ActivityContext.lookupContext(context) + + init { + inflate(context, R.layout.customizable_taskbar_view, this) + } + + override fun setInsets(insets: Rect?) { + // Ignore, we just implement Insettable to draw behind system insets. + } +} diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt new file mode 100644 index 0000000000..415a05156e --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt @@ -0,0 +1,97 @@ +/* + * Copyright (C) 2024 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.customization + +import android.annotation.SuppressLint +import android.content.Context +import android.util.AttributeSet +import android.view.LayoutInflater +import android.widget.LinearLayout +import androidx.annotation.DimenRes +import androidx.annotation.DrawableRes +import androidx.core.view.setPadding +import com.android.launcher3.R +import com.android.launcher3.Utilities.dpToPx +import com.android.launcher3.config.FeatureFlags +import com.android.launcher3.taskbar.TaskbarActivityContext +import com.android.launcher3.views.ActivityContext +import com.android.launcher3.views.IconButtonView + +/** Taskbar all apps button container for customizable taskbar. */ +class TaskbarAllAppsButtonContainer +@JvmOverloads +constructor( + context: Context, + attrs: AttributeSet? = null, + defStyleAttr: Int = 0, +) : LinearLayout(context, attrs), TaskbarContainer { + + private val allAppsButton: IconButtonView = + LayoutInflater.from(context).inflate(R.layout.taskbar_all_apps_button, this, false) + as IconButtonView + private val activityContext: TaskbarActivityContext = ActivityContext.lookupContext(context) + + override val spaceNeeded: Int + get() { + return dpToPx(activityContext.taskbarSpecsEvaluator.taskbarIconSize.size.toFloat()) + } + + init { + setUpIcon() + } + + @SuppressLint("UseCompatLoadingForDrawables", "ResourceAsColor") + private fun setUpIcon() { + val drawable = + resources.getDrawable( + getAllAppsButton(activityContext.taskbarFeatureEvaluator.isTransient) + ) + val padding = activityContext.taskbarSpecsEvaluator.taskbarIconPadding + + allAppsButton.setIconDrawable(drawable) + allAppsButton.setPadding(/* left= */ padding) + allAppsButton.setForegroundTint(activityContext.getColor(R.color.all_apps_button_color)) + + // TODO(jagrutdesai) : add click listeners in future cl + addView(allAppsButton) + } + + @DrawableRes + private fun getAllAppsButton(isTransientTaskbar: Boolean): Int { + val shouldSelectTransientIcon = + isTransientTaskbar || + (FeatureFlags.enableTaskbarPinning() && !activityContext.isThreeButtonNav) + return if (FeatureFlags.ENABLE_ALL_APPS_SEARCH_IN_TASKBAR.get()) { + if (shouldSelectTransientIcon) R.drawable.ic_transient_taskbar_all_apps_search_button + else R.drawable.ic_taskbar_all_apps_search_button + } else { + if (shouldSelectTransientIcon) R.drawable.ic_transient_taskbar_all_apps_button + else R.drawable.ic_taskbar_all_apps_button + } + } + + @DimenRes + fun getAllAppsButtonTranslationXOffset(isTransientTaskbar: Boolean): Int { + return if (isTransientTaskbar) { + R.dimen.transient_taskbar_all_apps_button_translation_x_offset + } else if (FeatureFlags.ENABLE_ALL_APPS_SEARCH_IN_TASKBAR.get()) { + R.dimen.taskbar_all_apps_search_button_translation_x_offset + } else { + R.dimen.taskbar_all_apps_button_translation_x_offset + } + } +} From ed6b942cb8e0891b516b7dcd8cd41a254b9d2e82 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Mon, 15 Jul 2024 14:40:46 -0700 Subject: [PATCH 04/13] Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: I52341b9a96ac15c3e27e6649b0b2fe47f1067e9b --- quickstep/res/values-ta/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quickstep/res/values-ta/strings.xml b/quickstep/res/values-ta/strings.xml index ed3ebeedaa..47d8055e16 100644 --- a/quickstep/res/values-ta/strings.xml +++ b/quickstep/res/values-ta/strings.xml @@ -122,7 +122,7 @@ "மூடுக" "முடிந்தது" "முகப்பு" - "அணுகல்தன்மை" + "மாற்றுத்திறன் வசதி" "பின்செல்லும்" "IME சுவிட்ச்சர்" "சமீபத்தியவை" From 4f8678642b5c15631d1cf89c6668fd3cf93d5d7c Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Mon, 15 Jul 2024 14:41:11 -0700 Subject: [PATCH 05/13] Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: If32af03d3162501f90d9a829ecf4429df1a8f4dd --- quickstep/res/values-ta/strings.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/quickstep/res/values-ta/strings.xml b/quickstep/res/values-ta/strings.xml index 8ca19b8d6e..dde9133c96 100644 --- a/quickstep/res/values-ta/strings.xml +++ b/quickstep/res/values-ta/strings.xml @@ -121,7 +121,7 @@ "மூடுக" "முடிந்தது" "முகப்பு" - "அணுகல்தன்மை" + "மாற்றுத்திறன் வசதி" "பின்செல்லும்" "IME சுவிட்ச்சர்" "சமீபத்தியவை" From e21b81fdebaca8bb601b7c699dc1c83e2b3794c8 Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Mon, 15 Jul 2024 14:42:07 -0700 Subject: [PATCH 06/13] Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: If667a4fdadf307506c44c894f4c52a849fc3f94a --- res/values-af/strings.xml | 3 +-- res/values-am/strings.xml | 3 +-- res/values-ar/strings.xml | 3 +-- res/values-as/strings.xml | 3 +-- res/values-az/strings.xml | 3 +-- res/values-b+sr+Latn/strings.xml | 3 +-- res/values-be/strings.xml | 3 +-- res/values-bg/strings.xml | 3 +-- res/values-bn/strings.xml | 3 +-- res/values-bs/strings.xml | 3 +-- res/values-ca/strings.xml | 3 +-- res/values-cs/strings.xml | 3 +-- res/values-da/strings.xml | 3 +-- res/values-de/strings.xml | 7 +++---- res/values-el/strings.xml | 3 +-- res/values-en-rAU/strings.xml | 3 +-- res/values-en-rGB/strings.xml | 3 +-- res/values-en-rIN/strings.xml | 3 +-- res/values-es-rUS/strings.xml | 3 +-- res/values-es/strings.xml | 3 +-- res/values-et/strings.xml | 3 +-- res/values-eu/strings.xml | 3 +-- res/values-fa/strings.xml | 3 +-- res/values-fi/strings.xml | 3 +-- res/values-fr-rCA/strings.xml | 3 +-- res/values-fr/strings.xml | 3 +-- res/values-gl/strings.xml | 3 +-- res/values-gu/strings.xml | 3 +-- res/values-hi/strings.xml | 3 +-- res/values-hr/strings.xml | 3 +-- res/values-hu/strings.xml | 5 ++--- res/values-hy/strings.xml | 3 +-- res/values-in/strings.xml | 3 +-- res/values-is/strings.xml | 3 +-- res/values-it/strings.xml | 3 +-- res/values-iw/strings.xml | 3 +-- res/values-ja/strings.xml | 3 +-- res/values-ka/strings.xml | 3 +-- res/values-kk/strings.xml | 3 +-- res/values-km/strings.xml | 3 +-- res/values-kn/strings.xml | 3 +-- res/values-ko/strings.xml | 3 +-- res/values-ky/strings.xml | 3 +-- res/values-lo/strings.xml | 3 +-- res/values-lt/strings.xml | 3 +-- res/values-lv/strings.xml | 3 +-- res/values-mk/strings.xml | 3 +-- res/values-ml/strings.xml | 3 +-- res/values-mn/strings.xml | 3 +-- res/values-mr/strings.xml | 3 +-- res/values-ms/strings.xml | 3 +-- res/values-my/strings.xml | 3 +-- res/values-nb/strings.xml | 3 +-- res/values-ne/strings.xml | 3 +-- res/values-nl/strings.xml | 3 +-- res/values-or/strings.xml | 3 +-- res/values-pa/strings.xml | 3 +-- res/values-pl/strings.xml | 3 +-- res/values-pt-rPT/strings.xml | 3 +-- res/values-pt/strings.xml | 3 +-- res/values-ro/strings.xml | 3 +-- res/values-ru/strings.xml | 3 +-- res/values-si/strings.xml | 3 +-- res/values-sk/strings.xml | 3 +-- res/values-sl/strings.xml | 3 +-- res/values-sq/strings.xml | 3 +-- res/values-sr/strings.xml | 3 +-- res/values-sv/strings.xml | 3 +-- res/values-sw/strings.xml | 3 +-- res/values-ta/strings.xml | 3 +-- res/values-te/strings.xml | 3 +-- res/values-th/strings.xml | 3 +-- res/values-tl/strings.xml | 3 +-- res/values-tr/strings.xml | 3 +-- res/values-uk/strings.xml | 3 +-- res/values-ur/strings.xml | 3 +-- res/values-uz/strings.xml | 3 +-- res/values-vi/strings.xml | 3 +-- res/values-zh-rCN/strings.xml | 3 +-- res/values-zh-rHK/strings.xml | 3 +-- res/values-zh-rTW/strings.xml | 3 +-- res/values-zu/strings.xml | 3 +-- 82 files changed, 85 insertions(+), 167 deletions(-) diff --git a/res/values-af/strings.xml b/res/values-af/strings.xml index 90e784dda9..1a8f8e26af 100644 --- a/res/values-af/strings.xml +++ b/res/values-af/strings.xml @@ -27,8 +27,7 @@ "Legstukke gedeaktiveer in Veiligmodus" "Kortpad is nie beskikbaar nie" "Tuis" - - + "Stel %1$s as verstektuisskermapp in Instellings" "Verdeelde skerm" "Programinligting vir %1$s" "Gebruikinstellings vir %1$s" diff --git a/res/values-am/strings.xml b/res/values-am/strings.xml index 1289b8c591..80447c5940 100644 --- a/res/values-am/strings.xml +++ b/res/values-am/strings.xml @@ -27,8 +27,7 @@ "ምግብሮች በደህንነቱ የተጠበቀ ሁኔታ ተሰናክለዋል" "አቋራጭ አይገኝም" "መነሻ" - - + "በቅንብሮች ውስጥ %1$sን እንደ ነባሪ የHome መተግበሪያ ያቀናብሩ" "የተከፈለ ማያ ገፅ" "የመተግበሪያ መረጃ ለ%1$s" "የ%1$s የአጠቃቀም ቅንብሮች" diff --git a/res/values-ar/strings.xml b/res/values-ar/strings.xml index 37d6fba70f..4eee1217a7 100644 --- a/res/values-ar/strings.xml +++ b/res/values-ar/strings.xml @@ -27,8 +27,7 @@ "الأدوات غير مفعّلة في الوضع الآمن" "الاختصار غير متاح" "الشاشة الرئيسية" - - + "يمكن ضبط \"%1$s\" كتطبيق الشاشة الرئيسية التلقائي من خلال \"الإعدادات\"" "تقسيم الشاشة" "‏معلومات تطبيق %1$s" "‏إعدادات استخدام \"%1$s\"" diff --git a/res/values-as/strings.xml b/res/values-as/strings.xml index e983ce8a28..52ec7ea8dd 100644 --- a/res/values-as/strings.xml +++ b/res/values-as/strings.xml @@ -27,8 +27,7 @@ "ৱিজেটবোৰক সুৰক্ষিত ম\'ডত অক্ষম কৰা হ’ল" "শ্বৰ্টকাট নাই" "গৃহ স্ক্ৰীন" - - + "ছেটিঙত %1$sক ডিফ’ল্ট গৃহপৃষ্ঠা এপ্‌ হিচাপে ছেট কৰক" "বিভাজিত স্ক্ৰীন" "%1$sৰ বাবে এপৰ তথ্য" "%1$sৰ বাবে ব্যৱহাৰৰ ছেটিং" diff --git a/res/values-az/strings.xml b/res/values-az/strings.xml index f390c7c260..6c1cc468a7 100644 --- a/res/values-az/strings.xml +++ b/res/values-az/strings.xml @@ -27,8 +27,7 @@ "Vidcetlər Güvənli rejimdə deaktiv edilib" "Qısayol əlçatan deyil" "Əsas səhifə" - - + "Ayarlarda %1$s tətbiqini defolt əsas ekran tətbiqi kimi ayarlayın" "Ekran bölünməsi" "%1$s ilə bağlı tətbiq məlumatı" "%1$s üzrə istifadə ayarları" diff --git a/res/values-b+sr+Latn/strings.xml b/res/values-b+sr+Latn/strings.xml index fc71eeb76e..24328cfaee 100644 --- a/res/values-b+sr+Latn/strings.xml +++ b/res/values-b+sr+Latn/strings.xml @@ -27,8 +27,7 @@ "Vidžeti su onemogućeni u Bezbednom režimu" "Prečica nije dostupna" "Početni ekran" - - + "Podesite %1$s kao podrazumevanu početnu aplikaciju u Podešavanjima" "Podeljeni ekran" "Informacije o aplikaciji za: %1$s" "Podešavanja potrošnje za %1$s" diff --git a/res/values-be/strings.xml b/res/values-be/strings.xml index 4589125b46..ebbb378123 100644 --- a/res/values-be/strings.xml +++ b/res/values-be/strings.xml @@ -27,8 +27,7 @@ "Віджэты адключаны ў Бяспечным рэжыме" "Ярлык недаступны" "Галоўны экран" - - + "Зрабіць %1$s стандартнай праграмай для галоўнага экрана, перайшоўшы ў Налады" "Падзелены экран" "Інфармацыя пра праграму для: %1$s" "%1$s: налады выкарыстання" diff --git a/res/values-bg/strings.xml b/res/values-bg/strings.xml index 643c24e5d6..d5d948eed1 100644 --- a/res/values-bg/strings.xml +++ b/res/values-bg/strings.xml @@ -27,8 +27,7 @@ "Приспособленията са деактивирани в безопасния режим" "Няма достъп до прекия път" "Начален екран" - - + "От настройките задайте %1$s като основното приложение за начален екран" "Разделен екран" "Информация за приложението за %1$s" "Настройки за използването на %1$s" diff --git a/res/values-bn/strings.xml b/res/values-bn/strings.xml index 7e10a0eaf1..cf75fb58d5 100644 --- a/res/values-bn/strings.xml +++ b/res/values-bn/strings.xml @@ -27,8 +27,7 @@ "সুরক্ষিত মোডে উইজেট নিষ্ক্রিয় থাকে" "শর্টকাটগুলি অনুপলব্ধ" "হোম" - - + "সেটিংসে গিয়ে %1$s অ্যাপকে ডিফল্ট হোম অ্যাপ হিসেবে সেট করুন" "স্প্লিট স্ক্রিন" "%1$s-এর জন্য অ্যাপ সম্পর্কিত তথ্য" "%1$s-এর জন্য ব্যবহারের সেটিংস" diff --git a/res/values-bs/strings.xml b/res/values-bs/strings.xml index 10aa86a871..1758c39bd8 100644 --- a/res/values-bs/strings.xml +++ b/res/values-bs/strings.xml @@ -27,8 +27,7 @@ "Vidžeti su onemogućeni u sigurnom načinu rada." "Prečica nije dostupna" "Početni ekran" - - + "Postavite %1$s kao zadanu aplikaciju za početni ekran u Postavkama" "Podijeljeni ekran" "Informacije o aplikaciji %1$s" "Postavke korištenja za: %1$s" diff --git a/res/values-ca/strings.xml b/res/values-ca/strings.xml index 3d2ff4a3d2..bf578f5cf1 100644 --- a/res/values-ca/strings.xml +++ b/res/values-ca/strings.xml @@ -27,8 +27,7 @@ "En Mode segur, els widgets estan desactivats." "La drecera no està disponible" "Inici" - - + "Defineix %1$s com a aplicació d\'inici predeterminada a Configuració" "Pantalla dividida" "Informació de l\'aplicació %1$s" "Configuració d\'ús de %1$s" diff --git a/res/values-cs/strings.xml b/res/values-cs/strings.xml index 14a35836d9..9a8fc6ffb8 100644 --- a/res/values-cs/strings.xml +++ b/res/values-cs/strings.xml @@ -27,8 +27,7 @@ "V nouzovém režimu jsou widgety zakázány." "Zkratka není k dispozici" "Domů" - - + "Nastavit %1$s jako výchozí vstupní aplikaci v Nastavení" "Rozdělit obrazovku" "Informace o aplikaci %1$s" "Nastavení využití pro aplikaci %1$s" diff --git a/res/values-da/strings.xml b/res/values-da/strings.xml index f18e1e8f03..208c2ef1d6 100644 --- a/res/values-da/strings.xml +++ b/res/values-da/strings.xml @@ -27,8 +27,7 @@ "Widgets er deaktiveret i Beskyttet tilstand" "Genvejen er ikke tilgængelig" "Startskærm" - - + "Angiv %1$s som standardstartapp i Indstillinger" "Opdel skærm" "Appinfo for %1$s" "Indstillinger for brug af %1$s" diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml index 9a4e5e2a30..380030b981 100644 --- a/res/values-de/strings.xml +++ b/res/values-de/strings.xml @@ -27,8 +27,7 @@ "Widgets im abgesicherten Modus deaktiviert" "Verknüpfung nicht verfügbar" "Startbildschirm" - - + "%1$s in den Einstellungen als Stand-Start-App festlegen" "Splitscreen" "App-Info für %1$s" "Nutzungseinstellungen für %1$s" @@ -37,7 +36,7 @@ "Dieses App-Paar wird auf diesem Gerät nicht unterstützt" "Gerät aufklappen, um dieses App-Paar zu verwenden" "App-Paar nicht verfügbar" - "Zum Verschieben des Widgets berühren und halten" + "Zum Verschieben des Widgets gedrückt halten" "Doppeltippen und halten, um ein Widget zu bewegen oder benutzerdefinierte Aktionen zu nutzen." "%1$d × %2$d" "%1$d breit und %2$d hoch" @@ -75,7 +74,7 @@ "App" "Alle Apps" "Benachrichtigungen" - "Zum Verschieben einer Verknüpfung berühren und halten" + "Zum Verschieben einer Verknüpfung gedrückt halten" "Doppeltippen und halten, um eine Verknüpfung zu bewegen oder benutzerdefinierte Aktionen zu nutzen." "Auf diesem Startbildschirm ist kein Platz mehr vorhanden" "Ablage \"Favoriten\" ist voll." diff --git a/res/values-el/strings.xml b/res/values-el/strings.xml index 840ff5b3b1..e86ebae4d9 100644 --- a/res/values-el/strings.xml +++ b/res/values-el/strings.xml @@ -27,8 +27,7 @@ "Τα γραφικά στοιχεία απενεργοποιήθηκαν στην ασφαλή λειτουργία" "Η συντόμευση δεν είναι διαθέσιμη" "Αρχική οθόνη" - - + "Ορίστε το %1$s ως την προεπιλεγμένη εφαρμογή αρχικής οθόνης στις Ρυθμίσεις" "Διαχωρισμός οθόνης" "Πληροφορίες εφαρμογής για %1$s" "Ρυθμίσεις χρήσης για %1$s" diff --git a/res/values-en-rAU/strings.xml b/res/values-en-rAU/strings.xml index 4deacb6b9d..50c597690f 100644 --- a/res/values-en-rAU/strings.xml +++ b/res/values-en-rAU/strings.xml @@ -27,8 +27,7 @@ "Widgets disabled in Safe mode" "Shortcut isn\'t available" "Home" - - + "Set %1$s as the default home app in Settings" "Split screen" "App info for %1$s" "Usage settings for %1$s" diff --git a/res/values-en-rGB/strings.xml b/res/values-en-rGB/strings.xml index 4deacb6b9d..50c597690f 100644 --- a/res/values-en-rGB/strings.xml +++ b/res/values-en-rGB/strings.xml @@ -27,8 +27,7 @@ "Widgets disabled in Safe mode" "Shortcut isn\'t available" "Home" - - + "Set %1$s as the default home app in Settings" "Split screen" "App info for %1$s" "Usage settings for %1$s" diff --git a/res/values-en-rIN/strings.xml b/res/values-en-rIN/strings.xml index 4deacb6b9d..50c597690f 100644 --- a/res/values-en-rIN/strings.xml +++ b/res/values-en-rIN/strings.xml @@ -27,8 +27,7 @@ "Widgets disabled in Safe mode" "Shortcut isn\'t available" "Home" - - + "Set %1$s as the default home app in Settings" "Split screen" "App info for %1$s" "Usage settings for %1$s" diff --git a/res/values-es-rUS/strings.xml b/res/values-es-rUS/strings.xml index e734744314..5879129996 100644 --- a/res/values-es-rUS/strings.xml +++ b/res/values-es-rUS/strings.xml @@ -27,8 +27,7 @@ "Widgets inhabilitados en modo seguro" "El acceso directo no está disponible" "Pantalla principal" - - + "Establece %1$s como la app de inicio predeterminada en Configuración" "Pantalla dividida" "Información de la app de %1$s" "Configuración del uso de %1$s" diff --git a/res/values-es/strings.xml b/res/values-es/strings.xml index 3e0e51645c..8d384acba4 100644 --- a/res/values-es/strings.xml +++ b/res/values-es/strings.xml @@ -27,8 +27,7 @@ "Widgets inhabilitados en modo Seguro" "Acceso directo no disponible" "Inicio" - - + "Define %1$s como aplicación de inicio predeterminada en Ajustes" "Pantalla dividida" "Información de la aplicación %1$s" "Ajustes de uso para %1$s" diff --git a/res/values-et/strings.xml b/res/values-et/strings.xml index bab7da3bc8..44448a6b0e 100644 --- a/res/values-et/strings.xml +++ b/res/values-et/strings.xml @@ -27,8 +27,7 @@ "Turvarežiimis on vidinad keelatud" "Otsetee pole saadaval" "Avakuva" - - + "Määrake rakendus %1$s seadetes avakuva vaikerakenduseks" "Jagatud ekraanikuva" "Rakenduse teave: %1$s" "Kasutuse seaded: %1$s" diff --git a/res/values-eu/strings.xml b/res/values-eu/strings.xml index b8017d6fee..6fc4cf458f 100644 --- a/res/values-eu/strings.xml +++ b/res/values-eu/strings.xml @@ -27,8 +27,7 @@ "Widgetak desgaitu egin dira modu seguruan" "Lasterbideak ez daude erabilgarri" "Orri nagusia" - - + "Ezarri %1$s hasierako aplikazio lehenetsi gisa ezarpenetan" "Pantaila zatitzea" "%1$s aplikazioari buruzko informazioa" "%1$s aplikazioaren erabilera-ezarpenak" diff --git a/res/values-fa/strings.xml b/res/values-fa/strings.xml index 5cbce53868..fffce130a3 100644 --- a/res/values-fa/strings.xml +++ b/res/values-fa/strings.xml @@ -27,8 +27,7 @@ "ابزارک‌ها در حالت ایمن غیرفعال هستند" "میان‌بر دردسترس نیست" "صفحه اصلی" - - + "تنظیم %1$s به‌عنوان برنامه صفحه اصلی پیش‌فرض در «تنظیمات»" "صفحهٔ دونیمه" "‏اطلاعات برنامه %1$s" "‏تنظیمات مصرف برای %1$s" diff --git a/res/values-fi/strings.xml b/res/values-fi/strings.xml index 181c89eaca..310053e557 100644 --- a/res/values-fi/strings.xml +++ b/res/values-fi/strings.xml @@ -27,8 +27,7 @@ "Widgetit poistettu käytöstä vikasietotilassa" "Pikakuvake ei ole käytettävissä." "Etusivu" - - + "Valitse %1$s oletusaloitusnäyttösovellukseksi asetuksissa" "Jaettu näyttö" "Sovellustiedot: %1$s" "Käyttöasetus tälle: %1$s" diff --git a/res/values-fr-rCA/strings.xml b/res/values-fr-rCA/strings.xml index f95f26d2f6..11b2c010e5 100644 --- a/res/values-fr-rCA/strings.xml +++ b/res/values-fr-rCA/strings.xml @@ -27,8 +27,7 @@ "Widgets désactivés en mode sans échec" "Le raccourci n\'est pas disponible" "Accueil" - - + "Définissez %1$s comme appli d\'accueil par défaut dans les paramètres" "Écran divisé" "Renseignements sur l\'appli pour %1$s" "Paramètres d\'utilisation pour %1$s" diff --git a/res/values-fr/strings.xml b/res/values-fr/strings.xml index 4a7c7fb6d9..b4c5ec6d0b 100644 --- a/res/values-fr/strings.xml +++ b/res/values-fr/strings.xml @@ -27,8 +27,7 @@ "Les widgets sont désactivés en mode sécurisé." "Raccourci non disponible" "Accueil" - - + "Définissez %1$s comme application d\'accueil par défaut dans Paramètres" "Écran partagé" "Infos sur l\'appli pour %1$s" "Paramètres d\'utilisation pour %1$s" diff --git a/res/values-gl/strings.xml b/res/values-gl/strings.xml index 0430a9a680..ac7280218c 100644 --- a/res/values-gl/strings.xml +++ b/res/values-gl/strings.xml @@ -27,8 +27,7 @@ "Os widgets están desactivados no modo seguro" "O atallo non está dispoñible" "Inicio" - - + "Define %1$s como aplicación de inicio predeterminada en Configuración" "Pantalla dividida" "Información da aplicación para %1$s" "Configuración de uso para %1$s" diff --git a/res/values-gu/strings.xml b/res/values-gu/strings.xml index 3c248dbb95..74747d05a6 100644 --- a/res/values-gu/strings.xml +++ b/res/values-gu/strings.xml @@ -27,8 +27,7 @@ "સુરક્ષિત મોડમાં વિજેટ્સ અક્ષમ કર્યા" "શૉર્ટકટ ઉપલબ્ધ નથી" "હોમ સ્ક્રીન" - - + "સેટિંગમાં %1$sને ડિફૉલ્ટ હોમ ઍપ તરીકે સેટ કરો" "સ્ક્રીનને વિભાજિત કરો" "%1$s માટે ઍપ માહિતી" "%1$sના વપરાશ સંબંધિત સેટિંગ" diff --git a/res/values-hi/strings.xml b/res/values-hi/strings.xml index 50d4762ff6..6071935713 100644 --- a/res/values-hi/strings.xml +++ b/res/values-hi/strings.xml @@ -27,8 +27,7 @@ "विजेट सुरक्षित मोड में अक्षम हैं" "शॉर्टकट उपलब्ध नहीं है" "होम स्क्रीन" - - + "सेटिंग में जाकर, %1$s को डिफ़ॉल्ट होम ऐप्लिकेशन के तौर पर सेट करें" "स्प्लिट स्क्रीन" "%1$s के लिए ऐप्लिकेशन की जानकारी" "%1$s के लिए खर्च की सेटिंग" diff --git a/res/values-hr/strings.xml b/res/values-hr/strings.xml index 7a1fd94cac..cf7a91a9db 100644 --- a/res/values-hr/strings.xml +++ b/res/values-hr/strings.xml @@ -27,8 +27,7 @@ "Widgeti su onemogućeni u Sigurnom načinu rada" "Prečac nije dostupan" "Početni zaslon" - - + "Postavite aplikaciju %1$s kao zadanu aplikaciju početnog zaslona u postavkama" "Podijeljeni zaslon" "Informacije o aplikaciji %1$s" "Postavke upotrebe za %1$s" diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml index 3b556da7ef..f306110f4f 100644 --- a/res/values-hu/strings.xml +++ b/res/values-hu/strings.xml @@ -27,8 +27,7 @@ "A modulok ki vannak kapcsolva Csökkentett módban" "A gyorsparancs nem áll rendelkezésre" "Kezdőképernyő" - - + "A(z) %1$s appot a Beállításokban adhatja meg alapértelmezett kezdőalkalmazásként." "Osztott képernyő" "Alkalmazásinformáció a következőhöz: %1$s" "A(z) %1$s használati beállításai" @@ -188,7 +187,7 @@ "Sikertelen: %1$s" "Privát terület" "Koppintson a beállításhoz vagy a megnyitáshoz" - "Privát terület" + "Privát" "Privát terület beállításai" "Privát, feloldott." "Privát, zárolt." diff --git a/res/values-hy/strings.xml b/res/values-hy/strings.xml index 491ba05be8..2d345f2931 100644 --- a/res/values-hy/strings.xml +++ b/res/values-hy/strings.xml @@ -27,8 +27,7 @@ "Վիջեթներն անջատված են անվտանգ ռեժիմում" "Դյուրանցումն անհասանելի է" "Հիմնական էկրան" - - + "Սահմանել %1$s գործարկիչը որպես մեկնարկի կանխադրված հավելված Կարգավորումներում" "Տրոհել էկրանը" "Տեղեկություններ %1$s հավելվածի մասին" "Օգտագործման կարգավորումներ (%1$s)" diff --git a/res/values-in/strings.xml b/res/values-in/strings.xml index 13353851b3..9ced9f4832 100644 --- a/res/values-in/strings.xml +++ b/res/values-in/strings.xml @@ -27,8 +27,7 @@ "Widget dinonaktifkan dalam mode Aman" "Pintasan tidak tersedia" "Layar utama" - - + "Jadikan %1$s sebagai aplikasi layar utama default di Setelan" "Layar terpisah" "Info aplikasi untuk %1$s" "Setelan penggunaan untuk %1$s" diff --git a/res/values-is/strings.xml b/res/values-is/strings.xml index 48652642e0..2ab781732c 100644 --- a/res/values-is/strings.xml +++ b/res/values-is/strings.xml @@ -27,8 +27,7 @@ "Græjur eru óvirkar í öruggri stillingu" "Flýtileið er ekki tiltæk" "Heim" - - + "Stilltu %1$s sem sjálfgefið heimaforrit í stillingunum" "Skipta skjá" "Upplýsingar um forrit fyrir %1$s" "Notkunarstillingar fyrir %1$s" diff --git a/res/values-it/strings.xml b/res/values-it/strings.xml index 41b4b609fa..a59de6c049 100644 --- a/res/values-it/strings.xml +++ b/res/values-it/strings.xml @@ -27,8 +27,7 @@ "Widget disabilitati in modalità provvisoria" "La scorciatoia non è disponibile" "Home" - - + "Imposta %1$s come app iniziale predefinita nelle Impostazioni" "Schermo diviso" "Informazioni sull\'app %1$s" "Impostazioni di utilizzo per %1$s" diff --git a/res/values-iw/strings.xml b/res/values-iw/strings.xml index ae71edd4e5..89a0c8aa8b 100644 --- a/res/values-iw/strings.xml +++ b/res/values-iw/strings.xml @@ -27,8 +27,7 @@ "ווידג\'טים מושבתים במצב בטוח" "קיצור הדרך אינו זמין" "בית" - - + "הגדרה של %1$s כאפליקציית הבית ב\'הגדרות\'" "מסך מפוצל" "‏פרטים על האפליקציה %1$s" "‏הגדרות שימוש ב-%1$s" diff --git a/res/values-ja/strings.xml b/res/values-ja/strings.xml index c64d3352b5..b48c7bea3e 100644 --- a/res/values-ja/strings.xml +++ b/res/values-ja/strings.xml @@ -27,8 +27,7 @@ "セーフモードではウィジェットは無効です" "ショートカットは使用できません" "ホーム" - - + "[設定] で %1$s をデフォルトのホームアプリに設定します" "分割画面" "%1$s のアプリ情報" "%1$s の使用設定" diff --git a/res/values-ka/strings.xml b/res/values-ka/strings.xml index bcced9a3ff..76b8b5d6c0 100644 --- a/res/values-ka/strings.xml +++ b/res/values-ka/strings.xml @@ -27,8 +27,7 @@ "უსაფრთხო რეჟიმში ვიჯეტი გამორთულია" "მალსახმობი მიუწვდომელია" "მთავარი გვერდი" - - + "%1$s-ის დაყენება ნაგულისხმევ Home აპად პარამეტრებში" "ეკრანის გაყოფა" "%1$s-ის აპის ინფო" "გამოყენების პარამეტრები %1$s-ისთვის" diff --git a/res/values-kk/strings.xml b/res/values-kk/strings.xml index 62bb983b79..95d4420157 100644 --- a/res/values-kk/strings.xml +++ b/res/values-kk/strings.xml @@ -27,8 +27,7 @@ "Қауіпсіз режимде виджеттер өшіріледі" "Таңбаша қолжетімді емес" "Негізгі экран" - - + "Параметрлерден %1$s қолданбасын әдепкі негізгі экран қолданбасы ретінде орнатыңыз." "Экранды бөлу" "%1$s қолданбасы туралы ақпарат" "%1$s пайдалану параметрлері" diff --git a/res/values-km/strings.xml b/res/values-km/strings.xml index b733699691..5c71276583 100644 --- a/res/values-km/strings.xml +++ b/res/values-km/strings.xml @@ -27,8 +27,7 @@ "បាន​បិទ​ធាតុ​ក្រាហ្វិក​ក្នុង​របៀប​សុវត្ថិភាព" "ផ្លូវកាត់មិនអាចប្រើបានទេ" "អេក្រង់ដើម" - - + "កំណត់ %1$s ជាកម្មវិធីអេក្រង់ដើមលំនាំដើមនៅក្នុង \"ការកំណត់\"" "មុខងារ​បំបែកអេក្រង់" "ព័ត៌មានកម្មវិធី​សម្រាប់ %1$s" "ការកំណត់ការប្រើប្រាស់សម្រាប់ %1$s" diff --git a/res/values-kn/strings.xml b/res/values-kn/strings.xml index 16b39f8bc2..931ca30b58 100644 --- a/res/values-kn/strings.xml +++ b/res/values-kn/strings.xml @@ -27,8 +27,7 @@ "ಸುರಕ್ಷಿತ ಮೋಡ್‌ನಲ್ಲಿ ವಿಜೆಟ್‌ಗಳನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ" "ಶಾರ್ಟ್‌ಕಟ್ ಲಭ್ಯವಿಲ್ಲ" "ಹೋಮ್" - - + "ಸೆಟ್ಟಿಂಗ್‌ಗಳಲ್ಲಿ %1$s ಅನ್ನು ಡೀಫಾಲ್ಟ್ ಹೋಮ್ ಆ್ಯಪ್ ಆಗಿ ಸೆಟ್‌ ಮಾಡಿ" "ಸ್ಪ್ಲಿಟ್ ಸ್ಕ್ರೀನ್" "%1$s ಗಾಗಿ ಆ್ಯಪ್ ಮಾಹಿತಿ" "%1$s ಗೆ ಸಂಬಂಧಿಸಿದ ಬಳಕೆಯ ಸೆಟ್ಟಿಂಗ್‌ಗಳು" diff --git a/res/values-ko/strings.xml b/res/values-ko/strings.xml index 1d5c58f9d8..9f64cfb24e 100644 --- a/res/values-ko/strings.xml +++ b/res/values-ko/strings.xml @@ -27,8 +27,7 @@ "안전 모드에서 위젯 사용 중지됨" "바로가기를 사용할 수 없음" "홈" - - + "설정에서 %1$s 런처를 기본 홈 앱으로 설정하세요." "화면 분할" "%1$s 앱 정보" "%1$s의 사용량 설정" diff --git a/res/values-ky/strings.xml b/res/values-ky/strings.xml index 6f01db33f7..3e67c365fb 100644 --- a/res/values-ky/strings.xml +++ b/res/values-ky/strings.xml @@ -27,8 +27,7 @@ "Виджеттер Коопсуз режимде өчүрүлгөн" "Кыска жол жок" "Башкы экран" - - + "Параметрлерден %1$s жүргүзгүчүн демейки башкы бет колдонмосу катары коюу" "Экранды бөлүү" "%1$s колдонмосу жөнүндө маалымат" "%1$s колдонмосун пайдалануу параметрлери" diff --git a/res/values-lo/strings.xml b/res/values-lo/strings.xml index 7ec103bc98..e0cd3ee883 100644 --- a/res/values-lo/strings.xml +++ b/res/values-lo/strings.xml @@ -27,8 +27,7 @@ "​ວິດ​ເຈັດ​ຖືກ​ປິດ​ໃນ Safe mode" "ບໍ່ສາມາດໃຊ້ທາງລັດໄດ້" "ໂຮມສະກຣີນ" - - + "ຕັ້ງ %1$s ເປັນແອັບໂຮມເລີ່ມຕົ້ນໃນການຕັ້ງຄ່າ" "ແບ່ງໜ້າຈໍ" "ຂໍ້ມູນແອັບສຳລັບ %1$s" "ການຕັ້ງຄ່າການນຳໃຊ້ສຳລັບ %1$s" diff --git a/res/values-lt/strings.xml b/res/values-lt/strings.xml index 1112474cd5..9846525aa7 100644 --- a/res/values-lt/strings.xml +++ b/res/values-lt/strings.xml @@ -27,8 +27,7 @@ "Valdikliai išjungti Saugiame režime" "Sparčiojo klavišo negalima naudoti" "Pagrindinis" - - + "Nustatykite „%1$s“ kaip numatytąją pagrindinę programą skiltyje „Nustatymai“" "Išskaidyto ekrano režimas" "Programos „%1$s“ informacija" "„%1$s“ naudojimo nustatymai" diff --git a/res/values-lv/strings.xml b/res/values-lv/strings.xml index 4f1a92cb06..49f7ffe3a4 100644 --- a/res/values-lv/strings.xml +++ b/res/values-lv/strings.xml @@ -27,8 +27,7 @@ "Logrīki atspējoti drošajā režīmā" "Saīsne nav pieejama." "Sākums" - - + "Iestatiet %1$s kā noklusējuma sākuma lietotni iestatījumos." "Sadalīt ekrānu" "%1$s: informācija par lietotni" "Lietojuma iestatījumi: %1$s" diff --git a/res/values-mk/strings.xml b/res/values-mk/strings.xml index d984ca03d2..2bfa089ad3 100644 --- a/res/values-mk/strings.xml +++ b/res/values-mk/strings.xml @@ -27,8 +27,7 @@ "Додатоците се оневозможени во безбеден режим" "Кратенката не е достапна" "Почетен екран" - - + "Поставете %1$s да биде стандардна апликација за почетен екран во „Поставки“" "Поделен екран" "Податоци за апликација за %1$s" "Поставки за користење за %1$s" diff --git a/res/values-ml/strings.xml b/res/values-ml/strings.xml index ea8849eb5c..a2babd510c 100644 --- a/res/values-ml/strings.xml +++ b/res/values-ml/strings.xml @@ -27,8 +27,7 @@ "സുരക്ഷിത മോഡിൽ വിജറ്റുകൾ പ്രവർത്തനരഹിതമാക്കി" "കുറുക്കുവഴി ലഭ്യമല്ല" "ഹോം" - - + "ക്രമീകരണത്തിൽ %1$s എന്നത് ഡിഫോൾട്ട് ഹോം ആപ്പായി സജ്ജീകരിക്കുക" "സ്‌ക്രീൻ വിഭജന മോഡ്" "%1$s എന്നതിന്റെ ആപ്പ് വിവരങ്ങൾ" "%1$s എന്നതിനുള്ള ഉപയോഗ ക്രമീകരണം" diff --git a/res/values-mn/strings.xml b/res/values-mn/strings.xml index 8554717ca1..93a1c9c6fa 100644 --- a/res/values-mn/strings.xml +++ b/res/values-mn/strings.xml @@ -27,8 +27,7 @@ "Safe горимд виджетүүдийг идэвхгүйжүүлсэн" "Товчлол алга" "Нүүр" - - + "%1$s-г Тохиргоонд өгөгдмөл үндсэн аппаар тохируулна уу" "Дэлгэцийг хуваах" "%1$s-н аппын мэдээлэл" "%1$s-н ашиглалтын тохиргоо" diff --git a/res/values-mr/strings.xml b/res/values-mr/strings.xml index dcdf25c290..c084fab567 100644 --- a/res/values-mr/strings.xml +++ b/res/values-mr/strings.xml @@ -27,8 +27,7 @@ "विजेट सुरक्षित मोडमध्ये अक्षम झाले" "शॉर्टकट उपलब्ध नाही" "होम" - - + "सेटिंग्ज मध्ये %1$s हे डीफॉल्ट होम अ‍ॅप म्हणून सेट करा" "स्प्लिट स्क्रीन" "%1$s साठी ॲपशी संबंधित माहिती" "%1$s साठी वापरासंबंधित सेटिंग्ज" diff --git a/res/values-ms/strings.xml b/res/values-ms/strings.xml index 22ee2cb1e7..72d92e21ce 100644 --- a/res/values-ms/strings.xml +++ b/res/values-ms/strings.xml @@ -27,8 +27,7 @@ "Widget dilumpuhkan dalam mod Selamat" "Pintasan tidak tersedia" "Rumah" - - + "Tetapkan %1$s sebagai apl skrin utama lalai dalam Tetapan" "Skrin pisah" "Maklumat apl untuk %1$s" "Tetapan penggunaan sebanyak %1$s" diff --git a/res/values-my/strings.xml b/res/values-my/strings.xml index bca1c6eed9..194ece73ba 100644 --- a/res/values-my/strings.xml +++ b/res/values-my/strings.xml @@ -27,8 +27,7 @@ "လုံခြုံရေး မုဒ်ထဲမှာ ဝီဂျက်များကို ပိတ်ထား" "ဖြတ်လမ်း မရနိုင်ပါ" "ပင်မစာမျက်နှာ" - - + "ဆက်တင်များတွင် %1$s ကို မူရင်းပင်မစာမျက်နှာအက်ပ်အဖြစ် သတ်မှတ်ရန်" "မျက်နှာပြင် ခွဲ၍ပြသခြင်း" "%1$s အတွက် အက်ပ်အချက်အလက်" "%1$s အတွက် အသုံးပြုမှုဆက်တင်များ" diff --git a/res/values-nb/strings.xml b/res/values-nb/strings.xml index 1440443422..a9e6c5d6cc 100644 --- a/res/values-nb/strings.xml +++ b/res/values-nb/strings.xml @@ -27,8 +27,7 @@ "Moduler er deaktivert i sikker modus" "Snarveien er ikke tilgjengelig" "Startskjerm" - - + "Angi %1$s som standard startsideapp i innstillingene" "Delt skjerm" "Appinformasjon for %1$s" "Bruksinnstillinger for %1$s" diff --git a/res/values-ne/strings.xml b/res/values-ne/strings.xml index 52613cb857..7880c36e85 100644 --- a/res/values-ne/strings.xml +++ b/res/values-ne/strings.xml @@ -27,8 +27,7 @@ "सुरक्षित मोडमा विगेटहरू अक्षम गरियो" "सर्टकट उपलब्ध छैन" "होम" - - + "सेटिङमा गई %1$s लाई डिफल्ट होम एपका रूपमा सेट गर्नुहोस्" "स्प्लिट स्क्रिन" "%1$s का हकमा एपसम्बन्धी जानकारी" "%1$s को प्रयोगसम्बन्धी सेटिङ" diff --git a/res/values-nl/strings.xml b/res/values-nl/strings.xml index 90dcd466d1..7b3f5639ae 100644 --- a/res/values-nl/strings.xml +++ b/res/values-nl/strings.xml @@ -27,8 +27,7 @@ "Widgets uitgezet in veilige modus" "Snelkoppeling is niet beschikbaar" "Startscherm" - - + "Stel %1$s in als de standaard startscherm-app in Instellingen" "Gesplitst scherm" "App-info voor %1$s" "Gebruiksinstellingen voor %1$s" diff --git a/res/values-or/strings.xml b/res/values-or/strings.xml index 6d2f87ef20..33b7664cfb 100644 --- a/res/values-or/strings.xml +++ b/res/values-or/strings.xml @@ -27,8 +27,7 @@ "ନିରାପଦ ମୋଡରେ ୱିଜେଟ୍‌ ଅକ୍ଷମ କରାଗଲା" "ଶର୍ଟକଟ୍‌ ଉପଲବ୍ଧ ନାହିଁ" "ହୋମ" - - + "ସେଟିଂସରେ %1$sକୁ ଡିଫଲ୍ଟ Home ଆପ ଭାବରେ ସେଟ କରନ୍ତୁ" "ସ୍କ୍ରିନ‌କୁ ସ୍ପ୍ଲିଟ କରନ୍ତୁ" "%1$s ପାଇଁ ଆପ ସୂଚନା" "%1$s ପାଇଁ ବ୍ୟବହାର ସେଟିଂସ" diff --git a/res/values-pa/strings.xml b/res/values-pa/strings.xml index 8cb5515145..71c25ebb95 100644 --- a/res/values-pa/strings.xml +++ b/res/values-pa/strings.xml @@ -27,8 +27,7 @@ "ਵਿਜੇਟ ਸੁਰੱਖਿਅਤ ਮੋਡ ਵਿੱਚ ਅਸਮਰਥਿਤ" "ਸ਼ਾਰਟਕੱਟ ਉਪਲਬਧ ਨਹੀਂ ਹੈ" "ਮੁੱਖ ਪੰਨਾ" - - + "ਸੈਟਿੰਗਾਂ ਵਿੱਚ ਜਾ ਕੇ %1$s ਨੂੰ ਪੂਰਵ-ਨਿਰਧਾਰਿਤ ਹੋਮ ਐਪ ਵਜੋਂ ਸੈੱਟ ਕਰੋ" "ਸਪਲਿਟ ਸਕ੍ਰੀਨ" "%1$s ਲਈ ਐਪ ਜਾਣਕਾਰੀ" "%1$s ਲਈ ਵਰਤੋਂ ਸੈਟਿੰਗਾਂ" diff --git a/res/values-pl/strings.xml b/res/values-pl/strings.xml index 685b7d911d..587c40fdc7 100644 --- a/res/values-pl/strings.xml +++ b/res/values-pl/strings.xml @@ -27,8 +27,7 @@ "Widżety są wyłączone w trybie bezpiecznym" "Skrót nie jest dostępny" "Ekran główny" - - + "Otwórz Ustawienia i ustaw aplikację %1$s jako domyślną aplikację ekranu głównego" "Podziel ekran" "Informacje o aplikacji: %1$s" "%1$s – ustawienia użycia" diff --git a/res/values-pt-rPT/strings.xml b/res/values-pt-rPT/strings.xml index 14a3cf52f4..67d93deebc 100644 --- a/res/values-pt-rPT/strings.xml +++ b/res/values-pt-rPT/strings.xml @@ -27,8 +27,7 @@ "Widgets desativados no Modo de segurança" "O atalho não está disponível" "Página inicial" - - + "Defina a app %1$s como a app inicial predefinida nas Definições" "Ecrã dividido" "Informações da app para %1$s" "Definições de utilização para %1$s" diff --git a/res/values-pt/strings.xml b/res/values-pt/strings.xml index 4c4b13bacd..4302588495 100644 --- a/res/values-pt/strings.xml +++ b/res/values-pt/strings.xml @@ -27,8 +27,7 @@ "Widgets desativados no modo de segurança" "O atalho não está disponível" "Início" - - + "Definir %1$s como app de início padrão nas Configurações" "Tela dividida" "Informações do app %1$s" "Configurações de uso de %1$s" diff --git a/res/values-ro/strings.xml b/res/values-ro/strings.xml index 256fa60964..d9e0413491 100644 --- a/res/values-ro/strings.xml +++ b/res/values-ro/strings.xml @@ -27,8 +27,7 @@ "Widgeturile sunt dezactivate în modul de siguranță" "Comanda rapidă nu este disponibilă" "Pagina de pornire" - - + "Setează %1$s drept aplicație ecran de pornire prestabilită, în Setări" "Ecran împărțit" "Informații despre aplicație pentru %1$s" "Setări de utilizare pentru %1$s" diff --git a/res/values-ru/strings.xml b/res/values-ru/strings.xml index 402143dde7..64a21f7278 100644 --- a/res/values-ru/strings.xml +++ b/res/values-ru/strings.xml @@ -27,8 +27,7 @@ "Виджеты отключены в безопасном режиме" "Ярлык недоступен" "Главный экран" - - + "Установить \"%1$s\" в качестве приложения главного экрана по умолчанию в настройках" "Разделить экран" "Сведения о приложении \"%1$s\"" "Настройки использования приложения \"%1$s\"" diff --git a/res/values-si/strings.xml b/res/values-si/strings.xml index 455f18b777..71efc03311 100644 --- a/res/values-si/strings.xml +++ b/res/values-si/strings.xml @@ -27,8 +27,7 @@ "සුරක්ෂිත ආකාරය තුළ විජටය අබල කරන ලදි" "කෙටි මග ලබා ගත නොහැකිය" "මුල් පිටුව" - - + "%1$s සැකසීම් තුළ පෙරනිමි මුල් පිටුව යෙදුම ලෙස සකසන්න" "බෙදුම් තිරය" "%1$s සඳහා යෙදුම් තතු" "%1$s සඳහා භාවිත සැකසීම්" diff --git a/res/values-sk/strings.xml b/res/values-sk/strings.xml index 7771cf7b46..43bc29c326 100644 --- a/res/values-sk/strings.xml +++ b/res/values-sk/strings.xml @@ -27,8 +27,7 @@ "Miniaplikácie sú v núdzovom režime zakázané" "Skratky nie sú k dispozícii" "Domov" - - + "Nastaviť aplikáciu %1$s ako predvolenú vstupnú aplikáciu v Nastaveniach" "Rozdeliť obrazovku" "Informácie o aplikácii pre %1$s" "Nastavenia používania pre %1$s" diff --git a/res/values-sl/strings.xml b/res/values-sl/strings.xml index 2e75b1761c..5d423ca27b 100644 --- a/res/values-sl/strings.xml +++ b/res/values-sl/strings.xml @@ -27,8 +27,7 @@ "Pripomočki so onemogočeni v varnem načinu" "Bližnjica ni na voljo" "Začetni zaslon" - - + "Nastavitev zaganjalnika %1$s kot privzete aplikacije za začetni zaslon v nastavitvah" "Razdeljen zaslon" "Podatki o aplikaciji za: %1$s" "Nastavitve uporabe za »%1$s«" diff --git a/res/values-sq/strings.xml b/res/values-sq/strings.xml index a00f11a6b3..8f4133dcf7 100644 --- a/res/values-sq/strings.xml +++ b/res/values-sq/strings.xml @@ -27,8 +27,7 @@ "Miniaplikacionet janë të çaktivizuara në modalitetin e sigurt" "Shkurtorja nuk është e disponueshme" "Ekrani bazë" - - + "Cakto %1$s si aplikacionin e parazgjedhur të ekranit bazë te \"Cilësimet\"" "Ekrani i ndarë" "Informacioni i aplikacionit për %1$s" "Cilësimet e përdorimit për \"%1$s\"" diff --git a/res/values-sr/strings.xml b/res/values-sr/strings.xml index 97d3ff48e7..cd6523ccb9 100644 --- a/res/values-sr/strings.xml +++ b/res/values-sr/strings.xml @@ -27,8 +27,7 @@ "Виџети су онемогућени у Безбедном режиму" "Пречица није доступна" "Почетни екран" - - + "Подесите %1$s као подразумевану почетну апликацију у Подешавањима" "Подељени екран" "Информације о апликацији за: %1$s" "Подешавања потрошње за %1$s" diff --git a/res/values-sv/strings.xml b/res/values-sv/strings.xml index 8a8524e6ef..4f95c6708e 100644 --- a/res/values-sv/strings.xml +++ b/res/values-sv/strings.xml @@ -27,8 +27,7 @@ "Widgets är inaktiverade i felsäkert läge" "Genvägen är inte tillgänglig" "Startskärm" - - + "Ställ in %1$s som standardstartskärmsapp i Inställningar" "Delad skärm" "Appinformation för %1$s" "Användningsinställningar för %1$s" diff --git a/res/values-sw/strings.xml b/res/values-sw/strings.xml index 64f629608e..53a9abe056 100644 --- a/res/values-sw/strings.xml +++ b/res/values-sw/strings.xml @@ -27,8 +27,7 @@ "Wijeti zimezimwa katika hali ya Usalama" "Hakuna njia ya mkato" "Skrini ya kwanza" - - + "Weka %1$s iwe programu chaguomsingi ya mwanzo kwenye Mipangilio" "Gawa skrini" "Maelezo ya programu ya %1$s" "Mipangilio ya matumizi ya %1$s" diff --git a/res/values-ta/strings.xml b/res/values-ta/strings.xml index 189f4ced83..738f85cafe 100644 --- a/res/values-ta/strings.xml +++ b/res/values-ta/strings.xml @@ -27,8 +27,7 @@ "பாதுகாப்புப் பயன்முறையில் விட்ஜெட்கள் முடக்கப்பட்டுள்ளன" "ஷார்ட்கட் இல்லை" "முகப்பு" - - + "அமைப்புகளில் %1$s என்பதை இயல்பு முகப்பு ஆப்ஸாக அமையுங்கள்" "திரைப் பிரிப்பு" "%1$sக்கான ஆப்ஸ் தகவல்கள்" "%1$sக்கான உபயோக அமைப்புகள்" diff --git a/res/values-te/strings.xml b/res/values-te/strings.xml index d789baa843..0520ebf65e 100644 --- a/res/values-te/strings.xml +++ b/res/values-te/strings.xml @@ -27,8 +27,7 @@ "సురక్షిత మోడ్‌లో విడ్జెట్‌లు నిలిపివేయబడ్డాయి" "షార్ట్‌కట్ అందుబాటులో లేదు" "మొదటి ట్యాబ్" - - + "సెట్టింగ్‌లలో %1$s‌ను ఆటోమేటిక్ సెట్టింగ్ మొదటి స్క్రీన్ యాప్‌గా సెట్ చేయండి" "స్ప్లిట్ స్క్రీన్" "%1$s కోసం యాప్ సమాచారం" "%1$sకు సంబంధించిన వినియోగ సెట్టింగ్‌లు" diff --git a/res/values-th/strings.xml b/res/values-th/strings.xml index f89638d8f2..554fd94e5f 100644 --- a/res/values-th/strings.xml +++ b/res/values-th/strings.xml @@ -27,8 +27,7 @@ "มีการปิดใช้งานวิดเจ็ตในเซฟโหมด" "ทางลัดไม่พร้อมใช้งาน" "หน้าแรก" - - + "ตั้ง %1$s เป็นแอปหน้าแรกเริ่มต้นในการตั้งค่า" "แยกหน้าจอ" "ข้อมูลแอปสำหรับ %1$s" "การตั้งค่าการใช้งานสำหรับ %1$s" diff --git a/res/values-tl/strings.xml b/res/values-tl/strings.xml index 8754a12a49..cb6fe66f8f 100644 --- a/res/values-tl/strings.xml +++ b/res/values-tl/strings.xml @@ -27,8 +27,7 @@ "Naka-disable ang mga widget sa Safe mode" "Hindi available ang shortcut" "Home" - - + "Itakda ang %1$s bilang default na home app sa Mga Setting" "Split screen" "Impormasyon ng app para sa %1$s" "Mga setting ng paggamit para sa %1$s" diff --git a/res/values-tr/strings.xml b/res/values-tr/strings.xml index 761ce56704..b12ec27378 100644 --- a/res/values-tr/strings.xml +++ b/res/values-tr/strings.xml @@ -27,8 +27,7 @@ "Güvenli modda widget\'lar devre dışı" "Kısayol kullanılamıyor" "Ana ekran" - - + "%1$s başlatıcısını Ayarlar\'da varsayılan ana ekran uygulaması olarak ayarlayın" "Bölünmüş ekran" "%1$s uygulama bilgileri" "%1$s ile ilgili kullanım ayarları" diff --git a/res/values-uk/strings.xml b/res/values-uk/strings.xml index 2c85d5a292..2a01daeb5c 100644 --- a/res/values-uk/strings.xml +++ b/res/values-uk/strings.xml @@ -27,8 +27,7 @@ "У безпечному режимі віджети вимкнено" "Ярлик недоступний" "Головний екран" - - + "Зробити %1$s додатком головного екрана за умовчанням у налаштуваннях" "Розділити екран" "Інформація про додаток для %1$s" "Параметри використання (%1$s)" diff --git a/res/values-ur/strings.xml b/res/values-ur/strings.xml index 09f4304744..544357e96a 100644 --- a/res/values-ur/strings.xml +++ b/res/values-ur/strings.xml @@ -27,8 +27,7 @@ "ویجیٹس کو محفوظ وضع میں غیر فعال کر دیا گیا" "شارٹ کٹ دستیاب نہیں ہے" "ہوم" - - + "ترتیبات میں %1$s کو بطور ڈیفالٹ ہوم ایپ سیٹ کریں" "اسپلٹ اسکرین" "‏%1$s کے لیے ایپ کی معلومات" "‏%1$s کیلئے استعمال کی ترتیبات" diff --git a/res/values-uz/strings.xml b/res/values-uz/strings.xml index d8dea69c4b..5038d4f6ab 100644 --- a/res/values-uz/strings.xml +++ b/res/values-uz/strings.xml @@ -27,8 +27,7 @@ "Xavfsiz rejimda vidjetlar o‘chirib qo‘yilgan" "Tezkor tugmadan foydalanib bo‘lmaydi" "Bosh ekran" - - + "Sozlamalar orqali %1$s ilovasini birlamchi bosh ekran ilovasi sifatida belgilash" "Ekranni ikkiga ajratish" "%1$s ilovasi axboroti" "%1$s uchun sarf sozlamalari" diff --git a/res/values-vi/strings.xml b/res/values-vi/strings.xml index 172c995629..e5252b1fc6 100644 --- a/res/values-vi/strings.xml +++ b/res/values-vi/strings.xml @@ -27,8 +27,7 @@ "Tiện ích bị vô hiệu hóa ở chế độ an toàn" "Lối tắt không khả dụng" "Màn hình chính" - - + "Đặt %1$s làm trình chạy mặc định trong phần Cài đặt" "Chia đôi màn hình" "Thông tin ứng dụng cho %1$s" "Chế độ cài đặt mức sử dụng %1$s" diff --git a/res/values-zh-rCN/strings.xml b/res/values-zh-rCN/strings.xml index 9c658dc88a..7a761585f4 100644 --- a/res/values-zh-rCN/strings.xml +++ b/res/values-zh-rCN/strings.xml @@ -27,8 +27,7 @@ "安全模式下不允许使用微件" "无法使用快捷方式" "主屏幕" - - + "在“设置”中将“%1$s”设为默认主屏幕应用" "分屏" "%1$s 的应用信息" "%1$s的使用设置" diff --git a/res/values-zh-rHK/strings.xml b/res/values-zh-rHK/strings.xml index af32638c17..ed6e52f9cb 100644 --- a/res/values-zh-rHK/strings.xml +++ b/res/values-zh-rHK/strings.xml @@ -27,8 +27,7 @@ "在安全模式中無法使用小工具" "沒有可用的捷徑" "主畫面" - - + "前往「設定」將「%1$s」設為預設主畫面應用程式" "分割螢幕" "%1$s 的應用程式資料" "「%1$s」的用量設定" diff --git a/res/values-zh-rTW/strings.xml b/res/values-zh-rTW/strings.xml index 54a1c5dd50..264d6079bf 100644 --- a/res/values-zh-rTW/strings.xml +++ b/res/values-zh-rTW/strings.xml @@ -27,8 +27,7 @@ "在安全模式下無法使用小工具" "目前無法使用捷徑" "主畫面" - - + "前往「設定」將「%1$s」設為預設主畫面應用程式" "分割畫面" "「%1$s」的應用程式資訊" "「%1$s」的用量設定" diff --git a/res/values-zu/strings.xml b/res/values-zu/strings.xml index 9752e184b0..be6cd493fe 100644 --- a/res/values-zu/strings.xml +++ b/res/values-zu/strings.xml @@ -27,8 +27,7 @@ "Amawijethi akhutshaziwe kwimodi yokuphepha" "Isinqamuleli asitholakali" "Ikhaya" - - + "Setha i-%1$s njenge-app yasekhaya ezenzakalelayo Kumasethingi" "Hlukanisa isikrini" "Ulwazi lwe-App ye-%1$s" "Amasethingi okusetshenziswa ka-%1$s" From 25da1d83d112ca2beb710537a0c0c3862a657abb Mon Sep 17 00:00:00 2001 From: Bill Yi Date: Mon, 15 Jul 2024 14:42:37 -0700 Subject: [PATCH 07/13] Import translations. DO NOT MERGE ANYWHERE Auto-generated-cl: translation import Change-Id: I57ee8549d4e1adfa45d23bdf0721055cf17fb619 --- res/values-de/strings.xml | 4 ++-- res/values-hu/strings.xml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/res/values-de/strings.xml b/res/values-de/strings.xml index b1d300ca3b..5933fbb76f 100644 --- a/res/values-de/strings.xml +++ b/res/values-de/strings.xml @@ -34,7 +34,7 @@ "Dieses App-Paar wird auf diesem Gerät nicht unterstützt" "Gerät aufklappen, um dieses App-Paar zu verwenden" "App-Paar nicht verfügbar" - "Zum Verschieben des Widgets berühren und halten" + "Zum Verschieben des Widgets gedrückt halten" "Doppeltippen und halten, um ein Widget zu bewegen oder benutzerdefinierte Aktionen zu nutzen." "%1$d × %2$d" "%1$d breit und %2$d hoch" @@ -73,7 +73,7 @@ "App" "Alle Apps" "Benachrichtigungen" - "Zum Verschieben einer Verknüpfung berühren und halten" + "Zum Verschieben einer Verknüpfung gedrückt halten" "Doppeltippen und halten, um eine Verknüpfung zu bewegen oder benutzerdefinierte Aktionen zu nutzen." "Auf diesem Startbildschirm ist kein Platz mehr vorhanden" "Ablage \"Favoriten\" ist voll." diff --git a/res/values-hu/strings.xml b/res/values-hu/strings.xml index ac872cfbc8..3c20ed3782 100644 --- a/res/values-hu/strings.xml +++ b/res/values-hu/strings.xml @@ -185,7 +185,7 @@ "Sikertelen: %1$s" "Privát terület" "Privát alkalmazások zárolásának és rejtve tartásának fenntartása" - "Privát terület" + "Privát" "Privát terület beállításai" "Privát terület zárolása/zárolásának feloldása" "Zárolás" From 7098ba260f8ba7b49c1fc8bcbf270e2c08567ab8 Mon Sep 17 00:00:00 2001 From: Sihua Ma Date: Mon, 15 Jul 2024 22:31:14 +0000 Subject: [PATCH 08/13] Add test for LauncherWidgetHolder Number of new tested methods: 8 Test: atest NexusLauncherRoboTests:LauncherWidgetHolderTest Test: SysUI studio Flag: TEST_ONLY Bug: 353303621 Change-Id: I8751c8de8d719e567775368d988794b2d7abc37e --- .../widget/LauncherWidgetHolder.java | 4 +- .../widget/LauncherWidgetHolderTest.kt | 149 ++++++++++++++++++ 2 files changed, 152 insertions(+), 1 deletion(-) create mode 100644 tests/multivalentTests/src/com/android/launcher3/widget/LauncherWidgetHolderTest.kt diff --git a/src/com/android/launcher3/widget/LauncherWidgetHolder.java b/src/com/android/launcher3/widget/LauncherWidgetHolder.java index 1fb8c8306f..3016c9a2fd 100644 --- a/src/com/android/launcher3/widget/LauncherWidgetHolder.java +++ b/src/com/android/launcher3/widget/LauncherWidgetHolder.java @@ -37,6 +37,7 @@ import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; import androidx.annotation.WorkerThread; import com.android.launcher3.BaseActivity; @@ -484,7 +485,8 @@ public class LauncherWidgetHolder { * Sets or unsets a flag the can change whether the widget host should be in the listening * state. */ - private void setShouldListenFlag(int flag, boolean on) { + @VisibleForTesting + void setShouldListenFlag(int flag, boolean on) { if (on) { mFlags.updateAndGet(old -> old | flag); } else { diff --git a/tests/multivalentTests/src/com/android/launcher3/widget/LauncherWidgetHolderTest.kt b/tests/multivalentTests/src/com/android/launcher3/widget/LauncherWidgetHolderTest.kt new file mode 100644 index 0000000000..1a659e2b56 --- /dev/null +++ b/tests/multivalentTests/src/com/android/launcher3/widget/LauncherWidgetHolderTest.kt @@ -0,0 +1,149 @@ +/* + * Copyright (C) 2024 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.widget + +import androidx.test.annotation.UiThreadTest +import androidx.test.filters.SmallTest +import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation +import com.android.launcher3.BuildConfig.WIDGETS_ENABLED +import com.android.launcher3.util.ActivityContextWrapper +import com.android.launcher3.util.LauncherMultivalentJUnit +import com.android.launcher3.widget.LauncherWidgetHolder.FLAG_ACTIVITY_RESUMED +import com.android.launcher3.widget.LauncherWidgetHolder.FLAG_ACTIVITY_STARTED +import com.android.launcher3.widget.LauncherWidgetHolder.FLAG_STATE_IS_NORMAL +import org.junit.After +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse +import org.junit.Assert.assertSame +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.mock +import org.mockito.Mockito.times +import org.mockito.Mockito.verify +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.whenever + +@SmallTest +@RunWith(LauncherMultivalentJUnit::class) +class LauncherWidgetHolderTest { + private lateinit var widgetHolder: LauncherWidgetHolder + + @Before + fun setUp() { + assertTrue(WIDGETS_ENABLED) + widgetHolder = + LauncherWidgetHolder(ActivityContextWrapper(getInstrumentation().targetContext)) {} + } + + @After + fun tearDown() { + widgetHolder.destroy() + } + + @Test + fun widget_holder_start_listening() { + val testView = mock(PendingAppWidgetHostView::class.java) + widgetHolder.mViews[0] = testView + widgetHolder.setListeningFlag(false) + assertFalse(widgetHolder.isListening) + widgetHolder.startListening() + widgetHolder.widgetHolderExecutor.submit {}.get() + getInstrumentation().waitForIdleSync() + assertTrue(widgetHolder.isListening) + verify(testView, times(1)).reInflate() + widgetHolder.clearWidgetViews() + } + + @Test + fun holder_start_listening_after_activity_start() { + widgetHolder.setShouldListenFlag(FLAG_STATE_IS_NORMAL or FLAG_ACTIVITY_RESUMED, true) + widgetHolder.setActivityStarted(false) + widgetHolder.widgetHolderExecutor.submit {}.get() + assertFalse(widgetHolder.shouldListen(widgetHolder.mFlags.get())) + widgetHolder.setActivityStarted(true) + widgetHolder.widgetHolderExecutor.submit {}.get() + assertTrue(widgetHolder.shouldListen(widgetHolder.mFlags.get())) + } + + @Test + fun holder_start_listening_after_activity_resume() { + widgetHolder.setShouldListenFlag(FLAG_STATE_IS_NORMAL or FLAG_ACTIVITY_STARTED, true) + widgetHolder.setActivityResumed(false) + widgetHolder.widgetHolderExecutor.submit {}.get() + assertFalse(widgetHolder.shouldListen(widgetHolder.mFlags.get())) + widgetHolder.setActivityResumed(true) + widgetHolder.widgetHolderExecutor.submit {}.get() + assertTrue(widgetHolder.shouldListen(widgetHolder.mFlags.get())) + } + + @Test + fun holder_start_listening_after_state_normal() { + widgetHolder.setShouldListenFlag(FLAG_ACTIVITY_RESUMED or FLAG_ACTIVITY_STARTED, true) + widgetHolder.setStateIsNormal(false) + widgetHolder.widgetHolderExecutor.submit {}.get() + assertFalse(widgetHolder.shouldListen(widgetHolder.mFlags.get())) + widgetHolder.setStateIsNormal(true) + widgetHolder.widgetHolderExecutor.submit {}.get() + assertTrue(widgetHolder.shouldListen(widgetHolder.mFlags.get())) + } + + @Test + @UiThreadTest + fun widget_holder_create_view() { + val mockProviderInfo = mock(LauncherAppWidgetProviderInfo::class.java) + doReturn(false).whenever(mockProviderInfo).isCustomWidget + assertEquals(0, widgetHolder.mViews.size()) + widgetHolder.createView(APP_WIDGET_ID, mockProviderInfo) + assertEquals(1, widgetHolder.mViews.size()) + assertEquals(APP_WIDGET_ID, widgetHolder.mViews.get(0).appWidgetId) + widgetHolder.deleteAppWidgetId(APP_WIDGET_ID) + assertEquals(0, widgetHolder.mViews.size()) + } + + @Test + fun holder_add_provider_change_listener() { + val listener = LauncherWidgetHolder.ProviderChangedListener {} + widgetHolder.addProviderChangeListener(listener) + getInstrumentation().waitForIdleSync() + assertEquals(1, widgetHolder.mProviderChangedListeners.size) + assertSame(widgetHolder.mProviderChangedListeners.first(), listener) + widgetHolder.removeProviderChangeListener(listener) + } + + @Test + fun holder_remove_provider_change_listener() { + val listener = LauncherWidgetHolder.ProviderChangedListener {} + widgetHolder.addProviderChangeListener(listener) + widgetHolder.removeProviderChangeListener(listener) + getInstrumentation().waitForIdleSync() + assertEquals(0, widgetHolder.mProviderChangedListeners.size) + } + + @Test + fun widget_holder_stop_listening() { + widgetHolder.setListeningFlag(true) + assertTrue(widgetHolder.isListening) + widgetHolder.stopListening() + widgetHolder.widgetHolderExecutor.submit {}.get() + assertFalse(widgetHolder.isListening) + } + + companion object { + private const val APP_WIDGET_ID = 0 + } +} From 7c8f90f04a19a254c56b2ffcccc06f24bc0d2be3 Mon Sep 17 00:00:00 2001 From: Fengjiang Li Date: Mon, 15 Jul 2024 16:48:24 -0700 Subject: [PATCH 09/13] [Test Week] Add ViewCacheTest Number of new tested methods: 3 Bug: 353303621 Test: This is the unit test Flag: NONE - unit test Change-Id: I235ba375db12b9f3fac6f204018b32e692835eab --- src/com/android/launcher3/util/ViewCache.java | 5 +- .../android/launcher3/util/ViewCacheTest.kt | 89 +++++++++++++++++++ 2 files changed, 93 insertions(+), 1 deletion(-) create mode 100644 tests/src/com/android/launcher3/util/ViewCacheTest.kt diff --git a/src/com/android/launcher3/util/ViewCache.java b/src/com/android/launcher3/util/ViewCache.java index 98e6822542..b98e977dd4 100644 --- a/src/com/android/launcher3/util/ViewCache.java +++ b/src/com/android/launcher3/util/ViewCache.java @@ -21,6 +21,8 @@ import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import androidx.annotation.VisibleForTesting; + import com.android.launcher3.R; /** @@ -67,7 +69,8 @@ public class ViewCache { } } - private static class CacheEntry { + @VisibleForTesting + static class CacheEntry { final int mMaxSize; final View[] mViews; diff --git a/tests/src/com/android/launcher3/util/ViewCacheTest.kt b/tests/src/com/android/launcher3/util/ViewCacheTest.kt new file mode 100644 index 0000000000..bad21c925d --- /dev/null +++ b/tests/src/com/android/launcher3/util/ViewCacheTest.kt @@ -0,0 +1,89 @@ +/* + * Copyright (C) 2024 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.util + +import android.view.View +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import com.android.launcher3.R +import com.android.launcher3.util.ViewCache.CacheEntry +import com.google.common.truth.Truth.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith + +@RunWith(AndroidJUnit4::class) +class ViewCacheTest { + + private lateinit var underTest: ViewCache + + private val context = InstrumentationRegistry.getInstrumentation().context + private val layoutId = + context.run { resources.getIdentifier("test_layout_appwidget_blue", "layout", packageName) } + + @Before + fun setUp() { + underTest = ViewCache() + underTest.setCacheSize(layoutId, 5) + } + + @Test + fun get_view_from_empty_cache() { + val view: View = underTest.getView(layoutId, context, null) + + val cacheEntry = view.getTag(R.id.cache_entry_tag_id) as ViewCache.CacheEntry + assertThat(cacheEntry).isNotNull() + assertThat(cacheEntry.mMaxSize).isEqualTo(5) + assertThat(cacheEntry.mCurrentSize).isEqualTo(0) + assertThat(cacheEntry.mViews[0]).isNull() + } + + @Test + fun recyclerView() { + val view: View = underTest.getView(layoutId, context, null) + val cacheEntry = view.getTag(R.id.cache_entry_tag_id) as ViewCache.CacheEntry + + underTest.recycleView(layoutId, view) + + assertThat(cacheEntry.mMaxSize).isEqualTo(5) + assertThat(cacheEntry.mCurrentSize).isEqualTo(1) + assertThat(cacheEntry.mViews[0]).isSameInstanceAs(view) + } + + @Test + fun get_view_from_cache() { + val view: View = underTest.getView(layoutId, context, null) + underTest.recycleView(layoutId, view) + + val newView = underTest.getView(layoutId, context, null) + + assertThat(view).isSameInstanceAs(newView) + } + + @Test + fun change_tag_id_recyclerView_noOp() { + val view: View = underTest.getView(layoutId, context, null) + val cacheEntry = view.getTag(R.id.cache_entry_tag_id) as ViewCache.CacheEntry + + view.setTag(R.id.cache_entry_tag_id, CacheEntry(3)) + underTest.recycleView(layoutId, view) + + assertThat(cacheEntry.mMaxSize).isEqualTo(5) + assertThat(cacheEntry.mCurrentSize).isEqualTo(0) + assertThat(cacheEntry.mViews[0]).isNull() + } +} From e3d3d653d1dfcecb6321fa3597096aaee7e5ad14 Mon Sep 17 00:00:00 2001 From: Brian Isganitis Date: Mon, 15 Jul 2024 16:12:41 -0400 Subject: [PATCH 10/13] Taskbar Pinning Preference Rule fixes. - Actually evaluate statement in tests. - Enable aconfig flag for taskbar pinning. - Allow DisplayController Taskbar mode to change in test harness. - Make DisplayController think it's in desktop mode when needed. Flag: TEST_ONLY Bug: 230027385 Test: TaskbarPinningPreferenceRuleTest Change-Id: I1c55f73cb5a4bb903cd2d628fa47fbd5e8b1bb86 --- .../rules/TaskbarPinningPreferenceRule.kt | 12 +++++++++ .../rules/TaskbarPinningPreferenceRuleTest.kt | 26 ++++++++++++++----- .../rules/TaskbarWindowSandboxContext.kt | 2 +- .../launcher3/util/DisplayController.java | 11 +++++++- 4 files changed, 43 insertions(+), 8 deletions(-) diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarPinningPreferenceRule.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarPinningPreferenceRule.kt index 60c8deca8d..2d26bbfe16 100644 --- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarPinningPreferenceRule.kt +++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarPinningPreferenceRule.kt @@ -16,11 +16,15 @@ package com.android.launcher3.taskbar.rules +import android.platform.test.flag.junit.FlagsParameterization +import android.platform.test.flag.junit.SetFlagsRule import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation import com.android.launcher3.ConstantItem +import com.android.launcher3.Flags.FLAG_ENABLE_TASKBAR_PINNING import com.android.launcher3.LauncherPrefs import com.android.launcher3.LauncherPrefs.Companion.TASKBAR_PINNING import com.android.launcher3.LauncherPrefs.Companion.TASKBAR_PINNING_IN_DESKTOP_MODE +import com.android.launcher3.util.DisplayController import kotlin.reflect.KProperty import org.junit.rules.TestRule import org.junit.runner.Description @@ -40,13 +44,20 @@ import org.junit.runners.model.Statement class TaskbarPinningPreferenceRule(context: TaskbarWindowSandboxContext) : TestRule { private val prefs = LauncherPrefs.get(context) + private val setFlagsRule = + SetFlagsRule(FlagsParameterization(mapOf(FLAG_ENABLE_TASKBAR_PINNING to true))) var isPinned by PinningPreference(TASKBAR_PINNING) var isPinnedInDesktopMode by PinningPreference(TASKBAR_PINNING_IN_DESKTOP_MODE) override fun apply(base: Statement, description: Description): Statement { + return setFlagsRule.apply(createStatement(base), description) + } + + private fun createStatement(base: Statement): Statement { return object : Statement() { override fun evaluate() { + DisplayController.enableTaskbarModePreferenceForTests(true) val wasPinned = isPinned val wasPinnedInDesktopMode = isPinnedInDesktopMode try { @@ -54,6 +65,7 @@ class TaskbarPinningPreferenceRule(context: TaskbarWindowSandboxContext) : TestR } finally { isPinned = wasPinned isPinnedInDesktopMode = wasPinnedInDesktopMode + DisplayController.enableTaskbarModePreferenceForTests(false) } } } diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarPinningPreferenceRuleTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarPinningPreferenceRuleTest.kt index 3f0a23886f..a709133b34 100644 --- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarPinningPreferenceRuleTest.kt +++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarPinningPreferenceRuleTest.kt @@ -19,6 +19,8 @@ package com.android.launcher3.taskbar.rules import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation import com.android.launcher3.util.DisplayController import com.android.launcher3.util.LauncherMultivalentJUnit +import com.android.launcher3.util.window.WindowManagerProxy +import com.google.android.apps.nexuslauncher.deviceemulator.TestWindowManagerProxy import com.google.common.truth.Truth.assertThat import org.junit.Test import org.junit.runner.Description @@ -51,6 +53,11 @@ class TaskbarPinningPreferenceRuleTest { @Test fun testEnableDesktopPinning_verifyDisplayController() { + context.applicationContext.putObject( + WindowManagerProxy.INSTANCE, + TestWindowManagerProxy(context).apply { isInDesktopMode = true }, + ) + onSetup { preferenceRule.isPinned = false preferenceRule.isPinnedInDesktopMode = true @@ -60,6 +67,11 @@ class TaskbarPinningPreferenceRuleTest { @Test fun testDisableDesktopPinning_verifyDisplayController() { + context.applicationContext.putObject( + WindowManagerProxy.INSTANCE, + TestWindowManagerProxy(context).apply { isInDesktopMode = true }, + ) + onSetup { preferenceRule.isPinned = false preferenceRule.isPinnedInDesktopMode = false @@ -83,12 +95,14 @@ class TaskbarPinningPreferenceRuleTest { /** Executes [runTest] after the [preferenceRule] setup phase completes. */ private fun onSetup(runTest: () -> Unit) { - preferenceRule.apply( - object : Statement() { - override fun evaluate() = runTest() - }, - DESCRIPTION, - ) + preferenceRule + .apply( + object : Statement() { + override fun evaluate() = runTest() + }, + DESCRIPTION, + ) + .evaluate() } private companion object { diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarWindowSandboxContext.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarWindowSandboxContext.kt index 321e7a93de..ee21df8f25 100644 --- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarWindowSandboxContext.kt +++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarWindowSandboxContext.kt @@ -50,7 +50,7 @@ private constructor(private val application: SandboxContext, base: Context) : Co ) } - override fun getApplicationContext() = application + override fun getApplicationContext(): SandboxContext = application companion object { /** Creates a [TaskbarWindowSandboxContext] to sandbox [base] for Taskbar tests. */ diff --git a/src/com/android/launcher3/util/DisplayController.java b/src/com/android/launcher3/util/DisplayController.java index 3dcc663c91..5b2f737895 100644 --- a/src/com/android/launcher3/util/DisplayController.java +++ b/src/com/android/launcher3/util/DisplayController.java @@ -76,6 +76,7 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable { private static final String TAG = "DisplayController"; private static final boolean DEBUG = false; + private static boolean sTaskbarModePreferenceStatusForTests = false; private static boolean sTransientTaskbarStatusForTests = true; // TODO(b/254119092) remove all logs with this tag @@ -202,6 +203,14 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable { sTransientTaskbarStatusForTests = enable; } + /** + * Enables respecting taskbar mode preference during test. + */ + @VisibleForTesting + public static void enableTaskbarModePreferenceForTests(boolean enable) { + sTaskbarModePreferenceStatusForTests = enable; + } + /** * Returns whether the taskbar is pinned in gesture navigation mode. */ @@ -461,7 +470,7 @@ public class DisplayController implements ComponentCallbacks, SafeCloseable { if (navigationMode != NavigationMode.NO_BUTTON) { return false; } - if (Utilities.isRunningInTestHarness()) { + if (Utilities.isRunningInTestHarness() && !sTaskbarModePreferenceStatusForTests) { // TODO(b/258604917): Once ENABLE_TASKBAR_PINNING is enabled, remove usage of // sTransientTaskbarStatusForTests and update test to directly // toggle shared preference to switch transient taskbar on/off. From d52e15b35a004f04b040bed65796e912158be665 Mon Sep 17 00:00:00 2001 From: Brian Isganitis Date: Mon, 15 Jul 2024 17:41:31 -0400 Subject: [PATCH 11/13] Add subrule for controlling an individual Taskbar preference. Test: TaskbarPreferenceRuleTest, TaskbarPinningPreferenceRuleTest Bug: 230027385 Flag: TEST_ONLY Change-Id: I382c7e17a5739823c25fc8d64c981405af56e3e4 --- .../rules/TaskbarPinningPreferenceRule.kt | 34 +++------- .../taskbar/rules/TaskbarPreferenceRule.kt | 54 +++++++++++++++ .../rules/TaskbarPreferenceRuleTest.kt | 65 +++++++++++++++++++ 3 files changed, 127 insertions(+), 26 deletions(-) create mode 100644 quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarPreferenceRule.kt create mode 100644 quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarPreferenceRuleTest.kt diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarPinningPreferenceRule.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarPinningPreferenceRule.kt index 2d26bbfe16..d417790d84 100644 --- a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarPinningPreferenceRule.kt +++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarPinningPreferenceRule.kt @@ -18,14 +18,11 @@ package com.android.launcher3.taskbar.rules import android.platform.test.flag.junit.FlagsParameterization import android.platform.test.flag.junit.SetFlagsRule -import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation -import com.android.launcher3.ConstantItem import com.android.launcher3.Flags.FLAG_ENABLE_TASKBAR_PINNING -import com.android.launcher3.LauncherPrefs import com.android.launcher3.LauncherPrefs.Companion.TASKBAR_PINNING import com.android.launcher3.LauncherPrefs.Companion.TASKBAR_PINNING_IN_DESKTOP_MODE import com.android.launcher3.util.DisplayController -import kotlin.reflect.KProperty +import org.junit.rules.RuleChain import org.junit.rules.TestRule import org.junit.runner.Description import org.junit.runners.model.Statement @@ -43,41 +40,26 @@ import org.junit.runners.model.Statement */ class TaskbarPinningPreferenceRule(context: TaskbarWindowSandboxContext) : TestRule { - private val prefs = LauncherPrefs.get(context) private val setFlagsRule = SetFlagsRule(FlagsParameterization(mapOf(FLAG_ENABLE_TASKBAR_PINNING to true))) + private val pinningRule = TaskbarPreferenceRule(context, TASKBAR_PINNING) + private val desktopPinningRule = TaskbarPreferenceRule(context, TASKBAR_PINNING_IN_DESKTOP_MODE) + private val ruleChain = + RuleChain.outerRule(setFlagsRule).around(pinningRule).around(desktopPinningRule) - var isPinned by PinningPreference(TASKBAR_PINNING) - var isPinnedInDesktopMode by PinningPreference(TASKBAR_PINNING_IN_DESKTOP_MODE) + var isPinned by pinningRule::value + var isPinnedInDesktopMode by desktopPinningRule::value override fun apply(base: Statement, description: Description): Statement { - return setFlagsRule.apply(createStatement(base), description) - } - - private fun createStatement(base: Statement): Statement { return object : Statement() { override fun evaluate() { DisplayController.enableTaskbarModePreferenceForTests(true) - val wasPinned = isPinned - val wasPinnedInDesktopMode = isPinnedInDesktopMode try { - base.evaluate() + ruleChain.apply(base, description).evaluate() } finally { - isPinned = wasPinned - isPinnedInDesktopMode = wasPinnedInDesktopMode DisplayController.enableTaskbarModePreferenceForTests(false) } } } } - - private inner class PinningPreference(private val constantItem: ConstantItem) { - operator fun getValue(thisRef: Any?, property: KProperty<*>): Boolean { - return prefs.get(constantItem) - } - - operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Boolean) { - getInstrumentation().runOnMainSync { prefs.put(constantItem, value) } - } - } } diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarPreferenceRule.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarPreferenceRule.kt new file mode 100644 index 0000000000..a76a77d170 --- /dev/null +++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarPreferenceRule.kt @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2024 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.rules + +import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation +import com.android.launcher3.ConstantItem +import com.android.launcher3.LauncherPrefs +import org.junit.rules.TestRule +import org.junit.runner.Description +import org.junit.runners.model.Statement + +/** + * Rule for modifying a Taskbar preference. + * + * The original preference value is restored on teardown. + */ +class TaskbarPreferenceRule( + context: TaskbarWindowSandboxContext, + private val constantItem: ConstantItem +) : TestRule { + + private val prefs = LauncherPrefs.get(context) + + var value: T + get() = prefs.get(constantItem) + set(value) = getInstrumentation().runOnMainSync { prefs.put(constantItem, value) } + + override fun apply(base: Statement, description: Description): Statement { + return object : Statement() { + override fun evaluate() { + val originalValue = value + try { + base.evaluate() + } finally { + value = originalValue + } + } + } + } +} diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarPreferenceRuleTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarPreferenceRuleTest.kt new file mode 100644 index 0000000000..22d2079821 --- /dev/null +++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/taskbar/rules/TaskbarPreferenceRuleTest.kt @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2024 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.rules + +import androidx.test.platform.app.InstrumentationRegistry.getInstrumentation +import com.android.launcher3.LauncherPrefs.Companion.TASKBAR_PINNING +import com.android.launcher3.util.LauncherMultivalentJUnit +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.Description +import org.junit.runner.RunWith +import org.junit.runners.model.Statement + +@RunWith(LauncherMultivalentJUnit::class) +class TaskbarPreferenceRuleTest { + + private val context = TaskbarWindowSandboxContext.create(getInstrumentation().targetContext) + private val preferenceRule = TaskbarPreferenceRule(context, TASKBAR_PINNING) + + @Test + fun testSetup_toggleBoolean_updatesPreferences() { + val originalValue = preferenceRule.value + onSetup { + preferenceRule.value = !preferenceRule.value + assertThat(preferenceRule.value).isNotEqualTo(originalValue) + } + } + + @Test + fun testTeardown_afterTogglingBoolean_preferenceReset() { + val originalValue = preferenceRule.value + onSetup { preferenceRule.value = !preferenceRule.value } + assertThat(preferenceRule.value).isEqualTo(originalValue) + } + + private fun onSetup(runTest: () -> Unit) { + preferenceRule + .apply( + object : Statement() { + override fun evaluate() = runTest() + }, + DESCRIPTION, + ) + .evaluate() + } + + private companion object { + private val DESCRIPTION = + Description.createSuiteDescription(TaskbarPreferenceRule::class.java) + } +} From 487a21726d022b90a2cbaea8841bfb2ea32347cb Mon Sep 17 00:00:00 2001 From: Stefan Andonian Date: Tue, 16 Jul 2024 16:45:15 +0000 Subject: [PATCH 12/13] [Test Week] Move ListenableHostView into it's own class for ease of unit testing. Bug: 353303621 Test: Verified with follow-up unit test CL. Flag: EXEMPT unit test Change-Id: Idfdaca0bee7f162f6e6ac60b1ddb53458603c227 --- .../widget/LauncherAppWidgetHost.java | 39 ------------ .../widget/LauncherWidgetHolder.java | 1 - .../launcher3/widget/ListenableHostView.java | 60 +++++++++++++++++++ 3 files changed, 60 insertions(+), 40 deletions(-) create mode 100644 src/com/android/launcher3/widget/ListenableHostView.java diff --git a/src/com/android/launcher3/widget/LauncherAppWidgetHost.java b/src/com/android/launcher3/widget/LauncherAppWidgetHost.java index 40c39840d6..71d850390d 100644 --- a/src/com/android/launcher3/widget/LauncherAppWidgetHost.java +++ b/src/com/android/launcher3/widget/LauncherAppWidgetHost.java @@ -21,22 +21,16 @@ import static com.android.launcher3.widget.LauncherWidgetHolder.APPWIDGET_HOST_I import android.appwidget.AppWidgetHost; import android.appwidget.AppWidgetProviderInfo; import android.content.Context; -import android.view.accessibility.AccessibilityNodeInfo; -import android.widget.RemoteViews; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.android.launcher3.LauncherAppState; import com.android.launcher3.util.Executors; -import com.android.launcher3.util.SafeCloseable; import com.android.launcher3.widget.LauncherWidgetHolder.ProviderChangedListener; import java.util.ArrayList; -import java.util.Collections; import java.util.List; -import java.util.Set; -import java.util.WeakHashMap; import java.util.function.IntConsumer; /** @@ -129,37 +123,4 @@ class LauncherAppWidgetHost extends AppWidgetHost { public void clearViews() { super.clearViews(); } - - public static class ListenableHostView extends LauncherAppWidgetHostView { - - private Set mUpdateListeners = Collections.EMPTY_SET; - - ListenableHostView(Context context) { - super(context); - } - - @Override - public void updateAppWidget(RemoteViews remoteViews) { - super.updateAppWidget(remoteViews); - mUpdateListeners.forEach(Runnable::run); - } - - @Override - public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { - super.onInitializeAccessibilityNodeInfo(info); - info.setClassName(LauncherAppWidgetHostView.class.getName()); - } - - /** - * Adds a callback to be run everytime the provided app widget updates. - * @return a closable to remove this callback - */ - public SafeCloseable addUpdateListener(Runnable callback) { - if (mUpdateListeners == Collections.EMPTY_SET) { - mUpdateListeners = Collections.newSetFromMap(new WeakHashMap<>()); - } - mUpdateListeners.add(callback); - return () -> mUpdateListeners.remove(callback); - } - } } diff --git a/src/com/android/launcher3/widget/LauncherWidgetHolder.java b/src/com/android/launcher3/widget/LauncherWidgetHolder.java index 1fb8c8306f..93451b3978 100644 --- a/src/com/android/launcher3/widget/LauncherWidgetHolder.java +++ b/src/com/android/launcher3/widget/LauncherWidgetHolder.java @@ -49,7 +49,6 @@ import com.android.launcher3.testing.shared.TestProtocol; import com.android.launcher3.util.LooperExecutor; import com.android.launcher3.util.ResourceBasedOverride; import com.android.launcher3.util.SafeCloseable; -import com.android.launcher3.widget.LauncherAppWidgetHost.ListenableHostView; import com.android.launcher3.widget.custom.CustomWidgetManager; import java.util.ArrayList; diff --git a/src/com/android/launcher3/widget/ListenableHostView.java b/src/com/android/launcher3/widget/ListenableHostView.java new file mode 100644 index 0000000000..b809db016b --- /dev/null +++ b/src/com/android/launcher3/widget/ListenableHostView.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2024 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.widget; + +import android.content.Context; +import android.view.accessibility.AccessibilityNodeInfo; +import android.widget.RemoteViews; + +import com.android.launcher3.util.SafeCloseable; + +import java.util.Collections; +import java.util.Set; +import java.util.WeakHashMap; + +public class ListenableHostView extends LauncherAppWidgetHostView { + + private Set mUpdateListeners = Collections.EMPTY_SET; + + ListenableHostView(Context context) { + super(context); + } + + @Override + public void updateAppWidget(RemoteViews remoteViews) { + super.updateAppWidget(remoteViews); + mUpdateListeners.forEach(Runnable::run); + } + + @Override + public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) { + super.onInitializeAccessibilityNodeInfo(info); + info.setClassName(LauncherAppWidgetHostView.class.getName()); + } + + /** + * Adds a callback to be run everytime the provided app widget updates. + * @return a closable to remove this callback + */ + public SafeCloseable addUpdateListener(Runnable callback) { + if (mUpdateListeners == Collections.EMPTY_SET) { + mUpdateListeners = Collections.newSetFromMap(new WeakHashMap<>()); + } + mUpdateListeners.add(callback); + return () -> mUpdateListeners.remove(callback); + } +} From 84151055b7eec862795d9dda7f76873da1464679 Mon Sep 17 00:00:00 2001 From: Jagrut Desai Date: Wed, 26 Jun 2024 13:43:07 -0700 Subject: [PATCH 13/13] Add support for different container and space calculation logic. This CL includes - Taskbar container enum class and interface for container to calculate their space needed. - Inits and getter of util classes in taksbar activity context. - Taskbar Icons specs not have default margins and minimum touch target specs - Updates to the test - Logic for calculating the space needed for taskbar container Test: Presubmit Bug: 341146605 Flag: com.android.launcher3.enable_taskbar_customization Change-Id: I89e9d521476902a9027f6fd0cebdc2690b2e5f31 --- .../taskbar/TaskbarActivityContext.java | 21 ++++++++ .../taskbar/customization/TaskbarContainer.kt | 16 +++--- .../customization/TaskbarContainers.kt | 27 ++++++++++ .../customization/TaskbarFeatureEvaluator.kt | 28 +++++++--- .../taskbar/customization/TaskbarIconSpecs.kt | 7 +++ .../customization/TaskbarSpecsEvaluator.kt | 53 +++++++++++++++++-- .../TaskbarSpecsEvaluatorTest.kt | 5 +- 7 files changed, 137 insertions(+), 20 deletions(-) create mode 100644 quickstep/src/com/android/launcher3/taskbar/customization/TaskbarContainers.kt diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java index 21a826870e..3481e520e1 100644 --- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java +++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java @@ -109,6 +109,8 @@ import com.android.launcher3.taskbar.bubbles.BubbleDragController; import com.android.launcher3.taskbar.bubbles.BubblePinController; import com.android.launcher3.taskbar.bubbles.BubbleStashController; import com.android.launcher3.taskbar.bubbles.BubbleStashedHandleViewController; +import com.android.launcher3.taskbar.customization.TaskbarFeatureEvaluator; +import com.android.launcher3.taskbar.customization.TaskbarSpecsEvaluator; import com.android.launcher3.taskbar.navbutton.NearestTouchFrame; import com.android.launcher3.taskbar.overlay.TaskbarOverlayController; import com.android.launcher3.testing.TestLogging; @@ -202,6 +204,10 @@ public class TaskbarActivityContext extends BaseTaskbarContext { private final LauncherPrefs mLauncherPrefs; + private final TaskbarFeatureEvaluator mTaskbarFeatureEvaluator; + + private final TaskbarSpecsEvaluator mTaskbarSpecsEvaluator; + public TaskbarActivityContext(Context windowContext, @Nullable Context navigationBarPanelContext, DeviceProfile launcherDp, TaskbarNavButtonController buttonController, ScopedUnfoldTransitionProgressProvider @@ -212,6 +218,13 @@ public class TaskbarActivityContext extends BaseTaskbarContext { applyDeviceProfile(launcherDp); final Resources resources = getResources(); + mTaskbarFeatureEvaluator = TaskbarFeatureEvaluator.getInstance(this); + mTaskbarSpecsEvaluator = new TaskbarSpecsEvaluator( + this, + mTaskbarFeatureEvaluator, + mDeviceProfile.inv.numRows, + mDeviceProfile.inv.numColumns); + mImeDrawsImeNavBar = getBoolByName(IME_DRAWS_IME_NAV_BAR_RES_NAME, resources, false); mIsSafeModeEnabled = TraceHelper.allowIpcs("isSafeMode", () -> getPackageManager().isSafeMode()); @@ -1608,6 +1621,14 @@ public class TaskbarActivityContext extends BaseTaskbarContext { return mControllers.taskbarStashController.isInStashedLauncherState(); } + public TaskbarFeatureEvaluator getTaskbarFeatureEvaluator() { + return mTaskbarFeatureEvaluator; + } + + public TaskbarSpecsEvaluator getTaskbarSpecsEvaluator() { + return mTaskbarSpecsEvaluator; + } + protected void dumpLogs(String prefix, PrintWriter pw) { pw.println(prefix + "TaskbarActivityContext:"); diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarContainer.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarContainer.kt index 3c4b63a1d7..35ae43cd29 100644 --- a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarContainer.kt +++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarContainer.kt @@ -13,15 +13,13 @@ * See the License for the specific language governing permissions and * limitations under the License. */ - package com.android.launcher3.taskbar.customization -/** Enums for all feature container that taskbar supports. */ -enum class TaskbarContainer { - ALL_APPS, - DIVIDER, - APP_ICONS, - RECENTS, - NAV_BUTTONS, - BUBBLES, +import androidx.annotation.Dimension + +/** + * Interface to be implemented by all taskbar container to expose [spaceNeeded] for each container. + */ +interface TaskbarContainer { + @get:Dimension(unit = Dimension.DP) val spaceNeeded: Int } diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarContainers.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarContainers.kt new file mode 100644 index 0000000000..d4548f5efe --- /dev/null +++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarContainers.kt @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 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.customization + +/** Enums for all feature container that taskbar supports. */ +enum class TaskbarContainers { + ALL_APPS, + DIVIDER, + APP_ICONS, + RECENTS, + NAV_BUTTONS, + BUBBLES, +} diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarFeatureEvaluator.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarFeatureEvaluator.kt index ac7dd0670d..c83ac5097e 100644 --- a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarFeatureEvaluator.kt +++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarFeatureEvaluator.kt @@ -16,27 +16,43 @@ package com.android.launcher3.taskbar.customization +import com.android.launcher3.Flags.enableRecentsInTaskbar import com.android.launcher3.config.FeatureFlags.enableTaskbarPinning import com.android.launcher3.taskbar.TaskbarActivityContext -import com.android.launcher3.taskbar.TaskbarControllers import com.android.launcher3.util.DisplayController /** Evaluates all the features taskbar can have. */ -class TaskbarFeatureEvaluator( +class TaskbarFeatureEvaluator +private constructor( private val taskbarActivityContext: TaskbarActivityContext, - private val taskbarControllers: TaskbarControllers, ) { + companion object { + @Volatile private lateinit var taskbarFeatureEvaluator: TaskbarFeatureEvaluator + + @JvmStatic + fun getInstance( + taskbarActivityContext: TaskbarActivityContext, + ): TaskbarFeatureEvaluator { + synchronized(this) { + if (!::taskbarFeatureEvaluator.isInitialized) { + taskbarFeatureEvaluator = TaskbarFeatureEvaluator(taskbarActivityContext) + } + return taskbarFeatureEvaluator + } + } + } + val hasAllApps = true val hasAppIcons = true val hasBubbles = false val hasNavButtons = taskbarActivityContext.isThreeButtonNav - val hasRecents: Boolean - get() = taskbarControllers.taskbarRecentAppsController.shownTasks.isNotEmpty() + val isRecentsEnabled: Boolean + get() = enableRecentsInTaskbar() val hasDivider: Boolean - get() = enableTaskbarPinning() || hasRecents + get() = enableTaskbarPinning() || isRecentsEnabled val isTransient: Boolean get() = DisplayController.isTransientTaskbar(taskbarActivityContext) diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarIconSpecs.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarIconSpecs.kt index 67bbcce7f6..887eb01f3d 100644 --- a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarIconSpecs.kt +++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarIconSpecs.kt @@ -29,6 +29,13 @@ object TaskbarIconSpecs { val defaultPersistentIconSize = iconSize40dp val defaultTransientIconSize = iconSize44dp + val minimumIconSize = iconSize40dp + + val defaultPersistentIconMargin = TaskbarIconMarginSize(6) + val defaultTransientIconMargin = TaskbarIconMarginSize(12) + + val minimumTaskbarIconTouchSize = TaskbarIconSize(48) + val transientTaskbarIconSizeByGridSize = mapOf( TransientTaskbarIconSizeKey(6, 5, false) to iconSize52dp, diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarSpecsEvaluator.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarSpecsEvaluator.kt index 0b7be40826..761b47e179 100644 --- a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarSpecsEvaluator.kt +++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarSpecsEvaluator.kt @@ -16,8 +16,33 @@ package com.android.launcher3.taskbar.customization +import com.android.launcher3.taskbar.TaskbarActivityContext + /** Evaluates the taskbar specs based on the taskbar grid size and the taskbar icon size. */ -class TaskbarSpecsEvaluator(private val taskbarFeatureEvaluator: TaskbarFeatureEvaluator) { +class TaskbarSpecsEvaluator( + private val taskbarActivityContext: TaskbarActivityContext, + private val taskbarFeatureEvaluator: TaskbarFeatureEvaluator, + numRows: Int = taskbarActivityContext.deviceProfile.inv.numRows, + numColumns: Int = taskbarActivityContext.deviceProfile.inv.numColumns, +) { + var taskbarIconSize: TaskbarIconSize = getIconSizeByGrid(numRows, numColumns) + + // TODO(b/341146605) : initialize it to taskbar container in later cl. + private var taskbarContainer: List = emptyList() + + val taskbarIconPadding: Int = + if (TaskbarIconSpecs.minimumTaskbarIconTouchSize.size > taskbarIconSize.size) { + (TaskbarIconSpecs.minimumTaskbarIconTouchSize.size - taskbarIconSize.size) / 2 + } else { + 0 + } + + val taskbarIconMargin: TaskbarIconMarginSize = + if (taskbarFeatureEvaluator.isTransient) { + TaskbarIconSpecs.defaultTransientIconMargin + } else { + TaskbarIconSpecs.defaultPersistentIconMargin + } fun getIconSizeByGrid(row: Int, column: Int): TaskbarIconSize { return if (taskbarFeatureEvaluator.isTransient) { @@ -36,8 +61,11 @@ class TaskbarSpecsEvaluator(private val taskbarFeatureEvaluator: TaskbarFeatureE val currentIconSizeIndex = TaskbarIconSpecs.transientTaskbarIconSizes.indexOf(iconSize) // return the current icon size if supplied icon size is unknown or we have reached the // min icon size. - return if (currentIconSizeIndex == -1 || currentIconSizeIndex == 0) iconSize - else TaskbarIconSpecs.transientTaskbarIconSizes[currentIconSizeIndex - 1] + return if (currentIconSizeIndex == -1 || currentIconSizeIndex == 0) { + iconSize + } else { + TaskbarIconSpecs.transientTaskbarIconSizes[currentIconSizeIndex - 1] + } } fun getIconSizeStepUp(iconSize: TaskbarIconSize): TaskbarIconSize { @@ -52,11 +80,28 @@ class TaskbarSpecsEvaluator(private val taskbarFeatureEvaluator: TaskbarFeatureE ) { iconSize } else { - TaskbarIconSpecs.transientTaskbarIconSizes.get(currentIconSizeIndex + 1) + TaskbarIconSpecs.transientTaskbarIconSizes[currentIconSizeIndex + 1] } } + + // TODO(jagrutdesai) : Call this in init once the containers are ready. + private fun calculateTaskbarIconSize() { + while ( + taskbarIconSize != TaskbarIconSpecs.minimumIconSize && + taskbarActivityContext.transientTaskbarBounds.width() < + calculateSpaceNeeded(taskbarContainer) + ) { + taskbarIconSize = getIconSizeStepDown(taskbarIconSize) + } + } + + private fun calculateSpaceNeeded(containers: List): Int { + return containers.sumOf { it.spaceNeeded } + } } data class TaskbarIconSize(val size: Int) data class TransientTaskbarIconSizeKey(val row: Int, val column: Int, val isLandscape: Boolean) + +data class TaskbarIconMarginSize(val size: Int) diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/taskbar/customization/TaskbarSpecsEvaluatorTest.kt b/quickstep/tests/multivalentTests/src/com/android/quickstep/taskbar/customization/TaskbarSpecsEvaluatorTest.kt index 0bf68ebe93..d66197a6fb 100644 --- a/quickstep/tests/multivalentTests/src/com/android/quickstep/taskbar/customization/TaskbarSpecsEvaluatorTest.kt +++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/taskbar/customization/TaskbarSpecsEvaluatorTest.kt @@ -16,6 +16,7 @@ package com.android.quickstep.taskbar.customization +import com.android.launcher3.taskbar.TaskbarActivityContext import com.android.launcher3.taskbar.customization.TaskbarFeatureEvaluator import com.android.launcher3.taskbar.customization.TaskbarIconSpecs import com.android.launcher3.taskbar.customization.TaskbarSpecsEvaluator @@ -32,7 +33,9 @@ import org.mockito.kotlin.whenever class TaskbarSpecsEvaluatorTest { private val taskbarFeatureEvaluator = mock() - private val taskbarSpecsEvaluator = spy(TaskbarSpecsEvaluator(taskbarFeatureEvaluator)) + private val taskbarActivityContext = mock() + private var taskbarSpecsEvaluator = + spy(TaskbarSpecsEvaluator(taskbarActivityContext, taskbarFeatureEvaluator, 0, 0)) @Test fun testGetIconSizeByGrid_whenTaskbarIsTransient_withValidRowAndColumnInLandscape() {