2019-12-29 22:15:01 +01:00
|
|
|
/*
|
|
|
|
* Copyright 2017 Mauricio Colli <mauriciocolli@outlook.com>
|
2020-07-25 08:39:42 +02:00
|
|
|
* Part of NewPipe
|
2019-12-29 22:15:01 +01:00
|
|
|
*
|
|
|
|
* License: GPL-3.0+
|
|
|
|
* This program 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.
|
|
|
|
*
|
|
|
|
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
package org.schabi.newpipe.player;
|
|
|
|
|
|
|
|
import android.app.Service;
|
|
|
|
import android.content.Context;
|
|
|
|
import android.content.Intent;
|
|
|
|
import android.os.Binder;
|
|
|
|
import android.os.IBinder;
|
2020-01-03 06:05:31 +01:00
|
|
|
import android.util.DisplayMetrics;
|
2019-12-29 22:15:01 +01:00
|
|
|
import android.util.Log;
|
|
|
|
import android.view.View;
|
2020-08-02 22:59:43 +02:00
|
|
|
import android.view.ViewGroup;
|
|
|
|
import android.view.WindowManager;
|
2019-12-29 22:15:01 +01:00
|
|
|
|
|
|
|
import org.schabi.newpipe.R;
|
|
|
|
import org.schabi.newpipe.util.ThemeHelper;
|
|
|
|
|
2020-07-13 03:17:21 +02:00
|
|
|
import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage;
|
|
|
|
|
2019-12-29 22:15:01 +01:00
|
|
|
|
|
|
|
/**
|
2020-07-14 19:21:32 +02:00
|
|
|
* One service for all players.
|
2019-12-29 22:15:01 +01:00
|
|
|
*
|
|
|
|
* @author mauriciocolli
|
|
|
|
*/
|
|
|
|
public final class MainPlayer extends Service {
|
|
|
|
private static final String TAG = "MainPlayer";
|
|
|
|
private static final boolean DEBUG = BasePlayer.DEBUG;
|
|
|
|
|
|
|
|
private VideoPlayerImpl playerImpl;
|
|
|
|
private WindowManager windowManager;
|
|
|
|
|
|
|
|
private final IBinder mBinder = new MainPlayer.LocalBinder();
|
|
|
|
|
|
|
|
public enum PlayerType {
|
|
|
|
VIDEO,
|
|
|
|
AUDIO,
|
|
|
|
POPUP
|
|
|
|
}
|
|
|
|
|
|
|
|
/*//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Notification
|
|
|
|
//////////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
2020-08-02 22:59:43 +02:00
|
|
|
static final String ACTION_CLOSE
|
|
|
|
= "org.schabi.newpipe.player.MainPlayer.CLOSE";
|
|
|
|
static final String ACTION_PLAY_PAUSE
|
|
|
|
= "org.schabi.newpipe.player.MainPlayer.PLAY_PAUSE";
|
|
|
|
static final String ACTION_OPEN_CONTROLS
|
|
|
|
= "org.schabi.newpipe.player.MainPlayer.OPEN_CONTROLS";
|
|
|
|
static final String ACTION_REPEAT
|
|
|
|
= "org.schabi.newpipe.player.MainPlayer.REPEAT";
|
|
|
|
static final String ACTION_PLAY_NEXT
|
|
|
|
= "org.schabi.newpipe.player.MainPlayer.ACTION_PLAY_NEXT";
|
|
|
|
static final String ACTION_PLAY_PREVIOUS
|
|
|
|
= "org.schabi.newpipe.player.MainPlayer.ACTION_PLAY_PREVIOUS";
|
|
|
|
static final String ACTION_FAST_REWIND
|
|
|
|
= "org.schabi.newpipe.player.MainPlayer.ACTION_FAST_REWIND";
|
|
|
|
static final String ACTION_FAST_FORWARD
|
|
|
|
= "org.schabi.newpipe.player.MainPlayer.ACTION_FAST_FORWARD";
|
|
|
|
static final String ACTION_BUFFERING
|
|
|
|
= "org.schabi.newpipe.player.BackgroundPlayer.ACTION_BUFFERING";
|
|
|
|
static final String ACTION_SHUFFLE
|
|
|
|
= "org.schabi.newpipe.player.BackgroundPlayer.ACTION_SHUFFLE";
|
|
|
|
|
|
|
|
static final String SET_IMAGE_RESOURCE_METHOD = "setImageResource";
|
2019-12-29 22:15:01 +01:00
|
|
|
|
|
|
|
/*//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Service's LifeCycle
|
|
|
|
//////////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onCreate() {
|
2020-07-14 19:21:32 +02:00
|
|
|
if (DEBUG) {
|
|
|
|
Log.d(TAG, "onCreate() called");
|
|
|
|
}
|
2020-07-13 03:17:21 +02:00
|
|
|
assureCorrectAppLanguage(this);
|
2019-12-29 22:15:01 +01:00
|
|
|
windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
|
|
|
|
|
|
|
|
ThemeHelper.setTheme(this);
|
|
|
|
createView();
|
|
|
|
}
|
|
|
|
|
|
|
|
private void createView() {
|
2020-07-25 08:45:33 +02:00
|
|
|
final View layout = View.inflate(this, R.layout.player, null);
|
2019-12-29 22:15:01 +01:00
|
|
|
|
|
|
|
playerImpl = new VideoPlayerImpl(this);
|
|
|
|
playerImpl.setup(layout);
|
|
|
|
playerImpl.shouldUpdateOnProgress = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-07-14 19:21:32 +02:00
|
|
|
public int onStartCommand(final Intent intent, final int flags, final int startId) {
|
|
|
|
if (DEBUG) {
|
|
|
|
Log.d(TAG, "onStartCommand() called with: intent = [" + intent
|
|
|
|
+ "], flags = [" + flags + "], startId = [" + startId + "]");
|
|
|
|
}
|
2020-07-28 20:36:06 +02:00
|
|
|
if (Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())
|
|
|
|
&& playerImpl.playQueue == null) {
|
|
|
|
// Player is not working, no need to process media button's action
|
|
|
|
return START_NOT_STICKY;
|
|
|
|
}
|
2020-01-15 19:32:29 +01:00
|
|
|
|
2020-07-14 19:21:32 +02:00
|
|
|
if (Intent.ACTION_MEDIA_BUTTON.equals(intent.getAction())
|
|
|
|
|| intent.getStringExtra(VideoPlayer.PLAY_QUEUE_KEY) != null) {
|
2020-01-15 19:32:29 +01:00
|
|
|
showNotificationAndStartForeground();
|
2020-07-14 19:21:32 +02:00
|
|
|
}
|
2020-01-15 19:32:29 +01:00
|
|
|
|
2019-12-29 22:15:01 +01:00
|
|
|
playerImpl.handleIntent(intent);
|
|
|
|
if (playerImpl.mediaSessionManager != null) {
|
|
|
|
playerImpl.mediaSessionManager.handleMediaButtonIntent(intent);
|
|
|
|
}
|
|
|
|
return START_NOT_STICKY;
|
|
|
|
}
|
|
|
|
|
2020-07-12 02:59:47 +02:00
|
|
|
public void stop(final boolean autoplayEnabled) {
|
2020-07-14 19:21:32 +02:00
|
|
|
if (DEBUG) {
|
|
|
|
Log.d(TAG, "stop() called");
|
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
|
|
|
|
if (playerImpl.getPlayer() != null) {
|
|
|
|
playerImpl.wasPlaying = playerImpl.getPlayer().getPlayWhenReady();
|
2020-01-15 19:32:29 +01:00
|
|
|
// Releases wifi & cpu, disables keepScreenOn, etc.
|
2020-07-14 19:21:32 +02:00
|
|
|
if (!autoplayEnabled) {
|
|
|
|
playerImpl.onPause();
|
|
|
|
}
|
|
|
|
// We can't just pause the player here because it will make transition
|
|
|
|
// from one stream to a new stream not smooth
|
2019-12-29 22:15:01 +01:00
|
|
|
playerImpl.getPlayer().stop(false);
|
|
|
|
playerImpl.setRecovery();
|
2020-07-25 06:00:53 +02:00
|
|
|
// Android TV will handle back button in case controls will be visible
|
|
|
|
// (one more additional unneeded click while the player is hidden)
|
|
|
|
playerImpl.hideControls(0, 0);
|
2020-07-14 19:21:32 +02:00
|
|
|
// Notification shows information about old stream but if a user selects
|
|
|
|
// a stream from backStack it's not actual anymore
|
2020-01-15 19:32:29 +01:00
|
|
|
// So we should hide the notification at all.
|
|
|
|
// When autoplay enabled such notification flashing is annoying so skip this case
|
2020-07-14 19:21:32 +02:00
|
|
|
if (!autoplayEnabled) {
|
|
|
|
stopForeground(true);
|
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-07-14 19:21:32 +02:00
|
|
|
public void onTaskRemoved(final Intent rootIntent) {
|
2019-12-29 22:15:01 +01:00
|
|
|
super.onTaskRemoved(rootIntent);
|
|
|
|
onDestroy();
|
|
|
|
// Unload from memory completely
|
|
|
|
Runtime.getRuntime().halt(0);
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onDestroy() {
|
2020-07-14 19:21:32 +02:00
|
|
|
if (DEBUG) {
|
|
|
|
Log.d(TAG, "destroy() called");
|
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
onClose();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-07-14 19:21:32 +02:00
|
|
|
protected void attachBaseContext(final Context base) {
|
2019-12-29 22:15:01 +01:00
|
|
|
super.attachBaseContext(AudioServiceLeakFix.preventLeakOf(base));
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2020-07-14 19:21:32 +02:00
|
|
|
public IBinder onBind(final Intent intent) {
|
2019-12-29 22:15:01 +01:00
|
|
|
return mBinder;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Actions
|
|
|
|
//////////////////////////////////////////////////////////////////////////*/
|
|
|
|
private void onClose() {
|
2020-07-14 19:21:32 +02:00
|
|
|
if (DEBUG) {
|
|
|
|
Log.d(TAG, "onClose() called");
|
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
|
|
|
|
if (playerImpl != null) {
|
|
|
|
removeViewFromParent();
|
|
|
|
|
2020-02-29 00:57:54 +01:00
|
|
|
playerImpl.setRecovery();
|
2019-12-29 22:15:01 +01:00
|
|
|
playerImpl.savePlaybackState();
|
|
|
|
playerImpl.stopActivityBinding();
|
2020-02-05 06:59:30 +01:00
|
|
|
playerImpl.removePopupFromView();
|
2019-12-29 22:15:01 +01:00
|
|
|
playerImpl.destroy();
|
|
|
|
}
|
2020-08-02 22:59:43 +02:00
|
|
|
NotificationUtil.getInstance().cancelNotification();
|
2019-12-29 22:15:01 +01:00
|
|
|
|
|
|
|
stopForeground(true);
|
|
|
|
stopSelf();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*//////////////////////////////////////////////////////////////////////////
|
|
|
|
// Utils
|
|
|
|
//////////////////////////////////////////////////////////////////////////*/
|
|
|
|
|
|
|
|
boolean isLandscape() {
|
2020-07-14 19:21:32 +02:00
|
|
|
// DisplayMetrics from activity context knows about MultiWindow feature
|
|
|
|
// while DisplayMetrics from app context doesn't
|
|
|
|
final DisplayMetrics metrics = (playerImpl != null
|
|
|
|
&& playerImpl.getParentActivity() != null)
|
|
|
|
? playerImpl.getParentActivity().getResources().getDisplayMetrics()
|
2020-01-03 06:05:31 +01:00
|
|
|
: getResources().getDisplayMetrics();
|
|
|
|
return metrics.heightPixels < metrics.widthPixels;
|
2019-12-29 22:15:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
public View getView() {
|
2020-07-14 19:21:32 +02:00
|
|
|
if (playerImpl == null) {
|
2019-12-29 22:15:01 +01:00
|
|
|
return null;
|
2020-07-14 19:21:32 +02:00
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
|
|
|
|
return playerImpl.getRootView();
|
|
|
|
}
|
|
|
|
|
|
|
|
public void removeViewFromParent() {
|
|
|
|
if (getView().getParent() != null) {
|
|
|
|
if (playerImpl.getParentActivity() != null) {
|
|
|
|
// This means view was added to fragment
|
2020-07-12 02:59:47 +02:00
|
|
|
final ViewGroup parent = (ViewGroup) getView().getParent();
|
2019-12-29 22:15:01 +01:00
|
|
|
parent.removeView(getView());
|
2020-07-14 19:21:32 +02:00
|
|
|
} else {
|
2019-12-29 22:15:01 +01:00
|
|
|
// This means view was added by windowManager for popup player
|
|
|
|
windowManager.removeViewImmediate(getView());
|
2020-07-14 19:21:32 +02:00
|
|
|
}
|
2019-12-29 22:15:01 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-01-15 19:32:29 +01:00
|
|
|
private void showNotificationAndStartForeground() {
|
2020-08-15 15:16:17 +02:00
|
|
|
NotificationUtil.getInstance().recreateNotification(playerImpl, true);
|
2020-08-02 22:59:43 +02:00
|
|
|
NotificationUtil.getInstance().setProgressbarOnOldNotifications(100, 0, false);
|
|
|
|
NotificationUtil.getInstance().startForegroundServiceWithNotification(this);
|
2019-12-29 22:15:01 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public class LocalBinder extends Binder {
|
|
|
|
|
|
|
|
public MainPlayer getService() {
|
|
|
|
return MainPlayer.this;
|
|
|
|
}
|
|
|
|
|
|
|
|
public VideoPlayerImpl getPlayer() {
|
|
|
|
return MainPlayer.this.playerImpl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|