• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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.permissioncontroller.permission.ui.handheld.v31
18 
19 import android.content.Context
20 import android.icu.util.Calendar
21 import android.os.Build
22 import android.provider.DeviceConfig
23 import android.text.format.DateFormat.getMediumDateFormat
24 import android.text.format.DateFormat.getTimeFormat
25 import android.util.Pair
26 import androidx.annotation.RequiresApi
27 import com.android.modules.utils.build.SdkLevel
28 import com.android.permissioncontroller.R
29 import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage.GroupUsage
30 import com.android.permissioncontroller.permission.utils.StringUtils
31 import java.util.Locale
32 
33 /** Whether to show the Permissions Hub.  */
34 private const val PROPERTY_PERMISSIONS_HUB_2_ENABLED = "permissions_hub_2_enabled"
35 
36 /** Whether to show the mic and camera icons.  */
37 const val PROPERTY_CAMERA_MIC_ICONS_ENABLED = "camera_mic_icons_enabled"
38 
39 /** Whether to show the location indicators. */
40 const val PROPERTY_LOCATION_INDICATORS_ENABLED = "location_indicators_enabled"
41 
42 /* Whether location accuracy feature is enabled */
43 const val PROPERTY_LOCATION_ACCURACY_ENABLED = "location_accuracy_enabled"
44 
45 /** Whether subattribution is enabled in Permissions Hub. */
46 const val PROPERTY_PERMISSIONS_HUB_SUBATTRIBUTION_ENABLED = "permissions_hub_subattribution_enabled"
47 
48 /** Whether to show 7-day toggle in privacy hub.  */
49 private const val PRIVACY_DASHBOARD_7_DAY_TOGGLE = "privacy_dashboard_7_day_toggle"
50 
51 /* Default location precision */
52 const val PROPERTY_LOCATION_PRECISION = "location_precision"
53 
54 const val SECONDS = 1
55 const val MINUTES = 2
56 const val HOURS = 3
57 const val DAYS = 4
58 
59 /**
60  * Whether the Permissions Hub 2 flag is enabled
61  *
62  * @return whether the flag is enabled
63  */
isPermissionsHub2FlagEnablednull64 fun isPermissionsHub2FlagEnabled(): Boolean {
65     return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
66         PROPERTY_PERMISSIONS_HUB_2_ENABLED, false)
67 }
68 /**
69  * Whether to show the Permissions Dashboard
70  *
71  * @return whether to show the Permissions Dashboard.
72  */
shouldShowPermissionsDashboardnull73 fun shouldShowPermissionsDashboard(): Boolean {
74     return isPermissionsHub2FlagEnabled()
75 }
76 
77 /**
78  * Whether we should enable the 7-day toggle in privacy dashboard
79  *
80  * @return whether the flag is enabled
81  */
is7DayToggleEnablednull82 fun is7DayToggleEnabled(): Boolean {
83     return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
84             PRIVACY_DASHBOARD_7_DAY_TOGGLE, false)
85 }
86 
87 /**
88  * Whether the Permissions Hub Subattribution flag is enabled
89  *
90  * @return whether the flag is enabled
91  */
isPermissionsHubSubattributionFlagEnablednull92 fun isPermissionsHubSubattributionFlagEnabled(): Boolean {
93     return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
94             PROPERTY_PERMISSIONS_HUB_SUBATTRIBUTION_ENABLED, true)
95 }
96 /**
97  * Whether to show the subattribution in the Permissions Dashboard
98  *
99  * @return whether to show subattribution in the Permissions Dashboard.
100  */
shouldShowSubattributionInPermissionsDashboardnull101 fun shouldShowSubattributionInPermissionsDashboard(): Boolean {
102     return SdkLevel.isAtLeastS() && isPermissionsHubSubattributionFlagEnabled()
103 }
104 
105 /**
106  * Whether the Camera and Mic Icons are enabled by flag.
107  *
108  * @return whether the Camera and Mic Icons are enabled.
109  */
isCameraMicIconsFlagEnablednull110 fun isCameraMicIconsFlagEnabled(): Boolean {
111     return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
112         PROPERTY_CAMERA_MIC_ICONS_ENABLED, true)
113 }
114 
115 /**
116  * Whether to show Camera and Mic Icons. They should be shown if the permission hub, or the icons
117  * specifically, are enabled.
118  *
119  * @return whether to show the icons.
120  */
shouldShowCameraMicIndicatorsnull121 fun shouldShowCameraMicIndicators(): Boolean {
122     return isCameraMicIconsFlagEnabled() || isPermissionsHub2FlagEnabled()
123 }
124 
125 /**
126  * Whether the location indicators are enabled by flag.
127  *
128  * @return whether the location indicators are enabled by flag.
129  */
isLocationIndicatorsFlagEnablednull130 fun isLocationIndicatorsFlagEnabled(): Boolean {
131     return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
132             PROPERTY_LOCATION_INDICATORS_ENABLED, false)
133 }
134 
135 /**
136  * Whether to show the location indicators. The location indicators are enable if the
137  * permission hub, or location indicator specifically are enabled.
138  */
shouldShowLocationIndicatorsnull139 fun shouldShowLocationIndicators(): Boolean {
140     return isLocationIndicatorsFlagEnabled() || isPermissionsHub2FlagEnabled()
141 }
142 
143 /**
144  * Whether the location accuracy feature is enabled
145  */
isLocationAccuracyEnablednull146 fun isLocationAccuracyEnabled(): Boolean {
147     return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
148         PROPERTY_LOCATION_ACCURACY_ENABLED, true)
149 }
150 
151 /**
152  * Default state of location precision
153  * true: default is FINE.
154  * false: default is COARSE.
155  */
getDefaultPrecisionnull156 fun getDefaultPrecision(): Boolean {
157     return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
158             PROPERTY_LOCATION_PRECISION, true)
159 }
160 
161 /**
162  * Build a string representing the given time if it happened on the current day and the date
163  * otherwise.
164  *
165  * @param context the context.
166  * @param lastAccessTime the time in milliseconds.
167  *
168  * @return a string representing the time or date of the given time or null if the time is 0.
169  */
getAbsoluteTimeStringnull170 fun getAbsoluteTimeString(context: Context, lastAccessTime: Long): String? {
171     if (lastAccessTime == 0L) {
172         return null
173     }
174     return if (isToday(lastAccessTime)) {
175         getTimeFormat(context).format(lastAccessTime)
176     } else {
177         getMediumDateFormat(context).format(lastAccessTime)
178     }
179 }
180 
181 /**
182  * Build a string representing the time of the most recent permission usage if it happened on
183  * the current day and the date otherwise.
184  *
185  * @param context the context.
186  * @param groupUsage the permission usage.
187  *
188  * @return a string representing the time or date of the most recent usage or null if there are
189  * no usages.
190  */
191 @RequiresApi(Build.VERSION_CODES.S)
getAbsoluteLastUsageStringnull192 fun getAbsoluteLastUsageString(context: Context, groupUsage: GroupUsage?): String? {
193     return if (groupUsage == null) {
194         null
195     } else getAbsoluteTimeString(context, groupUsage.lastAccessTime)
196 }
197 
198 /**
199  * Build a string representing the duration of a permission usage.
200  *
201  * @return a string representing the duration of this app's usage or null if there are no
202  * usages.
203  */
204 @RequiresApi(Build.VERSION_CODES.S)
getUsageDurationStringnull205 fun getUsageDurationString(context: Context, groupUsage: GroupUsage?): String? {
206     return if (groupUsage == null) {
207         null
208     } else getTimeDiffStr(context, groupUsage.accessDuration)
209 }
210 
211 /**
212  * Build a string representing the number of milliseconds passed in.  It rounds to the nearest
213  * unit.  For example, given a duration of 3500 and an English locale, this can return
214  * "3 seconds".
215  * @param context The context.
216  * @param duration The number of milliseconds.
217  * @return a string representing the given number of milliseconds.
218  */
getTimeDiffStrnull219 fun getTimeDiffStr(context: Context, duration: Long): String {
220     val timeDiffAndUnit = calculateTimeDiffAndUnit(duration)
221     return when (timeDiffAndUnit.second) {
222         SECONDS -> StringUtils.getIcuPluralsString(context,
223             R.string.seconds, timeDiffAndUnit.first.toInt())
224         MINUTES -> StringUtils.getIcuPluralsString(context,
225             R.string.minutes, timeDiffAndUnit.first.toInt())
226         HOURS -> StringUtils.getIcuPluralsString(context,
227             R.string.hours, timeDiffAndUnit.first.toInt())
228         else -> StringUtils.getIcuPluralsString(context,
229             R.string.days, timeDiffAndUnit.first.toInt())
230     }
231 }
232 
233 /**
234  * Build a string representing the duration used of milliseconds passed in.
235  * @return a string representing the duration used in the nearest unit. ex: Used for 3 mins
236  */
getDurationUsedStrnull237 fun getDurationUsedStr(context: Context, duration: Long): String {
238     val timeDiffAndUnit = calculateTimeDiffAndUnit(duration)
239     return when (timeDiffAndUnit.second) {
240         SECONDS -> StringUtils.getIcuPluralsString(context,
241             R.string.duration_used_seconds, timeDiffAndUnit.first.toInt())
242         MINUTES -> StringUtils.getIcuPluralsString(context,
243             R.string.duration_used_minutes, timeDiffAndUnit.first.toInt())
244         HOURS -> StringUtils.getIcuPluralsString(context,
245             R.string.duration_used_hours, timeDiffAndUnit.first.toInt())
246         else -> StringUtils.getIcuPluralsString(context,
247             R.string.duration_used_days, timeDiffAndUnit.first.toInt())
248     }
249 }
250 
251 /**
252  * Given the duration in milliseconds, calculate the time of that duration in the nearest unit.
253  * @return a Pair of the <duration in the nearest unit, the nearest unit>
254  */
calculateTimeDiffAndUnitnull255 fun calculateTimeDiffAndUnit(duration: Long): Pair<Long, Int> {
256     val seconds = Math.max(1, duration / 1000)
257 
258     if (seconds < 60) {
259         return Pair.create(seconds, SECONDS)
260     }
261     val minutes = seconds / 60
262     if (minutes < 60) {
263         return Pair.create(minutes, MINUTES)
264     }
265     val hours = minutes / 60
266     if (hours < 24) {
267         return Pair.create(hours, HOURS)
268     }
269     val days = hours / 24
270     return Pair.create(days, DAYS)
271 }
272 
273 /**
274  * Check whether the given time (in milliseconds) is in the current day.
275  *
276  * @param time the time in milliseconds
277  *
278  * @return whether the given time is in the current day.
279  */
isTodaynull280 private fun isToday(time: Long): Boolean {
281     val today: Calendar = Calendar.getInstance(Locale.getDefault())
282     today.setTimeInMillis(System.currentTimeMillis())
283     today.set(Calendar.HOUR_OF_DAY, 0)
284     today.set(Calendar.MINUTE, 0)
285     today.set(Calendar.SECOND, 0)
286     today.set(Calendar.MILLISECOND, 0)
287     val date: Calendar = Calendar.getInstance(Locale.getDefault())
288     date.setTimeInMillis(time)
289     return !date.before(today)
290 }
291