aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorandroid-build-team Robot <android-build-team-robot@google.com>2019-07-04 03:11:49 +0000
committerandroid-build-team Robot <android-build-team-robot@google.com>2019-07-04 03:11:49 +0000
commit7e2f97029306e0cc5542fa5da3f2fd2634cff3b7 (patch)
treebb7c0c90f67c9b39f8abf3158a0aaaac93a76799
parent9389d4977135d19339157225d2505e660a1b43f9 (diff)
parenta1863280183008deeb1b6ce026a680266f944669 (diff)
downloadtests-android10-release.tar.gz
Snap for 5706892 from a1863280183008deeb1b6ce026a680266f944669 to qt-releaseandroid-vts-10.0_r9android-vts-10.0_r8android-vts-10.0_r7android-vts-10.0_r6android-vts-10.0_r5android-vts-10.0_r4android-vts-10.0_r3android-vts-10.0_r2android-vts-10.0_r16android-vts-10.0_r15android-vts-10.0_r14android-vts-10.0_r13android-vts-10.0_r12android-vts-10.0_r11android-vts-10.0_r10android-vts-10.0_r1android-security-10.0.0_r75android-security-10.0.0_r74android-security-10.0.0_r73android-security-10.0.0_r72android-security-10.0.0_r71android-security-10.0.0_r70android-security-10.0.0_r69android-security-10.0.0_r68android-security-10.0.0_r67android-security-10.0.0_r66android-security-10.0.0_r65android-security-10.0.0_r64android-security-10.0.0_r63android-security-10.0.0_r62android-security-10.0.0_r61android-security-10.0.0_r60android-security-10.0.0_r59android-security-10.0.0_r58android-security-10.0.0_r57android-security-10.0.0_r56android-security-10.0.0_r55android-security-10.0.0_r54android-security-10.0.0_r53android-security-10.0.0_r52android-security-10.0.0_r51android-security-10.0.0_r50android-security-10.0.0_r49android-security-10.0.0_r48android-cts-10.0_r9android-cts-10.0_r8android-cts-10.0_r7android-cts-10.0_r6android-cts-10.0_r5android-cts-10.0_r4android-cts-10.0_r3android-cts-10.0_r2android-cts-10.0_r16android-cts-10.0_r15android-cts-10.0_r14android-cts-10.0_r13android-cts-10.0_r12android-cts-10.0_r11android-cts-10.0_r10android-cts-10.0_r1android-10.0.0_r6android-10.0.0_r5android-10.0.0_r47android-10.0.0_r46android-10.0.0_r4android-10.0.0_r3android-10.0.0_r2android-10.0.0_r17android-10.0.0_r11android-10.0.0_r10android-10.0.0_r1android10-tests-releaseandroid10-security-releaseandroid10-s3-releaseandroid10-s2-releaseandroid10-s1-releaseandroid10-release
Change-Id: Ifbdc774275694428d51b5036087780b8bad63054
-rw-r--r--TestMediaApp/assets/media_items/advanced.json29
-rw-r--r--TestMediaApp/assets/media_items/mixed.json10
-rw-r--r--TestMediaApp/assets/media_items/only_nodes.json9
-rw-r--r--TestMediaApp/res/drawable/ic_heart_less_less.pngbin0 -> 1160 bytes
-rw-r--r--TestMediaApp/res/drawable/ic_heart_plus_plus.xml13
-rw-r--r--TestMediaApp/res/values/strings.xml4
-rw-r--r--TestMediaApp/src/com/android/car/media/testmediaapp/TmaMediaEvent.java6
-rw-r--r--TestMediaApp/src/com/android/car/media/testmediaapp/TmaMediaItem.java104
-rw-r--r--TestMediaApp/src/com/android/car/media/testmediaapp/TmaPlayer.java141
-rw-r--r--TestMediaApp/src/com/android/car/media/testmediaapp/loader/TmaLoaderUtils.java19
-rw-r--r--TestMediaApp/src/com/android/car/media/testmediaapp/loader/TmaMediaEventReader.java2
-rw-r--r--TestMediaApp/src/com/android/car/media/testmediaapp/loader/TmaMediaItemReader.java13
12 files changed, 295 insertions, 55 deletions
diff --git a/TestMediaApp/assets/media_items/advanced.json b/TestMediaApp/assets/media_items/advanced.json
new file mode 100644
index 0000000..4cf34ea
--- /dev/null
+++ b/TestMediaApp/assets/media_items/advanced.json
@@ -0,0 +1,29 @@
+{
+ "FLAGS": "browsable",
+
+ "METADATA": {
+ "MEDIA_ID": "advanced",
+ "DISPLAY_TITLE": "Advanced"
+ },
+
+ "CHILDREN": [
+ {
+ "FLAGS": "playable",
+ "METADATA": {
+ "MEDIA_ID": "advanced custom_action",
+ "DISPLAY_TITLE": "Custom Action",
+ "DURATION": 10000
+ },
+ "CUSTOM_ACTIONS": ["HEART_LESS_LESS", "HEART_PLUS_PLUS"]
+ },
+ {
+ "FLAGS": "browsable",
+ "PLAYABLE_HINT": "GRID",
+ "METADATA": {
+ "MEDIA_ID": "advanced art nodes",
+ "DISPLAY_TITLE": "Album Art"
+ },
+ "INCLUDE":"media_items/album_art/art_nodes.json"
+ }
+ ]
+} \ No newline at end of file
diff --git a/TestMediaApp/assets/media_items/mixed.json b/TestMediaApp/assets/media_items/mixed.json
index 872fa4f..7e04e85 100644
--- a/TestMediaApp/assets/media_items/mixed.json
+++ b/TestMediaApp/assets/media_items/mixed.json
@@ -36,6 +36,16 @@
{
"FLAGS": "browsable",
"PLAYABLE_HINT": "LIST",
+ "BROWSABLE_HINT": "LIST",
+ "METADATA": {
+ "MEDIA_ID": "mixed advanced",
+ "DISPLAY_TITLE": "Advanced"
+ },
+ "INCLUDE":"media_items/advanced.json"
+ },
+ {
+ "FLAGS": "browsable",
+ "PLAYABLE_HINT": "LIST",
"BROWSABLE_HINT": "GRID",
"METADATA": {
"MEDIA_ID": "mixed rabbit hole",
diff --git a/TestMediaApp/assets/media_items/only_nodes.json b/TestMediaApp/assets/media_items/only_nodes.json
index 7150104..4858438 100644
--- a/TestMediaApp/assets/media_items/only_nodes.json
+++ b/TestMediaApp/assets/media_items/only_nodes.json
@@ -19,12 +19,13 @@
},
{
"FLAGS": "browsable",
- "PLAYABLE_HINT": "GRID",
+ "PLAYABLE_HINT": "LIST",
+ "BROWSABLE_HINT": "LIST",
"METADATA": {
- "MEDIA_ID": "only_nodes art nodes",
- "DISPLAY_TITLE": "Album Art"
+ "MEDIA_ID": "only_nodes advanced",
+ "DISPLAY_TITLE": "Advanced"
},
- "INCLUDE":"media_items/album_art/art_nodes.json"
+ "INCLUDE":"media_items/advanced.json"
},
{
"FLAGS": "browsable",
diff --git a/TestMediaApp/res/drawable/ic_heart_less_less.png b/TestMediaApp/res/drawable/ic_heart_less_less.png
new file mode 100644
index 0000000..e65feac
--- /dev/null
+++ b/TestMediaApp/res/drawable/ic_heart_less_less.png
Binary files differ
diff --git a/TestMediaApp/res/drawable/ic_heart_plus_plus.xml b/TestMediaApp/res/drawable/ic_heart_plus_plus.xml
new file mode 100644
index 0000000..447431e
--- /dev/null
+++ b/TestMediaApp/res/drawable/ic_heart_plus_plus.xml
@@ -0,0 +1,13 @@
+<vector android:height="24dp" android:viewportHeight="13.229167"
+ android:viewportWidth="13.229166" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
+ <path android:fillColor="#00000000"
+ android:pathData="m6.6643,12.8277c0.7318,-3.7194 6.3472,-4.3109 6.3382,-8.9125 -0.009,-4.6016 -4.5689,-5.0237 -6.4909,0.4739 -1.7711,-5.5585 -6.2755,-4.9643 -6.2231,-0.4533 0.0525,4.511 5.8295,5.1724 6.3758,8.8919z"
+ android:strokeAlpha="1" android:strokeColor="#000000"
+ android:strokeLineCap="butt" android:strokeLineJoin="miter" android:strokeWidth="0.26458332"/>
+ <path android:fillAlpha="1" android:fillColor="#000000"
+ android:pathData="m10.173,6.7307l0,-1.5351l1.5351,-0l0,-0.4685l-1.5351,-0l0,-1.5351L9.71,3.1919l0,1.5351L8.1749,4.727l0,0.4685L9.71,5.1955l0,1.5351z"
+ android:strokeColor="#00000000" android:strokeWidth="0.26458332"/>
+ <path android:fillAlpha="1" android:fillColor="#000000"
+ android:pathData="m3.7619,6.585l0,-1.5351l1.5351,-0l0,-0.4685L3.7619,4.5813l0,-1.5351L3.2989,3.0462l0,1.5351L1.7637,4.5813l0,0.4685l1.5351,-0l0,1.5351z"
+ android:strokeColor="#00000000" android:strokeWidth="0.26458332"/>
+</vector>
diff --git a/TestMediaApp/res/values/strings.xml b/TestMediaApp/res/values/strings.xml
index e35fcd4..62ec9e1 100644
--- a/TestMediaApp/res/values/strings.xml
+++ b/TestMediaApp/res/values/strings.xml
@@ -25,4 +25,8 @@
will truncate it when needed.
</string>
+ <string name="heart_plus_plus" translatable="false">Heart++</string>
+
+ <string name="heart_less_less" translatable="false">Heart--</string>
+
</resources>
diff --git a/TestMediaApp/src/com/android/car/media/testmediaapp/TmaMediaEvent.java b/TestMediaApp/src/com/android/car/media/testmediaapp/TmaMediaEvent.java
index 98a9b6b..3bec7ac 100644
--- a/TestMediaApp/src/com/android/car/media/testmediaapp/TmaMediaEvent.java
+++ b/TestMediaApp/src/com/android/car/media/testmediaapp/TmaMediaEvent.java
@@ -102,9 +102,9 @@ public class TmaMediaEvent {
PREFS
}
- public final EventState mState;
- public final StateErrorCode mErrorCode;
- public final String mErrorMessage;
+ final EventState mState;
+ final StateErrorCode mErrorCode;
+ final String mErrorMessage;
final String mActionLabel;
final ResolutionIntent mResolutionIntent;
/** How long to wait before sending the event to the app. */
diff --git a/TestMediaApp/src/com/android/car/media/testmediaapp/TmaMediaItem.java b/TestMediaApp/src/com/android/car/media/testmediaapp/TmaMediaItem.java
index 6b56884..6925588 100644
--- a/TestMediaApp/src/com/android/car/media/testmediaapp/TmaMediaItem.java
+++ b/TestMediaApp/src/com/android/car/media/testmediaapp/TmaMediaItem.java
@@ -16,6 +16,9 @@
package com.android.car.media.testmediaapp;
+import androidx.annotation.Nullable;
+
+import static android.support.v4.media.MediaBrowserCompat.MediaItem.FLAG_PLAYABLE;
import static android.support.v4.media.MediaMetadataCompat.METADATA_KEY_DURATION;
import static android.support.v4.media.MediaMetadataCompat.METADATA_KEY_MEDIA_ID;
@@ -26,9 +29,11 @@ import static com.android.car.media.common.MediaConstants.CONTENT_STYLE_PLAYABLE
import android.os.Bundle;
import android.support.v4.media.MediaBrowserCompat.MediaItem;
+import android.support.v4.media.MediaBrowserCompat.MediaItem.Flags;
import android.support.v4.media.MediaDescriptionCompat;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.session.MediaSessionCompat;
+import android.support.v4.media.session.MediaSessionCompat.QueueItem;
import java.util.ArrayList;
import java.util.Collections;
@@ -37,6 +42,8 @@ import java.util.List;
/** Our internal representation of media items. */
public class TmaMediaItem {
+ private static final String CUSTOM_ACTION_PREFIX = "com.android.car.media.testmediaapp.";
+
/** The name of each entry is the value used in the json file. */
public enum ContentStyle {
NONE (0),
@@ -48,6 +55,24 @@ public class TmaMediaItem {
}
}
+ public enum TmaCustomAction {
+ HEART_PLUS_PLUS(CUSTOM_ACTION_PREFIX + "heart_plus_plus", R.string.heart_plus_plus,
+ R.drawable.ic_heart_plus_plus),
+ HEART_LESS_LESS(CUSTOM_ACTION_PREFIX + "heart_less_less", R.string.heart_less_less,
+ R.drawable.ic_heart_less_less);
+
+ final String mId;
+ final int mNameId;
+ final int mIcon;
+
+ TmaCustomAction(String id, int name, int icon) {
+ mId = id;
+ mNameId = name;
+ mIcon = icon;
+ }
+
+ }
+
private final @MediaItem.Flags int mFlags;
private final MediaMetadataCompat mMediaMetadata;
private final ContentStyle mPlayableStyle;
@@ -55,41 +80,87 @@ public class TmaMediaItem {
/** Read only list. */
final List<TmaMediaItem> mChildren;
+ /** Read only list. */
+ private final List<TmaMediaItem> mPlayableChildren;
+ /** Read only list. */
+ final List<TmaCustomAction> mCustomActions;
/** Read only list. Events triggered when starting the playback. */
final List<TmaMediaEvent> mMediaEvents;
/** References another json file where to get extra children from. */
final String mInclude;
+ private @Nullable TmaMediaItem mParent;
+ int mHearts;
+
- public TmaMediaItem(@MediaItem.Flags int flags, ContentStyle playableStyle,
- ContentStyle browsableStyle, MediaMetadataCompat metadata,
- List<TmaMediaEvent> mediaEvents, List<TmaMediaItem> children, String include) {
+ public TmaMediaItem(@Flags int flags, ContentStyle playableStyle, ContentStyle browsableStyle,
+ MediaMetadataCompat metadata, List<TmaCustomAction> customActions,
+ List<TmaMediaEvent> mediaEvents,
+ List<TmaMediaItem> children, String include) {
mFlags = flags;
mPlayableStyle = playableStyle;
mBrowsableStyle = browsableStyle;
mMediaMetadata = metadata;
+ mCustomActions = Collections.unmodifiableList(customActions);
mChildren = Collections.unmodifiableList(children);
mMediaEvents = Collections.unmodifiableList(mediaEvents);
mInclude = include;
+ List<TmaMediaItem> playableChildren = new ArrayList<>(children.size());
+ for (TmaMediaItem child: mChildren) {
+ child.setParent(this);
+ if ((child.mFlags & FLAG_PLAYABLE) != 0) {
+ playableChildren.add(child);
+ }
+ }
+ mPlayableChildren = Collections.unmodifiableList(playableChildren);
+ }
+
+ private void setParent(@Nullable TmaMediaItem parent) {
+ mParent = parent;
}
- public String getMediaId() {
+ @Nullable
+ TmaMediaItem getParent() {
+ return mParent;
+ }
+
+ TmaMediaItem getPlayableByIndex(long index) {
+ return mPlayableChildren.get((int)index);
+ }
+
+ @Nullable
+ TmaMediaItem getPrevious() {
+ if (mParent == null) return null;
+ List<TmaMediaItem> queueItems = mParent.mPlayableChildren;
+ int myIndex = queueItems.indexOf(this);
+ return (myIndex > 0) ? queueItems.get(myIndex - 1) : null;
+ }
+
+ @Nullable
+ TmaMediaItem getNext() {
+ if (mParent == null) return null;
+ List<TmaMediaItem> queueItems = mParent.mPlayableChildren;
+ int myIndex = queueItems.indexOf(this);
+ return (myIndex < queueItems.size() - 1) ? queueItems.get(myIndex + 1) : null;
+ }
+
+ String getMediaId() {
return mMediaMetadata.getString(METADATA_KEY_MEDIA_ID);
}
/** Returns -1 if the duration key is unspecified or <= 0. */
- public long getDuration() {
+ long getDuration() {
long result = mMediaMetadata.getLong(METADATA_KEY_DURATION);
if (result <= 0) return -1;
return result;
}
- public TmaMediaItem append(List<TmaMediaItem> children) {
+ TmaMediaItem append(List<TmaMediaItem> children) {
List<TmaMediaItem> allChildren = new ArrayList<>(mChildren.size() + children.size());
allChildren.addAll(mChildren);
allChildren.addAll(children);
return new TmaMediaItem(mFlags, mPlayableStyle, mBrowsableStyle, mMediaMetadata,
- mMediaEvents, allChildren, null);
+ mCustomActions, mMediaEvents, allChildren, null);
}
void updateSessionMetadata(MediaSessionCompat session) {
@@ -100,6 +171,25 @@ public class TmaMediaItem {
return new MediaItem(buildDescription(), mFlags);
}
+ List<QueueItem> buildQueue() {
+ int count = mPlayableChildren.size();
+ List<QueueItem> queue = new ArrayList<>(count);
+ for (int i = 0 ; i < count; i++) {
+ TmaMediaItem child = mPlayableChildren.get(i);
+ queue.add(new QueueItem(child.buildDescription(), i));
+ }
+ return queue;
+ }
+
+ /** Returns the id of the item in the queue. */
+ long getQueueId() {
+ if (mParent != null) {
+ int index = mParent.mPlayableChildren.indexOf(this);
+ if (index >= 0) return index;
+ }
+ return MediaSessionCompat.QueueItem.UNKNOWN_ID;
+ }
+
private MediaDescriptionCompat buildDescription() {
// Use the default media description but add our extras.
diff --git a/TestMediaApp/src/com/android/car/media/testmediaapp/TmaPlayer.java b/TestMediaApp/src/com/android/car/media/testmediaapp/TmaPlayer.java
index f20bc99..c6213b2 100644
--- a/TestMediaApp/src/com/android/car/media/testmediaapp/TmaPlayer.java
+++ b/TestMediaApp/src/com/android/car/media/testmediaapp/TmaPlayer.java
@@ -18,6 +18,8 @@ package com.android.car.media.testmediaapp;
import static android.media.AudioManager.AUDIOFOCUS_GAIN;
import static android.media.AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
+import static android.support.v4.media.session.PlaybackStateCompat.ACTION_PAUSE;
+import static android.support.v4.media.session.PlaybackStateCompat.ACTION_PLAY;
import static android.support.v4.media.session.PlaybackStateCompat.ACTION_PLAY_FROM_MEDIA_ID;
import static android.support.v4.media.session.PlaybackStateCompat.ACTION_SEEK_TO;
import static android.support.v4.media.session.PlaybackStateCompat.ACTION_SKIP_TO_NEXT;
@@ -29,7 +31,7 @@ import static android.support.v4.media.session.PlaybackStateCompat.STATE_ERROR;
import static com.android.car.media.common.MediaConstants.ERROR_RESOLUTION_ACTION_INTENT;
import static com.android.car.media.common.MediaConstants.ERROR_RESOLUTION_ACTION_LABEL;
-import android.annotation.Nullable;
+import androidx.annotation.Nullable;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
@@ -40,9 +42,11 @@ import android.os.Handler;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat;
import android.util.Log;
+import android.widget.Toast;
import com.android.car.media.testmediaapp.TmaMediaEvent.EventState;
import com.android.car.media.testmediaapp.TmaMediaEvent.ResolutionIntent;
+import com.android.car.media.testmediaapp.TmaMediaItem.TmaCustomAction;
import com.android.car.media.testmediaapp.prefs.TmaEnumPrefs.TmaAccountType;
import com.android.car.media.testmediaapp.prefs.TmaPrefs;
import com.android.car.media.testmediaapp.prefs.TmaPrefsActivity;
@@ -55,20 +59,6 @@ public class TmaPlayer extends MediaSessionCompat.Callback {
private static final String TAG = "TmaPlayer";
- // TODO: refactor actions (make some per item, per state ??)...
- private static final long PLAYING_ACTIONS = PlaybackStateCompat.ACTION_PAUSE
- | ACTION_PLAY_FROM_MEDIA_ID | ACTION_SKIP_TO_NEXT | ACTION_SKIP_TO_PREVIOUS
- | ACTION_SKIP_TO_QUEUE_ITEM | ACTION_SEEK_TO;
-
- private static final long PAUSED_ACTIONS = PlaybackStateCompat.ACTION_PLAY
- | ACTION_PLAY_FROM_MEDIA_ID | ACTION_SKIP_TO_NEXT | ACTION_SKIP_TO_PREVIOUS
- | ACTION_SEEK_TO;
-
- private static final long STOPPED_ACTIONS = PlaybackStateCompat.ACTION_PLAY
- | ACTION_PLAY_FROM_MEDIA_ID | ACTION_SKIP_TO_NEXT | ACTION_SKIP_TO_PREVIOUS
- | ACTION_SEEK_TO;
-
-
private final Context mContext;
private final TmaPrefs mPrefs;
private final TmaLibrary mLibrary;
@@ -109,7 +99,7 @@ public class TmaPlayer extends MediaSessionCompat.Callback {
PlaybackStateCompat.Builder state = new PlaybackStateCompat.Builder()
.setState(event.mState.mValue, mCurrentPositionMs, mPlaybackSpeed)
.setErrorMessage(event.mErrorCode.mValue, event.mErrorMessage)
- .setActions(PLAYING_ACTIONS);
+ .setActions(addActions(ACTION_PAUSE));
if (ResolutionIntent.PREFS.equals(event.mResolutionIntent)) {
Intent prefsIntent = new Intent();
prefsIntent.setClass(mContext, TmaPrefsActivity.class);
@@ -121,24 +111,67 @@ public class TmaPlayer extends MediaSessionCompat.Callback {
extras.putParcelable(ERROR_RESOLUTION_ACTION_INTENT, pendingIntent);
state.setExtras(extras);
}
+
+ setActiveItemState(state);
mSession.setPlaybackState(state.build());
}
+ /** Sets custom action, queue id, etc. */
+ private void setActiveItemState(PlaybackStateCompat.Builder state) {
+ if (mActiveItem != null) {
+ for (TmaCustomAction action : mActiveItem.mCustomActions) {
+ String name = mContext.getResources().getString(action.mNameId);
+ state.addCustomAction(action.mId, name, action.mIcon);
+ }
+ state.setActiveQueueItemId(mActiveItem.getQueueId());
+ }
+ }
+
+ private void playItem(@Nullable TmaMediaItem item) {
+ if (item != null && item.getParent() != null) {
+ if (mIsPlaying) {
+ stopPlayback();
+ }
+ mActiveItem = item;
+ mSession.setQueue(item.getParent().buildQueue());
+ startPlayBack(true);
+ }
+ }
+
@Override
public void onPlayFromMediaId(String mediaId, Bundle extras) {
super.onPlayFromMediaId(mediaId, extras);
- mActiveItem = mLibrary.getMediaItemById(mediaId);
- if (requestAudioFocus()) {
- startPlayBack();
+ playItem(mLibrary.getMediaItemById(mediaId));
+ }
+
+ @Override
+ public void onSkipToQueueItem(long id) {
+ super.onSkipToQueueItem(id);
+ if (mActiveItem != null && mActiveItem.getParent() != null) {
+ playItem(mActiveItem.getParent().getPlayableByIndex(id));
+ }
+ }
+
+ @Override
+ public void onSkipToNext() {
+ super.onSkipToNext();
+ if (mActiveItem != null) {
+ playItem(mActiveItem.getNext());
+ }
+ }
+
+ @Override
+ public void onSkipToPrevious() {
+ super.onSkipToPrevious();
+ if (mActiveItem != null) {
+ playItem(mActiveItem.getPrevious());
}
}
@Override
public void onPlay() {
super.onPlay();
- if (requestAudioFocus()) {
- startPlayBack();
- }
+ startPlayBack(true);
}
@Override
@@ -149,9 +182,8 @@ public class TmaPlayer extends MediaSessionCompat.Callback {
mHandler.removeCallbacks(mTrackTimer);
}
mCurrentPositionMs = pos;
- if (wasPlaying) {
- startPlayBack();
- }
+ boolean requestAudioFocus = !wasPlaying;
+ startPlayBack(requestAudioFocus);
}
@Override
@@ -167,11 +199,32 @@ public class TmaPlayer extends MediaSessionCompat.Callback {
sendStopPlaybackState();
}
- private boolean requestAudioFocus() {
+ @Override
+ public void onCustomAction(String action, Bundle extras) {
+ super.onCustomAction(action, extras);
+ if (mActiveItem != null) {
+ if (TmaCustomAction.HEART_PLUS_PLUS.mId.equals(action)) {
+ mActiveItem.mHearts++;
+ toast("" + mActiveItem.mHearts);
+ } else if (TmaCustomAction.HEART_LESS_LESS.mId.equals(action)) {
+ mActiveItem.mHearts--;
+ toast("" + mActiveItem.mHearts);
+ }
+ }
+ }
+
+ /** Note: this is for quick feedback implementation, media apps should avoid toasts... */
+ private void toast(String message) {
+ Toast.makeText(mContext, message, Toast.LENGTH_LONG).show();
+ }
+
+ private boolean audioFocusGranted() {
return mAudioManager.requestAudioFocus(mAudioFocusRequest) == AUDIOFOCUS_REQUEST_GRANTED;
}
private void onProcessMediaEvent() {
+ if (mActiveItem == null) return;
+
TmaMediaEvent event = mActiveItem.mMediaEvents.get(mNextEventIndex);
if (event.premiumAccountRequired() &&
@@ -205,7 +258,9 @@ public class TmaPlayer extends MediaSessionCompat.Callback {
}
}
- private void startPlayBack() {
+ private void startPlayBack(boolean requestAudioFocus) {
+ if (requestAudioFocus && !audioFocusGranted()) return;
+
if (mActiveItem == null || mActiveItem.mMediaEvents.size() <= 0) {
PlaybackStateCompat state = new PlaybackStateCompat.Builder()
.setState(STATE_ERROR, mCurrentPositionMs, mPlaybackSpeed)
@@ -217,6 +272,7 @@ public class TmaPlayer extends MediaSessionCompat.Callback {
mActiveItem.updateSessionMetadata(mSession);
+ mHandler.removeCallbacks(mEventTrigger);
mNextEventIndex = 0;
mHandler.postDelayed(mEventTrigger, mActiveItem.mMediaEvents.get(0).mPostDelayMs);
}
@@ -224,11 +280,11 @@ public class TmaPlayer extends MediaSessionCompat.Callback {
private void pausePlayback() {
mCurrentPositionMs += (System.currentTimeMillis() - mPlaybackStartTimeMs) / mPlaybackSpeed;
mHandler.removeCallbacks(mTrackTimer);
- PlaybackStateCompat state = new PlaybackStateCompat.Builder()
+ PlaybackStateCompat.Builder state = new PlaybackStateCompat.Builder()
.setState(PlaybackStateCompat.STATE_PAUSED, mCurrentPositionMs, mPlaybackSpeed)
- .setActions(PAUSED_ACTIONS)
- .build();
- mSession.setPlaybackState(state);
+ .setActions(addActions(ACTION_PLAY));
+ setActiveItemState(state);
+ mSession.setPlaybackState(state.build());
mIsPlaying = false;
}
@@ -240,10 +296,25 @@ public class TmaPlayer extends MediaSessionCompat.Callback {
}
private void sendStopPlaybackState() {
- PlaybackStateCompat state = new PlaybackStateCompat.Builder()
+ PlaybackStateCompat.Builder state = new PlaybackStateCompat.Builder()
.setState(PlaybackStateCompat.STATE_STOPPED, mCurrentPositionMs, mPlaybackSpeed)
- .setActions(STOPPED_ACTIONS)
- .build();
- mSession.setPlaybackState(state);
+ .setActions(addActions(ACTION_PLAY));
+ setActiveItemState(state);
+ mSession.setPlaybackState(state.build());
+ }
+
+ private long addActions(long actions) {
+ actions |= ACTION_PLAY_FROM_MEDIA_ID | ACTION_SKIP_TO_QUEUE_ITEM | ACTION_SEEK_TO;
+
+ if (mActiveItem != null) {
+ if (mActiveItem.getNext() != null) {
+ actions |= ACTION_SKIP_TO_NEXT;
+ }
+ if (mActiveItem.getPrevious() != null) {
+ actions |= ACTION_SKIP_TO_PREVIOUS;
+ }
+ }
+
+ return actions;
}
}
diff --git a/TestMediaApp/src/com/android/car/media/testmediaapp/loader/TmaLoaderUtils.java b/TestMediaApp/src/com/android/car/media/testmediaapp/loader/TmaLoaderUtils.java
index 7cc4eb5..125c3a7 100644
--- a/TestMediaApp/src/com/android/car/media/testmediaapp/loader/TmaLoaderUtils.java
+++ b/TestMediaApp/src/com/android/car/media/testmediaapp/loader/TmaLoaderUtils.java
@@ -30,7 +30,9 @@ import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
+import java.util.ArrayList;
import java.util.HashMap;
+import java.util.List;
import java.util.Map;
class TmaLoaderUtils {
@@ -83,6 +85,23 @@ class TmaLoaderUtils {
}
}
+ /** Returns the array mapped to the name of the given key, or empty if missing. */
+ static <T extends Enum, U extends Enum> List<U> getEnumArray(JSONObject json, T key,
+ Map<String, U> enumMap) {
+ try {
+ JSONArray array = json.has(key.name()) ? json.getJSONArray(key.name()) : null;
+ int count = (array != null) ? array.length() : 0;
+ List<U> result = new ArrayList<>(count);
+ for (int i = 0; i < count; i++) {
+ result.add(enumMap.get(array.getString(i)));
+ }
+ return result;
+ } catch (JSONException e) {
+ Log.e(TAG, "JSONException getting array for: " + key + " e: " + e);
+ return new ArrayList<>();
+ }
+ }
+
/** Returns the string mapped to the name of the given key, or null if missing. */
@Nullable
static <T extends Enum> String getString(JSONObject json, T key) {
diff --git a/TestMediaApp/src/com/android/car/media/testmediaapp/loader/TmaMediaEventReader.java b/TestMediaApp/src/com/android/car/media/testmediaapp/loader/TmaMediaEventReader.java
index 4ec47e2..b6d1a7a 100644
--- a/TestMediaApp/src/com/android/car/media/testmediaapp/loader/TmaMediaEventReader.java
+++ b/TestMediaApp/src/com/android/car/media/testmediaapp/loader/TmaMediaEventReader.java
@@ -75,7 +75,7 @@ class TmaMediaEventReader {
}
@Nullable
- public TmaMediaEvent fromJson(@Nullable JSONObject json) {
+ TmaMediaEvent fromJson(@Nullable JSONObject json) {
if (json == null) return null;
return new TmaMediaEvent(
getEnum(json, Keys.STATE, mEventStates, EventState.NONE),
diff --git a/TestMediaApp/src/com/android/car/media/testmediaapp/loader/TmaMediaItemReader.java b/TestMediaApp/src/com/android/car/media/testmediaapp/loader/TmaMediaItemReader.java
index 3416a9f..2d4b845 100644
--- a/TestMediaApp/src/com/android/car/media/testmediaapp/loader/TmaMediaItemReader.java
+++ b/TestMediaApp/src/com/android/car/media/testmediaapp/loader/TmaMediaItemReader.java
@@ -23,15 +23,15 @@ import static com.android.car.media.testmediaapp.TmaMediaEvent.INSTANT_PLAYBACK;
import static com.android.car.media.testmediaapp.loader.TmaLoaderUtils.enumNamesToValues;
import static com.android.car.media.testmediaapp.loader.TmaLoaderUtils.getArray;
import static com.android.car.media.testmediaapp.loader.TmaLoaderUtils.getEnum;
+import static com.android.car.media.testmediaapp.loader.TmaLoaderUtils.getEnumArray;
import static com.android.car.media.testmediaapp.loader.TmaLoaderUtils.getString;
-import android.support.v4.media.MediaMetadataCompat;
-import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.Nullable;
import com.android.car.media.testmediaapp.TmaMediaEvent;
+import com.android.car.media.testmediaapp.TmaMediaItem.TmaCustomAction;
import com.android.car.media.testmediaapp.TmaMediaItem;
import com.android.car.media.testmediaapp.TmaMediaItem.ContentStyle;
@@ -57,6 +57,7 @@ class TmaMediaItemReader {
METADATA,
CHILDREN,
INCLUDE,
+ CUSTOM_ACTIONS,
EVENTS
}
@@ -73,18 +74,20 @@ class TmaMediaItemReader {
private final TmaMediaEventReader mMediaEventReader;
private final Map<String, Integer> mFlags = new HashMap<>(2);
private final Map<String, ContentStyle> mContentStyles;
+ private final Map<String, TmaCustomAction> mCustomActions;
private TmaMediaItemReader() {
mMediaMetadataReader = TmaMediaMetadataReader.getInstance();
mMediaEventReader = TmaMediaEventReader.getInstance();
mContentStyles = enumNamesToValues(ContentStyle.values());
+ mCustomActions = enumNamesToValues(TmaMediaItem.TmaCustomAction.values());
mFlags.put("browsable", FLAG_BROWSABLE);
mFlags.put("playable", FLAG_PLAYABLE);
}
@Nullable
- public TmaMediaItem fromJson(@Nullable JSONObject json) {
+ TmaMediaItem fromJson(@Nullable JSONObject json) {
if (json == null) return null;
try {
// Media events
@@ -107,11 +110,11 @@ class TmaMediaItemReader {
}
- return new TmaMediaItem(
- TmaLoaderUtils.parseFlags(getString(json, Keys.FLAGS), mFlags),
+ return new TmaMediaItem(TmaLoaderUtils.parseFlags(getString(json, Keys.FLAGS), mFlags),
getEnum(json, Keys.PLAYABLE_HINT, mContentStyles, ContentStyle.NONE),
getEnum(json, Keys.BROWSABLE_HINT, mContentStyles, ContentStyle.NONE),
mMediaMetadataReader.fromJson(json.getJSONObject(Keys.METADATA.name())),
+ getEnumArray(json, Keys.CUSTOM_ACTIONS, mCustomActions),
mediaEvents, mediaItems, getString(json, Keys.INCLUDE));
} catch (JSONException e) {
Log.e(TAG, "Json failure: " + e);