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 *
53 * @return a string representing the time or date of the given time or null if the time is 0.
54 */
getAbsoluteTimeStringnull55 fun getAbsoluteTimeString(context: Context, lastAccessTime: Long): String? {
56 if (lastAccessTime == 0L) {
57 return null
58 }
59 return if (isToday(lastAccessTime)) {
60 getTimeFormat(context).format(lastAccessTime)
61 } else {
62 getMediumDateFormat(context).format(lastAccessTime)
63 }
64 }
65
66 /**
67 * Build a string representing the time of the most recent permission usage if it happened on
68 * the current day and the date otherwise.
69 *
70 * @param context the context.
71 * @param groupUsage the permission usage.
72 *
73 * @return a string representing the time or date of the most recent usage or null if there are
74 * no usages.
75 */
76 @RequiresApi(Build.VERSION_CODES.S)
getAbsoluteLastUsageStringnull77 fun getAbsoluteLastUsageString(context: Context, groupUsage: GroupUsage?): String? {
78 return if (groupUsage == null) {
79 null
80 } else getAbsoluteTimeString(context, groupUsage.lastAccessTime)
81 }
82
83 /**
84 * Build a string representing the duration of a permission usage.
85 *
86 * @return a string representing the duration of this app's usage or null if there are no
87 * usages.
88 */
89 @RequiresApi(Build.VERSION_CODES.S)
getUsageDurationStringnull90 fun getUsageDurationString(context: Context, groupUsage: GroupUsage?): String? {
91 return if (groupUsage == null) {
92 null
93 } else getTimeDiffStr(context, groupUsage.accessDuration)
94 }
95
96 /**
97 * Build a string representing the number of milliseconds passed in. It rounds to the nearest
98 * unit. For example, given a duration of 3500 and an English locale, this can return
99 * "3 seconds".
100 * @param context The context.
101 * @param duration The number of milliseconds.
102 * @return a string representing the given number of milliseconds.
103 */
getTimeDiffStrnull104 fun getTimeDiffStr(context: Context, duration: Long): String {
105 val timeDiffAndUnit = calculateTimeDiffAndUnit(duration)
106 return when (timeDiffAndUnit.second) {
107 SECONDS -> StringUtils.getIcuPluralsString(context,
108 R.string.seconds, timeDiffAndUnit.first.toInt())
109 MINUTES -> StringUtils.getIcuPluralsString(context,
110 R.string.minutes, timeDiffAndUnit.first.toInt())
111 HOURS -> StringUtils.getIcuPluralsString(context,
112 R.string.hours, timeDiffAndUnit.first.toInt())
113 else -> StringUtils.getIcuPluralsString(context,
114 R.string.days, timeDiffAndUnit.first.toInt())
115 }
116 }
117
118 /**
119 * Build a string representing the duration used of milliseconds passed in.
120 * @return a string representing the duration used in the nearest unit. ex: Used for 3 mins
121 */
getDurationUsedStrnull122 fun getDurationUsedStr(context: Context, duration: Long): String {
123 val timeDiffAndUnit = calculateTimeDiffAndUnit(duration)
124 return when (timeDiffAndUnit.second) {
125 SECONDS -> StringUtils.getIcuPluralsString(context,
126 R.string.duration_used_seconds, timeDiffAndUnit.first.toInt())
127 MINUTES -> StringUtils.getIcuPluralsString(context,
128 R.string.duration_used_minutes, timeDiffAndUnit.first.toInt())
129 HOURS -> StringUtils.getIcuPluralsString(context,
130 R.string.duration_used_hours, timeDiffAndUnit.first.toInt())
131 else -> StringUtils.getIcuPluralsString(context,
132 R.string.duration_used_days, timeDiffAndUnit.first.toInt())
133 }
134 }
135
136 /**
137 * Given the duration in milliseconds, calculate the time of that duration in the nearest unit.
138 * @return a Pair of the <duration in the nearest unit, the nearest unit>
139 */
calculateTimeDiffAndUnitnull140 fun calculateTimeDiffAndUnit(duration: Long): Pair<Long, Int> {
141 val seconds = Math.max(1, duration / 1000)
142
143 if (seconds < 60) {
144 return Pair.create(seconds, SECONDS)
145 }
146 val minutes = seconds / 60
147 if (minutes < 60) {
148 return Pair.create(minutes, MINUTES)
149 }
150 val hours = minutes / 60
151 if (hours < 24) {
152 return Pair.create(hours, HOURS)
153 }
154 val days = hours / 24
155 return Pair.create(days, DAYS)
156 }
157
158 /**
159 * Check whether the given time (in milliseconds) is in the current day.
160 *
161 * @param time the time in milliseconds
162 *
163 * @return whether the given time is in the current day.
164 */
isTodaynull165 private fun isToday(time: Long): Boolean {
166 val today: Calendar = Calendar.getInstance(Locale.getDefault())
167 today.setTimeInMillis(System.currentTimeMillis())
168 today.set(Calendar.HOUR_OF_DAY, 0)
169 today.set(Calendar.MINUTE, 0)
170 today.set(Calendar.SECOND, 0)
171 today.set(Calendar.MILLISECOND, 0)
172 val date: Calendar = Calendar.getInstance(Locale.getDefault())
173 date.setTimeInMillis(time)
174 return !date.before(today)
175 }
176