some improvements for background player

This commit is contained in:
Christian Schabesberger 2015-12-25 00:09:35 +01:00
parent 6a9f6ef651
commit e83ca0dfda
16 changed files with 203 additions and 69 deletions

View File

@ -3,6 +3,7 @@ package org.schabi.newpipe;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.preference.PreferenceManager;
@ -40,7 +41,10 @@ class ActionBarHandler {
private static final String TAG = ActionBarHandler.class.toString();
private static final String KORE_PACKET = "org.xbmc.kore";
private int serviceId;
private String websiteUrl = "";
private Bitmap videoThumbnail = null;
private String channelName = "";
private AppCompatActivity activity;
private VideoInfo.VideoStream[] videoStreams = null;
private VideoInfo.AudioStream audioStream = null;
@ -73,6 +77,18 @@ class ActionBarHandler {
}
}
public void setServiceId(int id) {
serviceId = id;
}
public void setSetVideoThumbnail(Bitmap bitmap) {
videoThumbnail = bitmap;
}
public void setChannelName(String name) {
channelName = name;
}
@SuppressWarnings("deprecation")
public void setStreams(VideoInfo.VideoStream[] videoStreams, VideoInfo.AudioStream[] audioStreams) {
this.videoStreams = videoStreams;
@ -143,17 +159,17 @@ class ActionBarHandler {
}
public boolean onItemSelected(MenuItem item) {
int id = item.getItemId();
switch(id) {
case R.id.menu_item_share:
if(!videoTitle.isEmpty()) {
int id = item.getItemId();
switch (id) {
case R.id.menu_item_share: {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_SEND);
intent.putExtra(Intent.EXTRA_TEXT, websiteUrl);
intent.setType("text/plain");
activity.startActivity(Intent.createChooser(intent, activity.getString(R.string.shareDialogTitle)));
}
return true;
}
case R.id.menu_item_openInBrowser: {
openInBrowser();
}
@ -175,6 +191,10 @@ class ActionBarHandler {
default:
Log.e(TAG, "Menu Item not known");
}
} else {
// That line may not be necessary.
return true;
}
return false;
}
@ -302,18 +322,24 @@ class ActionBarHandler {
.getBoolean(activity.getString(R.string.useExternalAudioPlayer), false);
Intent intent;
if (!externalAudioPlayer && android.os.Build.VERSION.SDK_INT >= 18)//internal music player: explicit intent
{
if (!externalAudioPlayer && android.os.Build.VERSION.SDK_INT >= 18) {
//internal music player: explicit intent
if (!BackgroundPlayer.isRunning && videoThumbnail != null) {
ActivityCommunicator.getCommunicator()
.backgroundPlayerThumbnail = videoThumbnail;
intent = new Intent(activity, BackgroundPlayer.class);
intent.setAction(Intent.ACTION_VIEW);
Log.i(TAG, "audioStream is null:" + (audioStream == null));
Log.i(TAG, "audioStream.url is null:"+(audioStream.url==null));
Log.i(TAG, "audioStream.url is null:" + (audioStream.url == null));
intent.setDataAndType(Uri.parse(audioStream.url),
MediaFormat.getMimeById(audioStream.format));
intent.putExtra(Intent.EXTRA_TITLE, videoTitle);
intent.putExtra("title", videoTitle);
intent.putExtra(BackgroundPlayer.TITLE, videoTitle);
intent.putExtra(BackgroundPlayer.WEB_URL, websiteUrl);
intent.putExtra(BackgroundPlayer.SERVICE_ID, serviceId);
intent.putExtra(BackgroundPlayer.CHANNEL_NAME, channelName);
activity.startService(intent);
}
} else {
intent = new Intent();
try {

View File

@ -0,0 +1,42 @@
package org.schabi.newpipe;
/**
* Created by Christian Schabesberger on 24.12.15.
*
* Copyright (C) Christian Schabesberger 2015 <chris.schabesberger@mailbox.org>
* ActivityCommunicator.java is part of NewPipe.
*
* NewPipe is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* NewPipe is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with NewPipe. If not, see <http://www.gnu.org/licenses/>.
*/
import android.graphics.Bitmap;
/**
* Singleton:
* Used to send data between certain Activity/Services within the same process.
* This can be considered as hack inside the Android universe. **/
public class ActivityCommunicator {
private static ActivityCommunicator activityCommunicator = null;
public static ActivityCommunicator getCommunicator() {
if(activityCommunicator == null) {
activityCommunicator = new ActivityCommunicator();
}
return activityCommunicator;
}
// Thumbnail send from VideoItemDetailFragment to BackgroundPlayer
public volatile Bitmap backgroundPlayerThumbnail;
}

View File

@ -8,6 +8,8 @@ import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.wifi.WifiManager;
@ -43,8 +45,22 @@ import java.io.IOException;
public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPreparedListener*/ {
private static final String TAG = BackgroundPlayer.class.toString();
private static final String ACTION_STOP = TAG+".STOP";
private static final String ACTION_PLAYPAUSE = TAG+".PLAYPAUSE";
private static final String ACTION_STOP = TAG + ".STOP";
private static final String ACTION_PLAYPAUSE = TAG + ".PLAYPAUSE";
// Extra intent arguments
public static final String TITLE = "title";
public static final String WEB_URL = "web_url";
public static final String SERVICE_ID = "service_id";
public static final String CHANNEL_NAME="channel_name";
private volatile String webUrl = "";
private volatile int serviceId = -1;
private volatile String channelName = "";
// Determines if the service is already running.
// Prevents launching the service twice.
public static volatile boolean isRunning = false;
public BackgroundPlayer() {
super();
@ -58,16 +74,22 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare
}
@Override
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, R.string.backgroundPlayerStartPlayingToast,
Toast.LENGTH_SHORT).show();
String source = intent.getDataString();
//Log.i(TAG, "backgroundPLayer source:"+source);
String videoTitle = intent.getStringExtra("title");
String videoTitle = intent.getStringExtra(TITLE);
webUrl = intent.getStringExtra(WEB_URL);
serviceId = intent.getIntExtra(SERVICE_ID, -1);
channelName = intent.getStringExtra(CHANNEL_NAME);
//do nearly everything in a separate thread
PlayerThread player = new PlayerThread(source, videoTitle, this);
player.start();
isRunning = true;
// If we get killed after returning here, don't restart
return START_NOT_STICKY;
}
@ -81,6 +103,7 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare
@Override
public void onDestroy() {
//Toast.makeText(this, "service done", Toast.LENGTH_SHORT).show();
isRunning = false;
}
private class PlayerThread extends Thread {
@ -92,6 +115,7 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare
private NotificationManager noteMgr;
private NotificationCompat.Builder noteBuilder;
private WifiManager.WifiLock wifiLock;
private Bitmap videoThumbnail = null;
public PlayerThread(String src, String title, BackgroundPlayer owner) {
this.source = src;
@ -102,11 +126,14 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare
}
@Override
public void run() {
Resources res = getApplicationContext().getResources();
mediaPlayer.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);//cpu lock
try {
mediaPlayer.setDataSource(source);
mediaPlayer.prepare(); //We are already in a separate worker thread,
//We are already in a separate worker thread,
//so calling the blocking prepare() method should be ok
mediaPlayer.prepare();
//alternatively:
//mediaPlayer.setOnPreparedListener(this);
@ -119,10 +146,18 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare
return;
}
try {
videoThumbnail = ActivityCommunicator.getCommunicator().backgroundPlayerThumbnail;
} catch (Exception e) {
Log.e(TAG, "Could not get video thumbnail from ActivityCommunicator");
e.printStackTrace();
}
WifiManager wifiMgr = ((WifiManager)getSystemService(Context.WIFI_SERVICE));
wifiLock = wifiMgr.createWifiLock(WifiManager.WIFI_MODE_FULL, TAG);
mediaPlayer.setOnCompletionListener(new EndListener(wifiLock));//listen for end of video
//listen for end of video
mediaPlayer.setOnCompletionListener(new EndListener(wifiLock));
//get audio focus
/*
@ -142,43 +177,65 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare
filter.addAction(ACTION_STOP);
registerReceiver(broadcastReceiver, filter);
PendingIntent playPI = PendingIntent.getBroadcast(owner, noteID, new Intent(ACTION_PLAYPAUSE), PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent playPI = PendingIntent.getBroadcast(owner, noteID,
new Intent(ACTION_PLAYPAUSE), PendingIntent.FLAG_UPDATE_CURRENT);
NotificationCompat.Action playButton = new NotificationCompat.Action.Builder
(R.drawable.ic_play_arrow_white_48dp, "Play", playPI).build();
/*
NotificationCompat.Action pauseButton = new NotificationCompat.Action.Builder
(R.drawable.ic_play_arrow_white_48dp, "Pause", playPI).build();
(R.drawable.ic_pause_white_24dp, "Pause", playPI).build();
*/
PendingIntent stopPI = PendingIntent.getBroadcast(owner, noteID,
new Intent(ACTION_STOP), PendingIntent.FLAG_UPDATE_CURRENT);
//todo: make it so that tapping the notification brings you back to the Video's DetailActivity
//using setContentIntent
noteBuilder = new NotificationCompat.Builder(owner);
noteBuilder
.setPriority(Notification.PRIORITY_LOW)
.setCategory(Notification.CATEGORY_TRANSPORT)
.setContentTitle(title)
.setContentText("NewPipe is playing in the background")//todo: translation string
//really? Id like to put something more helpful here.
//.setContentText("NewPipe is playing in the background")
.setContentText(channelName)
//.setAutoCancel(!mediaPlayer.isPlaying())
.setOngoing(true)
.setDeleteIntent(stopPI)
//.setProgress(vidLength, 0, false) //doesn't fit with Notification.MediaStyle
.setSmallIcon(R.mipmap.ic_launcher)
.setTicker(title + " - NewPipe")
//doesn't fit with Notification.MediaStyle
//.setProgress(vidLength, 0, false)
.setSmallIcon(R.drawable.ic_play_circle_filled_white_24dp)
.setLargeIcon(videoThumbnail)
.setTicker(
String.format(res.getString(
R.string.backgroundPlayerTickerText), title))
.addAction(playButton);
/* .setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setLargeIcon(cover)*/
//.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
//.setLargeIcon(cover)
if(android.os.Build.VERSION.SDK_INT >= 16)
noteBuilder.setPriority(Notification.PRIORITY_LOW);
if(android.os.Build.VERSION.SDK_INT >= 21)
noteBuilder.setCategory(Notification.CATEGORY_TRANSPORT);
noteBuilder.setStyle(new NotificationCompat.MediaStyle()
//.setMediaSession(mMediaSession.getSessionToken())
.setShowActionsInCompactView(new int[] {0})
.setShowActionsInCompactView(new int[]{0})
.setShowCancelButton(true)
.setCancelButtonIntent(stopPI)
);
.setCancelButtonIntent(stopPI));
if(videoThumbnail != null) {
noteBuilder.setLargeIcon(videoThumbnail);
}
startForeground(noteID, noteBuilder.build());
Notification note = noteBuilder.build();
Intent openDetailView = new Intent(getApplicationContext(),
VideoItemDetailActivity.class);
openDetailView.putExtra(VideoItemDetailFragment.STREAMING_SERVICE, serviceId);
openDetailView.putExtra(VideoItemDetailFragment.VIDEO_URL, webUrl);
openDetailView.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
note.contentIntent = PendingIntent.getActivity(getApplicationContext(),
noteID, openDetailView,
PendingIntent.FLAG_UPDATE_CURRENT);
startForeground(noteID, note);
//currently decommissioned progressbar looping update code - works, but doesn't fit inside
//Notification.MediaStyle Notification layout.
@ -202,7 +259,7 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
Log.i(TAG, "received broadcast action:"+action);
//Log.i(TAG, "received broadcast action:"+action);
if(action.equals(ACTION_PLAYPAUSE)) {
if(mediaPlayer.isPlaying()) {
mediaPlayer.pause();
@ -214,7 +271,8 @@ public class BackgroundPlayer extends Service /*implements MediaPlayer.OnPrepare
}
}
else if(action.equals(ACTION_STOP)) {
mediaPlayer.stop();//this auto-releases CPU lock
//this auto-releases CPU lock
mediaPlayer.stop();
afterPlayCleanup();
}
}

View File

@ -73,7 +73,7 @@ public class VideoItemDetailFragment extends Fragment {
* The fragment argument representing the item ID that this fragment
* represents.
*/
public static final String ARG_ITEM_ID = "item_id";
//public static final String ARG_ITEM_ID = "item_id";
public static final String VIDEO_URL = "video_url";
public static final String STREAMING_SERVICE = "streaming_service";
public static final String AUTO_PLAY = "auto_play";
@ -81,6 +81,8 @@ public class VideoItemDetailFragment extends Fragment {
private AppCompatActivity activity;
private ActionBarHandler actionBarHandler;
private int streamingServiceId = -1;
private boolean autoPlayEnabled = false;
private VideoInfo currentVideoInfo = null;
private boolean showNextVideoItem = false;
@ -105,6 +107,7 @@ public class VideoItemDetailFragment extends Fragment {
this.service = service;
this.videoUrl = videoUrl;
}
@Override
public void run() {
try {
@ -176,6 +179,7 @@ public class VideoItemDetailFragment extends Fragment {
switch (id) {
case SetThumbnailRunnable.VIDEO_THUMBNAIL:
thumbnailView = (ImageView) a.findViewById(R.id.detailThumbnailView);
actionBarHandler.setSetVideoThumbnail(thumbnail);
break;
case SetThumbnailRunnable.CHANNEL_THUMBNAIL:
thumbnailView = (ImageView) a.findViewById(R.id.detailUploaderThumbnailView);
@ -238,6 +242,7 @@ public class VideoItemDetailFragment extends Fragment {
case VideoInfo.VIDEO_AVAILABLE: {
videoTitleView.setText(info.title);
uploaderView.setText(info.uploader);
actionBarHandler.setChannelName(info.uploader);
Locale locale = getPreferredLocale();
NumberFormat nf = NumberFormat.getInstance(locale);
@ -266,6 +271,7 @@ public class VideoItemDetailFragment extends Fragment {
descriptionView.setText(Html.fromHtml(info.description));
descriptionView.setMovementMethod(LinkMovementMethod.getInstance());
actionBarHandler.setServiceId(streamingServiceId);
actionBarHandler.setVideoInfo(info.webpage_url, info.title);
actionBarHandler.setStartPosition(info.startPosition);
@ -288,12 +294,12 @@ public class VideoItemDetailFragment extends Fragment {
public void onClick(View v) {
Intent detailIntent =
new Intent(getActivity(), VideoItemDetailActivity.class);
detailIntent.putExtra(
VideoItemDetailFragment.ARG_ITEM_ID, currentVideoInfo.nextVideo.id);
/*detailIntent.putExtra(
VideoItemDetailFragment.ARG_ITEM_ID, currentVideoInfo.nextVideo.id); */
detailIntent.putExtra(
VideoItemDetailFragment.VIDEO_URL, currentVideoInfo.nextVideo.webpage_url);
//todo: make id dynamic the following line is crap
detailIntent.putExtra(VideoItemDetailFragment.STREAMING_SERVICE, 0);
detailIntent.putExtra(VideoItemDetailFragment.STREAMING_SERVICE, streamingServiceId);
startActivity(detailIntent);
}
});
@ -369,8 +375,8 @@ public class VideoItemDetailFragment extends Fragment {
// Otherwise the applications would crash.
if(playVideoButton != null) {
try {
StreamingService streamingService = ServiceList.getService(
getArguments().getInt(STREAMING_SERVICE));
streamingServiceId = getArguments().getInt(STREAMING_SERVICE);
StreamingService streamingService = ServiceList.getService(streamingServiceId);
Thread videoExtractorThread = new Thread(new VideoExtractorRunnable(
getArguments().getString(VIDEO_URL), streamingService));

View File

@ -190,7 +190,7 @@ public class VideoItemListActivity extends AppCompatActivity
// adding or replacing the detail fragment using a
// fragment transaction.
Bundle arguments = new Bundle();
arguments.putString(VideoItemDetailFragment.ARG_ITEM_ID, id);
//arguments.putString(VideoItemDetailFragment.ARG_ITEM_ID, id);
arguments.putString(VideoItemDetailFragment.VIDEO_URL, webpage_url);
arguments.putInt(VideoItemDetailFragment.STREAMING_SERVICE, currentStreamingServiceId);
videoFragment = new VideoItemDetailFragment();
@ -209,7 +209,7 @@ public class VideoItemListActivity extends AppCompatActivity
// In single-pane mode, simply start the detail activity
// for the selected item ID.
Intent detailIntent = new Intent(this, VideoItemDetailActivity.class);
detailIntent.putExtra(VideoItemDetailFragment.ARG_ITEM_ID, id);
//detailIntent.putExtra(VideoItemDetailFragment.ARG_ITEM_ID, id);
detailIntent.putExtra(VideoItemDetailFragment.VIDEO_URL, webpage_url);
detailIntent.putExtra(VideoItemDetailFragment.STREAMING_SERVICE, currentStreamingServiceId);
startActivity(detailIntent);

Binary file not shown.

After

Width:  |  Height:  |  Size: 105 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 358 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 90 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 464 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 92 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 666 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 94 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 883 B

View File

@ -50,6 +50,8 @@
<string name="settingsCategoryVideoAudioTitle">VIDEO &amp; AUDIO</string>
<string name="settingsCategoryVideoInfoTittle">INFO</string>
<string name="settingsCategoryEtcTitle">ETC</string>
<string name="backgroundPlayerTickerText" translatable="false">%1$s - NewPipe</string>
<string name="backgroundPlayerStartPlayingToast">Playing in background</string>
<!-- Content descriptions (for better accessibility) -->
<string name="itemThumbnailViewDescription">Video preview thumbnail</string>