diff options
11 files changed, 173 insertions, 40 deletions
| diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt index 3eb58bba1ca4..ec76f433b23b 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt @@ -38,6 +38,7 @@ import androidx.core.view.accessibility.AccessibilityNodeInfoCompat  import androidx.recyclerview.widget.RecyclerView  import com.android.systemui.R  import com.android.systemui.controls.ControlInterface +import com.android.systemui.controls.ui.CanUseIconPredicate  import com.android.systemui.controls.ui.RenderInfo  private typealias ModelFavoriteChanger = (String, Boolean) -> Unit @@ -51,7 +52,8 @@ private typealias ModelFavoriteChanger = (String, Boolean) -> Unit   * @property elevation elevation of each control view   */  class ControlAdapter( -    private val elevation: Float +    private val elevation: Float, +    private val currentUserId: Int,  ) : RecyclerView.Adapter<Holder>() {      companion object { @@ -107,7 +109,8 @@ class ControlAdapter(                          background = parent.context.getDrawable(                                  R.drawable.control_background_ripple)                      }, -                    model?.moveHelper // Indicates that position information is needed +                    currentUserId, +                    model?.moveHelper, // Indicates that position information is needed                  ) { id, favorite ->                      model?.changeFavoriteStatus(id, favorite)                  } @@ -212,8 +215,9 @@ private class ZoneHolder(view: View) : Holder(view) {   */  internal class ControlHolder(      view: View, +    currentUserId: Int,      val moveHelper: ControlsModel.MoveHelper?, -    val favoriteCallback: ModelFavoriteChanger +    val favoriteCallback: ModelFavoriteChanger,  ) : Holder(view) {      private val favoriteStateDescription =          itemView.context.getString(R.string.accessibility_control_favorite) @@ -228,6 +232,7 @@ internal class ControlHolder(          visibility = View.VISIBLE      } +    private val canUseIconPredicate = CanUseIconPredicate(currentUserId)      private val accessibilityDelegate = ControlHolderAccessibilityDelegate(          this::stateDescription,          this::getLayoutPosition, @@ -287,7 +292,9 @@ internal class ControlHolder(          val fg = context.getResources().getColorStateList(ri.foreground, context.getTheme())          icon.imageTintList = null -        ci.customIcon?.let { +        ci.customIcon +                ?.takeIf(canUseIconPredicate) +                ?.let {              icon.setImageIcon(it)          } ?: run {              icon.setImageDrawable(ri.icon) diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt index f611c3ef966d..d6cbb02712c2 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt @@ -180,7 +180,7 @@ class ControlsEditingActivity @Inject constructor(          val elevation = resources.getFloat(R.dimen.control_card_elevation)          val recyclerView = requireViewById<RecyclerView>(R.id.list)          recyclerView.alpha = 0.0f -        val adapter = ControlAdapter(elevation).apply { +        val adapter = ControlAdapter(elevation, currentUserTracker.currentUserId).apply {              registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {                  var hasAnimated = false                  override fun onChanged() { diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt index dca52a9678b9..a5cc07885a14 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt @@ -164,7 +164,7 @@ class ControlsFavoritingActivity @Inject constructor(                  }                  executor.execute { -                    structurePager.adapter = StructureAdapter(listOfStructures) +                    structurePager.adapter = StructureAdapter(listOfStructures, currentUserTracker.currentUserId)                      structurePager.setCurrentItem(structureIndex)                      if (error) {                          statusText.text = resources.getString(R.string.controls_favorite_load_error, @@ -210,7 +210,7 @@ class ControlsFavoritingActivity @Inject constructor(          structurePager.alpha = 0.0f          pageIndicator.alpha = 0.0f          structurePager.apply { -            adapter = StructureAdapter(emptyList()) +            adapter = StructureAdapter(emptyList(), currentUserTracker.currentUserId)              registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {                  override fun onPageSelected(position: Int) {                      super.onPageSelected(position) diff --git a/packages/SystemUI/src/com/android/systemui/controls/management/StructureAdapter.kt b/packages/SystemUI/src/com/android/systemui/controls/management/StructureAdapter.kt index 747bcbe1c229..5977d379acde 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/management/StructureAdapter.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/management/StructureAdapter.kt @@ -24,13 +24,15 @@ import androidx.recyclerview.widget.RecyclerView  import com.android.systemui.R  class StructureAdapter( -    private val models: List<StructureContainer> +    private val models: List<StructureContainer>, +    private val currentUserId: Int,  ) : RecyclerView.Adapter<StructureAdapter.StructureHolder>() {      override fun onCreateViewHolder(parent: ViewGroup, p1: Int): StructureHolder {          val layoutInflater = LayoutInflater.from(parent.context)          return StructureHolder( -            layoutInflater.inflate(R.layout.controls_structure_page, parent, false) +            layoutInflater.inflate(R.layout.controls_structure_page, parent, false), +            currentUserId,          )      } @@ -40,7 +42,8 @@ class StructureAdapter(          holder.bind(models[index].model)      } -    class StructureHolder(view: View) : RecyclerView.ViewHolder(view) { +    class StructureHolder(view: View, currentUserId: Int) : +            RecyclerView.ViewHolder(view) {          private val recyclerView: RecyclerView          private val controlAdapter: ControlAdapter @@ -48,7 +51,7 @@ class StructureAdapter(          init {              recyclerView = itemView.requireViewById<RecyclerView>(R.id.listAll)              val elevation = itemView.context.resources.getFloat(R.dimen.control_card_elevation) -            controlAdapter = ControlAdapter(elevation) +            controlAdapter = ControlAdapter(elevation, currentUserId)              setUpRecyclerView()          } diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/CanUseIconPredicate.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/CanUseIconPredicate.kt new file mode 100644 index 000000000000..61c21237144d --- /dev/null +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/CanUseIconPredicate.kt @@ -0,0 +1,30 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.controls.ui + +import android.content.ContentProvider +import android.graphics.drawable.Icon + +class CanUseIconPredicate(private val currentUserId: Int) : (Icon) -> Boolean { + +    override fun invoke(icon: Icon): Boolean = +        if (icon.type == Icon.TYPE_URI || icon.type == Icon.TYPE_URI_ADAPTIVE_BITMAP) { +            ContentProvider.getUserIdFromUri(icon.uri, currentUserId) == currentUserId +        } else { +            true +        } +} diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt index 6a9aaf865251..931062865c64 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt @@ -68,7 +68,8 @@ class ControlViewHolder(      val bgExecutor: DelayableExecutor,      val controlActionCoordinator: ControlActionCoordinator,      val controlsMetricsLogger: ControlsMetricsLogger, -    val uid: Int +    val uid: Int, +    val currentUserId: Int,  ) {      companion object { @@ -85,29 +86,9 @@ class ControlViewHolder(          private val ATTR_DISABLED = intArrayOf(-android.R.attr.state_enabled)          const val MIN_LEVEL = 0          const val MAX_LEVEL = 10000 - -        fun findBehaviorClass( -            status: Int, -            template: ControlTemplate, -            deviceType: Int -        ): Supplier<out Behavior> { -            return when { -                status != Control.STATUS_OK -> Supplier { StatusBehavior() } -                template == ControlTemplate.NO_TEMPLATE -> Supplier { TouchBehavior() } -                template is ThumbnailTemplate -> Supplier { ThumbnailBehavior() } - -                // Required for legacy support, or where cameras do not use the new template -                deviceType == DeviceTypes.TYPE_CAMERA -> Supplier { TouchBehavior() } -                template is ToggleTemplate -> Supplier { ToggleBehavior() } -                template is StatelessTemplate -> Supplier { TouchBehavior() } -                template is ToggleRangeTemplate -> Supplier { ToggleRangeBehavior() } -                template is RangeTemplate -> Supplier { ToggleRangeBehavior() } -                template is TemperatureControlTemplate -> Supplier { TemperatureControlBehavior() } -                else -> Supplier { DefaultBehavior() } -            } -        }      } +    private val canUseIconPredicate = CanUseIconPredicate(currentUserId)      private val toggleBackgroundIntensity: Float = layout.context.resources              .getFraction(R.fraction.controls_toggle_bg_intensity, 1, 1)      private var stateAnimator: ValueAnimator? = null @@ -147,6 +128,27 @@ class ControlViewHolder(          status.setSelected(true)      } +    fun findBehaviorClass( +            status: Int, +            template: ControlTemplate, +            deviceType: Int +    ): Supplier<out Behavior> { +        return when { +            status != Control.STATUS_OK -> Supplier { StatusBehavior() } +            template == ControlTemplate.NO_TEMPLATE -> Supplier { TouchBehavior() } +            template is ThumbnailTemplate -> Supplier { ThumbnailBehavior(currentUserId) } + +            // Required for legacy support, or where cameras do not use the new template +            deviceType == DeviceTypes.TYPE_CAMERA -> Supplier { TouchBehavior() } +            template is ToggleTemplate -> Supplier { ToggleBehavior() } +            template is StatelessTemplate -> Supplier { TouchBehavior() } +            template is ToggleRangeTemplate -> Supplier { ToggleRangeBehavior() } +            template is RangeTemplate -> Supplier { ToggleRangeBehavior() } +            template is TemperatureControlTemplate -> Supplier { TemperatureControlBehavior() } +            else -> Supplier { DefaultBehavior() } +        } +    } +      fun bindData(cws: ControlWithState, isLocked: Boolean) {          // If an interaction is in progress, the update may visually interfere with the action the          // action the user wants to make. Don't apply the update, and instead assume a new update @@ -473,7 +475,9 @@ class ControlViewHolder(          status.setTextColor(color) -        control?.getCustomIcon()?.let { +        control?.customIcon +                ?.takeIf(canUseIconPredicate) +                ?.let {              icon.setImageIcon(it)              icon.imageTintList = it.tintList          } ?: run { diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt index 1268250137b8..aa390d9af7e8 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt @@ -411,7 +411,8 @@ class ControlsUiControllerImpl @Inject constructor (                      bgExecutor,                      controlActionCoordinator,                      controlsMetricsLogger, -                    selected.uid +                    selected.uid, +                    controlsController.get().currentUserId,                  )                  cvh.bindData(it, false /* isLocked, will be ignored on initial load */)                  controlViewsById.put(key, cvh) diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt index a7dc09bb17e5..39d69704d817 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt @@ -63,7 +63,7 @@ class TemperatureControlBehavior : Behavior {              // interactions (touch, range)              subBehavior = cvh.bindBehavior(                  subBehavior, -                ControlViewHolder.findBehaviorClass( +                cvh.findBehaviorClass(                      control.status,                      subTemplate,                      control.deviceType diff --git a/packages/SystemUI/src/com/android/systemui/controls/ui/ThumbnailBehavior.kt b/packages/SystemUI/src/com/android/systemui/controls/ui/ThumbnailBehavior.kt index c2168aa8d9d9..0b57e792f9f7 100644 --- a/packages/SystemUI/src/com/android/systemui/controls/ui/ThumbnailBehavior.kt +++ b/packages/SystemUI/src/com/android/systemui/controls/ui/ThumbnailBehavior.kt @@ -33,7 +33,7 @@ import com.android.systemui.controls.ui.ControlViewHolder.Companion.MIN_LEVEL   * Supports display of static images on the background of the tile. When marked active, the title   * and subtitle will not be visible. To be used with {@link Thumbnailtemplate} only.   */ -class ThumbnailBehavior : Behavior { +class ThumbnailBehavior(currentUserId: Int) : Behavior {      lateinit var template: ThumbnailTemplate      lateinit var control: Control      lateinit var cvh: ControlViewHolder @@ -42,6 +42,7 @@ class ThumbnailBehavior : Behavior {      private var shadowRadius: Float = 0f      private var shadowColor: Int = 0 +    private val canUseIconPredicate = CanUseIconPredicate(currentUserId)      private val enabled: Boolean          get() = template.isActive() @@ -80,11 +81,16 @@ class ThumbnailBehavior : Behavior {              cvh.status.setShadowLayer(shadowOffsetX, shadowOffsetY, shadowRadius, shadowColor)              cvh.bgExecutor.execute { -                val drawable = template.getThumbnail().loadDrawable(cvh.context) +                val drawable = template.thumbnail +                        ?.takeIf(canUseIconPredicate) +                        ?.loadDrawable(cvh.context)                  cvh.uiExecutor.execute {                      val radius = cvh.context.getResources()                          .getDimensionPixelSize(R.dimen.control_corner_radius).toFloat() -                    clipLayer.setDrawable(CornerDrawable(drawable, radius)) +                    // TODO(b/290037843): Add a placeholder +                    drawable?.let { +                        clipLayer.drawable = CornerDrawable(it, radius) +                    }                      clipLayer.setColorFilter(BlendModeColorFilter(cvh.context.resources                          .getColor(R.color.control_thumbnail_tint), BlendMode.LUMINOSITY))                      cvh.applyRenderInfo(enabled, colorOffset) diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/CanUseIconPredicateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/CanUseIconPredicateTest.kt new file mode 100644 index 000000000000..bfdb9231a9f8 --- /dev/null +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/CanUseIconPredicateTest.kt @@ -0,0 +1,81 @@ +/* + * Copyright (C) 2023 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + *      http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.controls.ui + +import android.content.ContentProvider +import android.graphics.Bitmap +import android.graphics.drawable.Icon +import android.net.Uri +import android.os.UserHandle +import android.testing.AndroidTestingRunner +import androidx.test.filters.SmallTest +import com.android.systemui.SysuiTestCase +import com.google.common.truth.Truth.assertThat +import org.junit.Test +import org.junit.runner.RunWith + +@SmallTest +@RunWith(AndroidTestingRunner::class) +class CanUseIconPredicateTest : SysuiTestCase() { + +    private companion object { +        const val USER_ID_1 = 1 +        const val USER_ID_2 = 2 +    } + +    val underTest: CanUseIconPredicate = CanUseIconPredicate(USER_ID_1) + +    @Test +    fun testReturnsFalseForDifferentUser() { +        val user2Icon = +            Icon.createWithContentUri( +                ContentProvider.createContentUriForUser( +                    Uri.parse("content://test"), +                    UserHandle.of(USER_ID_2) +                ) +            ) + +        assertThat(underTest.invoke(user2Icon)).isFalse() +    } + +    @Test +    fun testReturnsTrueForCorrectUser() { +        val user1Icon = +            Icon.createWithContentUri( +                ContentProvider.createContentUriForUser( +                    Uri.parse("content://test"), +                    UserHandle.of(USER_ID_1) +                ) +            ) + +        assertThat(underTest.invoke(user1Icon)).isTrue() +    } + +    @Test +    fun testReturnsTrueForUriWithoutUser() { +        val uriIcon = Icon.createWithContentUri(Uri.parse("content://test")) + +        assertThat(underTest.invoke(uriIcon)).isTrue() +    } + +    @Test +    fun testReturnsTrueForNonUriIcon() { +        val bitmapIcon = Icon.createWithBitmap(Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)) + +        assertThat(underTest.invoke(bitmapIcon)).isTrue() +    } +} diff --git a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt index d3c465dab438..42f28c8c6043 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/controls/ui/ControlViewHolderTest.kt @@ -66,7 +66,8 @@ class ControlViewHolderTest : SysuiTestCase() {                      FakeExecutor(clock),                      mock(ControlActionCoordinator::class.java),                      mock(ControlsMetricsLogger::class.java), -                    uid = 100 +                    uid = 100, +                    0,              )              val cws = ControlWithState( |