Skip to content
Browse files

Backport the AudioPreview activity from Music2 to Music

Change-Id: I1e1ec54a4613ef60e9a4539d5c50ed0874764b93
  • Loading branch information...
1 parent a51d6fe commit 8d08ec235831d71fdd7f7b6f7757c2bc19528fae Marco Nelissen committed May 10, 2010
Showing with 638 additions and 240 deletions.
  1. +2 −0 Android.mk
  2. +25 −6 AndroidManifest.xml
  3. +3 −0 proguard.flags
  4. +52 −0 res/drawable-finger/progress_horizontal.xml
  5. BIN res/drawable-hdpi-finger/panel_now_playing_progress_bg.9.png
  6. BIN res/drawable-hdpi/btn_playback_ic_pause_small.png
  7. BIN res/drawable-hdpi/btn_playback_ic_play_small.png
  8. BIN res/drawable-hdpi/btn_playback_small_normal.png
  9. BIN res/drawable-hdpi/btn_playback_small_pressed.png
  10. BIN res/drawable-hdpi/btn_playback_small_selected.png
  11. BIN res/drawable-hdpi/now_playing_progress_handle_normal.png
  12. BIN res/drawable-hdpi/now_playing_progress_handle_pressed.png
  13. BIN res/drawable-hdpi/now_playing_progress_handle_selected.png
  14. BIN res/drawable-mdpi-finger/panel_now_playing_progress_bg.9.png
  15. BIN res/drawable-mdpi/btn_playback_ic_pause_small.png
  16. BIN res/drawable-mdpi/btn_playback_ic_play_small.png
  17. BIN res/drawable-mdpi/btn_playback_small_normal.png
  18. BIN res/drawable-mdpi/btn_playback_small_pressed.png
  19. BIN res/drawable-mdpi/btn_playback_small_selected.png
  20. BIN res/drawable-mdpi/now_playing_progress_handle_normal.png
  21. BIN res/drawable-mdpi/now_playing_progress_handle_pressed.png
  22. BIN res/drawable-mdpi/now_playing_progress_handle_selected.png
  23. +21 −0 res/drawable/btn_nowplaying_background_small.xml
  24. +34 −0 res/drawable/seek_thumb.xml
  25. +98 −0 res/layout-finger/audiopreview.xml
  26. +381 −0 src/com/android/music/AudioPreview.java
  27. +1 −2 src/com/android/music/IMediaPlaybackService.aidl
  28. +10 −45 src/com/android/music/MediaPlaybackActivity.java
  29. +11 −66 src/com/android/music/MediaPlaybackService.java
  30. +0 −121 src/com/android/music/StreamStarter.java
