1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 * except in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the 10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11 * KIND, either express or implied. See the License for the specific language governing 12 * permissions and limitations under the License. 13 */ 14 15 package com.android.systemui.privacy 16 17 import android.content.Context 18 import android.content.pm.ActivityInfo 19 import android.content.res.Configuration 20 import android.util.AttributeSet 21 import android.view.Gravity.CENTER_VERTICAL 22 import android.view.Gravity.END 23 import android.view.ViewGroup 24 import android.view.ViewGroup.LayoutParams.MATCH_PARENT 25 import android.view.ViewGroup.LayoutParams.WRAP_CONTENT 26 import android.widget.ImageView 27 import android.widget.LinearLayout 28 import com.android.settingslib.Utils 29 import com.android.systemui.R 30 import com.android.systemui.animation.view.LaunchableFrameLayout 31 import com.android.systemui.statusbar.events.BackgroundAnimatableView 32 33 class OngoingPrivacyChip @JvmOverloads constructor( 34 context: Context, 35 attrs: AttributeSet? = null, 36 defStyleAttrs: Int = 0, 37 defStyleRes: Int = 0 38 ) : LaunchableFrameLayout(context, attrs, defStyleAttrs, defStyleRes), BackgroundAnimatableView { 39 40 private var configuration: Configuration 41 private var iconMargin = 0 42 private var iconSize = 0 43 private var iconColor = 0 44 45 private val iconsContainer: LinearLayout 46 47 var privacyList = emptyList<PrivacyItem>() 48 set(value) { 49 field = value 50 updateView(PrivacyChipBuilder(context, field)) 51 } 52 53 init { 54 inflate(context, R.layout.ongoing_privacy_chip, this) 55 id = R.id.privacy_chip 56 layoutParams = LayoutParams(WRAP_CONTENT, MATCH_PARENT, CENTER_VERTICAL or END) 57 clipChildren = true 58 clipToPadding = true 59 iconsContainer = requireViewById(R.id.icons_container) 60 configuration = Configuration(context.resources.configuration) 61 updateResources() 62 } 63 64 /** 65 * When animating as a chip in the status bar, we want to animate the width for the container 66 * of the privacy items. We have to subtract our own top and left offset because the bounds 67 * come to us as absolute on-screen bounds, and `iconsContainer` is laid out relative to the 68 * frame layout's bounds. 69 */ 70 override fun setBoundsForAnimation(l: Int, t: Int, r: Int, b: Int) { 71 iconsContainer.setLeftTopRightBottom(l - left, t - top, r - left, b - top) 72 } 73 74 // Should only be called if the builder icons or app changed 75 private fun updateView(builder: PrivacyChipBuilder) { 76 fun setIcons(chipBuilder: PrivacyChipBuilder, iconsContainer: ViewGroup) { 77 iconsContainer.removeAllViews() 78 chipBuilder.generateIcons().forEachIndexed { i, it -> 79 it.mutate() 80 it.setTint(iconColor) 81 val image = ImageView(context).apply { 82 setImageDrawable(it) 83 scaleType = ImageView.ScaleType.CENTER_INSIDE 84 } 85 iconsContainer.addView(image, iconSize, iconSize) 86 if (i != 0) { 87 val lp = image.layoutParams as MarginLayoutParams 88 lp.marginStart = iconMargin 89 image.layoutParams = lp 90 } 91 } 92 } 93 94 if (!privacyList.isEmpty()) { 95 generateContentDescription(builder) 96 setIcons(builder, iconsContainer) 97 } else { 98 iconsContainer.removeAllViews() 99 } 100 requestLayout() 101 } 102 103 private fun generateContentDescription(builder: PrivacyChipBuilder) { 104 val typesText = builder.joinTypes() 105 contentDescription = context.getString( 106 R.string.ongoing_privacy_chip_content_multiple_apps, typesText) 107 } 108 109 override fun onConfigurationChanged(newConfig: Configuration?) { 110 super.onConfigurationChanged(newConfig) 111 if (newConfig != null) { 112 val diff = newConfig.diff(configuration) 113 configuration.setTo(newConfig) 114 if (diff.and(ActivityInfo.CONFIG_DENSITY.or(ActivityInfo.CONFIG_FONT_SCALE)) != 0) { 115 updateResources() 116 } 117 } 118 } 119 120 private fun updateResources() { 121 iconMargin = context.resources 122 .getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_margin) 123 iconSize = context.resources 124 .getDimensionPixelSize(R.dimen.ongoing_appops_chip_icon_size) 125 iconColor = 126 Utils.getColorAttrDefaultColor(context, com.android.internal.R.attr.colorPrimary) 127 128 val height = context.resources 129 .getDimensionPixelSize(R.dimen.ongoing_appops_chip_height) 130 val padding = context.resources 131 .getDimensionPixelSize(R.dimen.ongoing_appops_chip_side_padding) 132 iconsContainer.layoutParams.height = height 133 iconsContainer.setPaddingRelative(padding, 0, padding, 0) 134 iconsContainer.background = context.getDrawable(R.drawable.statusbar_privacy_chip_bg) 135 } 136 }