• 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.deskclock.widget
18 
19 import android.content.Context
20 import android.database.ContentObserver
21 import android.net.Uri
22 import android.os.Handler
23 import android.os.Looper
24 import android.provider.Settings
25 import android.text.format.DateFormat
26 import android.util.AttributeSet
27 import android.widget.TextView
28 import androidx.annotation.VisibleForTesting
29 
30 import com.android.deskclock.Utils
31 import com.android.deskclock.data.DataModel
32 
33 import java.util.Calendar
34 import java.util.TimeZone
35 
36 /**
37  * Based on [android.widget.TextClock], This widget displays a constant time of day using
38  * format specifiers. [android.widget.TextClock] doesn't support a non-ticking clock.
39  */
40 class TextTime @JvmOverloads constructor(
41     context: Context?,
42     attrs: AttributeSet? = null,
43     defStyle: Int = 0
44 ) : TextView(context, attrs, defStyle) {
45     private var mFormat12: CharSequence? = Utils.get12ModeFormat(0.3f, false)
46     private var mFormat24: CharSequence? = Utils.get24ModeFormat(false)
47     private var mFormat: CharSequence? = null
48 
49     private var mAttached = false
50 
51     private var mHour = 0
52     private var mMinute = 0
53 
54     private val mFormatChangeObserver: ContentObserver =
55             object : ContentObserver(Handler(Looper.myLooper()!!)) {
onChangenull56         override fun onChange(selfChange: Boolean) {
57             chooseFormat()
58             updateTime()
59         }
60 
onChangenull61         override fun onChange(selfChange: Boolean, uri: Uri?) {
62             chooseFormat()
63             updateTime()
64         }
65     }
66 
67     var format12Hour: CharSequence?
68         get() = mFormat12
69         set(format) {
70             mFormat12 = format
71             chooseFormat()
72             updateTime()
73         }
74 
75     var format24Hour: CharSequence?
76         get() = mFormat24
77         set(format) {
78             mFormat24 = format
79             chooseFormat()
80             updateTime()
81         }
82 
83     init {
84         chooseFormat()
85     }
86 
chooseFormatnull87     private fun chooseFormat() {
88         val format24Requested: Boolean = DataModel.dataModel.is24HourFormat()
89         mFormat = if (format24Requested) {
90             mFormat24 ?: DEFAULT_FORMAT_24_HOUR
91         } else {
92             mFormat12 ?: DEFAULT_FORMAT_12_HOUR
93         }
94     }
95 
onAttachedToWindownull96     override fun onAttachedToWindow() {
97         super.onAttachedToWindow()
98         if (!mAttached) {
99             mAttached = true
100             registerObserver()
101             updateTime()
102         }
103     }
104 
onDetachedFromWindownull105     override fun onDetachedFromWindow() {
106         super.onDetachedFromWindow()
107         if (mAttached) {
108             unregisterObserver()
109             mAttached = false
110         }
111     }
112 
registerObservernull113     private fun registerObserver() {
114         val resolver = context.contentResolver
115         resolver.registerContentObserver(Settings.System.CONTENT_URI, true, mFormatChangeObserver)
116     }
117 
unregisterObservernull118     private fun unregisterObserver() {
119         val resolver = context.contentResolver
120         resolver.unregisterContentObserver(mFormatChangeObserver)
121     }
122 
setTimenull123     fun setTime(hour: Int, minute: Int) {
124         mHour = hour
125         mMinute = minute
126         updateTime()
127     }
128 
updateTimenull129     private fun updateTime() {
130         // Format the time relative to UTC to ensure hour and minute are not adjusted for DST.
131         val calendar: Calendar = DataModel.dataModel.calendar
132         calendar.timeZone = UTC
133         calendar[Calendar.HOUR_OF_DAY] = mHour
134         calendar[Calendar.MINUTE] = mMinute
135         val text = DateFormat.format(mFormat, calendar)
136         setText(text)
137         // Strip away the spans from text so talkback is not confused
138         contentDescription = text.toString()
139     }
140 
141     companion object {
142         /** UTC does not have DST rules and will not alter the [.mHour] and [.mMinute].  */
143         private val UTC = TimeZone.getTimeZone("UTC")
144 
145         @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
146         val DEFAULT_FORMAT_12_HOUR: CharSequence = "h:mm a"
147 
148         @VisibleForTesting(otherwise = VisibleForTesting.PRIVATE)
149         val DEFAULT_FORMAT_24_HOUR: CharSequence = "H:mm"
150     }
151 }