View
2 Android.mk
@@ -10,6 +10,8 @@ LOCAL_PACKAGE_NAME := Music
LOCAL_SDK_VERSION := 8
+LOCAL_PROGUARD_FLAGS := -include $(LOCAL_PATH)/proguard.flags
+
include $(BUILD_PACKAGE)
# Use the folloing include to make our test apk.
View
31 AndroidManifest.xml
@@ -72,7 +72,7 @@
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="content"/>
- <data android:scheme="file"/>
+ <data android:host="media"/>
<data android:mimeType="audio/*"/>
<data android:mimeType="application/ogg"/>
<data android:mimeType="application/x-ogg"/>
@@ -84,8 +84,17 @@
</intent-filter>
</activity>
- <activity android:name="com.android.music.StreamStarter"
- android:theme="@android:style/Theme.Dialog" >
+ <activity android:name="AudioPreview" android:theme="@android:style/Theme.Dialog"
+ android:excludeFromRecents="true" >
+ <intent-filter>
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <data android:scheme="file"/>
+ <data android:mimeType="audio/*"/>
+ <data android:mimeType="application/ogg"/>
+ <data android:mimeType="application/x-ogg"/>
+ <data android:mimeType="application/itunes"/>
+ </intent-filter>
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
@@ -96,11 +105,21 @@
<data android:mimeType="audio/mpeg"/>
<data android:mimeType="audio/mp4"/>
<data android:mimeType="audio/mp4a-latm"/>
- <data android:mimeType="application/ogg"/>
- <data android:mimeType="application/x-ogg"/>
- <data android:mimeType="audio/ogg"/>
+ </intent-filter>
+ <intent-filter
+ android:priority="-1">
+ <action android:name="android.intent.action.VIEW" />
+ <category android:name="android.intent.category.DEFAULT" />
+ <category android:name="android.intent.category.BROWSABLE" />
+ <data android:scheme="content" />
+ <data android:mimeType="audio/mp3"/>
+ <data android:mimeType="audio/x-mp3"/>
+ <data android:mimeType="audio/mpeg"/>
+ <data android:mimeType="audio/mp4"/>
+ <data android:mimeType="audio/mp4a-latm"/>
</intent-filter>
</activity>
+
<activity android:name="com.android.music.ArtistAlbumBrowserActivity">
<intent-filter>
<action android:name="android.intent.action.PICK" />
View
3 proguard.flags
@@ -0,0 +1,3 @@
+-keep class com.android.music.AudioPreview {
+ public void playPauseClicked(android.view.View);
+}
View
52 res/drawable-finger/progress_horizontal.xml
@@ -0,0 +1,52 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:id="@android:id/background">
+ <shape>
+ <gradient
+ android:startColor="#3f808080"
+ android:endColor="#7f000000"
+ android:angle="270"
+ />
+ </shape>
+ </item>
+
+ <item android:id="@android:id/secondaryProgress">
+ <clip android:drawable="@drawable/panel_now_playing_progress_bg"/>
+ </item>
+
+ <!-- Can't actually use
+ android:drawable="@drawable/panel_now_playing_progress_bar"
+ here, so fake it with a similar looking gradient.
+ -->
+ <item android:id="@android:id/progress">
+ <clip>
+ <shape>
+ <gradient
+ android:startColor="#ff85d20f"
+ android:endColor="#ff466607"
+ android:angle="270"
+ />
+ </shape>
+ </clip>
+ </item>
+
+
+
+</layer-list>
+
View
BIN res/drawable-hdpi-finger/panel_now_playing_progress_bg.9.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN res/drawable-hdpi/btn_playback_ic_pause_small.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN res/drawable-hdpi/btn_playback_ic_play_small.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN res/drawable-hdpi/btn_playback_small_normal.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN res/drawable-hdpi/btn_playback_small_pressed.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN res/drawable-hdpi/btn_playback_small_selected.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN res/drawable-hdpi/now_playing_progress_handle_normal.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN res/drawable-hdpi/now_playing_progress_handle_pressed.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN res/drawable-hdpi/now_playing_progress_handle_selected.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN res/drawable-mdpi-finger/panel_now_playing_progress_bg.9.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN res/drawable-mdpi/btn_playback_ic_pause_small.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN res/drawable-mdpi/btn_playback_ic_play_small.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN res/drawable-mdpi/btn_playback_small_normal.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN res/drawable-mdpi/btn_playback_small_pressed.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN res/drawable-mdpi/btn_playback_small_selected.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN res/drawable-mdpi/now_playing_progress_handle_normal.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN res/drawable-mdpi/now_playing_progress_handle_pressed.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN res/drawable-mdpi/now_playing_progress_handle_selected.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
21 res/drawable/btn_nowplaying_background_small.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 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/btn_playback_small_pressed" />
+ <item android:state_focused="true" android:state_enabled="true" android:drawable="@drawable/btn_playback_small_selected" />
+ <item android:drawable="@drawable/btn_playback_small_normal" />
+</selector>
View
34 res/drawable/seek_thumb.xml
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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.
+-->
+
+<!-- This is the thumb on the seek bar. -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+
+ <item android:state_pressed="true"
+ android:state_window_focused="true"
+ android:drawable="@drawable/now_playing_progress_handle_pressed" />
+
+ <item android:state_focused="true"
+ android:state_window_focused="true"
+ android:drawable="@drawable/now_playing_progress_handle_selected" />
+
+ <item android:state_selected="true"
+ android:state_window_focused="true"
+ android:drawable="@drawable/now_playing_progress_handle_selected" />
+
+ <item android:drawable="@drawable/now_playing_progress_handle_normal" />
+
+</selector>
View
98 res/layout-finger/audiopreview.xml
@@ -0,0 +1,98 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2008 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="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="10dip">
+
+ <ProgressBar android:id="@+id/spinner"
+ style="?android:attr/progressBarStyleLarge"
+ android:layout_gravity="center"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <TextView android:id="@+id/loading"
+ android:paddingTop="5dip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:textSize="14sp"
+ android:textColor="#ffffffff" />
+
+ <SeekBar android:id="@+id/progress"
+ android:background="#000000f0"
+ android:progressDrawable="@drawable/progress_horizontal"
+ android:thumb="@drawable/seek_thumb"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingLeft="8dip"
+ android:paddingRight="8dip"
+ android:visibility="gone" />
+
+ <LinearLayout
+ android:id="@+id/titleandbuttons"
+ android:orientation="horizontal"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:visibility="gone" >
+
+ <LinearLayout
+ android:orientation="vertical"
+ android:layout_width="0dip"
+ android:layout_weight="1"
+ android:layout_height="wrap_content"
+ android:layout_marginLeft="8dip">
+
+ <TextView android:id="@+id/line1"
+ android:paddingTop="5dip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:textSize="14sp"
+ android:textColor="#ffffffff" />
+
+ <TextView android:id="@+id/line2"
+ android:paddingTop="5dip"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:ellipsize="end"
+ android:textSize="14sp"
+ android:textColor="#ffffffff" />
+ </LinearLayout>
+
+ <LinearLayout
+ android:orientation="horizontal"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content">
+
+ <ImageButton
+ android:id="@+id/playpause"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_vertical"
+ android:layout_marginLeft="16dip"
+ android:background="@drawable/btn_nowplaying_background_small"
+ android:src="@drawable/btn_playback_ic_play_small"
+ android:onClick="playPauseClicked"
+ />
+
+ </LinearLayout>
+ </LinearLayout>
+
+</LinearLayout>
+
View
381 src/com/android/music/AudioPreview.java
@@ -0,0 +1,381 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.music;
+
+import android.app.Activity;
+import android.content.AsyncQueryHandler;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.media.AudioManager.OnAudioFocusChangeListener;
+import android.media.MediaPlayer.OnCompletionListener;
+import android.media.MediaPlayer.OnErrorListener;
+import android.media.MediaPlayer.OnPreparedListener;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Handler;
+import android.provider.MediaStore;
+import android.provider.OpenableColumns;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.KeyEvent;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+import android.widget.ImageButton;
+import android.widget.ProgressBar;
+import android.widget.SeekBar;
+import android.widget.TextView;
+import android.widget.SeekBar.OnSeekBarChangeListener;
+
+import java.io.IOException;
+
+public class AudioPreview extends Activity implements OnPreparedListener, OnErrorListener, OnCompletionListener
+{
+ private final static String TAG = "AudioPreview";
+ private MediaPlayer mPlayer;
+ private TextView mTextLine1;
+ private TextView mTextLine2;
+ private TextView mLoadingText;
+ private SeekBar mSeekBar;
+ private Handler mProgressRefresher;
+ private boolean mSeeking = false;
+ private int mDuration;
+ private Uri mUri;
+ private long mMediaId = -1;
+ private static final int OPEN_IN_MUSIC = 1;
+ private AudioManager mAudioManager;
+ private boolean mPausedByTransientLossOfFocus;
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ Intent intent = getIntent();
+ if (intent == null) {
+ finish();
+ return;
+ }
+ mUri = intent.getData();
+ if (mUri == null) {
+ finish();
+ return;
+ }
+ String scheme = mUri.getScheme();
+
+ setVolumeControlStream(AudioManager.STREAM_MUSIC);
+ requestWindowFeature(Window.FEATURE_NO_TITLE);
+ setContentView(R.layout.audiopreview);
+ getWindow().setLayout(WindowManager.LayoutParams.MATCH_PARENT,
+ WindowManager.LayoutParams.WRAP_CONTENT);
+
+ mTextLine1 = (TextView) findViewById(R.id.line1);
+ mTextLine2 = (TextView) findViewById(R.id.line2);
+ mLoadingText = (TextView) findViewById(R.id.loading);
+ if (scheme.equals("http")) {
+ String msg = getString(R.string.streamloadingtext, mUri.getHost());
+ mLoadingText.setText(msg);
+ } else {
+ mLoadingText.setVisibility(View.GONE);
+ }
+ mSeekBar = (SeekBar) findViewById(R.id.progress);
+ mProgressRefresher = new Handler();
+ mAudioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
+
+ MediaPlayer player = (MediaPlayer) getLastNonConfigurationInstance();
+ boolean prepare = false;
+ if (player == null) {
+ player = new MediaPlayer();
+ prepare = true;
+ } else {
+ mPlayer = player;
+ showPostPrepareUI();
+ }
+ player.setOnPreparedListener(this);
+ player.setOnErrorListener(this);
+ player.setOnCompletionListener(this);
+ if (mPlayer == null) {
+ // mPlayer will be set by the onPrepared callback
+ try {
+ player.setDataSource(this, mUri);
+ player.prepareAsync();
+ } catch (IOException ex) {
+ finish();
+ return;
+ }
+ }
+
+ AsyncQueryHandler mAsyncQueryHandler = new AsyncQueryHandler(getContentResolver()) {
+ @Override
+ protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
+ int titleIdx = cursor.getColumnIndex(MediaStore.Audio.Media.TITLE);
+ int artistIdx = cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST);
+ int idIdx = cursor.getColumnIndex(MediaStore.Audio.Media._ID);
+ int displaynameIdx = cursor.getColumnIndex(OpenableColumns.DISPLAY_NAME);
+ if (cursor.moveToFirst()) {
+
+ if (idIdx >=0) {
+ mMediaId = cursor.getLong(idIdx);
+ }
+
+ if (titleIdx >= 0) {
+ String title = cursor.getString(titleIdx);
+ mTextLine1.setText(title);
+ if (artistIdx >= 0) {
+ String artist = cursor.getString(artistIdx);
+ mTextLine2.setText(artist);
+ }
+ } else if (displaynameIdx >= 0) {
+ String name = cursor.getString(displaynameIdx);
+ mTextLine1.setText(name);
+ } else {
+ // Couldn't find anything to display, what to do now?
+ Log.w(TAG, "Cursor had no names for us");
+ }
+ } else {
+ // What to do here? When would this even happen?
+ Log.w(TAG, "empty cursor");
+ }
+ setNames();
+ }
+ };
+
+ if (scheme.equals(ContentResolver.SCHEME_CONTENT)) {
+ if (mUri.getAuthority() == MediaStore.AUTHORITY) {
+ // try to get title and artist from the media content provider
+ mAsyncQueryHandler.startQuery(0, null, mUri, new String [] {
+ MediaStore.Audio.Media.TITLE, MediaStore.Audio.Media.ARTIST},
+ null, null, null);
+ } else {
+ // try to get the display name from another content provider
+ mAsyncQueryHandler.startQuery(0, null, mUri, new String [] {
+ OpenableColumns.DISPLAY_NAME},
+ null, null, null);
+ }
+ } else if (scheme.equals("file")) {
+ // check if this file is in the media database (clicking on a download
+ // in the download manager might follow this path
+ String path = mUri.getPath();
+ mAsyncQueryHandler.startQuery(0, null, MediaStore.Audio.Media.EXTERNAL_CONTENT_URI,
+ new String [] {MediaStore.Audio.Media._ID,
+ MediaStore.Audio.Media.TITLE, MediaStore.Audio.Media.ARTIST},
+ MediaStore.Audio.Media.DATA + "=?", new String [] {path}, null);
+ } else {
+ // We can't get metadata from the file/stream itself yet, because
+ // that API is hidden, so instead we display the URI being played
+ // (below, in onPrepared)
+ }
+ }
+
+ @Override
+ public Object onRetainNonConfigurationInstance() {
+ MediaPlayer player = mPlayer;
+ mPlayer = null;
+ return player;
+ }
+
+
+ @Override
+ public void onDestroy() {
+ mProgressRefresher.removeCallbacksAndMessages(null);
+ if (mPlayer != null) {
+ mPlayer.release();
+ mAudioManager.abandonAudioFocus(mAudioFocusListener);
+ }
+ super.onDestroy();
+ }
+
+ public void onPrepared(MediaPlayer mp) {
+ if (isFinishing()) return;
+ mPlayer = mp;
+ start();
+ setNames();
+ showPostPrepareUI();
+ }
+
+ private void showPostPrepareUI() {
+ ProgressBar pb = (ProgressBar) findViewById(R.id.spinner);
+ pb.setVisibility(View.GONE);
+ mSeekBar.setVisibility(View.VISIBLE);
+ mDuration = mPlayer.getDuration();
+ mSeekBar.setMax(mDuration);
+ mSeekBar.setOnSeekBarChangeListener(mSeekListener);
+ mLoadingText.setVisibility(View.GONE);
+ View v = findViewById(R.id.titleandbuttons);
+ v.setVisibility(View.VISIBLE);
+ start(); // because it requests audio focus
+ updatePlayPause();
+ }
+
+ private OnAudioFocusChangeListener mAudioFocusListener = new OnAudioFocusChangeListener() {
+ public void onAudioFocusChange(int focusChange) {
+ if (mPlayer == null) {
+ // this activity has handed its MediaPlayer off to the next activity
+ // (e.g. portrait/landscape switch) and should abandon its focus
+ mAudioManager.abandonAudioFocus(this);
+ return;
+ }
+ switch (focusChange) {
+ case AudioManager.AUDIOFOCUS_LOSS:
+ mPausedByTransientLossOfFocus = false;
+ mPlayer.pause();
+ break;
+ case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
+ case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK:
+ if (mPlayer.isPlaying()) {
+ mPausedByTransientLossOfFocus = true;
+ mPlayer.pause();
+ }
+ break;
+ case AudioManager.AUDIOFOCUS_GAIN:
+ if (mPausedByTransientLossOfFocus) {
+ mPausedByTransientLossOfFocus = false;
+ start();
+ }
+ break;
+ }
+ updatePlayPause();
+ }
+ };
+
+ private void start() {
+ mAudioManager.requestAudioFocus(mAudioFocusListener, AudioManager.STREAM_MUSIC,
+ AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
+ mPlayer.start();
+ mProgressRefresher.postDelayed(new ProgressRefresher(), 200);
+ }
+
+ public void setNames() {
+ if (TextUtils.isEmpty(mTextLine1.getText())) {
+ mTextLine1.setText(mUri.getLastPathSegment());
+ }
+ if (TextUtils.isEmpty(mTextLine2.getText())) {
+ mTextLine2.setVisibility(View.GONE);
+ } else {
+ mTextLine2.setVisibility(View.VISIBLE);
+ }
+ }
+
+ class ProgressRefresher implements Runnable {
+
+ public void run() {
+ if (!mSeeking) {
+ int progress = mPlayer.getCurrentPosition() / mDuration;
+ mSeekBar.setProgress(mPlayer.getCurrentPosition());
+ }
+ mProgressRefresher.removeCallbacksAndMessages(null);
+ mProgressRefresher.postDelayed(new ProgressRefresher(), 200);
+ }
+ }
+
+ private void updatePlayPause() {
+ ImageButton b = (ImageButton) findViewById(R.id.playpause);
+ if (b != null) {
+ if (mPlayer.isPlaying()) {
+ b.setImageResource(R.drawable.btn_playback_ic_pause_small);
+ } else {
+ b.setImageResource(R.drawable.btn_playback_ic_play_small);
+ mProgressRefresher.removeCallbacksAndMessages(null);
+ }
+ }
+ }
+
+ private OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
+ public void onStartTrackingTouch(SeekBar bar) {
+ mSeeking = true;
+ }
+ public void onProgressChanged(SeekBar bar, int progress, boolean fromuser) {
+ if (!fromuser) {
+ return;
+ }
+ mPlayer.seekTo(progress);
+ }
+ public void onStopTrackingTouch(SeekBar bar) {
+ mSeeking = false;
+ }
+ };
+
+ public boolean onError(MediaPlayer mp, int what, int extra) {
+ finish();
+ return true;
+ }
+
+ public void onCompletion(MediaPlayer mp) {
+ mSeekBar.setProgress(mDuration);
+ updatePlayPause();
+ }
+
+ public void playPauseClicked(View v) {
+ if (mPlayer.isPlaying()) {
+ mPlayer.pause();
+ } else {
+ start();
+ }
+ updatePlayPause();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+ // TODO: if mMediaId != -1, then the playing file has an entry in the media
+ // database, and we could open it in the full music app instead.
+ // Ideally, we would hand off the currently running mediaplayer
+ // to the music UI, which can probably be done via a public static
+ menu.add(0, OPEN_IN_MUSIC, 0, "open in music");
+ return true;
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ MenuItem item = menu.findItem(OPEN_IN_MUSIC);
+ if (mMediaId >= 0) {
+ item.setVisible(true);
+ return true;
+ }
+ item.setVisible(false);
+ return false;
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_HEADSETHOOK:
+ case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
+ if (mPlayer.isPlaying()) {
+ mPlayer.pause();
+ } else {
+ start();
+ }
+ updatePlayPause();
+ return true;
+ case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
+ case KeyEvent.KEYCODE_MEDIA_NEXT:
+ case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
+ case KeyEvent.KEYCODE_MEDIA_REWIND:
+ return true;
+ case KeyEvent.KEYCODE_MEDIA_STOP:
+ finish();
+ return true;
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+}
View
3 src/com/android/music/IMediaPlaybackService.aidl
@@ -21,8 +21,7 @@ import android.graphics.Bitmap;
interface IMediaPlaybackService
{
- void openFile(String path, boolean oneShot);
- void openFileAsync(String path);
+ void openFile(String path);
void open(in long [] list, int position);
int getQueuePosition();
boolean isPlaying();
View
55 src/com/android/music/MediaPlaybackActivity.java
@@ -68,8 +68,7 @@
View.OnTouchListener, View.OnLongClickListener
{
private static final int USE_AS_RINGTONE = CHILD_MENU_BASE;
-
- private boolean mOneShot = false;
+
private boolean mSeeking = false;
private boolean mDeviceHasDpad;
private long mStartSeekPos = 0;
@@ -150,12 +149,6 @@ public void onCreate(Bundle icicle)
seeker.setOnSeekBarChangeListener(mSeekListener);
}
mProgress.setMax(1000);
-
- if (icicle != null) {
- mOneShot = icicle.getBoolean("oneshot");
- } else {
- mOneShot = getIntent().getBooleanExtra("oneshot", false);
- }
mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop();
}
@@ -466,12 +459,6 @@ public void onRepeat(View v, long howlong, int repcnt) {
@Override
public void onStop() {
paused = true;
- if (mService != null && mOneShot && getChangingConfigurations() == 0) {
- try {
- mService.stop();
- } catch (RemoteException ex) {
- }
- }
mHandler.removeMessages(REFRESH);
unregisterReceiver(mStatusListener);
MusicUtils.unbindFromService(mToken);
@@ -480,12 +467,6 @@ public void onStop() {
}
@Override
- public void onSaveInstanceState(Bundle outState) {
- outState.putBoolean("oneshot", mOneShot);
- super.onSaveInstanceState(outState);
- }
-
- @Override
public void onStart() {
super.onStart();
paused = false;
@@ -509,7 +490,6 @@ public void onStart() {
@Override
public void onNewIntent(Intent intent) {
setIntent(intent);
- mOneShot = intent.getBooleanExtra("oneshot", false);
}
@Override
@@ -534,7 +514,7 @@ public boolean onCreateOptionsMenu(Menu menu) {
// if we're in one shot mode. In most cases, these menu items are not
// useful in those modes, so for consistency we never show them in these
// modes, instead of tailoring them to the specific file being played.
- if (MusicUtils.getCurrentAudioId() >= 0 && !mOneShot) {
+ if (MusicUtils.getCurrentAudioId() >= 0) {
menu.add(0, GOTO_START, 0, R.string.goto_start).setIcon(R.drawable.ic_menu_music_library);
menu.add(0, PARTY_SHUFFLE, 0, R.string.party_shuffle); // icon will be set in onPrepareOptionsMenu()
SubMenu sub = menu.addSubMenu(0, ADD_TO_PLAYLIST, 0,
@@ -1043,12 +1023,8 @@ private void startPlayback() {
filename = uri.toString();
}
try {
- if (! ContentResolver.SCHEME_CONTENT.equals(scheme) ||
- ! MediaStore.AUTHORITY.equals(uri.getAuthority())) {
- mOneShot = true;
- }
mService.stop();
- mService.openFile(filename, mOneShot);
+ mService.openFile(filename);
mService.play();
setIntent(new Intent());
} catch (Exception ex) {
@@ -1071,17 +1047,11 @@ public void onServiceConnected(ComponentName classname, IBinder obj) {
if (mService.getAudioId() >= 0 || mService.isPlaying() ||
mService.getPath() != null) {
// something is playing now, we're done
- if (mOneShot || mService.getAudioId() < 0) {
- mRepeatButton.setVisibility(View.INVISIBLE);
- mShuffleButton.setVisibility(View.INVISIBLE);
- mQueueButton.setVisibility(View.INVISIBLE);
- } else {
- mRepeatButton.setVisibility(View.VISIBLE);
- mShuffleButton.setVisibility(View.VISIBLE);
- mQueueButton.setVisibility(View.VISIBLE);
- setRepeatButtonImage();
- setShuffleButtonImage();
- }
+ mRepeatButton.setVisibility(View.VISIBLE);
+ mShuffleButton.setVisibility(View.VISIBLE);
+ mQueueButton.setVisibility(View.VISIBLE);
+ setRepeatButtonImage();
+ setShuffleButtonImage();
setPauseButtonImage();
return;
}
@@ -1253,13 +1223,8 @@ public void onReceive(Context context, Intent intent) {
updateTrackInfo();
setPauseButtonImage();
queueNextRefresh(1);
- } else if (action.equals(MediaPlaybackService.PLAYBACK_COMPLETE)) {
- if (mOneShot) {
- finish();
- } else {
- setPauseButtonImage();
- }
- } else if (action.equals(MediaPlaybackService.PLAYSTATE_CHANGED)) {
+ } else if (action.equals(MediaPlaybackService.PLAYBACK_COMPLETE) ||
+ action.equals(MediaPlaybackService.PLAYSTATE_CHANGED)) {
setPauseButtonImage();
}
}
View
77 src/com/android/music/MediaPlaybackService.java
@@ -82,7 +82,6 @@
public static final String META_CHANGED = "com.android.music.metachanged";
public static final String QUEUE_CHANGED = "com.android.music.queuechanged";
public static final String PLAYBACK_COMPLETE = "com.android.music.playbackcomplete";
- public static final String ASYNC_OPEN_COMPLETE = "com.android.music.asyncopencomplete";
public static final String SERVICECMD = "com.android.music.musicservicecommand";
public static final String CMDNAME = "command";
@@ -109,7 +108,6 @@
private int mRepeatMode = REPEAT_NONE;
private int mMediaMountedCount = 0;
private long [] mAutoShuffleList = null;
- private boolean mOneShot;
private long [] mPlayList = null;
private int mPlayListLen = 0;
private Vector<Integer> mHistory = new Vector<Integer>(MAX_HISTORY_SIZE);
@@ -140,6 +138,7 @@
private boolean mIsSupposedToBePlaying = false;
private boolean mQuietMode = false;
private AudioManager mAudioManager;
+ private boolean mQueueIsSaveable = true;
// used to track what type of audio focus loss caused the playback to pause
private boolean mPausedByTransientLossOfFocus = false;
@@ -194,11 +193,8 @@ public void handleMessage(Message msg) {
if (mRepeatMode == REPEAT_CURRENT) {
seek(0);
play();
- } else if (!mOneShot) {
- next(false);
} else {
- notifyChange(PLAYBACK_COMPLETE);
- mIsSupposedToBePlaying = false;
+ next(false);
}
break;
case RELEASE_WAKELOCK:
@@ -353,9 +349,10 @@ public void onDestroy() {
};
private void saveQueue(boolean full) {
- if (mOneShot) {
+ if (!mQueueIsSaveable) {
return;
}
+
Editor ed = mPreferences.edit();
//long start = System.currentTimeMillis();
if (full) {
@@ -692,14 +689,13 @@ public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (action.equals(Intent.ACTION_MEDIA_EJECT)) {
saveQueue(true);
- mOneShot = true; // This makes us not save the state again later,
- // which would be wrong because the song ids and
- // card id might not match.
+ mQueueIsSaveable = false;
closeExternalStorageFiles(intent.getData().getPath());
} else if (action.equals(Intent.ACTION_MEDIA_MOUNTED)) {
mMediaMountedCount++;
mCardId = MusicUtils.getCardId(MediaPlaybackService.this);
reloadQueue();
+ mQueueIsSaveable = true;
notifyChange(QUEUE_CHANGED);
notifyChange(META_CHANGED);
}
@@ -933,6 +929,7 @@ private void openCurrent() {
mCursor.close();
mCursor = null;
}
+
if (mPlayListLen == 0) {
return;
}
@@ -945,7 +942,7 @@ private void openCurrent() {
mCursorCols, "_id=" + id , null, null);
if (mCursor != null) {
mCursor.moveToFirst();
- open(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI + "/" + id, false);
+ open(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI + "/" + id);
// go to bookmark if needed
if (isPodcast()) {
long bookmark = getBookmark();
@@ -957,44 +954,17 @@ private void openCurrent() {
}
}
- public void openAsync(String path) {
- synchronized (this) {
- if (path == null) {
- return;
- }
-
- mRepeatMode = REPEAT_NONE;
- ensurePlayListCapacity(1);
- mPlayListLen = 1;
- mPlayPos = -1;
-
- mFileToPlay = path;
- mCursor = null;
- mPlayer.setDataSourceAsync(mFileToPlay);
- mOneShot = true;
- }
- }
-
/**
* Opens the specified file and readies it for playback.
*
* @param path The full path of the file to be opened.
- * @param oneshot when set to true, playback will stop after this file completes, instead
- * of moving on to the next track in the list
*/
- public void open(String path, boolean oneshot) {
+ public void open(String path) {
synchronized (this) {
if (path == null) {
return;
}
- if (oneshot) {
- mRepeatMode = REPEAT_NONE;
- ensurePlayListCapacity(1);
- mPlayListLen = 1;
- mPlayPos = -1;
- }
-
// if mCursor is null, try to associate path with a database cursor
if (mCursor == null) {
@@ -1031,7 +1001,6 @@ public void open(String path, boolean oneshot) {
}
mFileToPlay = path;
mPlayer.setDataSource(mFileToPlay);
- mOneShot = oneshot;
if (! mPlayer.isInitialized()) {
stop(true);
if (mOpenFailedCounter++ < 10 && mPlayListLen > 1) {
@@ -1190,12 +1159,6 @@ public boolean isPlaying() {
public void prev() {
synchronized (this) {
- if (mOneShot) {
- // we were playing a specific file not part of a playlist, so there is no 'previous'
- seek(0);
- play();
- return;
- }
if (mShuffleMode == SHUFFLE_NORMAL) {
// go to previously-played track and remove it from the history
int histsize = mHistory.size();
@@ -1222,13 +1185,6 @@ public void prev() {
public void next(boolean force) {
synchronized (this) {
- if (mOneShot) {
- // we were playing a specific file not part of a playlist, so there is no 'next'
- seek(0);
- play();
- return;
- }
-
if (mPlayListLen <= 0) {
Log.d(LOGTAG, "No play queue");
return;
@@ -1706,7 +1662,6 @@ public void setDataSourceAsync(String path) {
mMediaPlayer.reset();
mMediaPlayer.setDataSource(path);
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
- mMediaPlayer.setOnPreparedListener(preparedlistener);
mMediaPlayer.prepareAsync();
} catch (IOException ex) {
// TODO: notify the user why the file couldn't be opened
@@ -1792,12 +1747,6 @@ public void onCompletion(MediaPlayer mp) {
}
};
- MediaPlayer.OnPreparedListener preparedlistener = new MediaPlayer.OnPreparedListener() {
- public void onPrepared(MediaPlayer mp) {
- notifyChange(ASYNC_OPEN_COMPLETE);
- }
- };
-
MediaPlayer.OnErrorListener errorListener = new MediaPlayer.OnErrorListener() {
public boolean onError(MediaPlayer mp, int what, int extra) {
switch (what) {
@@ -1849,13 +1798,9 @@ public void setVolume(float vol) {
mService = new WeakReference<MediaPlaybackService>(service);
}
- public void openFileAsync(String path)
- {
- mService.get().openAsync(path);
- }
- public void openFile(String path, boolean oneShot)
+ public void openFile(String path)
{
- mService.get().open(path, oneShot);
+ mService.get().open(path);
}
public void open(long [] list, int position) {
mService.get().open(list, position);
View
121 src/com/android/music/StreamStarter.java
@@ -1,121 +0,0 @@
-/*
- * Copyright (C) 2008 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.music;
-
-import com.android.music.MusicUtils.ServiceToken;
-
-import android.app.Activity;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.ServiceConnection;
-import android.media.AudioManager;
-import android.net.Uri;
-import android.os.Bundle;
-import android.os.IBinder;
-import android.os.RemoteException;
-import android.view.Window;
-import android.widget.TextView;
-import android.widget.Toast;
-
-public class StreamStarter extends Activity
-{
- private ServiceToken mToken;
-
- @Override
- public void onCreate(Bundle icicle) {
- super.onCreate(icicle);
- setVolumeControlStream(AudioManager.STREAM_MUSIC);
-
- requestWindowFeature(Window.FEATURE_NO_TITLE);
- setContentView(R.layout.streamstarter);
-
- TextView tv = (TextView) findViewById(R.id.streamloading);
-
- Uri uri = getIntent().getData();
- String msg = getString(R.string.streamloadingtext, uri.getHost());
- tv.setText(msg);
- }
-
- @Override
- public void onResume() {
- super.onResume();
-
- mToken = MusicUtils.bindToService(this, new ServiceConnection() {
- public void onServiceConnected(ComponentName classname, IBinder obj) {
- try {
- IntentFilter f = new IntentFilter();
- f.addAction(MediaPlaybackService.ASYNC_OPEN_COMPLETE);
- f.addAction(MediaPlaybackService.PLAYBACK_COMPLETE);
- registerReceiver(mStatusListener, new IntentFilter(f));
- MusicUtils.sService.openFileAsync(getIntent().getData().toString());
- } catch (RemoteException ex) {
- }
- }
-
- public void onServiceDisconnected(ComponentName classname) {
- }
- });
- }
-
- private BroadcastReceiver mStatusListener = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- String action = intent.getAction();
- if (action.equals(MediaPlaybackService.PLAYBACK_COMPLETE)) {
- // You would come here only in case of a failure in the
- // MediaPlayerService before PrepareAsync completes
- String msg = getString(R.string.fail_to_start_stream);
- Toast mt = Toast.makeText(StreamStarter.this, msg, Toast.LENGTH_SHORT);
- mt.show();
- finish();
- return;
- }
- try {
- MusicUtils.sService.play();
- intent = new Intent("com.android.music.PLAYBACK_VIEWER");
- intent.putExtra("oneshot", true);
- startActivity(intent);
- } catch (RemoteException ex) {
- }
- finish();
- }
- };
-
- @Override
- public void onPause() {
- if (MusicUtils.sService != null) {
- try {
- // This looks a little weird (when it's not playing, stop playing)
- // but it is correct. When nothing is playing, it means that this
- // was paused before a connection was established, in which case
- // we stop trying to connect/play.
- // Otherwise, this call to onPause() was a result of the call to
- // finish() above, and we should let playback continue.
- if (! MusicUtils.sService.isPlaying()) {
- MusicUtils.sService.stop();
- }
- } catch (RemoteException ex) {
- }
- }
- unregisterReceiver(mStatusListener);
- MusicUtils.unbindFromService(mToken);
- super.onPause();
- }
-}

0 comments on commit 8d08ec2

Please sign in to comment.
Something went wrong with that request. Please try again.