• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 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.shared.clocks
18 
19 import android.icu.text.DateFormat
20 import android.icu.text.SimpleDateFormat
21 import android.icu.util.TimeZone as IcuTimeZone
22 import android.icu.util.ULocale
23 import androidx.annotation.VisibleForTesting
24 import java.util.Calendar
25 import java.util.Locale
26 import java.util.TimeZone
27 
28 open class TimespecHandler(val cal: Calendar) {
29     var timeZone: TimeZone
30         get() = cal.timeZone
31         set(value) {
32             cal.timeZone = value
33             onTimeZoneChanged()
34         }
35 
36     @VisibleForTesting var fakeTimeMills: Long? = null
37 
updateTimenull38     fun updateTime() {
39         var timeMs = fakeTimeMills ?: System.currentTimeMillis()
40         cal.timeInMillis = (timeMs * TIME_TRAVEL_SCALE).toLong()
41     }
42 
onTimeZoneChangednull43     protected open fun onTimeZoneChanged() {}
44 
45     companion object {
46         // Modifying this will cause the clock to run faster or slower. This is a useful way of
47         // manually checking that clocks are correctly animating through time.
48         private const val TIME_TRAVEL_SCALE = 1.0
49     }
50 }
51 
52 class DigitalTimespecHandler(
53     val timespec: DigitalTimespec,
54     private val timeFormat: String,
55     cal: Calendar = Calendar.getInstance(),
56 ) : TimespecHandler(cal) {
57     var is24Hr = false
58         set(value) {
59             field = value
60             applyPattern()
61         }
62 
63     private var dateFormat = updateSimpleDateFormat(Locale.getDefault())
64     private var contentDescriptionFormat = getContentDescriptionFormat(Locale.getDefault())
65 
66     init {
67         applyPattern()
68     }
69 
onTimeZoneChangednull70     override fun onTimeZoneChanged() {
71         dateFormat.timeZone = IcuTimeZone.getTimeZone(cal.timeZone.id)
72         contentDescriptionFormat?.timeZone = IcuTimeZone.getTimeZone(cal.timeZone.id)
73         applyPattern()
74     }
75 
updateLocalenull76     fun updateLocale(locale: Locale) {
77         dateFormat = updateSimpleDateFormat(locale)
78         contentDescriptionFormat = getContentDescriptionFormat(locale)
79         onTimeZoneChanged()
80     }
81 
updateSimpleDateFormatnull82     private fun updateSimpleDateFormat(locale: Locale): DateFormat {
83         if (locale.language.equals(Locale.ENGLISH.language)) {
84             // force date format in English, and time format to use format defined in json
85             return SimpleDateFormat(timeFormat, timeFormat, ULocale.forLocale(locale))
86         } else {
87             return SimpleDateFormat.getInstanceForSkeleton(timeFormat, locale)
88         }
89     }
90 
getContentDescriptionFormatnull91     private fun getContentDescriptionFormat(locale: Locale): DateFormat? {
92         return when (timespec) {
93             DigitalTimespec.TIME_FULL_FORMAT ->
94                 SimpleDateFormat.getInstanceForSkeleton("hh:mm", locale)
95             else -> null
96         }
97     }
98 
applyPatternnull99     private fun applyPattern() {
100         val timeFormat24Hour = timeFormat.replace("hh", "h").replace("h", "HH")
101         val format = if (is24Hr) timeFormat24Hour else timeFormat
102         (dateFormat as SimpleDateFormat).applyPattern(format)
103         (contentDescriptionFormat as? SimpleDateFormat)?.applyPattern(
104             if (is24Hr) CONTENT_DESCRIPTION_TIME_FORMAT_24_HOUR
105             else CONTENT_DESCRIPTION_TIME_FORMAT_12_HOUR
106         )
107     }
108 
getSingleDigitnull109     private fun getSingleDigit(offset: Int): String {
110         val text = dateFormat.format(cal.time).toString()
111         return text.substring(offset, offset + 1)
112     }
113 
getDigitStringnull114     fun getDigitString(): String {
115         return when (timespec) {
116             DigitalTimespec.FIRST_DIGIT -> getSingleDigit(0)
117             DigitalTimespec.SECOND_DIGIT -> getSingleDigit(1)
118             DigitalTimespec.DIGIT_PAIR -> dateFormat.format(cal.time).toString()
119             DigitalTimespec.TIME_FULL_FORMAT -> dateFormat.format(cal.time).toString()
120         }
121     }
122 
getContentDescriptionnull123     fun getContentDescription(): String? {
124         return when (timespec) {
125             DigitalTimespec.TIME_FULL_FORMAT -> {
126                 contentDescriptionFormat?.format(cal.time).toString()
127             }
128             else -> return null
129         }
130     }
131 
132     companion object {
133         const val CONTENT_DESCRIPTION_TIME_FORMAT_12_HOUR = "hh:mm"
134         const val CONTENT_DESCRIPTION_TIME_FORMAT_24_HOUR = "HH:mm"
135     }
136 }
137