1 /* 2 * Copyright (C) 2022 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.decor 18 import android.content.Context 19 import android.view.DisplayCutout 20 import android.view.Surface 21 import android.view.View 22 import android.view.ViewGroup 23 24 /** 25 * An interface for providing view with a specific functionality. Take an example, if privacy dot 26 * is enabled, there are 4 DecorProviders which are used to provide privacy dot views on top-left, 27 * top-right, bottom-left, bottom-right. 28 */ 29 abstract class DecorProvider { 30 31 /** Id for the view which is created through inflateView() */ 32 abstract val viewId: Int 33 34 /** The number of total aligned bounds */ 35 val numOfAlignedBound: Int 36 get() = alignedBounds.size 37 38 /** The aligned bounds for the view which is created through inflateView() */ 39 abstract val alignedBounds: List<Int> 40 41 /** 42 * Called when res info changed. 43 * Child provider needs to implement it if its view needs to be updated. 44 */ 45 abstract fun onReloadResAndMeasure( 46 view: View, 47 reloadToken: Int, 48 @Surface.Rotation rotation: Int, 49 tintColor: Int, 50 displayUniqueId: String? 51 ) 52 53 /** Inflate view into parent as current rotation */ 54 abstract fun inflateView( 55 context: Context, 56 parent: ViewGroup, 57 @Surface.Rotation rotation: Int, 58 tintColor: Int 59 ): View 60 61 override fun toString() = "${javaClass.simpleName}{alignedBounds=$alignedBounds}" 62 } 63 64 /** 65 * A provider for view shown on corner. 66 */ 67 abstract class CornerDecorProvider : DecorProvider() { 68 /** The first bound which a corner view is aligned based on rotation 0 */ 69 @DisplayCutout.BoundsPosition protected abstract val alignedBound1: Int 70 /** The second bound which a corner view is aligned based on rotation 0 */ 71 @DisplayCutout.BoundsPosition protected abstract val alignedBound2: Int 72 73 override val alignedBounds: List<Int> by lazy { 74 listOf(alignedBound1, alignedBound2) 75 } 76 } 77 78 /** 79 * A provider for view shown on bound. 80 */ 81 abstract class BoundDecorProvider : DecorProvider() { 82 /** The bound which a view is aligned based on rotation 0 */ 83 @DisplayCutout.BoundsPosition protected abstract val alignedBound: Int 84 85 override val alignedBounds: List<Int> by lazy { 86 listOf(alignedBound) 87 } 88 } 89 90 /** 91 * Split list to 2 sub-lists, and return it back as Pair<>. The providers on the first list contains 92 * this alignedBound element. The providers on the second list do not contain this alignedBound 93 * element. 94 */ 95 fun List<DecorProvider>.partitionAlignedBound( 96 @DisplayCutout.BoundsPosition alignedBound: Int 97 ): Pair<List<DecorProvider>, List<DecorProvider>> { 98 return partition { it.alignedBounds.contains(alignedBound) } 99 } 100 101 /** 102 * Get the proper bound from DecorProvider list 103 * Time complexity: O(N), N is the number of providers 104 * 105 * Choose order 106 * 1. Return null if list is empty 107 * 2. If list contains BoundDecorProvider, return its alignedBound[0] because it is a must-have 108 * bound 109 * 3. Return the bound with most DecorProviders 110 */ 111 fun List<DecorProvider>.getProperBound(): Int? { 112 // Return null if list is empty 113 if (isEmpty()) { 114 return null 115 } 116 117 // Choose alignedBounds[0] of BoundDecorProvider if any 118 val singleBoundProvider = firstOrNull { it.numOfAlignedBound == 1 } 119 if (singleBoundProvider != null) { 120 return singleBoundProvider.alignedBounds[0] 121 } 122 123 // Return the bound with most DecorProviders 124 val boundCount = intArrayOf(0, 0, 0, 0) 125 for (provider in this) { 126 for (bound in provider.alignedBounds) { 127 boundCount[bound]++ 128 } 129 } 130 var maxCount = 0 131 var maxCountBound: Int? = null 132 val bounds = arrayOf( 133 // Put top and bottom at first to get the highest priority to be chosen 134 DisplayCutout.BOUNDS_POSITION_TOP, 135 DisplayCutout.BOUNDS_POSITION_BOTTOM, 136 DisplayCutout.BOUNDS_POSITION_LEFT, 137 DisplayCutout.BOUNDS_POSITION_RIGHT 138 ) 139 for (bound in bounds) { 140 if (boundCount[bound] > maxCount) { 141 maxCountBound = bound 142 maxCount = boundCount[bound] 143 } 144 } 145 return maxCountBound 146 } 147