From e1b1338840f8a5c75c1911085af03975422116db Mon Sep 17 00:00:00 2001 From: daniel oeh Date: Sat, 22 Mar 2014 18:05:51 +0100 Subject: Added presto client dependency as source code --- build.gradle | 6 - src/com/aocate/media/AndroidMediaPlayer.java | 470 +++++++ src/com/aocate/media/MediaPlayer.java | 1294 ++++++++++++++++++++ src/com/aocate/media/MediaPlayerImpl.java | 118 ++ src/com/aocate/media/ServiceBackedMediaPlayer.java | 1170 ++++++++++++++++++ src/com/aocate/media/SpeedAdjustmentAlgorithm.java | 31 + .../aocate/presto/service/IDeathCallback_0_8.aidl | 18 + .../IOnBufferingUpdateListenerCallback_0_8.aidl | 19 + .../service/IOnCompletionListenerCallback_0_8.aidl | 19 + .../service/IOnErrorListenerCallback_0_8.aidl | 19 + .../service/IOnInfoListenerCallback_0_8.aidl | 19 + ...stmentAvailableChangedListenerCallback_0_8.aidl | 19 + .../service/IOnPreparedListenerCallback_0_8.aidl | 19 + .../IOnSeekCompleteListenerCallback_0_8.aidl | 19 + ...stmentAvailableChangedListenerCallback_0_8.aidl | 19 + src/com/aocate/presto/service/IPlayMedia_0_8.aidl | 75 ++ 16 files changed, 3328 insertions(+), 6 deletions(-) create mode 100644 src/com/aocate/media/AndroidMediaPlayer.java create mode 100644 src/com/aocate/media/MediaPlayer.java create mode 100644 src/com/aocate/media/MediaPlayerImpl.java create mode 100644 src/com/aocate/media/ServiceBackedMediaPlayer.java create mode 100644 src/com/aocate/media/SpeedAdjustmentAlgorithm.java create mode 100644 src/com/aocate/presto/service/IDeathCallback_0_8.aidl create mode 100644 src/com/aocate/presto/service/IOnBufferingUpdateListenerCallback_0_8.aidl create mode 100644 src/com/aocate/presto/service/IOnCompletionListenerCallback_0_8.aidl create mode 100644 src/com/aocate/presto/service/IOnErrorListenerCallback_0_8.aidl create mode 100644 src/com/aocate/presto/service/IOnInfoListenerCallback_0_8.aidl create mode 100644 src/com/aocate/presto/service/IOnPitchAdjustmentAvailableChangedListenerCallback_0_8.aidl create mode 100644 src/com/aocate/presto/service/IOnPreparedListenerCallback_0_8.aidl create mode 100644 src/com/aocate/presto/service/IOnSeekCompleteListenerCallback_0_8.aidl create mode 100644 src/com/aocate/presto/service/IOnSpeedAdjustmentAvailableChangedListenerCallback_0_8.aidl create mode 100644 src/com/aocate/presto/service/IPlayMedia_0_8.aidl diff --git a/build.gradle b/build.gradle index 20f0d6fdb..8c12c60fd 100644 --- a/build.gradle +++ b/build.gradle @@ -17,11 +17,6 @@ dependencies { println "Creating libs directory" libsdir.mkdir() } - def prestoLib = new File('libs/presto_client-0.8.5.jar') - if (!prestoLib.exists()) { - println "Downloading presto library into libs folder" - new URL('http://www.aocate.com/presto/client/presto_client-0.8.5.jar').withInputStream{ i -> prestoLib.withOutputStream{ it << i }} - } compile 'com.android.support:appcompat-v7:19.0.+' compile 'org.apache.commons:commons-lang3:3.2.1' @@ -33,7 +28,6 @@ dependencies { compile 'commons-io:commons-io:2.4' compile 'com.nineoldandroids:library:2.4.0' compile project(':submodules:dslv:library') - compile files('libs/presto_client-0.8.5.jar') } android { diff --git a/src/com/aocate/media/AndroidMediaPlayer.java b/src/com/aocate/media/AndroidMediaPlayer.java new file mode 100644 index 000000000..17ee74a13 --- /dev/null +++ b/src/com/aocate/media/AndroidMediaPlayer.java @@ -0,0 +1,470 @@ +// 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 java.io.IOException; + +import android.content.Context; +import android.media.MediaPlayer; +import android.net.Uri; +import android.util.Log; + +public class AndroidMediaPlayer extends MediaPlayerImpl { + private final static String AMP_TAG = "AocateAndroidMediaPlayer"; + + // private static final long TIMEOUT_DURATION_MS = 500; + + android.media.MediaPlayer mp = null; + + private android.media.MediaPlayer.OnBufferingUpdateListener onBufferingUpdateListener = new android.media.MediaPlayer.OnBufferingUpdateListener() { + public void onBufferingUpdate(android.media.MediaPlayer mp, int percent) { + if (owningMediaPlayer != null) { + owningMediaPlayer.lock.lock(); + try { + if ((owningMediaPlayer.onBufferingUpdateListener != null) + && (owningMediaPlayer.mpi == AndroidMediaPlayer.this)) { + owningMediaPlayer.onBufferingUpdateListener.onBufferingUpdate(owningMediaPlayer, percent); + } + } + finally { + owningMediaPlayer.lock.unlock(); + } + } + + } + }; + + private android.media.MediaPlayer.OnCompletionListener onCompletionListener = new android.media.MediaPlayer.OnCompletionListener() { + public void onCompletion(android.media.MediaPlayer mp) { + Log.d(AMP_TAG, "onCompletionListener being called"); + if (owningMediaPlayer != null) { + owningMediaPlayer.lock.lock(); + try { + if (owningMediaPlayer.onCompletionListener != null) { + owningMediaPlayer.onCompletionListener.onCompletion(owningMediaPlayer); + } + } + finally { + owningMediaPlayer.lock.unlock(); + } + } + } + }; + + private android.media.MediaPlayer.OnErrorListener onErrorListener = new android.media.MediaPlayer.OnErrorListener() { + public boolean onError(android.media.MediaPlayer mp, int what, int extra) { + // Once we're in errored state, any received messages are going to be junked + if (owningMediaPlayer != null) { + owningMediaPlayer.lock.lock(); + try { + if (owningMediaPlayer.onErrorListener != null) { + return owningMediaPlayer.onErrorListener.onError(owningMediaPlayer, what, extra); + } + } + finally { + owningMediaPlayer.lock.unlock(); + } + } + return false; + } + }; + + private android.media.MediaPlayer.OnInfoListener onInfoListener = new android.media.MediaPlayer.OnInfoListener() { + public boolean onInfo(android.media.MediaPlayer mp, int what, int extra) { + if (owningMediaPlayer != null) { + owningMediaPlayer.lock.lock(); + try { + if ((owningMediaPlayer.onInfoListener != null) + && (owningMediaPlayer.mpi == AndroidMediaPlayer.this)) { + return owningMediaPlayer.onInfoListener.onInfo(owningMediaPlayer, what, extra); + } + } + finally { + owningMediaPlayer.lock.unlock(); + } + } + return false; + } + }; + + // We have to assign this.onPreparedListener because the + // onPreparedListener in owningMediaPlayer sets the state + // to PREPARED. Due to prepareAsync, that's the only + // reasonable place to do it + // The others it just didn't make sense to have a setOnXListener that didn't use the parameter + private android.media.MediaPlayer.OnPreparedListener onPreparedListener = new android.media.MediaPlayer.OnPreparedListener() { + public void onPrepared(android.media.MediaPlayer mp) { + Log.d(AMP_TAG, "Calling onPreparedListener.onPrepared()"); + if (AndroidMediaPlayer.this.owningMediaPlayer != null) { + AndroidMediaPlayer.this.lockMuteOnPreparedCount.lock(); + try { + if (AndroidMediaPlayer.this.muteOnPreparedCount > 0) { + AndroidMediaPlayer.this.muteOnPreparedCount--; + } + else { + AndroidMediaPlayer.this.muteOnPreparedCount = 0; + if (AndroidMediaPlayer.this.owningMediaPlayer.onPreparedListener != null) { + Log.d(AMP_TAG, "Invoking AndroidMediaPlayer.this.owningMediaPlayer.onPreparedListener.onPrepared"); + AndroidMediaPlayer.this.owningMediaPlayer.onPreparedListener.onPrepared(AndroidMediaPlayer.this.owningMediaPlayer); + } + } + } + finally { + AndroidMediaPlayer.this.lockMuteOnPreparedCount.unlock(); + } + if (owningMediaPlayer.mpi != AndroidMediaPlayer.this) { + Log.d(AMP_TAG, "owningMediaPlayer has changed implementation"); + } + } + } + }; + + private android.media.MediaPlayer.OnSeekCompleteListener onSeekCompleteListener = new android.media.MediaPlayer.OnSeekCompleteListener() { + public void onSeekComplete(android.media.MediaPlayer mp) { + if (owningMediaPlayer != null) { + owningMediaPlayer.lock.lock(); + try { + lockMuteOnSeekCount.lock(); + try { + if (AndroidMediaPlayer.this.muteOnSeekCount > 0) { + AndroidMediaPlayer.this.muteOnSeekCount--; + } + else { + AndroidMediaPlayer.this.muteOnSeekCount = 0; + if (AndroidMediaPlayer.this.owningMediaPlayer.onSeekCompleteListener != null) { + owningMediaPlayer.onSeekCompleteListener.onSeekComplete(owningMediaPlayer); + } + } + } + finally { + lockMuteOnSeekCount.unlock(); + } + } + finally { + owningMediaPlayer.lock.unlock(); + } + } + } + }; + + public AndroidMediaPlayer(com.aocate.media.MediaPlayer owningMediaPlayer, Context context) { + super(owningMediaPlayer, context); + + mp = new MediaPlayer(); + +// final ReentrantLock lock = new ReentrantLock(); +// Handler handler = new Handler(Looper.getMainLooper()) { +// @Override +// public void handleMessage(Message msg) { +// Log.d(AMP_TAG, "Instantiating new AndroidMediaPlayer from Handler"); +// lock.lock(); +// if (mp == null) { +// mp = new MediaPlayer(); +// } +// lock.unlock(); +// } +// }; +// +// long endTime = System.currentTimeMillis() + TIMEOUT_DURATION_MS; +// +// while (true) { +// // Retry messages until mp isn't null or it's time to give up +// handler.sendMessage(handler.obtainMessage()); +// if ((mp != null) +// || (endTime < System.currentTimeMillis())) { +// break; +// } +// try { +// Thread.sleep(50); +// } catch (InterruptedException e) { +// // TODO Auto-generated catch block +// e.printStackTrace(); +// } +// } + + if (mp == null) { + throw new IllegalStateException("Did not instantiate android.media.MediaPlayer successfully"); + } + + mp.setOnBufferingUpdateListener(this.onBufferingUpdateListener); + mp.setOnCompletionListener(this.onCompletionListener); + mp.setOnErrorListener(this.onErrorListener); + mp.setOnInfoListener(this.onInfoListener); + Log.d(AMP_TAG, " ++++++++++++++++++++++++++++++++ Setting prepared listener to this.onPreparedListener"); + mp.setOnPreparedListener(this.onPreparedListener); + mp.setOnSeekCompleteListener(this.onSeekCompleteListener); + } + + @Override + public boolean canSetPitch() { + return false; + } + + @Override + public boolean canSetSpeed() { + return false; + } + + @Override + public float getCurrentPitchStepsAdjustment() { + return 0; + } + + @Override + public int getCurrentPosition() { + owningMediaPlayer.lock.lock(); + try { + return mp.getCurrentPosition(); + } + finally { + owningMediaPlayer.lock.unlock(); + } + } + + @Override + public float getCurrentSpeedMultiplier() { + return 1f; + } + + @Override + public int getDuration() { + owningMediaPlayer.lock.lock(); + try { + return mp.getDuration(); + } + finally { + owningMediaPlayer.lock.unlock(); + } + } + + @Override + public float getMaxSpeedMultiplier() { + return 1f; + } + + @Override + public float getMinSpeedMultiplier() { + return 1f; + } + + @Override + public boolean isLooping() { + owningMediaPlayer.lock.lock(); + try { + return mp.isLooping(); + } + finally { + owningMediaPlayer.lock.unlock(); + } + } + + @Override + public boolean isPlaying() { + owningMediaPlayer.lock.lock(); + try { + return mp.isPlaying(); + } + finally { + owningMediaPlayer.lock.unlock(); + } + } + + @Override + public void pause() { + owningMediaPlayer.lock.lock(); + try { + mp.pause(); + } + finally { + owningMediaPlayer.lock.unlock(); + } + } + + @Override + public void prepare() throws IllegalStateException, IOException { + owningMediaPlayer.lock.lock(); + Log.d(AMP_TAG, "prepare()"); + try { + mp.prepare(); + Log.d(AMP_TAG, "Finish prepare()"); + } + finally { + owningMediaPlayer.lock.unlock(); + } + } + + @Override + public void prepareAsync() { + mp.prepareAsync(); + } + + @Override + public void release() { + owningMediaPlayer.lock.lock(); + try { + if (mp != null) { + Log.d(AMP_TAG, "mp.release()"); + mp.release(); + } + } + finally { + owningMediaPlayer.lock.unlock(); + } + } + + @Override + public void reset() { + owningMediaPlayer.lock.lock(); + try { + mp.reset(); + } + finally { + owningMediaPlayer.lock.unlock(); + } + } + + @Override + public void seekTo(int msec) throws IllegalStateException { + owningMediaPlayer.lock.lock(); + try { + mp.setOnSeekCompleteListener(this.onSeekCompleteListener); + mp.seekTo(msec); + } + finally { + owningMediaPlayer.lock.unlock(); + } + } + + @Override + public void setAudioStreamType(int streamtype) { + owningMediaPlayer.lock.lock(); + try { + mp.setAudioStreamType(streamtype); + } + finally { + owningMediaPlayer.lock.unlock(); + } + } + + @Override + public void setDataSource(Context context, Uri uri) + throws IllegalArgumentException, IllegalStateException, IOException { + owningMediaPlayer.lock.lock(); + try { + Log.d(AMP_TAG, "setDataSource(context, " + uri.toString() + ")"); + mp.setDataSource(context, uri); + } + finally { + owningMediaPlayer.lock.unlock(); + } + } + + @Override + public void setDataSource(String path) throws IllegalArgumentException, + IllegalStateException, IOException { + owningMediaPlayer.lock.lock(); + try { + Log.d(AMP_TAG, "setDataSource(" + path + ")"); + mp.setDataSource(path); + } + finally { + owningMediaPlayer.lock.unlock(); + } + } + + @Override + public void setEnableSpeedAdjustment(boolean enableSpeedAdjustment) { + // Can't! + } + + @Override + public void setLooping(boolean loop) { + owningMediaPlayer.lock.lock(); + try { + mp.setLooping(loop); + } + finally { + owningMediaPlayer.lock.unlock(); + } + } + + @Override + public void setPitchStepsAdjustment(float pitchSteps) { + // Can't! + } + + @Override + public void setPlaybackPitch(float f) { + // Can't! + } + + @Override + public void setPlaybackSpeed(float f) { + // Can't! + Log.d(AMP_TAG, "setPlaybackSpeed(" + f + ")"); + } + + @Override + public void setSpeedAdjustmentAlgorithm(int algorithm) { + // Can't! + Log.d(AMP_TAG, "setSpeedAdjustmentAlgorithm(" + algorithm + ")"); + } + + @Override + public void setVolume(float leftVolume, float rightVolume) { + owningMediaPlayer.lock.lock(); + try { + mp.setVolume(leftVolume, rightVolume); + } + finally { + owningMediaPlayer.lock.unlock(); + } + } + + @Override + public void setWakeMode(Context context, int mode) { + owningMediaPlayer.lock.lock(); + try { + if (mode != 0) { + mp.setWakeMode(context, mode); + } + } + finally { + owningMediaPlayer.lock.unlock(); + } + } + + @Override + public void start() { + owningMediaPlayer.lock.lock(); + try { + mp.start(); + } + finally { + owningMediaPlayer.lock.unlock(); + } + } + + @Override + public void stop() { + owningMediaPlayer.lock.lock(); + try { + mp.stop(); + } + finally { + owningMediaPlayer.lock.unlock(); + } + } +} diff --git a/src/com/aocate/media/MediaPlayer.java b/src/com/aocate/media/MediaPlayer.java new file mode 100644 index 000000000..995dbbc51 --- /dev/null +++ b/src/com/aocate/media/MediaPlayer.java @@ -0,0 +1,1294 @@ +// 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 java.io.IOException; +import java.util.List; +import java.util.concurrent.locks.ReentrantLock; + +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.IBinder; +import android.os.Message; +import android.os.Handler.Callback; +import android.util.Log; + +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 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 { + assert (this.mpi instanceof ServiceBackedMediaPlayer); + 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 diff --git a/src/com/aocate/media/MediaPlayerImpl.java b/src/com/aocate/media/MediaPlayerImpl.java new file mode 100644 index 000000000..856ab47ce --- /dev/null +++ b/src/com/aocate/media/MediaPlayerImpl.java @@ -0,0 +1,118 @@ +// 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 java.io.IOException; +import java.util.concurrent.locks.ReentrantLock; + +import android.content.Context; +import android.net.Uri; +import android.util.Log; + +public abstract class MediaPlayerImpl { + private static final String MPI_TAG = "AocateMediaPlayerImpl"; + protected final MediaPlayer owningMediaPlayer; + protected final Context mContext; + protected int muteOnPreparedCount = 0; + protected int muteOnSeekCount = 0; + + public MediaPlayerImpl(MediaPlayer owningMediaPlayer, Context context) { + this.owningMediaPlayer = owningMediaPlayer; + + this.mContext = context; + } + + public abstract boolean canSetPitch(); + + public abstract boolean canSetSpeed(); + + public abstract float getCurrentPitchStepsAdjustment(); + + public abstract int getCurrentPosition(); + + public abstract float getCurrentSpeedMultiplier(); + + public abstract int getDuration(); + + public abstract float getMaxSpeedMultiplier(); + + public abstract float getMinSpeedMultiplier(); + + public abstract boolean isLooping(); + + public abstract boolean isPlaying(); + + public abstract void pause(); + + public abstract void prepare() throws IllegalStateException, IOException; + + public abstract void prepareAsync(); + + public abstract void release(); + + public abstract void reset(); + + public abstract void seekTo(int msec) throws IllegalStateException; + + public abstract void setAudioStreamType(int streamtype); + + public abstract void setDataSource(Context context, Uri uri) throws IllegalArgumentException, IllegalStateException, IOException; + + public abstract void setDataSource(String path) throws IllegalArgumentException, IllegalStateException, IOException; + + public abstract void setEnableSpeedAdjustment(boolean enableSpeedAdjustment); + + public abstract void setLooping(boolean loop); + + public abstract void setPitchStepsAdjustment(float pitchSteps); + + public abstract void setPlaybackPitch(float f); + + public abstract void setPlaybackSpeed(float f); + + public abstract void setSpeedAdjustmentAlgorithm(int algorithm); + + public abstract void setVolume(float leftVolume, float rightVolume); + + public abstract void setWakeMode(Context context, int mode); + + public abstract void start(); + + public abstract void stop(); + + protected ReentrantLock lockMuteOnPreparedCount = new ReentrantLock(); + public void muteNextOnPrepare() { + lockMuteOnPreparedCount.lock(); + Log.d(MPI_TAG, "muteNextOnPrepare()"); + try { + this.muteOnPreparedCount++; + } + finally { + lockMuteOnPreparedCount.unlock(); + } + } + + protected ReentrantLock lockMuteOnSeekCount = new ReentrantLock(); + public void muteNextSeek() { + lockMuteOnSeekCount.lock(); + Log.d(MPI_TAG, "muteNextOnSeek()"); + try { + this.muteOnSeekCount++; + } + finally { + lockMuteOnSeekCount.unlock(); + } + } +} diff --git a/src/com/aocate/media/ServiceBackedMediaPlayer.java b/src/com/aocate/media/ServiceBackedMediaPlayer.java new file mode 100644 index 000000000..ef4572d33 --- /dev/null +++ b/src/com/aocate/media/ServiceBackedMediaPlayer.java @@ -0,0 +1,1170 @@ +// 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 java.io.IOException; + +import android.content.ComponentName; +import android.content.Context; +import android.content.Intent; +import android.content.ServiceConnection; +import android.media.AudioManager; +import android.net.Uri; +import android.os.IBinder; +import android.os.PowerManager; +import android.os.RemoteException; +import android.os.PowerManager.WakeLock; +import android.util.Log; + +import com.aocate.media.MediaPlayer.State; +import com.aocate.presto.service.IDeathCallback_0_8; +import com.aocate.presto.service.IOnBufferingUpdateListenerCallback_0_8; +import com.aocate.presto.service.IOnCompletionListenerCallback_0_8; +import com.aocate.presto.service.IOnErrorListenerCallback_0_8; +import com.aocate.presto.service.IOnInfoListenerCallback_0_8; +import com.aocate.presto.service.IOnPitchAdjustmentAvailableChangedListenerCallback_0_8; +import com.aocate.presto.service.IOnPreparedListenerCallback_0_8; +import com.aocate.presto.service.IOnSeekCompleteListenerCallback_0_8; +import com.aocate.presto.service.IOnSpeedAdjustmentAvailableChangedListenerCallback_0_8; +import com.aocate.presto.service.IPlayMedia_0_8; + +/** + * Class for connecting to remote speed-altering, media playing Service + * Note that there is unusually high coupling between MediaPlayer and this + * class. This is an unfortunate compromise, since the alternative was to + * track state in two different places in this code (plus the internal state + * of the remote media player). + * @author aocate + * + */ +public class ServiceBackedMediaPlayer extends MediaPlayerImpl { + static final String INTENT_NAME = "com.aocate.intent.PLAY_AUDIO_ADJUST_SPEED_0_8"; + + private static final String SBMP_TAG = "AocateServiceBackedMediaPlayer"; + + private ServiceConnection mPlayMediaServiceConnection = null; + protected IPlayMedia_0_8 pmInterface = null; + private Intent playMediaServiceIntent = null; + // 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. + + private long sessionId = 0; + private boolean isErroring = false; + private int mAudioStreamType = AudioManager.STREAM_MUSIC; + + private WakeLock mWakeLock = null; + + // 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 + public ServiceBackedMediaPlayer(MediaPlayer owningMediaPlayer, final Context context, final ServiceConnection serviceConnection) { + super(owningMediaPlayer, context); + Log.d(SBMP_TAG, "Instantiating ServiceBackedMediaPlayer 87"); + this.playMediaServiceIntent = + new Intent(INTENT_NAME); + this.mPlayMediaServiceConnection = new ServiceConnection() { + public void onServiceConnected(ComponentName name, IBinder service) { + IPlayMedia_0_8 tmpPlayMediaInterface = IPlayMedia_0_8.Stub.asInterface((IBinder) service); + + Log.d(SBMP_TAG, "Setting up pmInterface 94"); + if (ServiceBackedMediaPlayer.this.sessionId == 0) { + try { + // The IDeathCallback isn't a conventional callback. + // It exists so that if the client ceases to exist, + // the Service becomes aware of that and can shut + // down whatever it needs to shut down + ServiceBackedMediaPlayer.this.sessionId = tmpPlayMediaInterface.startSession(new IDeathCallback_0_8.Stub() { + }); + // This is really bad if this fails + } catch (RemoteException e) { + e.printStackTrace(); + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + + Log.d(SBMP_TAG, "Assigning pmInterface"); + + ServiceBackedMediaPlayer.this.setOnBufferingUpdateCallback(tmpPlayMediaInterface); + ServiceBackedMediaPlayer.this.setOnCompletionCallback(tmpPlayMediaInterface); + ServiceBackedMediaPlayer.this.setOnErrorCallback(tmpPlayMediaInterface); + ServiceBackedMediaPlayer.this.setOnInfoCallback(tmpPlayMediaInterface); + ServiceBackedMediaPlayer.this.setOnPitchAdjustmentAvailableChangedListener(tmpPlayMediaInterface); + ServiceBackedMediaPlayer.this.setOnPreparedCallback(tmpPlayMediaInterface); + ServiceBackedMediaPlayer.this.setOnSeekCompleteCallback(tmpPlayMediaInterface); + ServiceBackedMediaPlayer.this.setOnSpeedAdjustmentAvailableChangedCallback(tmpPlayMediaInterface); + + // In order to avoid race conditions from the sessionId or listener not being assigned + pmInterface = tmpPlayMediaInterface; + + Log.d(SBMP_TAG, "Invoking onServiceConnected"); + serviceConnection.onServiceConnected(name, service); + } + + public void onServiceDisconnected(ComponentName name) { + Log.d(SBMP_TAG, "onServiceDisconnected 114"); + + pmInterface = null; + + sessionId = 0; + + serviceConnection.onServiceDisconnected(name); + } + }; + + Log.d(SBMP_TAG, "Connecting PlayMediaService 124"); + if (!ConnectPlayMediaService()) { + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + + private boolean ConnectPlayMediaService() { + Log.d(SBMP_TAG, "ConnectPlayMediaService()"); + + if (MediaPlayer.isIntentAvailable(mContext, INTENT_NAME)) { + Log.d(SBMP_TAG, INTENT_NAME + " is available"); + if (pmInterface == null) { + try { + Log.d(SBMP_TAG, "Binding service"); + return mContext.bindService(playMediaServiceIntent, mPlayMediaServiceConnection, Context.BIND_AUTO_CREATE); + } catch (Exception e) { + return false; + } + } else { + Log.d(SBMP_TAG, "Service already bound"); + return true; + } + } + else { + Log.d(SBMP_TAG, INTENT_NAME + " is not available"); + return false; + } + } + + /** + * Returns true if pitch can be changed at this moment + * @return True if pitch can be changed + */ + @Override + public boolean canSetPitch() { + Log.d(SBMP_TAG, "canSetPitch() 155"); + + if (pmInterface == null) { + if (!ConnectPlayMediaService()) { + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + if (pmInterface != null) { + // Can't set pitch if the service isn't connected + try { + return pmInterface.canSetPitch(ServiceBackedMediaPlayer.this.sessionId); + } catch (RemoteException e) { + e.printStackTrace(); + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + return false; + } + + /** + * Returns true if speed can be changed at this moment + * @return True if speed can be changed + */ + @Override + public boolean canSetSpeed() { + Log.d(SBMP_TAG, "canSetSpeed() 180"); + if (pmInterface == null) { + if (!ConnectPlayMediaService()) { + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + if (pmInterface != null) { + // Can't set speed if the service isn't connected + try { + return pmInterface.canSetSpeed(ServiceBackedMediaPlayer.this.sessionId); + } catch (RemoteException e) { + e.printStackTrace(); + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + return false; + } + + void error(int what, int extra) { + owningMediaPlayer.lock.lock(); + Log.e(SBMP_TAG, "error(" + what + ", " + extra + ")"); + try { + if (!this.isErroring) { + this.isErroring = true; + owningMediaPlayer.state = State.ERROR; + if (owningMediaPlayer.onErrorListener != null) { + if (owningMediaPlayer.onErrorListener.onError(owningMediaPlayer, what, extra)) { + return; + } + } + if (owningMediaPlayer.onCompletionListener != null) { + owningMediaPlayer.onCompletionListener.onCompletion(owningMediaPlayer); + } + } + } + finally { + this.isErroring = false; + owningMediaPlayer.lock.unlock(); + } + } + + protected void finalize() throws Throwable { + owningMediaPlayer.lock.lock(); + try { + Log.d(SBMP_TAG, "finalize() 224"); + this.release(); + } + finally { + owningMediaPlayer.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 + */ + @Override + public float getCurrentPitchStepsAdjustment() { + Log.d(SBMP_TAG, "getCurrentPitchStepsAdjustment() 240"); + if (pmInterface == null) { + if (!ConnectPlayMediaService()) { + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + if (pmInterface != null) { + // Can't set pitch if the service isn't connected + try { + return pmInterface.getCurrentPitchStepsAdjustment( + ServiceBackedMediaPlayer.this.sessionId); + } catch (RemoteException e) { + e.printStackTrace(); + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + return 0f; + } + + /** + * Functions identically to android.media.MediaPlayer.getCurrentPosition() + * @return Current position (in milliseconds) + */ + @Override + public int getCurrentPosition() { + if (pmInterface == null) { + if (!ConnectPlayMediaService()) { + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + try { + return pmInterface.getCurrentPosition( + ServiceBackedMediaPlayer.this.sessionId); + } catch (RemoteException e) { + e.printStackTrace(); + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + return 0; + } + + /** + * Returns the current speed multiplier. Defaults to 1.0 (normal speed) + * @return The current speed multiplier + */ + @Override + public float getCurrentSpeedMultiplier() { + Log.d(SBMP_TAG, "getCurrentSpeedMultiplier() 286"); + if (pmInterface == null) { + if (!ConnectPlayMediaService()) { + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + if (pmInterface != null) { + // Can't set speed if the service isn't connected + try { + return pmInterface.getCurrentSpeedMultiplier( + ServiceBackedMediaPlayer.this.sessionId); + } catch (RemoteException e) { + e.printStackTrace(); + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + return 1; + } + + /** + * Functions identically to android.media.MediaPlayer.getDuration() + * @return Length of the track (in milliseconds) + */ + @Override + public int getDuration() { + Log.d(SBMP_TAG, "getDuration() 311"); + if (pmInterface == null) { + if (!ConnectPlayMediaService()) { + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + try { + return pmInterface.getDuration(ServiceBackedMediaPlayer.this.sessionId); + } catch (RemoteException e) { + e.printStackTrace(); + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + return 0; + } + + /** + * Get the maximum value that can be passed to setPlaybackSpeed + * @return The maximum speed multiplier + */ + @Override + public float getMaxSpeedMultiplier() { + Log.d(SBMP_TAG, "getMaxSpeedMultiplier() 332"); + if (pmInterface == null) { + if (!ConnectPlayMediaService()) { + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + if (pmInterface != null) { + // Can't set speed if the Service isn't connected + try { + return pmInterface.getMaxSpeedMultiplier( + ServiceBackedMediaPlayer.this.sessionId); + } catch (RemoteException e) { + e.printStackTrace(); + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + return 1f; + } + + /** + * Get the minimum value that can be passed to setPlaybackSpeed + * @return The minimum speed multiplier + */ + @Override + public float getMinSpeedMultiplier() { + Log.d(SBMP_TAG, "getMinSpeedMultiplier() 357"); + if (pmInterface == null) { + if (!ConnectPlayMediaService()) { + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + if (pmInterface != null) { + // Can't set speed if the Service isn't connected + try { + return pmInterface.getMinSpeedMultiplier( + ServiceBackedMediaPlayer.this.sessionId); + } catch (RemoteException e) { + e.printStackTrace(); + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + return 1f; + } + + public int getServiceVersionCode() { + Log.d(SBMP_TAG, "getVersionCode"); + if (pmInterface == null) { + if (!ConnectPlayMediaService()) { + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + try { + return pmInterface.getVersionCode(); + } catch (RemoteException e) { + e.printStackTrace(); + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + return 0; + } + + public String getServiceVersionName() { + Log.d(SBMP_TAG, "getVersionName"); + if (pmInterface == null) { + if (!ConnectPlayMediaService()) { + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + try { + return pmInterface.getVersionName(); + } catch (RemoteException e) { + e.printStackTrace(); + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + return ""; + } + + public boolean isConnected() { + return (pmInterface != null); + } + + /** + * Functions identically to android.media.MediaPlayer.isLooping() + * @return True if the track is looping + */ + @Override + public boolean isLooping() { + Log.d(SBMP_TAG, "isLooping() 382"); + if (pmInterface == null) { + if (!ConnectPlayMediaService()) { + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + try { + return pmInterface.isLooping(ServiceBackedMediaPlayer.this.sessionId); + } catch (RemoteException e) { + e.printStackTrace(); + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + return false; + } + + /** + * Functions identically to android.media.MediaPlayer.isPlaying() + * @return True if the track is playing + */ + @Override + public boolean isPlaying() { + if (pmInterface == null) { + if (!ConnectPlayMediaService()) { + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + if (pmInterface != null) { + try { + return pmInterface.isPlaying(ServiceBackedMediaPlayer.this.sessionId); + } catch (RemoteException e) { + e.printStackTrace(); + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + return false; + } + + /** + * Functions identically to android.media.MediaPlayer.pause() + * Pauses the track + */ + @Override + public void pause() { + Log.d(SBMP_TAG, "pause() 424"); + if (pmInterface == null) { + if (!ConnectPlayMediaService()) { + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + try { + pmInterface.pause(ServiceBackedMediaPlayer.this.sessionId); + } catch (RemoteException e) { + e.printStackTrace(); + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + + /** + * Functions identically to android.media.MediaPlayer.prepare() + * Prepares the track. This or prepareAsync must be called before start() + */ + @Override + public void prepare() throws IllegalStateException, IOException { + Log.d(SBMP_TAG, "prepare() 444"); + Log.d(SBMP_TAG, "onPreparedCallback is: " + ((this.mOnPreparedCallback == null) ? "null" : "non-null")); + if (pmInterface == null) { + Log.d(SBMP_TAG, "prepare: pmInterface is null"); + if (!ConnectPlayMediaService()) { + Log.d(SBMP_TAG, "prepare: Failed to connect play media service"); + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + if (pmInterface != null) { + Log.d(SBMP_TAG, "prepare: pmInterface isn't null"); + try { + Log.d(SBMP_TAG, "prepare: Remote invoke pmInterface.prepare(" + ServiceBackedMediaPlayer.this.sessionId + ")"); + pmInterface.prepare(ServiceBackedMediaPlayer.this.sessionId); + Log.d(SBMP_TAG, "prepare: prepared"); + } catch (RemoteException e) { + Log.d(SBMP_TAG, "prepare: RemoteException"); + e.printStackTrace(); + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + Log.d(SBMP_TAG, "Done with prepare()"); + } + + /** + * Functions identically to android.media.MediaPlayer.prepareAsync() + * Prepares the track. This or prepare must be called before start() + */ + @Override + public void prepareAsync() { + Log.d(SBMP_TAG, "prepareAsync() 469"); + if (pmInterface == null) { + if (!ConnectPlayMediaService()) { + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + try { + pmInterface.prepareAsync(ServiceBackedMediaPlayer.this.sessionId); + } catch (RemoteException e) { + e.printStackTrace(); + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + + /** + * Functions identically to android.media.MediaPlayer.release() + * Releases the underlying resources used by the media player. + */ + @Override + public void release() { + Log.d(SBMP_TAG, "release() 492"); + if (pmInterface == null) { + if (!ConnectPlayMediaService()) { + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + if (pmInterface != null) { + Log.d(SBMP_TAG, "release() 500"); + try { + pmInterface.release(ServiceBackedMediaPlayer.this.sessionId); + } catch (RemoteException e) { + e.printStackTrace(); + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + mContext.unbindService(this.mPlayMediaServiceConnection); + // Don't try to keep awake (if we were) + this.setWakeMode(mContext, 0); + pmInterface = null; + this.sessionId = 0; + } + + if ((this.mWakeLock != null) && this.mWakeLock.isHeld()) { + Log.d(SBMP_TAG, "Releasing wakelock"); + this.mWakeLock.release(); + } + } + + /** + * Functions identically to android.media.MediaPlayer.reset() + * Resets the track to idle state + */ + @Override + public void reset() { + Log.d(SBMP_TAG, "reset() 523"); + if (pmInterface == null) { + if (!ConnectPlayMediaService()) { + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + try { + pmInterface.reset(ServiceBackedMediaPlayer.this.sessionId); + } catch (RemoteException e) { + e.printStackTrace(); + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + + /** + * Functions identically to android.media.MediaPlayer.seekTo(int msec) + * Seeks to msec in the track + */ + @Override + public void seekTo(int msec) throws IllegalStateException { + Log.d(SBMP_TAG, "seekTo(" + msec + ")"); + if (pmInterface == null) { + if (!ConnectPlayMediaService()) { + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + try { + pmInterface.seekTo(ServiceBackedMediaPlayer.this.sessionId, msec); + } catch (RemoteException e) { + e.printStackTrace(); + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + + /** + * Functions identically to android.media.MediaPlayer.setAudioStreamType(int streamtype) + * Sets the audio stream type. + */ + @Override + public void setAudioStreamType(int streamtype) { + Log.d(SBMP_TAG, "setAudioStreamType(" + streamtype + ")"); + if (pmInterface == null) { + if (!ConnectPlayMediaService()) { + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + try { + pmInterface.setAudioStreamType( + ServiceBackedMediaPlayer.this.sessionId, + this.mAudioStreamType); + } catch (RemoteException e) { + e.printStackTrace(); + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + + + /** + * Functions identically to android.media.MediaPlayer.setDataSource(Context context, Uri uri) + * Sets uri as data source in the context given + */ + @Override + public void setDataSource(Context context, Uri uri) throws IllegalArgumentException, IllegalStateException, IOException { + Log.d(SBMP_TAG, "setDataSource(context, uri)"); + if (pmInterface == null) { + if (!ConnectPlayMediaService()) { + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + try { + pmInterface.setDataSourceUri( + ServiceBackedMediaPlayer.this.sessionId, + uri); + } catch (RemoteException e) { + e.printStackTrace(); + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + + /** + * Functions identically to android.media.MediaPlayer.setDataSource(String path) + * Sets the data source of the track to a file given. + */ + @Override + public void setDataSource(String path) throws IllegalArgumentException, IllegalStateException, IOException { + Log.d(SBMP_TAG, "setDataSource(path)"); + if (pmInterface == null) { + if (!ConnectPlayMediaService()) { + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + if (pmInterface == null) { + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + else { + try { + pmInterface.setDataSourceString( + ServiceBackedMediaPlayer.this.sessionId, + path); + } catch (RemoteException e) { + e.printStackTrace(); + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + } + + /** + * 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. + */ + @Override + public void setEnableSpeedAdjustment(boolean enableSpeedAdjustment) { + // TODO: This has no business being here, I think + owningMediaPlayer.lock.lock(); + Log.d(SBMP_TAG, "setEnableSpeedAdjustment(enableSpeedAdjustment)"); + try { + if (pmInterface == null) { + if (!ConnectPlayMediaService()) { + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + if (pmInterface != null) { + // Can't set speed if the Service isn't connected + try { + pmInterface.setEnableSpeedAdjustment( + ServiceBackedMediaPlayer.this.sessionId, + enableSpeedAdjustment); + } catch (RemoteException e) { + e.printStackTrace(); + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + } + finally { + owningMediaPlayer.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 + */ + @Override + public void setLooping(boolean loop) { + Log.d(SBMP_TAG, "setLooping(" + loop + ")"); + if (pmInterface == null) { + if (!ConnectPlayMediaService()) { + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + try { + pmInterface.setLooping(ServiceBackedMediaPlayer.this.sessionId, loop); + } catch (RemoteException e) { + e.printStackTrace(); + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + + /** + * 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 + */ + @Override + public void setPitchStepsAdjustment(float pitchSteps) { + Log.d(SBMP_TAG, "setPitchStepsAdjustment(" + pitchSteps + ")"); + if (pmInterface == null) { + if (!ConnectPlayMediaService()) { + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + if (pmInterface != null) { + // Can't set speed if the Service isn't connected + try { + pmInterface.setPitchStepsAdjustment( + ServiceBackedMediaPlayer.this.sessionId, + pitchSteps); + } catch (RemoteException e) { + e.printStackTrace(); + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + } + + /** + * 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 + */ + @Override + public void setPlaybackPitch(float f) { + Log.d(SBMP_TAG, "setPlaybackPitch(" + f + ")"); + if (pmInterface == null) { + if (!ConnectPlayMediaService()) { + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + if (pmInterface != null) { + // Can't set speed if the Service isn't connected + try { + pmInterface.setPlaybackPitch( + ServiceBackedMediaPlayer.this.sessionId, + f); + } catch (RemoteException e) { + e.printStackTrace(); + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + } + + /** + * 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 + */ + @Override + public void setPlaybackSpeed(float f) { + Log.d(SBMP_TAG, "setPlaybackSpeed(" + f + ")"); + if (pmInterface == null) { + if (!ConnectPlayMediaService()) { + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + if (pmInterface != null) { + // Can't set speed if the Service isn't connected + try { + pmInterface.setPlaybackSpeed( + ServiceBackedMediaPlayer.this.sessionId, + f); + } catch (RemoteException e) { + e.printStackTrace(); + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + } + + @Override + public void setSpeedAdjustmentAlgorithm(int algorithm) { + if (pmInterface == null) { + if (!ConnectPlayMediaService()) { + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + try { + pmInterface.setSpeedAdjustmentAlgorithm( + ServiceBackedMediaPlayer.this.sessionId, + algorithm); + } catch (RemoteException e) { + e.printStackTrace(); + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + + /** + * Functions identically to android.media.MediaPlayer.setVolume(float leftVolume, float rightVolume) + * Sets the stereo volume + */ + @Override + public void setVolume(float leftVolume, float rightVolume) { + Log.d(SBMP_TAG, "setVolume(" + leftVolume + ", " + rightVolume + ")"); + if (pmInterface == null) { + if (!ConnectPlayMediaService()) { + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + try { + pmInterface.setVolume( + ServiceBackedMediaPlayer.this.sessionId, + leftVolume, + rightVolume); + } catch (RemoteException e) { + e.printStackTrace(); + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + + /** + * 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. + */ + @Override + // This does not just call .setWakeMode() in the Service because doing so + // would add a permission requirement to the Service. Do it here, and it's + // the client app's responsibility to request that permission + public void setWakeMode(Context context, int mode) { + Log.d(SBMP_TAG, "setWakeMode(context, " + mode + ")"); + if ((this.mWakeLock != null) + && (this.mWakeLock.isHeld())) { + this.mWakeLock.release(); + } + if (mode != 0) { + if (this.mWakeLock == null) { + PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE); + // Since mode can't be changed on the fly, we have to allocate a new one + this.mWakeLock = pm.newWakeLock(mode, this.getClass().getName()); + } + + this.mWakeLock.acquire(); + } + } + + private IOnBufferingUpdateListenerCallback_0_8.Stub mOnBufferingUpdateCallback = null; + private void setOnBufferingUpdateCallback(IPlayMedia_0_8 iface) { + try { + if (this.mOnBufferingUpdateCallback == null) { + mOnBufferingUpdateCallback = new IOnBufferingUpdateListenerCallback_0_8.Stub() { + public void onBufferingUpdate(int percent) + throws RemoteException { + owningMediaPlayer.lock.lock(); + try { + if ((owningMediaPlayer.onBufferingUpdateListener != null) + && (owningMediaPlayer.mpi == ServiceBackedMediaPlayer.this)) { + owningMediaPlayer.onBufferingUpdateListener.onBufferingUpdate(owningMediaPlayer, percent); + } + } + finally { + owningMediaPlayer.lock.unlock(); + } + } + }; + } + iface.registerOnBufferingUpdateCallback( + ServiceBackedMediaPlayer.this.sessionId, + mOnBufferingUpdateCallback); + } catch (RemoteException e) { + e.printStackTrace(); + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + + private IOnCompletionListenerCallback_0_8.Stub mOnCompletionCallback = null; + private void setOnCompletionCallback(IPlayMedia_0_8 iface) { + try { + if (this.mOnCompletionCallback == null) { + this.mOnCompletionCallback = new IOnCompletionListenerCallback_0_8.Stub() { + public void onCompletion() throws RemoteException { + owningMediaPlayer.lock.lock(); + Log.d(SBMP_TAG, "onCompletionListener being called"); + try { + if (owningMediaPlayer.onCompletionListener != null) { + owningMediaPlayer.onCompletionListener.onCompletion(owningMediaPlayer); + } + } + finally { + owningMediaPlayer.lock.unlock(); + } + } + }; + } + iface.registerOnCompletionCallback( + ServiceBackedMediaPlayer.this.sessionId, + this.mOnCompletionCallback); + } catch (RemoteException e) { + e.printStackTrace(); + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + + private IOnErrorListenerCallback_0_8.Stub mOnErrorCallback = null; + private void setOnErrorCallback(IPlayMedia_0_8 iface) { + try { + if (this.mOnErrorCallback == null) { + this.mOnErrorCallback = new IOnErrorListenerCallback_0_8.Stub() { + public boolean onError(int what, int extra) throws RemoteException { + owningMediaPlayer.lock.lock(); + try { + if (owningMediaPlayer.onErrorListener != null) { + return owningMediaPlayer.onErrorListener.onError(owningMediaPlayer, what, extra); + } + return false; + } + finally { + owningMediaPlayer.lock.unlock(); + } + } + }; + } + iface.registerOnErrorCallback( + ServiceBackedMediaPlayer.this.sessionId, + this.mOnErrorCallback); + } catch (RemoteException e) { + e.printStackTrace(); + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + + private IOnInfoListenerCallback_0_8.Stub mOnInfoCallback = null; + private void setOnInfoCallback(IPlayMedia_0_8 iface) { + try { + if (this.mOnInfoCallback == null) { + this.mOnInfoCallback = new IOnInfoListenerCallback_0_8.Stub() { + public boolean onInfo(int what, int extra) throws RemoteException { + owningMediaPlayer.lock.lock(); + try { + if ((owningMediaPlayer.onInfoListener != null) + && (owningMediaPlayer.mpi == ServiceBackedMediaPlayer.this)) { + return owningMediaPlayer.onInfoListener.onInfo(owningMediaPlayer, what, extra); + } + } + finally { + owningMediaPlayer.lock.unlock(); + } + return false; + } + }; + } + iface.registerOnInfoCallback( + ServiceBackedMediaPlayer.this.sessionId, + this.mOnInfoCallback); + } catch (RemoteException e) { + e.printStackTrace(); + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + + private IOnPitchAdjustmentAvailableChangedListenerCallback_0_8.Stub mOnPitchAdjustmentAvailableChangedCallback = null; + private void setOnPitchAdjustmentAvailableChangedListener(IPlayMedia_0_8 iface) { + try { + if (this.mOnPitchAdjustmentAvailableChangedCallback == null) { + this.mOnPitchAdjustmentAvailableChangedCallback = new IOnPitchAdjustmentAvailableChangedListenerCallback_0_8.Stub() { + public void onPitchAdjustmentAvailableChanged( + boolean pitchAdjustmentAvailable) + throws RemoteException { + owningMediaPlayer.lock.lock(); + try { + if (owningMediaPlayer.onPitchAdjustmentAvailableChangedListener != null) { + owningMediaPlayer.onPitchAdjustmentAvailableChangedListener.onPitchAdjustmentAvailableChanged(owningMediaPlayer, pitchAdjustmentAvailable); + } + } + finally { + owningMediaPlayer.lock.unlock(); + } + } + }; + } + iface.registerOnPitchAdjustmentAvailableChangedCallback( + ServiceBackedMediaPlayer.this.sessionId, + this.mOnPitchAdjustmentAvailableChangedCallback); + } catch (RemoteException e) { + e.printStackTrace(); + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + + private IOnPreparedListenerCallback_0_8.Stub mOnPreparedCallback = null; + private void setOnPreparedCallback(IPlayMedia_0_8 iface) { + try { + if (this.mOnPreparedCallback == null) { + this.mOnPreparedCallback = new IOnPreparedListenerCallback_0_8.Stub() { + public void onPrepared() throws RemoteException { + owningMediaPlayer.lock.lock(); + Log.d(SBMP_TAG, "setOnPreparedCallback.mOnPreparedCallback.onPrepared 1050"); + try { + Log.d(SBMP_TAG, "owningMediaPlayer.onPreparedListener is " + ((owningMediaPlayer.onPreparedListener == null) ? "null" : "non-null")); + Log.d(SBMP_TAG, "owningMediaPlayer.mpi is " + ((owningMediaPlayer.mpi == ServiceBackedMediaPlayer.this) ? "this" : "not this")); + ServiceBackedMediaPlayer.this.lockMuteOnPreparedCount.lock(); + try { + if (ServiceBackedMediaPlayer.this.muteOnPreparedCount > 0) { + ServiceBackedMediaPlayer.this.muteOnPreparedCount--; + } + else { + ServiceBackedMediaPlayer.this.muteOnPreparedCount = 0; + if (ServiceBackedMediaPlayer.this.owningMediaPlayer.onPreparedListener != null) { + owningMediaPlayer.onPreparedListener.onPrepared(owningMediaPlayer); + } + } + } + finally { + ServiceBackedMediaPlayer.this.lockMuteOnPreparedCount.unlock(); + } + } + finally { + owningMediaPlayer.lock.unlock(); + } + } + }; + } + iface.registerOnPreparedCallback( + ServiceBackedMediaPlayer.this.sessionId, + this.mOnPreparedCallback); + } catch (RemoteException e) { + e.printStackTrace(); + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + + private IOnSeekCompleteListenerCallback_0_8.Stub mOnSeekCompleteCallback = null; + private void setOnSeekCompleteCallback(IPlayMedia_0_8 iface) { + try { + if (this.mOnSeekCompleteCallback == null) { + this.mOnSeekCompleteCallback = new IOnSeekCompleteListenerCallback_0_8.Stub() { + public void onSeekComplete() throws RemoteException { + Log.d(SBMP_TAG, "onSeekComplete() 941"); + owningMediaPlayer.lock.lock(); + try { + if (ServiceBackedMediaPlayer.this.muteOnSeekCount > 0) { + Log.d(SBMP_TAG, "The next " + ServiceBackedMediaPlayer.this.muteOnSeekCount + " seek events are muted (counting this one)"); + ServiceBackedMediaPlayer.this.muteOnSeekCount--; + } + else { + ServiceBackedMediaPlayer.this.muteOnSeekCount = 0; + Log.d(SBMP_TAG, "Attempting to invoke next seek event"); + if (ServiceBackedMediaPlayer.this.owningMediaPlayer.onSeekCompleteListener != null) { + Log.d(SBMP_TAG, "Invoking onSeekComplete"); + owningMediaPlayer.onSeekCompleteListener.onSeekComplete(owningMediaPlayer); + } + } + } + finally { + owningMediaPlayer.lock.unlock(); + } + } + }; + } + iface.registerOnSeekCompleteCallback( + ServiceBackedMediaPlayer.this.sessionId, + this.mOnSeekCompleteCallback); + } catch (RemoteException e) { + e.printStackTrace(); + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + + private IOnSpeedAdjustmentAvailableChangedListenerCallback_0_8.Stub mOnSpeedAdjustmentAvailableChangedCallback = null; + private void setOnSpeedAdjustmentAvailableChangedCallback(IPlayMedia_0_8 iface) { + try { + Log.d(SBMP_TAG, "Setting the service of on speed adjustment available changed"); + if (this.mOnSpeedAdjustmentAvailableChangedCallback == null) { + this.mOnSpeedAdjustmentAvailableChangedCallback = new IOnSpeedAdjustmentAvailableChangedListenerCallback_0_8.Stub() { + public void onSpeedAdjustmentAvailableChanged( + boolean speedAdjustmentAvailable) + throws RemoteException { + owningMediaPlayer.lock.lock(); + try { + if (owningMediaPlayer.onSpeedAdjustmentAvailableChangedListener != null) { + owningMediaPlayer.onSpeedAdjustmentAvailableChangedListener.onSpeedAdjustmentAvailableChanged(owningMediaPlayer, speedAdjustmentAvailable); + } + } + finally { + owningMediaPlayer.lock.unlock(); + } + } + }; + } + iface.registerOnSpeedAdjustmentAvailableChangedCallback( + ServiceBackedMediaPlayer.this.sessionId, + this.mOnSpeedAdjustmentAvailableChangedCallback); + } catch (RemoteException e) { + e.printStackTrace(); + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + + /** + * Functions identically to android.media.MediaPlayer.start() + * Starts a track playing + */ + @Override + public void start() { + Log.d(SBMP_TAG, "start()"); + if (pmInterface == null) { + if (!ConnectPlayMediaService()) { + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + try { + pmInterface.start(ServiceBackedMediaPlayer.this.sessionId); + } catch (RemoteException e) { + e.printStackTrace(); + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + + /** + * Functions identically to android.media.MediaPlayer.stop() + * Stops a track playing and resets its position to the start. + */ + @Override + public void stop() { + Log.d(SBMP_TAG, "stop()"); + if (pmInterface == null) { + if (!ConnectPlayMediaService()) { + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } + try { + pmInterface.stop(ServiceBackedMediaPlayer.this.sessionId); + } catch (RemoteException e) { + e.printStackTrace(); + ServiceBackedMediaPlayer.this.error(MediaPlayer.MEDIA_ERROR_UNKNOWN, 0); + } + } +} \ No newline at end of file diff --git a/src/com/aocate/media/SpeedAdjustmentAlgorithm.java b/src/com/aocate/media/SpeedAdjustmentAlgorithm.java new file mode 100644 index 000000000..d337a0452 --- /dev/null +++ b/src/com/aocate/media/SpeedAdjustmentAlgorithm.java @@ -0,0 +1,31 @@ +// 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; + +public class SpeedAdjustmentAlgorithm { + /** + * Use this to use the user-specified algorithm + */ + public static int DEFAULT = 0; + + /** + * Better for voice audio + */ + public static int SONIC = 1; + /** + * Better for music audio + */ + public static int WSOLA = 2; +} diff --git a/src/com/aocate/presto/service/IDeathCallback_0_8.aidl b/src/com/aocate/presto/service/IDeathCallback_0_8.aidl new file mode 100644 index 000000000..6bdc76801 --- /dev/null +++ b/src/com/aocate/presto/service/IDeathCallback_0_8.aidl @@ -0,0 +1,18 @@ +// 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.presto.service; + +oneway interface IDeathCallback_0_8 { +} \ No newline at end of file diff --git a/src/com/aocate/presto/service/IOnBufferingUpdateListenerCallback_0_8.aidl b/src/com/aocate/presto/service/IOnBufferingUpdateListenerCallback_0_8.aidl new file mode 100644 index 000000000..7357e402e --- /dev/null +++ b/src/com/aocate/presto/service/IOnBufferingUpdateListenerCallback_0_8.aidl @@ -0,0 +1,19 @@ +// 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.presto.service; + +interface IOnBufferingUpdateListenerCallback_0_8 { + void onBufferingUpdate(int percent); +} \ No newline at end of file diff --git a/src/com/aocate/presto/service/IOnCompletionListenerCallback_0_8.aidl b/src/com/aocate/presto/service/IOnCompletionListenerCallback_0_8.aidl new file mode 100644 index 000000000..d5edea729 --- /dev/null +++ b/src/com/aocate/presto/service/IOnCompletionListenerCallback_0_8.aidl @@ -0,0 +1,19 @@ +// 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.presto.service; + +interface IOnCompletionListenerCallback_0_8 { + void onCompletion(); +} \ No newline at end of file diff --git a/src/com/aocate/presto/service/IOnErrorListenerCallback_0_8.aidl b/src/com/aocate/presto/service/IOnErrorListenerCallback_0_8.aidl new file mode 100644 index 000000000..2c4f2df3e --- /dev/null +++ b/src/com/aocate/presto/service/IOnErrorListenerCallback_0_8.aidl @@ -0,0 +1,19 @@ +// 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.presto.service; + +interface IOnErrorListenerCallback_0_8 { + boolean onError(int what, int extra); +} diff --git a/src/com/aocate/presto/service/IOnInfoListenerCallback_0_8.aidl b/src/com/aocate/presto/service/IOnInfoListenerCallback_0_8.aidl new file mode 100644 index 000000000..9dbd1d260 --- /dev/null +++ b/src/com/aocate/presto/service/IOnInfoListenerCallback_0_8.aidl @@ -0,0 +1,19 @@ +// 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.presto.service; + +interface IOnInfoListenerCallback_0_8 { + boolean onInfo(int what, int extra); +} \ No newline at end of file diff --git a/src/com/aocate/presto/service/IOnPitchAdjustmentAvailableChangedListenerCallback_0_8.aidl b/src/com/aocate/presto/service/IOnPitchAdjustmentAvailableChangedListenerCallback_0_8.aidl new file mode 100644 index 000000000..41223a97b --- /dev/null +++ b/src/com/aocate/presto/service/IOnPitchAdjustmentAvailableChangedListenerCallback_0_8.aidl @@ -0,0 +1,19 @@ +// 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.presto.service; + +interface IOnPitchAdjustmentAvailableChangedListenerCallback_0_8 { + void onPitchAdjustmentAvailableChanged(boolean pitchAdjustmentAvailable); +} \ No newline at end of file diff --git a/src/com/aocate/presto/service/IOnPreparedListenerCallback_0_8.aidl b/src/com/aocate/presto/service/IOnPreparedListenerCallback_0_8.aidl new file mode 100644 index 000000000..7be8f1237 --- /dev/null +++ b/src/com/aocate/presto/service/IOnPreparedListenerCallback_0_8.aidl @@ -0,0 +1,19 @@ +// 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.presto.service; + +interface IOnPreparedListenerCallback_0_8 { + void onPrepared(); +} \ No newline at end of file diff --git a/src/com/aocate/presto/service/IOnSeekCompleteListenerCallback_0_8.aidl b/src/com/aocate/presto/service/IOnSeekCompleteListenerCallback_0_8.aidl new file mode 100644 index 000000000..5bdda98b6 --- /dev/null +++ b/src/com/aocate/presto/service/IOnSeekCompleteListenerCallback_0_8.aidl @@ -0,0 +1,19 @@ +// 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.presto.service; + +interface IOnSeekCompleteListenerCallback_0_8 { + void onSeekComplete(); +} \ No newline at end of file diff --git a/src/com/aocate/presto/service/IOnSpeedAdjustmentAvailableChangedListenerCallback_0_8.aidl b/src/com/aocate/presto/service/IOnSpeedAdjustmentAvailableChangedListenerCallback_0_8.aidl new file mode 100644 index 000000000..a69c1cf34 --- /dev/null +++ b/src/com/aocate/presto/service/IOnSpeedAdjustmentAvailableChangedListenerCallback_0_8.aidl @@ -0,0 +1,19 @@ +// 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.presto.service; + +interface IOnSpeedAdjustmentAvailableChangedListenerCallback_0_8 { + void onSpeedAdjustmentAvailableChanged(boolean speedAdjustmentAvailable); +} \ No newline at end of file diff --git a/src/com/aocate/presto/service/IPlayMedia_0_8.aidl b/src/com/aocate/presto/service/IPlayMedia_0_8.aidl new file mode 100644 index 000000000..12a6047de --- /dev/null +++ b/src/com/aocate/presto/service/IPlayMedia_0_8.aidl @@ -0,0 +1,75 @@ +// 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.presto.service; + +import com.aocate.presto.service.IDeathCallback_0_8; +import com.aocate.presto.service.IOnBufferingUpdateListenerCallback_0_8; +import com.aocate.presto.service.IOnCompletionListenerCallback_0_8; +import com.aocate.presto.service.IOnErrorListenerCallback_0_8; +import com.aocate.presto.service.IOnPitchAdjustmentAvailableChangedListenerCallback_0_8; +import com.aocate.presto.service.IOnPreparedListenerCallback_0_8; +import com.aocate.presto.service.IOnSeekCompleteListenerCallback_0_8; +import com.aocate.presto.service.IOnSpeedAdjustmentAvailableChangedListenerCallback_0_8; +import com.aocate.presto.service.IOnInfoListenerCallback_0_8; + +interface IPlayMedia_0_8 { + boolean canSetPitch(long sessionId); + boolean canSetSpeed(long sessionId); + float getCurrentPitchStepsAdjustment(long sessionId); + int getCurrentPosition(long sessionId); + float getCurrentSpeedMultiplier(long sessionId); + int getDuration(long sessionId); + float getMaxSpeedMultiplier(long sessionId); + float getMinSpeedMultiplier(long sessionId); + int getVersionCode(); + String getVersionName(); + boolean isLooping(long sessionId); + boolean isPlaying(long sessionId); + void pause(long sessionId); + void prepare(long sessionId); + void prepareAsync(long sessionId); + void registerOnBufferingUpdateCallback(long sessionId, IOnBufferingUpdateListenerCallback_0_8 cb); + void registerOnCompletionCallback(long sessionId, IOnCompletionListenerCallback_0_8 cb); + void registerOnErrorCallback(long sessionId, IOnErrorListenerCallback_0_8 cb); + void registerOnInfoCallback(long sessionId, IOnInfoListenerCallback_0_8 cb); + void registerOnPitchAdjustmentAvailableChangedCallback(long sessionId, IOnPitchAdjustmentAvailableChangedListenerCallback_0_8 cb); + void registerOnPreparedCallback(long sessionId, IOnPreparedListenerCallback_0_8 cb); + void registerOnSeekCompleteCallback(long sessionId, IOnSeekCompleteListenerCallback_0_8 cb); + void registerOnSpeedAdjustmentAvailableChangedCallback(long sessionId, IOnSpeedAdjustmentAvailableChangedListenerCallback_0_8 cb); + void release(long sessionId); + void reset(long sessionId); + void seekTo(long sessionId, int msec); + void setAudioStreamType(long sessionId, int streamtype); + void setDataSourceString(long sessionId, String path); + void setDataSourceUri(long sessionId, in Uri uri); + void setEnableSpeedAdjustment(long sessionId, boolean enableSpeedAdjustment); + void setLooping(long sessionId, boolean looping); + void setPitchStepsAdjustment(long sessionId, float pitchSteps); + void setPlaybackPitch(long sessionId, float f); + void setPlaybackSpeed(long sessionId, float f); + void setSpeedAdjustmentAlgorithm(long sessionId, int algorithm); + void setVolume(long sessionId, float left, float right); + void start(long sessionId); + long startSession(IDeathCallback_0_8 cb); + void stop(long sessionId); + void unregisterOnBufferingUpdateCallback(long sessionId, IOnBufferingUpdateListenerCallback_0_8 cb); + void unregisterOnCompletionCallback(long sessionId, IOnCompletionListenerCallback_0_8 cb); + void unregisterOnErrorCallback(long sessionId, IOnErrorListenerCallback_0_8 cb); + void unregisterOnInfoCallback(long sessionId, IOnInfoListenerCallback_0_8 cb); + void unregisterOnPitchAdjustmentAvailableChangedCallback(long sessionId, IOnPitchAdjustmentAvailableChangedListenerCallback_0_8 cb); + void unregisterOnPreparedCallback(long sessionId, IOnPreparedListenerCallback_0_8 cb); + void unregisterOnSeekCompleteCallback(long sessionId, IOnSeekCompleteListenerCallback_0_8 cb); + void unregisterOnSpeedAdjustmentAvailableChangedCallback(long sessionId, IOnSpeedAdjustmentAvailableChangedListenerCallback_0_8 cb); +} \ No newline at end of file -- cgit v1.2.3