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