Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(168)

Side by Side Diff: chrome/android/java/src/org/chromium/chrome/browser/media/remote/ExpandedControllerActivity.java

Issue 2888653002: [Cast,Android] Replace custom MediaController with android.widget.MediaController. (Closed)
Patch Set: Dedented the color in the xml file. Created 3 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « chrome/android/java/res/values/colors.xml ('k') | third_party/android_media/BUILD.gn » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package org.chromium.chrome.browser.media.remote; 5 package org.chromium.chrome.browser.media.remote;
6 6
7 import android.content.Context; 7 import android.content.Context;
8 import android.content.Intent; 8 import android.content.Intent;
9 import android.graphics.Bitmap; 9 import android.graphics.Bitmap;
10 import android.graphics.Color; 10 import android.graphics.Color;
11 import android.os.Bundle; 11 import android.os.Bundle;
12 import android.os.Handler; 12 import android.os.Handler;
13 import android.support.v4.app.FragmentActivity; 13 import android.support.v4.app.FragmentActivity;
14 import android.support.v4.media.TransportMediator;
15 import android.support.v4.media.TransportPerformer;
16 import android.text.TextUtils; 14 import android.text.TextUtils;
17 import android.view.KeyEvent; 15 import android.view.KeyEvent;
18 import android.view.View; 16 import android.view.View;
19 import android.view.ViewGroup; 17 import android.view.ViewGroup;
20 import android.view.Window; 18 import android.view.Window;
21 import android.view.WindowManager; 19 import android.view.WindowManager;
22 import android.widget.ImageView; 20 import android.widget.ImageView;
21 import android.widget.MediaController;
22 import android.widget.MediaController.MediaPlayerControl;
23 import android.widget.TextView; 23 import android.widget.TextView;
24 24
25 import com.google.android.gms.cast.CastMediaControlIntent; 25 import com.google.android.gms.cast.CastMediaControlIntent;
26 26
27 import org.chromium.chrome.R; 27 import org.chromium.chrome.R;
28 import org.chromium.chrome.browser.media.remote.RemoteVideoInfo.PlayerState; 28 import org.chromium.chrome.browser.media.remote.RemoteVideoInfo.PlayerState;
29 import org.chromium.chrome.browser.metrics.MediaNotificationUma; 29 import org.chromium.chrome.browser.metrics.MediaNotificationUma;
30 import org.chromium.third_party.android.media.MediaController;
31 30
32 /** 31 /**
33 * The activity that's opened by clicking the video flinging (casting) notificat ion. 32 * The activity that's opened by clicking the video flinging (casting) notificat ion.
34 * 33 *
35 * TODO(aberent): Refactor to merge some common logic with {@link CastNotificati onControl}. 34 * TODO(aberent): Refactor to merge some common logic with {@link CastNotificati onControl}.
36 */ 35 */
37 public class ExpandedControllerActivity 36 public class ExpandedControllerActivity
38 extends FragmentActivity implements MediaRouteController.UiListener { 37 extends FragmentActivity implements MediaRouteController.UiListener {
39 private static final int PROGRESS_UPDATE_PERIOD_IN_MS = 1000; 38 private static final int PROGRESS_UPDATE_PERIOD_IN_MS = 1000;
40 // The alpha value for the poster/placeholder image, an integer between 0 an d 256 (opaque). 39 // The alpha value for the poster/placeholder image, an integer between 0 an d 256 (opaque).
41 private static final int POSTER_IMAGE_ALPHA = 200; 40 private static final int POSTER_IMAGE_ALPHA = 200;
42 41
42 // Subclass of {@link android.widget.MediaController} that never hides itsel f.
43 class AlwaysShownMediaController extends MediaController {
44 public AlwaysShownMediaController(Context context) {
45 super(context);
46 }
47
48 @Override
49 public void show(int timeout) {
50 // Never auto-hide the controls.
51 super.show(0);
52 }
53
54 @Override
55 public boolean dispatchKeyEvent(KeyEvent event) {
56 int keyCode = event.getKeyCode();
57 // MediaController hides the controls when back or menu are pressed.
58 // Close the activity on back and ignore menu.
59 if (keyCode == KeyEvent.KEYCODE_BACK) {
60 finish();
61 return true;
62 } else if (keyCode == KeyEvent.KEYCODE_MENU) {
63 return true;
64 }
65 return super.dispatchKeyEvent(event);
66 }
67
68 @Override
69 public void hide() {
70 // Don't allow the controls to hide until explicitly asked to do so from the host
71 // activity.
72 }
73
74 /**
75 * Actually hides the controls which prevents some window leaks.
76 */
77 public void cleanup() {
78 super.hide();
79 }
80 };
81
43 private Handler mHandler; 82 private Handler mHandler;
44 // We don't use the standard android.media.MediaController, but a custom one . 83 private AlwaysShownMediaController mMediaController;
45 // See the class itself for details.
46 private MediaController mMediaController;
47 private FullscreenMediaRouteButton mMediaRouteButton; 84 private FullscreenMediaRouteButton mMediaRouteButton;
48 private MediaRouteController mMediaRouteController; 85 private MediaRouteController mMediaRouteController;
49 private RemoteVideoInfo mVideoInfo; 86 private RemoteVideoInfo mVideoInfo;
50 private String mScreenName; 87 private String mScreenName;
51 private TransportMediator mTransportMediator;
52 88
53 /** 89 /**
54 * Handle actions from on-screen media controls. 90 * Handle actions from on-screen media controls.
55 */ 91 */
56 private TransportPerformer mTransportPerformer = new TransportPerformer() { 92 private MediaPlayerControl mMediaPlayerControl = new MediaPlayerControl() {
57 @Override 93 @Override
58 public void onStart() { 94 public boolean canPause() {
95 return true;
96 }
97
98 @Override
99 public boolean canSeekBackward() {
100 return getDuration() > 0 && getCurrentPosition() > 0;
101 }
102
103 @Override
104 public boolean canSeekForward() {
105 return getDuration() > 0 && getCurrentPosition() < getDuration();
106 }
107
108 @Override
109 public int getAudioSessionId() {
110 // TODO(avayvod): not sure 0 is a valid value to return.
111 return 0;
112 }
113
114 @Override
115 public int getBufferPercentage() {
116 int duration = getDuration();
117 if (duration == 0) return 0;
118 return (getCurrentPosition() * 100) / duration;
119 }
120
121 @Override
122 public int getCurrentPosition() {
123 if (mMediaRouteController == null) return 0;
124 return (int) mMediaRouteController.getPosition();
125 }
126
127 @Override
128 public int getDuration() {
129 if (mMediaRouteController == null) return 0;
130 return (int) mMediaRouteController.getDuration();
131 }
132
133 @Override
134 public boolean isPlaying() {
135 if (mMediaRouteController == null) return false;
136 return mMediaRouteController.isPlaying();
137 }
138
139 @Override
140 public void pause() {
141 if (mMediaRouteController == null) return;
142 mMediaRouteController.pause();
143 RecordCastAction.recordFullscreenControlsAction(
144 RecordCastAction.FULLSCREEN_CONTROLS_PAUSE,
145 mMediaRouteController.getMediaStateListener() != null);
146 }
147
148 @Override
149 public void start() {
59 if (mMediaRouteController == null) return; 150 if (mMediaRouteController == null) return;
60 mMediaRouteController.resume(); 151 mMediaRouteController.resume();
61 RecordCastAction.recordFullscreenControlsAction( 152 RecordCastAction.recordFullscreenControlsAction(
62 RecordCastAction.FULLSCREEN_CONTROLS_RESUME, 153 RecordCastAction.FULLSCREEN_CONTROLS_RESUME,
63 mMediaRouteController.getMediaStateListener() != null); 154 mMediaRouteController.getMediaStateListener() != null);
64 } 155 }
65 156
66 @Override 157 @Override
67 public void onStop() { 158 public void seekTo(int pos) {
68 if (mMediaRouteController == null) return;
69 onPause();
70 mMediaRouteController.release();
71 }
72
73 @Override
74 public void onPause() {
75 if (mMediaRouteController == null) return;
76 mMediaRouteController.pause();
77 RecordCastAction.recordFullscreenControlsAction(
78 RecordCastAction.FULLSCREEN_CONTROLS_PAUSE,
79 mMediaRouteController.getMediaStateListener() != null);
80 }
81
82 @Override
83 public long onGetDuration() {
84 if (mMediaRouteController == null) return 0;
85 return mMediaRouteController.getDuration();
86 }
87
88 @Override
89 public long onGetCurrentPosition() {
90 if (mMediaRouteController == null) return 0;
91 return mMediaRouteController.getPosition();
92 }
93
94 @Override
95 public void onSeekTo(long pos) {
96 if (mMediaRouteController == null) return; 159 if (mMediaRouteController == null) return;
97 mMediaRouteController.seekTo(pos); 160 mMediaRouteController.seekTo(pos);
98 RecordCastAction.recordFullscreenControlsAction( 161 RecordCastAction.recordFullscreenControlsAction(
99 RecordCastAction.FULLSCREEN_CONTROLS_SEEK, 162 RecordCastAction.FULLSCREEN_CONTROLS_SEEK,
100 mMediaRouteController.getMediaStateListener() != null); 163 mMediaRouteController.getMediaStateListener() != null);
101 } 164 }
165 };
102 166
167 private Runnable mControlsUpdater = new Runnable() {
103 @Override 168 @Override
104 public boolean onIsPlaying() { 169 public void run() {
105 if (mMediaRouteController == null) return false; 170 mMediaController.show();
106 return mMediaRouteController.isPlaying();
107 }
108
109 @Override
110 public int onGetTransportControlFlags() {
111 int flags = TransportMediator.FLAG_KEY_MEDIA_REWIND
112 | TransportMediator.FLAG_KEY_MEDIA_FAST_FORWARD;
113 if (mMediaRouteController != null && mMediaRouteController.isPlaying ()) {
114 flags |= TransportMediator.FLAG_KEY_MEDIA_PAUSE;
115 } else {
116 flags |= TransportMediator.FLAG_KEY_MEDIA_PLAY;
117 }
118 return flags;
119 } 171 }
120 }; 172 };
121 173
122 private Runnable mProgressUpdater = new Runnable() {
123 @Override
124 public void run() {
125 if (mMediaRouteController.isPlaying()) {
126 mMediaController.updateProgress();
127 mHandler.postDelayed(this, PROGRESS_UPDATE_PERIOD_IN_MS);
128 } else {
129 mHandler.removeCallbacks(this);
130 }
131 }
132 };
133
134 @Override 174 @Override
135 protected void onCreate(Bundle savedInstanceState) { 175 protected void onCreate(Bundle savedInstanceState) {
136 super.onCreate(savedInstanceState); 176 super.onCreate(savedInstanceState);
137 177
138 MediaNotificationUma.recordClickSource(getIntent()); 178 MediaNotificationUma.recordClickSource(getIntent());
139 179
140 mMediaRouteController = 180 mMediaRouteController =
141 RemoteMediaPlayerController.instance().getCurrentlyPlayingMediaR outeController(); 181 RemoteMediaPlayerController.instance().getCurrentlyPlayingMediaR outeController();
142 182
143 if (mMediaRouteController == null || mMediaRouteController.routeIsDefaul tRoute()) { 183 if (mMediaRouteController == null || mMediaRouteController.routeIsDefaul tRoute()) {
144 // We don't want to do anything for the default (local) route 184 // We don't want to do anything for the default (local) route
145 finish(); 185 finish();
146 return; 186 return;
147 } 187 }
148 188
149 // Make the activity full screen. 189 // Make the activity full screen.
150 requestWindowFeature(Window.FEATURE_NO_TITLE); 190 requestWindowFeature(Window.FEATURE_NO_TITLE);
151 getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, 191 getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
152 WindowManager.LayoutParams.FLAG_FULLSCREEN); 192 WindowManager.LayoutParams.FLAG_FULLSCREEN);
153 193
154 // requestWindowFeature must be called before adding content. 194 // requestWindowFeature must be called before adding content.
155 setContentView(R.layout.expanded_cast_controller); 195 setContentView(R.layout.expanded_cast_controller);
156 mHandler = new Handler(); 196 mHandler = new Handler();
157 197
158 ViewGroup rootView = (ViewGroup) findViewById(android.R.id.content); 198 ViewGroup rootView = (ViewGroup) findViewById(android.R.id.content);
159 rootView.setBackgroundColor(Color.BLACK); 199 rootView.setBackgroundColor(Color.BLACK);
160 200
161 mMediaRouteController.addUiListener(this); 201 mMediaRouteController.addUiListener(this);
162 202
163 // Create transport controller to control video, giving the callback
164 // interface to receive actions from.
165 mTransportMediator = new TransportMediator(this, mTransportPerformer);
166
167 // Create and initialize the media control UI. 203 // Create and initialize the media control UI.
168 mMediaController = (MediaController) findViewById(R.id.cast_media_contro ller); 204 mMediaController = new AlwaysShownMediaController(this);
169 mMediaController.setMediaPlayer(mTransportMediator); 205 mMediaController.setEnabled(true);
206 mMediaController.setMediaPlayer(mMediaPlayerControl);
207 mMediaController.setAnchorView(rootView);
170 208
171 View button = getLayoutInflater().inflate(R.layout.cast_controller_media _route_button, 209 View button = getLayoutInflater().inflate(R.layout.cast_controller_media _route_button,
172 rootView, false); 210 rootView, false);
173 211
174 if (button instanceof FullscreenMediaRouteButton) { 212 if (button instanceof FullscreenMediaRouteButton) {
175 mMediaRouteButton = (FullscreenMediaRouteButton) button; 213 mMediaRouteButton = (FullscreenMediaRouteButton) button;
176 rootView.addView(mMediaRouteButton); 214 rootView.addView(mMediaRouteButton);
177 mMediaRouteButton.bringToFront(); 215 mMediaRouteButton.bringToFront();
178 mMediaRouteButton.initialize(mMediaRouteController); 216 mMediaRouteButton.initialize(mMediaRouteController);
179 } else { 217 } else {
180 mMediaRouteButton = null; 218 mMediaRouteButton = null;
181 } 219 }
182 220
183 // Initialize the video info. 221 // Initialize the video info.
184 setVideoInfo(new RemoteVideoInfo(null, 0, RemoteVideoInfo.PlayerState.ST OPPED, 0, null)); 222 mVideoInfo = new RemoteVideoInfo(null, 0, RemoteVideoInfo.PlayerState.ST OPPED, 0, null);
185
186 mMediaController.refresh();
187
188 scheduleProgressUpdate();
189 } 223 }
190 224
191 @Override 225 @Override
192 protected void onResume() { 226 protected void onResume() {
193 super.onResume(); 227 super.onResume();
228
194 if (mVideoInfo.state == PlayerState.FINISHED) finish(); 229 if (mVideoInfo.state == PlayerState.FINISHED) finish();
195 if (mMediaRouteController == null) return; 230 if (mMediaRouteController == null) return;
196 231
197 // Lifetime of the media element is bound to that of the {@link MediaSta teListener} 232 // Lifetime of the media element is bound to that of the {@link MediaSta teListener}
198 // of the {@link MediaRouteController}. 233 // of the {@link MediaRouteController}.
199 RecordCastAction.recordFullscreenControlsShown( 234 RecordCastAction.recordFullscreenControlsShown(
200 mMediaRouteController.getMediaStateListener() != null); 235 mMediaRouteController.getMediaStateListener() != null);
201 236
202 mMediaRouteController.prepareMediaRoute(); 237 mMediaRouteController.prepareMediaRoute();
203 238
204 ImageView iv = (ImageView) findViewById(R.id.cast_background_image); 239 ImageView iv = (ImageView) findViewById(R.id.cast_background_image);
205 if (iv == null) return; 240 if (iv == null) return;
206 Bitmap posterBitmap = mMediaRouteController.getPoster(); 241 Bitmap posterBitmap = mMediaRouteController.getPoster();
207 if (posterBitmap != null) iv.setImageBitmap(posterBitmap); 242 if (posterBitmap != null) iv.setImageBitmap(posterBitmap);
208 iv.setImageAlpha(POSTER_IMAGE_ALPHA); 243 iv.setImageAlpha(POSTER_IMAGE_ALPHA);
244
245 // Can't show the media controller until attached to window.
246 scheduleControlsUpdate();
209 } 247 }
210 248
211 @Override 249 @Override
212 protected void onDestroy() { 250 protected void onDestroy() {
213 cleanup(); 251 cleanup();
214 super.onDestroy(); 252 super.onDestroy();
215 } 253 }
216 254
217 @Override 255 @Override
218 public boolean dispatchKeyEvent(KeyEvent event) { 256 public boolean dispatchKeyEvent(KeyEvent event) {
219 int keyCode = event.getKeyCode(); 257 int keyCode = event.getKeyCode();
220 if ((keyCode != KeyEvent.KEYCODE_VOLUME_DOWN && keyCode != KeyEvent.KEYC ODE_VOLUME_UP) 258 if ((keyCode != KeyEvent.KEYCODE_VOLUME_DOWN && keyCode != KeyEvent.KEYC ODE_VOLUME_UP)
221 || mVideoInfo.state == PlayerState.FINISHED) { 259 || mVideoInfo.state == PlayerState.FINISHED) {
222 return super.dispatchKeyEvent(event); 260 return super.dispatchKeyEvent(event);
223 } 261 }
224 262
225 return handleVolumeKeyEvent(mMediaRouteController, event); 263 return handleVolumeKeyEvent(mMediaRouteController, event);
226 } 264 }
227 265
228 private void cleanup() { 266 private void cleanup() {
229 if (mHandler != null) mHandler.removeCallbacks(mProgressUpdater); 267 if (mHandler != null) mHandler.removeCallbacks(mControlsUpdater);
230 if (mMediaRouteController != null) mMediaRouteController.removeUiListene r(this); 268 if (mMediaRouteController != null) mMediaRouteController.removeUiListene r(this);
231 mMediaRouteController = null; 269 mMediaRouteController = null;
232 mProgressUpdater = null; 270 mControlsUpdater = null;
271 mMediaController.cleanup();
272 mMediaController = null;
233 } 273 }
234 274
235 /** 275 /**
236 * Sets the remote's video information to display. 276 * Sets the remote's video information to display.
237 */ 277 */
238 private final void setVideoInfo(RemoteVideoInfo videoInfo) { 278 private final void setVideoInfo(RemoteVideoInfo videoInfo) {
239 if ((mVideoInfo == null) ? (videoInfo == null) : mVideoInfo.equals(video Info)) return; 279 if ((mVideoInfo == null) ? (videoInfo == null) : mVideoInfo.equals(video Info)) return;
240 280
241 mVideoInfo = videoInfo; 281 mVideoInfo = videoInfo;
242 onVideoInfoChanged(); 282 updateUi();
243 } 283 }
244 284
245 private void scheduleProgressUpdate() { 285 private void scheduleControlsUpdate() {
246 mHandler.removeCallbacks(mProgressUpdater); 286 mHandler.removeCallbacks(mControlsUpdater);
247 if (mMediaRouteController.isPlaying()) { 287 mHandler.post(mControlsUpdater);
248 mHandler.post(mProgressUpdater);
249 }
250 } 288 }
251 289
252 /** 290 /**
253 * Sets the name to display for the device. 291 * Sets the name to display for the device.
254 */ 292 */
255 private void setScreenName(String screenName) { 293 private void setScreenName(String screenName) {
256 if (TextUtils.equals(mScreenName, screenName)) return; 294 if (TextUtils.equals(mScreenName, screenName)) return;
257 295
258 mScreenName = screenName; 296 mScreenName = screenName;
259 onScreenNameChanged();
260 }
261
262 private void onVideoInfoChanged() {
263 updateUi(); 297 updateUi();
264 } 298 }
265 299
266 private void onScreenNameChanged() {
267 updateUi();
268 }
269
270 private void updateUi() { 300 private void updateUi() {
271 if (mMediaController == null || mMediaRouteController == null) return; 301 if (mMediaController == null || mMediaRouteController == null) return;
272 302
273 String deviceName = mMediaRouteController.getRouteName(); 303 String deviceName = mMediaRouteController.getRouteName();
274 String castText = ""; 304 String castText = "";
275 if (deviceName != null) { 305 if (deviceName != null) {
276 castText = getResources().getString(R.string.cast_casting_video, dev iceName); 306 castText = getResources().getString(R.string.cast_casting_video, dev iceName);
277 } 307 }
278 TextView castTextView = (TextView) findViewById(R.id.cast_screen_title); 308 TextView castTextView = (TextView) findViewById(R.id.cast_screen_title);
279 castTextView.setText(castText); 309 castTextView.setText(castText);
280 310
281 mMediaController.refresh(); 311 scheduleControlsUpdate();
282 } 312 }
283 313
284 @Override 314 @Override
285 public void onRouteSelected(String name, MediaRouteController mediaRouteCont roller) { 315 public void onRouteSelected(String name, MediaRouteController mediaRouteCont roller) {
286 setScreenName(name); 316 setScreenName(name);
287 } 317 }
288 318
289 @Override 319 @Override
290 public void onRouteUnselected(MediaRouteController mediaRouteController) { 320 public void onRouteUnselected(MediaRouteController mediaRouteController) {
291 finish(); 321 finish();
292 } 322 }
293 323
294 @Override 324 @Override
295 public void onPrepared(MediaRouteController mediaRouteController) { 325 public void onPrepared(MediaRouteController mediaRouteController) {
296 // No implementation. 326 // No implementation.
297 } 327 }
298 328
299 @Override 329 @Override
300 public void onError(int error, String message) { 330 public void onError(int error, String message) {
301 if (error == CastMediaControlIntent.ERROR_CODE_SESSION_START_FAILED) fin ish(); 331 if (error == CastMediaControlIntent.ERROR_CODE_SESSION_START_FAILED) fin ish();
302 } 332 }
303 333
304 @Override 334 @Override
305 public void onPlaybackStateChanged(PlayerState newState) { 335 public void onPlaybackStateChanged(PlayerState newState) {
306 RemoteVideoInfo videoInfo = new RemoteVideoInfo(mVideoInfo); 336 RemoteVideoInfo videoInfo = new RemoteVideoInfo(mVideoInfo);
307 videoInfo.state = newState; 337 videoInfo.state = newState;
308 setVideoInfo(videoInfo); 338 setVideoInfo(videoInfo);
309 339
310 scheduleProgressUpdate(); 340 scheduleControlsUpdate();
311 341
312 if (newState == PlayerState.FINISHED || newState == PlayerState.INVALIDA TED) { 342 if (newState == PlayerState.FINISHED || newState == PlayerState.INVALIDA TED) {
313 // If we are switching to a finished state, stop the notifications. 343 // If we are switching to a finished state, stop the notifications.
314 finish(); 344 finish();
315 } 345 }
316 } 346 }
317 347
318 @Override 348 @Override
319 public void onDurationUpdated(long durationMillis) { 349 public void onDurationUpdated(long durationMillis) {
320 RemoteVideoInfo videoInfo = new RemoteVideoInfo(mVideoInfo); 350 RemoteVideoInfo videoInfo = new RemoteVideoInfo(mVideoInfo);
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after
368 * @param context the Context to start this activity within. 398 * @param context the Context to start this activity within.
369 */ 399 */
370 public static void startActivity(Context context) { 400 public static void startActivity(Context context) {
371 if (context == null) return; 401 if (context == null) return;
372 402
373 Intent intent = new Intent(context, ExpandedControllerActivity.class); 403 Intent intent = new Intent(context, ExpandedControllerActivity.class);
374 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 404 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
375 context.startActivity(intent); 405 context.startActivity(intent);
376 } 406 }
377 } 407 }
OLDNEW
« no previous file with comments | « chrome/android/java/res/values/colors.xml ('k') | third_party/android_media/BUILD.gn » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698