1 // Copyright 2019 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #ifndef ASH_LOGIN_UI_LOCK_SCREEN_MEDIA_CONTROLS_VIEW_H_ 6 #define ASH_LOGIN_UI_LOCK_SCREEN_MEDIA_CONTROLS_VIEW_H_ 7 8 #include "ash/ash_export.h" 9 #include "base/containers/flat_set.h" 10 #include "base/memory/weak_ptr.h" 11 #include "base/power_monitor/power_observer.h" 12 #include "base/timer/timer.h" 13 #include "mojo/public/cpp/bindings/receiver.h" 14 #include "mojo/public/cpp/bindings/remote.h" 15 #include "services/media_session/public/mojom/media_controller.mojom.h" 16 #include "services/media_session/public/mojom/media_session.mojom.h" 17 #include "ui/compositor/layer_animation_observer.h" 18 #include "ui/views/metadata/metadata_header_macros.h" 19 #include "ui/views/view.h" 20 21 namespace views { 22 class Button; 23 class Label; 24 class ImageView; 25 } // namespace views 26 27 namespace media_message_center { 28 class MediaControlsProgressView; 29 } 30 31 namespace ash { 32 33 namespace { 34 class MediaActionButton; 35 } // namespace 36 37 class MediaControlsHeaderView; 38 class NonAccessibleView; 39 40 class ASH_EXPORT LockScreenMediaControlsView 41 : public views::View, 42 public media_session::mojom::MediaControllerObserver, 43 public media_session::mojom::MediaControllerImageObserver, 44 public base::PowerObserver, 45 public ui::ImplicitAnimationObserver { 46 public: 47 METADATA_HEADER(LockScreenMediaControlsView); 48 // The name of the histogram that records the reason why the controls were 49 // hidden. 50 static const char kMediaControlsHideHistogramName[]; 51 52 // The name of the histogram that records whether the media controls were 53 // shown and the reason why. 54 static const char kMediaControlsShownHistogramName[]; 55 56 // The name of the histogram that records when a user interacts with the 57 // media controls. 58 static const char kMediaControlsUserActionHistogramName[]; 59 60 using MediaControlsEnabled = base::RepeatingCallback<bool()>; 61 62 // The reason why the media controls were hidden. This is recorded in 63 // metrics and new values should only be added to the end. 64 enum class HideReason { 65 kSessionChanged, 66 kDismissedByUser, 67 kUnlocked, 68 kDeviceSleep, 69 kMaxValue = kDeviceSleep 70 }; 71 72 // Whether the controls were shown or not shown and the reason why. This is 73 // recorded in metrics and new values should only be added to the end. 74 enum class Shown { 75 kNotShownControlsDisabled, 76 kNotShownNoSession, 77 kNotShownSessionPaused, 78 kShown, 79 kNotShownSessionSensitive, 80 kMaxValue = kNotShownSessionSensitive 81 }; 82 83 struct Callbacks { 84 Callbacks(); 85 ~Callbacks(); 86 87 // Called in |MediaSessionInfoChanged| to determine the visibility of the 88 // media controls. 89 MediaControlsEnabled media_controls_enabled; 90 91 // Called when the controls should be hidden on the lock screen. 92 base::RepeatingClosure hide_media_controls; 93 94 // Called when the controls should be drawn on the lock screen. 95 base::RepeatingClosure show_media_controls; 96 97 DISALLOW_COPY_AND_ASSIGN(Callbacks); 98 }; 99 100 explicit LockScreenMediaControlsView(const Callbacks& callbacks); 101 ~LockScreenMediaControlsView() override; 102 103 // views::View: 104 gfx::Size CalculatePreferredSize() const override; 105 void Layout() override; 106 void GetAccessibleNodeData(ui::AXNodeData* node_data) override; 107 void OnMouseEntered(const ui::MouseEvent& event) override; 108 void OnMouseExited(const ui::MouseEvent& event) override; 109 110 // media_session::mojom::MediaControllerObserver: 111 void MediaSessionInfoChanged( 112 media_session::mojom::MediaSessionInfoPtr session_info) override; 113 void MediaSessionMetadataChanged( 114 const base::Optional<media_session::MediaMetadata>& metadata) override; 115 void MediaSessionActionsChanged( 116 const std::vector<media_session::mojom::MediaSessionAction>& actions) 117 override; 118 void MediaSessionChanged( 119 const base::Optional<base::UnguessableToken>& request_id) override; 120 void MediaSessionPositionChanged( 121 const base::Optional<media_session::MediaPosition>& position) override; 122 123 // media_session::mojom::MediaControllerImageObserver: 124 void MediaControllerImageChanged( 125 media_session::mojom::MediaSessionImageType type, 126 const SkBitmap& bitmap) override; 127 128 // ui::ImplicitAnimationObserver: 129 void OnImplicitAnimationsCompleted() override; 130 131 // ui::EventHandler: 132 void OnGestureEvent(ui::GestureEvent* event) override; 133 134 // base::PowerObserver: 135 void OnSuspend() override; 136 137 void ButtonPressed(media_session::mojom::MediaSessionAction action); 138 139 void FlushForTesting(); 140 set_media_controller_for_testing(mojo::Remote<media_session::mojom::MediaController> controller)141 void set_media_controller_for_testing( 142 mojo::Remote<media_session::mojom::MediaController> controller) { 143 media_controller_remote_ = std::move(controller); 144 } 145 set_timer_for_testing(std::unique_ptr<base::OneShotTimer> test_timer)146 void set_timer_for_testing(std::unique_ptr<base::OneShotTimer> test_timer) { 147 hide_controls_timer_ = std::move(test_timer); 148 } 149 150 private: 151 friend class LockScreenMediaControlsViewTest; 152 153 // Hide the controls because of |reason|. 154 void Hide(HideReason reason); 155 156 void HideArtwork(); 157 158 // Set whether the controls should be shown and record the reason why. 159 void SetShown(Shown shown); 160 161 // Performs "SeekTo" through |media_controller_ptr_|. The seek time is 162 // calculated using |seek_progress| and the total duration of the media. 163 void SeekTo(double seek_progress); 164 165 // Hides the controls and stops media playback. 166 void Dismiss(); 167 168 // Sets the media artwork to |img|. If |img| is nullopt, the default artwork 169 // is set instead. 170 void SetArtwork(base::Optional<gfx::ImageSkia> img); 171 172 // Returns the rounded rectangle clip path for the current artwork. 173 SkPath GetArtworkClipPath() const; 174 175 // Updates the visibility of buttons on |button_row_| depending on what is 176 // available in the current media session. 177 void UpdateActionButtonsVisibility(); 178 179 // Toggles the media play/pause button between "play" and "pause" as 180 // necessary. 181 void SetIsPlaying(bool playing); 182 183 // Updates the y position and opacity of |contents_view_| during dragging. 184 void UpdateDrag(const gfx::Point& location_in_screen); 185 186 // If the drag velocity is past the threshold or the drag position is past 187 // the height threshold, this calls |HideControlsAnimation()|. Otherwise, this 188 // will call |ResetControlsAnimation()|. 189 void EndDrag(); 190 191 // Updates the opacity of |contents_view_| based on its current position. 192 void UpdateOpacity(); 193 194 // Animates |contents_view_| up and off the screen. 195 void RunHideControlsAnimation(); 196 197 // Animates |contents_view_| to its original position. 198 void RunResetControlsAnimation(); 199 200 // Used to control the active session. 201 mojo::Remote<media_session::mojom::MediaController> media_controller_remote_; 202 203 // Used to receive updates to the active media controller. 204 mojo::Receiver<media_session::mojom::MediaControllerObserver> 205 observer_receiver_{this}; 206 207 // Used to receive updates to the active media's icon. 208 mojo::Receiver<media_session::mojom::MediaControllerImageObserver> 209 icon_observer_receiver_{this}; 210 211 // Used to receive updates to the active media's artwork. 212 mojo::Receiver<media_session::mojom::MediaControllerImageObserver> 213 artwork_observer_receiver_{this}; 214 215 // The id of the current media session. It will be null if there is not 216 // a current session. 217 base::Optional<base::UnguessableToken> media_session_id_; 218 219 // The MediaPosition associated with the current media session. 220 base::Optional<media_session::MediaPosition> position_; 221 222 // Automatically hides the controls a few seconds if no media playing. 223 std::unique_ptr<base::OneShotTimer> hide_controls_timer_ = 224 std::make_unique<base::OneShotTimer>(); 225 226 // Make artwork view invisible if there is no artwork update after receiving 227 // an empty artwork. 228 std::unique_ptr<base::OneShotTimer> hide_artwork_timer_ = 229 std::make_unique<base::OneShotTimer>(); 230 231 // Caches the text to be read by screen readers describing the media controls 232 // view. 233 base::string16 accessible_name_; 234 235 // Set of enabled actions. 236 base::flat_set<media_session::mojom::MediaSessionAction> enabled_actions_; 237 238 // Contains the visible and draggable UI of the media controls. 239 views::View* contents_view_ = nullptr; 240 241 // The reason we hid the media controls. 242 base::Optional<HideReason> hide_reason_; 243 244 // Whether the controls were shown or not and the reason why. 245 base::Optional<Shown> shown_; 246 247 // Container views attached to |contents_view_|. 248 MediaControlsHeaderView* header_row_ = nullptr; 249 views::ImageView* session_artwork_ = nullptr; 250 views::Label* title_label_ = nullptr; 251 views::Label* artist_label_ = nullptr; 252 NonAccessibleView* button_row_ = nullptr; 253 MediaActionButton* play_pause_button_ = nullptr; 254 media_message_center::MediaControlsProgressView* progress_ = nullptr; 255 256 std::vector<views::Button*> media_action_buttons_; 257 258 // Callbacks. 259 const MediaControlsEnabled media_controls_enabled_; 260 const base::RepeatingClosure hide_media_controls_; 261 const base::RepeatingClosure show_media_controls_; 262 263 // The location of the initial gesture event in screen coordinates. 264 gfx::Point initial_drag_point_; 265 266 // The velocity of the gesture event. 267 float last_fling_velocity_ = 0; 268 269 // True if the user is in the process of gesture-dragging |contents_view_|. 270 bool is_in_drag_ = false; 271 272 base::WeakPtrFactory<LockScreenMediaControlsView> weak_ptr_factory_{this}; 273 274 DISALLOW_COPY_AND_ASSIGN(LockScreenMediaControlsView); 275 }; 276 277 } // namespace ash 278 279 #endif // ASH_LOGIN_UI_LOCK_SCREEN_MEDIA_CONTROLS_VIEW_H_ 280