diff options
61 files changed, 1075 insertions, 523 deletions
diff --git a/samples/RandomMusicPlayer/Android.mk b/samples/RandomMusicPlayer/Android.mk new file mode 100644 index 000000000..91637b17b --- /dev/null +++ b/samples/RandomMusicPlayer/Android.mk @@ -0,0 +1,16 @@ +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_MODULE_TAGS := samples + +# Only compile source java files in this apk. +LOCAL_SRC_FILES := $(call all-java-files-under, src) + +LOCAL_PACKAGE_NAME := RandomMusicPlayer + +LOCAL_SDK_VERSION := current + +include $(BUILD_PACKAGE) + +# Use the following include to make our test apk. +include $(call all-makefiles-under,$(LOCAL_PATH)) diff --git a/samples/RandomMusicPlayer/AndroidManifest.xml b/samples/RandomMusicPlayer/AndroidManifest.xml index 5a0fc2859..6e52b6129 100644 --- a/samples/RandomMusicPlayer/AndroidManifest.xml +++ b/samples/RandomMusicPlayer/AndroidManifest.xml @@ -1,30 +1,34 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2011 The Android Open Source Project +<!-- + Copyright (C) 2011 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 + 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 + 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. --> + 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.musicplayer" - android:versionCode="1" - android:versionName="1.0"> + package="com.example.android.musicplayer" + android:versionCode="1" + android:versionName="1.0"> - <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="8" /> - <uses-permission android:name="android.permission.WAKE_LOCK"/> + <uses-sdk android:minSdkVersion="7" android:targetSdkVersion="14" /> + + <uses-permission android:name="android.permission.INTERNET" /> + <uses-permission android:name="android.permission.WAKE_LOCK" /> + + <application android:icon="@drawable/ic_launcher" android:label="@string/app_title"> - <application android:icon="@drawable/ic_launcher" android:label="Random Music Player"> <activity android:name=".MainActivity" - android:label="Random Music Player" - android:theme="@android:style/Theme.Black.NoTitleBar"> + android:label="@string/app_title" + android:theme="@android:style/Theme.Black.NoTitleBar"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> @@ -33,6 +37,7 @@ <service android:exported="false" android:name=".MusicService"> <intent-filter> + <action android:name="com.example.android.musicplayer.action.TOGGLE_PLAYBACK" /> <action android:name="com.example.android.musicplayer.action.PLAY" /> <action android:name="com.example.android.musicplayer.action.PAUSE" /> <action android:name="com.example.android.musicplayer.action.SKIP" /> @@ -49,6 +54,10 @@ <intent-filter> <action android:name="android.media.AUDIO_BECOMING_NOISY" /> </intent-filter> + <intent-filter> + <action android:name="android.intent.action.MEDIA_BUTTON" /> + </intent-filter> </receiver> + </application> </manifest> diff --git a/samples/RandomMusicPlayer/default.properties b/samples/RandomMusicPlayer/default.properties deleted file mode 100644 index e2e8061f2..000000000 --- a/samples/RandomMusicPlayer/default.properties +++ /dev/null @@ -1,11 +0,0 @@ -# This file is automatically generated by Android Tools. -# Do not modify this file -- YOUR CHANGES WILL BE ERASED! -# -# This file must be checked in Version Control Systems. -# -# To customize properties used by the Ant build system use, -# "build.properties", and override values to adapt the script to your -# project structure. - -# Project target. -target=android-8 diff --git a/samples/RandomMusicPlayer/res/drawable-hdpi-v11/ic_stat_playing.png b/samples/RandomMusicPlayer/res/drawable-hdpi-v11/ic_stat_playing.png Binary files differnew file mode 100755 index 000000000..cc62c29f1 --- /dev/null +++ b/samples/RandomMusicPlayer/res/drawable-hdpi-v11/ic_stat_playing.png diff --git a/samples/RandomMusicPlayer/res/drawable-hdpi-v9/ic_stat_playing.png b/samples/RandomMusicPlayer/res/drawable-hdpi-v9/ic_stat_playing.png Binary files differindex d111aab81..9351f4b3f 100644..100755 --- a/samples/RandomMusicPlayer/res/drawable-hdpi-v9/ic_stat_playing.png +++ b/samples/RandomMusicPlayer/res/drawable-hdpi-v9/ic_stat_playing.png diff --git a/samples/RandomMusicPlayer/res/drawable-hdpi/ic_launcher.png b/samples/RandomMusicPlayer/res/drawable-hdpi/ic_launcher.png Binary files differindex abd905500..972c79184 100644..100755 --- a/samples/RandomMusicPlayer/res/drawable-hdpi/ic_launcher.png +++ b/samples/RandomMusicPlayer/res/drawable-hdpi/ic_launcher.png diff --git a/samples/RandomMusicPlayer/res/drawable-hdpi/ic_stat_playing.png b/samples/RandomMusicPlayer/res/drawable-hdpi/ic_stat_playing.png Binary files differindex c1dd9da6a..099c44eba 100644..100755 --- a/samples/RandomMusicPlayer/res/drawable-hdpi/ic_stat_playing.png +++ b/samples/RandomMusicPlayer/res/drawable-hdpi/ic_stat_playing.png diff --git a/samples/RandomMusicPlayer/res/drawable-hdpi/selector_eject.xml b/samples/RandomMusicPlayer/res/drawable-hdpi/selector_eject.xml deleted file mode 100644 index 300e75aa0..000000000 --- a/samples/RandomMusicPlayer/res/drawable-hdpi/selector_eject.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2011 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. --> - - <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_pressed="true" - android:drawable="@drawable/eject_pressed" /> <!-- pressed --> - <item android:state_focused="true" - android:drawable="@drawable/eject_pressed" /> <!-- focused --> - <item android:drawable="@drawable/eject" /> <!-- default --> - </selector> diff --git a/samples/RandomMusicPlayer/res/drawable-hdpi/selector_ff.xml b/samples/RandomMusicPlayer/res/drawable-hdpi/selector_ff.xml deleted file mode 100644 index 2d399b4d6..000000000 --- a/samples/RandomMusicPlayer/res/drawable-hdpi/selector_ff.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2011 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. --> - - <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_pressed="true" - android:drawable="@drawable/ff_pressed" /> <!-- pressed --> - <item android:state_focused="true" - android:drawable="@drawable/ff_pressed" /> <!-- focused --> - <item android:drawable="@drawable/ff" /> <!-- default --> - </selector> diff --git a/samples/RandomMusicPlayer/res/drawable-hdpi/selector_pause.xml b/samples/RandomMusicPlayer/res/drawable-hdpi/selector_pause.xml deleted file mode 100644 index 2d6c4bee2..000000000 --- a/samples/RandomMusicPlayer/res/drawable-hdpi/selector_pause.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2011 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. --> - - <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_pressed="true" - android:drawable="@drawable/pause_pressed" /> <!-- pressed --> - <item android:state_focused="true" - android:drawable="@drawable/pause_pressed" /> <!-- focused --> - <item android:drawable="@drawable/pause" /> <!-- default --> - </selector> diff --git a/samples/RandomMusicPlayer/res/drawable-hdpi/selector_play.xml b/samples/RandomMusicPlayer/res/drawable-hdpi/selector_play.xml deleted file mode 100644 index d2eea023f..000000000 --- a/samples/RandomMusicPlayer/res/drawable-hdpi/selector_play.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2011 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. --> - - <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_pressed="true" - android:drawable="@drawable/play_pressed" /> <!-- pressed --> - <item android:state_focused="true" - android:drawable="@drawable/play_pressed" /> <!-- focused --> - <item android:drawable="@drawable/play" /> <!-- default --> - </selector> diff --git a/samples/RandomMusicPlayer/res/drawable-hdpi/selector_rew.xml b/samples/RandomMusicPlayer/res/drawable-hdpi/selector_rew.xml deleted file mode 100644 index 5f5f88a92..000000000 --- a/samples/RandomMusicPlayer/res/drawable-hdpi/selector_rew.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2011 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. --> - - <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_pressed="true" - android:drawable="@drawable/rew_pressed" /> <!-- pressed --> - <item android:state_focused="true" - android:drawable="@drawable/rew_pressed" /> <!-- focused --> - <item android:drawable="@drawable/rew" /> <!-- default --> - </selector> diff --git a/samples/RandomMusicPlayer/res/drawable-hdpi/selector_stop.xml b/samples/RandomMusicPlayer/res/drawable-hdpi/selector_stop.xml deleted file mode 100644 index 57784173c..000000000 --- a/samples/RandomMusicPlayer/res/drawable-hdpi/selector_stop.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2011 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. --> - - <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_pressed="true" - android:drawable="@drawable/stop_pressed" /> <!-- pressed --> - <item android:state_focused="true" - android:drawable="@drawable/stop_pressed" /> <!-- focused --> - <item android:drawable="@drawable/stop" /> <!-- default --> - </selector> diff --git a/samples/RandomMusicPlayer/res/drawable-ldpi-v9/ic_stat_playing.png b/samples/RandomMusicPlayer/res/drawable-ldpi-v9/ic_stat_playing.png Binary files differdeleted file mode 100644 index 6a4082313..000000000 --- a/samples/RandomMusicPlayer/res/drawable-ldpi-v9/ic_stat_playing.png +++ /dev/null diff --git a/samples/RandomMusicPlayer/res/drawable-ldpi/ic_launcher.png b/samples/RandomMusicPlayer/res/drawable-ldpi/ic_launcher.png Binary files differdeleted file mode 100644 index 6f1277a87..000000000 --- a/samples/RandomMusicPlayer/res/drawable-ldpi/ic_launcher.png +++ /dev/null diff --git a/samples/RandomMusicPlayer/res/drawable-ldpi/ic_stat_playing.png b/samples/RandomMusicPlayer/res/drawable-ldpi/ic_stat_playing.png Binary files differdeleted file mode 100644 index fb21884d3..000000000 --- a/samples/RandomMusicPlayer/res/drawable-ldpi/ic_stat_playing.png +++ /dev/null diff --git a/samples/RandomMusicPlayer/res/drawable-mdpi-v11/ic_stat_playing.png b/samples/RandomMusicPlayer/res/drawable-mdpi-v11/ic_stat_playing.png Binary files differnew file mode 100755 index 000000000..049ff3d7e --- /dev/null +++ b/samples/RandomMusicPlayer/res/drawable-mdpi-v11/ic_stat_playing.png diff --git a/samples/RandomMusicPlayer/res/drawable-mdpi-v9/ic_stat_playing.png b/samples/RandomMusicPlayer/res/drawable-mdpi-v9/ic_stat_playing.png Binary files differindex b5a66dfc7..7970cb976 100644..100755 --- a/samples/RandomMusicPlayer/res/drawable-mdpi-v9/ic_stat_playing.png +++ b/samples/RandomMusicPlayer/res/drawable-mdpi-v9/ic_stat_playing.png diff --git a/samples/RandomMusicPlayer/res/drawable-mdpi/eject.png b/samples/RandomMusicPlayer/res/drawable-mdpi/eject.png Binary files differdeleted file mode 100644 index 650b38aeb..000000000 --- a/samples/RandomMusicPlayer/res/drawable-mdpi/eject.png +++ /dev/null diff --git a/samples/RandomMusicPlayer/res/drawable-mdpi/eject_pressed.png b/samples/RandomMusicPlayer/res/drawable-mdpi/eject_pressed.png Binary files differdeleted file mode 100644 index 065b30d9e..000000000 --- a/samples/RandomMusicPlayer/res/drawable-mdpi/eject_pressed.png +++ /dev/null diff --git a/samples/RandomMusicPlayer/res/drawable-mdpi/ff.png b/samples/RandomMusicPlayer/res/drawable-mdpi/ff.png Binary files differdeleted file mode 100644 index 508f741fc..000000000 --- a/samples/RandomMusicPlayer/res/drawable-mdpi/ff.png +++ /dev/null diff --git a/samples/RandomMusicPlayer/res/drawable-mdpi/ff_pressed.png b/samples/RandomMusicPlayer/res/drawable-mdpi/ff_pressed.png Binary files differdeleted file mode 100644 index 468ae8e69..000000000 --- a/samples/RandomMusicPlayer/res/drawable-mdpi/ff_pressed.png +++ /dev/null diff --git a/samples/RandomMusicPlayer/res/drawable-mdpi/ic_launcher.png b/samples/RandomMusicPlayer/res/drawable-mdpi/ic_launcher.png Binary files differindex abd905500..01b53fd50 100644..100755 --- a/samples/RandomMusicPlayer/res/drawable-mdpi/ic_launcher.png +++ b/samples/RandomMusicPlayer/res/drawable-mdpi/ic_launcher.png diff --git a/samples/RandomMusicPlayer/res/drawable-mdpi/ic_stat_playing.png b/samples/RandomMusicPlayer/res/drawable-mdpi/ic_stat_playing.png Binary files differindex c1dd9da6a..5ffc8cc35 100644..100755 --- a/samples/RandomMusicPlayer/res/drawable-mdpi/ic_stat_playing.png +++ b/samples/RandomMusicPlayer/res/drawable-mdpi/ic_stat_playing.png diff --git a/samples/RandomMusicPlayer/res/drawable-mdpi/pause.png b/samples/RandomMusicPlayer/res/drawable-mdpi/pause.png Binary files differdeleted file mode 100644 index 13581de34..000000000 --- a/samples/RandomMusicPlayer/res/drawable-mdpi/pause.png +++ /dev/null diff --git a/samples/RandomMusicPlayer/res/drawable-mdpi/pause_pressed.png b/samples/RandomMusicPlayer/res/drawable-mdpi/pause_pressed.png Binary files differdeleted file mode 100644 index 9ddd07dab..000000000 --- a/samples/RandomMusicPlayer/res/drawable-mdpi/pause_pressed.png +++ /dev/null diff --git a/samples/RandomMusicPlayer/res/drawable-mdpi/play.png b/samples/RandomMusicPlayer/res/drawable-mdpi/play.png Binary files differdeleted file mode 100644 index e34b48e80..000000000 --- a/samples/RandomMusicPlayer/res/drawable-mdpi/play.png +++ /dev/null diff --git a/samples/RandomMusicPlayer/res/drawable-mdpi/play_pressed.png b/samples/RandomMusicPlayer/res/drawable-mdpi/play_pressed.png Binary files differdeleted file mode 100644 index 790cd294a..000000000 --- a/samples/RandomMusicPlayer/res/drawable-mdpi/play_pressed.png +++ /dev/null diff --git a/samples/RandomMusicPlayer/res/drawable-mdpi/rew.png b/samples/RandomMusicPlayer/res/drawable-mdpi/rew.png Binary files differdeleted file mode 100644 index 26864b7e3..000000000 --- a/samples/RandomMusicPlayer/res/drawable-mdpi/rew.png +++ /dev/null diff --git a/samples/RandomMusicPlayer/res/drawable-mdpi/rew_pressed.png b/samples/RandomMusicPlayer/res/drawable-mdpi/rew_pressed.png Binary files differdeleted file mode 100644 index 54c38a747..000000000 --- a/samples/RandomMusicPlayer/res/drawable-mdpi/rew_pressed.png +++ /dev/null diff --git a/samples/RandomMusicPlayer/res/drawable-mdpi/selector_eject.xml b/samples/RandomMusicPlayer/res/drawable-mdpi/selector_eject.xml deleted file mode 100644 index 300e75aa0..000000000 --- a/samples/RandomMusicPlayer/res/drawable-mdpi/selector_eject.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2011 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. --> - - <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_pressed="true" - android:drawable="@drawable/eject_pressed" /> <!-- pressed --> - <item android:state_focused="true" - android:drawable="@drawable/eject_pressed" /> <!-- focused --> - <item android:drawable="@drawable/eject" /> <!-- default --> - </selector> diff --git a/samples/RandomMusicPlayer/res/drawable-mdpi/selector_ff.xml b/samples/RandomMusicPlayer/res/drawable-mdpi/selector_ff.xml deleted file mode 100644 index 2d399b4d6..000000000 --- a/samples/RandomMusicPlayer/res/drawable-mdpi/selector_ff.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2011 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. --> - - <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_pressed="true" - android:drawable="@drawable/ff_pressed" /> <!-- pressed --> - <item android:state_focused="true" - android:drawable="@drawable/ff_pressed" /> <!-- focused --> - <item android:drawable="@drawable/ff" /> <!-- default --> - </selector> diff --git a/samples/RandomMusicPlayer/res/drawable-mdpi/selector_pause.xml b/samples/RandomMusicPlayer/res/drawable-mdpi/selector_pause.xml deleted file mode 100644 index 2d6c4bee2..000000000 --- a/samples/RandomMusicPlayer/res/drawable-mdpi/selector_pause.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2011 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. --> - - <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_pressed="true" - android:drawable="@drawable/pause_pressed" /> <!-- pressed --> - <item android:state_focused="true" - android:drawable="@drawable/pause_pressed" /> <!-- focused --> - <item android:drawable="@drawable/pause" /> <!-- default --> - </selector> diff --git a/samples/RandomMusicPlayer/res/drawable-mdpi/selector_play.xml b/samples/RandomMusicPlayer/res/drawable-mdpi/selector_play.xml deleted file mode 100644 index d2eea023f..000000000 --- a/samples/RandomMusicPlayer/res/drawable-mdpi/selector_play.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2011 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. --> - - <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_pressed="true" - android:drawable="@drawable/play_pressed" /> <!-- pressed --> - <item android:state_focused="true" - android:drawable="@drawable/play_pressed" /> <!-- focused --> - <item android:drawable="@drawable/play" /> <!-- default --> - </selector> diff --git a/samples/RandomMusicPlayer/res/drawable-mdpi/selector_rew.xml b/samples/RandomMusicPlayer/res/drawable-mdpi/selector_rew.xml deleted file mode 100644 index 5f5f88a92..000000000 --- a/samples/RandomMusicPlayer/res/drawable-mdpi/selector_rew.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2011 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. --> - - <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_pressed="true" - android:drawable="@drawable/rew_pressed" /> <!-- pressed --> - <item android:state_focused="true" - android:drawable="@drawable/rew_pressed" /> <!-- focused --> - <item android:drawable="@drawable/rew" /> <!-- default --> - </selector> diff --git a/samples/RandomMusicPlayer/res/drawable-mdpi/selector_stop.xml b/samples/RandomMusicPlayer/res/drawable-mdpi/selector_stop.xml deleted file mode 100644 index 57784173c..000000000 --- a/samples/RandomMusicPlayer/res/drawable-mdpi/selector_stop.xml +++ /dev/null @@ -1,22 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2011 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. --> - - <selector xmlns:android="http://schemas.android.com/apk/res/android"> - <item android:state_pressed="true" - android:drawable="@drawable/stop_pressed" /> <!-- pressed --> - <item android:state_focused="true" - android:drawable="@drawable/stop_pressed" /> <!-- focused --> - <item android:drawable="@drawable/stop" /> <!-- default --> - </selector> diff --git a/samples/RandomMusicPlayer/res/drawable-mdpi/stop.png b/samples/RandomMusicPlayer/res/drawable-mdpi/stop.png Binary files differdeleted file mode 100644 index 45eff238e..000000000 --- a/samples/RandomMusicPlayer/res/drawable-mdpi/stop.png +++ /dev/null diff --git a/samples/RandomMusicPlayer/res/drawable-mdpi/stop_pressed.png b/samples/RandomMusicPlayer/res/drawable-mdpi/stop_pressed.png Binary files differdeleted file mode 100644 index c7bda81a0..000000000 --- a/samples/RandomMusicPlayer/res/drawable-mdpi/stop_pressed.png +++ /dev/null diff --git a/samples/RandomMusicPlayer/res/drawable-nodpi/dummy_album_art.png b/samples/RandomMusicPlayer/res/drawable-nodpi/dummy_album_art.png Binary files differnew file mode 100644 index 000000000..e50b97ee8 --- /dev/null +++ b/samples/RandomMusicPlayer/res/drawable-nodpi/dummy_album_art.png diff --git a/samples/RandomMusicPlayer/res/drawable-xhdpi-v11/ic_stat_playing.png b/samples/RandomMusicPlayer/res/drawable-xhdpi-v11/ic_stat_playing.png Binary files differnew file mode 100755 index 000000000..f9404986b --- /dev/null +++ b/samples/RandomMusicPlayer/res/drawable-xhdpi-v11/ic_stat_playing.png diff --git a/samples/RandomMusicPlayer/res/drawable-xhdpi-v9/ic_stat_playing.png b/samples/RandomMusicPlayer/res/drawable-xhdpi-v9/ic_stat_playing.png Binary files differnew file mode 100755 index 000000000..d0f2a2b36 --- /dev/null +++ b/samples/RandomMusicPlayer/res/drawable-xhdpi-v9/ic_stat_playing.png diff --git a/samples/RandomMusicPlayer/res/drawable-xhdpi/ic_launcher.png b/samples/RandomMusicPlayer/res/drawable-xhdpi/ic_launcher.png Binary files differnew file mode 100755 index 000000000..af762f2be --- /dev/null +++ b/samples/RandomMusicPlayer/res/drawable-xhdpi/ic_launcher.png diff --git a/samples/RandomMusicPlayer/res/drawable-xhdpi/ic_stat_playing.png b/samples/RandomMusicPlayer/res/drawable-xhdpi/ic_stat_playing.png Binary files differnew file mode 100755 index 000000000..aa46a0cb7 --- /dev/null +++ b/samples/RandomMusicPlayer/res/drawable-xhdpi/ic_stat_playing.png diff --git a/samples/RandomMusicPlayer/res/drawable/btn_eject.xml b/samples/RandomMusicPlayer/res/drawable/btn_eject.xml new file mode 100644 index 000000000..e3e40ae87 --- /dev/null +++ b/samples/RandomMusicPlayer/res/drawable/btn_eject.xml @@ -0,0 +1,21 @@ +<!-- + Copyright (C) 2011 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. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_pressed="true" android:drawable="@drawable/eject_pressed" /> + <item android:state_focused="true" android:drawable="@drawable/eject_pressed" /> + <item android:drawable="@drawable/eject" /> +</selector> diff --git a/samples/RandomMusicPlayer/res/drawable/btn_ff.xml b/samples/RandomMusicPlayer/res/drawable/btn_ff.xml new file mode 100644 index 000000000..c87fa065d --- /dev/null +++ b/samples/RandomMusicPlayer/res/drawable/btn_ff.xml @@ -0,0 +1,21 @@ +<!-- + Copyright (C) 2011 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. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_pressed="true" android:drawable="@drawable/ff_pressed" /> + <item android:state_focused="true" android:drawable="@drawable/ff_pressed" /> + <item android:drawable="@drawable/ff" /> +</selector> diff --git a/samples/RandomMusicPlayer/res/drawable/btn_pause.xml b/samples/RandomMusicPlayer/res/drawable/btn_pause.xml new file mode 100644 index 000000000..7aadd2fa9 --- /dev/null +++ b/samples/RandomMusicPlayer/res/drawable/btn_pause.xml @@ -0,0 +1,21 @@ +<!-- + Copyright (C) 2011 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. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_pressed="true" android:drawable="@drawable/pause_pressed" /> + <item android:state_focused="true" android:drawable="@drawable/pause_pressed" /> + <item android:drawable="@drawable/pause" /> +</selector> diff --git a/samples/RandomMusicPlayer/res/drawable/btn_play.xml b/samples/RandomMusicPlayer/res/drawable/btn_play.xml new file mode 100644 index 000000000..dc319c56e --- /dev/null +++ b/samples/RandomMusicPlayer/res/drawable/btn_play.xml @@ -0,0 +1,21 @@ +<!-- + Copyright (C) 2011 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. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_pressed="true" android:drawable="@drawable/play_pressed" /> + <item android:state_focused="true" android:drawable="@drawable/play_pressed" /> + <item android:drawable="@drawable/play" /> +</selector> diff --git a/samples/RandomMusicPlayer/res/drawable/btn_rew.xml b/samples/RandomMusicPlayer/res/drawable/btn_rew.xml new file mode 100644 index 000000000..cc4dad80c --- /dev/null +++ b/samples/RandomMusicPlayer/res/drawable/btn_rew.xml @@ -0,0 +1,21 @@ +<!-- + Copyright (C) 2011 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. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_pressed="true" android:drawable="@drawable/rew_pressed" /> + <item android:state_focused="true" android:drawable="@drawable/rew_pressed" /> + <item android:drawable="@drawable/rew" /> +</selector> diff --git a/samples/RandomMusicPlayer/res/drawable/btn_stop.xml b/samples/RandomMusicPlayer/res/drawable/btn_stop.xml new file mode 100644 index 000000000..5ce2d17e0 --- /dev/null +++ b/samples/RandomMusicPlayer/res/drawable/btn_stop.xml @@ -0,0 +1,21 @@ +<!-- + Copyright (C) 2011 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. + --> + +<selector xmlns:android="http://schemas.android.com/apk/res/android"> + <item android:state_pressed="true" android:drawable="@drawable/stop_pressed" /> + <item android:state_focused="true" android:drawable="@drawable/stop_pressed" /> + <item android:drawable="@drawable/stop" /> +</selector> diff --git a/samples/RandomMusicPlayer/res/layout-land/main.xml b/samples/RandomMusicPlayer/res/layout-land/main.xml index c9072bf84..b8070bbcc 100644 --- a/samples/RandomMusicPlayer/res/layout-land/main.xml +++ b/samples/RandomMusicPlayer/res/layout-land/main.xml @@ -1,83 +1,68 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2011 The Android Open Source Project +<!-- + Copyright (C) 2011 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 + 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 + 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. --> + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + --> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:gravity="center" - android:background="#000040" - > + android:background="#000040"> -<TextView android:text="Random Music Player" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:padding="20dp" - android:textColor="#ffffff" - android:textSize="20sp" - android:textStyle="bold" - /> + <TextView android:text="@string/app_title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:padding="20dp" + android:textColor="#ffffff" + android:textSize="20sp" + android:textStyle="bold" /> -<LinearLayout - android:orientation="horizontal" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:gravity="center" - > -<Button - android:id="@+id/rewindbutton" - android:background="@drawable/selector_rew" - android:layout_width="64dp" - android:layout_height="64dp" - android:layout_margin="5dp" - /> -<Button - android:id="@+id/playbutton" - android:background="@drawable/selector_play" - android:layout_width="64dp" - android:layout_height="64dp" - android:layout_margin="5dp" - /> -<Button - android:id="@+id/pausebutton" - android:background="@drawable/selector_pause" - android:layout_width="64dp" - android:layout_height="64dp" - android:layout_margin="5dp" - /> -<Button - android:id="@+id/skipbutton" - android:background="@drawable/selector_ff" - android:layout_width="64dp" - android:layout_height="64dp" - android:layout_margin="5dp" - /> -<Button - android:id="@+id/stopbutton" - android:background="@drawable/selector_stop" - android:layout_width="64dp" - android:layout_height="64dp" - android:layout_margin="5dp" - /> -<Button - android:id="@+id/ejectbutton" - android:background="@drawable/selector_eject" - android:layout_width="64dp" - android:layout_height="64dp" - android:layout_margin="5dp" - /> + <LinearLayout android:orientation="horizontal" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center"> + <Button android:id="@+id/rewindbutton" + android:background="@drawable/btn_rew" + android:layout_width="64dp" + android:layout_height="64dp" + android:layout_margin="5dp" /> + <Button android:id="@+id/playbutton" + android:background="@drawable/btn_play" + android:layout_width="64dp" + android:layout_height="64dp" + android:layout_margin="5dp" /> + <Button android:id="@+id/pausebutton" + android:background="@drawable/btn_pause" + android:layout_width="64dp" + android:layout_height="64dp" + android:layout_margin="5dp" /> + <Button android:id="@+id/skipbutton" + android:background="@drawable/btn_ff" + android:layout_width="64dp" + android:layout_height="64dp" + android:layout_margin="5dp" /> + <Button android:id="@+id/stopbutton" + android:background="@drawable/btn_stop" + android:layout_width="64dp" + android:layout_height="64dp" + android:layout_margin="5dp" /> + <Button android:id="@+id/ejectbutton" + android:background="@drawable/btn_eject" + android:layout_width="64dp" + android:layout_height="64dp" + android:layout_margin="5dp" /> -</LinearLayout> + </LinearLayout> </LinearLayout> diff --git a/samples/RandomMusicPlayer/res/layout-port/main.xml b/samples/RandomMusicPlayer/res/layout-port/main.xml deleted file mode 100644 index ab86ae51b..000000000 --- a/samples/RandomMusicPlayer/res/layout-port/main.xml +++ /dev/null @@ -1,93 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2011 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. --> - -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="vertical" - android:layout_width="fill_parent" - android:layout_height="fill_parent" - android:gravity="center" - android:background="#000040" - > - -<TextView android:text="Random Music Player" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:padding="20dp" - android:textColor="#ffffff" - android:textSize="20sp" - android:textStyle="bold" - /> - -<LinearLayout - android:orientation="horizontal" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:gravity="center" - android:layout_margin="10dp" - > -<Button - android:id="@+id/rewindbutton" - android:background="@drawable/selector_rew" - android:layout_width="64dp" - android:layout_height="64dp" - android:layout_margin="5dp" - /> -<Button - android:id="@+id/playbutton" - android:background="@drawable/selector_play" - android:layout_width="64dp" - android:layout_height="64dp" - android:layout_margin="5dp" - /> -<Button - android:id="@+id/pausebutton" - android:background="@drawable/selector_pause" - android:layout_width="64dp" - android:layout_height="64dp" - android:layout_margin="5dp" - /> -<Button - android:id="@+id/skipbutton" - android:background="@drawable/selector_ff" - android:layout_width="64dp" - android:layout_height="64dp" - android:layout_margin="5dp" - /> -</LinearLayout> - -<LinearLayout - android:orientation="horizontal" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:gravity="center" - android:layout_margin="10dp" - > -<Button - android:id="@+id/stopbutton" - android:background="@drawable/selector_stop" - android:layout_width="64dp" - android:layout_height="64dp" - android:layout_margin="5dp" - /> -<Button - android:id="@+id/ejectbutton" - android:background="@drawable/selector_eject" - android:layout_width="64dp" - android:layout_height="64dp" - android:layout_margin="5dp" - /> - -</LinearLayout> -</LinearLayout> diff --git a/samples/RandomMusicPlayer/res/layout/main.xml b/samples/RandomMusicPlayer/res/layout/main.xml new file mode 100644 index 000000000..a41a710b8 --- /dev/null +++ b/samples/RandomMusicPlayer/res/layout/main.xml @@ -0,0 +1,76 @@ +<!-- + Copyright (C) 2011 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. + --> + +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:orientation="vertical" + android:layout_width="fill_parent" + android:layout_height="fill_parent" + android:gravity="center" + android:background="#000040"> + + <TextView android:text="@string/app_title" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:padding="20dp" + android:textColor="#ffffff" + android:textSize="20sp" + android:textStyle="bold" /> + + <LinearLayout android:orientation="horizontal" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center" + android:layout_margin="10dp"> + <Button android:id="@+id/rewindbutton" + android:background="@drawable/btn_rew" + android:layout_width="64dp" + android:layout_height="64dp" + android:layout_margin="5dp" /> + <Button android:id="@+id/playbutton" + android:background="@drawable/btn_play" + android:layout_width="64dp" + android:layout_height="64dp" + android:layout_margin="5dp" /> + <Button android:id="@+id/pausebutton" + android:background="@drawable/btn_pause" + android:layout_width="64dp" + android:layout_height="64dp" + android:layout_margin="5dp" /> + <Button android:id="@+id/skipbutton" + android:background="@drawable/btn_ff" + android:layout_width="64dp" + android:layout_height="64dp" + android:layout_margin="5dp" /> + </LinearLayout> + + <LinearLayout android:orientation="horizontal" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:gravity="center" + android:layout_margin="10dp"> + <Button android:id="@+id/stopbutton" + android:background="@drawable/btn_stop" + android:layout_width="64dp" + android:layout_height="64dp" + android:layout_margin="5dp" /> + <Button android:id="@+id/ejectbutton" + android:background="@drawable/btn_eject" + android:layout_width="64dp" + android:layout_height="64dp" + android:layout_margin="5dp" /> + + </LinearLayout> +</LinearLayout> diff --git a/samples/RandomMusicPlayer/res/values/strings.xml b/samples/RandomMusicPlayer/res/values/strings.xml new file mode 100644 index 000000000..39db75e6d --- /dev/null +++ b/samples/RandomMusicPlayer/res/values/strings.xml @@ -0,0 +1,19 @@ +<!-- + Copyright (C) 2011 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_title">Random Music Player</string> +</resources> diff --git a/samples/RandomMusicPlayer/src/com/example/android/musicplayer/AudioFocusHelper.java b/samples/RandomMusicPlayer/src/com/example/android/musicplayer/AudioFocusHelper.java index 4b8b54abc..980bac808 100644 --- a/samples/RandomMusicPlayer/src/com/example/android/musicplayer/AudioFocusHelper.java +++ b/samples/RandomMusicPlayer/src/com/example/android/musicplayer/AudioFocusHelper.java @@ -51,7 +51,6 @@ public class AudioFocusHelper implements AudioManager.OnAudioFocusChangeListener * Called by AudioManager on audio focus changes. We implement this by calling our * MusicFocusable appropriately to relay the message. */ - @Override public void onAudioFocusChange(int focusChange) { if (mFocusable == null) return; switch (focusChange) { diff --git a/samples/RandomMusicPlayer/src/com/example/android/musicplayer/MainActivity.java b/samples/RandomMusicPlayer/src/com/example/android/musicplayer/MainActivity.java index 4974a218e..4d5224b2a 100644 --- a/samples/RandomMusicPlayer/src/com/example/android/musicplayer/MainActivity.java +++ b/samples/RandomMusicPlayer/src/com/example/android/musicplayer/MainActivity.java @@ -22,6 +22,7 @@ import android.content.DialogInterface; import android.content.Intent; import android.net.Uri; import android.os.Bundle; +import android.view.KeyEvent; import android.view.View; import android.view.View.OnClickListener; import android.widget.Button; @@ -71,7 +72,6 @@ public class MainActivity extends Activity implements OnClickListener { mEjectButton.setOnClickListener(this); } - @Override public void onClick(View target) { // Send the correct intent to the MusicService, according to the button that was clicked if (target == mPlayButton) @@ -119,4 +119,15 @@ public class MainActivity extends Activity implements OnClickListener { alertBuilder.show(); } + + @Override + public boolean onKeyDown(int keyCode, KeyEvent event) { + switch (keyCode) { + case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: + case KeyEvent.KEYCODE_HEADSETHOOK: + startService(new Intent(MusicService.ACTION_TOGGLE_PLAYBACK)); + return true; + } + return super.onKeyDown(keyCode, event); + } } diff --git a/samples/RandomMusicPlayer/src/com/example/android/musicplayer/MediaButtonHelper.java b/samples/RandomMusicPlayer/src/com/example/android/musicplayer/MediaButtonHelper.java new file mode 100644 index 000000000..1bacee5cf --- /dev/null +++ b/samples/RandomMusicPlayer/src/com/example/android/musicplayer/MediaButtonHelper.java @@ -0,0 +1,85 @@ +package com.example.android.musicplayer; + +import android.content.ComponentName; +import android.media.AudioManager; +import android.util.Log; + +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + * Class that assists with handling new media button APIs available in API level 8. + */ +public class MediaButtonHelper { + // Backwards compatibility code (methods available as of API Level 8) + private static final String TAG = "MediaButtonHelper"; + + static { + initializeStaticCompatMethods(); + } + + static Method sMethodRegisterMediaButtonEventReceiver; + static Method sMethodUnregisterMediaButtonEventReceiver; + + static void initializeStaticCompatMethods() { + try { + sMethodRegisterMediaButtonEventReceiver = AudioManager.class.getMethod( + "registerMediaButtonEventReceiver", + new Class[] { ComponentName.class }); + sMethodUnregisterMediaButtonEventReceiver = AudioManager.class.getMethod( + "unregisterMediaButtonEventReceiver", + new Class[] { ComponentName.class }); + } catch (NoSuchMethodException e) { + // Silently fail when running on an OS before API level 8. + } + } + + public static void registerMediaButtonEventReceiverCompat(AudioManager audioManager, + ComponentName receiver) { + if (sMethodRegisterMediaButtonEventReceiver == null) + return; + + try { + sMethodRegisterMediaButtonEventReceiver.invoke(audioManager, receiver); + } catch (InvocationTargetException e) { + // Unpack original exception when possible + Throwable cause = e.getCause(); + if (cause instanceof RuntimeException) { + throw (RuntimeException) cause; + } else if (cause instanceof Error) { + throw (Error) cause; + } else { + // Unexpected checked exception; wrap and re-throw + throw new RuntimeException(e); + } + } catch (IllegalAccessException e) { + Log.e(TAG, "IllegalAccessException invoking registerMediaButtonEventReceiver."); + e.printStackTrace(); + } + } + + @SuppressWarnings("unused") + public static void unregisterMediaButtonEventReceiverCompat(AudioManager audioManager, + ComponentName receiver) { + if (sMethodUnregisterMediaButtonEventReceiver == null) + return; + + try { + sMethodUnregisterMediaButtonEventReceiver.invoke(audioManager, receiver); + } catch (InvocationTargetException e) { + // Unpack original exception when possible + Throwable cause = e.getCause(); + if (cause instanceof RuntimeException) { + throw (RuntimeException) cause; + } else if (cause instanceof Error) { + throw (Error) cause; + } else { + // Unexpected checked exception; wrap and re-throw + throw new RuntimeException(e); + } + } catch (IllegalAccessException e) { + Log.e(TAG, "IllegalAccessException invoking unregisterMediaButtonEventReceiver."); + e.printStackTrace(); + } + } +} diff --git a/samples/RandomMusicPlayer/src/com/example/android/musicplayer/MusicIntentReceiver.java b/samples/RandomMusicPlayer/src/com/example/android/musicplayer/MusicIntentReceiver.java index cc03d5e23..28f19d4af 100644 --- a/samples/RandomMusicPlayer/src/com/example/android/musicplayer/MusicIntentReceiver.java +++ b/samples/RandomMusicPlayer/src/com/example/android/musicplayer/MusicIntentReceiver.java @@ -19,22 +19,53 @@ package com.example.android.musicplayer; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import android.util.Log; +import android.view.KeyEvent; import android.widget.Toast; /** * Receives broadcasted intents. In particular, we are interested in the - * android.media.AUDIO_BECOMING_NOISY intent, which is broadcast, for example, when the user - * disconnects the headphones. This class works because we are declaring it in a <receiver> - * tag in AndroidManifest.xml. + * android.media.AUDIO_BECOMING_NOISY and android.intent.action.MEDIA_BUTTON intents, which is + * broadcast, for example, when the user disconnects the headphones. This class works because we are + * declaring it in a <receiver> tag in AndroidManifest.xml. */ public class MusicIntentReceiver extends BroadcastReceiver { @Override - public void onReceive(Context ctx, Intent intent) { + public void onReceive(Context context, Intent intent) { if (intent.getAction().equals(android.media.AudioManager.ACTION_AUDIO_BECOMING_NOISY)) { - Toast.makeText(ctx, "Headphones disconnected.", Toast.LENGTH_SHORT).show(); + Toast.makeText(context, "Headphones disconnected.", Toast.LENGTH_SHORT).show(); // send an intent to our MusicService to telling it to pause the audio - ctx.startService(new Intent(MusicService.ACTION_PAUSE)); + context.startService(new Intent(MusicService.ACTION_PAUSE)); + + } else if (intent.getAction().equals(Intent.ACTION_MEDIA_BUTTON)) { + KeyEvent keyEvent = (KeyEvent) intent.getExtras().get(Intent.EXTRA_KEY_EVENT); + if (keyEvent.getAction() != KeyEvent.ACTION_DOWN) + return; + + switch (keyEvent.getKeyCode()) { + case KeyEvent.KEYCODE_HEADSETHOOK: + case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE: + context.startService(new Intent(MusicService.ACTION_TOGGLE_PLAYBACK)); + break; + case KeyEvent.KEYCODE_MEDIA_PLAY: + context.startService(new Intent(MusicService.ACTION_PLAY)); + break; + case KeyEvent.KEYCODE_MEDIA_PAUSE: + context.startService(new Intent(MusicService.ACTION_PAUSE)); + break; + case KeyEvent.KEYCODE_MEDIA_STOP: + context.startService(new Intent(MusicService.ACTION_STOP)); + break; + case KeyEvent.KEYCODE_MEDIA_NEXT: + context.startService(new Intent(MusicService.ACTION_SKIP)); + break; + case KeyEvent.KEYCODE_MEDIA_PREVIOUS: + // TODO: ensure that doing this in rapid succession actually plays the + // previous song + context.startService(new Intent(MusicService.ACTION_REWIND)); + break; + } } } } diff --git a/samples/RandomMusicPlayer/src/com/example/android/musicplayer/MusicRetriever.java b/samples/RandomMusicPlayer/src/com/example/android/musicplayer/MusicRetriever.java index 44d644756..5e6f2f142 100644 --- a/samples/RandomMusicPlayer/src/com/example/android/musicplayer/MusicRetriever.java +++ b/samples/RandomMusicPlayer/src/com/example/android/musicplayer/MusicRetriever.java @@ -16,16 +16,17 @@ package com.example.android.musicplayer; -import java.util.ArrayList; -import java.util.List; -import java.util.Random; - import android.content.ContentResolver; import android.content.ContentUris; import android.database.Cursor; import android.net.Uri; +import android.provider.MediaStore; import android.util.Log; +import java.util.ArrayList; +import java.util.List; +import java.util.Random; + /** * Retrieves and organizes media to play. Before being used, you must call {@link #prepare()}, * which will retrieve all of the music on the user's device (by performing a query on a content @@ -57,7 +58,8 @@ public class MusicRetriever { // Perform a query on the content resolver. The URI we're passing specifies that we // want to query for all audio media on external storage (e.g. SD card) - Cursor cur = mContentResolver.query(uri, null, null, null, null); + Cursor cur = mContentResolver.query(uri, null, + MediaStore.Audio.Media.IS_MUSIC + " = 1", null, null); Log.i(TAG, "Query finished. " + (cur == null ? "Returned NULL." : "Returned a cursor.")); if (cur == null) { @@ -73,9 +75,12 @@ public class MusicRetriever { Log.i(TAG, "Listing..."); - // retrieve the indices of the columns where the ID and title of the song are - int titleColumn = cur.getColumnIndex(android.provider.MediaStore.Audio.Media.TITLE); - int idColumn = cur.getColumnIndex(android.provider.MediaStore.Audio.Media._ID); + // retrieve the indices of the columns where the ID, title, etc. of the song are + int artistColumn = cur.getColumnIndex(MediaStore.Audio.Media.ARTIST); + int titleColumn = cur.getColumnIndex(MediaStore.Audio.Media.TITLE); + int albumColumn = cur.getColumnIndex(MediaStore.Audio.Media.ALBUM); + int durationColumn = cur.getColumnIndex(MediaStore.Audio.Media.DURATION); + int idColumn = cur.getColumnIndex(MediaStore.Audio.Media._ID); Log.i(TAG, "Title column index: " + String.valueOf(titleColumn)); Log.i(TAG, "ID column index: " + String.valueOf(titleColumn)); @@ -83,7 +88,12 @@ public class MusicRetriever { // add each song to mItems do { Log.i(TAG, "ID: " + cur.getString(idColumn) + " Title: " + cur.getString(titleColumn)); - mItems.add(new Item(cur.getLong(idColumn), cur.getString(titleColumn))); + mItems.add(new Item( + cur.getLong(idColumn), + cur.getString(artistColumn), + cur.getString(titleColumn), + cur.getString(albumColumn), + cur.getLong(durationColumn))); } while (cur.moveToNext()); Log.i(TAG, "Done querying media. MusicRetriever is ready."); @@ -99,17 +109,41 @@ public class MusicRetriever { return mItems.get(mRandom.nextInt(mItems.size())); } - public class Item { + public static class Item { long id; + String artist; String title; + String album; + long duration; - public Item(long id, String title) { + public Item(long id, String artist, String title, String album, long duration) { this.id = id; + this.artist = artist; this.title = title; + this.album = album; + this.duration = duration; + } + + public long getId() { + return id; + } + + public String getArtist() { + return artist; + } + + public String getTitle() { + return title; + } + + public String getAlbum() { + return album; + } + + public long getDuration() { + return duration; } - public long getId() { return id; } - public String getTitle() { return title; } public Uri getURI() { return ContentUris.withAppendedId( android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, id); diff --git a/samples/RandomMusicPlayer/src/com/example/android/musicplayer/MusicService.java b/samples/RandomMusicPlayer/src/com/example/android/musicplayer/MusicService.java index 9bd12512b..25f5d81fb 100644 --- a/samples/RandomMusicPlayer/src/com/example/android/musicplayer/MusicService.java +++ b/samples/RandomMusicPlayer/src/com/example/android/musicplayer/MusicService.java @@ -16,19 +16,22 @@ package com.example.android.musicplayer; -import java.io.IOException; - import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; +import android.content.ComponentName; import android.content.Context; import android.content.Intent; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; import android.media.AudioManager; +import android.media.MediaMetadataRetriever; import android.media.MediaPlayer; import android.media.MediaPlayer.OnCompletionListener; import android.media.MediaPlayer.OnErrorListener; import android.media.MediaPlayer.OnPreparedListener; +import android.media.RemoteControlClient; import android.net.Uri; import android.net.wifi.WifiManager; import android.net.wifi.WifiManager.WifiLock; @@ -37,9 +40,11 @@ import android.os.PowerManager; import android.util.Log; import android.widget.Toast; +import java.io.IOException; + /** * Service that handles media playback. This is the Service through which we perform all the media - * handling in our application. Upon initialization, it starts a {@link MediaRetriever} to scan + * handling in our application. Upon initialization, it starts a {@link MusicRetriever} to scan * the user's media. Then, it waits for Intents (which come from our main activity, * {@link MainActivity}, which signal the service to perform specific operations: Play, Pause, * Rewind, Skip, etc. @@ -48,7 +53,25 @@ public class MusicService extends Service implements OnCompletionListener, OnPre OnErrorListener, MusicFocusable, PrepareMusicRetrieverTask.MusicRetrieverPreparedListener { - NotificationManager mNotificationManager; + // The tag we put on debug messages + final static String TAG = "RandomMusicPlayer"; + + // These are the Intent actions that we are prepared to handle. Notice that the fact these + // constants exist in our class is a mere convenience: what really defines the actions our + // service can handle are the <action> tags in the <intent-filters> tag for our service in + // AndroidManifest.xml. + public static final String ACTION_TOGGLE_PLAYBACK = + "com.example.android.musicplayer.action.TOGGLE_PLAYBACK"; + public static final String ACTION_PLAY = "com.example.android.musicplayer.action.PLAY"; + public static final String ACTION_PAUSE = "com.example.android.musicplayer.action.PAUSE"; + public static final String ACTION_STOP = "com.example.android.musicplayer.action.STOP"; + public static final String ACTION_SKIP = "com.example.android.musicplayer.action.SKIP"; + public static final String ACTION_REWIND = "com.example.android.musicplayer.action.REWIND"; + public static final String ACTION_URL = "com.example.android.musicplayer.action.URL"; + + // The volume we set the media player to when we lose audio focus, but are allowed to reduce + // the volume instead of stopping playback. + public static final float DUCK_VOLUME = 0.1f; // our media player MediaPlayer mPlayer = null; @@ -104,24 +127,6 @@ public class MusicService extends Service implements OnCompletionListener, OnPre // device from shutting off the Wifi radio WifiLock mWifiLock; - // The tag we put on debug messages - final static String TAG = "RandomMusicPlayer"; - - // These are the Intent actions that we are prepared to handle. Notice that the fact these - // constants exist in our class is a mere convenience: what really defines the actions our - // service can handle are the <action> tags in the <intent-filters> tag for our service in - // AndroidManifest.xml. - public static final String ACTION_PLAY = "com.example.android.musicplayer.action.PLAY"; - public static final String ACTION_PAUSE = "com.example.android.musicplayer.action.PAUSE"; - public static final String ACTION_STOP = "com.example.android.musicplayer.action.STOP"; - public static final String ACTION_SKIP = "com.example.android.musicplayer.action.SKIP"; - public static final String ACTION_REWIND = "com.example.android.musicplayer.action.REWIND"; - public static final String ACTION_URL = "com.example.android.musicplayer.action.URL"; - - // The volume we set the media player to when we lose audio focus, but are allowed to reduce - // the volume instead of stopping playback. - public final float DUCK_VOLUME = 0.1f; - // The ID we use for the notification (the onscreen alert that appears at the notification // area at the top of the screen as an icon -- and as text as well if the user expands the // notification area). @@ -131,6 +136,20 @@ public class MusicService extends Service implements OnCompletionListener, OnPre // providing titles and URIs as we need. MusicRetriever mRetriever; + // our RemoteControlClient object, which will use remote control APIs available in + // SDK level >= 14, if they're available. + RemoteControlClientCompat mRemoteControlClientCompat; + + // Dummy album art we will pass to the remote control (if the APIs are available). + Bitmap mDummyAlbumArt; + + // The component name of MusicIntentReceiver, for use with media button and remote control + // APIs + ComponentName mMediaButtonReceiverComponent; + + AudioManager mAudioManager; + NotificationManager mNotificationManager; + Notification mNotification = null; /** @@ -167,6 +186,7 @@ public class MusicService extends Service implements OnCompletionListener, OnPre .createWifiLock(WifiManager.WIFI_MODE_FULL, "mylock"); mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE); + mAudioManager = (AudioManager) getSystemService(AUDIO_SERVICE); // Create the retriever and start an asynchronous task that will prepare it. mRetriever = new MusicRetriever(getContentResolver()); @@ -177,6 +197,10 @@ public class MusicService extends Service implements OnCompletionListener, OnPre mAudioFocusHelper = new AudioFocusHelper(getApplicationContext(), this); else mAudioFocus = AudioFocus.Focused; // no focus feature, so we always "have" audio focus + + mDummyAlbumArt = BitmapFactory.decodeResource(getResources(), R.drawable.dummy_album_art); + + mMediaButtonReceiverComponent = new ComponentName(this, MusicIntentReceiver.class); } /** @@ -187,7 +211,8 @@ public class MusicService extends Service implements OnCompletionListener, OnPre @Override public int onStartCommand(Intent intent, int flags, int startId) { String action = intent.getAction(); - if (action.equals(ACTION_PLAY)) processPlayRequest(); + if (action.equals(ACTION_TOGGLE_PLAYBACK)) processTogglePlaybackRequest(); + else if (action.equals(ACTION_PLAY)) processPlayRequest(); else if (action.equals(ACTION_PAUSE)) processPauseRequest(); else if (action.equals(ACTION_SKIP)) processSkipRequest(); else if (action.equals(ACTION_STOP)) processStopRequest(); @@ -198,6 +223,14 @@ public class MusicService extends Service implements OnCompletionListener, OnPre // restart in case it's killed. } + void processTogglePlaybackRequest() { + if (mState == State.Paused || mState == State.Stopped) { + processPlayRequest(); + } else { + processPauseRequest(); + } + } + void processPlayRequest() { if (mState == State.Retrieving) { // If we are still retrieving media, just set the flag to start playing when we're @@ -209,6 +242,8 @@ public class MusicService extends Service implements OnCompletionListener, OnPre tryToGetAudioFocus(); + // actually play the song + if (mState == State.Stopped) { // If we're stopped, just go ahead to the next song and start playing playNextSong(null); @@ -219,6 +254,12 @@ public class MusicService extends Service implements OnCompletionListener, OnPre setUpAsForeground(mSongTitle + " (playing)"); configAndStartMediaPlayer(); } + + // Tell any remote controls that our playback state is 'playing'. + if (mRemoteControlClientCompat != null) { + mRemoteControlClientCompat + .setPlaybackState(RemoteControlClient.PLAYSTATE_PLAYING); + } } void processPauseRequest() { @@ -234,7 +275,13 @@ public class MusicService extends Service implements OnCompletionListener, OnPre mState = State.Paused; mPlayer.pause(); relaxResources(false); // while paused, we always retain the MediaPlayer - giveUpAudioFocus(); + // do not give up audio focus + } + + // Tell any remote controls that our playback state is 'paused'. + if (mRemoteControlClientCompat != null) { + mRemoteControlClientCompat + .setPlaybackState(RemoteControlClient.PLAYSTATE_PAUSED); } } @@ -251,13 +298,23 @@ public class MusicService extends Service implements OnCompletionListener, OnPre } void processStopRequest() { - if (mState == State.Playing || mState == State.Paused) { + processStopRequest(false); + } + + void processStopRequest(boolean force) { + if (mState == State.Playing || mState == State.Paused || force) { mState = State.Stopped; // let go of all resources... relaxResources(true); giveUpAudioFocus(); + // Tell any remote controls that our playback state is 'paused'. + if (mRemoteControlClientCompat != null) { + mRemoteControlClientCompat + .setPlaybackState(RemoteControlClient.PLAYSTATE_STOPPED); + } + // service is no longer necessary. Will be started again if needed. stopSelf(); } @@ -330,14 +387,6 @@ public class MusicService extends Service implements OnCompletionListener, OnPre } } - /** - * Shortcut to making and displaying a toast. Seemed cleaner than repeating - * this code everywhere, at least for this sample. - */ - void say(String message) { - Toast.makeText(this, message, Toast.LENGTH_SHORT).show(); - } - void tryToGetAudioFocus() { if (mAudioFocus != AudioFocus.Focused && mAudioFocusHelper != null && mAudioFocusHelper.requestFocus()) @@ -355,34 +404,80 @@ public class MusicService extends Service implements OnCompletionListener, OnPre relaxResources(false); // release everything except MediaPlayer try { + MusicRetriever.Item playingItem = null; if (manualUrl != null) { // set the source of the media player to a manual URL or path createMediaPlayerIfNeeded(); mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); mPlayer.setDataSource(manualUrl); - mSongTitle = manualUrl; mIsStreaming = manualUrl.startsWith("http:") || manualUrl.startsWith("https:"); + + playingItem = new MusicRetriever.Item(0, null, manualUrl, null, 0); } else { mIsStreaming = false; // playing a locally available song - MusicRetriever.Item item = mRetriever.getRandomItem(); - if (item == null) { - say("No song to play :-("); + playingItem = mRetriever.getRandomItem(); + if (playingItem == null) { + Toast.makeText(this, + "No available music to play. Place some music on your external storage " + + "device (e.g. your SD card) and try again.", + Toast.LENGTH_LONG).show(); + processStopRequest(true); // stop everything! return; } // set the source of the media player a a content URI createMediaPlayerIfNeeded(); mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); - mPlayer.setDataSource(getApplicationContext(), item.getURI()); - mSongTitle = item.getTitle(); + mPlayer.setDataSource(getApplicationContext(), playingItem.getURI()); } + mSongTitle = playingItem.getTitle(); mState = State.Preparing; setUpAsForeground(mSongTitle + " (loading)"); + // Use the media button APIs (if available) to register ourselves for media button + // events + + MediaButtonHelper.registerMediaButtonEventReceiverCompat( + mAudioManager, mMediaButtonReceiverComponent); + + // Use the remote control APIs (if available) to set the playback state + + if (mRemoteControlClientCompat == null) { + Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON); + intent.setComponent(mMediaButtonReceiverComponent); + mRemoteControlClientCompat = new RemoteControlClientCompat( + PendingIntent.getBroadcast(this /*context*/, + 0 /*requestCode, ignored*/, intent /*intent*/, 0 /*flags*/)); + RemoteControlHelper.registerRemoteControlClient(mAudioManager, + mRemoteControlClientCompat); + } + + mRemoteControlClientCompat.setPlaybackState( + RemoteControlClient.PLAYSTATE_PLAYING); + + mRemoteControlClientCompat.setTransportControlFlags( + RemoteControlClient.FLAG_KEY_MEDIA_PLAY | + RemoteControlClient.FLAG_KEY_MEDIA_PAUSE | + RemoteControlClient.FLAG_KEY_MEDIA_NEXT | + RemoteControlClient.FLAG_KEY_MEDIA_STOP); + + // Update the remote controls + mRemoteControlClientCompat.editMetadata(true) + .putString(MediaMetadataRetriever.METADATA_KEY_ARTIST, playingItem.getArtist()) + .putString(MediaMetadataRetriever.METADATA_KEY_ALBUM, playingItem.getAlbum()) + .putString(MediaMetadataRetriever.METADATA_KEY_TITLE, playingItem.getTitle()) + .putLong(MediaMetadataRetriever.METADATA_KEY_DURATION, + playingItem.getDuration()) + // TODO: fetch real item artwork + .putBitmap( + RemoteControlClientCompat.MetadataEditorCompat.METADATA_KEY_ARTWORK, + mDummyAlbumArt) + .apply(); + // starts preparing the media player in the background. When it's done, it will call // our OnPreparedListener (that is, the onPrepared() method on this class, since we set // the listener to 'this'). @@ -403,14 +498,12 @@ public class MusicService extends Service implements OnCompletionListener, OnPre } /** Called when media player is done playing current song. */ - @Override public void onCompletion(MediaPlayer player) { // The media player finished playing the current song, so we go ahead and start the next. playNextSong(null); } /** Called when media player is done preparing. */ - @Override public void onPrepared(MediaPlayer player) { // The media player is done preparing. That means we can start playing! mState = State.Playing; @@ -449,7 +542,6 @@ public class MusicService extends Service implements OnCompletionListener, OnPre * Called when there's an error playing media. When this happens, the media player goes to * the Error state. We warn the user about the error and reset the media player. */ - @Override public boolean onError(MediaPlayer mp, int what, int extra) { Toast.makeText(getApplicationContext(), "Media player error! Resetting.", Toast.LENGTH_SHORT).show(); @@ -461,7 +553,6 @@ public class MusicService extends Service implements OnCompletionListener, OnPre return true; // true indicates we handled the error } - @Override public void onGainedAudioFocus() { Toast.makeText(getApplicationContext(), "gained audio focus.", Toast.LENGTH_SHORT).show(); mAudioFocus = AudioFocus.Focused; @@ -471,7 +562,6 @@ public class MusicService extends Service implements OnCompletionListener, OnPre configAndStartMediaPlayer(); } - @Override public void onLostAudioFocus(boolean canDuck) { Toast.makeText(getApplicationContext(), "lost audio focus." + (canDuck ? "can duck" : "no duck"), Toast.LENGTH_SHORT).show(); @@ -482,7 +572,6 @@ public class MusicService extends Service implements OnCompletionListener, OnPre configAndStartMediaPlayer(); } - @Override public void onMusicRetrieverPrepared() { // Done retrieving! mState = State.Stopped; diff --git a/samples/RandomMusicPlayer/src/com/example/android/musicplayer/RemoteControlClientCompat.java b/samples/RandomMusicPlayer/src/com/example/android/musicplayer/RemoteControlClientCompat.java new file mode 100644 index 000000000..9541a91d0 --- /dev/null +++ b/samples/RandomMusicPlayer/src/com/example/android/musicplayer/RemoteControlClientCompat.java @@ -0,0 +1,353 @@ +/* + * Copyright (C) 2011 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.musicplayer; + +import android.app.PendingIntent; +import android.graphics.Bitmap; +import android.os.Looper; +import android.util.Log; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; + +/** + * RemoteControlClient enables exposing information meant to be consumed by remote controls capable + * of displaying metadata, artwork and media transport control buttons. A remote control client + * object is associated with a media button event receiver. This event receiver must have been + * previously registered with + * {@link android.media.AudioManager#registerMediaButtonEventReceiver(android.content.ComponentName)} + * before the RemoteControlClient can be registered through + * {@link android.media.AudioManager#registerRemoteControlClient(android.media.RemoteControlClient)}. + */ +@SuppressWarnings({"rawtypes", "unchecked"}) +public class RemoteControlClientCompat { + + private static final String TAG = "RemoteControlCompat"; + + private static Class sRemoteControlClientClass; + + // RCC short for RemoteControlClient + private static Method sRCCEditMetadataMethod; + private static Method sRCCSetPlayStateMethod; + private static Method sRCCSetTransportControlFlags; + + private static boolean sHasRemoteControlAPIs = false; + + static { + try { + ClassLoader classLoader = RemoteControlClientCompat.class.getClassLoader(); + sRemoteControlClientClass = getActualRemoteControlClientClass(classLoader); + // dynamically populate the playstate and flag values in case they change + // in future versions. + for (Field field : RemoteControlClientCompat.class.getFields()) { + try { + Field realField = sRemoteControlClientClass.getField(field.getName()); + Object realValue = realField.get(null); + field.set(null, realValue); + } catch (NoSuchFieldException e) { + Log.w(TAG, "Could not get real field: " + field.getName()); + } catch (IllegalArgumentException e) { + Log.w(TAG, "Error trying to pull field value for: " + field.getName() + + " " + e.getMessage()); + } catch (IllegalAccessException e) { + Log.w(TAG, "Error trying to pull field value for: " + field.getName() + + " " + e.getMessage()); + } + } + + // get the required public methods on RemoteControlClient + sRCCEditMetadataMethod = sRemoteControlClientClass.getMethod("editMetadata", + boolean.class); + sRCCSetPlayStateMethod = sRemoteControlClientClass.getMethod("setPlaybackState", + int.class); + sRCCSetTransportControlFlags = sRemoteControlClientClass.getMethod( + "setTransportControlFlags", int.class); + + sHasRemoteControlAPIs = true; + } catch (ClassNotFoundException e) { + // Silently fail when running on an OS before ICS. + } catch (NoSuchMethodException e) { + // Silently fail when running on an OS before ICS. + } catch (IllegalArgumentException e) { + // Silently fail when running on an OS before ICS. + } catch (SecurityException e) { + // Silently fail when running on an OS before ICS. + } + } + + public static Class getActualRemoteControlClientClass(ClassLoader classLoader) + throws ClassNotFoundException { + return classLoader.loadClass("android.media.RemoteControlClient"); + } + + private Object mActualRemoteControlClient; + + public RemoteControlClientCompat(PendingIntent pendingIntent) { + if (!sHasRemoteControlAPIs) { + return; + } + try { + mActualRemoteControlClient = + sRemoteControlClientClass.getConstructor(PendingIntent.class) + .newInstance(pendingIntent); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public RemoteControlClientCompat(PendingIntent pendingIntent, Looper looper) { + if (!sHasRemoteControlAPIs) { + return; + } + + try { + mActualRemoteControlClient = + sRemoteControlClientClass.getConstructor(PendingIntent.class, Looper.class) + .newInstance(pendingIntent, looper); + } catch (Exception e) { + Log.e(TAG, "Error creating new instance of " + sRemoteControlClientClass.getName(), e); + } + } + + /** + * Class used to modify metadata in a {@link android.media.RemoteControlClient} object. Use + * {@link android.media.RemoteControlClient#editMetadata(boolean)} to create an instance of an + * editor, on which you set the metadata for the RemoteControlClient instance. Once all the + * information has been set, use {@link #apply()} to make it the new metadata that should be + * displayed for the associated client. Once the metadata has been "applied", you cannot reuse + * this instance of the MetadataEditor. + */ + public class MetadataEditorCompat { + + private Method mPutStringMethod; + private Method mPutBitmapMethod; + private Method mPutLongMethod; + private Method mClearMethod; + private Method mApplyMethod; + + private Object mActualMetadataEditor; + + /** + * The metadata key for the content artwork / album art. + */ + public final static int METADATA_KEY_ARTWORK = 100; + + private MetadataEditorCompat(Object actualMetadataEditor) { + if (sHasRemoteControlAPIs && actualMetadataEditor == null) { + throw new IllegalArgumentException("Remote Control API's exist, " + + "should not be given a null MetadataEditor"); + } + if (sHasRemoteControlAPIs) { + Class metadataEditorClass = actualMetadataEditor.getClass(); + + try { + mPutStringMethod = metadataEditorClass.getMethod("putString", + int.class, String.class); + mPutBitmapMethod = metadataEditorClass.getMethod("putBitmap", + int.class, Bitmap.class); + mPutLongMethod = metadataEditorClass.getMethod("putLong", + int.class, long.class); + mClearMethod = metadataEditorClass.getMethod("clear", new Class[]{}); + mApplyMethod = metadataEditorClass.getMethod("apply", new Class[]{}); + } catch (Exception e) { + throw new RuntimeException(e.getMessage(), e); + } + } + mActualMetadataEditor = actualMetadataEditor; + } + + /** + * Adds textual information to be displayed. + * Note that none of the information added after {@link #apply()} has been called, + * will be displayed. + * @param key The identifier of a the metadata field to set. Valid values are + * {@link android.media.MediaMetadataRetriever#METADATA_KEY_ALBUM}, + * {@link android.media.MediaMetadataRetriever#METADATA_KEY_ALBUMARTIST}, + * {@link android.media.MediaMetadataRetriever#METADATA_KEY_TITLE}, + * {@link android.media.MediaMetadataRetriever#METADATA_KEY_ARTIST}, + * {@link android.media.MediaMetadataRetriever#METADATA_KEY_AUTHOR}, + * {@link android.media.MediaMetadataRetriever#METADATA_KEY_COMPILATION}, + * {@link android.media.MediaMetadataRetriever#METADATA_KEY_COMPOSER}, + * {@link android.media.MediaMetadataRetriever#METADATA_KEY_DATE}, + * {@link android.media.MediaMetadataRetriever#METADATA_KEY_GENRE}, + * {@link android.media.MediaMetadataRetriever#METADATA_KEY_TITLE}, + * {@link android.media.MediaMetadataRetriever#METADATA_KEY_WRITER}. + * @param value The text for the given key, or {@code null} to signify there is no valid + * information for the field. + * @return Returns a reference to the same MetadataEditor object, so you can chain put + * calls together. + */ + public MetadataEditorCompat putString(int key, String value) { + if (sHasRemoteControlAPIs) { + try { + mPutStringMethod.invoke(mActualMetadataEditor, key, value); + } catch (Exception e) { + throw new RuntimeException(e.getMessage(), e); + } + } + return this; + } + + /** + * Sets the album / artwork picture to be displayed on the remote control. + * @param key the identifier of the bitmap to set. The only valid value is + * {@link #METADATA_KEY_ARTWORK} + * @param bitmap The bitmap for the artwork, or null if there isn't any. + * @return Returns a reference to the same MetadataEditor object, so you can chain put + * calls together. + * @throws IllegalArgumentException + * @see android.graphics.Bitmap + */ + public MetadataEditorCompat putBitmap(int key, Bitmap bitmap) { + if (sHasRemoteControlAPIs) { + try { + mPutBitmapMethod.invoke(mActualMetadataEditor, key, bitmap); + } catch (Exception e) { + throw new RuntimeException(e.getMessage(), e); + } + } + return this; + } + + /** + * Adds numerical information to be displayed. + * Note that none of the information added after {@link #apply()} has been called, + * will be displayed. + * @param key the identifier of a the metadata field to set. Valid values are + * {@link android.media.MediaMetadataRetriever#METADATA_KEY_CD_TRACK_NUMBER}, + * {@link android.media.MediaMetadataRetriever#METADATA_KEY_DISC_NUMBER}, + * {@link android.media.MediaMetadataRetriever#METADATA_KEY_DURATION} (with a value + * expressed in milliseconds), + * {@link android.media.MediaMetadataRetriever#METADATA_KEY_YEAR}. + * @param value The long value for the given key + * @return Returns a reference to the same MetadataEditor object, so you can chain put + * calls together. + * @throws IllegalArgumentException + */ + public MetadataEditorCompat putLong(int key, long value) { + if (sHasRemoteControlAPIs) { + try { + mPutLongMethod.invoke(mActualMetadataEditor, key, value); + } catch (Exception e) { + throw new RuntimeException(e.getMessage(), e); + } + } + return this; + } + + /** + * Clears all the metadata that has been set since the MetadataEditor instance was + * created with {@link android.media.RemoteControlClient#editMetadata(boolean)}. + */ + public void clear() { + if (sHasRemoteControlAPIs) { + try { + mClearMethod.invoke(mActualMetadataEditor, (Object[]) null); + } catch (Exception e) { + throw new RuntimeException(e.getMessage(), e); + } + } + } + + /** + * Associates all the metadata that has been set since the MetadataEditor instance was + * created with {@link android.media.RemoteControlClient#editMetadata(boolean)}, or since + * {@link #clear()} was called, with the RemoteControlClient. Once "applied", this + * MetadataEditor cannot be reused to edit the RemoteControlClient's metadata. + */ + public void apply() { + if (sHasRemoteControlAPIs) { + try { + mApplyMethod.invoke(mActualMetadataEditor, (Object[]) null); + } catch (Exception e) { + throw new RuntimeException(e.getMessage(), e); + } + } + } + } + + /** + * Creates a {@link android.media.RemoteControlClient.MetadataEditor}. + * @param startEmpty Set to false if you want the MetadataEditor to contain the metadata that + * was previously applied to the RemoteControlClient, or true if it is to be created empty. + * @return a new MetadataEditor instance. + */ + public MetadataEditorCompat editMetadata(boolean startEmpty) { + Object metadataEditor; + if (sHasRemoteControlAPIs) { + try { + metadataEditor = sRCCEditMetadataMethod.invoke(mActualRemoteControlClient, + startEmpty); + } catch (Exception e) { + throw new RuntimeException(e); + } + } else { + metadataEditor = null; + } + return new MetadataEditorCompat(metadataEditor); + } + + /** + * Sets the current playback state. + * @param state The current playback state, one of the following values: + * {@link android.media.RemoteControlClient#PLAYSTATE_STOPPED}, + * {@link android.media.RemoteControlClient#PLAYSTATE_PAUSED}, + * {@link android.media.RemoteControlClient#PLAYSTATE_PLAYING}, + * {@link android.media.RemoteControlClient#PLAYSTATE_FAST_FORWARDING}, + * {@link android.media.RemoteControlClient#PLAYSTATE_REWINDING}, + * {@link android.media.RemoteControlClient#PLAYSTATE_SKIPPING_FORWARDS}, + * {@link android.media.RemoteControlClient#PLAYSTATE_SKIPPING_BACKWARDS}, + * {@link android.media.RemoteControlClient#PLAYSTATE_BUFFERING}, + * {@link android.media.RemoteControlClient#PLAYSTATE_ERROR}. + */ + public void setPlaybackState(int state) { + if (sHasRemoteControlAPIs) { + try { + sRCCSetPlayStateMethod.invoke(mActualRemoteControlClient, state); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + /** + * Sets the flags for the media transport control buttons that this client supports. + * @param transportControlFlags A combination of the following flags: + * {@link android.media.RemoteControlClient#FLAG_KEY_MEDIA_PREVIOUS}, + * {@link android.media.RemoteControlClient#FLAG_KEY_MEDIA_REWIND}, + * {@link android.media.RemoteControlClient#FLAG_KEY_MEDIA_PLAY}, + * {@link android.media.RemoteControlClient#FLAG_KEY_MEDIA_PLAY_PAUSE}, + * {@link android.media.RemoteControlClient#FLAG_KEY_MEDIA_PAUSE}, + * {@link android.media.RemoteControlClient#FLAG_KEY_MEDIA_STOP}, + * {@link android.media.RemoteControlClient#FLAG_KEY_MEDIA_FAST_FORWARD}, + * {@link android.media.RemoteControlClient#FLAG_KEY_MEDIA_NEXT} + */ + public void setTransportControlFlags(int transportControlFlags) { + if (sHasRemoteControlAPIs) { + try { + sRCCSetTransportControlFlags.invoke(mActualRemoteControlClient, + transportControlFlags); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + } + + public final Object getActualRemoteControlClientObject() { + return mActualRemoteControlClient; + } +} diff --git a/samples/RandomMusicPlayer/src/com/example/android/musicplayer/RemoteControlHelper.java b/samples/RandomMusicPlayer/src/com/example/android/musicplayer/RemoteControlHelper.java new file mode 100644 index 000000000..714d3c524 --- /dev/null +++ b/samples/RandomMusicPlayer/src/com/example/android/musicplayer/RemoteControlHelper.java @@ -0,0 +1,87 @@ +/* + * Copyright (C) 2011 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.musicplayer; + +import android.media.AudioManager; +import android.util.Log; + +import java.lang.reflect.Method; + +/** + * Contains methods to handle registering/unregistering remote control clients. These methods only + * run on ICS devices. On previous devices, all methods are no-ops. + */ +@SuppressWarnings({"unchecked", "rawtypes"}) +public class RemoteControlHelper { + private static final String TAG = "RemoteControlHelper"; + + private static boolean sHasRemoteControlAPIs = false; + + private static Method sRegisterRemoteControlClientMethod; + private static Method sUnregisterRemoteControlClientMethod; + + static { + try { + ClassLoader classLoader = RemoteControlHelper.class.getClassLoader(); + Class sRemoteControlClientClass = + RemoteControlClientCompat.getActualRemoteControlClientClass(classLoader); + sRegisterRemoteControlClientMethod = AudioManager.class.getMethod( + "registerRemoteControlClient", new Class[]{sRemoteControlClientClass}); + sUnregisterRemoteControlClientMethod = AudioManager.class.getMethod( + "unregisterRemoteControlClient", new Class[]{sRemoteControlClientClass}); + sHasRemoteControlAPIs = true; + } catch (ClassNotFoundException e) { + // Silently fail when running on an OS before ICS. + } catch (NoSuchMethodException e) { + // Silently fail when running on an OS before ICS. + } catch (IllegalArgumentException e) { + // Silently fail when running on an OS before ICS. + } catch (SecurityException e) { + // Silently fail when running on an OS before ICS. + } + } + + public static void registerRemoteControlClient(AudioManager audioManager, + RemoteControlClientCompat remoteControlClient) { + if (!sHasRemoteControlAPIs) { + return; + } + + try { + sRegisterRemoteControlClientMethod.invoke(audioManager, + remoteControlClient.getActualRemoteControlClientObject()); + } catch (Exception e) { + Log.e(TAG, e.getMessage(), e); + } + } + + + public static void unregisterRemoteControlClient(AudioManager audioManager, + RemoteControlClientCompat remoteControlClient) { + if (!sHasRemoteControlAPIs) { + return; + } + + try { + sUnregisterRemoteControlClientMethod.invoke(audioManager, + remoteControlClient.getActualRemoteControlClientObject()); + } catch (Exception e) { + Log.e(TAG, e.getMessage(), e); + } + } +} + |
