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.text.format.DateFormat.getMediumDateFormat
23 import android.text.format.DateFormat.getTimeFormat
24 import android.util.Pair
25 import androidx.annotation.RequiresApi
26 import com.android.modules.utils.build.SdkLevel
27 import com.android.permissioncontroller.R
28 import com.android.permissioncontroller.permission.model.v31.AppPermissionUsage.GroupUsage
29 import com.android.permissioncontroller.permission.utils.StringUtils
30 import java.util.Locale
31
32 const val SECONDS = 1
33 const val MINUTES = 2
34 const val HOURS = 3
35 const val DAYS = 4
36
37 /**
38 * Whether to show the subattribution in the Permissions Dashboard
39 *
40 * @return whether to show subattribution in the Permissions Dashboard.
41 */
shouldShowSubattributionInPermissionsDashboardnull42 fun shouldShowSubattributionInPermissionsDashboard(): Boolean {
43 return SdkLevel.isAtLeastS()
44 }
45
46 /**
47 * Build a string representing the given time if it happened on the current day and the date
48 * otherwise.
49 *
50 * @param context the context.
51 * @param lastAccessTime the time in milliseconds.
52 * @return a string representing the time or date of the given time or null if the time is 0.
53 */
getAbsoluteTimeStringnull54 fun getAbsoluteTimeString(context: Context, lastAccessTime: Long): String? {
55 if (lastAccessTime == 0L) {
56 return null
57 }
58 return if (isToday(lastAccessTime)) {
59 getTimeFormat(context).format(lastAccessTime)
60 } else {
61 getMediumDateFormat(context).format(lastAccessTime)
62 }
63 }
64
65 /**
66 * Build a string representing the time of the most recent permission usage if it happened on the
67 * current day and the date otherwise.
68 *
69 * @param context the context.
70 * @param groupUsage the permission usage.
71 * @return a string representing the time or date of the most recent usage or null if there are no
72 * usages.
73 */
74 @RequiresApi(Build.VERSION_CODES.S)
getAbsoluteLastUsageStringnull75 fun getAbsoluteLastUsageString(context: Context, groupUsage: GroupUsage?): String? {
76 return if (groupUsage == null) {
77 null
78 } else getAbsoluteTimeString(context, groupUsage.lastAccessTime)
79 }
80
81 /**
82 * Build a string representing the duration of a permission usage.
83 *
84 * @return a string representing the duration of this app's usage or null if there are no usages.
85 */
86 @RequiresApi(Build.VERSION_CODES.S)
getUsageDurationStringnull87 fun getUsageDurationString(context: Context, groupUsage: GroupUsage?): String? {
88 return if (groupUsage == null) {
89 null
90 } else getTimeDiffStr(context, groupUsage.accessDuration)
91 }
92
93 /**
94 * Build a string representing the number of milliseconds passed in. It rounds to the nearest unit.
95 * For example, given a duration of 3500 and an English locale, this can return "3 seconds".
96 *
97 * @param context The context.
98 * @param duration The number of milliseconds.
99 * @return a string representing the given number of milliseconds.
100 */
getTimeDiffStrnull101 fun getTimeDiffStr(context: Context, duration: Long): String {
102 val timeDiffAndUnit = calculateTimeDiffAndUnit(duration)
103 return when (timeDiffAndUnit.second) {
104 SECONDS ->
105 StringUtils.getIcuPluralsString(
106 context,
107 R.string.seconds,
108 timeDiffAndUnit.first.toInt()
109 )
110 MINUTES ->
111 StringUtils.getIcuPluralsString(
112 context,
113 R.string.minutes,
114 timeDiffAndUnit.first.toInt()
115 )
116 HOURS ->
117 StringUtils.getIcuPluralsString(context, R.string.hours, timeDiffAndUnit.first.toInt())
118 else ->
119 StringUtils.getIcuPluralsString(context, R.string.days, timeDiffAndUnit.first.toInt())
120 }
121 }
122
123 /**
124 * Build a string representing the duration used of milliseconds passed in.
125 *
126 * @return a string representing the duration used in the nearest unit. ex: Used for 3 mins
127 */
getDurationUsedStrnull128 fun getDurationUsedStr(context: Context, duration: Long): String {
129 val timeDiffAndUnit = calculateTimeDiffAndUnit(duration)
130 return when (timeDiffAndUnit.second) {
131 SECONDS ->
132 StringUtils.getIcuPluralsString(
133 context,
134 R.string.duration_used_seconds,
135 timeDiffAndUnit.first.toInt()
136 )
137 MINUTES ->
138 StringUtils.getIcuPluralsString(
139 context,
140 R.string.duration_used_minutes,
141 timeDiffAndUnit.first.toInt()
142 )
143 HOURS ->
144 StringUtils.getIcuPluralsString(
145 context,
146 R.string.duration_used_hours,
147 timeDiffAndUnit.first.toInt()
148 )
149 else ->
150 StringUtils.getIcuPluralsString(
151 context,
152 R.string.duration_used_days,
153 timeDiffAndUnit.first.toInt()
154 )
155 }
156 }
157
158 /**
159 * Given the duration in milliseconds, calculate the time of that duration in the nearest unit.
160 *
161 * @return a Pair of the <duration in the nearest unit, the nearest unit>
162 */
calculateTimeDiffAndUnitnull163 fun calculateTimeDiffAndUnit(duration: Long): Pair<Long, Int> {
164 val seconds = Math.max(1, duration / 1000)
165
166 if (seconds < 60) {
167 return Pair.create(seconds, SECONDS)
168 }
169 val minutes = seconds / 60
170 if (minutes < 60) {
171 return Pair.create(minutes, MINUTES)
172 }
173 val hours = minutes / 60
174 if (hours < 24) {
175 return Pair.create(hours, HOURS)
176 }
177 val days = hours / 24
178 return Pair.create(days, DAYS)
179 }
180
181 /**
182 * Check whether the given time (in milliseconds) is in the current day.
183 *
184 * @param time the time in milliseconds
185 * @return whether the given time is in the current day.
186 */
isTodaynull187 private fun isToday(time: Long): Boolean {
188 val today: Calendar = Calendar.getInstance(Locale.getDefault())
189 today.setTimeInMillis(System.currentTimeMillis())
190 today.set(Calendar.HOUR_OF_DAY, 0)
191 today.set(Calendar.MINUTE, 0)
192 today.set(Calendar.SECOND, 0)
193 today.set(Calendar.MILLISECOND, 0)
194 val date: Calendar = Calendar.getInstance(Locale.getDefault())
195 date.setTimeInMillis(time)
196 return !date.before(today)
197 }
198