summaryrefslogtreecommitdiff
path: root/core/src/main/java/com/aocate/media/MediaPlayer.java
diff options
context:
space:
mode:
authordaniel oeh <daniel.oeh@gmail.com>2014-10-24 20:40:07 +0200
committerdaniel oeh <daniel.oeh@gmail.com>2014-10-24 20:40:07 +0200
commitcc052e91ad8a87b00b93649ec0f6a06bcae6267a (patch)
tree12cacac4fb5c94af2955812a3167eefb325f286d /core/src/main/java/com/aocate/media/MediaPlayer.java
parentbaa7d5f11283cb7668d45b561af5d38f0ccb9632 (diff)
parentb5066d02b4acf31da093190a1a57a9d961bb04ca (diff)
downloadAntennaPod-cc052e91ad8a87b00b93649ec0f6a06bcae6267a.zip
Merge branch 'migration' into develop
Non-GUI classes have been moved into the 'core' project in order to allow AntennaPod SP to reference it as a subproject. Conflicts: app/src/main/AndroidManifest.xml build.gradle core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSSimpleChapters.java core/src/main/java/de/danoeh/antennapod/core/util/ChapterUtils.java gradle/wrapper/gradle-wrapper.properties pom.xml
Diffstat (limited to 'core/src/main/java/com/aocate/media/MediaPlayer.java')
-rw-r--r--core/src/main/java/com/aocate/media/MediaPlayer.java1278
1 files changed, 1278 insertions, 0 deletions
diff --git a/core/src/main/java/com/aocate/media/MediaPlayer.java b/core/src/main/java/com/aocate/media/MediaPlayer.java
new file mode 100644
index 000000000..c73c5219e
--- /dev/null
+++ b/core/src/main/java/com/aocate/media/MediaPlayer.java
@@ -0,0 +1,1278 @@
+// Copyright 2011, Aocate, Inc.
+//
+// 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.aocate.media;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.media.AudioManager;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Handler.Callback;
+import android.os.IBinder;
+import android.os.Message;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.concurrent.locks.ReentrantLock;
+
+import de.danoeh.antennapod.core.BuildConfig;
+
+public class MediaPlayer {
+ public interface OnBufferingUpdateListener {
+ public abstract void onBufferingUpdate(MediaPlayer arg0, int percent);
+ }
+
+ public interface OnCompletionListener {
+ public abstract void onCompletion(MediaPlayer arg0);
+ }
+
+ public interface OnErrorListener {
+ public abstract boolean onError(MediaPlayer arg0, int what, int extra);
+ }
+
+ public interface OnInfoListener {
+ public abstract boolean onInfo(MediaPlayer arg0, int what, int extra);
+ }
+
+ public interface OnPitchAdjustmentAvailableChangedListener {
+ /**
+ * @param arg0 The owning media player
+ * @param pitchAdjustmentAvailable True if pitch adjustment is available, false if not
+ */
+ public abstract void onPitchAdjustmentAvailableChanged(
+ MediaPlayer arg0, boolean pitchAdjustmentAvailable);
+ }
+
+ public interface OnPreparedListener {
+ public abstract void onPrepared(MediaPlayer arg0);
+ }
+
+ public interface OnSeekCompleteListener {
+ public abstract void onSeekComplete(MediaPlayer arg0);
+ }
+
+ public interface OnSpeedAdjustmentAvailableChangedListener {
+ /**
+ * @param arg0 The owning media player
+ * @param speedAdjustmentAvailable True if speed adjustment is available, false if not
+ */
+ public abstract void onSpeedAdjustmentAvailableChanged(
+ MediaPlayer arg0, boolean speedAdjustmentAvailable);
+ }
+
+ public enum State {
+ IDLE, INITIALIZED, PREPARED, STARTED, PAUSED, STOPPED, PREPARING, PLAYBACK_COMPLETED, END, ERROR
+ }
+
+ private static Uri SPEED_ADJUSTMENT_MARKET_URI = Uri
+ .parse("market://details?id=com.aocate.presto");
+
+ private static Intent prestoMarketIntent = null;
+
+ public static final int MEDIA_ERROR_SERVER_DIED = android.media.MediaPlayer.MEDIA_ERROR_SERVER_DIED;
+ public static final int MEDIA_ERROR_UNKNOWN = android.media.MediaPlayer.MEDIA_ERROR_UNKNOWN;
+ public static final int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK = android.media.MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK;
+
+ /**
+ * Indicates whether the specified action can be used as an intent. This
+ * method queries the package manager for installed packages that can
+ * respond to an intent with the specified action. If no suitable package is
+ * found, this method returns false.
+ *
+ * @param context The application's environment.
+ * @param action The Intent action to check for availability.
+ * @return True if an Intent with the specified action can be sent and
+ * responded to, false otherwise.
+ */
+ public static boolean isIntentAvailable(Context context, String action) {
+ final PackageManager packageManager = context.getPackageManager();
+ final Intent intent = new Intent(action);
+ List<ResolveInfo> list = packageManager.queryIntentServices(intent,
+ PackageManager.MATCH_DEFAULT_ONLY);
+ return list.size() > 0;
+ }
+
+ /**
+ * Indicates whether the Presto library is installed
+ *
+ * @param context The context to use to query the package manager.
+ * @return True if the Presto library is installed, false if not.
+ */
+ public static boolean isPrestoLibraryInstalled(Context context) {
+ return isIntentAvailable(context, ServiceBackedMediaPlayer.INTENT_NAME);
+ }
+
+ /**
+ * Return an Intent that opens the Android Market page for the speed
+ * alteration library
+ *
+ * @return The Intent for the Presto library on the Android Market
+ */
+ public static Intent getPrestoMarketIntent() {
+ if (prestoMarketIntent == null) {
+ prestoMarketIntent = new Intent(Intent.ACTION_VIEW,
+ SPEED_ADJUSTMENT_MARKET_URI);
+ }
+ return prestoMarketIntent;
+ }
+
+ /**
+ * Open the Android Market page for the Presto library
+ *
+ * @param context The context from which to open the Android Market page
+ */
+ public static void openPrestoMarketIntent(Context context) {
+ context.startActivity(getPrestoMarketIntent());
+ }
+
+ private static final String MP_TAG = "AocateReplacementMediaPlayer";
+
+ private static final double PITCH_STEP_CONSTANT = 1.0594630943593;
+
+ private AndroidMediaPlayer amp = null;
+ // This is whether speed adjustment should be enabled (by the Service)
+ // To avoid the Service entirely, set useService to false
+ protected boolean enableSpeedAdjustment = true;
+ private int lastKnownPosition = 0;
+ // In some cases, we're going to have to replace the
+ // android.media.MediaPlayer on the fly, and we don't want to touch the
+ // wrong media player, so lock it way too much.
+ ReentrantLock lock = new ReentrantLock();
+ private int mAudioStreamType = AudioManager.STREAM_MUSIC;
+ private Context mContext;
+ private boolean mIsLooping = false;
+ private float mLeftVolume = 1f;
+ private float mPitchStepsAdjustment = 0f;
+ private float mRightVolume = 1f;
+ private float mSpeedMultiplier = 1f;
+ private int mWakeMode = 0;
+ MediaPlayerImpl mpi = null;
+ protected boolean pitchAdjustmentAvailable = false;
+ private ServiceBackedMediaPlayer sbmp = null;
+ protected boolean speedAdjustmentAvailable = false;
+
+ private Handler mServiceDisconnectedHandler = null;
+
+ // Some parts of state cannot be found by calling MediaPlayerImpl functions,
+ // so store our own state. This also helps copy state when changing
+ // implementations
+ State state = State.INITIALIZED;
+ String stringDataSource = null;
+ Uri uriDataSource = null;
+ private boolean useService = false;
+
+ // Naming Convention for Listeners
+ // Most listeners can both be set by clients and called by MediaPlayImpls
+ // There are a few that have to do things in this class as well as calling
+ // the function. In all cases, onX is what is called by MediaPlayerImpl
+ // If there is work to be done in this class, then the listener that is
+ // set by setX is X (with the first letter lowercase).
+ OnBufferingUpdateListener onBufferingUpdateListener = null;
+ OnCompletionListener onCompletionListener = null;
+ OnErrorListener onErrorListener = null;
+ OnInfoListener onInfoListener = null;
+
+ // Special case. Pitch adjustment ceases to be available when we switch
+ // to the android.media.MediaPlayer (though it is not guaranteed to be
+ // available when using the ServiceBackedMediaPlayer)
+ OnPitchAdjustmentAvailableChangedListener onPitchAdjustmentAvailableChangedListener = new OnPitchAdjustmentAvailableChangedListener() {
+ public void onPitchAdjustmentAvailableChanged(MediaPlayer arg0,
+ boolean pitchAdjustmentAvailable) {
+ lock.lock();
+ try {
+ Log
+ .d(
+ MP_TAG,
+ "onPitchAdjustmentAvailableChangedListener.onPitchAdjustmentAvailableChanged being called");
+ if (MediaPlayer.this.pitchAdjustmentAvailable != pitchAdjustmentAvailable) {
+ Log.d(MP_TAG, "Pitch adjustment state has changed from "
+ + MediaPlayer.this.pitchAdjustmentAvailable
+ + " to " + pitchAdjustmentAvailable);
+ MediaPlayer.this.pitchAdjustmentAvailable = pitchAdjustmentAvailable;
+ if (MediaPlayer.this.pitchAdjustmentAvailableChangedListener != null) {
+ MediaPlayer.this.pitchAdjustmentAvailableChangedListener
+ .onPitchAdjustmentAvailableChanged(arg0,
+ pitchAdjustmentAvailable);
+ }
+ }
+ } finally {
+ lock.unlock();
+ }
+ }
+ };
+ OnPitchAdjustmentAvailableChangedListener pitchAdjustmentAvailableChangedListener = null;
+
+ MediaPlayer.OnPreparedListener onPreparedListener = new MediaPlayer.OnPreparedListener() {
+ public void onPrepared(MediaPlayer arg0) {
+ Log.d(MP_TAG, "onPreparedListener 242 setting state to PREPARED");
+ MediaPlayer.this.state = State.PREPARED;
+ if (MediaPlayer.this.preparedListener != null) {
+ Log.d(MP_TAG, "Calling preparedListener");
+ MediaPlayer.this.preparedListener.onPrepared(arg0);
+ }
+ Log.d(MP_TAG, "Wrap up onPreparedListener");
+ }
+ };
+
+ OnPreparedListener preparedListener = null;
+ OnSeekCompleteListener onSeekCompleteListener = null;
+
+ // Special case. Speed adjustment ceases to be available when we switch
+ // to the android.media.MediaPlayer (though it is not guaranteed to be
+ // available when using the ServiceBackedMediaPlayer)
+ OnSpeedAdjustmentAvailableChangedListener onSpeedAdjustmentAvailableChangedListener = new OnSpeedAdjustmentAvailableChangedListener() {
+ public void onSpeedAdjustmentAvailableChanged(MediaPlayer arg0,
+ boolean speedAdjustmentAvailable) {
+ lock.lock();
+ try {
+ Log
+ .d(
+ MP_TAG,
+ "onSpeedAdjustmentAvailableChangedListener.onSpeedAdjustmentAvailableChanged being called");
+ if (MediaPlayer.this.speedAdjustmentAvailable != speedAdjustmentAvailable) {
+ Log.d(MP_TAG, "Speed adjustment state has changed from "
+ + MediaPlayer.this.speedAdjustmentAvailable
+ + " to " + speedAdjustmentAvailable);
+ MediaPlayer.this.speedAdjustmentAvailable = speedAdjustmentAvailable;
+ if (MediaPlayer.this.speedAdjustmentAvailableChangedListener != null) {
+ MediaPlayer.this.speedAdjustmentAvailableChangedListener
+ .onSpeedAdjustmentAvailableChanged(arg0,
+ speedAdjustmentAvailable);
+ }
+ }
+ } finally {
+ lock.unlock();
+ }
+ }
+ };
+ OnSpeedAdjustmentAvailableChangedListener speedAdjustmentAvailableChangedListener = null;
+
+ private int speedAdjustmentAlgorithm = SpeedAdjustmentAlgorithm.SONIC;
+
+ public MediaPlayer(final Context context) {
+ this(context, true);
+ }
+
+ public MediaPlayer(final Context context, boolean useService) {
+ this.mContext = context;
+ this.useService = useService;
+
+ // So here's the major problem
+ // Sometimes the service won't exist or won't be connected,
+ // so start with an android.media.MediaPlayer, and when
+ // the service is connected, use that from then on
+ this.mpi = this.amp = new AndroidMediaPlayer(this, context);
+
+ // setupMpi will go get the Service, if it can, then bring that
+ // implementation into sync
+ Log.d(MP_TAG, "setupMpi");
+ setupMpi(context);
+ }
+
+ private boolean invalidServiceConnectionConfiguration() {
+ if (!(this.mpi instanceof ServiceBackedMediaPlayer)) {
+ if (this.useService && isPrestoLibraryInstalled()) {
+ // In this case, the Presto library has been installed
+ // or something while playing sound
+ // We could be using the service, but we're not
+ Log.d(MP_TAG, "We could be using the service, but we're not 316");
+ return true;
+ }
+ // If useService is false, then we shouldn't be using the SBMP
+ // If the Presto library isn't installed, ditto
+ Log.d(MP_TAG, "this.mpi is not a ServiceBackedMediaPlayer, but we couldn't use it anyway 321");
+ return false;
+ } else {
+ if (BuildConfig.DEBUG && !(this.mpi instanceof ServiceBackedMediaPlayer))
+ throw new AssertionError();
+ if (this.useService && isPrestoLibraryInstalled()) {
+ // We should be using the service, and we are. Great!
+ Log.d(MP_TAG, "We could be using a ServiceBackedMediaPlayer and we are 327");
+ return false;
+ }
+ // We're trying to use the service when we shouldn't,
+ // that's an invalid configuration
+ Log.d(MP_TAG, "We're trying to use a ServiceBackedMediaPlayer but we shouldn't be 332");
+ return true;
+ }
+ }
+
+ private void setupMpi(final Context context) {
+ lock.lock();
+ try {
+ Log.d(MP_TAG, "setupMpi 336");
+ // Check if the client wants to use the service at all,
+ // then if we're already using the right kind of media player
+ if (this.useService && isPrestoLibraryInstalled()) {
+ if ((this.mpi != null)
+ && (this.mpi instanceof ServiceBackedMediaPlayer)) {
+ Log.d(MP_TAG, "Already using ServiceBackedMediaPlayer");
+ return;
+ }
+ if (this.sbmp == null) {
+ Log.d(MP_TAG, "Instantiating new ServiceBackedMediaPlayer 346");
+ this.sbmp = new ServiceBackedMediaPlayer(this, context,
+ new ServiceConnection() {
+ public void onServiceConnected(
+ ComponentName className,
+ final IBinder service) {
+ Thread t = new Thread(new Runnable() {
+ public void run() {
+ // This lock probably isn't granular
+ // enough
+ MediaPlayer.this.lock.lock();
+ Log.d(MP_TAG,
+ "onServiceConnected 257");
+ try {
+ MediaPlayer.this
+ .switchMediaPlayerImpl(
+ MediaPlayer.this.amp,
+ MediaPlayer.this.sbmp);
+ Log.d(MP_TAG, "End onServiceConnected 362");
+ } finally {
+ MediaPlayer.this.lock.unlock();
+ }
+ }
+ });
+ t.start();
+ }
+
+ public void onServiceDisconnected(
+ ComponentName className) {
+ MediaPlayer.this.lock.lock();
+ try {
+ // Can't get any more useful information
+ // out of sbmp
+ if (MediaPlayer.this.sbmp != null) {
+ MediaPlayer.this.sbmp.release();
+ }
+ // Unlike most other cases, sbmp gets set
+ // to null since there's nothing useful
+ // backing it now
+ MediaPlayer.this.sbmp = null;
+
+ if (mServiceDisconnectedHandler == null) {
+ mServiceDisconnectedHandler = new Handler(new Callback() {
+ public boolean handleMessage(Message msg) {
+ // switchMediaPlayerImpl won't try to
+ // clone anything from null
+ lock.lock();
+ try {
+ if (MediaPlayer.this.amp == null) {
+ // This should never be in this state
+ MediaPlayer.this.amp = new AndroidMediaPlayer(
+ MediaPlayer.this,
+ MediaPlayer.this.mContext);
+ }
+ // Use sbmp instead of null in case by some miracle it's
+ // been restored in the meantime
+ MediaPlayer.this.switchMediaPlayerImpl(
+ MediaPlayer.this.sbmp,
+ MediaPlayer.this.amp);
+ return true;
+ } finally {
+ lock.unlock();
+ }
+ }
+ });
+ }
+
+ // This code needs to execute on the
+ // original thread to instantiate
+ // the new object in the right place
+ mServiceDisconnectedHandler
+ .sendMessage(
+ mServiceDisconnectedHandler
+ .obtainMessage());
+ // Note that we do NOT want to set
+ // useService. useService is about
+ // what the user wants, not what they
+ // get
+ } finally {
+ MediaPlayer.this.lock.unlock();
+ }
+ }
+ }
+ );
+ }
+ switchMediaPlayerImpl(this.amp, this.sbmp);
+ } else {
+ if ((this.mpi != null)
+ && (this.mpi instanceof AndroidMediaPlayer)) {
+ Log.d(MP_TAG, "Already using AndroidMediaPlayer");
+ return;
+ }
+ if (this.amp == null) {
+ Log.d(MP_TAG, "Instantiating new AndroidMediaPlayer (this should be impossible)");
+ this.amp = new AndroidMediaPlayer(this, context);
+ }
+ switchMediaPlayerImpl(this.sbmp, this.amp);
+ }
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ private void switchMediaPlayerImpl(MediaPlayerImpl from, MediaPlayerImpl to) {
+ lock.lock();
+ try {
+ Log.d(MP_TAG, "switchMediaPlayerImpl");
+ if ((from == to)
+ // Same object, nothing to synchronize
+ || (to == null)
+ // Nothing to copy to (maybe this should throw an error?)
+ || ((to instanceof ServiceBackedMediaPlayer) && !((ServiceBackedMediaPlayer) to).isConnected())
+ // ServiceBackedMediaPlayer hasn't yet connected, onServiceConnected will take care of the transition
+ || (MediaPlayer.this.state == State.END)) {
+ // State.END is after a release(), no further functions should
+ // be called on this class and from is likely to have problems
+ // retrieving state that won't be used anyway
+ return;
+ }
+ // Extract all that we can from the existing implementation
+ // and copy it to the new implementation
+
+ Log.d(MP_TAG, "switchMediaPlayerImpl(), current state is "
+ + this.state.toString());
+
+ to.reset();
+
+ // Do this first so we don't have to prepare the same
+ // data file twice
+ to.setEnableSpeedAdjustment(MediaPlayer.this.enableSpeedAdjustment);
+
+ // This is a reasonable place to set all of these,
+ // none of them require prepare() or the like first
+ to.setAudioStreamType(this.mAudioStreamType);
+ to.setSpeedAdjustmentAlgorithm(this.speedAdjustmentAlgorithm);
+ to.setLooping(this.mIsLooping);
+ to.setPitchStepsAdjustment(this.mPitchStepsAdjustment);
+ Log.d(MP_TAG, "Setting playback speed to " + this.mSpeedMultiplier);
+ to.setPlaybackSpeed(this.mSpeedMultiplier);
+ to.setVolume(MediaPlayer.this.mLeftVolume,
+ MediaPlayer.this.mRightVolume);
+ to.setWakeMode(this.mContext, this.mWakeMode);
+
+ Log.d(MP_TAG, "asserting at least one data source is null");
+ assert ((MediaPlayer.this.stringDataSource == null) || (MediaPlayer.this.uriDataSource == null));
+
+ if (uriDataSource != null) {
+ Log.d(MP_TAG, "switchMediaPlayerImpl(): uriDataSource != null");
+ try {
+ to.setDataSource(this.mContext, uriDataSource);
+ } catch (IllegalArgumentException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IllegalStateException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ if (stringDataSource != null) {
+ Log.d(MP_TAG,
+ "switchMediaPlayerImpl(): stringDataSource != null");
+ try {
+ to.setDataSource(stringDataSource);
+ } catch (IllegalArgumentException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IllegalStateException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ if ((this.state == State.PREPARED)
+ || (this.state == State.PREPARING)
+ || (this.state == State.PAUSED)
+ || (this.state == State.STOPPED)
+ || (this.state == State.STARTED)
+ || (this.state == State.PLAYBACK_COMPLETED)) {
+ Log.d(MP_TAG, "switchMediaPlayerImpl(): prepare and seek");
+ // Use prepare here instead of prepareAsync so that
+ // we wait for it to be ready before we try to use it
+ try {
+ to.muteNextOnPrepare();
+ to.prepare();
+ } catch (IllegalStateException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IOException e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ int seekPos = 0;
+ if (from != null) {
+ seekPos = from.getCurrentPosition();
+ } else if (this.lastKnownPosition < to.getDuration()) {
+ // This can happen if the Service unexpectedly
+ // disconnected. Because it would result in too much
+ // information being passed around, we don't constantly
+ // poll for the lastKnownPosition, but we'll save it
+ // when getCurrentPosition is called
+ seekPos = this.lastKnownPosition;
+ }
+ to.muteNextSeek();
+ to.seekTo(seekPos);
+ }
+ if ((from != null)
+ && from.isPlaying()) {
+ from.pause();
+ }
+ if ((this.state == State.STARTED)
+ || (this.state == State.PAUSED)
+ || (this.state == State.STOPPED)) {
+ Log.d(MP_TAG, "switchMediaPlayerImpl(): start");
+ if (to != null) {
+ to.start();
+ }
+ }
+
+ if (this.state == State.PAUSED) {
+ Log.d(MP_TAG, "switchMediaPlayerImpl(): paused");
+ if (to != null) {
+ to.pause();
+ }
+ } else if (this.state == State.STOPPED) {
+ Log.d(MP_TAG, "switchMediaPlayerImpl(): stopped");
+ if (to != null) {
+ to.stop();
+ }
+ }
+
+ this.mpi = to;
+
+ // Cheating here by relying on the side effect in
+ // on(Pitch|Speed)AdjustmentAvailableChanged
+ if ((to.canSetPitch() != this.pitchAdjustmentAvailable)
+ && (this.onPitchAdjustmentAvailableChangedListener != null)) {
+ this.onPitchAdjustmentAvailableChangedListener
+ .onPitchAdjustmentAvailableChanged(this, to
+ .canSetPitch());
+ }
+ if ((to.canSetSpeed() != this.speedAdjustmentAvailable)
+ && (this.onSpeedAdjustmentAvailableChangedListener != null)) {
+ this.onSpeedAdjustmentAvailableChangedListener
+ .onSpeedAdjustmentAvailableChanged(this, to
+ .canSetSpeed());
+ }
+ Log.d(MP_TAG, "switchMediaPlayerImpl() 625 " + this.state.toString());
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Returns true if pitch can be changed at this moment
+ *
+ * @return True if pitch can be changed
+ */
+ public boolean canSetPitch() {
+ lock.lock();
+ try {
+ return this.mpi.canSetPitch();
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Returns true if speed can be changed at this moment
+ *
+ * @return True if speed can be changed
+ */
+ public boolean canSetSpeed() {
+ lock.lock();
+ try {
+ return this.mpi.canSetSpeed();
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ protected void finalize() throws Throwable {
+ lock.lock();
+ try {
+ Log.d(MP_TAG, "finalize() 626");
+ this.release();
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Returns the number of steps (in a musical scale) by which playback is
+ * currently shifted. When greater than zero, pitch is shifted up. When less
+ * than zero, pitch is shifted down.
+ *
+ * @return The number of steps pitch is currently shifted by
+ */
+ public float getCurrentPitchStepsAdjustment() {
+ lock.lock();
+ try {
+ return this.mpi.getCurrentPitchStepsAdjustment();
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Functions identically to android.media.MediaPlayer.getCurrentPosition()
+ * Accurate only to frame size of encoded data (26 ms for MP3s)
+ *
+ * @return Current position (in milliseconds)
+ */
+ public int getCurrentPosition() {
+ lock.lock();
+ try {
+ return (this.lastKnownPosition = this.mpi.getCurrentPosition());
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Returns the current speed multiplier. Defaults to 1.0 (normal speed)
+ *
+ * @return The current speed multiplier
+ */
+ public float getCurrentSpeedMultiplier() {
+ lock.lock();
+ try {
+ return this.mpi.getCurrentSpeedMultiplier();
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Functions identically to android.media.MediaPlayer.getDuration()
+ *
+ * @return Length of the track (in milliseconds)
+ */
+ public int getDuration() {
+ lock.lock();
+ try {
+ return this.mpi.getDuration();
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Get the maximum value that can be passed to setPlaybackSpeed
+ *
+ * @return The maximum speed multiplier
+ */
+ public float getMaxSpeedMultiplier() {
+ lock.lock();
+ try {
+ return this.mpi.getMaxSpeedMultiplier();
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Get the minimum value that can be passed to setPlaybackSpeed
+ *
+ * @return The minimum speed multiplier
+ */
+ public float getMinSpeedMultiplier() {
+ lock.lock();
+ try {
+ return this.mpi.getMinSpeedMultiplier();
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Gets the version code of the backing service
+ *
+ * @return -1 if ServiceBackedMediaPlayer is not used, 0 if the service is not
+ * connected, otherwise the version code retrieved from the service
+ */
+ public int getServiceVersionCode() {
+ lock.lock();
+ try {
+ if (this.mpi instanceof ServiceBackedMediaPlayer) {
+ return ((ServiceBackedMediaPlayer) this.mpi).getServiceVersionCode();
+ } else {
+ return -1;
+ }
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Gets the version name of the backing service
+ *
+ * @return null if ServiceBackedMediaPlayer is not used, empty string if
+ * the service is not connected, otherwise the version name retrieved from
+ * the service
+ */
+ public String getServiceVersionName() {
+ lock.lock();
+ try {
+ if (this.mpi instanceof ServiceBackedMediaPlayer) {
+ return ((ServiceBackedMediaPlayer) this.mpi).getServiceVersionName();
+ } else {
+ return null;
+ }
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Functions identically to android.media.MediaPlayer.isLooping()
+ *
+ * @return True if the track is looping
+ */
+ public boolean isLooping() {
+ lock.lock();
+ try {
+ return this.mpi.isLooping();
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Functions identically to android.media.MediaPlayer.isPlaying()
+ *
+ * @return True if the track is playing
+ */
+ public boolean isPlaying() {
+ lock.lock();
+ try {
+ return this.mpi.isPlaying();
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Returns true if this MediaPlayer has access to the Presto
+ * library
+ *
+ * @return True if the Presto library is installed
+ */
+ public boolean isPrestoLibraryInstalled() {
+ if ((this.mpi == null) || (this.mpi.mContext == null)) {
+ return false;
+ }
+ return isPrestoLibraryInstalled(this.mpi.mContext);
+ }
+
+ /**
+ * Open the Android Market page in the same context as this MediaPlayer
+ */
+ public void openPrestoMarketIntent() {
+ if ((this.mpi != null) && (this.mpi.mContext != null)) {
+ openPrestoMarketIntent(this.mpi.mContext);
+ }
+ }
+
+ /**
+ * Functions identically to android.media.MediaPlayer.pause() Pauses the
+ * track
+ */
+ public void pause() {
+ lock.lock();
+ try {
+ if (invalidServiceConnectionConfiguration()) {
+ setupMpi(this.mpi.mContext);
+ }
+ this.state = State.PAUSED;
+ this.mpi.pause();
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Functions identically to android.media.MediaPlayer.prepare() Prepares the
+ * track. This or prepareAsync must be called before start()
+ */
+ public void prepare() throws IllegalStateException, IOException {
+ lock.lock();
+ try {
+ Log.d(MP_TAG, "prepare() 746 using " + ((this.mpi == null) ? "null (this shouldn't happen)" : this.mpi.getClass().toString()) + " state " + this.state.toString());
+ Log.d(MP_TAG, "onPreparedListener is: " + ((this.onPreparedListener == null) ? "null" : "non-null"));
+ Log.d(MP_TAG, "preparedListener is: " + ((this.preparedListener == null) ? "null" : "non-null"));
+ if (invalidServiceConnectionConfiguration()) {
+ setupMpi(this.mpi.mContext);
+ }
+ this.mpi.prepare();
+ this.state = State.PREPARED;
+ Log.d(MP_TAG, "prepare() finished 778");
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Functions identically to android.media.MediaPlayer.prepareAsync()
+ * Prepares the track. This or prepare must be called before start()
+ */
+ public void prepareAsync() {
+ lock.lock();
+ try {
+ Log.d(MP_TAG, "prepareAsync() 779");
+ if (invalidServiceConnectionConfiguration()) {
+ setupMpi(this.mpi.mContext);
+ }
+ this.state = State.PREPARING;
+ this.mpi.prepareAsync();
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Functions identically to android.media.MediaPlayer.release() Releases the
+ * underlying resources used by the media player.
+ */
+ public void release() {
+ lock.lock();
+ try {
+ Log.d(MP_TAG, "Releasing MediaPlayer 791");
+
+ this.state = State.END;
+ if (this.amp != null) {
+ this.amp.release();
+ }
+ if (this.sbmp != null) {
+ this.sbmp.release();
+ }
+
+ this.onBufferingUpdateListener = null;
+ this.onCompletionListener = null;
+ this.onErrorListener = null;
+ this.onInfoListener = null;
+ this.preparedListener = null;
+ this.onPitchAdjustmentAvailableChangedListener = null;
+ this.pitchAdjustmentAvailableChangedListener = null;
+ Log.d(MP_TAG, "Setting onSeekCompleteListener to null 871");
+ this.onSeekCompleteListener = null;
+ this.onSpeedAdjustmentAvailableChangedListener = null;
+ this.speedAdjustmentAvailableChangedListener = null;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Functions identically to android.media.MediaPlayer.reset() Resets the
+ * track to idle state
+ */
+ public void reset() {
+ lock.lock();
+ try {
+ this.state = State.IDLE;
+ this.stringDataSource = null;
+ this.uriDataSource = null;
+ this.mpi.reset();
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Functions identically to android.media.MediaPlayer.seekTo(int msec) Seeks
+ * to msec in the track
+ */
+ public void seekTo(int msec) throws IllegalStateException {
+ lock.lock();
+ try {
+ this.mpi.seekTo(msec);
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Functions identically to android.media.MediaPlayer.setAudioStreamType(int
+ * streamtype) Sets the audio stream type.
+ */
+ public void setAudioStreamType(int streamtype) {
+ lock.lock();
+ try {
+ this.mAudioStreamType = streamtype;
+ this.mpi.setAudioStreamType(streamtype);
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Functions identically to android.media.MediaPlayer.setDataSource(Context
+ * context, Uri uri) Sets uri as data source in the context given
+ */
+ public void setDataSource(Context context, Uri uri)
+ throws IllegalArgumentException, IllegalStateException, IOException {
+ lock.lock();
+ try {
+ Log.d(MP_TAG, "In setDataSource(context, " + uri.toString() + "), using " + this.mpi.getClass().toString());
+ if (invalidServiceConnectionConfiguration()) {
+ setupMpi(this.mpi.mContext);
+ }
+ this.state = State.INITIALIZED;
+ this.stringDataSource = null;
+ this.uriDataSource = uri;
+ this.mpi.setDataSource(context, uri);
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Functions identically to android.media.MediaPlayer.setDataSource(String
+ * path) Sets the data source of the track to a file given.
+ */
+ public void setDataSource(String path) throws IllegalArgumentException,
+ IllegalStateException, IOException {
+ lock.lock();
+ try {
+ Log.d(MP_TAG, "In setDataSource(context, " + path + ")");
+ if (invalidServiceConnectionConfiguration()) {
+ setupMpi(this.mpi.mContext);
+ }
+ this.state = State.INITIALIZED;
+ this.stringDataSource = path;
+ this.uriDataSource = null;
+ this.mpi.setDataSource(path);
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Sets whether to use speed adjustment or not. Speed adjustment on is more
+ * computation-intensive than with it off.
+ *
+ * @param enableSpeedAdjustment Whether speed adjustment should be supported.
+ */
+ public void setEnableSpeedAdjustment(boolean enableSpeedAdjustment) {
+ lock.lock();
+ try {
+ this.enableSpeedAdjustment = enableSpeedAdjustment;
+ this.mpi.setEnableSpeedAdjustment(enableSpeedAdjustment);
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Functions identically to android.media.MediaPlayer.setLooping(boolean
+ * loop) Sets the track to loop infinitely if loop is true, play once if
+ * loop is false
+ */
+ public void setLooping(boolean loop) {
+ lock.lock();
+ try {
+ this.mIsLooping = loop;
+ this.mpi.setLooping(loop);
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Sets the number of steps (in a musical scale) by which playback is
+ * currently shifted. When greater than zero, pitch is shifted up. When less
+ * than zero, pitch is shifted down.
+ *
+ * @param pitchSteps The number of steps by which to shift playback
+ */
+ public void setPitchStepsAdjustment(float pitchSteps) {
+ lock.lock();
+ try {
+ this.mPitchStepsAdjustment = pitchSteps;
+ this.mpi.setPitchStepsAdjustment(pitchSteps);
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Set the algorithm to use for changing the speed and pitch of audio
+ * See SpeedAdjustmentAlgorithm constants for more details
+ *
+ * @param algorithm The algorithm to use.
+ */
+ public void setSpeedAdjustmentAlgorithm(int algorithm) {
+ lock.lock();
+ try {
+ this.speedAdjustmentAlgorithm = algorithm;
+ if (this.mpi != null) {
+ this.mpi.setSpeedAdjustmentAlgorithm(algorithm);
+ }
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ private static float getPitchStepsAdjustment(float pitch) {
+ return (float) (Math.log(pitch) / (2 * Math.log(PITCH_STEP_CONSTANT)));
+ }
+
+ /**
+ * Sets the percentage by which pitch is currently shifted. When greater
+ * than zero, pitch is shifted up. When less than zero, pitch is shifted
+ * down
+ *
+ * @param f The percentage to shift pitch
+ */
+ public void setPlaybackPitch(float pitch) {
+ lock.lock();
+ try {
+ this.mPitchStepsAdjustment = getPitchStepsAdjustment(pitch);
+ this.mpi.setPlaybackPitch(pitch);
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Set playback speed. 1.0 is normal speed, 2.0 is double speed, and so on.
+ * Speed should never be set to 0 or below.
+ *
+ * @param f The speed multiplier to use for further playback
+ */
+ public void setPlaybackSpeed(float f) {
+ lock.lock();
+ try {
+ this.mSpeedMultiplier = f;
+ this.mpi.setPlaybackSpeed(f);
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Sets whether to use speed adjustment or not. Speed adjustment on is more
+ * computation-intensive than with it off.
+ *
+ * @param enableSpeedAdjustment Whether speed adjustment should be supported.
+ */
+ public void setUseService(boolean useService) {
+ lock.lock();
+ try {
+ this.useService = useService;
+ setupMpi(this.mpi.mContext);
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Functions identically to android.media.MediaPlayer.setVolume(float
+ * leftVolume, float rightVolume) Sets the stereo volume
+ */
+ public void setVolume(float leftVolume, float rightVolume) {
+ lock.lock();
+ try {
+ this.mLeftVolume = leftVolume;
+ this.mRightVolume = rightVolume;
+ this.mpi.setVolume(leftVolume, rightVolume);
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Functions identically to android.media.MediaPlayer.setWakeMode(Context
+ * context, int mode) Acquires a wake lock in the context given. You must
+ * request the appropriate permissions in your AndroidManifest.xml file.
+ */
+ public void setWakeMode(Context context, int mode) {
+ lock.lock();
+ try {
+ this.mWakeMode = mode;
+ this.mpi.setWakeMode(context, mode);
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Functions identically to
+ * android.media.MediaPlayer.setOnCompletionListener(OnCompletionListener
+ * listener) Sets a listener to be used when a track completes playing.
+ */
+ public void setOnBufferingUpdateListener(OnBufferingUpdateListener listener) {
+ lock.lock();
+ try {
+ this.onBufferingUpdateListener = listener;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Functions identically to
+ * android.media.MediaPlayer.setOnCompletionListener(OnCompletionListener
+ * listener) Sets a listener to be used when a track completes playing.
+ */
+ public void setOnCompletionListener(OnCompletionListener listener) {
+ lock.lock();
+ try {
+ this.onCompletionListener = listener;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Functions identically to
+ * android.media.MediaPlayer.setOnErrorListener(OnErrorListener listener)
+ * Sets a listener to be used when a track encounters an error.
+ */
+ public void setOnErrorListener(OnErrorListener listener) {
+ lock.lock();
+ try {
+ this.onErrorListener = listener;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Functions identically to
+ * android.media.MediaPlayer.setOnInfoListener(OnInfoListener listener) Sets
+ * a listener to be used when a track has info.
+ */
+ public void setOnInfoListener(OnInfoListener listener) {
+ lock.lock();
+ try {
+ this.onInfoListener = listener;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Sets a listener that will fire when pitch adjustment becomes available or
+ * stops being available
+ */
+ public void setOnPitchAdjustmentAvailableChangedListener(
+ OnPitchAdjustmentAvailableChangedListener listener) {
+ lock.lock();
+ try {
+ this.pitchAdjustmentAvailableChangedListener = listener;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Functions identically to
+ * android.media.MediaPlayer.setOnPreparedListener(OnPreparedListener
+ * listener) Sets a listener to be used when a track finishes preparing.
+ */
+ public void setOnPreparedListener(OnPreparedListener listener) {
+ lock.lock();
+ Log.d(MP_TAG, " ++++++++++++++++++++++++++++++++++++++++++++ setOnPreparedListener");
+ try {
+ this.preparedListener = listener;
+ // For this one, we do not explicitly set the MediaPlayer or the
+ // Service listener. This is because in addition to calling the
+ // listener provided by the client, it's necessary to change
+ // state to PREPARED. See prepareAsync for implementation details
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Functions identically to
+ * android.media.MediaPlayer.setOnSeekCompleteListener
+ * (OnSeekCompleteListener listener) Sets a listener to be used when a track
+ * finishes seeking.
+ */
+ public void setOnSeekCompleteListener(OnSeekCompleteListener listener) {
+ lock.lock();
+ try {
+ this.onSeekCompleteListener = listener;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Sets a listener that will fire when speed adjustment becomes available or
+ * stops being available
+ */
+ public void setOnSpeedAdjustmentAvailableChangedListener(
+ OnSpeedAdjustmentAvailableChangedListener listener) {
+ lock.lock();
+ try {
+ this.speedAdjustmentAvailableChangedListener = listener;
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Functions identically to android.media.MediaPlayer.start() Starts a track
+ * playing
+ */
+ public void start() {
+ lock.lock();
+ try {
+ Log.d(MP_TAG, "start() 1149");
+ if (invalidServiceConnectionConfiguration()) {
+ setupMpi(this.mpi.mContext);
+ }
+ this.state = State.STARTED;
+ Log.d(MP_TAG, "start() 1154");
+ this.mpi.start();
+ } finally {
+ lock.unlock();
+ }
+ }
+
+ /**
+ * Functions identically to android.media.MediaPlayer.stop() Stops a track
+ * playing and resets its position to the start.
+ */
+ public void stop() {
+ lock.lock();
+ try {
+ if (invalidServiceConnectionConfiguration()) {
+ setupMpi(this.mpi.mContext);
+ }
+ this.state = State.STOPPED;
+ this.mpi.stop();
+ } finally {
+ lock.unlock();
+ }
+ }
+} \ No newline at end of file