diff options
author | Yao Chen <yaochen@google.com> | 2014-07-17 14:39:34 -0700 |
---|---|---|
committer | Yao Chen <yaochen@google.com> | 2014-07-21 21:14:43 -0700 |
commit | 17d47989ee53c9e54f250d29a343ba949edf0ff9 (patch) | |
tree | 666e0297c2c5ec1f740dade8738c7cdeb5ee12fb /tests/MusicServiceDemo | |
parent | 66e207ec54e2085c0eb70d5189562effafa2c9c5 (diff) | |
download | frameworks_base-17d47989ee53c9e54f250d29a343ba949edf0ff9.zip frameworks_base-17d47989ee53c9e54f250d29a343ba949edf0ff9.tar.gz frameworks_base-17d47989ee53c9e54f250d29a343ba949edf0ff9.tar.bz2 |
Updated media browsing API & tests.
Change-Id: I3a1e3eead99a98b1890e05843dd90203cee517b7
Diffstat (limited to 'tests/MusicServiceDemo')
21 files changed, 1193 insertions, 0 deletions
diff --git a/tests/MusicServiceDemo/Android.mk b/tests/MusicServiceDemo/Android.mk new file mode 100644 index 0000000..feef67a --- /dev/null +++ b/tests/MusicServiceDemo/Android.mk @@ -0,0 +1,35 @@ +# Copyright (C) 2013 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. + +LOCAL_PATH := $(call my-dir) + +include $(CLEAR_VARS) + +LOCAL_PACKAGE_NAME := MusicServiceDemo +#LOCAL_SDK_VERSION := current +LOCAL_SRC_FILES := $(call all-java-files-under, src) +LOCAL_STATIC_JAVA_LIBRARIES := \ + android-support-v4 \ + android-support-v7-appcompat + +LOCAL_RESOURCE_DIR := \ + $(LOCAL_PATH)/res \ + frameworks/support/v7/appcompat/res +LOCAL_PROGUARD_ENABLED := disabled +#LOCAL_PROGUARD_FLAG_FILES := proguard.flags + +LOCAL_AAPT_FLAGS := \ + --auto-add-overlay \ + --extra-packages android.support.v7.appcompat +include $(BUILD_PACKAGE) diff --git a/tests/MusicServiceDemo/AndroidManifest.xml b/tests/MusicServiceDemo/AndroidManifest.xml new file mode 100644 index 0000000..4178a80 --- /dev/null +++ b/tests/MusicServiceDemo/AndroidManifest.xml @@ -0,0 +1,55 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2014 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. + --> +<manifest xmlns:android="http://schemas.android.com/apk/res/android" + package="com.example.android.musicservicedemo" + android:versionCode="1" + android:versionName="1.0" > + + <uses-permission android:name="android.permission.INTERNET" /> + <uses-permission android:name="android.permission.WAKE_LOCK" /> + + <uses-sdk + android:minSdkVersion="9" + android:targetSdkVersion="19" /> + + <application + android:allowBackup="true" + android:icon="@drawable/ic_launcher" + android:label="@string/app_name" + android:theme="@style/AppTheme" + > + + <activity + android:name="com.example.android.automotive.musicplayer.MainActivity" + android:label="@string/app_name" > + <intent-filter> + <action android:name="android.intent.action.MAIN" /> + <category android:name="android.intent.category.LAUNCHER" /> + </intent-filter> + </activity> + + <service + android:name=".BrowserService" + android:exported="true" + > + <intent-filter> + <action android:name="android.media.browse.MediaBrowseService" /> + </intent-filter> + </service> + </application> + +</manifest> diff --git a/tests/MusicServiceDemo/proguard-project.txt b/tests/MusicServiceDemo/proguard-project.txt new file mode 100644 index 0000000..f2fe155 --- /dev/null +++ b/tests/MusicServiceDemo/proguard-project.txt @@ -0,0 +1,20 @@ +# To enable ProGuard in your project, edit project.properties +# to define the proguard.config property as described in that file. +# +# Add project specific ProGuard rules here. +# By default, the flags in this file are appended to flags specified +# in ${sdk.dir}/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the ProGuard +# include property in project.properties. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# Add any project specific keep options here: + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} diff --git a/tests/MusicServiceDemo/res/drawable-hdpi/ic_launcher.png b/tests/MusicServiceDemo/res/drawable-hdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..47d6854 --- /dev/null +++ b/tests/MusicServiceDemo/res/drawable-hdpi/ic_launcher.png diff --git a/tests/MusicServiceDemo/res/drawable-mdpi/ic_launcher.png b/tests/MusicServiceDemo/res/drawable-mdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..01b53fd --- /dev/null +++ b/tests/MusicServiceDemo/res/drawable-mdpi/ic_launcher.png diff --git a/tests/MusicServiceDemo/res/drawable-xhdpi/ic_launcher.png b/tests/MusicServiceDemo/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..af762f2 --- /dev/null +++ b/tests/MusicServiceDemo/res/drawable-xhdpi/ic_launcher.png diff --git a/tests/MusicServiceDemo/res/drawable-xxhdpi/ic_launcher.png b/tests/MusicServiceDemo/res/drawable-xxhdpi/ic_launcher.png Binary files differnew file mode 100644 index 0000000..eef47aa --- /dev/null +++ b/tests/MusicServiceDemo/res/drawable-xxhdpi/ic_launcher.png diff --git a/tests/MusicServiceDemo/res/drawable-xxhdpi/thumbsup.png b/tests/MusicServiceDemo/res/drawable-xxhdpi/thumbsup.png Binary files differnew file mode 100644 index 0000000..ea98c95 --- /dev/null +++ b/tests/MusicServiceDemo/res/drawable-xxhdpi/thumbsup.png diff --git a/tests/MusicServiceDemo/res/layout/activity_main.xml b/tests/MusicServiceDemo/res/layout/activity_main.xml new file mode 100644 index 0000000..71753e3 --- /dev/null +++ b/tests/MusicServiceDemo/res/layout/activity_main.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2014 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. + --> +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:id="@+id/container" + android:layout_width="match_parent" + android:layout_height="match_parent" + tools:context="com.example.android.automotive.musicplayer.MainActivity" + tools:ignore="MergeRootFrame" /> diff --git a/tests/MusicServiceDemo/res/layout/fragment_main.xml b/tests/MusicServiceDemo/res/layout/fragment_main.xml new file mode 100644 index 0000000..8796e86 --- /dev/null +++ b/tests/MusicServiceDemo/res/layout/fragment_main.xml @@ -0,0 +1,32 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2014 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. + --> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:paddingBottom="@dimen/activity_vertical_margin" + android:paddingLeft="@dimen/activity_horizontal_margin" + android:paddingRight="@dimen/activity_horizontal_margin" + android:paddingTop="@dimen/activity_vertical_margin" + tools:context="com.example.android.automotive.musicplayer.MainActivity$PlaceholderFragment" > + + <TextView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="@string/app_name" /> + +</RelativeLayout> diff --git a/tests/MusicServiceDemo/res/values/colors.xml b/tests/MusicServiceDemo/res/values/colors.xml new file mode 100644 index 0000000..44dd05d --- /dev/null +++ b/tests/MusicServiceDemo/res/values/colors.xml @@ -0,0 +1,22 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- Copyright (C) 2014 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. +--> + +<resources> + <color name="yellow">#ffffff00</color> + <color name="green">#ff00ff00</color> + <color name="blue">#ff0000ff</color> + <color name="red">#ffff0000</color> +</resources> diff --git a/tests/MusicServiceDemo/res/values/dimens.xml b/tests/MusicServiceDemo/res/values/dimens.xml new file mode 100644 index 0000000..9f63ef2 --- /dev/null +++ b/tests/MusicServiceDemo/res/values/dimens.xml @@ -0,0 +1,23 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2014 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. + --> +<resources> + + <!-- Default screen margins, per the Android Design guidelines. --> + <dimen name="activity_horizontal_margin">16dp</dimen> + <dimen name="activity_vertical_margin">16dp</dimen> + +</resources> diff --git a/tests/MusicServiceDemo/res/values/strings.xml b/tests/MusicServiceDemo/res/values/strings.xml new file mode 100644 index 0000000..14c0171 --- /dev/null +++ b/tests/MusicServiceDemo/res/values/strings.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2014 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. + --> +<resources> + + <string name="app_name">Music Service Demo</string> + <string name="action_settings">Settings</string> + <string name="thumbs_up">Thumbs Up</string> + <string name="music_error">No music found</string> + <string name="now_playing">Now Playing</string> + +</resources> diff --git a/tests/MusicServiceDemo/res/values/styles.xml b/tests/MusicServiceDemo/res/values/styles.xml new file mode 100644 index 0000000..b83662d --- /dev/null +++ b/tests/MusicServiceDemo/res/values/styles.xml @@ -0,0 +1,36 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + Copyright (C) 2014 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. + --> +<resources> + + <!-- + Base application theme, dependent on API level. This theme is replaced + by AppBaseTheme from res/values-vXX/styles.xml on newer devices. + --> + <style name="AppBaseTheme" parent="Theme.AppCompat.Light"> + <!-- + Theme customizations available in newer API levels can go in + res/values-vXX/styles.xml, while customizations related to + backward-compatibility can go here. + --> + </style> + + <!-- Application theme. --> + <style name="AppTheme" parent="AppBaseTheme"> + <!-- All customizations that are NOT specific to a particular API-level can go here. --> + </style> + +</resources> diff --git a/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/BrowserService.java b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/BrowserService.java new file mode 100644 index 0000000..9ca156f --- /dev/null +++ b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/BrowserService.java @@ -0,0 +1,246 @@ +/* + * Copyright (C) 2014 Google Inc. All Rights Reserved. + * + * 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.example.android.musicservicedemo; + +import android.app.SearchManager; +import android.app.Service; +import android.content.Context; +import android.content.Intent; +import android.content.UriMatcher; +import android.content.res.Resources.NotFoundException; +import android.database.MatrixCursor; +import android.graphics.Bitmap; +import android.media.AudioManager; +import android.media.MediaPlayer; +import android.media.MediaPlayer.OnCompletionListener; +import android.media.MediaPlayer.OnErrorListener; +import android.media.MediaPlayer.OnPreparedListener; +import android.media.browse.MediaBrowserItem; +import android.media.browse.MediaBrowserService; +import android.media.session.MediaSession; +import android.net.Uri; +import android.net.wifi.WifiManager; +import android.net.wifi.WifiManager.WifiLock; +import android.os.Bundle; +import android.os.Handler; +import android.os.IBinder; +import android.os.Message; +import android.os.PowerManager; +import android.os.RemoteException; +import android.os.SystemClock; +import android.util.Log; + +import com.example.android.musicservicedemo.browser.MusicProvider; +import com.example.android.musicservicedemo.browser.MusicProviderTask; +import com.example.android.musicservicedemo.browser.MusicProviderTaskListener; +import com.example.android.musicservicedemo.browser.MusicTrack; + +import org.json.JSONException; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +/** + * Service that implements MediaBrowserService and returns our menu hierarchy. + */ +public class BrowserService extends MediaBrowserService { + private static final String TAG = "BrowserService"; + + // URI paths for browsing music + public static final String BROWSE_ROOT_BASE_PATH = "browse"; + public static final String NOW_PLAYING_PATH = "now_playing"; + public static final String PIANO_BASE_PATH = "piano"; + public static final String VOICE_BASE_PATH = "voice"; + + // Content URIs + public static final String AUTHORITY = "com.example.android.automotive.musicplayer"; + public static final Uri BASE_URI = Uri.parse("content://" + AUTHORITY); + public static final Uri BROWSE_URI = Uri.withAppendedPath(BASE_URI, BROWSE_ROOT_BASE_PATH); + + // URI matcher constants for browsing paths + public static final int BROWSE_ROOT = 1; + public static final int NOW_PLAYING = 2; + public static final int PIANO = 3; + public static final int VOICE = 4; + + // Map the the URI paths with the URI matcher constants + private static final UriMatcher sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); + static { + sUriMatcher.addURI(AUTHORITY, BROWSE_ROOT_BASE_PATH, BROWSE_ROOT); + sUriMatcher.addURI(AUTHORITY, NOW_PLAYING_PATH, NOW_PLAYING); + sUriMatcher.addURI(AUTHORITY, PIANO_BASE_PATH, PIANO); + sUriMatcher.addURI(AUTHORITY, VOICE_BASE_PATH, VOICE); + } + + // Media metadata that will be provided for a media container + public static final String[] MEDIA_CONTAINER_PROJECTION = { + "uri", + "title", + "subtitle", + "image_uri", + "supported_actions" + }; + + // MusicProvider will download the music catalog + private MusicProvider mMusicProvider; + + private MediaSession mSession; + + @Override + public void onCreate() { + super.onCreate(); + + mSession = new MediaSession(this, "com.example.android.musicservicedemo.BrowserService"); + setSessionToken(mSession.getSessionToken()); + } + + @Override + public void onDestroy() { + super.onDestroy(); + } + + @Override + public Uri onGetRoot(String clientPackageName, int clientUid, Bundle rootHints) { + return BROWSE_URI; + } + + @Override + public List<MediaBrowserItem> onLoadChildren(Uri parentUri) { + final ArrayList<MediaBrowserItem> results = new ArrayList(); + + for (int i=0; i<10; i++) { + results.add(new MediaBrowserItem.Builder(Uri.withAppendedPath(BASE_URI, Integer.toString(i)), + MediaBrowserItem.FLAG_BROWSABLE, "Title " + i).setSummary("Summary " + i).build()); + } + + return results; + } + + @Override + public Bitmap onGetThumbnail(Uri uri, int width, int height, int density) { + return null; + } + + /* + @Override + public void query(final Query query, final IMetadataResultHandler metadataResultHandler, + final IErrorHandler errorHandler) + throws RemoteException { + Log.d(TAG, "query: " + query); + Utils.checkNotNull(query); + Utils.checkNotNull(metadataResultHandler); + Utils.checkNotNull(errorHandler); + + // Handle async response + new Thread(new Runnable() { + public void run() { + try { + // Pre-load the list of music + List<MusicTrack> musicTracks = getMusicList(); + if (musicTracks == null) { + notifyListenersOnPlaybackStateUpdate(getCurrentPlaybackState()); + errorHandler.onError(new Error(Error.UNKNOWN, + getString(R.string.music_error))); + return; + } + + final Uri uri = query.getUri(); + int match = sUriMatcher.match(uri); + Log.d(TAG, "Queried: " + uri + "; match: " + match); + switch (match) { + case BROWSE_ROOT: + { + Log.d(TAG, "Browse_root"); + + try { + MatrixCursor matrixCursor = mMusicProvider + .getRootContainerCurser(); + DataHolder holder = new DataHolder(MEDIA_CONTAINER_PROJECTION, + matrixCursor, null); + + Log.d(TAG, "on metadata response called " + holder.getCount()); + metadataResultHandler.onMetadataResponse(holder); + } catch (RemoteException e) { + Log.w(TAG, "Error delivering metadata in the callback.", e); + } + break; + } + case NOW_PLAYING: + { + try { + Log.d(TAG, "query NOW_PLAYING"); + MatrixCursor matrixCursor = mMusicProvider + .getRootItemCursor( + PIANO); + DataHolder holder = new DataHolder(MEDIA_CONTAINER_PROJECTION, + matrixCursor, null); + Log.d(TAG, "on metadata response called " + holder.getCount()); + metadataResultHandler.onMetadataResponse(holder); + } catch (RemoteException e) { + Log.w(TAG, "Error querying NOW_PLAYING"); + } + break; + } + case PIANO: + { + try { + Log.d(TAG, "query PIANO"); + MatrixCursor matrixCursor = mMusicProvider + .getRootItemCursor( + PIANO); + DataHolder holder = new DataHolder(MEDIA_CONTAINER_PROJECTION, + matrixCursor, null); + Log.d(TAG, "on metadata response called " + holder.getCount()); + metadataResultHandler.onMetadataResponse(holder); + } catch (RemoteException e) { + Log.w(TAG, "Error querying PIANO"); + } + break; + } + case VOICE: + { + try { + Log.d(TAG, "query VOICE"); + MatrixCursor matrixCursor = mMusicProvider + .getRootItemCursor( + VOICE); + DataHolder holder = new DataHolder(MEDIA_CONTAINER_PROJECTION, + matrixCursor, null); + Log.d(TAG, "on metadata response called " + holder.getCount()); + metadataResultHandler.onMetadataResponse(holder); + } catch (RemoteException e) { + Log.w(TAG, "Error querying VOICE"); + } + break; + } + default: + { + Log.w(TAG, "Skipping unmatched URI: " + uri); + } + } + } catch (NotFoundException e) { + Log.e(TAG, "::run:", e); + } catch (RemoteException e) { + Log.e(TAG, "::run:", e); + } + } // end run + }).start(); + } + + */ +} diff --git a/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/MainActivity.java b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/MainActivity.java new file mode 100644 index 0000000..db45b9d --- /dev/null +++ b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/MainActivity.java @@ -0,0 +1,101 @@ +/* Copyright 2014 Google Inc. All Rights Reserved. + * + * 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.example.android.musicservicedemo; + +import android.os.Bundle; +import android.support.v4.app.Fragment; +import android.support.v7.app.ActionBarActivity; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; + +import com.example.android.musicservicedemo.R; + +// TODO Local UI + +/** + * Main activity of the app. + */ +public class MainActivity extends ActionBarActivity { + + private static final String LOG = "MainActivity"; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.activity_main); + + if (savedInstanceState == null) { + getSupportFragmentManager().beginTransaction() + .add(R.id.container, new PlaceholderFragment()) + .commit(); + } + + } + + /* + * (non-Javadoc) + * @see android.app.Activity#onCreateOptionsMenu(android.view.Menu) + */ + @Override + public boolean onCreateOptionsMenu(Menu menu) { + + // Inflate the menu; this adds items to the action bar if it is present. + //getMenuInflater().inflate(R.menu.main, menu); + return true; + } + + /* + * (non-Javadoc) + * @see android.app.Activity#onOptionsItemSelected(android.view.MenuItem) + */ + @Override + public boolean onOptionsItemSelected(MenuItem item) { + // Handle action bar item clicks here. The action bar will + // automatically handle clicks on the Home/Up button, so long + // as you specify a parent activity in AndroidManifest.xml. + int id = item.getItemId(); + // if (id == R.id.action_settings) { + // return true; + // } + return super.onOptionsItemSelected(item); + } + + /** + * A placeholder fragment containing a simple view. + */ + public static class PlaceholderFragment extends Fragment { + + public PlaceholderFragment() { + } + + /* + * (non-Javadoc) + * @see + * android.support.v4.app.Fragment#onCreateView(android.view.LayoutInflater + * , android.view.ViewGroup, android.os.Bundle) + */ + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + View rootView = inflater.inflate(R.layout.fragment_main, container, false); + return rootView; + } + } + +} diff --git a/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/Utils.java b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/Utils.java new file mode 100644 index 0000000..3589761 --- /dev/null +++ b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/Utils.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2014 Google Inc. All Rights Reserved. + * + * 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.example.android.musicservicedemo; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.util.Log; + +import java.io.IOException; +import java.io.InputStream; +import java.net.HttpURLConnection; +import java.net.URL; + +public class Utils { + + private static final String TAG = "Utils"; + + /** + * Utility method to check that parameters are not null + * + * @param object + */ + public static final void checkNotNull(Object object) { + if (object == null) { + throw new NullPointerException(); + } + } + + /** + * Utility to download a bitmap + * + * @param source + * @return + */ + public static Bitmap getBitmapFromURL(String source) { + try { + URL url = new URL(source); + HttpURLConnection httpConnection = (HttpURLConnection) url.openConnection(); + httpConnection.setDoInput(true); + httpConnection.connect(); + InputStream inputStream = httpConnection.getInputStream(); + return BitmapFactory.decodeStream(inputStream); + } catch (IOException e) { + Log.e(TAG, "getBitmapFromUrl: " + source, e); + } + return null; + } + + /** + * Utility method to wrap an index + * + * @param i + * @param size + * @return + */ + public static int wrapIndex(int i, int size) { + int m = i % size; + if (m < 0) { // java modulus can be negative + m += size; + } + return m; + } +} diff --git a/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/browser/MusicProvider.java b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/browser/MusicProvider.java new file mode 100644 index 0000000..15038d7 --- /dev/null +++ b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/browser/MusicProvider.java @@ -0,0 +1,266 @@ +/* + * Copyright (C) 2014 Google Inc. All Rights Reserved. + * + * 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.example.android.musicservicedemo.browser; + +import android.database.MatrixCursor; +import android.media.session.PlaybackState; +import android.net.Uri; +import android.util.Log; + +import com.example.android.musicservicedemo.BrowserService; + +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.io.BufferedInputStream; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.URLConnection; +import java.util.ArrayList; +import java.util.List; + +/** + * Utility class to get a list of MusicTrack's based on a server-side JSON + * configuration. + */ +public class MusicProvider { + + private static final String TAG = "MusicProvider"; + + private static final String MUSIC_URL = "http://storage.googleapis.com/automotive-media/music.json"; + + private static String MUSIC = "music"; + private static String TITLE = "title"; + private static String ALBUM = "album"; + private static String ARTIST = "artist"; + private static String GENRE = "genre"; + private static String SOURCE = "source"; + private static String IMAGE = "image"; + private static String TRACK_NUMBER = "trackNumber"; + private static String TOTAL_TRACK_COUNT = "totalTrackCount"; + private static String DURATION = "duration"; + + // Cache for music track data + private static List<MusicTrack> mMusicList; + + /** + * Get the cached list of music tracks + * + * @return + * @throws JSONException + */ + public List<MusicTrack> getMedia() throws JSONException { + if (null != mMusicList && mMusicList.size() > 0) { + return mMusicList; + } + return null; + } + + /** + * Get the list of music tracks from a server and return the list of + * MusicTrack objects. + * + * @return + * @throws JSONException + */ + public List<MusicTrack> retreiveMedia() throws JSONException { + if (null != mMusicList) { + return mMusicList; + } + int slashPos = MUSIC_URL.lastIndexOf('/'); + String path = MUSIC_URL.substring(0, slashPos + 1); + JSONObject jsonObj = parseUrl(MUSIC_URL); + + try { + JSONArray videos = jsonObj.getJSONArray(MUSIC); + if (null != videos) { + mMusicList = new ArrayList<MusicTrack>(); + for (int j = 0; j < videos.length(); j++) { + JSONObject music = videos.getJSONObject(j); + String title = music.getString(TITLE); + String album = music.getString(ALBUM); + String artist = music.getString(ARTIST); + String genre = music.getString(GENRE); + String source = music.getString(SOURCE); + // Media is stored relative to JSON file + if (!source.startsWith("http")) { + source = path + source; + } + String image = music.getString(IMAGE); + if (!image.startsWith("http")) { + image = path + image; + } + int trackNumber = music.getInt(TRACK_NUMBER); + int totalTrackCount = music.getInt(TOTAL_TRACK_COUNT); + int duration = music.getInt(DURATION) * 1000; // ms + + mMusicList.add(new MusicTrack(title, album, artist, genre, source, + image, trackNumber, totalTrackCount, duration)); + } + } + } catch (NullPointerException e) { + Log.e(TAG, "retreiveMedia", e); + } + return mMusicList; + } + + /** + * Download a JSON file from a server, parse the content and return the JSON + * object. + * + * @param urlString + * @return + */ + private JSONObject parseUrl(String urlString) { + InputStream is = null; + try { + java.net.URL url = new java.net.URL(urlString); + URLConnection urlConnection = url.openConnection(); + is = new BufferedInputStream(urlConnection.getInputStream()); + BufferedReader reader = new BufferedReader(new InputStreamReader( + urlConnection.getInputStream(), "iso-8859-1"), 8); + StringBuilder sb = new StringBuilder(); + String line = null; + while ((line = reader.readLine()) != null) { + sb.append(line); + } + return new JSONObject(sb.toString()); + } catch (Exception e) { + Log.d(TAG, "Failed to parse the json for media list", e); + return null; + } finally { + if (null != is) { + try { + is.close(); + } catch (IOException e) { + // ignore + } + } + } + } + + public MatrixCursor getRootContainerCurser() { + MatrixCursor matrixCursor = new MatrixCursor(BrowserService.MEDIA_CONTAINER_PROJECTION); + Uri.Builder pianoBuilder = new Uri.Builder(); + pianoBuilder.authority(BrowserService.AUTHORITY); + pianoBuilder.appendPath(BrowserService.PIANO_BASE_PATH); + matrixCursor.addRow(new Object[] { + pianoBuilder.build(), + BrowserService.PIANO_BASE_PATH, + "subtitle", + null, + 0 + }); + + Uri.Builder voiceBuilder = new Uri.Builder(); + voiceBuilder.authority(BrowserService.AUTHORITY); + voiceBuilder.appendPath(BrowserService.VOICE_BASE_PATH); + matrixCursor.addRow(new Object[] { + voiceBuilder.build(), + BrowserService.VOICE_BASE_PATH, + "subtitle", + null, + 0 + }); + return matrixCursor; + } + + public MatrixCursor getRootItemCursor(int type) { + if (type == BrowserService.NOW_PLAYING) { + MatrixCursor matrixCursor = new MatrixCursor(BrowserService.MEDIA_CONTAINER_PROJECTION); + + try { + // Just return all of the tracks for now + List<MusicTrack> musicTracks = retreiveMedia(); + for (MusicTrack musicTrack : musicTracks) { + Uri.Builder builder = new Uri.Builder(); + builder.authority(BrowserService.AUTHORITY); + builder.appendPath(BrowserService.NOW_PLAYING_PATH); + builder.appendPath(musicTrack.getTitle()); + matrixCursor.addRow(new Object[] { + builder.build(), + musicTrack.getTitle(), + musicTrack.getArtist(), + musicTrack.getImage(), + PlaybackState.ACTION_PLAY + }); + Log.d(TAG, "Uri " + builder.build()); + } + } catch (JSONException e) { + Log.e(TAG, "::getRootItemCursor:", e); + } + + Log.d(TAG, "cursor: " + matrixCursor.getCount()); + return matrixCursor; + } else if (type == BrowserService.PIANO) { + MatrixCursor matrixCursor = new MatrixCursor(BrowserService.MEDIA_CONTAINER_PROJECTION); + + try { + List<MusicTrack> musicTracks = retreiveMedia(); + for (MusicTrack musicTrack : musicTracks) { + Uri.Builder builder = new Uri.Builder(); + builder.authority(BrowserService.AUTHORITY); + builder.appendPath(BrowserService.PIANO_BASE_PATH); + builder.appendPath(musicTrack.getTitle()); + matrixCursor.addRow(new Object[] { + builder.build(), + musicTrack.getTitle(), + musicTrack.getArtist(), + musicTrack.getImage(), + PlaybackState.ACTION_PLAY + }); + Log.d(TAG, "Uri " + builder.build()); + } + } catch (JSONException e) { + Log.e(TAG, "::getRootItemCursor:", e); + } + + Log.d(TAG, "cursor: " + matrixCursor.getCount()); + return matrixCursor; + } else if (type == BrowserService.VOICE) { + MatrixCursor matrixCursor = new MatrixCursor(BrowserService.MEDIA_CONTAINER_PROJECTION); + + try { + List<MusicTrack> musicTracks = retreiveMedia(); + for (MusicTrack musicTrack : musicTracks) { + Uri.Builder builder = new Uri.Builder(); + builder.authority(BrowserService.AUTHORITY); + builder.appendPath(BrowserService.VOICE_BASE_PATH); + builder.appendPath(musicTrack.getTitle()); + matrixCursor.addRow(new Object[] { + builder.build(), + musicTrack.getTitle(), + musicTrack.getArtist(), + musicTrack.getImage(), + PlaybackState.ACTION_PLAY + }); + Log.d(TAG, "Uri " + builder.build()); + } + } catch (JSONException e) { + Log.e(TAG, "::getRootItemCursor:", e); + } + + Log.d(TAG, "cursor: " + matrixCursor.getCount()); + return matrixCursor; + + } + return null; + } +} diff --git a/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/browser/MusicProviderTask.java b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/browser/MusicProviderTask.java new file mode 100644 index 0000000..ffda110 --- /dev/null +++ b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/browser/MusicProviderTask.java @@ -0,0 +1,70 @@ +/* + * Copyright (C) 2014 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.example.android.musicservicedemo.browser; + +import android.os.AsyncTask; +import android.util.Log; + +import org.json.JSONException; + +/** + * Asynchronous task to retrieve the music data using MusicProvider. + */ +public class MusicProviderTask extends AsyncTask<Void, Void, Void> { + + private static final String TAG = "MusicProviderTask"; + + MusicProvider mMusicProvider; + MusicProviderTaskListener mMusicProviderTaskListener; + + /** + * Initialize the task with the provider to download the music data and the + * listener to be informed when the task is done. + * + * @param musicProvider + * @param listener + */ + public MusicProviderTask(MusicProvider musicProvider, + MusicProviderTaskListener listener) { + mMusicProvider = musicProvider; + mMusicProviderTaskListener = listener; + } + + /* + * (non-Javadoc) + * @see android.os.AsyncTask#doInBackground(java.lang.Object[]) + */ + @Override + protected Void doInBackground(Void... arg0) { + try { + mMusicProvider.retreiveMedia(); + } catch (JSONException e) { + Log.e(TAG, "::doInBackground:", e); + } + return null; + } + + /* + * (non-Javadoc) + * @see android.os.AsyncTask#onPostExecute(java.lang.Object) + */ + @Override + protected void onPostExecute(Void result) { + mMusicProviderTaskListener.onMusicProviderTaskCompleted(); + } + +} diff --git a/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/browser/MusicProviderTaskListener.java b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/browser/MusicProviderTaskListener.java new file mode 100644 index 0000000..b1d168f --- /dev/null +++ b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/browser/MusicProviderTaskListener.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2014 Google Inc. All Rights Reserved. + * + * 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.example.android.musicservicedemo.browser; + +/** + * Callback listener for completion of MusicProviderTask + */ +public interface MusicProviderTaskListener { + public void onMusicProviderTaskCompleted(); +} diff --git a/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/browser/MusicTrack.java b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/browser/MusicTrack.java new file mode 100644 index 0000000..02ea899 --- /dev/null +++ b/tests/MusicServiceDemo/src/com/example/android/musicservicedemo/browser/MusicTrack.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2014 Google Inc. All Rights Reserved. + * + * 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.example.android.musicservicedemo.browser; + +/** + * A class to model music track metadata. + */ +public class MusicTrack { + + private static final String TAG = "MusicTrack"; + + private String mTitle; + private String mAlbum; + private String mArtist; + private String mGenre; + private String mSource; + private String mImage; + private int mTrackNumber; + private int mTotalTrackCount; + private int mDuration; + + /** + * Constructor creating a MusicTrack instance. + * + * @param title + * @param album + * @param artist + * @param genre + * @param source + * @param image + * @param trackNumber + * @param totalTrackCount + * @param duration + */ + public MusicTrack(String title, String album, String artist, String genre, String source, + String image, int trackNumber, int totalTrackCount, int duration) { + this.mTitle = title; + this.mAlbum = album; + this.mArtist = artist; + this.mGenre = genre; + this.mSource = source; + this.mImage = image; + this.mTrackNumber = trackNumber; + this.mTotalTrackCount = totalTrackCount; + this.mDuration = duration; + } + + public String getTitle() { + return mTitle; + } + + public void setTitle(String mTitle) { + this.mTitle = mTitle; + } + + public String getAlbum() { + return mAlbum; + } + + public void setAlbum(String mAlbum) { + this.mAlbum = mAlbum; + } + + public String getArtist() { + return mArtist; + } + + public void setArtist(String mArtist) { + this.mArtist = mArtist; + } + + public String getGenre() { + return mGenre; + } + + public void setGenre(String mGenre) { + this.mGenre = mGenre; + } + + public String getSource() { + return mSource; + } + + public void setSource(String mSource) { + this.mSource = mSource; + } + + public String getImage() { + return mImage; + } + + public void setImage(String mImage) { + this.mImage = mImage; + } + + public int getTrackNumber() { + return mTrackNumber; + } + + public void setTrackNumber(int mTrackNumber) { + this.mTrackNumber = mTrackNumber; + } + + public int getTotalTrackCount() { + return mTotalTrackCount; + } + + public void setTotalTrackCount(int mTotalTrackCount) { + this.mTotalTrackCount = mTotalTrackCount; + } + + public int getDuration() { + return mDuration; + } + + public void setDuration(int mDuration) { + this.mDuration = mDuration; + } + + public String toString() { + return mTitle; + } + +} |