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.data
18 
19 import android.app.Notification
20 import android.app.NotificationChannel
21 import android.app.PendingIntent
22 import android.content.Context
23 import android.content.Intent
24 import android.content.res.Resources
25 import android.os.SystemClock
26 import android.view.View.GONE
27 import android.view.View.VISIBLE
28 import android.widget.RemoteViews
29 import androidx.annotation.DrawableRes
30 import androidx.annotation.StringRes
31 import androidx.core.app.NotificationCompat
32 import androidx.core.app.NotificationCompat.Action
33 import androidx.core.app.NotificationCompat.Builder
34 import androidx.core.app.NotificationManagerCompat
35 import androidx.core.content.ContextCompat
36 
37 import com.android.deskclock.R
38 import com.android.deskclock.Utils
39 import com.android.deskclock.events.Events
40 import com.android.deskclock.stopwatch.StopwatchService
41 
42 /**
43  * Builds notification to reflect the latest state of the stopwatch and recorded laps.
44  */
45 internal class StopwatchNotificationBuilder {
buildChannelnull46     fun buildChannel(context: Context, notificationManager: NotificationManagerCompat) {
47         if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
48             val channel = NotificationChannel(
49                     STOPWATCH_NOTIFICATION_CHANNEL_ID,
50                     context.getString(R.string.default_label),
51                     NotificationManagerCompat.IMPORTANCE_DEFAULT)
52             notificationManager.createNotificationChannel(channel)
53         }
54     }
55 
buildnull56     fun build(context: Context, nm: NotificationModel, stopwatch: Stopwatch?): Notification {
57         @StringRes val eventLabel: Int = R.string.label_notification
58 
59         // Intent to load the app when the notification is tapped.
60         val showApp: Intent = Intent(context, StopwatchService::class.java)
61                 .setAction(StopwatchService.ACTION_SHOW_STOPWATCH)
62                 .putExtra(Events.EXTRA_EVENT_LABEL, eventLabel)
63 
64         val pendingShowApp: PendingIntent = PendingIntent.getService(context, 0, showApp,
65                 PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_UPDATE_CURRENT)
66 
67         // Compute some values required below.
68         val running = stopwatch!!.isRunning
69         val pname: String = context.getPackageName()
70         val res: Resources = context.getResources()
71         val base: Long = SystemClock.elapsedRealtime() - stopwatch.totalTime
72 
73         val content = RemoteViews(pname, R.layout.chronometer_notif_content)
74         content.setChronometer(R.id.chronometer, base, null, running)
75 
76         val actions: MutableList<Action> = ArrayList<Action>(2)
77 
78         if (running) {
79             // Left button: Pause
80             val pause: Intent = Intent(context, StopwatchService::class.java)
81                     .setAction(StopwatchService.ACTION_PAUSE_STOPWATCH)
82                     .putExtra(Events.EXTRA_EVENT_LABEL, eventLabel)
83 
84             @DrawableRes val icon1: Int = R.drawable.ic_pause_24dp
85             val title1: CharSequence = res.getText(R.string.sw_pause_button)
86             val intent1: PendingIntent = Utils.pendingServiceIntent(context, pause)
87             actions.add(Action.Builder(icon1, title1, intent1).build())
88 
89             // Right button: Add Lap
90             if (DataModel.dataModel.canAddMoreLaps()) {
91                 val lap: Intent = Intent(context, StopwatchService::class.java)
92                         .setAction(StopwatchService.ACTION_LAP_STOPWATCH)
93                         .putExtra(Events.EXTRA_EVENT_LABEL, eventLabel)
94 
95                 @DrawableRes val icon2: Int = R.drawable.ic_sw_lap_24dp
96                 val title2: CharSequence = res.getText(R.string.sw_lap_button)
97                 val intent2: PendingIntent = Utils.pendingServiceIntent(context, lap)
98                 actions.add(Action.Builder(icon2, title2, intent2).build())
99             }
100 
101             // Show the current lap number if any laps have been recorded.
102             val lapCount = DataModel.dataModel.laps.size
103             if (lapCount > 0) {
104                 val lapNumber = lapCount + 1
105                 val lap: String = res.getString(R.string.sw_notification_lap_number, lapNumber)
106                 content.setTextViewText(R.id.state, lap)
107                 content.setViewVisibility(R.id.state, VISIBLE)
108             } else {
109                 content.setViewVisibility(R.id.state, GONE)
110             }
111         } else {
112             // Left button: Start
113             val start: Intent = Intent(context, StopwatchService::class.java)
114                     .setAction(StopwatchService.ACTION_START_STOPWATCH)
115                     .putExtra(Events.EXTRA_EVENT_LABEL, eventLabel)
116 
117             @DrawableRes val icon1: Int = R.drawable.ic_start_24dp
118             val title1: CharSequence = res.getText(R.string.sw_start_button)
119             val intent1: PendingIntent = Utils.pendingServiceIntent(context, start)
120             actions.add(Action.Builder(icon1, title1, intent1).build())
121 
122             // Right button: Reset (dismisses notification and resets stopwatch)
123             val reset: Intent = Intent(context, StopwatchService::class.java)
124                     .setAction(StopwatchService.ACTION_RESET_STOPWATCH)
125                     .putExtra(Events.EXTRA_EVENT_LABEL, eventLabel)
126 
127             @DrawableRes val icon2: Int = R.drawable.ic_reset_24dp
128             val title2: CharSequence = res.getText(R.string.sw_reset_button)
129             val intent2: PendingIntent = Utils.pendingServiceIntent(context, reset)
130             actions.add(Action.Builder(icon2, title2, intent2).build())
131 
132             // Indicate the stopwatch is paused.
133             content.setTextViewText(R.id.state, res.getString(R.string.swn_paused))
134             content.setViewVisibility(R.id.state, VISIBLE)
135         }
136         val notification: Builder = Builder(
137                 context, STOPWATCH_NOTIFICATION_CHANNEL_ID)
138                 .setLocalOnly(true)
139                 .setOngoing(running)
140                 .setCustomContentView(content)
141                 .setContentIntent(pendingShowApp)
142                 .setAutoCancel(stopwatch.isPaused)
143                 .setPriority(NotificationManagerCompat.IMPORTANCE_HIGH)
144                 .setSmallIcon(R.drawable.stat_notify_stopwatch)
145                 .setStyle(NotificationCompat.DecoratedCustomViewStyle())
146                 .setColor(ContextCompat.getColor(context, R.color.default_background))
147 
148         if (Utils.isNOrLater) {
149             notification.setGroup(nm.stopwatchNotificationGroupKey)
150         }
151 
152         for (action in actions) {
153             notification.addAction(action)
154         }
155 
156         return notification.build()
157     }
158 
159     companion object {
160         /**
161          * Notification channel containing all stopwatch notifications.
162          */
163         private const val STOPWATCH_NOTIFICATION_CHANNEL_ID = "StopwatchNotification"
164     }
165 }