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

Side by Side Diff: chrome/android/java/src/org/chromium/chrome/browser/media/ui/MediaNotificationManager.java

Issue 1847063005: [Media, UI] Change MediaNotification style to MediaStyle (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: fix tests and rebase Created 4 years, 8 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
OLDNEW
1 // Copyright 2015 The Chromium Authors. All rights reserved. 1 // Copyright 2015 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.ui; 5 package org.chromium.chrome.browser.media.ui;
6 6
7 import android.app.Notification; 7 import android.app.Notification;
8 import android.app.PendingIntent; 8 import android.app.PendingIntent;
9 import android.app.Service; 9 import android.app.Service;
10 import android.content.BroadcastReceiver; 10 import android.content.BroadcastReceiver;
11 import android.content.ComponentName; 11 import android.content.ComponentName;
12 import android.content.Context; 12 import android.content.Context;
13 import android.content.Intent; 13 import android.content.Intent;
14 import android.content.IntentFilter; 14 import android.content.IntentFilter;
15 import android.graphics.Bitmap; 15 import android.graphics.Bitmap;
16 import android.graphics.drawable.BitmapDrawable; 16 import android.graphics.drawable.BitmapDrawable;
17 import android.graphics.drawable.Drawable; 17 import android.graphics.drawable.Drawable;
18 import android.media.AudioManager; 18 import android.media.AudioManager;
19 import android.os.Build; 19 import android.os.Build;
20 import android.os.IBinder; 20 import android.os.IBinder;
21 import android.support.v4.app.NotificationCompat;
22 import android.support.v4.app.NotificationManagerCompat; 21 import android.support.v4.app.NotificationManagerCompat;
23 import android.support.v4.media.MediaMetadataCompat; 22 import android.support.v4.media.MediaMetadataCompat;
24 import android.support.v4.media.session.MediaSessionCompat; 23 import android.support.v4.media.session.MediaSessionCompat;
25 import android.support.v4.media.session.PlaybackStateCompat; 24 import android.support.v4.media.session.PlaybackStateCompat;
25 import android.support.v7.app.NotificationCompat;
26 import android.support.v7.media.MediaRouter; 26 import android.support.v7.media.MediaRouter;
27 import android.text.TextUtils; 27 import android.text.TextUtils;
28 import android.util.SparseArray; 28 import android.util.SparseArray;
29 import android.view.KeyEvent; 29 import android.view.KeyEvent;
30 import android.view.View; 30 import android.view.View;
31 import android.widget.RemoteViews; 31 import android.widget.RemoteViews;
32 32
33 import org.chromium.base.ApiCompatibilityUtils; 33 import org.chromium.base.ApiCompatibilityUtils;
34 import org.chromium.base.VisibleForTesting; 34 import org.chromium.base.VisibleForTesting;
35 import org.chromium.chrome.R; 35 import org.chromium.chrome.R;
36 import org.chromium.chrome.browser.ChromeFeatureList;
36 37
37 import javax.annotation.Nullable; 38 import javax.annotation.Nullable;
38 39
39 /** 40 /**
40 * A class for notifications that provide information and optional media control s for a given media. 41 * A class for notifications that provide information and optional media control s for a given media.
41 * Internally implements a Service for transforming notification Intents into 42 * Internally implements a Service for transforming notification Intents into
42 * {@link MediaNotificationListener} calls for all registered listeners. 43 * {@link MediaNotificationListener} calls for all registered listeners.
43 * There's one service started for a distinct notification id. 44 * There's one service started for a distinct notification id.
44 */ 45 */
45 public class MediaNotificationManager { 46 public class MediaNotificationManager {
47 private static final String TAG = "MediaNotification";
46 48
47 private static final String TAG = "MediaNotification"; 49 private static final int WEARABLE_NOTIFICATION_BACKGROUND_WIDTH = 400;
50 private static final int WEARABLE_NOTIFICATION_BACKGROUND_HEIGHT = 400;
mlamouri (slow - plz ping) 2016/04/07 19:47:34 Where did you find these numbers?
Zhiqiang Zhang (Slow) 2016/04/11 09:26:28 http://developer.android.com/training/wearables/no
48 51
49 // We're always used on the UI thread but the LOCK is required by lint when creating the 52 // We're always used on the UI thread but the LOCK is required by lint when creating the
50 // singleton. 53 // singleton.
51 private static final Object LOCK = new Object(); 54 private static final Object LOCK = new Object();
52 55
53 // Maps the notification ids to their corresponding notification managers. 56 // Maps the notification ids to their corresponding notification managers.
54 private static SparseArray<MediaNotificationManager> sManagers; 57 private static SparseArray<MediaNotificationManager> sManagers;
55 58
56 /** 59 /**
57 * Service used to transform intent requests triggered from the notification into 60 * Service used to transform intent requests triggered from the notification into
(...skipping 415 matching lines...) Expand 10 before | Expand all | Expand 10 after
473 } 476 }
474 mContext.stopService(createIntent(mContext)); 477 mContext.stopService(createIntent(mContext));
475 mMediaNotificationInfo = null; 478 mMediaNotificationInfo = null;
476 } 479 }
477 480
478 private void hideNotification(int tabId) { 481 private void hideNotification(int tabId) {
479 if (mMediaNotificationInfo == null || tabId != mMediaNotificationInfo.ta bId) return; 482 if (mMediaNotificationInfo == null || tabId != mMediaNotificationInfo.ta bId) return;
480 clearNotification(); 483 clearNotification();
481 } 484 }
482 485
483 private RemoteViews createContentView() {
484 RemoteViews contentView =
485 new RemoteViews(mContext.getPackageName(), R.layout.playback_not ification_bar);
486
487 // By default, play/pause button is the only one.
488 int playPauseButtonId = R.id.button1;
489 // On Android pre-L, dismissing the notification when the service is no longer in foreground
490 // doesn't work. Instead, a STOP button is shown.
491 if (mMediaNotificationInfo.supportsSwipeAway()
492 && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP
493 || mMediaNotificationInfo.supportsStop()) {
494 contentView.setOnClickPendingIntent(R.id.button1,
495 createPendingIntent(ListenerService.ACTION_STOP));
496 contentView.setContentDescription(R.id.button1, mStopDescription);
497
498 // If the play/pause needs to be shown, it moves over to the second button from the end.
499 playPauseButtonId = R.id.button2;
500 }
501
502 contentView.setTextViewText(R.id.title, mMediaNotificationInfo.metadata. getTitle());
503 contentView.setTextViewText(R.id.status, mMediaNotificationInfo.origin);
504 if (mNotificationIcon != null) {
505 contentView.setImageViewBitmap(R.id.icon, mNotificationIcon);
506 } else {
507 contentView.setImageViewResource(R.id.icon, mMediaNotificationInfo.i con);
508 }
509
510 if (mMediaNotificationInfo.supportsPlayPause()) {
511 if (mMediaNotificationInfo.isPaused) {
512 contentView.setImageViewResource(playPauseButtonId, R.drawable.i c_vidcontrol_play);
513 contentView.setContentDescription(playPauseButtonId, mPlayDescri ption);
514 contentView.setOnClickPendingIntent(playPauseButtonId,
515 createPendingIntent(ListenerService.ACTION_PLAY));
516 } else {
517 // If we're here, the notification supports play/pause button an d is playing.
518 contentView.setImageViewResource(playPauseButtonId, R.drawable.i c_vidcontrol_pause);
519 contentView.setContentDescription(playPauseButtonId, mPauseDescr iption);
520 contentView.setOnClickPendingIntent(playPauseButtonId,
521 createPendingIntent(ListenerService.ACTION_PAUSE));
522 }
523
524 contentView.setViewVisibility(playPauseButtonId, View.VISIBLE);
525 } else {
526 contentView.setViewVisibility(playPauseButtonId, View.GONE);
527 }
528
529 return contentView;
530 }
531
532 private MediaMetadataCompat createMetadata() { 486 private MediaMetadataCompat createMetadata() {
533 MediaMetadataCompat.Builder metadataBuilder = new MediaMetadataCompat.Bu ilder(); 487 MediaMetadataCompat.Builder metadataBuilder = new MediaMetadataCompat.Bu ilder();
534 488
535 // Choose the image to use as the icon. 489 // Choose the image to use as the icon.
536 Bitmap mediaSessionImage = mMediaNotificationInfo.image == null ? mDefau ltMediaSessionImage 490 Bitmap mediaSessionImage = mMediaNotificationInfo.image == null ? mDefau ltMediaSessionImage
537 : mMediaNotificationInfo.image; 491 : mMediaNotificationInfo.image;
538 492
539 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 493 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
540 metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_T ITLE, 494 metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_T ITLE,
541 mMediaNotificationInfo.metadata.getTitle()); 495 mMediaNotificationInfo.metadata.getTitle());
542 metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_S UBTITLE, 496 metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_S UBTITLE,
543 mMediaNotificationInfo.origin); 497 mMediaNotificationInfo.origin);
544 metadataBuilder.putBitmap(MediaMetadataCompat.METADATA_KEY_DISPLAY_I CON, 498 if (mMediaNotificationInfo.largeIcon == null) {
545 mediaSessionImage); 499 metadataBuilder.putBitmap(MediaMetadataCompat.METADATA_KEY_DISPL AY_ICON,
500 mediaSessionImage);
501 } else {
502 metadataBuilder.putBitmap(MediaMetadataCompat.METADATA_KEY_DISPL AY_ICON,
503 scaleBitmapForWearable(mMediaNotificationInfo.largeIcon) );
504 }
546 // METADATA_KEY_ART is optional and should only be used if we can pr ovide something 505 // METADATA_KEY_ART is optional and should only be used if we can pr ovide something
547 // better than the default image. 506 // better than the default image.
mlamouri (slow - plz ping) 2016/04/07 19:47:34 Should we try to use METADATA_KEY_ART with the lar
Zhiqiang Zhang (Slow) 2016/04/11 09:26:28 Done. Also I merged MediaNotificationInfo.image an
548 if (mMediaNotificationInfo.image != null) { 507 if (mMediaNotificationInfo.image != null) {
549 metadataBuilder.putBitmap(MediaMetadataCompat.METADATA_KEY_ART, 508 metadataBuilder.putBitmap(MediaMetadataCompat.METADATA_KEY_ART,
550 mMediaNotificationInfo.image); 509 mMediaNotificationInfo.image);
551 } 510 }
552 } else { 511 } else {
553 metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_TITLE, 512 metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_TITLE,
554 mMediaNotificationInfo.metadata.getTitle()); 513 mMediaNotificationInfo.metadata.getTitle());
555 metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, 514 metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_ARTIST,
556 mMediaNotificationInfo.origin); 515 mMediaNotificationInfo.origin);
557 metadataBuilder.putBitmap(MediaMetadataCompat.METADATA_KEY_ART, medi aSessionImage); 516 if (mMediaNotificationInfo.largeIcon == null) {
517 metadataBuilder.putBitmap(MediaMetadataCompat.METADATA_KEY_ART, mediaSessionImage);
518 } else {
519 metadataBuilder.putBitmap(MediaMetadataCompat.METADATA_KEY_ART,
520 mMediaNotificationInfo.largeIcon);
521 }
558 } 522 }
559 523
560 if (!TextUtils.isEmpty(mMediaNotificationInfo.metadata.getArtist())) { 524 if (!TextUtils.isEmpty(mMediaNotificationInfo.metadata.getArtist())) {
561 metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, 525 metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_ARTIST,
562 mMediaNotificationInfo.metadata.getArtist()); 526 mMediaNotificationInfo.metadata.getArtist());
563 } 527 }
564 if (!TextUtils.isEmpty(mMediaNotificationInfo.metadata.getAlbum())) { 528 if (!TextUtils.isEmpty(mMediaNotificationInfo.metadata.getAlbum())) {
565 metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_ALBUM, 529 metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_ALBUM,
566 mMediaNotificationInfo.metadata.getAlbum()); 530 mMediaNotificationInfo.metadata.getAlbum());
567 } 531 }
568 532
569 return metadataBuilder.build(); 533 return metadataBuilder.build();
570 } 534 }
571 535
572 private void updateNotification() { 536 private void updateNotification() {
573 if (mService == null) return; 537 if (mService == null) return;
574 538
575 if (mMediaNotificationInfo == null) return; 539 if (mMediaNotificationInfo == null) return;
576 540
577 // Android doesn't badge the icons for RemoteViews automatically when 541 updateMediaSession();
578 // running the app under the Work profile. 542
579 if (mNotificationIcon == null) { 543 mNotificationBuilder = new NotificationCompat.Builder(mContext);
580 Drawable notificationIconDrawable = ApiCompatibilityUtils.getUserBad gedIcon( 544 if (ChromeFeatureList.isEnabled(ChromeFeatureList.USE_NEW_MEDIA_NOTIFICA TION)) {
581 mContext, mMediaNotificationInfo.icon); 545 setMediaStyleLayoutForNotificationBuilder(mNotificationBuilder);
582 mNotificationIcon = drawableToBitmap(notificationIconDrawable); 546 } else {
547 setCustomLayoutForNotificationBuilder(mNotificationBuilder);
583 } 548 }
584 549 // TODO: smallIcon set to Chrome's icon?
585 if (mNotificationBuilder == null) { 550 mNotificationBuilder.setSmallIcon(mMediaNotificationInfo.icon);
586 mNotificationBuilder = new NotificationCompat.Builder(mContext) 551 mNotificationBuilder.setAutoCancel(false);
587 .setSmallIcon(mMediaNotificationInfo.icon) 552 mNotificationBuilder.setLocalOnly(true);
588 .setAutoCancel(false) 553 mNotificationBuilder.setDeleteIntent(createPendingIntent(ListenerService .ACTION_STOP));
589 .setLocalOnly(true)
590 .setDeleteIntent(createPendingIntent(ListenerService.ACTION_STOP ));
591 }
592 554
593 if (mMediaNotificationInfo.supportsSwipeAway()) { 555 if (mMediaNotificationInfo.supportsSwipeAway()) {
594 mNotificationBuilder.setOngoing(!mMediaNotificationInfo.isPaused); 556 mNotificationBuilder.setOngoing(!mMediaNotificationInfo.isPaused);
595 } 557 }
596 558
597 // The intent will currently only be null when using a custom tab. 559 // The intent will currently only be null when using a custom tab.
598 // TODO(avayvod) work out what we should do in this case. See https://cr bug.com/585395. 560 // TODO(avayvod) work out what we should do in this case. See https://cr bug.com/585395.
599 if (mMediaNotificationInfo.contentIntent != null) { 561 if (mMediaNotificationInfo.contentIntent != null) {
600 mNotificationBuilder.setContentIntent(PendingIntent.getActivity(mCon text, 562 mNotificationBuilder.setContentIntent(PendingIntent.getActivity(mCon text,
601 mMediaNotificationInfo.tabId, 563 mMediaNotificationInfo.tabId,
602 mMediaNotificationInfo.contentIntent, 0)); 564 mMediaNotificationInfo.contentIntent, 0));
603 } 565 }
604 566
605 mNotificationBuilder.setContent(createContentView());
606 mNotificationBuilder.setVisibility( 567 mNotificationBuilder.setVisibility(
607 mMediaNotificationInfo.isPrivate ? NotificationCompat.VISIBILITY _PRIVATE 568 mMediaNotificationInfo.isPrivate ? NotificationCompat.VISIBILITY _PRIVATE
608 : NotificationCompat.VISIBILITY _PUBLIC); 569 : NotificationCompat.VISIBILITY _PUBLIC);
609 570
610
611 if (mMediaNotificationInfo.supportsPlayPause()) {
612
613 if (mMediaSession == null) mMediaSession = createMediaSession();
614 try {
615 // Tell the MediaRouter about the session, so that Chrome can co ntrol the volume
616 // on the remote cast device (if any).
617 // Pre-MR1 versions of JB do not have the complete MediaRouter A PIs,
618 // so getting the MediaRouter instance will throw an exception.
619 MediaRouter.getInstance(mContext).setMediaSessionCompat(mMediaSe ssion);
620 } catch (NoSuchMethodError e) {
621 // Do nothing. Chrome can't be casting without a MediaRouter, so there is nothing
622 // to do here.
623 }
624 mMediaSession.setMetadata(createMetadata());
625
626 PlaybackStateCompat.Builder playbackStateBuilder = new PlaybackState Compat.Builder()
627 .setActions(PlaybackStateCompat.ACTION_PLAY | PlaybackStateC ompat.ACTION_PAUSE);
628 if (mMediaNotificationInfo.isPaused) {
629 playbackStateBuilder.setState(PlaybackStateCompat.STATE_PAUSED,
630 PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN, 1.0f);
631 } else {
632 playbackStateBuilder.setState(PlaybackStateCompat.STATE_PLAYING,
633 PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN, 1.0f);
634 }
635 mMediaSession.setPlaybackState(playbackStateBuilder.build());
636 }
637
638 Notification notification = mNotificationBuilder.build(); 571 Notification notification = mNotificationBuilder.build();
639 572
640 // We keep the service as a foreground service while the media is playin g. When it is not, 573 // We keep the service as a foreground service while the media is playin g. When it is not,
641 // the service isn't stopped but is no longer in foreground, thus at a l ower priority. 574 // the service isn't stopped but is no longer in foreground, thus at a l ower priority.
642 // While the service is in foreground, the associated notification can't be swipped away. 575 // While the service is in foreground, the associated notification can't be swipped away.
643 // Moving it back to background allows the user to remove the notificati on. 576 // Moving it back to background allows the user to remove the notificati on.
644 if (mMediaNotificationInfo.supportsSwipeAway() && mMediaNotificationInfo .isPaused) { 577 if (mMediaNotificationInfo.supportsSwipeAway() && mMediaNotificationInfo .isPaused) {
645 mService.stopForeground(false /* removeNotification */); 578 mService.stopForeground(false /* removeNotification */);
646 579
647 NotificationManagerCompat manager = NotificationManagerCompat.from(m Context); 580 NotificationManagerCompat manager = NotificationManagerCompat.from(m Context);
648 manager.notify(mMediaNotificationInfo.id, notification); 581 manager.notify(mMediaNotificationInfo.id, notification);
649 } else { 582 } else {
650 mService.startForeground(mMediaNotificationInfo.id, notification); 583 mService.startForeground(mMediaNotificationInfo.id, notification);
651 } 584 }
652 } 585 }
653 586
587 private void updateMediaSession() {
588 if (!mMediaNotificationInfo.supportsPlayPause()) return;
589
590 if (mMediaSession == null) mMediaSession = createMediaSession();
591
592 try {
593 // Tell the MediaRouter about the session, so that Chrome can contro l the volume
594 // on the remote cast device (if any).
595 // Pre-MR1 versions of JB do not have the complete MediaRouter APIs,
596 // so getting the MediaRouter instance will throw an exception.
597 MediaRouter.getInstance(mContext).setMediaSessionCompat(mMediaSessio n);
598 } catch (NoSuchMethodError e) {
599 // Do nothing. Chrome can't be casting without a MediaRouter, so the re is nothing
600 // to do here.
601 }
602
603 mMediaSession.setMetadata(createMetadata());
604
605 PlaybackStateCompat.Builder playbackStateBuilder = new PlaybackStateComp at.Builder()
606 .setActions(PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompa t.ACTION_PAUSE);
607 if (mMediaNotificationInfo.isPaused) {
608 playbackStateBuilder.setState(PlaybackStateCompat.STATE_PAUSED,
609 PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN, 1.0f);
610 } else {
611 // If notification only supports stop, still pretend
612 playbackStateBuilder.setState(PlaybackStateCompat.STATE_PLAYING,
613 PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN, 1.0f);
614 }
615 mMediaSession.setPlaybackState(playbackStateBuilder.build());
616 }
617
654 private MediaSessionCompat createMediaSession() { 618 private MediaSessionCompat createMediaSession() {
655 MediaSessionCompat mediaSession = new MediaSessionCompat( 619 MediaSessionCompat mediaSession = new MediaSessionCompat(
656 mContext, 620 mContext,
657 mContext.getString(R.string.app_name), 621 mContext.getString(R.string.app_name),
658 new ComponentName(mContext.getPackageName(), 622 new ComponentName(mContext.getPackageName(),
659 getButtonReceiverClassName()), 623 getButtonReceiverClassName()),
660 null); 624 null);
661 mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS 625 mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS
662 | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS); 626 | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
663 mediaSession.setCallback(mMediaSessionCallback); 627 mediaSession.setCallback(mMediaSessionCallback);
664 628
665 // TODO(mlamouri): the following code is to work around a bug that hopef ully 629 // TODO(mlamouri): the following code is to work around a bug that hopef ully
666 // MediaSessionCompat will handle directly. see b/24051980. 630 // MediaSessionCompat will handle directly. see b/24051980.
667 try { 631 try {
668 mediaSession.setActive(true); 632 mediaSession.setActive(true);
669 } catch (NullPointerException e) { 633 } catch (NullPointerException e) {
670 // Some versions of KitKat do not support AudioManager.registerMedia ButtonIntent 634 // Some versions of KitKat do not support AudioManager.registerMedia ButtonIntent
671 // with a PendingIntent. They will throw a NullPointerException, in which case 635 // with a PendingIntent. They will throw a NullPointerException, in which case
672 // they should be able to activate a MediaSessionCompat with only tr ansport 636 // they should be able to activate a MediaSessionCompat with only tr ansport
673 // controls. 637 // controls.
674 mediaSession.setActive(false); 638 mediaSession.setActive(false);
675 mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONT ROLS); 639 mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONT ROLS);
676 mediaSession.setActive(true); 640 mediaSession.setActive(true);
677 } 641 }
678 return mediaSession; 642 return mediaSession;
679 } 643 }
680 644
645 private void setMediaStyleLayoutForNotificationBuilder(NotificationCompat.Bu ilder builder) {
646 NotificationCompat.MediaStyle style = new NotificationCompat.MediaStyle( );
647 if (mMediaSession != null) {
mlamouri (slow - plz ping) 2016/04/07 19:47:34 Is such a thing even possible?
Zhiqiang Zhang (Slow) 2016/04/11 09:26:28 Done. Merged all logic when mNotificationInfo.supp
648 style.setMediaSession(mMediaSession.getSessionToken());
649 }
650
651 // TODO: with custom notifications, can do:
652 // - setContentTitle for track title,
653 // - setContetText() for track artist,
654 // - setSubText() for track album
655 // or use subText() for origin and setContentText for artist + album.
mlamouri (slow - plz ping) 2016/04/07 19:47:34 After we ship the new style, we should see how to
Zhiqiang Zhang (Slow) 2016/04/11 09:26:28 Done.
656 builder.setContentTitle(mMediaNotificationInfo.metadata.getTitle());
657 builder.setContentText(mMediaNotificationInfo.origin);
658 // TODO: have our own "Media" icon so we can have large resolution in ca se of we have no
659 // large icon?
660 if (mMediaNotificationInfo.largeIcon != null) {
661 builder.setLargeIcon(mMediaNotificationInfo.largeIcon);
662 } else {
663 builder.setLargeIcon(mDefaultMediaSessionImage);
664 }
665 builder.setShowWhen(false);
666
667 if (mMediaNotificationInfo.supportsPlayPause()) {
mlamouri (slow - plz ping) 2016/04/07 19:47:34 Add a TODO: if it doesn't supports play/pause, we
Zhiqiang Zhang (Slow) 2016/04/11 09:26:28 The logic is already that. Added comments.
668 if (mMediaNotificationInfo.isPaused) {
669 builder.addAction(R.drawable.ic_vidcontrol_play, mPlayDescriptio n,
670 createPendingIntent(ListenerService.ACTION_PLAY));
671 } else {
672 // If we're here, the notification supports play/pause button an d is playing.
673 builder.addAction(R.drawable.ic_vidcontrol_pause, mPauseDescript ion,
674 createPendingIntent(ListenerService.ACTION_PAUSE));
675 }
676 style.setShowActionsInCompactView(0);
677 }
678
679 if (mMediaNotificationInfo.supportsStop()) {
680 builder.addAction(R.drawable.ic_vidcontrol_stop, mStopDescription,
681 createPendingIntent(ListenerService.ACTION_STOP));
682 }
683 if (mMediaSession != null) {
684 builder.setStyle(style);
685 }
686 }
687
688 private void setCustomLayoutForNotificationBuilder(NotificationCompat.Builde r builder) {
689 builder.setContent(createContentView());
690 }
691
692 private RemoteViews createContentView() {
693 RemoteViews contentView =
694 new RemoteViews(mContext.getPackageName(), R.layout.playback_no tification_bar);
695
696 // By default, play/pause button is the only one.
697 int playPauseButtonId = R.id.button1;
698 // On Android pre-L, dismissing the notification when the service is no longer in foreground
699 // doesn't work. Instead, a STOP button is shown.
700 if (mMediaNotificationInfo.supportsSwipeAway()
701 && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP
702 || mMediaNotificationInfo.supportsStop()) {
703 contentView.setOnClickPendingIntent(R.id.button1,
704 createPendingIntent(ListenerService.ACTION_STOP));
705 contentView.setContentDescription(R.id.button1, mStopDescription);
706
707 // If the play/pause needs to be shown, it moves over to the second button from the end.
708 playPauseButtonId = R.id.button2;
709 }
710
711 contentView.setTextViewText(R.id.title, mMediaNotificationInfo.metadata. getTitle());
712 contentView.setTextViewText(R.id.status, mMediaNotificationInfo.origin);
713
714 // Android doesn't badge the icons for RemoteViews automatically when
715 // running the app under the Work profile.
716 if (mNotificationIcon == null) {
717 Drawable notificationIconDrawable = ApiCompatibilityUtils.getUserBad gedIcon(
718 mContext, mMediaNotificationInfo.icon);
719 mNotificationIcon = drawableToBitmap(notificationIconDrawable);
720 }
721
722 if (mNotificationIcon != null) {
723 contentView.setImageViewBitmap(R.id.icon, mNotificationIcon);
724 } else {
725 contentView.setImageViewResource(R.id.icon, mMediaNotificationInfo.i con);
726 }
727
728 if (mMediaNotificationInfo.supportsPlayPause()) {
729 if (mMediaNotificationInfo.isPaused) {
730 contentView.setImageViewResource(playPauseButtonId, R.drawable.i c_vidcontrol_play);
731 contentView.setContentDescription(playPauseButtonId, mPlayDescri ption);
732 contentView.setOnClickPendingIntent(playPauseButtonId,
733 createPendingIntent(ListenerService.ACTION_PLAY));
734 } else {
735 // If we're here, the notification supports play/pause button an d is playing.
736 contentView.setImageViewResource(playPauseButtonId, R.drawable.i c_vidcontrol_pause);
737 contentView.setContentDescription(playPauseButtonId, mPauseDescr iption);
738 contentView.setOnClickPendingIntent(playPauseButtonId,
739 createPendingIntent(ListenerService.ACTION_PAUSE));
740 }
741
742 contentView.setViewVisibility(playPauseButtonId, View.VISIBLE);
743 } else {
744 contentView.setViewVisibility(playPauseButtonId, View.GONE);
745 }
746
747 return contentView;
748 }
749
750 /**
751 * Scales the Bitmap to have better looking on Wearable devices.
752 * The returned Bitmap size will be exactly 400*400.
753 */
754 private Bitmap scaleBitmapForWearable(Bitmap original) {
755 Bitmap result = Bitmap.createScaledBitmap(
756 original,
757 WEARABLE_NOTIFICATION_BACKGROUND_WIDTH,
758 WEARABLE_NOTIFICATION_BACKGROUND_HEIGHT,
759 true);
760 return result;
761 }
762
681 private Bitmap drawableToBitmap(Drawable drawable) { 763 private Bitmap drawableToBitmap(Drawable drawable) {
682 if (!(drawable instanceof BitmapDrawable)) return null; 764 if (!(drawable instanceof BitmapDrawable)) return null;
683 765
684 BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable; 766 BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
685 return bitmapDrawable.getBitmap(); 767 return bitmapDrawable.getBitmap();
686 } 768 }
687 } 769 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698