Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 757f2860 authored by Danny Baumann's avatar Danny Baumann
Browse files

Use L's new notification and media session API.

Change-Id: Ic9e7facdfa7213439b168a915ac9f2e8afc0a252
parent 4547a6da
Loading
Loading
Loading
Loading
+0 −129
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
  Copyright (C) 2012 Andrew Neal
  Copyright (C) 2014 The CyanogenMod Project

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at
 
       http://www.apache.org/licenses/LICENSE-2.0
 
  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="128.0dip"
    tools:ignore="ContentDescription" >

    <!-- The height cannot be specified any other way. It must read "128.0dip" and cannot be referenced. I think it's a bug. -->

    <ImageView
        android:id="@+id/notification_expanded_base_image"
        android:layout_width="@dimen/notification_expanded_height"
        android:layout_height="@dimen/notification_expanded_height"
        android:layout_alignParentBottom="true"
        android:layout_alignParentLeft="true"
        android:scaleType="fitXY" />

    <LinearLayout
        android:id="@+id/notification_expanded_buttons"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:layout_toRightOf="@+id/notification_expanded_base_image"
        android:dividerPadding="@dimen/notification_expanded_buttons_divider_padding"
        android:gravity="center_vertical"
        android:orientation="horizontal"
        android:showDividers="middle" >

        <ImageButton
            android:id="@+id/notification_expanded_base_previous"
            android:layout_width="0dp"
            android:layout_height="@dimen/notification_expanded_button_height"
            android:layout_weight="1"
            android:background="?android:selectableItemBackground"
            android:padding="@dimen/notification_expanded_button_padding"
            android:scaleType="fitCenter"
            android:src="@drawable/btn_playback_previous" />

        <ImageButton
            android:id="@+id/notification_expanded_base_play"
            android:layout_width="0dp"
            android:layout_height="@dimen/notification_expanded_button_height"
            android:layout_weight="1"
            android:background="?android:selectableItemBackground"
            android:padding="@dimen/notification_expanded_button_padding"
            android:scaleType="fitCenter"
            android:src="@drawable/btn_playback_pause" />

        <ImageButton
            android:id="@+id/notification_expanded_base_next"
            android:layout_width="0dp"
            android:layout_height="@dimen/notification_expanded_button_height"
            android:layout_weight="1"
            android:background="?android:selectableItemBackground"
            android:padding="@dimen/notification_expanded_button_padding"
            android:scaleType="fitCenter"
            android:src="@drawable/btn_playback_next" />
    </LinearLayout>

    <LinearLayout
        android:layout_gravity="fill_horizontal"
        android:layout_width="wrap_content"
        android:layout_height="1dp"
        android:layout_above="@+id/notification_expanded_buttons"
        android:layout_alignParentRight="true"
        android:layout_toRightOf="@+id/notification_expanded_base_image"
        android:background="@color/widget_divider" />

    <ImageButton
        android:id="@+id/notification_expanded_base_collapse"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:background="?android:selectableItemBackground"
        android:padding="@dimen/notification_expanded_collapse_padding"
        android:src="@drawable/btn_notification_collapse" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentTop="true"
        android:layout_gravity="center_vertical"
        android:layout_toLeftOf="@+id/notification_expanded_base_collapse"
        android:layout_toRightOf="@+id/notification_expanded_base_image"
        android:orientation="vertical"
        android:paddingLeft="@dimen/notification_info_container_padding_left"
        android:paddingTop="@dimen/notification_expanded_content_padding_top" >

        <TextView
            android:id="@+id/notification_expanded_base_line_one"
            style="@style/NotificationText"
            android:textStyle="bold"
            android:textColor="@color/widget_text"
            android:textSize="@dimen/text_size_large"/>

        <TextView
            android:id="@+id/notification_expanded_base_line_two"
            style="@style/NotificationText"
            android:textColor="@color/widget_text"
            android:fontFamily="sans-serif-light"
            android:textSize="@dimen/text_size_small"/>

        <TextView
            android:id="@+id/notification_expanded_base_line_three"
            style="@style/NotificationText"
            android:textColor="@color/widget_text"
            android:fontFamily="sans-serif-light"
            android:textSize="@dimen/text_size_small"/>
    </LinearLayout>

</RelativeLayout>
 No newline at end of file
