aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorandroid-build-team Robot <android-build-team-robot@google.com>2019-10-28 15:29:42 +0000
committerandroid-build-team Robot <android-build-team-robot@google.com>2019-10-28 15:29:42 +0000
commita829943c39b100d34e4065583b4eb67a41658675 (patch)
treeaae9173350881e3380ce15f12d618e1e922719ba
parent47791d7120dfa667e3a30ff487ab11a10623d1bf (diff)
parent5cb8036a6830d6981100efaf7a6ee389264cf440 (diff)
downloadtests-a829943c39b100d34e4065583b4eb67a41658675.tar.gz
Snap for 5970985 from 5cb8036a6830d6981100efaf7a6ee389264cf440 to qt-d4-release
Change-Id: I29af7418650cc6b91791354ea30fdf4b7e1d8760
-rw-r--r--.gitignore7
-rw-r--r--TestMediaApp/Android.mk3
-rw-r--r--TestMediaApp/AndroidManifest.xml14
-rw-r--r--TestMediaApp/assets/media_items/album_art/art_nodes.json9
-rw-r--r--TestMediaApp/assets/media_items/album_art/nature/art_nature_1024.json18
-rw-r--r--TestMediaApp/assets/media_items/album_art/nature/art_nature_128.json18
-rw-r--r--TestMediaApp/assets/media_items/album_art/nature/art_nature_256.json18
-rw-r--r--TestMediaApp/assets/media_items/album_art/nature/art_nature_512.json18
-rw-r--r--TestMediaApp/assets/media_items/album_art/nature/art_nature_64.json18
-rw-r--r--TestMediaApp/assets/media_items/album_art/nature/art_nature_files.json92
-rw-r--r--TestMediaApp/assets/media_items/simple_leaves.json38
-rw-r--r--TestMediaApp/assets/media_items/single_node.json22
-rw-r--r--TestMediaApp/build.gradle60
-rw-r--r--TestMediaApp/res/drawable/button_ripple_bg.xml2
-rw-r--r--TestMediaApp/res/drawable/ic_close.xml27
-rw-r--r--TestMediaApp/res/values/styles.xml7
-rw-r--r--TestMediaApp/res/xml/automotive_app_desc.xml20
-rw-r--r--TestMediaApp/src/com/android/car/media/testmediaapp/MediaKeys.java68
-rw-r--r--TestMediaApp/src/com/android/car/media/testmediaapp/TmaBrowser.java51
-rw-r--r--TestMediaApp/src/com/android/car/media/testmediaapp/TmaLibrary.java2
-rw-r--r--TestMediaApp/src/com/android/car/media/testmediaapp/TmaMediaEvent.java13
-rw-r--r--TestMediaApp/src/com/android/car/media/testmediaapp/TmaMediaItem.java17
-rw-r--r--TestMediaApp/src/com/android/car/media/testmediaapp/TmaPlayer.java54
-rw-r--r--TestMediaApp/src/com/android/car/media/testmediaapp/TmaPublicProvider.java (renamed from TestMediaApp/src/com/android/car/media/testmediaapp/TmaAssetProvider.java)75
-rw-r--r--TestMediaApp/src/com/android/car/media/testmediaapp/loader/TmaMediaEventReader.java5
-rw-r--r--TestMediaApp/src/com/android/car/media/testmediaapp/loader/TmaMediaMetadataReader.java9
-rw-r--r--TestMediaApp/src/com/android/car/media/testmediaapp/prefs/TmaEnumPrefs.java25
-rw-r--r--TestMediaApp/src/com/android/car/media/testmediaapp/prefs/TmaPrefs.java10
-rw-r--r--TestMediaApp/src/com/android/car/media/testmediaapp/prefs/TmaPrefsFragment.java3
-rw-r--r--build.gradle41
-rw-r--r--gradle/wrapper/gradle-wrapper.jarbin0 -> 54329 bytes
-rw-r--r--gradle/wrapper/gradle-wrapper.properties6
-rwxr-xr-xgradlew172
-rw-r--r--read-me.txt1
-rw-r--r--readme.md49
-rw-r--r--settings.gradle17
36 files changed, 913 insertions, 96 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..bd59d21
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,7 @@
+*.iml
+.gradle
+/local.properties
+/.idea
+.DS_Store
+build/
+
diff --git a/TestMediaApp/Android.mk b/TestMediaApp/Android.mk
index b97a07e..67127e0 100644
--- a/TestMediaApp/Android.mk
+++ b/TestMediaApp/Android.mk
@@ -34,11 +34,12 @@ LOCAL_CERTIFICATE := platform
LOCAL_MODULE_TAGS := optional
# car_car is ok here because this is meant to simulate a third party media app
+# Do NOT add dependencies preventing the app from being unbundled (compiled with gradle in Studio).
LOCAL_STATIC_ANDROID_LIBRARIES := \
androidx.car_car \
androidx.appcompat_appcompat \
androidx.preference_preference \
- car-media-common
+ androidx.legacy_legacy-support-v4
LOCAL_USE_AAPT2 := true
diff --git a/TestMediaApp/AndroidManifest.xml b/TestMediaApp/AndroidManifest.xml
index 859815b..911145e 100644
--- a/TestMediaApp/AndroidManifest.xml
+++ b/TestMediaApp/AndroidManifest.xml
@@ -17,9 +17,10 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.car.media.testmediaapp" >
- <uses-sdk
- android:minSdkVersion="21"
- android:targetSdkVersion="28"/>
+ <uses-feature
+ android:name="android.hardware.type.automotive"
+ android:required="true"/>
+
<application
android:allowBackup="true"
@@ -27,10 +28,11 @@
android:supportsRtl="true"
android:theme="@style/TestMediaAppTheme" >
+ <!-- This provider is read-only, only returns album art, and is not a security risk -->
<provider
- android:name=".TmaAssetProvider"
+ android:name=".TmaPublicProvider"
android:exported="true"
- android:authorities="com.android.car.media.testmediaapp.assets"/>
+ android:authorities="com.android.car.media.testmediaapp.public"/>
<service
android:name=".TmaBrowser"
@@ -65,6 +67,8 @@
<!-- To use the app on a phone. -->
+ <meta-data android:name="com.google.android.gms.car.application"
+ android:resource="@xml/automotive_app_desc"/>
<activity android:name=".phone.TmaLauncherActivity" >
<intent-filter>
diff --git a/TestMediaApp/assets/media_items/album_art/art_nodes.json b/TestMediaApp/assets/media_items/album_art/art_nodes.json
index 434ed23..974268b 100644
--- a/TestMediaApp/assets/media_items/album_art/art_nodes.json
+++ b/TestMediaApp/assets/media_items/album_art/art_nodes.json
@@ -52,6 +52,15 @@
"DISPLAY_TITLE": "Nature 1024"
},
"INCLUDE":"media_items/album_art/nature/art_nature_1024.json"
+ },
+ {
+ "FLAGS": "browsable",
+ "PLAYABLE_HINT": "GRID",
+ "METADATA": {
+ "MEDIA_ID": "album_art/art_nodes nature files",
+ "DISPLAY_TITLE": "Nature files"
+ },
+ "INCLUDE":"media_items/album_art/nature/art_nature_files.json"
}
]
} \ No newline at end of file
diff --git a/TestMediaApp/assets/media_items/album_art/nature/art_nature_1024.json b/TestMediaApp/assets/media_items/album_art/nature/art_nature_1024.json
index b1759f2..c34ed80 100644
--- a/TestMediaApp/assets/media_items/album_art/nature/art_nature_1024.json
+++ b/TestMediaApp/assets/media_items/album_art/nature/art_nature_1024.json
@@ -13,7 +13,7 @@
"MEDIA_ID": "art_nature_1024_leaves bee",
"DISPLAY_TITLE": "Bee",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-1024/bee.jpg"
+ "ART_URI": "assets/bitmaps/nature-1024/bee.jpg"
}
},
{
@@ -22,7 +22,7 @@
"MEDIA_ID": "art_nature_1024_leaves clouds",
"DISPLAY_TITLE": "Clouds",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-1024/clouds.jpg"
+ "ART_URI": "assets/bitmaps/nature-1024/clouds.jpg"
}
},
{
@@ -31,7 +31,7 @@
"MEDIA_ID": "art_nature_1024_leaves flower1",
"DISPLAY_TITLE": "Flower 1",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-1024/flower1.jpg"
+ "ART_URI": "assets/bitmaps/nature-1024/flower1.jpg"
}
},
{
@@ -40,7 +40,7 @@
"MEDIA_ID": "art_nature_1024_leaves flower2",
"DISPLAY_TITLE": "Flower 2",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-1024/flower2.jpg"
+ "ART_URI": "assets/bitmaps/nature-1024/flower2.jpg"
}
},
{
@@ -49,7 +49,7 @@
"MEDIA_ID": "art_nature_1024_leaves flower3",
"DISPLAY_TITLE": "Flower3 ",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-1024/flower3.jpg"
+ "ART_URI": "assets/bitmaps/nature-1024/flower3.jpg"
}
},
{
@@ -58,7 +58,7 @@
"MEDIA_ID": "art_nature_1024_leaves flowers",
"DISPLAY_TITLE": "Flowers",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-1024/flowers.jpg"
+ "ART_URI": "assets/bitmaps/nature-1024/flowers.jpg"
}
},
{
@@ -67,7 +67,7 @@
"MEDIA_ID": "art_nature_1024_leaves leaves",
"DISPLAY_TITLE": "Leaves",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-1024/leaves.jpg"
+ "ART_URI": "assets/bitmaps/nature-1024/leaves.jpg"
}
},
{
@@ -76,7 +76,7 @@
"MEDIA_ID": "art_nature_1024_leaves sage",
"DISPLAY_TITLE": "Sage",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-1024/sage.jpg"
+ "ART_URI": "assets/bitmaps/nature-1024/sage.jpg"
}
},
{
@@ -85,7 +85,7 @@
"MEDIA_ID": "art_nature_1024_leaves tree",
"DISPLAY_TITLE": "Tree",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-1024/tree.jpg"
+ "ART_URI": "assets/bitmaps/nature-1024/tree.jpg"
}
}
]
diff --git a/TestMediaApp/assets/media_items/album_art/nature/art_nature_128.json b/TestMediaApp/assets/media_items/album_art/nature/art_nature_128.json
index 0fdd629..67200e8 100644
--- a/TestMediaApp/assets/media_items/album_art/nature/art_nature_128.json
+++ b/TestMediaApp/assets/media_items/album_art/nature/art_nature_128.json
@@ -13,7 +13,7 @@
"MEDIA_ID": "art_nature_128 bee",
"DISPLAY_TITLE": "Bee",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-128/bee.jpg"
+ "ART_URI": "assets/bitmaps/nature-128/bee.jpg"
}
},
{
@@ -22,7 +22,7 @@
"MEDIA_ID": "art_nature_128 clouds",
"DISPLAY_TITLE": "Clouds",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-128/clouds.jpg"
+ "ART_URI": "assets/bitmaps/nature-128/clouds.jpg"
}
},
{
@@ -31,7 +31,7 @@
"MEDIA_ID": "art_nature_128 flower1",
"DISPLAY_TITLE": "Flower 1",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-128/flower1.jpg"
+ "ART_URI": "assets/bitmaps/nature-128/flower1.jpg"
}
},
{
@@ -40,7 +40,7 @@
"MEDIA_ID": "art_nature_128 flower2",
"DISPLAY_TITLE": "Flower 2",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-128/flower2.jpg"
+ "ART_URI": "assets/bitmaps/nature-128/flower2.jpg"
}
},
{
@@ -49,7 +49,7 @@
"MEDIA_ID": "art_nature_128 flower3",
"DISPLAY_TITLE": "Flower3 ",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-128/flower3.jpg"
+ "ART_URI": "assets/bitmaps/nature-128/flower3.jpg"
}
},
{
@@ -58,7 +58,7 @@
"MEDIA_ID": "art_nature_128 flowers",
"DISPLAY_TITLE": "Flowers",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-128/flowers.jpg"
+ "ART_URI": "assets/bitmaps/nature-128/flowers.jpg"
}
},
{
@@ -67,7 +67,7 @@
"MEDIA_ID": "art_nature_128 leaves",
"DISPLAY_TITLE": "Leaves",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-128/leaves.jpg"
+ "ART_URI": "assets/bitmaps/nature-128/leaves.jpg"
}
},
{
@@ -76,7 +76,7 @@
"MEDIA_ID": "art_nature_128 sage",
"DISPLAY_TITLE": "Sage",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-128/sage.jpg"
+ "ART_URI": "assets/bitmaps/nature-128/sage.jpg"
}
},
{
@@ -85,7 +85,7 @@
"MEDIA_ID": "art_nature_128 tree",
"DISPLAY_TITLE": "Tree",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-128/tree.jpg"
+ "ART_URI": "assets/bitmaps/nature-128/tree.jpg"
}
}
]
diff --git a/TestMediaApp/assets/media_items/album_art/nature/art_nature_256.json b/TestMediaApp/assets/media_items/album_art/nature/art_nature_256.json
index 200ecc1..719665e 100644
--- a/TestMediaApp/assets/media_items/album_art/nature/art_nature_256.json
+++ b/TestMediaApp/assets/media_items/album_art/nature/art_nature_256.json
@@ -13,7 +13,7 @@
"MEDIA_ID": "art_nature_256 bee",
"DISPLAY_TITLE": "Bee",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-256/bee.jpg"
+ "ART_URI": "assets/bitmaps/nature-256/bee.jpg"
}
},
{
@@ -22,7 +22,7 @@
"MEDIA_ID": "art_nature_256 clouds",
"DISPLAY_TITLE": "Clouds",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-256/clouds.jpg"
+ "ART_URI": "assets/bitmaps/nature-256/clouds.jpg"
}
},
{
@@ -31,7 +31,7 @@
"MEDIA_ID": "art_nature_256 flower1",
"DISPLAY_TITLE": "Flower 1",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-256/flower1.jpg"
+ "ART_URI": "assets/bitmaps/nature-256/flower1.jpg"
}
},
{
@@ -40,7 +40,7 @@
"MEDIA_ID": "art_nature_256 flower2",
"DISPLAY_TITLE": "Flower 2",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-256/flower2.jpg"
+ "ART_URI": "assets/bitmaps/nature-256/flower2.jpg"
}
},
{
@@ -49,7 +49,7 @@
"MEDIA_ID": "art_nature_256 flower3",
"DISPLAY_TITLE": "Flower3 ",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-256/flower3.jpg"
+ "ART_URI": "assets/bitmaps/nature-256/flower3.jpg"
}
},
{
@@ -58,7 +58,7 @@
"MEDIA_ID": "art_nature_256 flowers",
"DISPLAY_TITLE": "Flowers",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-256/flowers.jpg"
+ "ART_URI": "assets/bitmaps/nature-256/flowers.jpg"
}
},
{
@@ -67,7 +67,7 @@
"MEDIA_ID": "art_nature_256 leaves",
"DISPLAY_TITLE": "Leaves",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-256/leaves.jpg"
+ "ART_URI": "assets/bitmaps/nature-256/leaves.jpg"
}
},
{
@@ -76,7 +76,7 @@
"MEDIA_ID": "art_nature_256 sage",
"DISPLAY_TITLE": "Sage",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-256/sage.jpg"
+ "ART_URI": "assets/bitmaps/nature-256/sage.jpg"
}
},
{
@@ -85,7 +85,7 @@
"MEDIA_ID": "art_nature_256 tree",
"DISPLAY_TITLE": "Tree",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-256/tree.jpg"
+ "ART_URI": "assets/bitmaps/nature-256/tree.jpg"
}
}
]
diff --git a/TestMediaApp/assets/media_items/album_art/nature/art_nature_512.json b/TestMediaApp/assets/media_items/album_art/nature/art_nature_512.json
index 56bb6a4..29cb783 100644
--- a/TestMediaApp/assets/media_items/album_art/nature/art_nature_512.json
+++ b/TestMediaApp/assets/media_items/album_art/nature/art_nature_512.json
@@ -13,7 +13,7 @@
"MEDIA_ID": "art_nature_512 bee",
"DISPLAY_TITLE": "Bee",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-512/bee.jpg"
+ "ART_URI": "assets/bitmaps/nature-512/bee.jpg"
}
},
{
@@ -22,7 +22,7 @@
"MEDIA_ID": "art_nature_512 clouds",
"DISPLAY_TITLE": "Clouds",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-512/clouds.jpg"
+ "ART_URI": "assets/bitmaps/nature-512/clouds.jpg"
}
},
{
@@ -31,7 +31,7 @@
"MEDIA_ID": "art_nature_512 flower1",
"DISPLAY_TITLE": "Flower 1",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-512/flower1.jpg"
+ "ART_URI": "assets/bitmaps/nature-512/flower1.jpg"
}
},
{
@@ -40,7 +40,7 @@
"MEDIA_ID": "art_nature_512 flower2",
"DISPLAY_TITLE": "Flower 2",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-512/flower2.jpg"
+ "ART_URI": "assets/bitmaps/nature-512/flower2.jpg"
}
},
{
@@ -49,7 +49,7 @@
"MEDIA_ID": "art_nature_512 flower3",
"DISPLAY_TITLE": "Flower3 ",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-512/flower3.jpg"
+ "ART_URI": "assets/bitmaps/nature-512/flower3.jpg"
}
},
{
@@ -58,7 +58,7 @@
"MEDIA_ID": "art_nature_512 flowers",
"DISPLAY_TITLE": "Flowers",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-512/flowers.jpg"
+ "ART_URI": "assets/bitmaps/nature-512/flowers.jpg"
}
},
{
@@ -67,7 +67,7 @@
"MEDIA_ID": "art_nature_512 leaves",
"DISPLAY_TITLE": "Leaves",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-512/leaves.jpg"
+ "ART_URI": "assets/bitmaps/nature-512/leaves.jpg"
}
},
{
@@ -76,7 +76,7 @@
"MEDIA_ID": "art_nature_512 sage",
"DISPLAY_TITLE": "Sage",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-512/sage.jpg"
+ "ART_URI": "assets/bitmaps/nature-512/sage.jpg"
}
},
{
@@ -85,7 +85,7 @@
"MEDIA_ID": "art_nature_512 tree",
"DISPLAY_TITLE": "Tree",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-512/tree.jpg"
+ "ART_URI": "assets/bitmaps/nature-512/tree.jpg"
}
}
]
diff --git a/TestMediaApp/assets/media_items/album_art/nature/art_nature_64.json b/TestMediaApp/assets/media_items/album_art/nature/art_nature_64.json
index 913dd07..72a3f41 100644
--- a/TestMediaApp/assets/media_items/album_art/nature/art_nature_64.json
+++ b/TestMediaApp/assets/media_items/album_art/nature/art_nature_64.json
@@ -13,7 +13,7 @@
"MEDIA_ID": "art_nature_64 bee",
"DISPLAY_TITLE": "Bee",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-64/bee.jpg"
+ "ART_URI": "assets/bitmaps/nature-64/bee.jpg"
}
},
{
@@ -22,7 +22,7 @@
"MEDIA_ID": "art_nature_64 clouds",
"DISPLAY_TITLE": "Clouds",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-64/clouds.jpg"
+ "ART_URI": "assets/bitmaps/nature-64/clouds.jpg"
}
},
{
@@ -31,7 +31,7 @@
"MEDIA_ID": "art_nature_64 flower1",
"DISPLAY_TITLE": "Flower 1",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-64/flower1.jpg"
+ "ART_URI": "assets/bitmaps/nature-64/flower1.jpg"
}
},
{
@@ -40,7 +40,7 @@
"MEDIA_ID": "art_nature_64 flower2",
"DISPLAY_TITLE": "Flower 2",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-64/flower2.jpg"
+ "ART_URI": "assets/bitmaps/nature-64/flower2.jpg"
}
},
{
@@ -49,7 +49,7 @@
"MEDIA_ID": "art_nature_64 flower3",
"DISPLAY_TITLE": "Flower3 ",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-64/flower3.jpg"
+ "ART_URI": "assets/bitmaps/nature-64/flower3.jpg"
}
},
{
@@ -58,7 +58,7 @@
"MEDIA_ID": "art_nature_64 flowers",
"DISPLAY_TITLE": "Flowers",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-64/flowers.jpg"
+ "ART_URI": "assets/bitmaps/nature-64/flowers.jpg"
}
},
{
@@ -67,7 +67,7 @@
"MEDIA_ID": "art_nature_64 leaves",
"DISPLAY_TITLE": "Leaves",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-64/leaves.jpg"
+ "ART_URI": "assets/bitmaps/nature-64/leaves.jpg"
}
},
{
@@ -76,7 +76,7 @@
"MEDIA_ID": "art_nature_64 sage",
"DISPLAY_TITLE": "Sage",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-64/sage.jpg"
+ "ART_URI": "assets/bitmaps/nature-64/sage.jpg"
}
},
{
@@ -85,7 +85,7 @@
"MEDIA_ID": "art_nature_64 tree",
"DISPLAY_TITLE": "Tree",
"DURATION": 10000,
- "ART_URI": "bitmaps/nature-64/tree.jpg"
+ "ART_URI": "assets/bitmaps/nature-64/tree.jpg"
}
}
]
diff --git a/TestMediaApp/assets/media_items/album_art/nature/art_nature_files.json b/TestMediaApp/assets/media_items/album_art/nature/art_nature_files.json
new file mode 100644
index 0000000..2643829
--- /dev/null
+++ b/TestMediaApp/assets/media_items/album_art/nature/art_nature_files.json
@@ -0,0 +1,92 @@
+{
+ "FLAGS": "browsable",
+
+ "METADATA": {
+ "MEDIA_ID": "art_nature_files_leaves",
+ "DISPLAY_TITLE": "Art nature files"
+ },
+
+ "CHILDREN": [
+ {
+ "FLAGS": "playable",
+ "METADATA": {
+ "MEDIA_ID": "art_nature_files_leaves bee",
+ "DISPLAY_TITLE": "Bee",
+ "DURATION": 10000,
+ "ART_URI": "files/bitmaps/nature-1024/bee.jpg"
+ }
+ },
+ {
+ "FLAGS": "playable",
+ "METADATA": {
+ "MEDIA_ID": "art_nature_files_leaves clouds",
+ "DISPLAY_TITLE": "Clouds",
+ "DURATION": 10000,
+ "ART_URI": "files/bitmaps/nature-1024/clouds.jpg"
+ }
+ },
+ {
+ "FLAGS": "playable",
+ "METADATA": {
+ "MEDIA_ID": "art_nature_files_leaves flower1",
+ "DISPLAY_TITLE": "Flower 1",
+ "DURATION": 10000,
+ "ART_URI": "files/bitmaps/nature-1024/flower1.jpg"
+ }
+ },
+ {
+ "FLAGS": "playable",
+ "METADATA": {
+ "MEDIA_ID": "art_nature_files_leaves flower2",
+ "DISPLAY_TITLE": "Flower 2",
+ "DURATION": 10000,
+ "ART_URI": "files/bitmaps/nature-1024/flower2.jpg"
+ }
+ },
+ {
+ "FLAGS": "playable",
+ "METADATA": {
+ "MEDIA_ID": "art_nature_files_leaves flower3",
+ "DISPLAY_TITLE": "Flower3 ",
+ "DURATION": 10000,
+ "ART_URI": "files/bitmaps/nature-1024/flower3.jpg"
+ }
+ },
+ {
+ "FLAGS": "playable",
+ "METADATA": {
+ "MEDIA_ID": "art_nature_files_leaves flowers",
+ "DISPLAY_TITLE": "Flowers",
+ "DURATION": 10000,
+ "ART_URI": "files/bitmaps/nature-1024/flowers.jpg"
+ }
+ },
+ {
+ "FLAGS": "playable",
+ "METADATA": {
+ "MEDIA_ID": "art_nature_files_leaves leaves",
+ "DISPLAY_TITLE": "Leaves",
+ "DURATION": 10000,
+ "ART_URI": "files/bitmaps/nature-1024/leaves.jpg"
+ }
+ },
+ {
+ "FLAGS": "playable",
+ "METADATA": {
+ "MEDIA_ID": "art_nature_files_leaves sage",
+ "DISPLAY_TITLE": "Sage",
+ "DURATION": 10000,
+ "ART_URI": "files/bitmaps/nature-1024/sage.jpg"
+ }
+ },
+ {
+ "FLAGS": "playable",
+ "METADATA": {
+ "MEDIA_ID": "art_nature_files_leaves tree",
+ "DISPLAY_TITLE": "Tree",
+ "DURATION": 10000,
+ "ART_URI": "files/bitmaps/nature-1024/tree.jpg"
+ }
+ }
+ ]
+} \ No newline at end of file
diff --git a/TestMediaApp/assets/media_items/simple_leaves.json b/TestMediaApp/assets/media_items/simple_leaves.json
index d70a2a4..dc6b0a3 100644
--- a/TestMediaApp/assets/media_items/simple_leaves.json
+++ b/TestMediaApp/assets/media_items/simple_leaves.json
@@ -11,7 +11,7 @@
"FLAGS": "playable",
"METADATA": {
"MEDIA_ID": "simple_leaves normal 10s song",
- "DISPLAY_TITLE": "A normal 10s song",
+ "DISPLAY_TITLE": "A normal 10s song with a long title. A normal 10s song with a long title. A normal 10s song with a long title. ",
"DURATION": 10000
}
},
@@ -20,6 +20,8 @@
"METADATA": {
"MEDIA_ID": "simple_leaves normal 1H song",
"DISPLAY_TITLE": "A normal 1H song",
+ "ARTIST": "Artist",
+ "ALBUM":"Album",
"DURATION": 3600000
}
},
@@ -28,6 +30,9 @@
"METADATA": {
"MEDIA_ID": "simple_leaves slow connection",
"DISPLAY_TITLE": "Connects and buffers for 4s each",
+ "DISPLAY_SUBTITLE": "A very long subtitle. A very long subtitle. A very long subtitle. A very long subtitle. A very long subtitle. A very long subtitle. ",
+ "ARTIST": "This is a very long artist name. This is a very long artist name. This is a very long artist name.",
+ "ALBUM":"Album",
"DURATION": 30000
},
"EVENTS": [
@@ -41,6 +46,7 @@
"METADATA": {
"MEDIA_ID": "simple_leaves poor internet",
"DISPLAY_TITLE": "Poor internet quality at 2s",
+ "ARTIST": "Artist",
"DURATION": 30000
},
"EVENTS": [
@@ -58,6 +64,8 @@
"METADATA": {
"MEDIA_ID": "simple_leaves cache failure",
"DISPLAY_TITLE": "Caching failure at 2s",
+ "DISPLAY_SUBTITLE": "Show a toast",
+ "ALBUM":"This is a very long album title. This is a very long album title. This is a very long album title.",
"DURATION": 30000
},
"EVENTS": [
@@ -75,6 +83,7 @@
"METADATA": {
"MEDIA_ID": "simple_leaves error code",
"DISPLAY_TITLE": "Parental Control error code at 1s",
+ "DISPLAY_SUBTITLE": "Show a toast",
"DURATION": 10000
},
"EVENTS": [
@@ -91,6 +100,7 @@
"METADATA": {
"MEDIA_ID": "simple_leaves premium required",
"DISPLAY_TITLE": "Paid account required at 1s",
+ "DISPLAY_SUBTITLE": "Show a dialog",
"DURATION": 50000
},
"EVENTS": [
@@ -104,6 +114,30 @@
"POST_DELAY_MS": 1000
}
]
+ },
+ {
+ "FLAGS": "playable",
+ "METADATA": {
+ "MEDIA_ID": "simple_leaves bluetooth disconnected and reconnected",
+ "DISPLAY_TITLE": "Bluetooth disconnected at 2s and reconnected at 8s",
+ "DURATION": 20000
+ },
+ "EVENTS": [
+ { "STATE": "PLAYING", "POST_DELAY_MS": 0 },
+ {
+ "STATE": "ERROR",
+ "ERROR_MESSAGE": "Bluetooth audio disconnected.",
+ "POST_DELAY_MS": 2000
+ },
+ {
+ "ACTION": "RESET_METADATA",
+ "POST_DELAY_MS": 6000
+ },
+ {
+ "STATE": "PLAYING",
+ "POST_DELAY_MS": 3000
+ }
+ ]
}
]
-} \ No newline at end of file
+}
diff --git a/TestMediaApp/assets/media_items/single_node.json b/TestMediaApp/assets/media_items/single_node.json
new file mode 100644
index 0000000..cf93cee
--- /dev/null
+++ b/TestMediaApp/assets/media_items/single_node.json
@@ -0,0 +1,22 @@
+{
+ "FLAGS": "browsable",
+ "PLAYABLE_HINT": "LIST",
+ "BROWSABLE_HINT": "GRID",
+
+ "METADATA": {
+ "MEDIA_ID": "single_node",
+ "DISPLAY_TITLE": "A lonely tab"
+ },
+
+ "CHILDREN": [
+ {
+ "FLAGS": "browsable",
+ "METADATA": {
+ "MEDIA_ID": "single_node simple_leaves",
+ "DISPLAY_TITLE": "Basic songs",
+ "ART_URI": "drawable/ic_heart_plus_plus"
+ },
+ "INCLUDE":"media_items/simple_leaves.json"
+ }
+ ]
+} \ No newline at end of file
diff --git a/TestMediaApp/build.gradle b/TestMediaApp/build.gradle
new file mode 100644
index 0000000..79fd66d
--- /dev/null
+++ b/TestMediaApp/build.gradle
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2019 The Android Open Source 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.
+ */
+
+
+apply plugin: 'com.android.application'
+
+android {
+ compileSdkVersion 28
+ defaultConfig {
+ applicationId "com.android.car.media.testmediaapp"
+ minSdkVersion 21
+ targetSdkVersion 28
+ versionCode 1
+ versionName "1.0"
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ lintOptions {
+ abortOnError false
+ }
+ buildTypes {
+ release {
+ minifyEnabled false
+ }
+ }
+
+ sourceSets {
+ main {
+ manifest.srcFile 'AndroidManifest.xml'
+ java.srcDirs = ['src']
+ resources.srcDirs = ['src']
+ aidl.srcDirs = ['src']
+ renderscript.srcDirs = ['src']
+ res.srcDirs = ['res']
+ assets.srcDirs = ['assets']
+ }
+ }
+}
+
+dependencies {
+ implementation 'androidx.appcompat:appcompat:1.0.2'
+ implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
+ implementation 'androidx.media:media:1.0.1'
+ implementation 'androidx.preference:preference:1.0.0'
+}
diff --git a/TestMediaApp/res/drawable/button_ripple_bg.xml b/TestMediaApp/res/drawable/button_ripple_bg.xml
index d012c94..9c99a25 100644
--- a/TestMediaApp/res/drawable/button_ripple_bg.xml
+++ b/TestMediaApp/res/drawable/button_ripple_bg.xml
@@ -17,4 +17,4 @@
<ripple
xmlns:android="http://schemas.android.com/apk/res/android"
- android:color="@*android:color/car_card_ripple_background" />
+ android:color="@color/ripple_background_color" />
diff --git a/TestMediaApp/res/drawable/ic_close.xml b/TestMediaApp/res/drawable/ic_close.xml
new file mode 100644
index 0000000..f4c1e3b
--- /dev/null
+++ b/TestMediaApp/res/drawable/ic_close.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ Copyright 2019 The Android Open Source 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.
+-->
+
+<vector
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="56dp"
+ android:height="56dp"
+ android:viewportHeight="24.0"
+ android:viewportWidth="24.0">
+ <path
+ android:fillColor="#FFF"
+ android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
+</vector> \ No newline at end of file
diff --git a/TestMediaApp/res/values/styles.xml b/TestMediaApp/res/values/styles.xml
index cf316c5..6a8e8b1 100644
--- a/TestMediaApp/res/values/styles.xml
+++ b/TestMediaApp/res/values/styles.xml
@@ -16,8 +16,11 @@
*/
-->
<resources>
- <style name="TestMediaAppTheme" parent="Theme.Car.Light.NoActionBar">
- <item name="android:windowBackground">@color/car_card_dark</item>
+ <style name="TestMediaAppTheme" parent="Theme.AppCompat.Light.NoActionBar">
+ <item name="android:windowBackground">@color/window_background </item>
</style>
+ <color name="window_background">#AAA</color>
+ <color name="ripple_background_color">#444</color>
+
</resources>
diff --git a/TestMediaApp/res/xml/automotive_app_desc.xml b/TestMediaApp/res/xml/automotive_app_desc.xml
new file mode 100644
index 0000000..3daa01a
--- /dev/null
+++ b/TestMediaApp/res/xml/automotive_app_desc.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ * Copyright (c) 2019, The Android Open Source 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.
+ */
+-->
+<automotiveApp xmlns:android="http://schemas.android.com/apk/res/android">
+ <uses name="media"/>
+</automotiveApp> \ No newline at end of file
diff --git a/TestMediaApp/src/com/android/car/media/testmediaapp/MediaKeys.java b/TestMediaApp/src/com/android/car/media/testmediaapp/MediaKeys.java
new file mode 100644
index 0000000..9c58483
--- /dev/null
+++ b/TestMediaApp/src/com/android/car/media/testmediaapp/MediaKeys.java
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2019, The Android Open Source 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.
+ */
+
+package com.android.car.media.testmediaapp;
+
+/**
+ * Copy of constants defined in com.android.car.media.common.MediaConstants until they can be moved
+ * to a shared location available to all media apps. This makes un-bundling TestMediaApp easier.
+ */
+public class MediaKeys {
+
+ /** Integer extra indicating the recommended size (in pixels) for media art bitmaps. */
+ public static final String EXTRA_MEDIA_ART_SIZE_HINT_PIXELS =
+ "android.media.extras.MEDIA_ART_SIZE_HINT_PIXELS";
+
+ /**
+ * Bundle extra holding the Pending Intent to launch to let users resolve the current error.
+ * See {@link #ERROR_RESOLUTION_ACTION_LABEL} for more details.
+ */
+ static final String ERROR_RESOLUTION_ACTION_INTENT =
+ "android.media.extras.ERROR_RESOLUTION_ACTION_INTENT";
+
+
+ /**
+ * Bundle extra indicating the label of the button users can tap to resolve an error state.
+ */
+ static final String ERROR_RESOLUTION_ACTION_LABEL =
+ "android.media.extras.ERROR_RESOLUTION_ACTION_LABEL";
+
+ /**
+ * Bundle extra indicating the presentation hint for playable media items. See {@link
+ * #CONTENT_STYLE_LIST_ITEM_HINT_VALUE} or {@link #CONTENT_STYLE_GRID_ITEM_HINT_VALUE}
+ */
+ static final String CONTENT_STYLE_PLAYABLE_HINT =
+ "android.media.browse.CONTENT_STYLE_PLAYABLE_HINT";
+
+ /**
+ * Bundle extra indicating the presentation hint for browsable media items. See {@link
+ * #CONTENT_STYLE_LIST_ITEM_HINT_VALUE} or {@link #CONTENT_STYLE_GRID_ITEM_HINT_VALUE}
+ */
+ static final String CONTENT_STYLE_BROWSABLE_HINT =
+ "android.media.browse.CONTENT_STYLE_BROWSABLE_HINT";
+
+ /**
+ * Value for {@link #CONTENT_STYLE_PLAYABLE_HINT} and {@link #CONTENT_STYLE_BROWSABLE_HINT} that
+ * hints the corresponding items should be presented as lists.
+ */
+ static final int CONTENT_STYLE_LIST_ITEM_HINT_VALUE = 1;
+
+ /**
+ * Value for {@link #CONTENT_STYLE_PLAYABLE_HINT} and {@link #CONTENT_STYLE_BROWSABLE_HINT} that
+ * hints the corresponding items should be presented as grids.
+ */
+ static final int CONTENT_STYLE_GRID_ITEM_HINT_VALUE = 2;
+}
diff --git a/TestMediaApp/src/com/android/car/media/testmediaapp/TmaBrowser.java b/TestMediaApp/src/com/android/car/media/testmediaapp/TmaBrowser.java
index a51e623..6aeac39 100644
--- a/TestMediaApp/src/com/android/car/media/testmediaapp/TmaBrowser.java
+++ b/TestMediaApp/src/com/android/car/media/testmediaapp/TmaBrowser.java
@@ -15,6 +15,10 @@
*/
package com.android.car.media.testmediaapp;
+import static com.android.car.media.testmediaapp.prefs.TmaEnumPrefs.TmaBrowseNodeType.LEAF_CHILDREN;
+import static com.android.car.media.testmediaapp.prefs.TmaEnumPrefs.TmaBrowseNodeType.QUEUE_ONLY;
+import static com.android.car.media.testmediaapp.prefs.TmaEnumPrefs.TmaLoginEventOrder.PLAYBACK_STATE_UPDATE_FIRST;
+
import android.content.Context;
import android.media.AudioManager;
import android.os.Bundle;
@@ -22,6 +26,7 @@ import android.os.Handler;
import android.support.v4.media.MediaBrowserCompat.MediaItem;
import android.support.v4.media.session.MediaSessionCompat;
import android.support.v4.media.session.PlaybackStateCompat;
+import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@@ -45,10 +50,16 @@ import java.util.List;
* {@link TmaPlayer}.
*/
public class TmaBrowser extends MediaBrowserServiceCompat {
+ private static final String TAG = "TmaBrowser";
private static final String MEDIA_SESSION_TAG = "TEST_MEDIA_SESSION";
private static final String ROOT_ID = "_ROOT_ID_";
private static final String SEARCH_SUPPORTED = "android.media.browse.SEARCH_SUPPORTED";
+ /**
+ * Extras key to allow Android Auto to identify the browse service from the media session.
+ */
+ private static final String BROWSE_SERVICE_FOR_SESSION_KEY =
+ "android.media.session.BROWSE_SERVICE";
private TmaPrefs mPrefs;
private Handler mHandler;
@@ -74,6 +85,9 @@ public class TmaBrowser extends MediaBrowserServiceCompat {
mSession.setCallback(mPlayer);
mSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS
| MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
+ Bundle mediaSessionExtras = new Bundle();
+ mediaSessionExtras.putString(BROWSE_SERVICE_FOR_SESSION_KEY, TmaBrowser.class.getName());
+ mSession.setExtras(mediaSessionExtras);
mPrefs.mAccountType.registerChangeListener(
(oldValue, newValue) -> onAccountChanged(newValue));
@@ -84,9 +98,11 @@ public class TmaBrowser extends MediaBrowserServiceCompat {
mPrefs.mRootReplyDelay.registerChangeListener(
(oldValue, newValue) -> invalidateRoot());
- Bundle extras = new Bundle();
- extras.putBoolean(SEARCH_SUPPORTED, true);
- mRoot = new BrowserRoot(ROOT_ID, extras);
+ Bundle browserRootExtras = new Bundle();
+ browserRootExtras.putBoolean(SEARCH_SUPPORTED, true);
+ mRoot = new BrowserRoot(ROOT_ID, browserRootExtras);
+
+ updatePlaybackState(mPrefs.mAccountType.getValue());
}
@Override
@@ -98,20 +114,35 @@ public class TmaBrowser extends MediaBrowserServiceCompat {
}
private void onAccountChanged(TmaAccountType accountType) {
+ if (PLAYBACK_STATE_UPDATE_FIRST.equals(mPrefs.mLoginEventOrder.getValue())) {
+ updatePlaybackState(accountType);
+ invalidateRoot();
+ } else {
+ invalidateRoot();
+ (new Handler()).postDelayed(() -> {
+ updatePlaybackState(accountType);
+ }, 3000);
+ }
+ }
+
+ private void updatePlaybackState(TmaAccountType accountType) {
if (accountType == TmaAccountType.NONE) {
+ mSession.setMetadata(null);
+ mPlayer.onStop();
mPlayer.setPlaybackState(
new TmaMediaEvent(TmaMediaEvent.EventState.ERROR,
TmaMediaEvent.StateErrorCode.AUTHENTICATION_EXPIRED,
getResources().getString(R.string.no_account),
getResources().getString(R.string.select_account),
- TmaMediaEvent.ResolutionIntent.PREFS, 0, null));
+ TmaMediaEvent.ResolutionIntent.PREFS,
+ TmaMediaEvent.Action.NONE, 0, null));
} else {
// TODO don't reset error in all cases...
PlaybackStateCompat.Builder playbackState = new PlaybackStateCompat.Builder();
playbackState.setState(PlaybackStateCompat.STATE_PAUSED, 0, 0);
+ playbackState.setActions(PlaybackStateCompat.ACTION_PREPARE);
mSession.setPlaybackState(playbackState.build());
}
- invalidateRoot();
}
private void invalidateRoot() {
@@ -121,6 +152,8 @@ public class TmaBrowser extends MediaBrowserServiceCompat {
@Override
public BrowserRoot onGetRoot(
@NonNull String clientPackageName, int clientUid, Bundle rootHints) {
+ Log.i(TAG, "onGetroot client: " + clientPackageName + " EXTRA_MEDIA_ART_SIZE_HINT_PIXELS: "
+ + rootHints.getInt(MediaKeys.EXTRA_MEDIA_ART_SIZE_HINT_PIXELS, 0));
return mRoot;
}
@@ -128,6 +161,14 @@ public class TmaBrowser extends MediaBrowserServiceCompat {
public void onLoadChildren(@NonNull String parentId, @NonNull Result<List<MediaItem>> result) {
mLastLoadedNodeId = parentId;
getMediaItemsWithDelay(parentId, result, null);
+
+ if (QUEUE_ONLY.equals(mPrefs.mRootNodeType.getValue()) && ROOT_ID.equals(parentId)) {
+ TmaMediaItem queue = mLibrary.getRoot(LEAF_CHILDREN);
+ if (queue != null) {
+ mSession.setQueue(queue.buildQueue());
+ mPlayer.prepareMediaItem(queue.getPlayableByIndex(0));
+ }
+ }
}
@Override
diff --git a/TestMediaApp/src/com/android/car/media/testmediaapp/TmaLibrary.java b/TestMediaApp/src/com/android/car/media/testmediaapp/TmaLibrary.java
index eb77019..8bda24d 100644
--- a/TestMediaApp/src/com/android/car/media/testmediaapp/TmaLibrary.java
+++ b/TestMediaApp/src/com/android/car/media/testmediaapp/TmaLibrary.java
@@ -48,6 +48,8 @@ class TmaLibrary {
mLoader = loader;
mRootAssetPaths.put(TmaBrowseNodeType.NULL, null);
mRootAssetPaths.put(TmaBrowseNodeType.EMPTY, "media_items/empty.json");
+ mRootAssetPaths.put(TmaBrowseNodeType.QUEUE_ONLY, "media_items/empty.json");
+ mRootAssetPaths.put(TmaBrowseNodeType.SINGLE_TAB, "media_items/single_node.json");
mRootAssetPaths.put(TmaBrowseNodeType.NODE_CHILDREN, "media_items/only_nodes.json");
mRootAssetPaths.put(TmaBrowseNodeType.LEAF_CHILDREN, "media_items/simple_leaves.json");
mRootAssetPaths.put(TmaBrowseNodeType.MIXED_CHILDREN, "media_items/mixed.json");
diff --git a/TestMediaApp/src/com/android/car/media/testmediaapp/TmaMediaEvent.java b/TestMediaApp/src/com/android/car/media/testmediaapp/TmaMediaEvent.java
index f6ec8af..491f940 100644
--- a/TestMediaApp/src/com/android/car/media/testmediaapp/TmaMediaEvent.java
+++ b/TestMediaApp/src/com/android/car/media/testmediaapp/TmaMediaEvent.java
@@ -53,7 +53,7 @@ public class TmaMediaEvent {
public static final TmaMediaEvent INSTANT_PLAYBACK =
new TmaMediaEvent(EventState.PLAYING, StateErrorCode.UNKNOWN_ERROR, null, null,
- ResolutionIntent.NONE, 0, null);
+ ResolutionIntent.NONE, Action.NONE, 0, null);
/** The name of each entry is the value used in the json file. */
public enum EventState {
@@ -105,23 +105,31 @@ public class TmaMediaEvent {
PREFS
}
+ /** The name of each entry is the value used in the json file. */
+ public enum Action {
+ NONE,
+ RESET_METADATA
+ }
+
final EventState mState;
final StateErrorCode mErrorCode;
final String mErrorMessage;
final String mActionLabel;
final ResolutionIntent mResolutionIntent;
+ final Action mAction;
/** How long to wait before sending the event to the app. */
final int mPostDelayMs;
private final String mExceptionClass;
public TmaMediaEvent(EventState state, StateErrorCode errorCode, String errorMessage,
- String actionLabel, ResolutionIntent resolutionIntent, int postDelayMs,
+ String actionLabel, ResolutionIntent resolutionIntent, Action action, int postDelayMs,
String exceptionClass) {
mState = state;
mErrorCode = errorCode;
mErrorMessage = errorMessage;
mActionLabel = actionLabel;
mResolutionIntent = resolutionIntent;
+ mAction = action;
mPostDelayMs = postDelayMs;
mExceptionClass = exceptionClass;
}
@@ -152,6 +160,7 @@ public class TmaMediaEvent {
", mErrorMessage='" + mErrorMessage + '\'' +
", mActionLabel='" + mActionLabel + '\'' +
", mResolutionIntent=" + mResolutionIntent +
+ ", mAction=" + mAction +
", mPostDelayMs=" + mPostDelayMs +
", mExceptionClass=" + mExceptionClass +
'}';
diff --git a/TestMediaApp/src/com/android/car/media/testmediaapp/TmaMediaItem.java b/TestMediaApp/src/com/android/car/media/testmediaapp/TmaMediaItem.java
index 591f4cf..f79e273 100644
--- a/TestMediaApp/src/com/android/car/media/testmediaapp/TmaMediaItem.java
+++ b/TestMediaApp/src/com/android/car/media/testmediaapp/TmaMediaItem.java
@@ -22,11 +22,6 @@ import static android.support.v4.media.MediaBrowserCompat.MediaItem.FLAG_PLAYABL
import static android.support.v4.media.MediaMetadataCompat.METADATA_KEY_DURATION;
import static android.support.v4.media.MediaMetadataCompat.METADATA_KEY_MEDIA_ID;
-import static com.android.car.media.common.MediaConstants.CONTENT_STYLE_BROWSABLE_HINT;
-import static com.android.car.media.common.MediaConstants.CONTENT_STYLE_GRID_ITEM_HINT_VALUE;
-import static com.android.car.media.common.MediaConstants.CONTENT_STYLE_LIST_ITEM_HINT_VALUE;
-import static com.android.car.media.common.MediaConstants.CONTENT_STYLE_PLAYABLE_HINT;
-
import android.os.Bundle;
import android.support.v4.media.MediaBrowserCompat.MediaItem;
import android.support.v4.media.MediaDescriptionCompat;
@@ -46,8 +41,8 @@ public class TmaMediaItem {
/** The name of each entry is the value used in the json file. */
public enum ContentStyle {
NONE (0),
- LIST (CONTENT_STYLE_LIST_ITEM_HINT_VALUE),
- GRID (CONTENT_STYLE_GRID_ITEM_HINT_VALUE);
+ LIST (MediaKeys.CONTENT_STYLE_LIST_ITEM_HINT_VALUE),
+ GRID (MediaKeys.CONTENT_STYLE_GRID_ITEM_HINT_VALUE);
final int mBundleValue;
ContentStyle(int value) {
mBundleValue = value;
@@ -123,7 +118,11 @@ public class TmaMediaItem {
return mParent;
}
+ @Nullable
TmaMediaItem getPlayableByIndex(long index) {
+ if (index < 0 || index >= mPlayableChildren.size()) {
+ return null;
+ }
return mPlayableChildren.get((int)index);
}
@@ -208,8 +207,8 @@ public class TmaMediaItem {
extras.putAll(metadataDescription.getExtras());
}
- extras.putInt(CONTENT_STYLE_PLAYABLE_HINT, mPlayableStyle.mBundleValue);
- extras.putInt(CONTENT_STYLE_BROWSABLE_HINT, mBrowsableStyle.mBundleValue);
+ extras.putInt(MediaKeys.CONTENT_STYLE_PLAYABLE_HINT, mPlayableStyle.mBundleValue);
+ extras.putInt(MediaKeys.CONTENT_STYLE_BROWSABLE_HINT, mBrowsableStyle.mBundleValue);
bob.setExtras(extras);
return bob.build();
diff --git a/TestMediaApp/src/com/android/car/media/testmediaapp/TmaPlayer.java b/TestMediaApp/src/com/android/car/media/testmediaapp/TmaPlayer.java
index dc368ea..d8fab6c 100644
--- a/TestMediaApp/src/com/android/car/media/testmediaapp/TmaPlayer.java
+++ b/TestMediaApp/src/com/android/car/media/testmediaapp/TmaPlayer.java
@@ -21,6 +21,7 @@ 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_PREPARE;
import static android.support.v4.media.session.PlaybackStateCompat.ACTION_SEEK_TO;
import static android.support.v4.media.session.PlaybackStateCompat.ACTION_SKIP_TO_NEXT;
import static android.support.v4.media.session.PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS;
@@ -28,9 +29,6 @@ import static android.support.v4.media.session.PlaybackStateCompat.ACTION_SKIP_T
import static android.support.v4.media.session.PlaybackStateCompat.ERROR_CODE_APP_ERROR;
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 androidx.annotation.Nullable;
import android.app.PendingIntent;
import android.content.Context;
@@ -44,6 +42,7 @@ import android.support.v4.media.session.PlaybackStateCompat;
import android.util.Log;
import android.widget.Toast;
+import com.android.car.media.testmediaapp.TmaMediaEvent.Action;
import com.android.car.media.testmediaapp.TmaMediaEvent.EventState;
import com.android.car.media.testmediaapp.TmaMediaEvent.ResolutionIntent;
import com.android.car.media.testmediaapp.TmaMediaItem.TmaCustomAction;
@@ -107,8 +106,8 @@ public class TmaPlayer extends MediaSessionCompat.Callback {
PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, prefsIntent, 0);
Bundle extras = new Bundle();
- extras.putString(ERROR_RESOLUTION_ACTION_LABEL, event.mActionLabel);
- extras.putParcelable(ERROR_RESOLUTION_ACTION_INTENT, pendingIntent);
+ extras.putString(MediaKeys.ERROR_RESOLUTION_ACTION_LABEL, event.mActionLabel);
+ extras.putParcelable(MediaKeys.ERROR_RESOLUTION_ACTION_INTENT, pendingIntent);
state.setExtras(extras);
}
@@ -145,6 +144,46 @@ public class TmaPlayer extends MediaSessionCompat.Callback {
}
@Override
+ public void onPrepareFromMediaId(String mediaId, Bundle extras) {
+ super.onPrepareFromMediaId(mediaId, extras);
+
+ TmaMediaItem item = mLibrary.getMediaItemById(mediaId);
+ prepareMediaItem(item);
+ }
+
+ @Override
+ public void onPrepare() {
+ super.onPrepare();
+ if (!mSession.isActive()) {
+ mSession.setActive(true);
+ }
+ // Prepare the first playable item (at root level) as the active item
+ if (mActiveItem == null) {
+ TmaMediaItem root = mLibrary.getRoot(mPrefs.mRootNodeType.getValue());
+ if (root != null) {
+ prepareMediaItem(root.getPlayableByIndex(0));
+ }
+ }
+ }
+
+ void prepareMediaItem(@Nullable TmaMediaItem item) {
+ if (item != null && item.getParent() != null) {
+ if (mIsPlaying) {
+ stopPlayback();
+ }
+ mActiveItem = item;
+ mActiveItem.updateSessionMetadata(mSession);
+ mSession.setQueue(item.getParent().buildQueue());
+
+ PlaybackStateCompat.Builder state = new PlaybackStateCompat.Builder()
+ .setState(PlaybackStateCompat.STATE_PAUSED, mCurrentPositionMs, mPlaybackSpeed)
+ .setActions(addActions(ACTION_PLAY));
+ setActiveItemState(state);
+ mSession.setPlaybackState(state.build());
+ }
+ }
+
+ @Override
public void onSkipToQueueItem(long id) {
super.onSkipToQueueItem(id);
if (mActiveItem != null && mActiveItem.getParent() != null) {
@@ -232,6 +271,8 @@ public class TmaPlayer extends MediaSessionCompat.Callback {
TmaAccountType.PAID.equals(mPrefs.mAccountType.getValue())) {
Log.i(TAG, "Ignoring even for paid account");
return;
+ } else if (Action.RESET_METADATA.equals(event.mAction)) {
+ mSession.setMetadata(mSession.getController().getMetadata());
} else {
setPlaybackState(event);
}
@@ -305,7 +346,8 @@ public class TmaPlayer extends MediaSessionCompat.Callback {
}
private long addActions(long actions) {
- actions |= ACTION_PLAY_FROM_MEDIA_ID | ACTION_SKIP_TO_QUEUE_ITEM | ACTION_SEEK_TO;
+ actions |= ACTION_PLAY_FROM_MEDIA_ID | ACTION_SKIP_TO_QUEUE_ITEM | ACTION_SEEK_TO
+ | ACTION_PREPARE;
if (mActiveItem != null) {
if (mActiveItem.getNext() != null) {
diff --git a/TestMediaApp/src/com/android/car/media/testmediaapp/TmaAssetProvider.java b/TestMediaApp/src/com/android/car/media/testmediaapp/TmaPublicProvider.java
index fc9fd49..e7eb31b 100644
--- a/TestMediaApp/src/com/android/car/media/testmediaapp/TmaAssetProvider.java
+++ b/TestMediaApp/src/com/android/car/media/testmediaapp/TmaPublicProvider.java
@@ -21,56 +21,105 @@ import android.content.ContentValues;
import android.content.res.AssetFileDescriptor;
import android.database.Cursor;
import android.net.Uri;
+import android.os.ParcelFileDescriptor;
import android.text.TextUtils;
import android.util.Log;
import com.android.car.media.testmediaapp.prefs.TmaPrefs;
+import java.io.File;
import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
-public class TmaAssetProvider extends ContentProvider {
+public class TmaPublicProvider extends ContentProvider {
private static final String TAG = "TmaAssetProvider";
- private static final String PACKAGE_NAME = "com.android.car.media.testmediaapp";
+ private static final int DEFAULT_BUFFER_SIZE = 1024 * 4;
- private static final String ASSET_URI_PREFIX =
- ContentResolver.SCHEME_CONTENT + "://" + PACKAGE_NAME + ".assets/";
+ private static final String AUTHORITY = "com.android.car.media.testmediaapp.public";
+
+ private static final String FILES = "/files/";
+ private static final String ASSETS = "/assets/";
+
+ private static final String CONTENT_URI_PREFIX =
+ ContentResolver.SCHEME_CONTENT + "://" + AUTHORITY + "/";
private static final String RESOURCE_URI_PREFIX =
- ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + PACKAGE_NAME + "/";
+ ContentResolver.SCHEME_ANDROID_RESOURCE + "://" + AUTHORITY + "/";
public static String buildUriString(String localArt) {
- String prefix = localArt.startsWith("drawable") ? RESOURCE_URI_PREFIX : ASSET_URI_PREFIX;
+ String prefix = localArt.startsWith("drawable") ? RESOURCE_URI_PREFIX : CONTENT_URI_PREFIX;
return prefix + localArt;
}
private int mAssetDelay = 0;
+
+ @Override
+ public ParcelFileDescriptor openFile(Uri uri, String mode) throws FileNotFoundException {
+ String path = uri.getPath();
+
+ if (TextUtils.isEmpty(path) || !path.startsWith(FILES)) {
+ throw new FileNotFoundException(path);
+ }
+
+ Log.i(TAG, "TmaAssetProvider#openFile uri: " + uri + " path: " + path);
+
+ File localFile = new File(getContext().getFilesDir(), path);
+ if (!localFile.exists()) {
+ downloadFile(localFile, path.substring(FILES.length()));
+ }
+
+ return ParcelFileDescriptor.open(localFile,ParcelFileDescriptor.MODE_READ_ONLY);
+ }
+
@Override
public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
- Log.i(TAG, "TmaAssetProvider#openAssetFile " + uri);
+ String path = uri.getPath();
+ if (TextUtils.isEmpty(path) || !path.startsWith(ASSETS)) {
+ // The ImageDecoder and media center code always try to open as asset first, but
+ // super delegates to openFile...
+ return super.openAssetFile(uri, mode);
+ }
+
+ Log.i(TAG, "TmaAssetProvider#openAssetFile uri: " + uri + " path: " + path);
try {
Thread.sleep(mAssetDelay + (int)(mAssetDelay * (Math.random())));
} catch (InterruptedException ignored) {
}
- String file_path = uri.getPath();
- if (TextUtils.isEmpty(file_path)) throw new FileNotFoundException();
try {
- if (file_path.startsWith("/")) {
- file_path = file_path.substring(1);
- }
- return getContext().getAssets().openFd(file_path);
+ return getContext().getAssets().openFd(path.substring(ASSETS.length()));
} catch (IOException e) {
Log.e(TAG, "openAssetFile failed: " + e);
return null;
}
}
+ private void downloadFile(File localFile, String assetsPath) {
+ try {
+ localFile.getParentFile().mkdirs();
+
+ InputStream input = getContext().getAssets().open(assetsPath);
+ OutputStream output = new FileOutputStream(localFile);
+
+ byte[] buffer = new byte[DEFAULT_BUFFER_SIZE];
+ int n;
+ while (-1 != (n = input.read(buffer))) {
+ output.write(buffer, 0, n);
+ }
+
+ } catch (IOException e) {
+ Log.e(TAG, "downloadFile failed: " + e);
+ }
+ }
+
@Override
public boolean onCreate() {
TmaPrefs.getInstance(getContext()).mAssetReplyDelay.registerChangeListener(
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 c2e573a..2222afa 100644
--- a/TestMediaApp/src/com/android/car/media/testmediaapp/loader/TmaMediaEventReader.java
+++ b/TestMediaApp/src/com/android/car/media/testmediaapp/loader/TmaMediaEventReader.java
@@ -26,6 +26,7 @@ import android.util.Log;
import androidx.annotation.Nullable;
import com.android.car.media.testmediaapp.TmaMediaEvent;
+import com.android.car.media.testmediaapp.TmaMediaEvent.Action;
import com.android.car.media.testmediaapp.TmaMediaEvent.EventState;
import com.android.car.media.testmediaapp.TmaMediaEvent.ResolutionIntent;
import com.android.car.media.testmediaapp.TmaMediaEvent.StateErrorCode;
@@ -53,6 +54,7 @@ class TmaMediaEventReader {
ERROR_MESSAGE,
ACTION_LABEL,
INTENT,
+ ACTION,
/** How long to wait before sending the event to the app. */
POST_DELAY_MS,
THROW_EXCEPTION
@@ -70,11 +72,13 @@ class TmaMediaEventReader {
private final Map<String, EventState> mEventStates;
private final Map<String, StateErrorCode> mErrorCodes;
private final Map<String, ResolutionIntent> mResolutionIntents;
+ private final Map<String, Action> mActions;
private TmaMediaEventReader() {
mEventStates = enumNamesToValues(EventState.values());
mErrorCodes = enumNamesToValues(StateErrorCode.values());
mResolutionIntents = enumNamesToValues(ResolutionIntent.values());
+ mActions = enumNamesToValues(Action.values());
}
@Nullable
@@ -86,6 +90,7 @@ class TmaMediaEventReader {
getString(json, Keys.ERROR_MESSAGE),
getString(json, Keys.ACTION_LABEL),
getEnum(json, Keys.INTENT, mResolutionIntents, ResolutionIntent.NONE),
+ getEnum(json, Keys.ACTION, mActions, Action.NONE),
getInt(json, Keys.POST_DELAY_MS),
getString(json, Keys.THROW_EXCEPTION));
}
diff --git a/TestMediaApp/src/com/android/car/media/testmediaapp/loader/TmaMediaMetadataReader.java b/TestMediaApp/src/com/android/car/media/testmediaapp/loader/TmaMediaMetadataReader.java
index 95f8f89..8cc4843 100644
--- a/TestMediaApp/src/com/android/car/media/testmediaapp/loader/TmaMediaMetadataReader.java
+++ b/TestMediaApp/src/com/android/car/media/testmediaapp/loader/TmaMediaMetadataReader.java
@@ -53,12 +53,13 @@ import static com.android.car.media.testmediaapp.loader.TmaLoaderUtils.enumNames
import android.support.v4.media.MediaMetadataCompat;
import android.util.Log;
-import com.android.car.media.testmediaapp.TmaAssetProvider;
+import com.android.car.media.testmediaapp.TmaPublicProvider;
import org.json.JSONException;
import org.json.JSONObject;
import java.util.EnumSet;
+import java.util.Iterator;
import java.util.Map;
import java.util.Set;
@@ -140,7 +141,9 @@ class TmaMediaMetadataReader {
MediaMetadataCompat fromJson(JSONObject object) throws JSONException {
MediaMetadataCompat.Builder builder = new MediaMetadataCompat.Builder();
- for (String jsonKey : object.keySet()) {
+ Iterator<String> keys = object.keys();
+ while (keys.hasNext()) {
+ String jsonKey = keys.next();
MetadataKey key = mMetadataKeys.get(jsonKey);
if (key != null) {
switch (key.mKeyType) {
@@ -150,7 +153,7 @@ class TmaMediaMetadataReader {
case TEXT:
String value = object.getString(jsonKey);
if (mUriKeys.contains(key)) {
- value = TmaAssetProvider.buildUriString(value);
+ value = TmaPublicProvider.buildUriString(value);
}
builder.putString(key.mLongName, value);
break;
diff --git a/TestMediaApp/src/com/android/car/media/testmediaapp/prefs/TmaEnumPrefs.java b/TestMediaApp/src/com/android/car/media/testmediaapp/prefs/TmaEnumPrefs.java
index ad870b6..347e52c 100644
--- a/TestMediaApp/src/com/android/car/media/testmediaapp/prefs/TmaEnumPrefs.java
+++ b/TestMediaApp/src/com/android/car/media/testmediaapp/prefs/TmaEnumPrefs.java
@@ -83,6 +83,8 @@ public class TmaEnumPrefs {
public enum TmaBrowseNodeType implements EnumPrefValue {
NULL("Null (error)", "null"),
EMPTY("Empty", "empty"),
+ QUEUE_ONLY("Queue only", "queue-only"),
+ SINGLE_TAB("Single browse-able tab", "single-tab"),
NODE_CHILDREN("Only browse-able content", "nodes"),
LEAF_CHILDREN("Only playable content (basic working and error cases)", "leaves"),
MIXED_CHILDREN("Mixed content (apps are not supposed to do that)", "mixed");
@@ -104,6 +106,29 @@ public class TmaEnumPrefs {
}
}
+ /* To simulate the events order after login. Media apps should update playback state first, then
+ * load the browse tree. But sometims some apps (e.g., GPB) don't follow this order strictly. */
+ public enum TmaLoginEventOrder implements EnumPrefValue {
+ PLAYBACK_STATE_UPDATE_FIRST("Update playback state first", "state-first"),
+ BROWSE_TREE_LOAD_FRIST("Load browse tree first", "tree-first");
+
+ private final PrefValueImpl mPrefValue;
+
+ TmaLoginEventOrder(String displayTitle, String id) {
+ mPrefValue = new PrefValueImpl(displayTitle, id);
+ }
+
+ @Override
+ public String getTitle() {
+ return mPrefValue.getTitle();
+ }
+
+ @Override
+ public String getId() {
+ return mPrefValue.getId();
+ }
+ }
+
private static class PrefValueImpl implements EnumPrefValue {
private final String mDisplayTitle;
diff --git a/TestMediaApp/src/com/android/car/media/testmediaapp/prefs/TmaPrefs.java b/TestMediaApp/src/com/android/car/media/testmediaapp/prefs/TmaPrefs.java
index 3305a06..8e9d89f 100644
--- a/TestMediaApp/src/com/android/car/media/testmediaapp/prefs/TmaPrefs.java
+++ b/TestMediaApp/src/com/android/car/media/testmediaapp/prefs/TmaPrefs.java
@@ -24,6 +24,7 @@ import androidx.preference.PreferenceManager;
import com.android.car.media.testmediaapp.prefs.TmaEnumPrefs.TmaAccountType;
import com.android.car.media.testmediaapp.prefs.TmaEnumPrefs.TmaBrowseNodeType;
+import com.android.car.media.testmediaapp.prefs.TmaEnumPrefs.TmaLoginEventOrder;
import com.android.car.media.testmediaapp.prefs.TmaEnumPrefs.TmaReplyDelay;
import java.util.HashMap;
@@ -45,6 +46,9 @@ public class TmaPrefs {
/** Wait time for openAssetFile. */
public final PrefEntry<TmaReplyDelay> mAssetReplyDelay;
+ /** Media apps event (update playback state, load browse tree) order after login. */
+ public final PrefEntry<TmaLoginEventOrder> mLoginEventOrder;
+
public synchronized static TmaPrefs getInstance(Context context) {
if (sPrefs == null) {
@@ -62,7 +66,8 @@ public class TmaPrefs {
ACCOUNT_TYPE_KEY,
ROOT_NODE_TYPE_KEY,
ROOT_REPLY_DELAY_KEY,
- ASSET_REPLY_DELAY_KEY
+ ASSET_REPLY_DELAY_KEY,
+ LOGIN_EVENT_ORDER_KEY
}
/**
@@ -128,6 +133,9 @@ public class TmaPrefs {
mAssetReplyDelay = new EnumPrefEntry<>(TmaPrefKey.ASSET_REPLY_DELAY_KEY,
TmaReplyDelay.values(), TmaReplyDelay.NONE);
+
+ mLoginEventOrder = new EnumPrefEntry<>(TmaPrefKey.LOGIN_EVENT_ORDER_KEY,
+ TmaLoginEventOrder.values(), TmaLoginEventOrder.PLAYBACK_STATE_UPDATE_FIRST);
}
diff --git a/TestMediaApp/src/com/android/car/media/testmediaapp/prefs/TmaPrefsFragment.java b/TestMediaApp/src/com/android/car/media/testmediaapp/prefs/TmaPrefsFragment.java
index 482de16..066cc9c 100644
--- a/TestMediaApp/src/com/android/car/media/testmediaapp/prefs/TmaPrefsFragment.java
+++ b/TestMediaApp/src/com/android/car/media/testmediaapp/prefs/TmaPrefsFragment.java
@@ -26,6 +26,7 @@ import androidx.preference.PreferenceScreen;
import com.android.car.media.testmediaapp.prefs.TmaEnumPrefs.TmaAccountType;
import com.android.car.media.testmediaapp.prefs.TmaEnumPrefs.TmaBrowseNodeType;
+import com.android.car.media.testmediaapp.prefs.TmaEnumPrefs.TmaLoginEventOrder;
import com.android.car.media.testmediaapp.prefs.TmaEnumPrefs.TmaReplyDelay;
import com.android.car.media.testmediaapp.prefs.TmaPrefs.PrefEntry;
@@ -46,6 +47,8 @@ public class TmaPrefsFragment extends PreferenceFragmentCompat {
TmaReplyDelay.values()));
screen.addPreference(createEnumPref(context, "Asset delay: random value in [v, 2v]",
prefs.mAssetReplyDelay, TmaReplyDelay.values()));
+ screen.addPreference(createEnumPref(context, "Login event order", prefs.mLoginEventOrder,
+ TmaLoginEventOrder.values()));
setPreferenceScreen(screen);
}
diff --git a/build.gradle b/build.gradle
new file mode 100644
index 0000000..df49ba3
--- /dev/null
+++ b/build.gradle
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2019 The Android Open Source 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.
+ */
+
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+
+buildscript {
+ repositories {
+ google()
+ jcenter()
+ }
+ dependencies {
+ classpath 'com.android.tools.build:gradle:3.5.0'
+
+ // NOTE: Do not place your application dependencies here; they belong
+ // in the individual module build.gradle files
+ }
+}
+
+allprojects {
+ repositories {
+ google()
+ jcenter()
+ }
+}
+
+task clean(type: Delete) {
+ delete rootProject.buildDir
+}
diff --git a/gradle/wrapper/gradle-wrapper.jar b/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..f6b961f
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.jar
Binary files differ
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..ce751bb
--- /dev/null
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Thu Sep 26 14:52:51 PDT 2019
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-5.4.1-all.zip
diff --git a/gradlew b/gradlew
new file mode 100755
index 0000000..cccdd3d
--- /dev/null
+++ b/gradlew
@@ -0,0 +1,172 @@
+#!/usr/bin/env sh
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >/dev/null
+APP_HOME="`pwd -P`"
+cd "$SAVED" >/dev/null
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn () {
+ echo "$*"
+}
+
+die () {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+ NONSTOP* )
+ nonstop=true
+ ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+ JAVACMD=`cygpath --unix "$JAVACMD"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Escape application args
+save () {
+ for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
+ echo " "
+}
+APP_ARGS=$(save "$@")
+
+# Collect all arguments for the java command, following the shell quoting and substitution rules
+eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
+
+# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong
+if [ "$(uname)" = "Darwin" ] && [ "$HOME" = "$PWD" ]; then
+ cd "$(dirname "$0")"
+fi
+
+exec "$JAVACMD" "$@"
diff --git a/read-me.txt b/read-me.txt
deleted file mode 100644
index a83c3e5..0000000
--- a/read-me.txt
+++ /dev/null
@@ -1 +0,0 @@
-This repository is only for test applications.
diff --git a/readme.md b/readme.md
new file mode 100644
index 0000000..f3b7ee8
--- /dev/null
+++ b/readme.md
@@ -0,0 +1,49 @@
+# Car test apps
+
+This repository is only for car test applications.
+
+## Building
+
+If you are not contributing to the repo, you can clone the repo via `git clone sso://googleplex-android/platform/packages/apps/Car/tests --branch pi-car-dev --single-branch`. Otherwise, see [workstation setup](#workstation-setup).
+
+Install [Android Studio](go/install-android-studio). Then import the `tests` Gradle project into Android Studio.
+
+### TestMediaApp
+
+TestMediaApp should be one of the run configurations. The green Run button should build and install the app on your phone.
+
+To see TestMediaApp in Android Auto Projected:
+
+1. Open Android Auto on phone
+2. Click hamburger icon at top left -> Settings
+3. Scroll to Version at bottom and tap ~10 times to unlock Developer Mode
+4. Click kebab icon at top right -> Developer settings
+5. Scroll to bottom and enable "Unknown sources"
+6. Exit and re-open Android Auto
+7. TestMediaApp should now be visible (click headphones icon in phone app to see app picker)
+
+## Contributing
+
+### Workstation setup
+
+Install [repo](https://source.android.com/setup/build/downloading#installing-repo) command line tool. Then run:
+
+```
+sudo apt-get install gitk
+sudo apt-get install git-gui
+mkdir WORKING_DIRECTORY_FOR_GIT_REPO
+cd WORKING_DIRECTORY_FOR_GIT_REPO
+repo init -u persistent-https://googleplex-android.git.corp.google.com/platform/manifest -b pi-car-dev -g name:platform/tools/repohooks,name:platform/packages/apps/Car/tests --depth=1
+repo sync
+```
+
+### Making a change
+
+```
+repo start BRANCH_NAME .
+# Make some changes
+git gui &
+# Use GUI to create a CL. Check amend box to update a work-in-progress CL
+repo upload .
+```
+
diff --git a/settings.gradle b/settings.gradle
new file mode 100644
index 0000000..38f6519
--- /dev/null
+++ b/settings.gradle
@@ -0,0 +1,17 @@
+/*
+ * Copyright (C) 2019 The Android Open Source 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.
+ */
+
+include ':TestMediaApp'