blob: be7e00c19ed44072f48b1507d05df33060429069 [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.permissioncontroller.permission.debug
import android.content.Context
import android.icu.util.Calendar
import android.provider.DeviceConfig
import android.text.format.DateFormat.getMediumDateFormat
import android.text.format.DateFormat.getTimeFormat
import android.util.Pair
import com.android.permissioncontroller.R
import com.android.permissioncontroller.permission.model.AppPermissionUsage.GroupUsage
import java.util.Locale
/** Whether to show the Permissions Hub. */
private const val PROPERTY_PERMISSIONS_HUB_2_ENABLED = "permissions_hub_2_enabled"
/** Whether to show the mic and camera icons. */
const val PROPERTY_CAMERA_MIC_ICONS_ENABLED = "camera_mic_icons_enabled"
/** Whether to show the location indicators. */
const val PROPERTY_LOCATION_INDICATORS_ENABLED = "location_indicators_enabled"
/* Whether location accuracy feature is enabled */
const val PROPERTY_LOCATION_ACCURACY_ENABLED = "location_accuracy_enabled"
/* Default location precision */
const val PROPERTY_LOCATION_PRECISION = "location_precision"
/* Whether privacy hub feature is enabled */
const val PROPERTY_PRIVACY_HUB_ENABLED = "privacy_hub_enabled"
const val SECONDS = 1
const val MINUTES = 2
const val HOURS = 3
const val DAYS = 4
/**
* Whether the Permissions Hub 2 flag is enabled
*
* @return whether the flag is enabled
*/
fun isPermissionsHub2FlagEnabled(): Boolean {
return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
PROPERTY_PERMISSIONS_HUB_2_ENABLED, false)
}
/**
* Whether to show the Permissions Dashboard
*
* @return whether to show the Permissions Dashboard.
*/
fun shouldShowPermissionsDashboard(): Boolean {
return isPermissionsHub2FlagEnabled()
}
/**
* Whether the Camera and Mic Icons are enabled by flag.
*
* @return whether the Camera and Mic Icons are enabled.
*/
fun isCameraMicIconsFlagEnabled(): Boolean {
return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
PROPERTY_CAMERA_MIC_ICONS_ENABLED, true)
}
/**
* Whether to show Camera and Mic Icons. They should be shown if the permission hub, or the icons
* specifically, are enabled.
*
* @return whether to show the icons.
*/
fun shouldShowCameraMicIndicators(): Boolean {
return isCameraMicIconsFlagEnabled() || isPermissionsHub2FlagEnabled()
}
/**
* Whether the location indicators are enabled by flag.
*
* @return whether the location indicators are enabled by flag.
*/
fun isLocationIndicatorsFlagEnabled(): Boolean {
return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
PROPERTY_LOCATION_INDICATORS_ENABLED, false)
}
/**
* Whether to show the location indicators. The location indicators are enable if the
* permission hub, or location indicator specifically are enabled.
*/
fun shouldShowLocationIndicators(): Boolean {
return isLocationIndicatorsFlagEnabled() || isPermissionsHub2FlagEnabled()
}
/**
* Whether the location accuracy feature is enabled
*/
fun isLocationAccuracyEnabled(): Boolean {
return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
PROPERTY_LOCATION_ACCURACY_ENABLED, true)
}
/**
* Default state of location precision
* true: default is FINE.
* false: default is COARSE.
*/
fun getDefaultPrecision(): Boolean {
return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
PROPERTY_LOCATION_PRECISION, true)
}
/**
* Whether the privacy hub feature is enabled
*/
fun isPrivacyHubEnabled(): Boolean {
return DeviceConfig.getBoolean(DeviceConfig.NAMESPACE_PRIVACY,
PROPERTY_PRIVACY_HUB_ENABLED, true)
}
/**
* Build a string representing the given time if it happened on the current day and the date
* otherwise.
*
* @param context the context.
* @param lastAccessTime the time in milliseconds.
*
* @return a string representing the time or date of the given time or null if the time is 0.
*/
fun getAbsoluteTimeString(context: Context, lastAccessTime: Long): String? {
if (lastAccessTime == 0L) {
return null
}
return if (isToday(lastAccessTime)) {
getTimeFormat(context).format(lastAccessTime)
} else {
getMediumDateFormat(context).format(lastAccessTime)
}
}
/**
* Build a string representing the time of the most recent permission usage if it happened on
* the current day and the date otherwise.
*
* @param context the context.
* @param groupUsage the permission usage.
*
* @return a string representing the time or date of the most recent usage or null if there are
* no usages.
*/
fun getAbsoluteLastUsageString(context: Context, groupUsage: GroupUsage?): String? {
return if (groupUsage == null) {
null
} else getAbsoluteTimeString(context, groupUsage.lastAccessTime)
}
/**
* Build a string representing the duration of a permission usage.
*
* @return a string representing the duration of this app's usage or null if there are no
* usages.
*/
fun getUsageDurationString(context: Context, groupUsage: GroupUsage?): String? {
return if (groupUsage == null) {
null
} else getTimeDiffStr(context, groupUsage.accessDuration)
}
/**
* Build a string representing the number of milliseconds passed in. It rounds to the nearest
* unit. For example, given a duration of 3500 and an English locale, this can return
* "3 seconds".
* @param context The context.
* @param duration The number of milliseconds.
* @return a string representing the given number of milliseconds.
*/
fun getTimeDiffStr(context: Context, duration: Long): String {
val timeDiffAndUnit = calculateTimeDiffAndUnit(duration)
return when (timeDiffAndUnit.second) {
SECONDS -> context.resources.getQuantityString(R.plurals.seconds,
timeDiffAndUnit.first.toInt(), timeDiffAndUnit.first)
MINUTES -> context.resources.getQuantityString(R.plurals.minutes,
timeDiffAndUnit.first.toInt(), timeDiffAndUnit.first)
HOURS -> context.resources.getQuantityString(R.plurals.hours,
timeDiffAndUnit.first.toInt(), timeDiffAndUnit.first)
else -> context.resources.getQuantityString(R.plurals.days,
timeDiffAndUnit.first.toInt(), timeDiffAndUnit.first)
}
}
/**
* Build a string representing the duration used of milliseconds passed in.
* @return a string representing the duration used in the nearest unit. ex: Used for 3 mins
*/
fun getDurationUsedStr(context: Context, duration: Long): String {
val timeDiffAndUnit = calculateTimeDiffAndUnit(duration)
return when (timeDiffAndUnit.second) {
SECONDS -> context.resources.getQuantityString(R.plurals.duration_used_seconds,
timeDiffAndUnit.first.toInt(), timeDiffAndUnit.first)
MINUTES -> context.resources.getQuantityString(R.plurals.duration_used_minutes,
timeDiffAndUnit.first.toInt(), timeDiffAndUnit.first)
HOURS -> context.resources.getQuantityString(R.plurals.duration_used_hours,
timeDiffAndUnit.first.toInt(), timeDiffAndUnit.first)
else -> context.resources.getQuantityString(R.plurals.duration_used_days,
timeDiffAndUnit.first.toInt(), timeDiffAndUnit.first)
}
}
/**
* Given the duration in milliseconds, calculate the time of that duration in the nearest unit.
* @return a Pair of the <duration in the nearest unit, the nearest unit>
*/
fun calculateTimeDiffAndUnit(duration: Long): Pair<Long, Int> {
val seconds = Math.max(1, duration / 1000)
if (seconds < 60) {
return Pair.create(seconds, SECONDS)
}
val minutes = seconds / 60
if (minutes < 60) {
return Pair.create(minutes, MINUTES)
}
val hours = minutes / 60
if (hours < 24) {
return Pair.create(hours, HOURS)
}
val days = hours / 24
return Pair.create(days, DAYS)
}
/**
* Check whether the given time (in milliseconds) is in the current day.
*
* @param time the time in milliseconds
*
* @return whether the given time is in the current day.
*/
private fun isToday(time: Long): Boolean {
val today: Calendar = Calendar.getInstance(Locale.getDefault())
today.setTimeInMillis(System.currentTimeMillis())
today.set(Calendar.HOUR_OF_DAY, 0)
today.set(Calendar.MINUTE, 0)
today.set(Calendar.SECOND, 0)
today.set(Calendar.MILLISECOND, 0)
val date: Calendar = Calendar.getInstance(Locale.getDefault())
date.setTimeInMillis(time)
return !date.before(today)
}