Merge "Honor the default wallet role in QuickAccessWallet." into main
diff --git a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
index 20b0932..1867a17 100644
--- a/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
+++ b/core/java/android/hardware/camera2/CameraExtensionCharacteristics.java
@@ -847,16 +847,18 @@
* of the camera resolutions advertised by
* {@link StreamConfigurationMap#getOutputSizes}.</p>
*
- * <p>Device-specific extensions currently support at most two
+ * <p>Device-specific extensions currently support at most three
* multi-frame capture surface formats. ImageFormat.JPEG will be supported by all
- * extensions and ImageFormat.YUV_420_888 may or may not be supported.</p>
+ * extensions while ImageFormat.YUV_420_888 and ImageFormat.JPEG_R may or may not be
+ * supported.</p>
*
* @param extension the extension type
* @param format device-specific extension output format
* @return non-modifiable list of available sizes or an empty list if the format is not
* supported.
- * @throws IllegalArgumentException in case of format different from ImageFormat.JPEG /
- * ImageFormat.YUV_420_888; or unsupported extension.
+ * @throws IllegalArgumentException in case of format different from ImageFormat.JPEG,
+ * ImageFormat.YUV_420_888, ImageFormat.JPEG_R; or
+ * unsupported extension.
*/
public @NonNull
List<Size> getExtensionSupportedSizes(@Extension int extension, int format) {
@@ -940,14 +942,16 @@
* @param format device-specific extension output format
* @return the range of estimated minimal and maximal capture latency in milliseconds
* or null if no capture latency info can be provided
- * @throws IllegalArgumentException in case of format different from {@link ImageFormat#JPEG} /
- * {@link ImageFormat#YUV_420_888}; or unsupported extension.
+ * @throws IllegalArgumentException in case of format different from {@link ImageFormat#JPEG},
+ * {@link ImageFormat#YUV_420_888}, {@link ImageFormat#JPEG_R};
+ * or unsupported extension.
*/
public @Nullable Range<Long> getEstimatedCaptureLatencyRangeMillis(@Extension int extension,
@NonNull Size captureOutputSize, @ImageFormat.Format int format) {
switch (format) {
case ImageFormat.YUV_420_888:
case ImageFormat.JPEG:
+ case ImageFormat.JPEG_R:
//No op
break;
default:
@@ -994,6 +998,10 @@
// specific and cannot be estimated accurately enough.
return null;
}
+ if (format == ImageFormat.JPEG_R) {
+ // JpegR/UltraHDR is not supported for basic extensions
+ return null;
+ }
LatencyRange latencyRange = extenders.second.getEstimatedCaptureLatencyRange(sz);
if (latencyRange != null) {
diff --git a/core/java/android/text/flags/flags.aconfig b/core/java/android/text/flags/flags.aconfig
index a1885ae..bf1a596 100644
--- a/core/java/android/text/flags/flags.aconfig
+++ b/core/java/android/text/flags/flags.aconfig
@@ -82,3 +82,10 @@
description: "A feature flag that implement inter character justification."
bug: "283193133"
}
+
+flag {
+ name: "escape_clears_focus"
+ namespace: "text"
+ description: "Feature flag for clearing focus when the escape key is pressed."
+ bug: "312921137"
+}
diff --git a/core/java/android/webkit/WebViewClient.java b/core/java/android/webkit/WebViewClient.java
index 2dfeae3..80aad60 100644
--- a/core/java/android/webkit/WebViewClient.java
+++ b/core/java/android/webkit/WebViewClient.java
@@ -65,6 +65,14 @@
* {@code true} causes the current WebView to abort loading the URL, while returning
* {@code false} causes the WebView to continue loading the URL as usual.
*
+ * <p>This callback is not called for all page navigations. In particular, this is not called
+ * for navigations which the app initiated with {@code loadUrl()}: this callback would not serve
+ * a purpose in this case, because the app already knows about the navigation. This callback
+ * lets the app know about navigations initiated by the web page (such as navigations initiated
+ * by JavaScript code), by the user (such as when the user taps on a link), or by an HTTP
+ * redirect (ex. if {@code loadUrl("foo.com")} redirects to {@code "bar.com"} because of HTTP
+ * 301).
+ *
* <p class="note"><b>Note:</b> Do not call {@link WebView#loadUrl(String)} with the request's
* URL and then return {@code true}. This unnecessarily cancels the current load and starts a
* new load with the same URL. The correct way to continue loading a given URL is to simply
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index 6da6a64..e812f85 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -9589,6 +9589,23 @@
}
break;
+ case KeyEvent.KEYCODE_ESCAPE:
+ if (com.android.text.flags.Flags.escapeClearsFocus() && event.hasNoModifiers()) {
+ if (mEditor != null && mEditor.getTextActionMode() != null) {
+ stopTextActionMode();
+ return KEY_EVENT_HANDLED;
+ }
+ if (hasFocus()) {
+ clearFocus();
+ InputMethodManager imm = getInputMethodManager();
+ if (imm != null) {
+ imm.hideSoftInputFromView(this, 0);
+ }
+ return KEY_EVENT_HANDLED;
+ }
+ }
+ break;
+
case KeyEvent.KEYCODE_CUT:
if (event.hasNoModifiers() && canCut()) {
if (onTextContextMenuItem(ID_CUT)) {
diff --git a/core/java/android/window/flags/responsible_apis.aconfig b/core/java/android/window/flags/responsible_apis.aconfig
index f67eefa..51890ec 100644
--- a/core/java/android/window/flags/responsible_apis.aconfig
+++ b/core/java/android/window/flags/responsible_apis.aconfig
@@ -36,13 +36,6 @@
}
flag {
- name: "bal_return_correct_code_if_caller_is_persistent_system_process"
- namespace: "responsible_apis"
- description: "Split visibility check and return a better status code in case of system process."
- bug: "171459802"
-}
-
-flag {
name: "bal_improve_real_caller_visibility_check"
namespace: "responsible_apis"
description: "Prevent a task to restart based on a visible window during task switch."
diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
index 556a315..409f15b 100644
--- a/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
+++ b/packages/SystemUI/compose/features/src/com/android/systemui/communal/ui/compose/CommunalHub.kt
@@ -624,17 +624,10 @@
modifier =
modifier.align(Alignment.Center).allowGestures(allowed = !viewModel.isEditMode),
factory = { context ->
- // The AppWidgetHostView will inherit the interaction handler from the
- // AppWidgetHost. So set the interaction handler here before creating the view, and
- // then clear it after the view is created. This is a workaround due to the fact
- // that the interaction handler cannot be specified when creating the view,
- // and there are race conditions if it is set after the view is created.
- model.appWidgetHost.setInteractionHandler(viewModel.getInteractionHandler())
val view =
model.appWidgetHost
.createViewForCommunal(context, model.appWidgetId, model.providerInfo)
.apply { updateAppWidgetSize(Bundle.EMPTY, listOf(size)) }
- model.appWidgetHost.setInteractionHandler(null)
// Remove the extra padding applied to AppWidgetHostView to allow widgets to
// occupy the entire box. The added padding is now adjusted to leave only sufficient
// space for displaying the outline around the box when the widget is selected.
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
index 2bfa7d9..cea49e1 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/AnimatableClockView.kt
@@ -27,6 +27,7 @@
import android.text.format.DateFormat
import android.util.AttributeSet
import android.util.MathUtils.constrainedMap
+import android.view.View
import android.widget.TextView
import com.android.app.animation.Interpolators
import com.android.internal.annotations.VisibleForTesting
@@ -51,7 +52,7 @@
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
- defStyleRes: Int = 0
+ defStyleRes: Int = 0,
) : TextView(context, attrs, defStyleAttr, defStyleRes) {
// To protect us from issues from this being null while the TextView constructor is running, we
// implement the get method and ensure a value is returned before initialization is complete.
@@ -61,6 +62,9 @@
get() = logger.buffer
set(value) { logger = Logger(value, TAG) }
+ var hasCustomPositionUpdatedAnimation: Boolean = false
+ var migratedClocks: Boolean = false
+
private val time = Calendar.getInstance()
private val dozingWeightInternal: Int
@@ -193,9 +197,18 @@
} else {
animator.updateLayout(layout)
}
+ if (migratedClocks && hasCustomPositionUpdatedAnimation) {
+ // Expand width to avoid clock being clipped during stepping animation
+ setMeasuredDimension(measuredWidth +
+ MeasureSpec.getSize(widthMeasureSpec) / 2, measuredHeight)
+ }
}
override fun onDraw(canvas: Canvas) {
+ if (migratedClocks && hasCustomPositionUpdatedAnimation) {
+ canvas.save()
+ canvas.translate((parent as View).measuredWidth / 4F, 0F)
+ }
logger.d({ "onDraw($str1)"}) { str1 = text.toString() }
// Use textAnimator to render text if animation is enabled.
// Otherwise default to using standard draw functions.
@@ -205,6 +218,9 @@
} else {
super.onDraw(canvas)
}
+ if (migratedClocks && hasCustomPositionUpdatedAnimation) {
+ canvas.restore()
+ }
}
override fun invalidate() {
diff --git a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
index 99d3216..001e3a5 100644
--- a/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
+++ b/packages/SystemUI/customization/src/com/android/systemui/shared/clocks/DefaultClockController.kt
@@ -193,6 +193,8 @@
ClockFaceConfig(hasCustomPositionUpdatedAnimation = hasStepClockAnimation)
init {
+ view.migratedClocks = migratedClocks
+ view.hasCustomPositionUpdatedAnimation = hasStepClockAnimation
animations = LargeClockAnimations(view, 0f, 0f)
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/widgets/CommunalAppWidgetHostTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/widgets/CommunalAppWidgetHostTest.kt
index e904236..3aa99c4 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/widgets/CommunalAppWidgetHostTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/ui/widgets/CommunalAppWidgetHostTest.kt
@@ -16,44 +16,52 @@
package com.android.systemui.communal.ui.widgets
+import android.testing.TestableLooper
+import android.testing.TestableLooper.RunWithLooper
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.communal.widgets.CommunalAppWidgetHost
import com.android.systemui.communal.widgets.CommunalAppWidgetHostView
-import com.android.systemui.kosmos.testScope
-import com.android.systemui.testKosmos
+import com.android.systemui.util.mockito.mock
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
@SmallTest
+@RunWithLooper(setAsMainLooper = true)
@RunWith(AndroidJUnit4::class)
class CommunalAppWidgetHostTest : SysuiTestCase() {
- private val kosmos = testKosmos()
- private val testScope = kosmos.testScope
+ private lateinit var testableLooper: TestableLooper
private lateinit var underTest: CommunalAppWidgetHost
@Before
fun setUp() {
- underTest = CommunalAppWidgetHost(context = context, hostId = 116)
+ testableLooper = TestableLooper.get(this)
+ underTest =
+ CommunalAppWidgetHost(
+ context = context,
+ hostId = 116,
+ interactionHandler = mock(),
+ looper = testableLooper.looper
+ )
}
@Test
- fun createViewForCommunal_returnCommunalAppWidgetView() =
- testScope.runTest {
- val appWidgetId = 789
- val view =
- underTest.createViewForCommunal(
- context = context,
- appWidgetId = appWidgetId,
- appWidget = null
- )
- assertThat(view).isInstanceOf(CommunalAppWidgetHostView::class.java)
- assertThat(view).isNotNull()
- assertThat(view.appWidgetId).isEqualTo(appWidgetId)
- }
+ fun createViewForCommunal_returnCommunalAppWidgetView() {
+ val appWidgetId = 789
+ val view =
+ underTest.createViewForCommunal(
+ context = context,
+ appWidgetId = appWidgetId,
+ appWidget = null
+ )
+ testableLooper.processAllMessages()
+
+ assertThat(view).isInstanceOf(CommunalAppWidgetHostView::class.java)
+ assertThat(view).isNotNull()
+ assertThat(view.appWidgetId).isEqualTo(appWidgetId)
+ }
}
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
index 125ede4..867a48d 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalEditModeViewModelTest.kt
@@ -41,7 +41,6 @@
import com.android.systemui.util.mockito.mock
import com.android.systemui.util.mockito.whenever
import com.google.common.truth.Truth.assertThat
-import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
import org.junit.Before
import org.junit.Test
@@ -51,7 +50,6 @@
import org.mockito.Mockito.verify
import org.mockito.MockitoAnnotations
-@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(AndroidJUnit4::class)
class CommunalEditModeViewModelTest : SysuiTestCase() {
@@ -134,19 +132,6 @@
}
@Test
- fun interactionHandlerIgnoresClicks() {
- val interactionHandler = underTest.getInteractionHandler()
- assertThat(
- interactionHandler.onInteraction(
- /* view = */ mock(),
- /* pendingIntent = */ mock(),
- /* response = */ mock()
- )
- )
- .isEqualTo(false)
- }
-
- @Test
fun reorderWidget_uiEventLogging_start() {
underTest.onReorderWidgetStart()
verify(uiEventLogger).log(CommunalUiEvent.COMMUNAL_HUB_REORDER_WIDGET_START)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
index f9cfc37..9ac21dd 100644
--- a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/view/viewmodel/CommunalViewModelTest.kt
@@ -32,7 +32,6 @@
import com.android.systemui.communal.shared.model.CommunalWidgetContentModel
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel
import com.android.systemui.communal.ui.viewmodel.CommunalViewModel.Companion.POPUP_AUTO_HIDE_TIMEOUT_MS
-import com.android.systemui.communal.widgets.WidgetInteractionHandler
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository
import com.android.systemui.media.controls.ui.MediaHierarchyManager
@@ -90,7 +89,6 @@
CommunalViewModel(
testScope,
withDeps.communalInteractor,
- WidgetInteractionHandler(mock()),
withDeps.tutorialInteractor,
mediaHost,
)
diff --git a/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt
new file mode 100644
index 0000000..69ff5ab
--- /dev/null
+++ b/packages/SystemUI/multivalentTests/src/com/android/systemui/communal/widgets/WidgetInteractionHandlerTest.kt
@@ -0,0 +1,87 @@
+/*
+ * 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.systemui.communal.widgets
+
+import android.app.PendingIntent
+import android.content.Intent
+import android.view.View
+import android.widget.FrameLayout
+import android.widget.RemoteViews.RemoteResponse
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.plugins.ActivityStarter
+import com.android.systemui.util.mockito.eq
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mock
+import org.mockito.Mockito.isNull
+import org.mockito.Mockito.notNull
+import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
+
+@SmallTest
+@RunWith(AndroidJUnit4::class)
+class WidgetInteractionHandlerTest : SysuiTestCase() {
+ @Mock private lateinit var activityStarter: ActivityStarter
+
+ private lateinit var underTest: WidgetInteractionHandler
+
+ private val testIntent =
+ PendingIntent.getActivity(
+ context,
+ /* requestCode = */ 0,
+ Intent("action"),
+ PendingIntent.FLAG_IMMUTABLE
+ )
+ private val testResponse = RemoteResponse.fromPendingIntent(testIntent)
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.initMocks(this)
+ underTest = WidgetInteractionHandler(activityStarter)
+ }
+
+ @Test
+ fun launchAnimatorIsUsedForWidgetView() {
+ val parent = FrameLayout(context)
+ val view = CommunalAppWidgetHostView(context)
+ parent.addView(view)
+
+ underTest.onInteraction(view, testIntent, testResponse)
+
+ verify(activityStarter)
+ .startPendingIntentMaybeDismissingKeyguard(
+ eq(testIntent),
+ isNull(),
+ notNull(),
+ )
+ }
+
+ @Test
+ fun launchAnimatorIsNotUsedForRegularView() {
+ val parent = FrameLayout(context)
+ val view = View(context)
+ parent.addView(view)
+
+ underTest.onInteraction(view, testIntent, testResponse)
+
+ verify(activityStarter)
+ .startPendingIntentMaybeDismissingKeyguard(eq(testIntent), isNull(), isNull())
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/common/ui/view/ViewExt.kt b/packages/SystemUI/src/com/android/systemui/common/ui/view/ViewExt.kt
index 8d04e3d..ce798ba 100644
--- a/packages/SystemUI/src/com/android/systemui/common/ui/view/ViewExt.kt
+++ b/packages/SystemUI/src/com/android/systemui/common/ui/view/ViewExt.kt
@@ -26,3 +26,20 @@
importantForAccessibility =
if (value) View.IMPORTANT_FOR_ACCESSIBILITY_YES else View.IMPORTANT_FOR_ACCESSIBILITY_NO
}
+
+/**
+ * Can be used to find the nearest parent of a view of a particular type.
+ *
+ * Usage:
+ * ```
+ * val textView = view.getNearestParent<TextView>()
+ * ```
+ */
+inline fun <reified T : View> View.getNearestParent(): T? {
+ var view: Any? = this
+ while (view is View) {
+ if (view is T) return view
+ view = view.parent
+ }
+ return null
+}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryModule.kt b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryModule.kt
index 52f42c1..ab0a2d0d 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/data/repository/CommunalWidgetRepositoryModule.kt
@@ -20,8 +20,10 @@
import android.appwidget.AppWidgetManager
import android.content.Context
import android.content.res.Resources
+import android.os.Looper
import com.android.systemui.communal.shared.CommunalWidgetHost
import com.android.systemui.communal.widgets.CommunalAppWidgetHost
+import com.android.systemui.communal.widgets.WidgetInteractionHandler
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.dagger.qualifiers.Main
@@ -48,8 +50,12 @@
@SysUISingleton
@Provides
- fun provideCommunalAppWidgetHost(@Application context: Context): CommunalAppWidgetHost {
- return CommunalAppWidgetHost(context, APP_WIDGET_HOST_ID)
+ fun provideCommunalAppWidgetHost(
+ @Application context: Context,
+ interactionHandler: WidgetInteractionHandler,
+ @Main looper: Looper,
+ ): CommunalAppWidgetHost {
+ return CommunalAppWidgetHost(context, APP_WIDGET_HOST_ID, interactionHandler, looper)
}
@SysUISingleton
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
index f1b16c5..acc7981 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/BaseCommunalViewModel.kt
@@ -17,7 +17,6 @@
package com.android.systemui.communal.ui.viewmodel
import android.content.ComponentName
-import android.widget.RemoteViews
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.model.CommunalContentModel
import com.android.systemui.communal.shared.model.CommunalSceneKey
@@ -103,9 +102,6 @@
/** Called as the UI requests to dismiss the CTA tile. */
open fun onDismissCtaTile() {}
- /** Gets the interaction handler used to handle taps on a remote view */
- abstract fun getInteractionHandler(): RemoteViews.InteractionHandler
-
/** Called as the user starts dragging a widget to reorder. */
open fun onReorderWidgetStart() {}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
index c69fa6f..237a0c0 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalEditModeViewModel.kt
@@ -16,7 +16,6 @@
package com.android.systemui.communal.ui.viewmodel
-import android.widget.RemoteViews
import com.android.internal.logging.UiEventLogger
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.model.CommunalContentModel
@@ -60,11 +59,6 @@
override fun onReorderWidgets(widgetIdToPriorityMap: Map<Int, Int>) =
communalInteractor.updateWidgetOrder(widgetIdToPriorityMap)
- override fun getInteractionHandler(): RemoteViews.InteractionHandler {
- // Ignore all interactions in edit mode.
- return RemoteViews.InteractionHandler { _, _, _ -> false }
- }
-
override fun onReorderWidgetStart() {
// Clear selection status
setSelectedIndex(null)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
index d619362..2bb9d0e 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/ui/viewmodel/CommunalViewModel.kt
@@ -16,11 +16,9 @@
package com.android.systemui.communal.ui.viewmodel
-import android.widget.RemoteViews
import com.android.systemui.communal.domain.interactor.CommunalInteractor
import com.android.systemui.communal.domain.interactor.CommunalTutorialInteractor
import com.android.systemui.communal.domain.model.CommunalContentModel
-import com.android.systemui.communal.widgets.WidgetInteractionHandler
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.media.controls.ui.MediaHierarchyManager
@@ -48,7 +46,6 @@
constructor(
@Application private val scope: CoroutineScope,
private val communalInteractor: CommunalInteractor,
- private val interactionHandler: WidgetInteractionHandler,
tutorialInteractor: CommunalTutorialInteractor,
@Named(MediaModule.COMMUNAL_HUB) mediaHost: MediaHost,
) : BaseCommunalViewModel(communalInteractor, mediaHost) {
@@ -93,8 +90,6 @@
}
}
- override fun getInteractionHandler(): RemoteViews.InteractionHandler = interactionHandler
-
override fun onHidePopupAfterDismissCta() {
cancelDelayedPopupHiding()
setPopupOnDismissCtaVisibility(false)
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHost.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHost.kt
index 003c9d5..61db026 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHost.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHost.kt
@@ -20,9 +20,16 @@
import android.appwidget.AppWidgetHostView
import android.appwidget.AppWidgetProviderInfo
import android.content.Context
+import android.os.Looper
+import android.widget.RemoteViews
/** Communal app widget host that creates a [CommunalAppWidgetHostView]. */
-class CommunalAppWidgetHost(context: Context, hostId: Int) : AppWidgetHost(context, hostId) {
+class CommunalAppWidgetHost(
+ context: Context,
+ hostId: Int,
+ interactionHandler: RemoteViews.InteractionHandler,
+ looper: Looper
+) : AppWidgetHost(context, hostId, interactionHandler, looper) {
override fun onCreateView(
context: Context,
appWidgetId: Int,
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostView.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostView.kt
index 2b7d823..840c3a8 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostView.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/CommunalAppWidgetHostView.kt
@@ -22,9 +22,17 @@
import android.graphics.Rect
import android.view.View
import android.view.ViewOutlineProvider
+import com.android.systemui.animation.LaunchableView
+import com.android.systemui.animation.LaunchableViewDelegate
/** AppWidgetHostView that displays in communal hub with support for rounded corners. */
-class CommunalAppWidgetHostView(context: Context) : AppWidgetHostView(context) {
+class CommunalAppWidgetHostView(context: Context) : AppWidgetHostView(context), LaunchableView {
+ private val launchableViewDelegate =
+ LaunchableViewDelegate(
+ this,
+ superSetVisibility = { super.setVisibility(it) },
+ )
+
// Mutable corner radius.
var enforcedCornerRadius: Float
@@ -73,4 +81,9 @@
outlineProvider = ViewOutlineProvider.BACKGROUND
clipToOutline = false
}
+
+ override fun setShouldBlockVisibilityChanges(block: Boolean) =
+ launchableViewDelegate.setShouldBlockVisibilityChanges(block)
+
+ override fun setVisibility(visibility: Int) = launchableViewDelegate.setVisibility(visibility)
}
diff --git a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt
index c8db70b..afa7fa9 100644
--- a/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt
+++ b/packages/SystemUI/src/com/android/systemui/communal/widgets/WidgetInteractionHandler.kt
@@ -19,6 +19,8 @@
import android.app.PendingIntent
import android.view.View
import android.widget.RemoteViews
+import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.common.ui.view.getNearestParent
import com.android.systemui.plugins.ActivityStarter
import javax.inject.Inject
@@ -33,17 +35,19 @@
response: RemoteViews.RemoteResponse
): Boolean =
when {
- pendingIntent.isActivity -> startActivity(pendingIntent)
+ pendingIntent.isActivity -> startActivity(view, pendingIntent)
else ->
RemoteViews.startPendingIntent(view, pendingIntent, response.getLaunchOptions(view))
}
- private fun startActivity(pendingIntent: PendingIntent): Boolean {
+ private fun startActivity(view: View, pendingIntent: PendingIntent): Boolean {
+ val hostView = view.getNearestParent<CommunalAppWidgetHostView>()
+ val animationController = hostView?.let(ActivityLaunchAnimator.Controller::fromView)
+
activityStarter.startPendingIntentMaybeDismissingKeyguard(
- /* intent = */ pendingIntent,
+ pendingIntent,
/* intentSentUiThreadCallback = */ null,
- // TODO(b/318758390): Properly animate activities started from widgets.
- /* animationController = */ null
+ animationController
)
return true
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt
index f33aed0..f2b28d9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/view/KeyguardRootView.kt
@@ -29,8 +29,4 @@
ConstraintLayout(
context,
attrs,
- ) {
- init {
- clipChildren = false
- }
-}
+ )
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
index 226a957..bd659d2 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotifCoordinators.kt
@@ -23,6 +23,7 @@
import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
import com.android.systemui.statusbar.notification.collection.listbuilder.pluggable.NotifSectioner
import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider
+import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
import javax.inject.Inject
/**
@@ -61,6 +62,7 @@
sensitiveContentCoordinator: SensitiveContentCoordinator,
dismissibilityCoordinator: DismissibilityCoordinator,
dreamCoordinator: DreamCoordinator,
+ statsLoggerCoordinator: NotificationStatsLoggerCoordinator,
) : NotifCoordinators {
private val mCoreCoordinators: MutableList<CoreCoordinator> = ArrayList()
@@ -104,6 +106,10 @@
mCoordinators.add(dreamCoordinator)
}
+ if (NotificationsLiveDataStoreRefactor.isEnabled) {
+ mCoordinators.add(statsLoggerCoordinator)
+ }
+
// Manually add Ordered Sections
mOrderedSections.add(headsUpCoordinator.sectioner) // HeadsUp
mOrderedSections.add(colorizedFgsCoordinator.sectioner) // ForegroundService
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotificationStatsLoggerCoordinator.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotificationStatsLoggerCoordinator.kt
new file mode 100644
index 0000000..6e81d93
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/collection/coordinator/NotificationStatsLoggerCoordinator.kt
@@ -0,0 +1,51 @@
+/*
+ * 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.systemui.statusbar.notification.collection.coordinator
+
+import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.coordinator.dagger.CoordinatorScope
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
+import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
+import com.android.systemui.statusbar.notification.stack.ui.view.NotificationStatsLogger
+import java.util.Optional
+import javax.inject.Inject
+
+@CoordinatorScope
+class NotificationStatsLoggerCoordinator
+@Inject
+constructor(private val loggerOptional: Optional<NotificationStatsLogger>) : Coordinator {
+
+ private val collectionListener =
+ object : NotifCollectionListener {
+ override fun onEntryUpdated(entry: NotificationEntry) {
+ super.onEntryUpdated(entry)
+ loggerOptional.ifPresent { it.onNotificationUpdated(entry.key) }
+ }
+
+ override fun onEntryRemoved(entry: NotificationEntry, reason: Int) {
+ super.onEntryRemoved(entry, reason)
+ loggerOptional.ifPresent { it.onNotificationRemoved(entry.key) }
+ }
+ }
+ override fun attach(pipeline: NotifPipeline) {
+ if (NotificationsLiveDataStoreRefactor.isUnexpectedlyInLegacyMode()) {
+ return
+ }
+ pipeline.addCollectionListener(collectionListener)
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
index ae77288..0bc8e68 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/NotificationIconContainerViewBinder.kt
@@ -142,7 +142,7 @@
/** Binds to [NotificationIconContainer.setAnimationsEnabled] */
private suspend fun Flow<Boolean>.bindAnimationsEnabled(view: NotificationIconContainer) {
- collect(view::setAnimationsEnabled)
+ collectTracingEach("NIC#bindAnimationsEnabled", view::setAnimationsEnabled)
}
private suspend fun NotificationIconContainerStatusBarViewModel.bindIsolatedIcon(
@@ -151,12 +151,12 @@
) {
coroutineScope {
launch {
- isolatedIconLocation.collect { location ->
+ isolatedIconLocation.collectTracingEach("NIC#isolatedIconLocation") { location ->
view.setIsolatedIconLocation(location, true)
}
}
launch {
- isolatedIcon.collect { iconInfo ->
+ isolatedIcon.collectTracingEach("NIC#showIconIsolated") { iconInfo ->
val iconView = iconInfo.value?.let { viewStore.iconView(it.notifKey) }
if (iconInfo.isAnimating) {
view.showIconIsolatedAnimated(iconView, iconInfo::stopAnimating)
@@ -214,7 +214,7 @@
val failedBindings = mutableSetOf<String>()
val boundViewsByNotifKey = ArrayMap<String, Pair<StatusBarIconView, Job>>()
var prevIcons = NotificationIconsViewData()
- collectTracingEach("NotifIconContainer#bindIcons") { iconsData: NotificationIconsViewData ->
+ collectTracingEach("NIC#bindIcons") { iconsData: NotificationIconsViewData ->
val iconsDiff = NotificationIconsViewData.computeDifference(iconsData, prevIcons)
prevIcons = iconsData
@@ -265,7 +265,11 @@
Pair(
sbiv,
launch {
- launch { layoutParams.collect { sbiv.layoutParams = it } }
+ launch {
+ layoutParams.collectTracingEach("SBIV#bindLayoutParams") {
+ sbiv.layoutParams = it
+ }
+ }
bindIcon(notifKey, sbiv)
},
)
@@ -369,6 +373,7 @@
)
}
-private suspend fun <T> Flow<T>.collectTracingEach(tag: String, collector: (T) -> Unit) {
- collect { traceSection(tag) { collector(it) } }
-}
+private suspend inline fun <T> Flow<T>.collectTracingEach(
+ tag: String,
+ crossinline collector: (T) -> Unit,
+) = collect { traceSection(tag) { collector(it) } }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/StatusBarIconViewBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/StatusBarIconViewBinder.kt
index 0331654..bfeaced 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/StatusBarIconViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/icon/ui/viewbinder/StatusBarIconViewBinder.kt
@@ -18,6 +18,7 @@
import android.graphics.Rect
import android.view.View
+import com.android.app.tracing.traceSection
import com.android.internal.util.ContrastColorUtil
import com.android.systemui.res.R
import com.android.systemui.statusbar.StatusBarIconView
@@ -33,18 +34,18 @@
// view-model (which, at the time of this writing, does not yet exist).
suspend fun bindColor(view: StatusBarIconView, color: Flow<Int>) {
- color.collect { color ->
+ color.collectTracingEach("SBIV#bindColor") { color ->
view.staticDrawableColor = color
view.setDecorColor(color)
}
}
suspend fun bindTintAlpha(view: StatusBarIconView, tintAlpha: Flow<Float>) {
- tintAlpha.collect { amt -> view.setTintAlpha(amt) }
+ tintAlpha.collectTracingEach("SBIV#bindTintAlpha") { amt -> view.setTintAlpha(amt) }
}
suspend fun bindAnimationsEnabled(view: StatusBarIconView, allowAnimation: Flow<Boolean>) {
- allowAnimation.collect(view::setAllowAnimation)
+ allowAnimation.collectTracingEach("SBIV#bindAnimationsEnabled", view::setAllowAnimation)
}
suspend fun bindIconColors(
@@ -52,7 +53,7 @@
iconColors: Flow<NotificationIconColors>,
contrastColorUtil: ContrastColorUtil,
) {
- iconColors.collect { colors ->
+ iconColors.collectTracingEach("SBIV#bindIconColors") { colors ->
val isPreL = java.lang.Boolean.TRUE == view.getTag(R.id.icon_is_pre_L)
val isColorized = !isPreL || NotificationUtils.isGrayscale(view, contrastColorUtil)
view.staticDrawableColor =
@@ -73,3 +74,8 @@
/* bottom = */ top + height,
)
}
+
+private suspend inline fun <T> Flow<T>.collectTracingEach(
+ tag: String,
+ crossinline collector: (T) -> Unit,
+) = collect { traceSection(tag) { collector(it) } }
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationStatsLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationStatsLogger.kt
index 5418616..365c02f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationStatsLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationStatsLogger.kt
@@ -29,9 +29,7 @@
activeNotifications: List<ActiveNotificationModel>,
)
fun onLockscreenOrShadeNotInteractive(activeNotifications: List<ActiveNotificationModel>)
- fun onNotificationRemoved(key: String)
- fun onNotificationUpdated(key: String)
- fun onNotificationListUpdated(
+ fun onNotificationLocationsChanged(
locationsProvider: Callable<Map<String, Int>>,
notificationRanks: Map<String, Int>,
)
@@ -41,4 +39,6 @@
location: Int,
isUserAction: Boolean
)
+ fun onNotificationRemoved(key: String)
+ fun onNotificationUpdated(key: String)
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationStatsLoggerImpl.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationStatsLoggerImpl.kt
index 0cb00bc..4897b42 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationStatsLoggerImpl.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationStatsLoggerImpl.kt
@@ -53,10 +53,11 @@
private val expansionStates: MutableMap<String, ExpansionState> =
ConcurrentHashMap<String, ExpansionState>()
- private val lastReportedExpansionValues: MutableMap<String, Boolean> =
+ @VisibleForTesting
+ val lastReportedExpansionValues: MutableMap<String, Boolean> =
ConcurrentHashMap<String, Boolean>()
- override fun onNotificationListUpdated(
+ override fun onNotificationLocationsChanged(
locationsProvider: Callable<Map<String, Int>>,
notificationRanks: Map<String, Int>,
) {
@@ -152,14 +153,12 @@
)
}
- // TODO(b/308623704) wire this in with NotifPipeline updates
override fun onNotificationRemoved(key: String) {
// No need to track expansion states for Notifications that are removed.
expansionStates.remove(key)
lastReportedExpansionValues.remove(key)
}
- // TODO(b/308623704) wire this in with NotifPipeline updates
override fun onNotificationUpdated(key: String) {
// When the Notification is updated, we should consider it as not yet logged.
lastReportedExpansionValues.remove(key)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStatsLoggerBinder.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStatsLoggerBinder.kt
index a05ad6e..a87c85f 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStatsLoggerBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/ui/viewbinder/NotificationStatsLoggerBinder.kt
@@ -41,6 +41,8 @@
logger: NotificationStatsLogger,
viewModel: NotificationLoggerViewModel,
) {
+ // Updates the logger about whether the Notification panel, and the individual Notifications
+ // are visible to the user.
viewModel.isLockscreenOrShadeInteractive
.sample(
combine(viewModel.isOnLockScreen, viewModel.activeNotifications, ::Pair),
@@ -52,14 +54,14 @@
isOnLockScreen = isOnLockScreen,
activeNotifications = notifications,
)
- view.onNotificationsUpdated
- // Delay the updates with [NOTIFICATION_UPDATES_PERIOD_MS]. If the original
+ view.onNotificationLocationsUpdated
+ // Delay the updates with [NOTIFICATION_UPDATE_PERIOD_MS]. If the original
// flow emits more than once during this period, only the latest value is
// emitted, meaning that we won't log the intermediate Notification states.
.throttle(NOTIFICATION_UPDATE_PERIOD_MS)
.sample(viewModel.activeNotificationRanks, ::Pair)
- .collect { (locationsProvider, notificationRanks) ->
- logger.onNotificationListUpdated(locationsProvider, notificationRanks)
+ .collect { (locationsProvider, ranks) ->
+ logger.onNotificationLocationsChanged(locationsProvider, ranks)
}
} else {
logger.onLockscreenOrShadeNotInteractive(
@@ -70,7 +72,7 @@
}
}
-private val NotificationStackScrollLayout.onNotificationsUpdated
+private val NotificationStackScrollLayout.onNotificationLocationsUpdated
get() =
ConflatedCallbackFlow.conflatedCallbackFlow {
val callback =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/NotificationStatsLoggerCoordinatorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/NotificationStatsLoggerCoordinatorTest.kt
new file mode 100644
index 0000000..c29ff41
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/collection/coordinator/NotificationStatsLoggerCoordinatorTest.kt
@@ -0,0 +1,72 @@
+/*
+ * 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.systemui.statusbar.notification.collection.coordinator
+
+import android.platform.test.annotations.EnableFlags
+import android.service.notification.NotificationListenerService.REASON_CANCEL
+import android.testing.AndroidTestingRunner
+import androidx.test.filters.SmallTest
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.statusbar.notification.collection.NotifPipeline
+import com.android.systemui.statusbar.notification.collection.NotificationEntry
+import com.android.systemui.statusbar.notification.collection.notifcollection.NotifCollectionListener
+import com.android.systemui.statusbar.notification.shared.NotificationsLiveDataStoreRefactor
+import com.android.systemui.statusbar.notification.stack.ui.view.NotificationStatsLogger
+import com.android.systemui.util.mockito.mock
+import com.android.systemui.util.mockito.whenever
+import com.android.systemui.util.mockito.withArgCaptor
+import java.util.Optional
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.verify
+
+@SmallTest
+@RunWith(AndroidTestingRunner::class)
+@EnableFlags(NotificationsLiveDataStoreRefactor.FLAG_NAME)
+class NotificationStatsLoggerCoordinatorTest : SysuiTestCase() {
+
+ private lateinit var collectionListener: NotifCollectionListener
+
+ private val pipeline: NotifPipeline = mock()
+ private val logger: NotificationStatsLogger = mock()
+ private val underTest = NotificationStatsLoggerCoordinator(Optional.of(logger))
+
+ @Before
+ fun attachPipeline() {
+ underTest.attach(pipeline)
+ collectionListener = withArgCaptor { verify(pipeline).addCollectionListener(capture()) }
+ }
+
+ @Test
+ fun onEntryAdded_loggerCalled() {
+ collectionListener.onEntryRemoved(mockEntry("key"), REASON_CANCEL)
+
+ verify(logger).onNotificationRemoved("key")
+ }
+
+ @Test
+ fun onEntryRemoved_loggerCalled() {
+ collectionListener.onEntryUpdated(mockEntry("key"))
+
+ verify(logger).onNotificationUpdated("key")
+ }
+
+ private fun mockEntry(key: String): NotificationEntry {
+ return mock { whenever(this.key).thenReturn(key) }
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationStatsLoggerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationStatsLoggerTest.kt
index d7d1ffc..c61756c 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationStatsLoggerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/ui/view/NotificationStatsLoggerTest.kt
@@ -67,7 +67,7 @@
// AND they're visible
val (ranks, locations) = fakeNotificationMaps("key0", "key1")
val callable = Callable { locations }
- underTest.onNotificationListUpdated(callable, ranks)
+ underTest.onNotificationLocationsChanged(callable, ranks)
runCurrent()
// THEN visibility changes are reported
@@ -103,13 +103,13 @@
// GIVEN some visible Notifications are reported
val (ranks, locations) = fakeNotificationMaps("key0", "key1")
val callable = Callable { locations }
- underTest.onNotificationListUpdated(callable, ranks)
+ underTest.onNotificationLocationsChanged(callable, ranks)
runCurrent()
clearInvocations(mockStatusBarService, mockNotificationListenerService)
// WHEN the same Notifications are removed
val emptyCallable = Callable { emptyMap<String, Int>() }
- underTest.onNotificationListUpdated(emptyCallable, emptyMap())
+ underTest.onNotificationLocationsChanged(emptyCallable, emptyMap())
runCurrent()
// THEN visibility changes are reported
@@ -140,13 +140,13 @@
// GIVEN some visible Notifications are reported
val (ranks, locations) = fakeNotificationMaps("key0", "key1")
val callable = Callable { locations }
- underTest.onNotificationListUpdated(callable, ranks)
+ underTest.onNotificationLocationsChanged(callable, ranks)
runCurrent()
clearInvocations(mockStatusBarService, mockNotificationListenerService)
// WHEN the same Notifications are becoming invisible
val emptyCallable = Callable { emptyMap<String, Int>() }
- underTest.onNotificationListUpdated(emptyCallable, ranks)
+ underTest.onNotificationLocationsChanged(emptyCallable, ranks)
runCurrent()
// THEN visibility changes are reported
@@ -176,13 +176,13 @@
testScope.runTest {
// GIVEN some visible Notifications are reported
val (ranks, locations) = fakeNotificationMaps("key0", "key1")
- underTest.onNotificationListUpdated({ locations }, ranks)
+ underTest.onNotificationLocationsChanged({ locations }, ranks)
runCurrent()
clearInvocations(mockStatusBarService, mockNotificationListenerService)
// WHEN the reported Notifications are changing positions
val (newRanks, newLocations) = fakeNotificationMaps("key1", "key0")
- underTest.onNotificationListUpdated({ newLocations }, newRanks)
+ underTest.onNotificationLocationsChanged({ newLocations }, newRanks)
runCurrent()
// THEN no visibility changes are reported
@@ -195,13 +195,13 @@
// GIVEN some visible Notifications are reported
val (ranks, locations) = fakeNotificationMaps("key0", "key1", "key2")
val callable = spy(Callable { locations })
- underTest.onNotificationListUpdated(callable, ranks)
+ underTest.onNotificationLocationsChanged(callable, ranks)
runCurrent()
clearInvocations(callable)
// WHEN a new update comes
val otherCallable = spy(Callable { locations })
- underTest.onNotificationListUpdated(otherCallable, ranks)
+ underTest.onNotificationLocationsChanged(otherCallable, ranks)
runCurrent()
// THEN we call the new Callable
@@ -215,7 +215,7 @@
// GIVEN some visible Notifications are reported
val (ranks, locations) = fakeNotificationMaps("key0", "key1")
val callable = Callable { locations }
- underTest.onNotificationListUpdated(callable, ranks)
+ underTest.onNotificationLocationsChanged(callable, ranks)
runCurrent()
clearInvocations(mockStatusBarService, mockNotificationListenerService)
@@ -281,7 +281,7 @@
}
@Test
- fun onNotificationExpanded_visibleLocation_expansionLogged() =
+ fun onNotificationExpansionChanged_whenExpandedInVisibleLocation_logsExpansion() =
testScope.runTest {
// WHEN a Notification is expanded
underTest.onNotificationExpansionChanged(
@@ -303,7 +303,33 @@
}
@Test
- fun onNotificationExpanded_notVisibleLocation_nothingLogged() =
+ fun onNotificationExpansionChanged_whenCalledTwiceWithTheSameUpdate_doesNotDuplicateLogs() =
+ testScope.runTest {
+ // GIVEN a Notification is expanded
+ underTest.onNotificationExpansionChanged(
+ key = "key",
+ isExpanded = true,
+ location = ExpandableViewState.LOCATION_MAIN_AREA,
+ isUserAction = true
+ )
+ runCurrent()
+ clearInvocations(mockStatusBarService)
+
+ // WHEN the logger receives the same expansion update
+ underTest.onNotificationExpansionChanged(
+ key = "key",
+ isExpanded = true,
+ location = ExpandableViewState.LOCATION_MAIN_AREA,
+ isUserAction = true
+ )
+ runCurrent()
+
+ // THEN the Expand event is not reported again
+ verifyZeroInteractions(mockStatusBarService)
+ }
+
+ @Test
+ fun onNotificationExpansionChanged_whenCalledForNotVisibleItem_nothingLogged() =
testScope.runTest {
// WHEN a NOT visible Notification is expanded
underTest.onNotificationExpansionChanged(
@@ -319,35 +345,73 @@
}
@Test
- fun onNotificationExpanded_notVisibleLocation_becomesVisible_expansionLogged() =
+ fun onNotificationExpansionChanged_whenNotVisibleItemBecomesVisible_logsChanges() =
testScope.runTest {
// WHEN a NOT visible Notification is expanded
underTest.onNotificationExpansionChanged(
key = "key",
isExpanded = true,
location = ExpandableViewState.LOCATION_GONE,
- isUserAction = true
+ isUserAction = false
)
runCurrent()
// AND it becomes visible
val (ranks, locations) = fakeNotificationMaps("key")
val callable = Callable { locations }
- underTest.onNotificationListUpdated(callable, ranks)
+ underTest.onNotificationLocationsChanged(callable, ranks)
runCurrent()
// THEN the Expand event is reported
verify(mockStatusBarService)
.onNotificationExpansionChanged(
/* key = */ "key",
- /* userAction = */ true,
+ /* userAction = */ false,
/* expanded = */ true,
NotificationVisibility.NotificationLocation.LOCATION_MAIN_AREA.ordinal
)
}
@Test
- fun onNotificationCollapsed_isFirstInteraction_nothingLogged() =
+ fun onNotificationExpansionChanged_whenUpdatedItemBecomesVisible_logsChanges() =
+ testScope.runTest {
+ // GIVEN a NOT visible Notification is expanded
+ underTest.onNotificationExpansionChanged(
+ key = "key",
+ isExpanded = true,
+ location = ExpandableViewState.LOCATION_GONE,
+ isUserAction = false
+ )
+ runCurrent()
+ // AND we open the shade, so we log its events
+ val (ranks, locations) = fakeNotificationMaps("key")
+ val callable = Callable { locations }
+ underTest.onNotificationLocationsChanged(callable, ranks)
+ runCurrent()
+ // AND we close the shade, so it is NOT visible
+ val emptyCallable = Callable { emptyMap<String, Int>() }
+ underTest.onNotificationLocationsChanged(emptyCallable, ranks)
+ runCurrent()
+ clearInvocations(mockStatusBarService) // clear the previous expand log
+
+ // WHEN it receives an update
+ underTest.onNotificationUpdated("key")
+ // AND it becomes visible again
+ underTest.onNotificationLocationsChanged(callable, ranks)
+ runCurrent()
+
+ // THEN we log its expand event again
+ verify(mockStatusBarService)
+ .onNotificationExpansionChanged(
+ /* key = */ "key",
+ /* userAction = */ false,
+ /* expanded = */ true,
+ NotificationVisibility.NotificationLocation.LOCATION_MAIN_AREA.ordinal
+ )
+ }
+
+ @Test
+ fun onNotificationExpansionChanged_whenCollapsedForTheFirstTime_nothingLogged() =
testScope.runTest {
// WHEN a Notification is collapsed, and it is the first interaction
underTest.onNotificationExpansionChanged(
@@ -364,7 +428,7 @@
}
@Test
- fun onNotificationExpandedAndCollapsed_expansionChangesLogged() =
+ fun onNotificationExpansionChanged_receivesMultipleUpdates_logsChanges() =
testScope.runTest {
// GIVEN a Notification is expanded
underTest.onNotificationExpansionChanged(
@@ -403,6 +467,60 @@
)
}
+ @Test
+ fun onNotificationUpdated_clearsTrackedExpansionChanges() =
+ testScope.runTest {
+ // GIVEN some notification updates are posted
+ underTest.onNotificationExpansionChanged(
+ key = "key1",
+ isExpanded = true,
+ location = ExpandableViewState.LOCATION_MAIN_AREA,
+ isUserAction = true
+ )
+ runCurrent()
+ underTest.onNotificationExpansionChanged(
+ key = "key2",
+ isExpanded = true,
+ location = ExpandableViewState.LOCATION_MAIN_AREA,
+ isUserAction = true
+ )
+ runCurrent()
+ clearInvocations(mockStatusBarService)
+
+ // WHEN a Notification is updated
+ underTest.onNotificationUpdated("key1")
+
+ // THEN the tracked expansion changes are updated
+ assertThat(underTest.lastReportedExpansionValues.keys).containsExactly("key2")
+ }
+
+ @Test
+ fun onNotificationRemoved_clearsTrackedExpansionChanges() =
+ testScope.runTest {
+ // GIVEN some notification updates are posted
+ underTest.onNotificationExpansionChanged(
+ key = "key1",
+ isExpanded = true,
+ location = ExpandableViewState.LOCATION_MAIN_AREA,
+ isUserAction = true
+ )
+ runCurrent()
+ underTest.onNotificationExpansionChanged(
+ key = "key2",
+ isExpanded = true,
+ location = ExpandableViewState.LOCATION_MAIN_AREA,
+ isUserAction = true
+ )
+ runCurrent()
+ clearInvocations(mockStatusBarService)
+
+ // WHEN a Notification is removed
+ underTest.onNotificationRemoved("key1")
+
+ // THEN it is removed from the tracked expansion changes
+ assertThat(underTest.lastReportedExpansionValues.keys).doesNotContain("key1")
+ }
+
private fun fakeNotificationMaps(
vararg keys: String
): Pair<Map<String, Int>, Map<String, Int>> {
diff --git a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
index 7fcef9c..3aa9cc8 100644
--- a/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
+++ b/packages/services/CameraExtensionsProxy/src/com/android/cameraextensions/CameraExtensionsProxyService.java
@@ -59,6 +59,7 @@
import android.hardware.camera2.extension.SizeList;
import android.hardware.camera2.impl.CameraMetadataNative;
import android.hardware.camera2.impl.PhysicalCaptureResultInfo;
+import android.hardware.camera2.utils.SurfaceUtils;
import android.media.Image;
import android.media.ImageReader;
import android.os.Binder;
@@ -1691,11 +1692,17 @@
private final Surface mSurface;
private final Size mSize;
private final int mImageFormat;
+ private final int mDataspace;
public OutputSurfaceImplStub(OutputSurface outputSurface) {
mSurface = outputSurface.surface;
mSize = new Size(outputSurface.size.width, outputSurface.size.height);
mImageFormat = outputSurface.imageFormat;
+ if (mSurface != null) {
+ mDataspace = SurfaceUtils.getSurfaceDataspace(mSurface);
+ } else {
+ mDataspace = -1;
+ }
}
@Override
@@ -1712,6 +1719,11 @@
public int getImageFormat() {
return mImageFormat;
}
+
+ @Override
+ public int getDataspace() {
+ return mDataspace;
+ }
}
private class PreviewExtenderImplStub extends IPreviewExtenderImpl.Stub implements
diff --git a/services/core/java/com/android/server/inputmethod/HardwareKeyboardShortcutController.java b/services/core/java/com/android/server/inputmethod/HardwareKeyboardShortcutController.java
index ad27c52..4b85d09 100644
--- a/services/core/java/com/android/server/inputmethod/HardwareKeyboardShortcutController.java
+++ b/services/core/java/com/android/server/inputmethod/HardwareKeyboardShortcutController.java
@@ -54,7 +54,9 @@
void reset(@NonNull ArrayMap<String, InputMethodInfo> methodMap) {
mSubtypeHandles.clear();
final InputMethodSettings settings = new InputMethodSettings(methodMap, mUserId);
- for (final InputMethodInfo imi : settings.getEnabledInputMethodListLocked()) {
+ final List<InputMethodInfo> inputMethods = settings.getEnabledInputMethodListLocked();
+ for (int i = 0; i < inputMethods.size(); ++i) {
+ final InputMethodInfo imi = inputMethods.get(i);
if (!imi.shouldShowInInputMethodPicker()) {
continue;
}
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodInfoUtils.java b/services/core/java/com/android/server/inputmethod/InputMethodInfoUtils.java
index a763251..542165d 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodInfoUtils.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodInfoUtils.java
@@ -46,7 +46,7 @@
private static final String TAG = "InputMethodInfoUtils";
/**
- * Used in {@link #getFallbackLocaleForDefaultIme(ArrayList, Context)} to find the fallback IMEs
+ * Used in {@link #getFallbackLocaleForDefaultIme(List, Context)} to find the fallback IMEs
* that are mainly used until the system becomes ready. Note that {@link Locale} in this array
* is checked with {@link Locale#equals(Object)}, which means that {@code Locale.ENGLISH}
* doesn't automatically match {@code Locale("en", "IN")}.
@@ -64,7 +64,7 @@
@NonNull
private final LinkedHashSet<InputMethodInfo> mInputMethodSet = new LinkedHashSet<>();
- InputMethodListBuilder fillImes(ArrayList<InputMethodInfo> imis, Context context,
+ InputMethodListBuilder fillImes(List<InputMethodInfo> imis, Context context,
boolean checkDefaultAttribute, @Nullable Locale locale, boolean checkCountry,
String requiredSubtypeMode) {
for (int i = 0; i < imis.size(); ++i) {
@@ -77,7 +77,7 @@
return this;
}
- InputMethodListBuilder fillAuxiliaryImes(ArrayList<InputMethodInfo> imis, Context context) {
+ InputMethodListBuilder fillAuxiliaryImes(List<InputMethodInfo> imis, Context context) {
// If one or more auxiliary input methods are available, OK to stop populating the list.
for (final InputMethodInfo imi : mInputMethodSet) {
if (imi.isAuxiliaryIme()) {
@@ -118,7 +118,7 @@
}
private static InputMethodListBuilder getMinimumKeyboardSetWithSystemLocale(
- ArrayList<InputMethodInfo> imis, Context context, @Nullable Locale systemLocale,
+ List<InputMethodInfo> imis, Context context, @Nullable Locale systemLocale,
@Nullable Locale fallbackLocale) {
// Once the system becomes ready, we pick up at least one keyboard in the following order.
// Secondary users fall into this category in general.
@@ -167,7 +167,7 @@
}
static ArrayList<InputMethodInfo> getDefaultEnabledImes(
- Context context, ArrayList<InputMethodInfo> imis, boolean onlyMinimum) {
+ Context context, List<InputMethodInfo> imis, boolean onlyMinimum) {
final Locale fallbackLocale = getFallbackLocaleForDefaultIme(imis, context);
// We will primarily rely on the system locale, but also keep relying on the fallback locale
// as a last resort.
@@ -186,7 +186,7 @@
}
static ArrayList<InputMethodInfo> getDefaultEnabledImes(
- Context context, ArrayList<InputMethodInfo> imis) {
+ Context context, List<InputMethodInfo> imis) {
return getDefaultEnabledImes(context, imis, false /* onlyMinimum */);
}
@@ -283,7 +283,7 @@
}
@Nullable
- private static Locale getFallbackLocaleForDefaultIme(ArrayList<InputMethodInfo> imis,
+ private static Locale getFallbackLocaleForDefaultIme(List<InputMethodInfo> imis,
Context context) {
// At first, find the fallback locale from the IMEs that are declared as "default" in the
// current locale. Note that IME developers can declare an IME as "default" only for
diff --git a/services/core/java/com/android/server/inputmethod/InputMethodSettings.java b/services/core/java/com/android/server/inputmethod/InputMethodSettings.java
index 4c7d755..9ddb428 100644
--- a/services/core/java/com/android/server/inputmethod/InputMethodSettings.java
+++ b/services/core/java/com/android/server/inputmethod/InputMethodSettings.java
@@ -67,7 +67,8 @@
builder.append(ime.first);
// Inputmethod and subtypes are saved in the settings as follows:
// ime0;subtype0;subtype1:ime1;subtype0:ime2:ime3;subtype0;subtype1
- for (String subtypeId : ime.second) {
+ for (int i = 0; i < ime.second.size(); ++i) {
+ final String subtypeId = ime.second.get(i);
builder.append(INPUT_METHOD_SUBTYPE_SEPARATOR).append(subtypeId);
}
}
@@ -123,17 +124,19 @@
}
List<InputMethodSubtype> getEnabledInputMethodSubtypeListLocked(InputMethodInfo imi) {
- List<Pair<String, ArrayList<String>>> imsList =
+ final List<Pair<String, ArrayList<String>>> imsList =
getEnabledInputMethodsAndSubtypeListLocked();
- ArrayList<InputMethodSubtype> enabledSubtypes = new ArrayList<>();
+ final List<InputMethodSubtype> enabledSubtypes = new ArrayList<>();
if (imi != null) {
- for (Pair<String, ArrayList<String>> imsPair : imsList) {
- InputMethodInfo info = mMethodMap.get(imsPair.first);
+ for (int i = 0; i < imsList.size(); ++i) {
+ final Pair<String, ArrayList<String>> imsPair = imsList.get(i);
+ final InputMethodInfo info = mMethodMap.get(imsPair.first);
if (info != null && info.getId().equals(imi.getId())) {
final int subtypeCount = info.getSubtypeCount();
- for (int i = 0; i < subtypeCount; ++i) {
- InputMethodSubtype ims = info.getSubtypeAt(i);
- for (String s : imsPair.second) {
+ for (int j = 0; j < subtypeCount; ++j) {
+ final InputMethodSubtype ims = info.getSubtypeAt(j);
+ for (int k = 0; k < imsPair.second.size(); ++k) {
+ final String s = imsPair.second.get(k);
if (String.valueOf(ims.hashCode()).equals(s)) {
enabledSubtypes.add(ims);
}
@@ -182,8 +185,9 @@
StringBuilder builder, List<Pair<String, ArrayList<String>>> imsList, String id) {
boolean isRemoved = false;
boolean needsAppendSeparator = false;
- for (Pair<String, ArrayList<String>> ims : imsList) {
- String curId = ims.first;
+ for (int i = 0; i < imsList.size(); ++i) {
+ final Pair<String, ArrayList<String>> ims = imsList.get(i);
+ final String curId = ims.first;
if (curId.equals(id)) {
// We are disabling this input method, and it is
// currently enabled. Skip it to remove from the
@@ -209,8 +213,9 @@
List<Pair<String, ArrayList<String>>> imsList,
Predicate<InputMethodInfo> matchingCondition) {
final ArrayList<InputMethodInfo> res = new ArrayList<>();
- for (Pair<String, ArrayList<String>> ims : imsList) {
- InputMethodInfo info = mMethodMap.get(ims.first);
+ for (int i = 0; i < imsList.size(); ++i) {
+ final Pair<String, ArrayList<String>> ims = imsList.get(i);
+ final InputMethodInfo info = mMethodMap.get(ims.first);
if (info != null && !info.isVrOnly()
&& (matchingCondition == null || matchingCondition.test(info))) {
res.add(info);
@@ -239,15 +244,16 @@
private void saveSubtypeHistory(
List<Pair<String, String>> savedImes, String newImeId, String newSubtypeId) {
- StringBuilder builder = new StringBuilder();
+ final StringBuilder builder = new StringBuilder();
boolean isImeAdded = false;
if (!TextUtils.isEmpty(newImeId) && !TextUtils.isEmpty(newSubtypeId)) {
builder.append(newImeId).append(INPUT_METHOD_SUBTYPE_SEPARATOR).append(
newSubtypeId);
isImeAdded = true;
}
- for (Pair<String, String> ime : savedImes) {
- String imeId = ime.first;
+ for (int i = 0; i < savedImes.size(); ++i) {
+ final Pair<String, String> ime = savedImes.get(i);
+ final String imeId = ime.first;
String subtypeId = ime.second;
if (TextUtils.isEmpty(subtypeId)) {
subtypeId = NOT_A_SUBTYPE_ID_STR;
@@ -265,8 +271,9 @@
}
private void addSubtypeToHistory(String imeId, String subtypeId) {
- List<Pair<String, String>> subtypeHistory = loadInputMethodAndSubtypeHistoryLocked();
- for (Pair<String, String> ime : subtypeHistory) {
+ final List<Pair<String, String>> subtypeHistory = loadInputMethodAndSubtypeHistoryLocked();
+ for (int i = 0; i < subtypeHistory.size(); ++i) {
+ final Pair<String, String> ime = subtypeHistory.get(i);
if (ime.first.equals(imeId)) {
if (DEBUG) {
Slog.v(TAG, "Subtype found in the history: " + imeId + ", "
@@ -334,10 +341,11 @@
}
private Pair<String, String> getLastSubtypeForInputMethodLockedInternal(String imeId) {
- List<Pair<String, ArrayList<String>>> enabledImes =
+ final List<Pair<String, ArrayList<String>>> enabledImes =
getEnabledInputMethodsAndSubtypeListLocked();
- List<Pair<String, String>> subtypeHistory = loadInputMethodAndSubtypeHistoryLocked();
- for (Pair<String, String> imeAndSubtype : subtypeHistory) {
+ final List<Pair<String, String>> subtypeHistory = loadInputMethodAndSubtypeHistoryLocked();
+ for (int i = 0; i < subtypeHistory.size(); ++i) {
+ final Pair<String, String> imeAndSubtype = subtypeHistory.get(i);
final String imeInTheHistory = imeAndSubtype.first;
// If imeId is empty, returns the first IME and subtype in the history
if (TextUtils.isEmpty(imeId) || imeInTheHistory.equals(imeId)) {
@@ -363,11 +371,12 @@
private String getEnabledSubtypeHashCodeForInputMethodAndSubtypeLocked(List<Pair<String,
ArrayList<String>>> enabledImes, String imeId, String subtypeHashCode) {
final LocaleList localeList = SystemLocaleWrapper.get(mCurrentUserId);
- for (Pair<String, ArrayList<String>> enabledIme : enabledImes) {
+ for (int i = 0; i < enabledImes.size(); ++i) {
+ final Pair<String, ArrayList<String>> enabledIme = enabledImes.get(i);
if (enabledIme.first.equals(imeId)) {
final ArrayList<String> explicitlyEnabledSubtypes = enabledIme.second;
final InputMethodInfo imi = mMethodMap.get(imeId);
- if (explicitlyEnabledSubtypes.size() == 0) {
+ if (explicitlyEnabledSubtypes.isEmpty()) {
// If there are no explicitly enabled subtypes, applicable subtypes are
// enabled implicitly.
// If IME is enabled and no subtypes are enabled, applicable subtypes
@@ -377,15 +386,16 @@
SubtypeUtils.getImplicitlyApplicableSubtypesLocked(localeList,
imi);
final int numSubtypes = implicitlyEnabledSubtypes.size();
- for (int i = 0; i < numSubtypes; ++i) {
- final InputMethodSubtype st = implicitlyEnabledSubtypes.get(i);
+ for (int j = 0; j < numSubtypes; ++j) {
+ final InputMethodSubtype st = implicitlyEnabledSubtypes.get(j);
if (String.valueOf(st.hashCode()).equals(subtypeHashCode)) {
return subtypeHashCode;
}
}
}
} else {
- for (String s : explicitlyEnabledSubtypes) {
+ for (int j = 0; j < explicitlyEnabledSubtypes.size(); ++j) {
+ final String s = explicitlyEnabledSubtypes.get(j);
if (s.equals(subtypeHashCode)) {
// If both imeId and subtypeId are enabled, return subtypeId.
try {