blob: 3953139d43fdba9c6f20e7b74668fc2028e62ac6 [file] [log] [blame]
/*
* Copyright (C) 2018 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.privacy
import android.app.ActivityManager
import android.app.AppOpsManager
import android.content.Context
import android.graphics.Color
import android.os.UserHandle
import android.os.UserManager
import android.util.AttributeSet
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import com.android.systemui.Dependency
import com.android.systemui.R
import com.android.systemui.appops.AppOpItem
import com.android.systemui.appops.AppOpsController
class OngoingPrivacyChip @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttrs: Int = 0,
defStyleRes: Int = 0
) : LinearLayout(context, attrs, defStyleAttrs, defStyleRes) {
companion object {
val OPS = intArrayOf(AppOpsManager.OP_CAMERA,
AppOpsManager.OP_RECORD_AUDIO,
AppOpsManager.OP_COARSE_LOCATION,
AppOpsManager.OP_FINE_LOCATION)
}
private lateinit var appName: TextView
private lateinit var iconsContainer: LinearLayout
private var privacyList = emptyList<PrivacyItem>()
private val appOpsController = Dependency.get(AppOpsController::class.java)
private val userManager = context.getSystemService(UserManager::class.java)
private val currentUser = ActivityManager.getCurrentUser()
private val currentUserIds = userManager.getProfiles(currentUser).map { it.id }
private var listening = false
var builder = PrivacyDialogBuilder(context, privacyList)
private val callback = object : AppOpsController.Callback {
override fun onActiveStateChanged(
code: Int,
uid: Int,
packageName: String,
active: Boolean
) {
val userId = UserHandle.getUserId(uid)
if (userId in currentUserIds) {
updatePrivacyList()
}
}
}
override fun onFinishInflate() {
super.onFinishInflate()
appName = findViewById(R.id.app_name)
iconsContainer = findViewById(R.id.icons_container)
}
fun setListening(listen: Boolean) {
if (listening == listen) return
listening = listen
if (listening) {
appOpsController.addCallback(OPS, callback)
updatePrivacyList()
} else {
appOpsController.removeCallback(OPS, callback)
}
}
private fun updatePrivacyList() {
privacyList = currentUserIds.flatMap { appOpsController.getActiveAppOpsForUser(it) }
.mapNotNull { toPrivacyItem(it) }
builder = PrivacyDialogBuilder(context, privacyList)
updateView()
}
private fun toPrivacyItem(appOpItem: AppOpItem): PrivacyItem? {
val type: PrivacyType = when (appOpItem.code) {
AppOpsManager.OP_CAMERA -> PrivacyType.TYPE_CAMERA
AppOpsManager.OP_COARSE_LOCATION -> PrivacyType.TYPE_LOCATION
AppOpsManager.OP_FINE_LOCATION -> PrivacyType.TYPE_LOCATION
AppOpsManager.OP_RECORD_AUDIO -> PrivacyType.TYPE_MICROPHONE
else -> return null
}
val app = PrivacyApplication(appOpItem.packageName, context)
return PrivacyItem(type, app, appOpItem.timeStarted)
}
// Should only be called if the builder icons or app changed
private fun updateView() {
fun setIcons(dialogBuilder: PrivacyDialogBuilder, iconsContainer: ViewGroup) {
iconsContainer.removeAllViews()
dialogBuilder.generateIcons().forEach {
it.mutate()
it.setTint(Color.WHITE)
iconsContainer.addView(ImageView(context).apply {
setImageDrawable(it)
maxHeight = this@OngoingPrivacyChip.height
})
}
}
if (privacyList.isEmpty()) {
visibility = GONE
return
} else {
generateContentDescription()
visibility = VISIBLE
setIcons(builder, iconsContainer)
appName.visibility = GONE
builder.app?.let {
appName.apply {
setText(it.applicationName)
setTextColor(Color.WHITE)
visibility = VISIBLE
}
}
}
requestLayout()
}
private fun generateContentDescription() {
val typesText = builder.generateTypesText()
if (builder.app != null) {
contentDescription = context.getString(R.string.ongoing_privacy_chip_content_single_app,
builder.app?.applicationName, typesText)
} else {
contentDescription = context.getString(
R.string.ongoing_privacy_chip_content_multiple_apps, typesText)
}
}
}