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 }