Refactoring and clean-up of fragment for media previews
[pub/Android/ownCloud.git] / src / com / owncloud / android / media / MediaService.java
index 987d94e..5f4e1ac 100644 (file)
@@ -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,21 @@ 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_SHORT_LIFE = 5000;
+    
+    /** Time To keep the control panel visible when the user does not use it */
+    public static final int MEDIA_CONTROL_PERMANENT = 0;
     
     /** 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,9 +130,79 @@ 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;
     
+
     
+    /**
+     * Helper method to get an error message suitable to show to users for errors occurred in media playback,
+     * 
+     * @param context   A context to access string resources.
+     * @param what      See {@link MediaPlayer.OnErrorListener#onError(MediaPlayer, int, int)
+     * @param extra     See {@link MediaPlayer.OnErrorListener#onError(MediaPlayer, int, int)
+     * @return          Message suitable to users.
+     */
+    public static String getMessageForMediaError(Context context, int what, int extra) {
+        int messageId;
+        
+        if (what == OC_MEDIA_ERROR) {
+            messageId = extra;
+                
+        } else if (extra == MediaPlayer.MEDIA_ERROR_UNSUPPORTED) {
+            /*  Added in API level 17
+                Bitstream is conforming to the related coding standard or file spec, but the media framework does not support the feature.
+                Constant Value: -1010 (0xfffffc0e)
+             */
+            messageId = R.string.media_err_unsupported;
+
+        } else if (extra == MediaPlayer.MEDIA_ERROR_IO) {
+            /*  Added in API level 17
+                File or network related operation errors.
+                Constant Value: -1004 (0xfffffc14) 
+             */
+            messageId = R.string.media_err_io;
+            
+        } else if (extra == MediaPlayer.MEDIA_ERROR_MALFORMED) {
+            /*  Added in API level 17
+                Bitstream is not conforming to the related coding standard or file spec.
+                Constant Value: -1007 (0xfffffc11) 
+             */
+            messageId = R.string.media_err_malformed;
+            
+        } else if (extra == MediaPlayer.MEDIA_ERROR_TIMED_OUT) {
+            /*  Added in API level 17
+                Some operation takes too long to complete, usually more than 3-5 seconds.
+                Constant Value: -110 (0xffffff92)
+            */
+            messageId = R.string.media_err_timeout;
+
+        } else if (what == MediaPlayer.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK) {
+            /*  Added in API level 3
+                The video is streamed and its container is not valid for progressive playback i.e the video's index (e.g moov atom) is not at the start of the file.
+                Constant Value: 200 (0x000000c8)
+            */
+            messageId = R.string.media_err_invalid_progressive_playback;
+                
+        } else {
+            /*  MediaPlayer.MEDIA_ERROR_UNKNOWN
+                Added in API level 1
+                Unspecified media player error.
+                Constant Value: 1 (0x00000001)
+            */
+            /*  MediaPlayer.MEDIA_ERROR_SERVER_DIED)
+                Added in API level 1
+                Media server died. In this case, the application must release the MediaPlayer object and instantiate a new one.
+                Constant Value: 100 (0x00000064) 
+             */
+            messageId = R.string.media_err_unknown;
+        }
+        return context.getString(messageId);
+    }
+
 
+    
     /**
      * Initialize a service instance
      * 
@@ -166,7 +245,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 +316,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
@@ -352,10 +429,13 @@ public class MediaService extends Service implements OnCompletionListener, OnPre
             createMediaPlayerIfNeeded();
             mPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
             String url = mFile.getStoragePath();
+            /* Streaming is not possible right now
             if (url == null || url.length() <= 0) {
                 url = AccountUtils.constructFullURLForAccount(this, mAccount) + mFile.getRemotePath();
             }
             mIsStreaming = url.startsWith("http:") || url.startsWith("https:");
+            */
+            mIsStreaming = false;
             
             mPlayer.setDataSource(url);
 
@@ -374,26 +454,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 +494,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_PERMANENT);
+        }
     }
     
 
@@ -478,19 +570,19 @@ 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));
+        if (mMediaController != null) {
+            mMediaController.hide();
+        }
 
-        mState = State.STOPPED;
-        releaseResources(true);
-        giveUpAudioFocus();
+        String message = getMessageForMediaError(this, what, extra);
+        Toast.makeText(getApplicationContext(), message, Toast.LENGTH_SHORT).show();
+        
+        processStopRequest(true);
         return true; 
     }
 
-    
     /**
      * Called by the system when another app tries to play some sound.
      * 
@@ -500,9 +592,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 +600,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 +675,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;
+    }
+
 }