1 package com.android.systemui.qs.tiles
2 
3 import android.app.AlarmManager
4 import android.app.AlarmManager.AlarmClockInfo
5 import android.content.Intent
6 import android.os.Handler
7 import android.os.Looper
8 import android.provider.AlarmClock
9 import android.service.quicksettings.Tile
10 import android.text.TextUtils
11 import android.text.format.DateFormat
12 import android.view.View
13 import androidx.annotation.VisibleForTesting
14 import com.android.internal.jank.InteractionJankMonitor
15 import com.android.internal.logging.MetricsLogger
16 import com.android.systemui.R
17 import com.android.systemui.animation.ActivityLaunchAnimator
18 import com.android.systemui.dagger.qualifiers.Background
19 import com.android.systemui.dagger.qualifiers.Main
20 import com.android.systemui.plugins.ActivityStarter
21 import com.android.systemui.plugins.FalsingManager
22 import com.android.systemui.plugins.qs.QSTile
23 import com.android.systemui.plugins.statusbar.StatusBarStateController
24 import com.android.systemui.qs.QSHost
25 import com.android.systemui.qs.QsEventLogger
26 import com.android.systemui.qs.logging.QSLogger
27 import com.android.systemui.qs.tileimpl.QSTileImpl
28 import com.android.systemui.settings.UserTracker
29 import com.android.systemui.statusbar.policy.NextAlarmController
30 import java.util.Locale
31 import javax.inject.Inject
32 
33 class AlarmTile @Inject constructor(
34     host: QSHost,
35     uiEventLogger: QsEventLogger,
36     @Background backgroundLooper: Looper,
37     @Main mainHandler: Handler,
38     falsingManager: FalsingManager,
39     metricsLogger: MetricsLogger,
40     statusBarStateController: StatusBarStateController,
41     activityStarter: ActivityStarter,
42     qsLogger: QSLogger,
43     private val userTracker: UserTracker,
44     nextAlarmController: NextAlarmController
45 ) : QSTileImpl<QSTile.State>(
46     host,
47     uiEventLogger,
48     backgroundLooper,
49     mainHandler,
50     falsingManager,
51     metricsLogger,
52     statusBarStateController,
53     activityStarter,
54     qsLogger
55 ) {
56 
57     private var lastAlarmInfo: AlarmManager.AlarmClockInfo? = null
58     private val icon = ResourceIcon.get(R.drawable.ic_alarm)
59     @VisibleForTesting
60     internal val defaultIntent = Intent(AlarmClock.ACTION_SHOW_ALARMS)
61     private val callback = NextAlarmController.NextAlarmChangeCallback { nextAlarm ->
62         lastAlarmInfo = nextAlarm
63         refreshState()
64     }
65 
66     init {
67         nextAlarmController.observe(this, callback)
68     }
69 
70     override fun newTileState(): QSTile.State {
71         return QSTile.State().apply {
72             handlesLongClick = false
73         }
74     }
75 
76     override fun handleClick(view: View?) {
77         val animationController = view?.let {
78             ActivityLaunchAnimator.Controller.fromView(
79                     it, InteractionJankMonitor.CUJ_SHADE_APP_LAUNCH_FROM_QS_TILE)
80         }
81         val pendingIntent = lastAlarmInfo?.showIntent
82         if (pendingIntent != null) {
83             mActivityStarter.postStartActivityDismissingKeyguard(pendingIntent, animationController)
84         } else {
85             mActivityStarter.postStartActivityDismissingKeyguard(defaultIntent, 0,
86                     animationController)
87         }
88     }
89 
90     override fun handleUpdateState(state: QSTile.State, arg: Any?) {
91         state.icon = icon
92         state.label = tileLabel
93         lastAlarmInfo?.let {
94             state.secondaryLabel = formatNextAlarm(it)
95             state.state = Tile.STATE_ACTIVE
96         } ?: run {
97             state.secondaryLabel = mContext.getString(R.string.qs_alarm_tile_no_alarm)
98             state.state = Tile.STATE_INACTIVE
99         }
100         state.contentDescription = TextUtils.concat(state.label, ", ", state.secondaryLabel)
101     }
102 
103     override fun getTileLabel(): CharSequence {
104         return mContext.getString(R.string.status_bar_alarm)
105     }
106 
107     private fun formatNextAlarm(info: AlarmClockInfo): String {
108         val skeleton = if (use24HourFormat()) "EHm" else "Ehma"
109         val pattern = DateFormat.getBestDateTimePattern(Locale.getDefault(), skeleton)
110         return DateFormat.format(pattern, info.triggerTime).toString()
111     }
112 
113     private fun use24HourFormat(): Boolean {
114         return DateFormat.is24HourFormat(mContext, userTracker.userId)
115     }
116 
117     override fun getMetricsCategory(): Int {
118         return 0
119     }
120 
121     override fun getLongClickIntent(): Intent? {
122         return null
123     }
124 
125     companion object {
126         const val TILE_SPEC = "alarm"
127     }
128 }
129