Selim Cinek | df5bf61 | 2016-02-26 09:56:31 -0800 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2016 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 | |
Rohan Shah | 20790b8 | 2018-07-02 17:21:04 -0700 | [diff] [blame] | 17 | package com.android.systemui.statusbar.notification.row.wrapper; |
Selim Cinek | df5bf61 | 2016-02-26 09:56:31 -0800 | [diff] [blame] | 18 | |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 19 | import static com.android.systemui.Dependency.MAIN_HANDLER; |
Selim Cinek | df5bf61 | 2016-02-26 09:56:31 -0800 | [diff] [blame] | 20 | |
Beth Thibodeau | 2e76ad5 | 2019-05-29 11:29:25 -0400 | [diff] [blame] | 21 | import android.annotation.Nullable; |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 22 | import android.app.Notification; |
| 23 | import android.content.Context; |
| 24 | import android.content.res.ColorStateList; |
| 25 | import android.media.MediaMetadata; |
| 26 | import android.media.session.MediaController; |
| 27 | import android.media.session.MediaSession; |
| 28 | import android.media.session.PlaybackState; |
Beth Thibodeau | 4b05fbc | 2019-05-16 12:51:20 -0400 | [diff] [blame] | 29 | import android.metrics.LogMaker; |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 30 | import android.os.Handler; |
Beth Thibodeau | 899b595 | 2020-01-07 17:50:07 -0500 | [diff] [blame] | 31 | import android.service.notification.StatusBarNotification; |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 32 | import android.text.format.DateUtils; |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 33 | import android.view.LayoutInflater; |
| 34 | import android.view.View; |
| 35 | import android.view.ViewStub; |
| 36 | import android.widget.SeekBar; |
| 37 | import android.widget.TextView; |
| 38 | |
| 39 | import com.android.internal.R; |
Beth Thibodeau | 4b05fbc | 2019-05-16 12:51:20 -0400 | [diff] [blame] | 40 | import com.android.internal.annotations.VisibleForTesting; |
| 41 | import com.android.internal.logging.MetricsLogger; |
| 42 | import com.android.internal.logging.nano.MetricsProto.MetricsEvent; |
Beth Thibodeau | 4e4c86e | 2019-08-01 10:57:29 -0400 | [diff] [blame] | 43 | import com.android.internal.widget.MediaNotificationView; |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 44 | import com.android.systemui.Dependency; |
Beth Thibodeau | 07d20c3 | 2019-10-16 13:45:56 -0400 | [diff] [blame] | 45 | import com.android.systemui.qs.QSPanel; |
| 46 | import com.android.systemui.qs.QuickQSPanel; |
Beth Thibodeau | eab4dde | 2019-02-07 11:37:02 -0500 | [diff] [blame] | 47 | import com.android.systemui.statusbar.NotificationMediaManager; |
Selim Cinek | df5bf61 | 2016-02-26 09:56:31 -0800 | [diff] [blame] | 48 | import com.android.systemui.statusbar.TransformableView; |
Gus Prevas | ab33679 | 2018-11-14 13:52:20 -0500 | [diff] [blame] | 49 | import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow; |
wilsonshih | e832194 | 2019-10-18 18:39:46 +0800 | [diff] [blame] | 50 | import com.android.systemui.statusbar.phone.NotificationShadeWindowController; |
Matt Pietal | 5a19cb6 | 2019-10-30 12:31:07 -0400 | [diff] [blame] | 51 | import com.android.systemui.util.Utils; |
Selim Cinek | df5bf61 | 2016-02-26 09:56:31 -0800 | [diff] [blame] | 52 | |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 53 | import java.util.Timer; |
| 54 | import java.util.TimerTask; |
| 55 | |
Selim Cinek | df5bf61 | 2016-02-26 09:56:31 -0800 | [diff] [blame] | 56 | /** |
| 57 | * Wraps a notification containing a media template |
| 58 | */ |
| 59 | public class NotificationMediaTemplateViewWrapper extends NotificationTemplateViewWrapper { |
| 60 | |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 61 | private static final long PROGRESS_UPDATE_INTERVAL = 1000; // 1s |
| 62 | private static final String COMPACT_MEDIA_TAG = "media"; |
| 63 | private final Handler mHandler = Dependency.get(MAIN_HANDLER); |
| 64 | private Timer mSeekBarTimer; |
| 65 | private View mActions; |
| 66 | private SeekBar mSeekBar; |
| 67 | private TextView mSeekBarElapsedTime; |
| 68 | private TextView mSeekBarTotalTime; |
| 69 | private long mDuration = 0; |
| 70 | private MediaController mMediaController; |
Beth Thibodeau | 2e76ad5 | 2019-05-29 11:29:25 -0400 | [diff] [blame] | 71 | private MediaMetadata mMediaMetadata; |
Beth Thibodeau | eab4dde | 2019-02-07 11:37:02 -0500 | [diff] [blame] | 72 | private NotificationMediaManager mMediaManager; |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 73 | private View mSeekBarView; |
| 74 | private Context mContext; |
Beth Thibodeau | 4b05fbc | 2019-05-16 12:51:20 -0400 | [diff] [blame] | 75 | private MetricsLogger mMetricsLogger; |
Beth Thibodeau | 4e4c86e | 2019-08-01 10:57:29 -0400 | [diff] [blame] | 76 | private boolean mIsViewVisible; |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 77 | |
Beth Thibodeau | 4b05fbc | 2019-05-16 12:51:20 -0400 | [diff] [blame] | 78 | @VisibleForTesting |
| 79 | protected SeekBar.OnSeekBarChangeListener mSeekListener = |
| 80 | new SeekBar.OnSeekBarChangeListener() { |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 81 | @Override |
| 82 | public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { |
| 83 | } |
| 84 | |
| 85 | @Override |
| 86 | public void onStartTrackingTouch(SeekBar seekBar) { |
| 87 | } |
| 88 | |
| 89 | @Override |
| 90 | public void onStopTrackingTouch(SeekBar seekBar) { |
Beth Thibodeau | 2e76ad5 | 2019-05-29 11:29:25 -0400 | [diff] [blame] | 91 | if (mMediaController != null) { |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 92 | mMediaController.getTransportControls().seekTo(mSeekBar.getProgress()); |
Beth Thibodeau | 4b05fbc | 2019-05-16 12:51:20 -0400 | [diff] [blame] | 93 | mMetricsLogger.write(newLog(MetricsEvent.TYPE_UPDATE)); |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 94 | } |
| 95 | } |
| 96 | }; |
| 97 | |
Beth Thibodeau | 4e4c86e | 2019-08-01 10:57:29 -0400 | [diff] [blame] | 98 | private MediaNotificationView.VisibilityChangeListener mVisibilityListener = |
| 99 | new MediaNotificationView.VisibilityChangeListener() { |
| 100 | @Override |
| 101 | public void onAggregatedVisibilityChanged(boolean isVisible) { |
| 102 | mIsViewVisible = isVisible; |
| 103 | if (isVisible && mMediaController != null) { |
| 104 | // Restart timer if we're currently playing and didn't already have one going |
| 105 | PlaybackState state = mMediaController.getPlaybackState(); |
| 106 | if (state != null && state.getState() == PlaybackState.STATE_PLAYING |
| 107 | && mSeekBarTimer == null && mSeekBarView != null |
| 108 | && mSeekBarView.getVisibility() != View.GONE) { |
| 109 | startTimer(); |
| 110 | } |
| 111 | } else { |
| 112 | clearTimer(); |
| 113 | } |
| 114 | } |
| 115 | }; |
| 116 | |
| 117 | private View.OnAttachStateChangeListener mAttachStateListener = |
| 118 | new View.OnAttachStateChangeListener() { |
| 119 | @Override |
| 120 | public void onViewAttachedToWindow(View v) { |
| 121 | } |
| 122 | |
| 123 | @Override |
| 124 | public void onViewDetachedFromWindow(View v) { |
| 125 | mIsViewVisible = false; |
| 126 | } |
| 127 | }; |
| 128 | |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 129 | private MediaController.Callback mMediaCallback = new MediaController.Callback() { |
| 130 | @Override |
| 131 | public void onSessionDestroyed() { |
| 132 | clearTimer(); |
| 133 | mMediaController.unregisterCallback(this); |
Beth Thibodeau | 4e4c86e | 2019-08-01 10:57:29 -0400 | [diff] [blame] | 134 | if (mView instanceof MediaNotificationView) { |
| 135 | ((MediaNotificationView) mView).removeVisibilityListener(mVisibilityListener); |
| 136 | mView.removeOnAttachStateChangeListener(mAttachStateListener); |
| 137 | } |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 138 | } |
| 139 | |
| 140 | @Override |
Beth Thibodeau | 2e76ad5 | 2019-05-29 11:29:25 -0400 | [diff] [blame] | 141 | public void onPlaybackStateChanged(@Nullable PlaybackState state) { |
| 142 | if (state == null) { |
| 143 | return; |
| 144 | } |
| 145 | |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 146 | if (state.getState() != PlaybackState.STATE_PLAYING) { |
Beth Thibodeau | 7d5c97a | 2019-04-10 15:28:07 -0400 | [diff] [blame] | 147 | // Update the UI once, in case playback info changed while we were paused |
Beth Thibodeau | 2e76ad5 | 2019-05-29 11:29:25 -0400 | [diff] [blame] | 148 | updatePlaybackUi(state); |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 149 | clearTimer(); |
Beth Thibodeau | 4b05fbc | 2019-05-16 12:51:20 -0400 | [diff] [blame] | 150 | } else if (mSeekBarTimer == null && mSeekBarView != null |
| 151 | && mSeekBarView.getVisibility() != View.GONE) { |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 152 | startTimer(); |
| 153 | } |
| 154 | } |
Beth Thibodeau | 2e76ad5 | 2019-05-29 11:29:25 -0400 | [diff] [blame] | 155 | |
| 156 | @Override |
| 157 | public void onMetadataChanged(@Nullable MediaMetadata metadata) { |
| 158 | if (mMediaMetadata == null || !mMediaMetadata.equals(metadata)) { |
| 159 | mMediaMetadata = metadata; |
| 160 | updateDuration(); |
| 161 | } |
| 162 | } |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 163 | }; |
| 164 | |
Selim Cinek | 7d1c63e | 2016-04-21 15:26:10 -0700 | [diff] [blame] | 165 | protected NotificationMediaTemplateViewWrapper(Context ctx, View view, |
| 166 | ExpandableNotificationRow row) { |
| 167 | super(ctx, view, row); |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 168 | mContext = ctx; |
Beth Thibodeau | eab4dde | 2019-02-07 11:37:02 -0500 | [diff] [blame] | 169 | mMediaManager = Dependency.get(NotificationMediaManager.class); |
Beth Thibodeau | 4b05fbc | 2019-05-16 12:51:20 -0400 | [diff] [blame] | 170 | mMetricsLogger = Dependency.get(MetricsLogger.class); |
Selim Cinek | df5bf61 | 2016-02-26 09:56:31 -0800 | [diff] [blame] | 171 | } |
| 172 | |
Selim Cinek | 414ad33 | 2017-02-24 19:06:12 -0800 | [diff] [blame] | 173 | private void resolveViews() { |
Selim Cinek | df5bf61 | 2016-02-26 09:56:31 -0800 | [diff] [blame] | 174 | mActions = mView.findViewById(com.android.internal.R.id.media_actions); |
Beth Thibodeau | 4e4c86e | 2019-08-01 10:57:29 -0400 | [diff] [blame] | 175 | mIsViewVisible = mView.isShown(); |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 176 | |
Ned Burns | 00b4b2d | 2019-10-17 22:09:27 -0400 | [diff] [blame] | 177 | final MediaSession.Token token = mRow.getEntry().getSbn().getNotification().extras |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 178 | .getParcelable(Notification.EXTRA_MEDIA_SESSION); |
| 179 | |
Beth Thibodeau | 899b595 | 2020-01-07 17:50:07 -0500 | [diff] [blame] | 180 | if (Utils.useQsMediaPlayer(mContext) && token != null) { |
Beth Thibodeau | 69b1dfd | 2019-11-08 15:39:25 -0500 | [diff] [blame] | 181 | final int[] compactActions = mRow.getEntry().getSbn().getNotification().extras |
| 182 | .getIntArray(Notification.EXTRA_COMPACT_ACTIONS); |
Beth Thibodeau | fd51bb2 | 2019-11-25 13:57:06 -0500 | [diff] [blame] | 183 | int tintColor = getNotificationHeader().getOriginalIconColor(); |
wilsonshih | e832194 | 2019-10-18 18:39:46 +0800 | [diff] [blame] | 184 | NotificationShadeWindowController ctrl = Dependency.get( |
| 185 | NotificationShadeWindowController.class); |
| 186 | QuickQSPanel panel = ctrl.getNotificationShadeView().findViewById( |
Beth Thibodeau | 07d20c3 | 2019-10-16 13:45:56 -0400 | [diff] [blame] | 187 | com.android.systemui.R.id.quick_qs_panel); |
Beth Thibodeau | 899b595 | 2020-01-07 17:50:07 -0500 | [diff] [blame] | 188 | StatusBarNotification sbn = mRow.getEntry().getSbn(); |
| 189 | Notification notif = sbn.getNotification(); |
Beth Thibodeau | 07d20c3 | 2019-10-16 13:45:56 -0400 | [diff] [blame] | 190 | panel.getMediaPlayer().setMediaSession(token, |
Beth Thibodeau | 899b595 | 2020-01-07 17:50:07 -0500 | [diff] [blame] | 191 | notif.getSmallIcon(), |
Beth Thibodeau | fd51bb2 | 2019-11-25 13:57:06 -0500 | [diff] [blame] | 192 | tintColor, |
| 193 | mBackgroundColor, |
Beth Thibodeau | 69b1dfd | 2019-11-08 15:39:25 -0500 | [diff] [blame] | 194 | mActions, |
Beth Thibodeau | 899b595 | 2020-01-07 17:50:07 -0500 | [diff] [blame] | 195 | compactActions, |
Beth Thibodeau | a3d9098 | 2020-04-13 23:42:48 -0400 | [diff] [blame] | 196 | notif.contentIntent, |
| 197 | sbn.getKey()); |
wilsonshih | e832194 | 2019-10-18 18:39:46 +0800 | [diff] [blame] | 198 | QSPanel bigPanel = ctrl.getNotificationShadeView().findViewById( |
Beth Thibodeau | 07d20c3 | 2019-10-16 13:45:56 -0400 | [diff] [blame] | 199 | com.android.systemui.R.id.quick_settings_panel); |
| 200 | bigPanel.addMediaSession(token, |
Beth Thibodeau | 899b595 | 2020-01-07 17:50:07 -0500 | [diff] [blame] | 201 | notif.getSmallIcon(), |
Beth Thibodeau | fd51bb2 | 2019-11-25 13:57:06 -0500 | [diff] [blame] | 202 | tintColor, |
| 203 | mBackgroundColor, |
Beth Thibodeau | 07d20c3 | 2019-10-16 13:45:56 -0400 | [diff] [blame] | 204 | mActions, |
Beth Thibodeau | a3d9098 | 2020-04-13 23:42:48 -0400 | [diff] [blame] | 205 | sbn, |
| 206 | sbn.getKey()); |
Beth Thibodeau | 07d20c3 | 2019-10-16 13:45:56 -0400 | [diff] [blame] | 207 | } |
| 208 | |
Beth Thibodeau | eab4dde | 2019-02-07 11:37:02 -0500 | [diff] [blame] | 209 | boolean showCompactSeekbar = mMediaManager.getShowCompactMediaSeekbar(); |
| 210 | if (token == null || (COMPACT_MEDIA_TAG.equals(mView.getTag()) && !showCompactSeekbar)) { |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 211 | if (mSeekBarView != null) { |
| 212 | mSeekBarView.setVisibility(View.GONE); |
| 213 | } |
| 214 | return; |
| 215 | } |
| 216 | |
| 217 | // Check for existing media controller and clean up / create as necessary |
Beth Thibodeau | d1eb0a8 | 2019-11-01 15:38:47 -0400 | [diff] [blame] | 218 | boolean shouldUpdateListeners = false; |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 219 | if (mMediaController == null || !mMediaController.getSessionToken().equals(token)) { |
| 220 | if (mMediaController != null) { |
| 221 | mMediaController.unregisterCallback(mMediaCallback); |
| 222 | } |
| 223 | mMediaController = new MediaController(mContext, token); |
Beth Thibodeau | d1eb0a8 | 2019-11-01 15:38:47 -0400 | [diff] [blame] | 224 | shouldUpdateListeners = true; |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 225 | } |
| 226 | |
Beth Thibodeau | 2e76ad5 | 2019-05-29 11:29:25 -0400 | [diff] [blame] | 227 | mMediaMetadata = mMediaController.getMetadata(); |
| 228 | if (mMediaMetadata != null) { |
| 229 | long duration = mMediaMetadata.getLong(MediaMetadata.METADATA_KEY_DURATION); |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 230 | if (duration <= 0) { |
| 231 | // Don't include the seekbar if this is a livestream |
Beth Thibodeau | 4b05fbc | 2019-05-16 12:51:20 -0400 | [diff] [blame] | 232 | if (mSeekBarView != null && mSeekBarView.getVisibility() != View.GONE) { |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 233 | mSeekBarView.setVisibility(View.GONE); |
Beth Thibodeau | 4b05fbc | 2019-05-16 12:51:20 -0400 | [diff] [blame] | 234 | mMetricsLogger.write(newLog(MetricsEvent.TYPE_CLOSE)); |
| 235 | clearTimer(); |
Beth Thibodeau | d1eb0a8 | 2019-11-01 15:38:47 -0400 | [diff] [blame] | 236 | } else if (mSeekBarView == null && shouldUpdateListeners) { |
Beth Thibodeau | 4b05fbc | 2019-05-16 12:51:20 -0400 | [diff] [blame] | 237 | // Only log if the controller changed, otherwise we would log multiple times for |
| 238 | // the same notification when user pauses/resumes |
| 239 | mMetricsLogger.write(newLog(MetricsEvent.TYPE_CLOSE)); |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 240 | } |
| 241 | return; |
Beth Thibodeau | 2e76ad5 | 2019-05-29 11:29:25 -0400 | [diff] [blame] | 242 | } else if (mSeekBarView != null && mSeekBarView.getVisibility() == View.GONE) { |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 243 | // Otherwise, make sure the seekbar is visible |
Beth Thibodeau | 2e76ad5 | 2019-05-29 11:29:25 -0400 | [diff] [blame] | 244 | mSeekBarView.setVisibility(View.VISIBLE); |
| 245 | mMetricsLogger.write(newLog(MetricsEvent.TYPE_OPEN)); |
| 246 | updateDuration(); |
| 247 | startTimer(); |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 248 | } |
| 249 | } |
| 250 | |
| 251 | // Inflate the seekbar template |
| 252 | ViewStub stub = mView.findViewById(R.id.notification_media_seekbar_container); |
| 253 | if (stub instanceof ViewStub) { |
| 254 | LayoutInflater layoutInflater = LayoutInflater.from(stub.getContext()); |
| 255 | stub.setLayoutInflater(layoutInflater); |
| 256 | stub.setLayoutResource(R.layout.notification_material_media_seekbar); |
| 257 | mSeekBarView = stub.inflate(); |
Beth Thibodeau | 4b05fbc | 2019-05-16 12:51:20 -0400 | [diff] [blame] | 258 | mMetricsLogger.write(newLog(MetricsEvent.TYPE_OPEN)); |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 259 | |
| 260 | mSeekBar = mSeekBarView.findViewById(R.id.notification_media_progress_bar); |
| 261 | mSeekBar.setOnSeekBarChangeListener(mSeekListener); |
| 262 | |
| 263 | mSeekBarElapsedTime = mSeekBarView.findViewById(R.id.notification_media_elapsed_time); |
| 264 | mSeekBarTotalTime = mSeekBarView.findViewById(R.id.notification_media_total_time); |
| 265 | |
Beth Thibodeau | d1eb0a8 | 2019-11-01 15:38:47 -0400 | [diff] [blame] | 266 | shouldUpdateListeners = true; |
| 267 | } |
| 268 | |
| 269 | if (shouldUpdateListeners) { |
| 270 | if (mView instanceof MediaNotificationView) { |
| 271 | MediaNotificationView mediaView = (MediaNotificationView) mView; |
| 272 | mediaView.addVisibilityListener(mVisibilityListener); |
| 273 | mView.addOnAttachStateChangeListener(mAttachStateListener); |
| 274 | } |
| 275 | |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 276 | if (mSeekBarTimer == null) { |
Beth Thibodeau | 2e76ad5 | 2019-05-29 11:29:25 -0400 | [diff] [blame] | 277 | if (mMediaController != null && canSeekMedia(mMediaController.getPlaybackState())) { |
Beth Thibodeau | 4b05fbc | 2019-05-16 12:51:20 -0400 | [diff] [blame] | 278 | // Log initial state, since it will not be updated |
| 279 | mMetricsLogger.write(newLog(MetricsEvent.TYPE_DETAIL, 1)); |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 280 | } else { |
Beth Thibodeau | 4b05fbc | 2019-05-16 12:51:20 -0400 | [diff] [blame] | 281 | setScrubberVisible(false); |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 282 | } |
Beth Thibodeau | 2e76ad5 | 2019-05-29 11:29:25 -0400 | [diff] [blame] | 283 | updateDuration(); |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 284 | startTimer(); |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 285 | mMediaController.registerCallback(mMediaCallback); |
| 286 | } |
| 287 | } |
| 288 | updateSeekBarTint(mSeekBarView); |
| 289 | } |
| 290 | |
| 291 | private void startTimer() { |
| 292 | clearTimer(); |
Beth Thibodeau | 4e4c86e | 2019-08-01 10:57:29 -0400 | [diff] [blame] | 293 | if (mIsViewVisible) { |
| 294 | mSeekBarTimer = new Timer(true /* isDaemon */); |
| 295 | mSeekBarTimer.schedule(new TimerTask() { |
| 296 | @Override |
| 297 | public void run() { |
| 298 | mHandler.post(mOnUpdateTimerTick); |
| 299 | } |
| 300 | }, 0, PROGRESS_UPDATE_INTERVAL); |
| 301 | } |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 302 | } |
| 303 | |
| 304 | private void clearTimer() { |
| 305 | if (mSeekBarTimer != null) { |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 306 | mSeekBarTimer.cancel(); |
| 307 | mSeekBarTimer.purge(); |
| 308 | mSeekBarTimer = null; |
| 309 | } |
| 310 | } |
| 311 | |
Beth Thibodeau | 4e4c86e | 2019-08-01 10:57:29 -0400 | [diff] [blame] | 312 | @Override |
| 313 | public void setRemoved() { |
| 314 | clearTimer(); |
| 315 | if (mMediaController != null) { |
| 316 | mMediaController.unregisterCallback(mMediaCallback); |
| 317 | } |
| 318 | if (mView instanceof MediaNotificationView) { |
| 319 | ((MediaNotificationView) mView).removeVisibilityListener(mVisibilityListener); |
| 320 | mView.removeOnAttachStateChangeListener(mAttachStateListener); |
| 321 | } |
| 322 | } |
| 323 | |
Beth Thibodeau | 2e76ad5 | 2019-05-29 11:29:25 -0400 | [diff] [blame] | 324 | private boolean canSeekMedia(@Nullable PlaybackState state) { |
| 325 | if (state == null) { |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 326 | return false; |
| 327 | } |
| 328 | |
Beth Thibodeau | 2e76ad5 | 2019-05-29 11:29:25 -0400 | [diff] [blame] | 329 | long actions = state.getActions(); |
Beth Thibodeau | 1c08164 | 2019-05-06 09:43:26 -0400 | [diff] [blame] | 330 | return ((actions & PlaybackState.ACTION_SEEK_TO) != 0); |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 331 | } |
| 332 | |
Beth Thibodeau | 4b05fbc | 2019-05-16 12:51:20 -0400 | [diff] [blame] | 333 | private void setScrubberVisible(boolean isVisible) { |
| 334 | if (mSeekBar == null || mSeekBar.isEnabled() == isVisible) { |
| 335 | return; |
| 336 | } |
| 337 | |
| 338 | mSeekBar.getThumb().setAlpha(isVisible ? 255 : 0); |
| 339 | mSeekBar.setEnabled(isVisible); |
| 340 | mMetricsLogger.write(newLog(MetricsEvent.TYPE_DETAIL, isVisible ? 1 : 0)); |
| 341 | } |
| 342 | |
Beth Thibodeau | 2e76ad5 | 2019-05-29 11:29:25 -0400 | [diff] [blame] | 343 | private void updateDuration() { |
| 344 | if (mMediaMetadata != null && mSeekBar != null) { |
| 345 | long duration = mMediaMetadata.getLong(MediaMetadata.METADATA_KEY_DURATION); |
| 346 | if (mDuration != duration) { |
| 347 | mDuration = duration; |
| 348 | mSeekBar.setMax((int) mDuration); |
| 349 | mSeekBarTotalTime.setText(millisecondsToTimeString(duration)); |
| 350 | } |
| 351 | } |
| 352 | } |
| 353 | |
| 354 | protected final Runnable mOnUpdateTimerTick = new Runnable() { |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 355 | @Override |
| 356 | public void run() { |
Beth Thibodeau | 4e937fa | 2019-04-24 15:15:09 -0400 | [diff] [blame] | 357 | if (mMediaController != null && mSeekBar != null) { |
Beth Thibodeau | 4e937fa | 2019-04-24 15:15:09 -0400 | [diff] [blame] | 358 | PlaybackState playbackState = mMediaController.getPlaybackState(); |
Beth Thibodeau | 2e76ad5 | 2019-05-29 11:29:25 -0400 | [diff] [blame] | 359 | if (playbackState != null) { |
| 360 | updatePlaybackUi(playbackState); |
Beth Thibodeau | 4e937fa | 2019-04-24 15:15:09 -0400 | [diff] [blame] | 361 | } else { |
Beth Thibodeau | 4e937fa | 2019-04-24 15:15:09 -0400 | [diff] [blame] | 362 | clearTimer(); |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 363 | } |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 364 | } else { |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 365 | clearTimer(); |
| 366 | } |
| 367 | } |
| 368 | }; |
| 369 | |
Beth Thibodeau | 2e76ad5 | 2019-05-29 11:29:25 -0400 | [diff] [blame] | 370 | private void updatePlaybackUi(PlaybackState state) { |
Beth Thibodeau | 4e4c86e | 2019-08-01 10:57:29 -0400 | [diff] [blame] | 371 | if (mSeekBar == null || mSeekBarElapsedTime == null) { |
| 372 | return; |
| 373 | } |
| 374 | |
Beth Thibodeau | 2e76ad5 | 2019-05-29 11:29:25 -0400 | [diff] [blame] | 375 | long position = state.getPosition(); |
| 376 | mSeekBar.setProgress((int) position); |
| 377 | |
| 378 | mSeekBarElapsedTime.setText(millisecondsToTimeString(position)); |
| 379 | |
| 380 | // Update scrubber in case available actions have changed |
| 381 | setScrubberVisible(canSeekMedia(state)); |
| 382 | } |
| 383 | |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 384 | private String millisecondsToTimeString(long milliseconds) { |
| 385 | long seconds = milliseconds / 1000; |
| 386 | String text = DateUtils.formatElapsedTime(seconds); |
| 387 | return text; |
Selim Cinek | df5bf61 | 2016-02-26 09:56:31 -0800 | [diff] [blame] | 388 | } |
| 389 | |
| 390 | @Override |
Selim Cinek | 131f1a4 | 2017-06-05 17:50:19 -0700 | [diff] [blame] | 391 | public void onContentUpdated(ExpandableNotificationRow row) { |
Selim Cinek | df5bf61 | 2016-02-26 09:56:31 -0800 | [diff] [blame] | 392 | // Reinspect the notification. Before the super call, because the super call also updates |
| 393 | // the transformation types and we need to have our values set by then. |
Selim Cinek | 414ad33 | 2017-02-24 19:06:12 -0800 | [diff] [blame] | 394 | resolveViews(); |
Selim Cinek | 131f1a4 | 2017-06-05 17:50:19 -0700 | [diff] [blame] | 395 | super.onContentUpdated(row); |
Selim Cinek | df5bf61 | 2016-02-26 09:56:31 -0800 | [diff] [blame] | 396 | } |
| 397 | |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 398 | private void updateSeekBarTint(View seekBarContainer) { |
| 399 | if (seekBarContainer == null) { |
| 400 | return; |
| 401 | } |
| 402 | |
| 403 | if (this.getNotificationHeader() == null) { |
| 404 | return; |
| 405 | } |
| 406 | |
| 407 | int tintColor = getNotificationHeader().getOriginalIconColor(); |
| 408 | mSeekBarElapsedTime.setTextColor(tintColor); |
| 409 | mSeekBarTotalTime.setTextColor(tintColor); |
Beth Thibodeau | 5500417 | 2019-05-30 17:52:08 -0400 | [diff] [blame] | 410 | mSeekBarTotalTime.setShadowLayer(1.5f, 1.5f, 1.5f, mBackgroundColor); |
Beth Thibodeau | cb39535 | 2019-01-25 15:39:54 -0500 | [diff] [blame] | 411 | |
| 412 | ColorStateList tintList = ColorStateList.valueOf(tintColor); |
| 413 | mSeekBar.setThumbTintList(tintList); |
| 414 | tintList = tintList.withAlpha(192); // 75% |
| 415 | mSeekBar.setProgressTintList(tintList); |
| 416 | tintList = tintList.withAlpha(128); // 50% |
| 417 | mSeekBar.setProgressBackgroundTintList(tintList); |
| 418 | } |
| 419 | |
Selim Cinek | df5bf61 | 2016-02-26 09:56:31 -0800 | [diff] [blame] | 420 | @Override |
| 421 | protected void updateTransformedTypes() { |
| 422 | // This also clears the existing types |
| 423 | super.updateTransformedTypes(); |
| 424 | if (mActions != null) { |
Selim Cinek | f88c629 | 2016-04-26 16:58:21 -0700 | [diff] [blame] | 425 | mTransformationHelper.addTransformedView(TransformableView.TRANSFORMING_VIEW_ACTIONS, |
Selim Cinek | df5bf61 | 2016-02-26 09:56:31 -0800 | [diff] [blame] | 426 | mActions); |
| 427 | } |
| 428 | } |
Selim Cinek | 63edaf2 | 2017-04-24 22:18:48 -0700 | [diff] [blame] | 429 | |
| 430 | @Override |
| 431 | public boolean isDimmable() { |
Selim Cinek | 74c2bad | 2017-11-27 15:09:36 +0100 | [diff] [blame] | 432 | return getCustomBackgroundColor() == 0; |
Selim Cinek | 63edaf2 | 2017-04-24 22:18:48 -0700 | [diff] [blame] | 433 | } |
Selim Cinek | 515b203 | 2017-11-15 10:20:19 -0800 | [diff] [blame] | 434 | |
| 435 | @Override |
Selim Cinek | 86bfcee | 2018-01-17 11:00:47 -0800 | [diff] [blame] | 436 | public boolean shouldClipToRounding(boolean topRounded, boolean bottomRounded) { |
Selim Cinek | 515b203 | 2017-11-15 10:20:19 -0800 | [diff] [blame] | 437 | return true; |
| 438 | } |
Beth Thibodeau | 4b05fbc | 2019-05-16 12:51:20 -0400 | [diff] [blame] | 439 | |
| 440 | /** |
| 441 | * Returns an initialized LogMaker for logging changes to the seekbar |
| 442 | * @return new LogMaker |
| 443 | */ |
| 444 | private LogMaker newLog(int event) { |
Ned Burns | 00b4b2d | 2019-10-17 22:09:27 -0400 | [diff] [blame] | 445 | String packageName = mRow.getEntry().getSbn().getPackageName(); |
Beth Thibodeau | 4b05fbc | 2019-05-16 12:51:20 -0400 | [diff] [blame] | 446 | |
| 447 | return new LogMaker(MetricsEvent.MEDIA_NOTIFICATION_SEEKBAR) |
| 448 | .setType(event) |
| 449 | .setPackageName(packageName); |
| 450 | } |
| 451 | |
| 452 | /** |
| 453 | * Returns an initialized LogMaker for logging changes with subtypes |
| 454 | * @return new LogMaker |
| 455 | */ |
| 456 | private LogMaker newLog(int event, int subtype) { |
Ned Burns | 00b4b2d | 2019-10-17 22:09:27 -0400 | [diff] [blame] | 457 | String packageName = mRow.getEntry().getSbn().getPackageName(); |
Beth Thibodeau | 4b05fbc | 2019-05-16 12:51:20 -0400 | [diff] [blame] | 458 | return new LogMaker(MetricsEvent.MEDIA_NOTIFICATION_SEEKBAR) |
| 459 | .setType(event) |
| 460 | .setSubtype(subtype) |
| 461 | .setPackageName(packageName); |
| 462 | } |
Selim Cinek | df5bf61 | 2016-02-26 09:56:31 -0800 | [diff] [blame] | 463 | } |