From: David A. Velasco Date: Tue, 12 Feb 2013 10:26:38 +0000 (+0100) Subject: Fixed bad behaviours when an error occurrs during audio playback X-Git-Tag: oc-android-1.4.3~39^2~63 X-Git-Url: http://git.linex4red.de/pub/Android/ownCloud.git/commitdiff_plain/11c1ad8f78b5453a5a4e7c93014eeaddcf453087 Fixed bad behaviours when an error occurrs during audio playback --- diff --git a/res/values/strings.xml b/res/values/strings.xml index e00771ab..55860c09 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -149,7 +149,12 @@ No media file found No account provided File not in a valid account - + Media cannot be streamed + Media cannot be played + Security error trying to play %1$s + Input error trying to play %1$s + Unexpected error tryung to playe %1$s + 15 Minutes 30 Minutes diff --git a/src/com/owncloud/android/media/MediaService.java b/src/com/owncloud/android/media/MediaService.java index 987d94e3..48aa8417 100644 --- a/src/com/owncloud/android/media/MediaService.java +++ b/src/com/owncloud/android/media/MediaService.java @@ -35,6 +35,7 @@ import android.net.wifi.WifiManager.WifiLock; import android.os.IBinder; import android.os.PowerManager; import android.util.Log; +import android.widget.MediaController; import android.widget.Toast; import java.io.IOException; @@ -66,13 +67,18 @@ public class MediaService extends Service implements OnCompletionListener, OnPre /// Keys to add extras to the action public static final String EXTRA_FILE = MY_PACKAGE + ".extra.FILE"; public static final String EXTRA_ACCOUNT = MY_PACKAGE + ".extra.ACCOUNT"; + + /** Error code for specific messages - see regular error codes at {@link MediaPlayer} */ + public static final int OC_MEDIA_ERROR = 0; + + /** Time To keep the control panel visible when the user does not use it */ + public static final int MEDIA_CONTROL_LIFE = 5000; /** Volume to set when audio focus is lost and ducking is allowed */ private static final float DUCK_VOLUME = 0.1f; /** Media player instance */ private MediaPlayer mPlayer = null; - /** Reference to the system AudioManager */ private AudioManager mAudioManager = null; @@ -121,7 +127,9 @@ public class MediaService extends Service implements OnCompletionListener, OnPre /** Interface to access the service through binding */ private IBinder mBinder; - + + /** Control panel shown to the user to control the playback, to register through binding */ + private MediaController mMediaController; /** @@ -166,7 +174,7 @@ public class MediaService extends Service implements OnCompletionListener, OnPre * @param intent Intent received in the request with the data to identify the file to play. */ private void processPlayFileRequest(Intent intent) { - if (mState == State.PLAYING || mState == State.PAUSED || mState == State.STOPPED) { + if (mState != State.PREPARING) { mFile = intent.getExtras().getParcelable(EXTRA_FILE); mAccount = intent.getExtras().getParcelable(EXTRA_ACCOUNT); tryToGetAudioFocus(); @@ -237,12 +245,10 @@ public class MediaService extends Service implements OnCompletionListener, OnPre * @param force When 'true', the playback is stopped no matter the value of mState */ protected void processStopRequest(boolean force) { - if (mState == State.PLAYING || mState == State.PAUSED || mState == State.STOPPED || force) { + if (mState != State.PREPARING || force) { mState = State.STOPPED; - mFile = null; mAccount = null; - releaseResources(true); giveUpAudioFocus(); stopSelf(); // service is no longer necessary @@ -374,26 +380,32 @@ public class MediaService extends Service implements OnCompletionListener, OnPre } catch (SecurityException e) { Log.e(TAG, "SecurityException playing " + mAccount.name + mFile.getRemotePath(), e); - // TODO message to the user + Toast.makeText(this, String.format(getString(R.string.media_err_security_ex), mFile.getFileName()), Toast.LENGTH_LONG).show(); + processStopRequest(true); } catch (IOException e) { Log.e(TAG, "IOException playing " + mAccount.name + mFile.getRemotePath(), e); - // TODO message to the user + Toast.makeText(this, String.format(getString(R.string.media_err_io_ex), mFile.getFileName()), Toast.LENGTH_LONG).show(); + processStopRequest(true); } catch (IllegalStateException e) { Log.e(TAG, "IllegalStateException " + mAccount.name + mFile.getRemotePath(), e); - // TODO message to the user + Toast.makeText(this, String.format(getString(R.string.media_err_unexpected), mFile.getFileName()), Toast.LENGTH_LONG).show(); + processStopRequest(true); } catch (IllegalArgumentException e) { Log.e(TAG, "IllegalArgumentException " + mAccount.name + mFile.getRemotePath(), e); - e.printStackTrace(); - // TODO message to the user + Toast.makeText(this, String.format(getString(R.string.media_err_unexpected), mFile.getFileName()), Toast.LENGTH_LONG).show(); + processStopRequest(true); } } /** Called when media player is done playing current song. */ public void onCompletion(MediaPlayer player) { + if (mMediaController != null) { + mMediaController.hide(); + } Toast.makeText(this, String.format(getString(R.string.media_event_done, mFile.getFileName())), Toast.LENGTH_LONG).show(); processStopRequest(true); return; @@ -408,7 +420,13 @@ public class MediaService extends Service implements OnCompletionListener, OnPre public void onPrepared(MediaPlayer player) { mState = State.PLAYING; updateNotification(String.format(getString(R.string.media_state_playing), mFile.getFileName())); + if (mMediaController != null) { + mMediaController.setEnabled(true); + } configAndStartMediaPlayer(); + if (mMediaController != null) { + mMediaController.show(MEDIA_CONTROL_LIFE); + } } @@ -478,15 +496,27 @@ public class MediaService extends Service implements OnCompletionListener, OnPre * Warns the user about the error and resets the media player. */ public boolean onError(MediaPlayer mp, int what, int extra) { - // TODO FOLLOW HERE!!!!!! + Log.e(TAG, "Error in audio playback, what = " + what + ", extra = " + extra); - Toast.makeText(getApplicationContext(), "Media player error! Resetting.", - Toast.LENGTH_SHORT).show(); - Log.e(TAG, "Error: what=" + String.valueOf(what) + ", extra=" + String.valueOf(extra)); - - mState = State.STOPPED; - releaseResources(true); - giveUpAudioFocus(); + if (mMediaController != null) { + mMediaController.hide(); + } + + int messageId; + if (what == OC_MEDIA_ERROR) { + messageId = extra; + + } else if (what == MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK) { + messageId = R.string.media_err_invalid_progressive_playback; + + } else { + // what == MediaPlayer.MEDIA_ERROR_UNKNOWN or MEDIA_ERROR_SERVER_DIED + messageId = R.string.media_err_unknown; + + } + Toast.makeText(getApplicationContext(), messageId, Toast.LENGTH_SHORT).show(); + + processStopRequest(true); return true; } @@ -500,9 +530,7 @@ public class MediaService extends Service implements OnCompletionListener, OnPre public void onAudioFocusChange(int focusChange) { if (focusChange > 0) { // focus gain; check AudioManager.AUDIOFOCUS_* values - Toast.makeText(getApplicationContext(), "gained audio focus.", Toast.LENGTH_SHORT).show(); mAudioFocus = AudioFocus.FOCUS; - // restart media player with new focus settings if (mState == State.PLAYING) configAndStartMediaPlayer(); @@ -510,10 +538,7 @@ public class MediaService extends Service implements OnCompletionListener, OnPre } else if (focusChange < 0) { // focus loss; check AudioManager.AUDIOFOCUS_* values boolean canDuck = AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK == focusChange; - Toast.makeText(getApplicationContext(), "lost audio focus." + (canDuck ? "can duck" : - "no duck"), Toast.LENGTH_SHORT).show(); mAudioFocus = canDuck ? AudioFocus.NO_FOCUS_CAN_DUCK : AudioFocus.NO_FOCUS; - // start/restart/pause media player with new focus settings if (mPlayer != null && mPlayer.isPlaying()) configAndStartMediaPlayer(); @@ -588,4 +613,17 @@ public class MediaService extends Service implements OnCompletionListener, OnPre return mState; } + + protected void setMediaContoller(MediaController mediaController) { + if (mMediaController != null) { + mMediaController.hide(); + } + mMediaController = mediaController; + + } + + protected MediaController getMediaController() { + return mMediaController; + } + } diff --git a/src/com/owncloud/android/media/MediaServiceBinder.java b/src/com/owncloud/android/media/MediaServiceBinder.java index 98b506c4..39628c7c 100644 --- a/src/com/owncloud/android/media/MediaServiceBinder.java +++ b/src/com/owncloud/android/media/MediaServiceBinder.java @@ -169,6 +169,23 @@ public class MediaServiceBinder extends Binder implements MediaController.MediaP mService.startService(i); } + + public void registerMediaController(MediaController mediaController) { + mService.setMediaContoller(mediaController); + } + + public void unregisterMediaController(MediaController mediaController) { + if (mService.getMediaController() == mediaController) { + mService.setMediaContoller(null); + } + + } + + public boolean isInPlaybackState() { + MediaService.State currentState = mService.getState(); + return (currentState == MediaService.State.PLAYING || currentState == MediaService.State.PAUSED); + } + } diff --git a/src/com/owncloud/android/ui/OnSwipeTouchListener.java b/src/com/owncloud/android/ui/OnSwipeTouchListener.java new file mode 100644 index 00000000..0d8874d7 --- /dev/null +++ b/src/com/owncloud/android/ui/OnSwipeTouchListener.java @@ -0,0 +1,82 @@ +package com.owncloud.android.ui; + +import android.content.Context; +import android.util.Log; +import android.view.GestureDetector; +import android.view.GestureDetector.SimpleOnGestureListener; +import android.view.MotionEvent; +import android.view.View; +import android.view.View.OnTouchListener; +import android.widget.Toast; + +public class OnSwipeTouchListener implements OnTouchListener { + + private final Context mContext; + private final GestureDetector mGestureDetector; + + public OnSwipeTouchListener(Context context) { + mContext = context; + mGestureDetector = new GestureDetector(context, new GestureListener()); + } + + public boolean onTouch(final View v, final MotionEvent event) { + //super.onTouch(v, event); + Log.d("SWIPE", "Swipe listener touched"); + return mGestureDetector.onTouchEvent(event); + } + + private final class GestureListener extends SimpleOnGestureListener { + + private static final int SWIPE_THRESHOLD = 100; + private static final int SWIPE_VELOCITY_THRESHOLD = 100; + + @Override + public boolean onDown(MotionEvent e) { + return true; + } + + @Override + public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { + boolean result = false; + try { + float diffY = e2.getY() - e1.getY(); + float diffX = e2.getX() - e1.getX(); + if (Math.abs(diffX) > Math.abs(diffY)) { + if (Math.abs(diffX) > SWIPE_THRESHOLD && Math.abs(velocityX) > SWIPE_VELOCITY_THRESHOLD) { + if (diffX > 0) { + onSwipeRight(); + } else { + onSwipeLeft(); + } + } + } else { + if (Math.abs(diffY) > SWIPE_THRESHOLD && Math.abs(velocityY) > SWIPE_VELOCITY_THRESHOLD) { + if (diffY > 0) { + onSwipeBottom(); + } else { + onSwipeTop(); + } + } + } + } catch (Exception exception) { + exception.printStackTrace(); + } + return result; + } + } + + public void onSwipeTop() { + Toast.makeText(mContext, "top", Toast.LENGTH_SHORT).show(); + } + public void onSwipeRight() { + Toast.makeText(mContext, "right", Toast.LENGTH_SHORT).show(); + } + public void onSwipeLeft() { + Toast.makeText(mContext, "left", Toast.LENGTH_SHORT).show(); + } + public void onSwipeBottom() { + Toast.makeText(mContext, "bottom", Toast.LENGTH_SHORT).show(); + } + +} + diff --git a/src/com/owncloud/android/ui/activity/VideoActivity.java b/src/com/owncloud/android/ui/activity/VideoActivity.java index fbd0ce2d..8147af73 100644 --- a/src/com/owncloud/android/ui/activity/VideoActivity.java +++ b/src/com/owncloud/android/ui/activity/VideoActivity.java @@ -37,6 +37,7 @@ import android.widget.VideoView; import com.owncloud.android.AccountUtils; import com.owncloud.android.R; import com.owncloud.android.datamodel.OCFile; +import com.owncloud.android.media.MediaService; /** * Activity implementing a basic video player. @@ -55,10 +56,6 @@ public class VideoActivity extends Activity implements OnCompletionListener, OnP /** Key to receive the ownCloud {@link Account} where the file to play is saved as an extra value in an {@link Intent} */ public static final String EXTRA_ACCOUNT = "ACCOUNT"; - // Time To keep the control panel visible when the user does not use it - private static final int MEDIA_CONTOL_LIFE = 5000; - - private static final int OC_MEDIA_ERROR = 0; private static final String TAG = null; private OCFile mFile; // video file to play @@ -104,7 +101,7 @@ public class VideoActivity extends Activity implements OnCompletionListener, OnP mVideoPlayer.setVideoURI(Uri.parse(url)); } else { - onError(null, OC_MEDIA_ERROR, R.string.media_err_no_account); + onError(null, MediaService.OC_MEDIA_ERROR, R.string.media_err_no_account); } // create and prepare control panel for the user @@ -114,7 +111,7 @@ public class VideoActivity extends Activity implements OnCompletionListener, OnP mVideoPlayer.setMediaController(mMediaController); } else { - onError(null, OC_MEDIA_ERROR, R.string.media_err_nothing_to_play); + onError(null, MediaService.OC_MEDIA_ERROR, R.string.media_err_nothing_to_play); } } @@ -128,8 +125,8 @@ public class VideoActivity extends Activity implements OnCompletionListener, OnP */ @Override public void onPrepared(MediaPlayer vp) { - mVideoPlayer.start(); // TODO maybe unnecessary - //mMediaController.show(5000); // TODO maybe unnecessary; maybe not, it's up when the Surface notifies the VideoView about creation + mVideoPlayer.start(); + mMediaController.show(5000); } @@ -163,7 +160,7 @@ public class VideoActivity extends Activity implements OnCompletionListener, OnP if (mVideoPlayer.getWindowToken() != null) { int messageId; - if (what == OC_MEDIA_ERROR) { + if (what == MediaService.OC_MEDIA_ERROR) { messageId = extra; } else if (what == MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK) { @@ -252,7 +249,7 @@ public class VideoActivity extends Activity implements OnCompletionListener, OnP @Override public boolean onTouchEvent (MotionEvent ev){ if (ev.getAction() == MotionEvent.ACTION_DOWN) { - mMediaController.show(MEDIA_CONTOL_LIFE); + mMediaController.show(MediaService.MEDIA_CONTROL_LIFE); return true; } else { return false; diff --git a/src/com/owncloud/android/ui/fragment/FileDetailFragment.java b/src/com/owncloud/android/ui/fragment/FileDetailFragment.java index a8235958..bfa4bbbc 100644 --- a/src/com/owncloud/android/ui/fragment/FileDetailFragment.java +++ b/src/com/owncloud/android/ui/fragment/FileDetailFragment.java @@ -97,6 +97,7 @@ import com.owncloud.android.operations.SynchronizeFileOperation; import com.owncloud.android.ui.activity.ConflictsResolveActivity; import com.owncloud.android.ui.activity.FileDetailActivity; import com.owncloud.android.ui.activity.FileDisplayActivity; +import com.owncloud.android.ui.OnSwipeTouchListener; import com.owncloud.android.ui.activity.TransferServiceGetter; import com.owncloud.android.ui.activity.VideoActivity; import com.owncloud.android.ui.dialog.EditNameDialog; @@ -111,7 +112,7 @@ import eu.alefzero.webdav.WebdavUtils; * This Fragment is used to display the details about a file. * * @author Bartek Przybylski - * + * @author David A. Velasco */ public class FileDetailFragment extends SherlockFragment implements OnClickListener, OnTouchListener, @@ -223,6 +224,7 @@ public class FileDetailFragment extends SherlockFragment implements super.onAttach(activity); try { mContainerActivity = (ContainerActivity) activity; + } catch (ClassCastException e) { throw new ClassCastException(activity.toString() + " must implement " + FileDetailFragment.ContainerActivity.class.getSimpleName()); } @@ -237,6 +239,7 @@ public class FileDetailFragment extends SherlockFragment implements super.onActivityCreated(savedInstanceState); if (mAccount != null) { mStorageManager = new FileDataStorageManager(mAccount, getActivity().getApplicationContext().getContentResolver());; + mView.setOnTouchListener(new OnSwipeTouchListener(getActivity())); } } @@ -273,9 +276,6 @@ public class FileDetailFragment extends SherlockFragment implements mPreview = (ImageView)mView.findViewById(R.id.fdPreview); // this is here just because it is nullified in onPause() - if (mMediaController != null) { - mMediaController.show(); - } } @@ -293,9 +293,6 @@ public class FileDetailFragment extends SherlockFragment implements mPreview = null; } - if (mMediaController != null) { - mMediaController.hide(); - } } @@ -304,9 +301,15 @@ public class FileDetailFragment extends SherlockFragment implements super.onStop(); if (mMediaServiceConnection != null) { Log.d(TAG, "Unbinding from MediaService ..."); + if (mMediaServiceBinder != null && mMediaController != null) { + mMediaServiceBinder.unregisterMediaController(mMediaController); + } getActivity().unbindService(mMediaServiceConnection); mMediaServiceBinder = null; - mMediaController = null; + if (mMediaController != null) { + mMediaController.hide(); + mMediaController = null; + } } } @@ -427,14 +430,20 @@ public class FileDetailFragment extends SherlockFragment implements Log.d(TAG, "starting playback of " + mFile.getStoragePath()); mMediaServiceBinder.start(mAccount, mFile); // this is a patch; need to synchronize this with the onPrepared() coming from MediaPlayer in the MediaService + /* mMediaController.postDelayed(new Runnable() { @Override public void run() { mMediaController.show(0); } } , 300); + */ } else { - mMediaController.show(0); + if (mMediaController.isShowing()) { + mMediaController.hide(); + } else { + mMediaController.show(MediaService.MEDIA_CONTROL_LIFE); + } } } else if (mFile.isVideo()) { @@ -450,25 +459,6 @@ public class FileDetailFragment extends SherlockFragment implements i.putExtra(VideoActivity.EXTRA_FILE, mFile); i.putExtra(VideoActivity.EXTRA_ACCOUNT, mAccount); startActivity(i); - - // TODO THROW AN ACTIVTIY JUST FOR PREVIEW VIDEO - /* - if (mMediaController == null) { - mMediaController = new MediaController(getActivity()); - mMediaController.setAnchorView(mVideoPreview); - //mMediaController.setEnabled(true); - } - //mMediaController.setMediaPlayer(mMediaServiceBinder); - if (!mVideoPreviewIsLoaded) { - mVideoPreviewIsLoaded = true; - mMediaController.setMediaPlayer(mVideoPreview); - mVideoPreview.setMediaController(mMediaController); - mVideoPreview.setVideoPath(mFile.getStoragePath()); - mVideoPreview.start(); - //mMediaController.show(0); - } else { - mMediaController.show(0); - }*/ } @@ -496,9 +486,7 @@ public class FileDetailFragment extends SherlockFragment implements if (mMediaController == null) { mMediaController = new MediaController(getSherlockActivity()); } - mMediaController.setMediaPlayer(mMediaServiceBinder); - mMediaController.setAnchorView(mPreview); - mMediaController.setEnabled(true); + prepareMediaController(); Log.d(TAG, "Successfully bound to MediaService, MediaController ready"); @@ -508,13 +496,20 @@ public class FileDetailFragment extends SherlockFragment implements } } + private void prepareMediaController() { + mMediaServiceBinder.registerMediaController(mMediaController); + mMediaController.setMediaPlayer(mMediaServiceBinder); + mMediaController.setAnchorView(getView()); + mMediaController.setEnabled(mMediaServiceBinder.isInPlaybackState()); + } + @Override public void onServiceDisconnected(ComponentName component) { if (component.equals(new ComponentName(getActivity(), MediaService.class))) { - Log.d(TAG, "Media service suddenly disconnected"); + Log.e(TAG, "Media service suddenly disconnected"); if (mMediaController != null) { mMediaController.hide(); - mMediaController.setMediaPlayer(null); // TODO check this is not an error + mMediaController.setMediaPlayer(null); mMediaController = null; } mMediaServiceBinder = null;