Skip to content

Commit 1538790

Browse files
Anton PotapovAndroid Build Coastguard Worker
authored andcommitted
Add userId check before loading icon in Device Controls
Test: manual with the steps from the bug Test: manual with a normal icon Test: atest CanUseIconPredicate Test: atest ControlViewHolderTest Bug: 272025416 (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:ffa97f42dd9496bb404e01727c923292d05a4466) (cherry picked from https://googleplex-android-review.googlesource.com/q/commit:570aad7c61e4fc8854ed1aba97cbb6e6a491ca6d) Merged-In: I60896a6f53307f0e97a9223b599a2891c6c0c08d Change-Id: I60896a6f53307f0e97a9223b599a2891c6c0c08d
1 parent b5adfd3 commit 1538790

11 files changed

Lines changed: 173 additions & 40 deletions

File tree

packages/SystemUI/src/com/android/systemui/controls/management/ControlAdapter.kt

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ import androidx.core.view.accessibility.AccessibilityNodeInfoCompat
3838
import androidx.recyclerview.widget.RecyclerView
3939
import com.android.systemui.R
4040
import com.android.systemui.controls.ControlInterface
41+
import com.android.systemui.controls.ui.CanUseIconPredicate
4142
import com.android.systemui.controls.ui.RenderInfo
4243

4344
private typealias ModelFavoriteChanger = (String, Boolean) -> Unit
@@ -51,7 +52,8 @@ private typealias ModelFavoriteChanger = (String, Boolean) -> Unit
5152
* @property elevation elevation of each control view
5253
*/
5354
class ControlAdapter(
54-
private val elevation: Float
55+
private val elevation: Float,
56+
private val currentUserId: Int,
5557
) : RecyclerView.Adapter<Holder>() {
5658

5759
companion object {
@@ -107,7 +109,8 @@ class ControlAdapter(
107109
background = parent.context.getDrawable(
108110
R.drawable.control_background_ripple)
109111
},
110-
model?.moveHelper // Indicates that position information is needed
112+
currentUserId,
113+
model?.moveHelper, // Indicates that position information is needed
111114
) { id, favorite ->
112115
model?.changeFavoriteStatus(id, favorite)
113116
}
@@ -212,8 +215,9 @@ private class ZoneHolder(view: View) : Holder(view) {
212215
*/
213216
internal class ControlHolder(
214217
view: View,
218+
currentUserId: Int,
215219
val moveHelper: ControlsModel.MoveHelper?,
216-
val favoriteCallback: ModelFavoriteChanger
220+
val favoriteCallback: ModelFavoriteChanger,
217221
) : Holder(view) {
218222
private val favoriteStateDescription =
219223
itemView.context.getString(R.string.accessibility_control_favorite)
@@ -228,6 +232,7 @@ internal class ControlHolder(
228232
visibility = View.VISIBLE
229233
}
230234

235+
private val canUseIconPredicate = CanUseIconPredicate(currentUserId)
231236
private val accessibilityDelegate = ControlHolderAccessibilityDelegate(
232237
this::stateDescription,
233238
this::getLayoutPosition,
@@ -287,7 +292,9 @@ internal class ControlHolder(
287292
val fg = context.getResources().getColorStateList(ri.foreground, context.getTheme())
288293

289294
icon.imageTintList = null
290-
ci.customIcon?.let {
295+
ci.customIcon
296+
?.takeIf(canUseIconPredicate)
297+
?.let {
291298
icon.setImageIcon(it)
292299
} ?: run {
293300
icon.setImageDrawable(ri.icon)

packages/SystemUI/src/com/android/systemui/controls/management/ControlsEditingActivity.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ open class ControlsEditingActivity @Inject constructor(
205205
val elevation = resources.getFloat(R.dimen.control_card_elevation)
206206
val recyclerView = requireViewById<RecyclerView>(R.id.list)
207207
recyclerView.alpha = 0.0f
208-
val adapter = ControlAdapter(elevation).apply {
208+
val adapter = ControlAdapter(elevation, userTracker.userId).apply {
209209
registerAdapterDataObserver(object : RecyclerView.AdapterDataObserver() {
210210
var hasAnimated = false
211211
override fun onChanged() {

packages/SystemUI/src/com/android/systemui/controls/management/ControlsFavoritingActivity.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ open class ControlsFavoritingActivity @Inject constructor(
175175
}
176176

177177
executor.execute {
178-
structurePager.adapter = StructureAdapter(listOfStructures)
178+
structurePager.adapter = StructureAdapter(listOfStructures, userTracker.userId)
179179
structurePager.setCurrentItem(structureIndex)
180180
if (error) {
181181
statusText.text = resources.getString(R.string.controls_favorite_load_error,
@@ -221,7 +221,7 @@ open class ControlsFavoritingActivity @Inject constructor(
221221
structurePager.alpha = 0.0f
222222
pageIndicator.alpha = 0.0f
223223
structurePager.apply {
224-
adapter = StructureAdapter(emptyList())
224+
adapter = StructureAdapter(emptyList(), userTracker.userId)
225225
registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
226226
override fun onPageSelected(position: Int) {
227227
super.onPageSelected(position)

packages/SystemUI/src/com/android/systemui/controls/management/StructureAdapter.kt

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,15 @@ import androidx.recyclerview.widget.RecyclerView
2424
import com.android.systemui.R
2525

2626
class StructureAdapter(
27-
private val models: List<StructureContainer>
27+
private val models: List<StructureContainer>,
28+
private val currentUserId: Int,
2829
) : RecyclerView.Adapter<StructureAdapter.StructureHolder>() {
2930

3031
override fun onCreateViewHolder(parent: ViewGroup, p1: Int): StructureHolder {
3132
val layoutInflater = LayoutInflater.from(parent.context)
3233
return StructureHolder(
33-
layoutInflater.inflate(R.layout.controls_structure_page, parent, false)
34+
layoutInflater.inflate(R.layout.controls_structure_page, parent, false),
35+
currentUserId,
3436
)
3537
}
3638

@@ -40,15 +42,16 @@ class StructureAdapter(
4042
holder.bind(models[index].model)
4143
}
4244

43-
class StructureHolder(view: View) : RecyclerView.ViewHolder(view) {
45+
class StructureHolder(view: View, currentUserId: Int) :
46+
RecyclerView.ViewHolder(view) {
4447

4548
private val recyclerView: RecyclerView
4649
private val controlAdapter: ControlAdapter
4750

4851
init {
4952
recyclerView = itemView.requireViewById<RecyclerView>(R.id.listAll)
5053
val elevation = itemView.context.resources.getFloat(R.dimen.control_card_elevation)
51-
controlAdapter = ControlAdapter(elevation)
54+
controlAdapter = ControlAdapter(elevation, currentUserId)
5255
setUpRecyclerView()
5356
}
5457

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* Copyright (C) 2023 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.android.systemui.controls.ui
18+
19+
import android.content.ContentProvider
20+
import android.graphics.drawable.Icon
21+
22+
class CanUseIconPredicate(private val currentUserId: Int) : (Icon) -> Boolean {
23+
24+
override fun invoke(icon: Icon): Boolean =
25+
if (icon.type == Icon.TYPE_URI || icon.type == Icon.TYPE_URI_ADAPTIVE_BITMAP) {
26+
ContentProvider.getUserIdFromUri(icon.uri, currentUserId) == currentUserId
27+
} else {
28+
true
29+
}
30+
}

packages/SystemUI/src/com/android/systemui/controls/ui/ControlViewHolder.kt

Lines changed: 27 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,8 @@ class ControlViewHolder(
6868
val bgExecutor: DelayableExecutor,
6969
val controlActionCoordinator: ControlActionCoordinator,
7070
val controlsMetricsLogger: ControlsMetricsLogger,
71-
val uid: Int
71+
val uid: Int,
72+
val currentUserId: Int,
7273
) {
7374

7475
companion object {
@@ -85,29 +86,9 @@ class ControlViewHolder(
8586
private val ATTR_DISABLED = intArrayOf(-android.R.attr.state_enabled)
8687
const val MIN_LEVEL = 0
8788
const val MAX_LEVEL = 10000
88-
89-
fun findBehaviorClass(
90-
status: Int,
91-
template: ControlTemplate,
92-
deviceType: Int
93-
): Supplier<out Behavior> {
94-
return when {
95-
status != Control.STATUS_OK -> Supplier { StatusBehavior() }
96-
template == ControlTemplate.NO_TEMPLATE -> Supplier { TouchBehavior() }
97-
template is ThumbnailTemplate -> Supplier { ThumbnailBehavior() }
98-
99-
// Required for legacy support, or where cameras do not use the new template
100-
deviceType == DeviceTypes.TYPE_CAMERA -> Supplier { TouchBehavior() }
101-
template is ToggleTemplate -> Supplier { ToggleBehavior() }
102-
template is StatelessTemplate -> Supplier { TouchBehavior() }
103-
template is ToggleRangeTemplate -> Supplier { ToggleRangeBehavior() }
104-
template is RangeTemplate -> Supplier { ToggleRangeBehavior() }
105-
template is TemperatureControlTemplate -> Supplier { TemperatureControlBehavior() }
106-
else -> Supplier { DefaultBehavior() }
107-
}
108-
}
10989
}
11090

91+
private val canUseIconPredicate = CanUseIconPredicate(currentUserId)
11192
private val toggleBackgroundIntensity: Float = layout.context.resources
11293
.getFraction(R.fraction.controls_toggle_bg_intensity, 1, 1)
11394
private var stateAnimator: ValueAnimator? = null
@@ -147,6 +128,27 @@ class ControlViewHolder(
147128
status.setSelected(true)
148129
}
149130

131+
fun findBehaviorClass(
132+
status: Int,
133+
template: ControlTemplate,
134+
deviceType: Int
135+
): Supplier<out Behavior> {
136+
return when {
137+
status != Control.STATUS_OK -> Supplier { StatusBehavior() }
138+
template == ControlTemplate.NO_TEMPLATE -> Supplier { TouchBehavior() }
139+
template is ThumbnailTemplate -> Supplier { ThumbnailBehavior(currentUserId) }
140+
141+
// Required for legacy support, or where cameras do not use the new template
142+
deviceType == DeviceTypes.TYPE_CAMERA -> Supplier { TouchBehavior() }
143+
template is ToggleTemplate -> Supplier { ToggleBehavior() }
144+
template is StatelessTemplate -> Supplier { TouchBehavior() }
145+
template is ToggleRangeTemplate -> Supplier { ToggleRangeBehavior() }
146+
template is RangeTemplate -> Supplier { ToggleRangeBehavior() }
147+
template is TemperatureControlTemplate -> Supplier { TemperatureControlBehavior() }
148+
else -> Supplier { DefaultBehavior() }
149+
}
150+
}
151+
150152
fun bindData(cws: ControlWithState, isLocked: Boolean) {
151153
// If an interaction is in progress, the update may visually interfere with the action the
152154
// action the user wants to make. Don't apply the update, and instead assume a new update
@@ -473,7 +475,9 @@ class ControlViewHolder(
473475

474476
status.setTextColor(color)
475477

476-
control?.getCustomIcon()?.let {
478+
control?.customIcon
479+
?.takeIf(canUseIconPredicate)
480+
?.let {
477481
icon.setImageIcon(it)
478482
icon.imageTintList = it.tintList
479483
} ?: run {

packages/SystemUI/src/com/android/systemui/controls/ui/ControlsUiControllerImpl.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -685,7 +685,8 @@ class ControlsUiControllerImpl @Inject constructor (
685685
bgExecutor,
686686
controlActionCoordinator,
687687
controlsMetricsLogger,
688-
selected.uid
688+
selected.uid,
689+
controlsController.get().currentUserId,
689690
)
690691
cvh.bindData(it, false /* isLocked, will be ignored on initial load */)
691692
controlViewsById.put(key, cvh)

packages/SystemUI/src/com/android/systemui/controls/ui/TemperatureControlBehavior.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ class TemperatureControlBehavior : Behavior {
6363
// interactions (touch, range)
6464
subBehavior = cvh.bindBehavior(
6565
subBehavior,
66-
ControlViewHolder.findBehaviorClass(
66+
cvh.findBehaviorClass(
6767
control.status,
6868
subTemplate,
6969
control.deviceType

packages/SystemUI/src/com/android/systemui/controls/ui/ThumbnailBehavior.kt

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ import com.android.systemui.controls.ui.ControlViewHolder.Companion.MIN_LEVEL
3333
* Supports display of static images on the background of the tile. When marked active, the title
3434
* and subtitle will not be visible. To be used with {@link Thumbnailtemplate} only.
3535
*/
36-
class ThumbnailBehavior : Behavior {
36+
class ThumbnailBehavior(currentUserId: Int) : Behavior {
3737
lateinit var template: ThumbnailTemplate
3838
lateinit var control: Control
3939
lateinit var cvh: ControlViewHolder
@@ -42,6 +42,7 @@ class ThumbnailBehavior : Behavior {
4242
private var shadowRadius: Float = 0f
4343
private var shadowColor: Int = 0
4444

45+
private val canUseIconPredicate = CanUseIconPredicate(currentUserId)
4546
private val enabled: Boolean
4647
get() = template.isActive()
4748

@@ -80,11 +81,16 @@ class ThumbnailBehavior : Behavior {
8081
cvh.status.setShadowLayer(shadowOffsetX, shadowOffsetY, shadowRadius, shadowColor)
8182

8283
cvh.bgExecutor.execute {
83-
val drawable = template.getThumbnail().loadDrawable(cvh.context)
84+
val drawable = template.thumbnail
85+
?.takeIf(canUseIconPredicate)
86+
?.loadDrawable(cvh.context)
8487
cvh.uiExecutor.execute {
8588
val radius = cvh.context.getResources()
8689
.getDimensionPixelSize(R.dimen.control_corner_radius).toFloat()
87-
clipLayer.setDrawable(CornerDrawable(drawable, radius))
90+
// TODO(b/290037843): Add a placeholder
91+
drawable?.let {
92+
clipLayer.drawable = CornerDrawable(it, radius)
93+
}
8894
clipLayer.setColorFilter(BlendModeColorFilter(cvh.context.resources
8995
.getColor(R.color.control_thumbnail_tint), BlendMode.LUMINOSITY))
9096
cvh.applyRenderInfo(enabled, colorOffset)
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Copyright (C) 2023 The Android Open Source Project
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.android.systemui.controls.ui
18+
19+
import android.content.ContentProvider
20+
import android.graphics.Bitmap
21+
import android.graphics.drawable.Icon
22+
import android.net.Uri
23+
import android.os.UserHandle
24+
import android.testing.AndroidTestingRunner
25+
import androidx.test.filters.SmallTest
26+
import com.android.systemui.SysuiTestCase
27+
import com.google.common.truth.Truth.assertThat
28+
import org.junit.Test
29+
import org.junit.runner.RunWith
30+
31+
@SmallTest
32+
@RunWith(AndroidTestingRunner::class)
33+
class CanUseIconPredicateTest : SysuiTestCase() {
34+
35+
private companion object {
36+
const val USER_ID_1 = 1
37+
const val USER_ID_2 = 2
38+
}
39+
40+
val underTest: CanUseIconPredicate = CanUseIconPredicate(USER_ID_1)
41+
42+
@Test
43+
fun testReturnsFalseForDifferentUser() {
44+
val user2Icon =
45+
Icon.createWithContentUri(
46+
ContentProvider.createContentUriForUser(
47+
Uri.parse("content://test"),
48+
UserHandle.of(USER_ID_2)
49+
)
50+
)
51+
52+
assertThat(underTest.invoke(user2Icon)).isFalse()
53+
}
54+
55+
@Test
56+
fun testReturnsTrueForCorrectUser() {
57+
val user1Icon =
58+
Icon.createWithContentUri(
59+
ContentProvider.createContentUriForUser(
60+
Uri.parse("content://test"),
61+
UserHandle.of(USER_ID_1)
62+
)
63+
)
64+
65+
assertThat(underTest.invoke(user1Icon)).isTrue()
66+
}
67+
68+
@Test
69+
fun testReturnsTrueForUriWithoutUser() {
70+
val uriIcon = Icon.createWithContentUri(Uri.parse("content://test"))
71+
72+
assertThat(underTest.invoke(uriIcon)).isTrue()
73+
}
74+
75+
@Test
76+
fun testReturnsTrueForNonUriIcon() {
77+
val bitmapIcon = Icon.createWithBitmap(Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888))
78+
79+
assertThat(underTest.invoke(bitmapIcon)).isTrue()
80+
}
81+
}

0 commit comments

Comments
 (0)