+0 −69
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!--
  Copyright (C) 2012 Andrew Neal
 
  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at
 
       http://www.apache.org/licenses/LICENSE-2.0
 
  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/notification_base"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:gravity="center_vertical"
    android:orientation="horizontal"
    tools:ignore="ContentDescription" >

    <ImageView
        android:id="@+id/notification_base_image"
        android:layout_width="@dimen/notification_big_icon_width"
        android:layout_height="@dimen/notification_big_icon_height"
        android:gravity="center" />

    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_gravity="center_vertical"
        android:layout_weight="1"
        android:orientation="vertical"
        android:paddingLeft="@dimen/notification_info_container_padding_left"
        android:paddingBottom="@dimen/notification_info_container_padding_bottom" >

        <TextView
            android:id="@+id/notification_base_line_one"
            style="@style/NotificationText"
            android:textAppearance="@android:style/TextAppearance.StatusBar.EventContent.Title" />

        <TextView
            android:id="@+id/notification_base_line_two"
            style="@style/NotificationText"
            android:textAppearance="@android:style/TextAppearance.StatusBar.EventContent" />

    </LinearLayout>

    <ImageButton
        android:id="@+id/notification_base_previous"
        style="@style/NotificationAction.Previous" />

    <ImageButton
        android:id="@+id/notification_base_play"
        style="@style/NotificationAction.Play" />

    <ImageButton
        android:id="@+id/notification_base_next"
        style="@style/NotificationAction.Next" />

    <ImageButton
        android:id="@+id/notification_base_collapse"
        style="@style/NotificationAction.Collapse" />

</LinearLayout>
 No newline at end of file
+114 −84
Original line number Diff line number Diff line
@@ -15,6 +15,7 @@ package com.cyanogenmod.eleven;

