diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 7028989..13fae5c 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -91,6 +91,14 @@
android:supportsPictureInPicture="true"
android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|screenLayout|uiMode|keyboardHidden"
android:exported="false"
+ android:launchMode="singleTask"
+ android:theme="@style/Theme.AsgardiusS3Manager.NoActionBar" />
+
= Build.VERSION_CODES.N
+ && this.getPackageManager()
+ .hasSystemFeature(
+ PackageManager.FEATURE_PICTURE_IN_PICTURE) && appOpsManager.checkOpNoThrow(
+ AppOpsManager.OPSTR_PICTURE_IN_PICTURE,
+ this.getPackageManager().getApplicationInfo(this.getPackageName(),
+ PackageManager.GET_META_DATA).uid, this.getPackageName())
+ == AppOpsManager.MODE_ALLOWED) {
+ Intent intent = new Intent(this, VideoPlayerPip.class);
+ intent.putExtra("video_url", url);
+ intent.putExtra("videocache", videocache);
+ intent.putExtra("buffersize", buffersize);
+ startActivity(intent);
+ } else {
+ Intent intent = new Intent(this, VideoPlayer.class);
+ intent.putExtra("video_url", url);
+ intent.putExtra("videocache", videocache);
+ intent.putExtra("buffersize", buffersize);
+ startActivity(intent);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ e.printStackTrace();
+ }
}
private void textViewer(String url) {
diff --git a/app/src/main/java/asgardius/page/s3manager/VideoPlayerPip.java b/app/src/main/java/asgardius/page/s3manager/VideoPlayerPip.java
new file mode 100644
index 0000000..44b3c9c
--- /dev/null
+++ b/app/src/main/java/asgardius/page/s3manager/VideoPlayerPip.java
@@ -0,0 +1,345 @@
+package asgardius.page.s3manager;
+
+import android.app.AppOpsManager;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.PictureInPictureParams;
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.net.wifi.WifiManager;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.PowerManager;
+import android.support.v4.media.session.MediaSessionCompat;
+import android.view.View;
+import android.widget.Toast;
+
+import androidx.annotation.Nullable;
+import androidx.appcompat.app.AppCompatActivity;
+
+import com.google.android.exoplayer2.C;
+import com.google.android.exoplayer2.DefaultLoadControl;
+import com.google.android.exoplayer2.DefaultRenderersFactory;
+import com.google.android.exoplayer2.ExoPlayer;
+import com.google.android.exoplayer2.MediaItem;
+import com.google.android.exoplayer2.PlaybackException;
+import com.google.android.exoplayer2.Player;
+import com.google.android.exoplayer2.audio.AudioAttributes;
+import com.google.android.exoplayer2.database.StandaloneDatabaseProvider;
+import com.google.android.exoplayer2.ext.mediasession.MediaSessionConnector;
+import com.google.android.exoplayer2.source.ProgressiveMediaSource;
+import com.google.android.exoplayer2.ui.PlayerNotificationManager;
+import com.google.android.exoplayer2.ui.StyledPlayerView;
+import com.google.android.exoplayer2.upstream.DefaultHttpDataSource;
+import com.google.android.exoplayer2.upstream.HttpDataSource;
+import com.google.android.exoplayer2.upstream.cache.CacheDataSource;
+import com.google.android.exoplayer2.upstream.cache.LeastRecentlyUsedCacheEvictor;
+import com.google.android.exoplayer2.upstream.cache.SimpleCache;
+
+import java.io.File;
+
+/**
+ * An example full-screen activity that shows and hides the system UI (i.e.
+ * status bar and navigation/system bar) with user interaction.
+ */
+public class VideoPlayerPip extends AppCompatActivity {
+
+ // creating a variable for exoplayerview.
+ protected StyledPlayerView playerView;
+ private WifiManager.WifiLock mWifiLock;
+ private PowerManager.WakeLock mWakeLock;
+ private PowerManager powerManager;
+ private long maxCacheSize;
+ LeastRecentlyUsedCacheEvictor evictor;
+ StandaloneDatabaseProvider standaloneDatabaseProvider;
+ SimpleCache simpleCache;
+ int videocache, buffersize;
+ ProgressiveMediaSource mediaSource;
+ DefaultLoadControl loadControl;
+ DefaultRenderersFactory renderersFactory;
+ ExoPlayer player;
+ long videoPosition;
+ MediaSessionCompat mediaSession;
+ MediaSessionConnector mediaSessionConnector;
+ StyledPlayerView.ControllerVisibilityListener control;
+ AppOpsManager appOpsManager;
+ private PlayerNotificationManager playerNotificationManager;
+ private int notificationId = 1234;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.activity_video_player);
+ if(Build.VERSION.SDK_INT >=Build.VERSION_CODES.O){
+
+ NotificationChannel channel= new NotificationChannel("playback","Video Playback", NotificationManager.IMPORTANCE_DEFAULT);
+ NotificationManager manager =getSystemService(NotificationManager.class);
+ channel.setSound(null, null);
+ manager.createNotificationChannel(channel);
+ }
+ appOpsManager = (AppOpsManager)getSystemService(Context.APP_OPS_SERVICE);
+ mediaSession = new MediaSessionCompat(this, getPackageName());
+ mediaSessionConnector = new MediaSessionConnector(mediaSession);
+ hideSystemBars();
+ AudioAttributes audioAttributes = new AudioAttributes.Builder()
+ .setUsage(C.USAGE_MEDIA)
+ .setContentType(C.AUDIO_CONTENT_TYPE_MOVIE)
+ .build();
+ // create Wifi and wake locks
+ mWifiLock = ((WifiManager) this.getApplicationContext().getSystemService(Context.WIFI_SERVICE)).createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, "S3Manager:wifi_lock");
+ powerManager = (PowerManager) getSystemService(POWER_SERVICE);
+ mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "S3Manager:wake_lock");
+ //Get media url
+ String videoURL = getIntent().getStringExtra("video_url");
+ videocache = getIntent().getIntExtra("videocache", 40);
+ buffersize = getIntent().getIntExtra("buffersize", 2000);
+ loadControl = new DefaultLoadControl.Builder().setBufferDurationsMs(2000, buffersize, 1500, 2000).build();
+
+ @DefaultRenderersFactory.ExtensionRendererMode int extensionRendererMode = DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER;
+
+ renderersFactory = new DefaultRenderersFactory(this) .setExtensionRendererMode(extensionRendererMode);
+ standaloneDatabaseProvider = new StandaloneDatabaseProvider(this);
+ maxCacheSize = (long)videocache * 1024 * 1024;
+ playerView = findViewById(R.id.player_view);
+ // creating a variable for exoplayer
+ player = new ExoPlayer.Builder(this).setLoadControl(loadControl).build();
+ player.setAudioAttributes(audioAttributes, true);
+ mediaSessionConnector.setPlayer(player);
+ mediaSession.setActive(true);
+ evictor = new LeastRecentlyUsedCacheEvictor(maxCacheSize);
+ simpleCache = new SimpleCache(
+ new File(this.getCacheDir(), "media"),
+ evictor,
+ standaloneDatabaseProvider);
+ mediaSource = new ProgressiveMediaSource.Factory(
+ new CacheDataSource.Factory()
+ .setCache(simpleCache)
+ .setUpstreamDataSourceFactory(new DefaultHttpDataSource.Factory()
+ .setUserAgent("ExoplayerDemo"))
+ .setFlags(CacheDataSource.FLAG_IGNORE_CACHE_ON_ERROR)
+ ).createMediaSource(MediaItem.fromUri(Uri.parse(videoURL)));
+ playerView.setPlayer(player);
+ control = new StyledPlayerView.ControllerVisibilityListener() {
+ @Override
+ public void onVisibilityChanged(int visibility) {
+ if (playerView.isControllerFullyVisible()) {
+ showSystemBars();
+ } else {
+ hideSystemBars();
+ }
+ }
+ };
+ playerView.setControllerVisibilityListener(control);
+ //MediaItem mediaItem = MediaItem.fromUri(videoURL);
+
+ // Set the media item to be played.
+ //player.setMediaItem(mediaItem);
+ // Prepare the player.
+ player.setPlayWhenReady(true);
+ playerNotificationManager = new PlayerNotificationManager.Builder(this, notificationId, "playback").build();
+ playerNotificationManager.setMediaSessionToken(mediaSession.getSessionToken());
+ playerNotificationManager.setPlayer(player);
+ player.setMediaSource(mediaSource);
+ player.prepare();
+ // Start the playback.
+ player.play();
+
+ player.addListener(new Player.Listener() {
+ @Override
+
+ public void onPlayerError(PlaybackException error) {
+ Throwable cause = error.getCause();
+ if (cause instanceof HttpDataSource.HttpDataSourceException) {
+ // An HTTP error occurred.
+ //System.out.println("Playback error F");
+ Toast.makeText(getApplicationContext(), getResources().getString(R.string.media_conn_fail), Toast.LENGTH_SHORT).show();
+ } else {
+ // An HTTP error occurred.
+ //System.out.println("Playback error F");
+ Toast.makeText(getApplicationContext(), getResources().getString(R.string.media_wrong_type), Toast.LENGTH_SHORT).show();
+ }
+ player.release();
+ finish();
+ }
+
+
+ });
+
+ player.addListener(new Player.Listener() {
+ @Override
+ public void onPlaybackStateChanged(@Player.State int state) {
+ if (state == 3) {
+ // Active playback.
+ //Acquiring WakeLock and WifiLock if not held
+ if (!mWifiLock.isHeld()) {
+ mWifiLock.acquire();
+ //System.out.println("WifiLock acquired");
+ }
+ if (!mWakeLock.isHeld()) {
+ mWakeLock.acquire();
+ //System.out.println("WakeLock acquired");
+ }
+ } else if (state == 2) {
+ // Buffering.
+ //Acquiring WakeLock and WifiLock if not held
+ if (!mWifiLock.isHeld()) {
+ mWifiLock.acquire();
+ //System.out.println("WifiLock acquired");
+ }
+ if (!mWakeLock.isHeld()) {
+ mWakeLock.acquire();
+ //System.out.println("WakeLock acquired");
+ }
+ } else {
+ //Player inactive
+ //Releasing WifiLock and WakeLock if held
+ if (mWifiLock.isHeld()) {
+ mWifiLock.release();
+ //System.out.println("WifiLock released");
+ }
+ if (mWakeLock.isHeld()) {
+ mWakeLock.release();
+ //System.out.println("WakeLock released");
+ }
+ // Not playing because playback is paused, ended, suppressed, or the player
+ // is buffering, stopped or failed. Check player.getPlayWhenReady,
+ // player.getPlaybackState, player.getPlaybackSuppressionReason and
+ // player.getPlaybackError for details.
+ }
+ }
+ });
+ }
+
+ private void hideSystemBars() {
+ // Set the IMMERSIVE flag.
+ // Set the content to appear under the system bars so that the content
+ // doesn't resize when the system bars hide and show.
+ View decorView = getWindow().getDecorView();
+ decorView.setSystemUiVisibility(
+ View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
+ | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
+ | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION // hide nav bar
+ | View.SYSTEM_UI_FLAG_FULLSCREEN // hide status bar
+ | View.SYSTEM_UI_FLAG_IMMERSIVE);
+ }
+
+ private void showSystemBars() {
+ View decorView = getWindow().getDecorView();
+ decorView.setSystemUiVisibility(
+ View.SYSTEM_UI_FLAG_LAYOUT_STABLE
+ | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
+ }
+
+
+ protected void enterPIPMode() {
+ try {
+ if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
+ && this.getPackageManager()
+ .hasSystemFeature(
+ PackageManager.FEATURE_PICTURE_IN_PICTURE) && appOpsManager.checkOpNoThrow(
+ AppOpsManager.OPSTR_PICTURE_IN_PICTURE,
+ this.getPackageManager().getApplicationInfo(this.getPackageName(),
+ PackageManager.GET_META_DATA).uid, this.getPackageName())
+ == AppOpsManager.MODE_ALLOWED) {
+ videoPosition = player.getCurrentPosition();
+ playerView.setUseController(false);
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ PictureInPictureParams params = new PictureInPictureParams.Builder().build();
+ this.enterPictureInPictureMode(params);
+ }else {
+ this.enterPictureInPictureMode();
+ }
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+
+ @Override
+
+ public void onDestroy() {
+ mediaSessionConnector.setPlayer(null);
+ mediaSession.setActive(false);
+ playerNotificationManager.setPlayer(null);
+ player.release();
+ playerView.setPlayer(null);
+ simpleCache.release();
+ super.onDestroy();
+ }
+
+ public void onResume(){
+ super.onResume();
+ // put your code here...
+ if (!playerView.getUseController()) {
+ playerView.setUseController(true);
+ }
+ }
+
+ public void onUserLeaveHint() {
+ super.onUserLeaveHint();
+ enterPIPMode();
+ }
+
+ public void onBackPressed() {
+ try {
+ if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
+ && this.getPackageManager()
+ .hasSystemFeature(
+ PackageManager.FEATURE_PICTURE_IN_PICTURE) && appOpsManager.checkOpNoThrow(
+ AppOpsManager.OPSTR_PICTURE_IN_PICTURE,
+ this.getPackageManager().getApplicationInfo(this.getPackageName(),
+ PackageManager.GET_META_DATA).uid, this.getPackageName())
+ == AppOpsManager.MODE_ALLOWED) {
+ enterPIPMode();
+ } else {
+ super.onBackPressed();
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ e.printStackTrace();
+ super.onBackPressed();
+ }
+ }
+
+ private class DescriptionAdapter implements
+ PlayerNotificationManager.MediaDescriptionAdapter {
+
+ @Override
+ public String getCurrentContentTitle(Player player) {
+ int window = player.getCurrentMediaItemIndex();
+ return getTitle().toString();
+ }
+
+ @Nullable
+ @Override
+ public String getCurrentContentText(Player player) {
+ int window = player.getCurrentMediaItemIndex();
+ return getCurrentContentText(player);
+ }
+
+ @Nullable
+ @Override
+ public Bitmap getCurrentLargeIcon(Player player,
+ PlayerNotificationManager.BitmapCallback callback) {
+ int window = player.getCurrentMediaItemIndex();
+ Bitmap largeIcon = getCurrentLargeIcon(player, callback);
+ /*if (largeIcon == null && getLargeIconUri(window) != null) {
+ // load bitmap async
+ loadBitmap(getLargeIconUri(window), callback);
+ return getPlaceholderBitmap();
+ }*/
+ return largeIcon;
+ }
+
+ @Nullable
+ @Override
+ public PendingIntent createCurrentContentIntent(Player player) {
+ int window = player.getCurrentMediaItemIndex();
+ //return createPendingIntent(window);
+ return null;
+ }
+ }
+}
\ No newline at end of file