mirror of
				https://github.com/TeamNewPipe/NewPipe
				synced 2025-10-31 15:23:00 +00:00 
			
		
		
		
	Implemented a playback notification with a progress bar. No playback controls yet.
This commit is contained in:
		| @@ -72,7 +72,10 @@ | |||||||
|             android:parentActivityName=".VideoItemDetailActivity" |             android:parentActivityName=".VideoItemDetailActivity" | ||||||
|             > |             > | ||||||
|         </activity> |         </activity> | ||||||
|         <service android:name=".BackgroundPlayer" |         <!--TODO: make label a translatable string --> | ||||||
|  |         <service | ||||||
|  |             android:name=".BackgroundPlayer" | ||||||
|  |             android:label="NewPipe Background Player" | ||||||
|             android:exported="false" > |             android:exported="false" > | ||||||
|         </service> |         </service> | ||||||
|         <activity |         <activity | ||||||
|   | |||||||
| @@ -2,6 +2,7 @@ package org.schabi.newpipe; | |||||||
|  |  | ||||||
| import android.app.IntentService; | import android.app.IntentService; | ||||||
| import android.app.Notification; | import android.app.Notification; | ||||||
|  | import android.app.NotificationManager; | ||||||
| import android.app.Service; | import android.app.Service; | ||||||
| import android.content.Context; | import android.content.Context; | ||||||
| import android.content.Intent; | import android.content.Intent; | ||||||
| @@ -15,8 +16,10 @@ import android.os.Looper; | |||||||
| import android.os.Message; | import android.os.Message; | ||||||
| import android.os.PowerManager; | import android.os.PowerManager; | ||||||
| import android.support.v7.app.NotificationCompat; | import android.support.v7.app.NotificationCompat; | ||||||
|  | import android.util.Log; | ||||||
| import android.widget.Toast; | import android.widget.Toast; | ||||||
| import android.os.Process; | import android.os.Process; | ||||||
|  | import android.widget.VideoView; | ||||||
|  |  | ||||||
| import java.io.IOException; | import java.io.IOException; | ||||||
|  |  | ||||||
| @@ -45,11 +48,13 @@ import java.io.IOException; | |||||||
| public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPreparedListener*/ { | public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPreparedListener*/ { | ||||||
|  |  | ||||||
|     private static final String TAG = BackgroundPlayer.class.toString(); |     private static final String TAG = BackgroundPlayer.class.toString(); | ||||||
|     private Looper mServiceLooper; |     //private Looper mServiceLooper; | ||||||
|     private ServiceHandler mServiceHandler; |     //private ServiceHandler mServiceHandler; | ||||||
|  |  | ||||||
|     public BackgroundPlayer() { |     public BackgroundPlayer() { | ||||||
|         super(); |         super(); | ||||||
|  |  | ||||||
|  |         VideoView v; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
| @@ -59,59 +64,69 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare | |||||||
|         // main thread, which we don't want to block.  We also make it |         // main thread, which we don't want to block.  We also make it | ||||||
|         // background priority so CPU-intensive work will not disrupt our UI. |         // background priority so CPU-intensive work will not disrupt our UI. | ||||||
|         super.onCreate(); |         super.onCreate(); | ||||||
|         HandlerThread thread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND); |         //HandlerThread thread = new HandlerThread(TAG, Process.THREAD_PRIORITY_BACKGROUND); | ||||||
|         thread.start(); |         //thread.start(); | ||||||
|  |  | ||||||
|         // Get the HandlerThread's Looper and use it for our Handler |         // Get the HandlerThread's Looper and use it for our Handler | ||||||
|         mServiceLooper = thread.getLooper(); |         //mServiceLooper = thread.getLooper(); | ||||||
|         mServiceHandler = new ServiceHandler(mServiceLooper); |         //mServiceHandler = new ServiceHandler(mServiceLooper); | ||||||
|     } |     } | ||||||
|     @Override |     @Override | ||||||
|     public int onStartCommand(Intent intent, int flags, int startId) { |     public int onStartCommand(Intent intent, int flags, int startId) { | ||||||
|         Toast.makeText(this, "Playing in background", Toast.LENGTH_SHORT).show();//todo:translation string |         Toast.makeText(this, "Playing in background", Toast.LENGTH_SHORT).show();//todo:translation string | ||||||
|  |  | ||||||
|         // For each start request, send a message to start a job and deliver the |         String source = intent.getDataString(); | ||||||
|         // start ID so we know which request we're stopping when we finish the job |         String videoTitle = intent.getStringExtra("title"); | ||||||
|         Message msg = mServiceHandler.obtainMessage(); |  | ||||||
|         msg.arg1 = startId; |  | ||||||
|         msg.obj = intent; |  | ||||||
|         mServiceHandler.sendMessage(msg); |  | ||||||
|  |  | ||||||
|         // If we get killed, after returning from here, don't restart |         PlayerThread player = new PlayerThread(source, videoTitle, this); | ||||||
|  |         player.start(); | ||||||
|  |  | ||||||
|  |         // If we get killed after returning here, don't restart | ||||||
|         return START_NOT_STICKY; |         return START_NOT_STICKY; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public IBinder onBind(Intent intent) { |     public IBinder onBind(Intent intent) { | ||||||
|         // We don't provide binding yet, so return null |         // We don't provide binding (yet?), so return null | ||||||
|         return null; |         return null; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Override |     @Override | ||||||
|     public void onDestroy() { |     public void onDestroy() { | ||||||
|         //Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show(); |         //Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show(); | ||||||
|         mServiceLooper.quit(); |         //mServiceLooper.quit(); | ||||||
|  |         //if (mMediaPlayer != null) mMediaPlayer.release(); | ||||||
|  |         //todo: call MediaPlayer.release() as soon as video is complete | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     protected void onHandleIntent(Intent intent) { |     private class PlayerThread extends Thread { | ||||||
|         String source = intent.getDataString(); |  | ||||||
|  |  | ||||||
|         if (intent.getAction().equals(ACTION_PLAY)) { |         private MediaPlayer mediaPlayer; | ||||||
|             mMediaPlayer.setOnPreparedListener(this); |         private String source; | ||||||
|             mMediaPlayer.prepareAsync(); // prepare async to not block main thread |         private String title; | ||||||
|         } |         private BackgroundPlayer owner; | ||||||
|  |         public PlayerThread(String src, String title, BackgroundPlayer owner) { | ||||||
|  |             this.source = src; | ||||||
|         MediaPlayer mediaPlayer = new MediaPlayer(); |             this.title = title; | ||||||
|         mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);//cpu lock |             this.owner = owner; | ||||||
|  |             mediaPlayer = new MediaPlayer(); | ||||||
|             mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); |             mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); | ||||||
|  |         } | ||||||
|  |         @Override | ||||||
|  |         public void run() { | ||||||
|  |             mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);//cpu lock | ||||||
|             try { |             try { | ||||||
|                 mediaPlayer.setDataSource(source); |                 mediaPlayer.setDataSource(source); | ||||||
|             mediaPlayer.prepare(); //IntentService already puts us in a separate worker thread, |                 mediaPlayer.prepare(); //We are already in a separate worker thread, | ||||||
|                 //so calling the blocking prepare() method should be ok |                 //so calling the blocking prepare() method should be ok | ||||||
|  |  | ||||||
|  |                 //mediaPlayer.setOnPreparedListener(this); | ||||||
|  |                 //mediaPlayer.prepareAsync(); //prepare async to not block main thread | ||||||
|             } catch (IOException ioe) { |             } catch (IOException ioe) { | ||||||
|                 ioe.printStackTrace(); |                 ioe.printStackTrace(); | ||||||
|             //can't really do anything useful without a file to play; exit early |                 Log.e(TAG, "video source:" + source); | ||||||
|  |                 Log.e(TAG, "video title:" + title); | ||||||
|  |                 //can't do anything useful without a file to play; exit early | ||||||
|                 return; |                 return; | ||||||
|             } |             } | ||||||
|  |  | ||||||
| @@ -119,43 +134,59 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare | |||||||
|             WifiManager.WifiLock wifiLock = wifiMgr.createWifiLock(WifiManager.WIFI_MODE_FULL, TAG); |             WifiManager.WifiLock wifiLock = wifiMgr.createWifiLock(WifiManager.WIFI_MODE_FULL, TAG); | ||||||
|  |  | ||||||
|             mediaPlayer.setOnCompletionListener(new EndListener(wifiLock));//listen for end of video |             mediaPlayer.setOnCompletionListener(new EndListener(wifiLock));//listen for end of video | ||||||
| /* |  | ||||||
|             //get audio focus |             //get audio focus | ||||||
|  |             /* | ||||||
|             AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); |             AudioManager audioManager = (AudioManager) getSystemService(Context.AUDIO_SERVICE); | ||||||
|             int result = audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, |             int result = audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, | ||||||
|                     AudioManager.AUDIOFOCUS_GAIN); |                     AudioManager.AUDIOFOCUS_GAIN); | ||||||
|  |  | ||||||
|             if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { |             if (result != AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { | ||||||
|                 // could not get audio focus. |                 // could not get audio focus. | ||||||
|         } |             }*/ | ||||||
| */ |  | ||||||
|             wifiLock.acquire(); |             wifiLock.acquire(); | ||||||
|         //mediaPlayer.setOnPreparedListener(this); |  | ||||||
|             mediaPlayer.start(); |             mediaPlayer.start(); | ||||||
|  |  | ||||||
|         String videoTitle = intent.getStringExtra("title"); |             //mediaPlayer.getCurrentPosition() | ||||||
|  |             int vidLength = mediaPlayer.getDuration(); | ||||||
|         Notification noti = new NotificationCompat.Builder(this) |     //todo: make it so that tapping the notification brings you back to the Video's DetailActivity | ||||||
|  |             NotificationCompat.Builder noteBuilder = new NotificationCompat.Builder(owner); | ||||||
|  |             noteBuilder | ||||||
|                     .setPriority(Notification.PRIORITY_LOW) |                     .setPriority(Notification.PRIORITY_LOW) | ||||||
|                     .setCategory(Notification.CATEGORY_TRANSPORT) |                     .setCategory(Notification.CATEGORY_TRANSPORT) | ||||||
|                 .setContentTitle(videoTitle) |                     .setContentTitle(title) | ||||||
|                 .setContentText("NewPipe is playing in the background")//todo: add translatable string |                     .setContentText("NewPipe is playing in the background")//todo: translation string | ||||||
|                     .setOngoing(true) |                     .setOngoing(true) | ||||||
|                 .setSmallIcon(R.mipmap.ic_launcher) |                     .setProgress(vidLength, 0, false) | ||||||
|                 .build(); |                     .setSmallIcon(R.mipmap.ic_launcher); | ||||||
|  |  | ||||||
|         startForeground(TAG.hashCode(), noti); |             int noteID = TAG.hashCode(); | ||||||
|  |             startForeground(noteID, noteBuilder.build()); | ||||||
|  |             NotificationManager noteMgr = (NotificationManager)getSystemService(NOTIFICATION_SERVICE); | ||||||
|  |             //update every 3s or 4 times in the video, whichever is shorter | ||||||
|  |             int sleepTime = Math.min(3000, (int)((double)vidLength/4)); | ||||||
|  |             while(mediaPlayer.isPlaying()) { | ||||||
|  |                 try { | ||||||
|  |                     Thread.sleep(sleepTime); | ||||||
|  |                 } catch (InterruptedException e) { | ||||||
|  |                     Log.d(TAG, "sleep failure"); | ||||||
|  |                 } | ||||||
|  |                 noteBuilder.setProgress(vidLength, mediaPlayer.getCurrentPosition(), false); | ||||||
|  |                 noteMgr.notify(noteID, noteBuilder.build()); | ||||||
|  |             } | ||||||
|  |             noteBuilder.setProgress(0, 0, false);//remove bar | ||||||
|  |             //noteMgr.notify(0, noteBuilder.build()); | ||||||
|  |         } | ||||||
|     } |     } | ||||||
| /* | /* | ||||||
|     private class ListenerThread extends Thread implements AudioManager.OnAudioFocusChangeListener { |     private class ListenerThread extends Thread implements AudioManager.OnAudioFocusChangeListener { | ||||||
|  |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|         @Override |         @Override | ||||||
|         public void onAudioFocusChange(int focusChange) { |         public void onAudioFocusChange(int focusChange) { | ||||||
|  |  | ||||||
|  |         } | ||||||
|     }*/ |     }*/ | ||||||
|  |  | ||||||
|  |  | ||||||
|     private class EndListener implements MediaPlayer.OnCompletionListener { |     private class EndListener implements MediaPlayer.OnCompletionListener { | ||||||
|         private WifiManager.WifiLock wl; |         private WifiManager.WifiLock wl; | ||||||
|         public EndListener(WifiManager.WifiLock wifiLock) { |         public EndListener(WifiManager.WifiLock wifiLock) { | ||||||
| @@ -164,21 +195,10 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare | |||||||
|  |  | ||||||
|         @Override |         @Override | ||||||
|         public void onCompletion(MediaPlayer mp) { |         public void onCompletion(MediaPlayer mp) { | ||||||
|             wl.release(); |             wl.release();//release wifilock | ||||||
|         } |             stopForeground(true);//remove ongoing notification | ||||||
|     } |             stopSelf(); | ||||||
|  |             //todo:release cpu lock | ||||||
|     // Handler that receives messages from the thread |  | ||||||
|     private final class ServiceHandler extends Handler { |  | ||||||
|         public ServiceHandler(Looper looper) { |  | ||||||
|             super(looper); |  | ||||||
|         } |  | ||||||
|         @Override |  | ||||||
|         public void handleMessage(Message msg) { |  | ||||||
|             onHandleIntent((Intent)msg.obj); |  | ||||||
|             // Stop the service using the startId, so that we don't stop |  | ||||||
|             // the service in the middle of handling another job |  | ||||||
|             stopSelfResult(msg.arg1); |  | ||||||
|         } |         } | ||||||
|     } |     } | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Adam Howard
					Adam Howard