import android.annotation.SuppressLint;
import android.app.AlarmManager;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.appwidget.AppWidgetManager;
@@ -32,9 +33,11 @@ import android.graphics.Bitmap;
import android.media.AudioManager;
import android.media.AudioManager.OnAudioFocusChangeListener;
import android.media.MediaMetadataRetriever;
import android.media.MediaMetadata;
import android.media.MediaPlayer;
import android.media.RemoteControlClient;
import android.media.audiofx.AudioEffect;
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
import android.net.Uri;
import android.os.Handler;
import android.os.HandlerThread;
@@ -442,7 +445,7 @@ public class MusicPlaybackService extends Service {
    /**
     * Lock screen controls
     */
    private RemoteControlClient mRemoteControlClient;
    private MediaSession mSession;

    private ComponentName mMediaButtonReceiverComponent;

@@ -483,11 +486,6 @@ public class MusicPlaybackService extends Service {
     */
    private ImageFetcher mImageFetcher;

    /**
     * Used to build the notification
     */
    private NotificationHelper mNotificationHelper;

    /**
     * Recently listened database
     */
@@ -567,9 +565,6 @@ public class MusicPlaybackService extends Service {
        // gets a pointer to the playback state store
        mPlaybackStateStore = MusicPlaybackState.getInstance(this);

        // Initialize the notification helper
        mNotificationHelper = new NotificationHelper(this);

        // Initialize the image fetcher
        mImageFetcher = ImageFetcher.getInstance(this);
        // Initialize the image cache
@@ -594,7 +589,7 @@ public class MusicPlaybackService extends Service {
        mAudioManager.registerMediaButtonEventReceiver(mMediaButtonReceiverComponent);

        // Use the remote control APIs to set the playback state
        setUpRemoteControlClient();
        setUpMediaSession();

        // Initialize the preferences
        mPreferences = getSharedPreferences("Service", 0);
@@ -648,45 +643,40 @@ public class MusicPlaybackService extends Service {
        notifyChange(META_CHANGED);
    }

    /**
     * Initializes the remote control client
     */
    private void setUpRemoteControlClient() {
        final Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
        mediaButtonIntent.setComponent(mMediaButtonReceiverComponent);
        mRemoteControlClient = new RemoteControlClient(
                PendingIntent.getBroadcast(getApplicationContext(), 0, mediaButtonIntent,
                        PendingIntent.FLAG_UPDATE_CURRENT));
        mAudioManager.registerRemoteControlClient(mRemoteControlClient);

        // Flags for the media transport control that this client supports.
        int flags = RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS
                | RemoteControlClient.FLAG_KEY_MEDIA_NEXT
                | RemoteControlClient.FLAG_KEY_MEDIA_PLAY
                | RemoteControlClient.FLAG_KEY_MEDIA_PAUSE
                | RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE
                | RemoteControlClient.FLAG_KEY_MEDIA_STOP;

        if (ApolloUtils.hasJellyBeanMR2()) {
            flags |= RemoteControlClient.FLAG_KEY_MEDIA_POSITION_UPDATE;

            mRemoteControlClient.setOnGetPlaybackPositionListener(
                    new RemoteControlClient.OnGetPlaybackPositionListener() {
    private void setUpMediaSession() {
        mSession = new MediaSession(this, "Eleven");
        mSession.setCallback(new MediaSession.Callback() {
            @Override
                public long onGetPlaybackPosition() {
                    return position();
            public void onPause() {
                pause();
                mPausedByTransientLossOfFocus = false;
            }
            });
            mRemoteControlClient.setPlaybackPositionUpdateListener(
                    new RemoteControlClient.OnPlaybackPositionUpdateListener() {
            @Override
                public void onPlaybackPositionUpdate(long newPositionMs) {
                    seek(newPositionMs);
            public void onPlay() {
                play();
            }
            });
            @Override
            public void onSeekTo(long pos) {
                seek(pos);
            }

        mRemoteControlClient.setTransportControlFlags(flags);
            @Override
            public void onSkipToNext() {
                gotoNext(true);
            }
            @Override
            public void onSkipToPrevious() {
                prev(false);
            }
            @Override
            public void onStop() {
                pause();
                mPausedByTransientLossOfFocus = false;
                seek(0);
                releaseServiceUiAndStop();
            }
        });
        mSession.setFlags(MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
        mSession.setActive(true);
    }

    /**
@@ -712,7 +702,7 @@ public class MusicPlaybackService extends Service {

        // Remove the audio focus listener and lock screen controls
        mAudioManager.abandonAudioFocus(mAudioFocusListener);
        mAudioManager.unregisterRemoteControlClient(mRemoteControlClient);
        mSession.release();

        // remove the media store observer
        getContentResolver().unregisterContentObserver(mMediaStoreObserver);
@@ -778,7 +768,7 @@ public class MusicPlaybackService extends Service {
        }

        if (D) Log.d(TAG, "Nothing is playing anymore, releasing notification");
        mNotificationHelper.killNotification();
        stopForeground(true);
        mAudioManager.abandonAudioFocus(mAudioFocusListener);

        if (!mServiceInUse) {
@@ -827,10 +817,9 @@ public class MusicPlaybackService extends Service {
     */
    private void updateNotification() {
        if (!mAnyActivityInForeground && isPlaying()) {
            mNotificationHelper.buildNotification(getAlbumName(), getArtistName(),
                    getTrackName(), getAlbumId(), getAlbumArt(true), isPlaying());
            buildNotification();
        } else if (mAnyActivityInForeground) {
            mNotificationHelper.killNotification();
            stopForeground(true);
        }
    }

@@ -1376,7 +1365,7 @@ public class MusicPlaybackService extends Service {
        if (D) Log.d(TAG, "notifyChange: what = " + what);

        // Update the lockscreen controls
        updateRemoteControlClient(what);
        updateMediaSession(what);

        if (what.equals(POSITION_CHANGED)) {
            return;
@@ -1424,7 +1413,7 @@ public class MusicPlaybackService extends Service {
        }

        if (what.equals(PLAYSTATE_CHANGED)) {
            mNotificationHelper.updatePlayState(isPlaying());
            buildNotification();
        }

        // Update the app-widgets
@@ -1433,21 +1422,14 @@ public class MusicPlaybackService extends Service {
        mAppWidgetLargeAlternate.notifyChange(this, what);
    }

    /**
     * Updates the lockscreen controls.
     *
     * @param what The broadcast
     */
    private void updateRemoteControlClient(final String what) {
    private void updateMediaSession(final String what) {
        int playState = mIsSupposedToBePlaying
                ? RemoteControlClient.PLAYSTATE_PLAYING
                : RemoteControlClient.PLAYSTATE_PAUSED;

        if (ApolloUtils.hasJellyBeanMR2()
                && (what.equals(PLAYSTATE_CHANGED) || what.equals(POSITION_CHANGED))) {
            mRemoteControlClient.setPlaybackState(playState, position(), 1.0f);
        } else if (what.equals(PLAYSTATE_CHANGED)) {
            mRemoteControlClient.setPlaybackState(playState);
                ? PlaybackState.STATE_PLAYING
                : PlaybackState.STATE_PAUSED;

        if (what.equals(PLAYSTATE_CHANGED) || what.equals(POSITION_CHANGED)) {
            mSession.setPlaybackState(new PlaybackState.Builder()
                    .setState(playState, position(), 1.0f).build());
        } else if (what.equals(META_CHANGED) || what.equals(QUEUE_CHANGED)) {
            Bitmap albumArt = getAlbumArt(false);
            if (albumArt != null) {
@@ -1459,21 +1441,69 @@ public class MusicPlaybackService extends Service {
                }
                albumArt = albumArt.copy(config, false);
            }
            mRemoteControlClient
                    .editMetadata(true)
                    .putString(MediaMetadataRetriever.METADATA_KEY_ARTIST, getArtistName())
                    .putString(MediaMetadataRetriever.METADATA_KEY_ALBUMARTIST,
                            getAlbumArtistName())
                    .putString(MediaMetadataRetriever.METADATA_KEY_ALBUM, getAlbumName())
                    .putString(MediaMetadataRetriever.METADATA_KEY_TITLE, getTrackName())
                    .putLong(MediaMetadataRetriever.METADATA_KEY_DURATION, duration())
                    .putBitmap(RemoteControlClient.MetadataEditor.BITMAP_KEY_ARTWORK, albumArt)
                    .apply();

            if (ApolloUtils.hasJellyBeanMR2()) {
                mRemoteControlClient.setPlaybackState(playState, position(), 1.0f);
            mSession.setMetadata(new MediaMetadata.Builder()
                    .putString(MediaMetadata.METADATA_KEY_ARTIST, getArtistName())
                    .putString(MediaMetadata.METADATA_KEY_ALBUM_ARTIST, getAlbumArtistName())
                    .putString(MediaMetadata.METADATA_KEY_ALBUM, getAlbumName())
                    .putString(MediaMetadata.METADATA_KEY_TITLE, getTrackName())
                    .putLong(MediaMetadata.METADATA_KEY_DURATION, duration())
                    .putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, albumArt)
                    .build());

            mSession.setPlaybackState(new PlaybackState.Builder()
                    .setState(playState, position(), 1.0f).build());
        }
    }

    private void buildNotification() {
        final String albumName = getAlbumName();
        final String artistName = getArtistName();
        final boolean isPlaying = isPlaying();
        String text = TextUtils.isEmpty(albumName)
                ? artistName : artistName + " - " + albumName;

        int playButtonResId = isPlaying
                ? R.drawable.btn_playback_pause : R.drawable.btn_playback_play;
        int playButtonTitleResId = isPlaying
                ? R.string.accessibility_pause : R.string.accessibility_play;

        Notification.MediaStyle style = new Notification.MediaStyle()
                .setMediaSession(mSession.getSessionToken())
                .setShowActionsInCompactView(0, 1, 2);

        Intent nowPlayingIntent = new Intent("com.cyanogenmod.eleven.AUDIO_PLAYER")
                .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        PendingIntent clickIntent = PendingIntent.getActivity(this, 0, nowPlayingIntent, 0);

        // TODO: Add back a beter small icon when we have time
        Notification notification = new Notification.Builder(this)
                .setSmallIcon(R.drawable.ic_launcher)
                .setLargeIcon(getAlbumArt(false))
                .setContentIntent(clickIntent)
                .setContentTitle(getTrackName())
                .setContentText(text)
                .setStyle(style)
                .setVisibility(Notification.VISIBILITY_PUBLIC)
                .addAction(R.drawable.btn_playback_previous,
                        getString(R.string.accessibility_prev),
                        retrievePlaybackAction(PREVIOUS_ACTION))
                .addAction(playButtonResId, getString(playButtonTitleResId),
                        retrievePlaybackAction(TOGGLEPAUSE_ACTION))
                .addAction(R.drawable.btn_playback_next,
                        getString(R.string.accessibility_next),
                        retrievePlaybackAction(NEXT_ACTION))
                .build();

        startForeground(hashCode(), notification);
    }

    private final PendingIntent retrievePlaybackAction(final String action) {
        final ComponentName serviceName = new ComponentName(this, MusicPlaybackService.class);
        Intent intent = new Intent(action);
        intent.setComponent(serviceName);

        return PendingIntent.getService(this, 0, intent, 0);
    }

    /**
+0 −272

File deleted.

Preview size limit exceeded, changes collapsed.

+4 −8
Original line number Diff line number Diff line
@@ -127,18 +127,14 @@ public class AppWidgetLarge extends AppWidgetBase {
        if (isPlaying) {
            appWidgetView.setImageViewResource(R.id.app_widget_large_play,
                    R.drawable.btn_playback_pause);
            if (ApolloUtils.hasJellyBean()) {
            appWidgetView.setContentDescription(R.id.app_widget_large_play,
                    service.getString(R.string.accessibility_pause));
            }
        } else {
            appWidgetView.setImageViewResource(R.id.app_widget_large_play,
                    R.drawable.btn_playback_play);
            if (ApolloUtils.hasJellyBean()) {
            appWidgetView.setContentDescription(R.id.app_widget_large_play,
                    service.getString(R.string.accessibility_play));
        }
        }

        // Link actions buttons to intents
        linkButtons(service, appWidgetView);
Loading