mirror of
				https://github.com/TeamNewPipe/NewPipe
				synced 2025-10-30 23:03:00 +00:00 
			
		
		
		
	All players in one place
- main, background, popup players now connected via one service, one view, one fragment, one activity and one gesture listener - playback position is synchronized between players. Easy to switch from one to another - expandable player at the bottom of the screen with cool animation and additional features like long click to open channel of a video, play/pause/close buttons and swipe down to dismiss - in-player integrated buttons for opening in browser, playing with Kodi, sharing a video - better background playback that can be activated in settings. Allows to automatically switch to audio-only mode when going to background and then switching to video-mode when returning to the app.
This commit is contained in:
		| @@ -43,6 +43,14 @@ | ||||
|             </intent-filter> | ||||
|         </service> | ||||
|  | ||||
|         <service | ||||
|                 android:name=".player.MainPlayer" | ||||
|                 android:exported="false"> | ||||
|             <intent-filter> | ||||
|                 <action android:name="android.intent.action.MEDIA_BUTTON" /> | ||||
|             </intent-filter> | ||||
|         </service> | ||||
|  | ||||
|         <activity | ||||
|             android:name=".player.BackgroundPlayerActivity" | ||||
|             android:launchMode="singleTask" | ||||
|   | ||||
| @@ -20,8 +20,7 @@ | ||||
|  | ||||
| package org.schabi.newpipe; | ||||
|  | ||||
| import android.content.Intent; | ||||
| import android.content.SharedPreferences; | ||||
| import android.content.*; | ||||
| import android.content.pm.PackageManager; | ||||
| import android.os.Build; | ||||
| import android.os.Bundle; | ||||
| @@ -29,19 +28,8 @@ import android.os.Handler; | ||||
| import android.os.Looper; | ||||
| import android.preference.PreferenceManager; | ||||
| import android.util.Log; | ||||
| import android.view.LayoutInflater; | ||||
| import android.view.Menu; | ||||
| import android.view.MenuInflater; | ||||
| import android.view.MenuItem; | ||||
| import android.view.View; | ||||
| import android.view.Window; | ||||
| import android.view.WindowManager; | ||||
| import android.widget.AdapterView; | ||||
| import android.widget.ArrayAdapter; | ||||
| import android.widget.Button; | ||||
| import android.widget.ImageView; | ||||
| import android.widget.Spinner; | ||||
| import android.widget.TextView; | ||||
| import android.view.*; | ||||
| import android.widget.*; | ||||
|  | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.appcompat.app.ActionBar; | ||||
| @@ -53,6 +41,7 @@ import androidx.drawerlayout.widget.DrawerLayout; | ||||
| import androidx.fragment.app.Fragment; | ||||
| import androidx.fragment.app.FragmentManager; | ||||
|  | ||||
| import com.google.android.material.bottomsheet.BottomSheetBehavior; | ||||
| import com.google.android.material.navigation.NavigationView; | ||||
|  | ||||
| import org.schabi.newpipe.extractor.NewPipe; | ||||
| @@ -64,16 +53,10 @@ import org.schabi.newpipe.fragments.BackPressable; | ||||
| import org.schabi.newpipe.fragments.MainFragment; | ||||
| import org.schabi.newpipe.fragments.detail.VideoDetailFragment; | ||||
| import org.schabi.newpipe.fragments.list.search.SearchFragment; | ||||
| import org.schabi.newpipe.player.VideoPlayer; | ||||
| import org.schabi.newpipe.player.playqueue.PlayQueue; | ||||
| import org.schabi.newpipe.report.ErrorActivity; | ||||
| import org.schabi.newpipe.util.Constants; | ||||
| import org.schabi.newpipe.util.KioskTranslator; | ||||
| import org.schabi.newpipe.util.NavigationHelper; | ||||
| import org.schabi.newpipe.util.PeertubeHelper; | ||||
| import org.schabi.newpipe.util.PermissionHelper; | ||||
| import org.schabi.newpipe.util.ServiceHelper; | ||||
| import org.schabi.newpipe.util.StateSaver; | ||||
| import org.schabi.newpipe.util.TLSSocketFactoryCompat; | ||||
| import org.schabi.newpipe.util.ThemeHelper; | ||||
| import org.schabi.newpipe.util.*; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| @@ -470,12 +453,25 @@ public class MainActivity extends AppCompatActivity { | ||||
|     public void onBackPressed() { | ||||
|         if (DEBUG) Log.d(TAG, "onBackPressed() called"); | ||||
|  | ||||
|         Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_holder); | ||||
|         // If current fragment implements BackPressable (i.e. can/wanna handle back press) delegate the back press to it | ||||
|         if (fragment instanceof BackPressable) { | ||||
|             if (((BackPressable) fragment).onBackPressed()) return; | ||||
|         } | ||||
|         FrameLayout bottomSheetLayout = findViewById(R.id.fragment_player_holder); | ||||
|         BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(bottomSheetLayout); | ||||
|  | ||||
|         if (bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_HIDDEN || bottomSheetBehavior.getState() == BottomSheetBehavior.STATE_COLLAPSED) { | ||||
|             Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_holder); | ||||
|             // If current fragment implements BackPressable (i.e. can/wanna handle back press) delegate the back press to it | ||||
|             if (fragment instanceof BackPressable) { | ||||
|                 if (((BackPressable) fragment).onBackPressed()) return; | ||||
|             } | ||||
|  | ||||
|         } else { | ||||
|             Fragment fragmentPlayer = getSupportFragmentManager().findFragmentById(R.id.fragment_player_holder); | ||||
|             // If current fragment implements BackPressable (i.e. can/wanna handle back press) delegate the back press to it | ||||
|             if (fragmentPlayer instanceof BackPressable) { | ||||
|                 if (!((BackPressable) fragmentPlayer).onBackPressed()) | ||||
|                     bottomSheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED); | ||||
|                 return; | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         if (getSupportFragmentManager().getBackStackEntryCount() == 1) { | ||||
|             finish(); | ||||
| @@ -494,7 +490,7 @@ public class MainActivity extends AppCompatActivity { | ||||
|                 NavigationHelper.openDownloads(this); | ||||
|                 break; | ||||
|             case PermissionHelper.DOWNLOAD_DIALOG_REQUEST_CODE: | ||||
|                 Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_holder); | ||||
|                 Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_player_holder); | ||||
|                 if (fragment instanceof VideoDetailFragment) { | ||||
|                     ((VideoDetailFragment) fragment).openDownloadDialog(); | ||||
|                 } | ||||
| @@ -595,6 +591,11 @@ public class MainActivity extends AppCompatActivity { | ||||
|         if (DEBUG) Log.d(TAG, "initFragments() called"); | ||||
|         StateSaver.clearStateFiles(); | ||||
|         if (getIntent() != null && getIntent().hasExtra(Constants.KEY_LINK_TYPE)) { | ||||
|             // When user watch a video inside popup and then tries to open the video in main player while the app is closed | ||||
|             // he will see a blank fragment on place of kiosk. Let's open it first | ||||
|             if (getSupportFragmentManager().getBackStackEntryCount() == 0) | ||||
|                 NavigationHelper.openMainFragment(getSupportFragmentManager()); | ||||
|  | ||||
|             handleIntent(getIntent()); | ||||
|         } else NavigationHelper.gotoMainFragment(getSupportFragmentManager()); | ||||
|     } | ||||
| @@ -643,7 +644,9 @@ public class MainActivity extends AppCompatActivity { | ||||
|                 switch (((StreamingService.LinkType) intent.getSerializableExtra(Constants.KEY_LINK_TYPE))) { | ||||
|                     case STREAM: | ||||
|                         boolean autoPlay = intent.getBooleanExtra(VideoDetailFragment.AUTO_PLAY, false); | ||||
|                         NavigationHelper.openVideoDetailFragment(getSupportFragmentManager(), serviceId, url, title, autoPlay); | ||||
|                         final String intentCacheKey = intent.getStringExtra(VideoPlayer.PLAY_QUEUE_KEY); | ||||
|                         final PlayQueue playQueue = intentCacheKey != null ? SerializedCache.getInstance().take(intentCacheKey, PlayQueue.class) : null; | ||||
|                         NavigationHelper.openVideoDetailFragment(getSupportFragmentManager(), serviceId, url, title, autoPlay, playQueue); | ||||
|                         break; | ||||
|                     case CHANNEL: | ||||
|                         NavigationHelper.openChannelFragment(getSupportFragmentManager(), | ||||
|   | ||||
| @@ -38,6 +38,7 @@ import org.schabi.newpipe.extractor.exceptions.ExtractionException; | ||||
| import org.schabi.newpipe.extractor.playlist.PlaylistInfo; | ||||
| import org.schabi.newpipe.extractor.stream.StreamInfo; | ||||
| import org.schabi.newpipe.extractor.stream.VideoStream; | ||||
| import org.schabi.newpipe.fragments.detail.VideoDetailFragment; | ||||
| import org.schabi.newpipe.player.playqueue.ChannelPlayQueue; | ||||
| import org.schabi.newpipe.player.playqueue.PlayQueue; | ||||
| import org.schabi.newpipe.player.playqueue.PlaylistPlayQueue; | ||||
| @@ -581,7 +582,7 @@ public class RouterActivity extends AppCompatActivity { | ||||
|                         playQueue = new SinglePlayQueue((StreamInfo) info); | ||||
|  | ||||
|                         if (playerChoice.equals(videoPlayerKey)) { | ||||
|                             NavigationHelper.playOnMainPlayer(this, playQueue, true); | ||||
|                             openMainPlayer(playQueue, choice); | ||||
|                         } else if (playerChoice.equals(backgroundPlayerKey)) { | ||||
|                             NavigationHelper.enqueueOnBackgroundPlayer(this, playQueue, true); | ||||
|                         } else if (playerChoice.equals(popupPlayerKey)) { | ||||
| @@ -594,7 +595,7 @@ public class RouterActivity extends AppCompatActivity { | ||||
|                     playQueue = info instanceof ChannelInfo ? new ChannelPlayQueue((ChannelInfo) info) : new PlaylistPlayQueue((PlaylistInfo) info); | ||||
|  | ||||
|                     if (playerChoice.equals(videoPlayerKey)) { | ||||
|                         NavigationHelper.playOnMainPlayer(this, playQueue, true); | ||||
|                         openMainPlayer(playQueue, choice); | ||||
|                     } else if (playerChoice.equals(backgroundPlayerKey)) { | ||||
|                         NavigationHelper.playOnBackgroundPlayer(this, playQueue, true); | ||||
|                     } else if (playerChoice.equals(popupPlayerKey)) { | ||||
| @@ -604,6 +605,16 @@ public class RouterActivity extends AppCompatActivity { | ||||
|             }; | ||||
|         } | ||||
|  | ||||
|         private void openMainPlayer(PlayQueue playQueue, Choice choice) { | ||||
|             Intent intent = NavigationHelper.getPlayerIntent(this, MainActivity.class, playQueue, true); | ||||
|             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | ||||
|             intent.putExtra(Constants.KEY_LINK_TYPE, choice.linkType); | ||||
|             intent.putExtra(Constants.KEY_URL, choice.url); | ||||
|             intent.putExtra(Constants.KEY_TITLE, ""); | ||||
|             intent.putExtra(VideoDetailFragment.AUTO_PLAY, true); | ||||
|             this.startActivity(intent); | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public void onDestroy() { | ||||
|             super.onDestroy(); | ||||
|   | ||||
| @@ -1,16 +1,20 @@ | ||||
| package org.schabi.newpipe.fragments.detail; | ||||
|  | ||||
| import org.schabi.newpipe.player.playqueue.PlayQueue; | ||||
|  | ||||
| import java.io.Serializable; | ||||
|  | ||||
| class StackItem implements Serializable { | ||||
|     private final int serviceId; | ||||
|     private String title; | ||||
|     private final String url; | ||||
|     private final PlayQueue playQueue; | ||||
|  | ||||
|     StackItem(int serviceId, String url, String title) { | ||||
|     StackItem(int serviceId, String url, String title, PlayQueue playQueue) { | ||||
|         this.serviceId = serviceId; | ||||
|         this.url = url; | ||||
|         this.title = title; | ||||
|         this.playQueue = playQueue; | ||||
|     } | ||||
|  | ||||
|     public void setTitle(String title) { | ||||
| @@ -29,6 +33,8 @@ class StackItem implements Serializable { | ||||
|         return url; | ||||
|     } | ||||
|  | ||||
|     public PlayQueue getPlayQueue() { return playQueue; } | ||||
|  | ||||
|     @Override | ||||
|     public String toString() { | ||||
|         return getServiceId() + ":" + getUrl() + " > " + getTitle(); | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -1,9 +1,11 @@ | ||||
| package org.schabi.newpipe.player; | ||||
|  | ||||
| import android.content.Intent; | ||||
| import android.view.Menu; | ||||
| import android.view.MenuItem; | ||||
|  | ||||
| import org.schabi.newpipe.R; | ||||
| import org.schabi.newpipe.util.NavigationHelper; | ||||
| import org.schabi.newpipe.util.PermissionHelper; | ||||
|  | ||||
| import static org.schabi.newpipe.player.BackgroundPlayer.ACTION_CLOSE; | ||||
| @@ -24,20 +26,20 @@ public final class BackgroundPlayerActivity extends ServicePlayerActivity { | ||||
|  | ||||
|     @Override | ||||
|     public Intent getBindIntent() { | ||||
|         return new Intent(this, BackgroundPlayer.class); | ||||
|         return new Intent(this, MainPlayer.class); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void startPlayerListener() { | ||||
|         if (player != null && player instanceof BackgroundPlayer.BasePlayerImpl) { | ||||
|             ((BackgroundPlayer.BasePlayerImpl) player).setActivityListener(this); | ||||
|         if (player instanceof VideoPlayerImpl) { | ||||
|             ((VideoPlayerImpl) player).setActivityListener(this); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void stopPlayerListener() { | ||||
|         if (player != null && player instanceof BackgroundPlayer.BasePlayerImpl) { | ||||
|             ((BackgroundPlayer.BasePlayerImpl) player).removeActivityListener(this); | ||||
|         if (player instanceof VideoPlayerImpl) { | ||||
|             ((VideoPlayerImpl) player).removeActivityListener(this); | ||||
|         } | ||||
|     } | ||||
|  | ||||
| @@ -56,14 +58,28 @@ public final class BackgroundPlayerActivity extends ServicePlayerActivity { | ||||
|             } | ||||
|  | ||||
|             this.player.setRecovery(); | ||||
|             getApplicationContext().sendBroadcast(getPlayerShutdownIntent()); | ||||
|             getApplicationContext().startService(getSwitchIntent(PopupVideoPlayer.class)); | ||||
|             NavigationHelper.playOnPopupPlayer(getApplicationContext(), player.playQueue, true); | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         if (item.getItemId() == R.id.action_switch_background) { | ||||
|             this.player.setRecovery(); | ||||
|             NavigationHelper.playOnBackgroundPlayer(getApplicationContext(), player.playQueue, true); | ||||
|             return true; | ||||
|         } | ||||
|  | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void setupMenu(Menu menu) { | ||||
|         if(player == null) return; | ||||
|  | ||||
|         menu.findItem(R.id.action_switch_popup).setVisible(!((VideoPlayerImpl)player).popupPlayerSelected()); | ||||
|         menu.findItem(R.id.action_switch_background).setVisible(!((VideoPlayerImpl)player).audioPlayerSelected()); | ||||
|     } | ||||
|  | ||||
|     //@Override | ||||
|     public Intent getPlayerShutdownIntent() { | ||||
|         return new Intent(ACTION_CLOSE); | ||||
|     } | ||||
|   | ||||
| @@ -151,6 +151,8 @@ public abstract class BasePlayer implements | ||||
|     public static final String RESUME_PLAYBACK = "resume_playback"; | ||||
|     @NonNull | ||||
|     public static final String SELECT_ON_APPEND = "select_on_append"; | ||||
|     @NonNull | ||||
|     public static final String PLAYER_TYPE = "player_type"; | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Playback | ||||
| @@ -182,6 +184,10 @@ public abstract class BasePlayer implements | ||||
|     protected final static int PROGRESS_LOOP_INTERVAL_MILLIS = 500; | ||||
|     protected final static int RECOVERY_SKIP_THRESHOLD_MILLIS = 3000; // 3 seconds | ||||
|  | ||||
|     public final static int PLAYER_TYPE_VIDEO = 0; | ||||
|     public final static int PLAYER_TYPE_AUDIO = 1; | ||||
|     public final static int PLAYER_TYPE_POPUP = 2; | ||||
|  | ||||
|     protected SimpleExoPlayer simpleExoPlayer; | ||||
|     protected AudioReactor audioReactor; | ||||
|     protected MediaSessionManager mediaSessionManager; | ||||
| @@ -290,12 +296,24 @@ public abstract class BasePlayer implements | ||||
|             if (item != null && item.getRecoveryPosition() == PlayQueueItem.RECOVERY_UNSET) { | ||||
|                 stateLoader = recordManager.loadStreamState(item) | ||||
|                         .observeOn(AndroidSchedulers.mainThread()) | ||||
|                         .doFinally(() -> initPlayback(queue, repeatMode, playbackSpeed, playbackPitch, playbackSkipSilence, | ||||
|                                 /*playOnInit=*/true)) | ||||
|                         // Do not place initPlayback() in doFinally() because it restarts playback after destroy() | ||||
|                         //.doFinally() | ||||
|                         .subscribe( | ||||
|                                 state -> queue.setRecovery(queue.getIndex(), state.getProgressTime()), | ||||
|                                 state -> { | ||||
|                                     queue.setRecovery(queue.getIndex(), state.getProgressTime()); | ||||
|                                     initPlayback(queue, repeatMode, playbackSpeed, playbackPitch, playbackSkipSilence, | ||||
|                                             /*playOnInit=*/true); | ||||
|                                 }, | ||||
|                                 error -> { | ||||
|                                     if (DEBUG) error.printStackTrace(); | ||||
|                                     // In case any error we can start playback without history | ||||
|                                     initPlayback(queue, repeatMode, playbackSpeed, playbackPitch, playbackSkipSilence, | ||||
|                                             /*playOnInit=*/true); | ||||
|                                 }, | ||||
|                                 () -> { | ||||
|                                     // Completed but not found in history | ||||
|                                     initPlayback(queue, repeatMode, playbackSpeed, playbackPitch, playbackSkipSilence, | ||||
|                                             /*playOnInit=*/true); | ||||
|                                 } | ||||
|                         ); | ||||
|                 databaseUpdateReactor.add(stateLoader); | ||||
| @@ -354,7 +372,7 @@ public abstract class BasePlayer implements | ||||
|  | ||||
|         databaseUpdateReactor.clear(); | ||||
|         progressUpdateReactor.set(null); | ||||
|  | ||||
|         ImageLoader.getInstance().stop(); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
| @@ -931,6 +949,7 @@ public abstract class BasePlayer implements | ||||
|         } | ||||
|  | ||||
|         simpleExoPlayer.setPlayWhenReady(true); | ||||
|         savePlaybackState(); | ||||
|     } | ||||
|  | ||||
|     public void onPause() { | ||||
| @@ -939,6 +958,7 @@ public abstract class BasePlayer implements | ||||
|  | ||||
|         audioReactor.abandonAudioFocus(); | ||||
|         simpleExoPlayer.setPlayWhenReady(false); | ||||
|         savePlaybackState(); | ||||
|     } | ||||
|  | ||||
|     public void onPlayPause() { | ||||
|   | ||||
							
								
								
									
										353
									
								
								app/src/main/java/org/schabi/newpipe/player/MainPlayer.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										353
									
								
								app/src/main/java/org/schabi/newpipe/player/MainPlayer.java
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,353 @@ | ||||
| /* | ||||
|  * Copyright 2017 Mauricio Colli <mauriciocolli@outlook.com> | ||||
|  * BackgroundPlayer.java is part of NewPipe | ||||
|  * | ||||
|  * 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.NotificationManager; | ||||
| import android.app.PendingIntent; | ||||
| import android.app.Service; | ||||
| import android.content.Context; | ||||
| import android.content.Intent; | ||||
| import android.os.Binder; | ||||
| import android.os.IBinder; | ||||
| import android.view.ViewGroup; | ||||
| import android.view.WindowManager; | ||||
| import androidx.core.app.NotificationCompat; | ||||
| import android.util.Log; | ||||
| import android.view.View; | ||||
| import android.widget.RemoteViews; | ||||
|  | ||||
| import com.google.android.exoplayer2.Player; | ||||
|  | ||||
| import org.schabi.newpipe.BuildConfig; | ||||
| import org.schabi.newpipe.MainActivity; | ||||
| import org.schabi.newpipe.R; | ||||
| import org.schabi.newpipe.player.helper.LockManager; | ||||
| import org.schabi.newpipe.util.NavigationHelper; | ||||
| import org.schabi.newpipe.util.ThemeHelper; | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * One service for all players | ||||
|  * | ||||
|  * @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 LockManager lockManager; | ||||
|  | ||||
|     private final IBinder mBinder = new MainPlayer.LocalBinder(); | ||||
|  | ||||
|     public enum PlayerType { | ||||
|         VIDEO, | ||||
|         AUDIO, | ||||
|         POPUP | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Notification | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     static final int NOTIFICATION_ID = 123789; | ||||
|     private NotificationManager notificationManager; | ||||
|     private NotificationCompat.Builder notBuilder; | ||||
|     private RemoteViews notRemoteView; | ||||
|     private RemoteViews bigNotRemoteView; | ||||
|  | ||||
|     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"; | ||||
|  | ||||
|     private static final String SET_IMAGE_RESOURCE_METHOD = "setImageResource"; | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Service's LifeCycle | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     @Override | ||||
|     public void onCreate() { | ||||
|         if (DEBUG) Log.d(TAG, "onCreate() called"); | ||||
|         notificationManager = ((NotificationManager) getSystemService(NOTIFICATION_SERVICE)); | ||||
|         lockManager = new LockManager(this); | ||||
|         windowManager = (WindowManager) getSystemService(WINDOW_SERVICE); | ||||
|  | ||||
|         ThemeHelper.setTheme(this); | ||||
|         createView(); | ||||
|     } | ||||
|  | ||||
|     private void createView() { | ||||
|         View layout = View.inflate(this, R.layout.activity_main_player, null); | ||||
|  | ||||
|         playerImpl = new VideoPlayerImpl(this); | ||||
|         playerImpl.setup(layout); | ||||
|         playerImpl.shouldUpdateOnProgress = true; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public int onStartCommand(Intent intent, int flags, int startId) { | ||||
|         if (DEBUG) Log.d(TAG, "onStartCommand() called with: intent = [" + intent + | ||||
|                 "], flags = [" + flags + "], startId = [" + startId + "]"); | ||||
|         playerImpl.handleIntent(intent); | ||||
|         if (playerImpl.mediaSessionManager != null) { | ||||
|             playerImpl.mediaSessionManager.handleMediaButtonIntent(intent); | ||||
|         } | ||||
|         return START_NOT_STICKY; | ||||
|     } | ||||
|  | ||||
|     public void stop() { | ||||
|         if (DEBUG) | ||||
|             Log.d(TAG, "stop() called"); | ||||
|  | ||||
|         if (playerImpl.getPlayer() != null) { | ||||
|             playerImpl.wasPlaying = playerImpl.getPlayer().getPlayWhenReady(); | ||||
|             playerImpl.getPlayer().stop(false); | ||||
|             playerImpl.setRecovery(); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onTaskRemoved(Intent rootIntent) { | ||||
|         super.onTaskRemoved(rootIntent); | ||||
|         onDestroy(); | ||||
|         // Unload from memory completely | ||||
|         Runtime.getRuntime().halt(0); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onDestroy() { | ||||
|         if (DEBUG) Log.d(TAG, "destroy() called"); | ||||
|         onClose(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     protected void attachBaseContext(Context base) { | ||||
|         super.attachBaseContext(AudioServiceLeakFix.preventLeakOf(base)); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public IBinder onBind(Intent intent) { | ||||
|         return mBinder; | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Actions | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|     private void onClose() { | ||||
|         if (DEBUG) Log.d(TAG, "onClose() called"); | ||||
|  | ||||
|         if (lockManager != null) { | ||||
|             lockManager.releaseWifiAndCpu(); | ||||
|         } | ||||
|         if (playerImpl != null) { | ||||
|             removeViewFromParent(); | ||||
|  | ||||
|             playerImpl.savePlaybackState(); | ||||
|             playerImpl.stopActivityBinding(); | ||||
|             playerImpl.destroy(); | ||||
|         } | ||||
|         if (notificationManager != null) notificationManager.cancel(NOTIFICATION_ID); | ||||
|         playerImpl = null; | ||||
|         lockManager = null; | ||||
|  | ||||
|         stopForeground(true); | ||||
|         stopSelf(); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Utils | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     boolean isLandscape() { | ||||
|         return getResources().getDisplayMetrics().heightPixels < getResources().getDisplayMetrics().widthPixels; | ||||
|     } | ||||
|  | ||||
|     public View getView() { | ||||
|         if (playerImpl == null) | ||||
|             return null; | ||||
|  | ||||
|         return playerImpl.getRootView(); | ||||
|     } | ||||
|  | ||||
|     public void removeViewFromParent() { | ||||
|         if (getView().getParent() != null) { | ||||
|             if (playerImpl.getParentActivity() != null) { | ||||
|                 // This means view was added to fragment | ||||
|                 ViewGroup parent = (ViewGroup) getView().getParent(); | ||||
|                 parent.removeView(getView()); | ||||
|             } else | ||||
|                 // This means view was added by windowManager for popup player | ||||
|                 windowManager.removeViewImmediate(getView()); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Notification | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     void resetNotification() { | ||||
|         notBuilder = createNotification(); | ||||
|     } | ||||
|  | ||||
|     private NotificationCompat.Builder createNotification() { | ||||
|         notRemoteView = new RemoteViews(BuildConfig.APPLICATION_ID, R.layout.player_notification); | ||||
|         bigNotRemoteView = new RemoteViews(BuildConfig.APPLICATION_ID, R.layout.player_notification_expanded); | ||||
|  | ||||
|         setupNotification(notRemoteView); | ||||
|         setupNotification(bigNotRemoteView); | ||||
|  | ||||
|         NotificationCompat.Builder builder = new NotificationCompat.Builder(this, getString(R.string.notification_channel_id)) | ||||
|                 .setOngoing(true) | ||||
|                 .setSmallIcon(R.drawable.ic_newpipe_triangle_white) | ||||
|                 .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) | ||||
|                 .setCustomContentView(notRemoteView) | ||||
|                 .setCustomBigContentView(bigNotRemoteView); | ||||
|         builder.setPriority(NotificationCompat.PRIORITY_MAX); | ||||
|         return builder; | ||||
|     } | ||||
|  | ||||
|     private void setupNotification(RemoteViews remoteViews) { | ||||
|         // Don't show anything until player is playing | ||||
|         if (playerImpl == null) return; | ||||
|  | ||||
|         remoteViews.setTextViewText(R.id.notificationSongName, playerImpl.getVideoTitle()); | ||||
|         remoteViews.setTextViewText(R.id.notificationArtist, playerImpl.getUploaderName()); | ||||
|         remoteViews.setImageViewBitmap(R.id.notificationCover, playerImpl.getThumbnail()); | ||||
|  | ||||
|         remoteViews.setOnClickPendingIntent(R.id.notificationPlayPause, | ||||
|                 PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_PLAY_PAUSE), PendingIntent.FLAG_UPDATE_CURRENT)); | ||||
|         remoteViews.setOnClickPendingIntent(R.id.notificationStop, | ||||
|                 PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_CLOSE), PendingIntent.FLAG_UPDATE_CURRENT)); | ||||
|         // Starts VideoDetailFragment or opens BackgroundPlayerActivity. | ||||
|         remoteViews.setOnClickPendingIntent(R.id.notificationContent, | ||||
|                 PendingIntent.getActivity(this, NOTIFICATION_ID, getIntentForNotification(), PendingIntent.FLAG_UPDATE_CURRENT)); | ||||
|         remoteViews.setOnClickPendingIntent(R.id.notificationRepeat, | ||||
|                 PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_REPEAT), PendingIntent.FLAG_UPDATE_CURRENT)); | ||||
|  | ||||
|  | ||||
|         if (playerImpl.playQueue != null && playerImpl.playQueue.size() > 1) { | ||||
|             remoteViews.setInt(R.id.notificationFRewind, SET_IMAGE_RESOURCE_METHOD, R.drawable.exo_controls_previous); | ||||
|             remoteViews.setInt(R.id.notificationFForward, SET_IMAGE_RESOURCE_METHOD, R.drawable.exo_controls_next); | ||||
|             remoteViews.setOnClickPendingIntent(R.id.notificationFRewind, | ||||
|                     PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_PLAY_PREVIOUS), PendingIntent.FLAG_UPDATE_CURRENT)); | ||||
|             remoteViews.setOnClickPendingIntent(R.id.notificationFForward, | ||||
|                     PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_PLAY_NEXT), PendingIntent.FLAG_UPDATE_CURRENT)); | ||||
|         } else { | ||||
|             remoteViews.setInt(R.id.notificationFRewind, SET_IMAGE_RESOURCE_METHOD, R.drawable.exo_controls_rewind); | ||||
|             remoteViews.setInt(R.id.notificationFForward, SET_IMAGE_RESOURCE_METHOD, R.drawable.exo_controls_fastforward); | ||||
|             remoteViews.setOnClickPendingIntent(R.id.notificationFRewind, | ||||
|                     PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_FAST_REWIND), PendingIntent.FLAG_UPDATE_CURRENT)); | ||||
|             remoteViews.setOnClickPendingIntent(R.id.notificationFForward, | ||||
|                     PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_FAST_FORWARD), PendingIntent.FLAG_UPDATE_CURRENT)); | ||||
|         } | ||||
|  | ||||
|         setRepeatModeIcon(remoteViews, playerImpl.getRepeatMode()); | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * Updates the notification, and the play/pause button in it. | ||||
|      * Used for changes on the remoteView | ||||
|      * | ||||
|      * @param drawableId if != -1, sets the drawable with that id on the play/pause button | ||||
|      */ | ||||
|     synchronized void updateNotification(int drawableId) { | ||||
|         //if (DEBUG) Log.d(TAG, "updateNotification() called with: drawableId = [" + drawableId + "]"); | ||||
|         if (notBuilder == null) return; | ||||
|         if (drawableId != -1) { | ||||
|             if (notRemoteView != null) notRemoteView.setImageViewResource(R.id.notificationPlayPause, drawableId); | ||||
|             if (bigNotRemoteView != null) bigNotRemoteView.setImageViewResource(R.id.notificationPlayPause, drawableId); | ||||
|         } | ||||
|         notificationManager.notify(NOTIFICATION_ID, notBuilder.build()); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Utils | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private void setRepeatModeIcon(final RemoteViews remoteViews, final int repeatMode) { | ||||
|         if (remoteViews == null) return; | ||||
|  | ||||
|         switch (repeatMode) { | ||||
|             case Player.REPEAT_MODE_OFF: | ||||
|                 remoteViews.setInt(R.id.notificationRepeat, SET_IMAGE_RESOURCE_METHOD, R.drawable.exo_controls_repeat_off); | ||||
|                 break; | ||||
|             case Player.REPEAT_MODE_ONE: | ||||
|                 remoteViews.setInt(R.id.notificationRepeat, SET_IMAGE_RESOURCE_METHOD, R.drawable.exo_controls_repeat_one); | ||||
|                 break; | ||||
|             case Player.REPEAT_MODE_ALL: | ||||
|                 remoteViews.setInt(R.id.notificationRepeat, SET_IMAGE_RESOURCE_METHOD, R.drawable.exo_controls_repeat_all); | ||||
|                 break; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private Intent getIntentForNotification() { | ||||
|         Intent intent; | ||||
|         if (playerImpl.audioPlayerSelected() || playerImpl.popupPlayerSelected()) { | ||||
|             // Means we play in popup or audio only. Let's show BackgroundPlayerActivity | ||||
|             intent = NavigationHelper.getBackgroundPlayerActivityIntent(getApplicationContext()); | ||||
|         } else { | ||||
|             // We are playing in fragment. Don't open another activity just show fragment. That's it | ||||
|             intent = NavigationHelper.getPlayerIntent(this, MainActivity.class, null, true); | ||||
|             intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | ||||
|             intent.setAction(Intent.ACTION_MAIN); | ||||
|             intent.addCategory(Intent.CATEGORY_LAUNCHER); | ||||
|         } | ||||
|         return intent; | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Getters | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     LockManager getLockManager() { | ||||
|         return lockManager; | ||||
|     } | ||||
|  | ||||
|     NotificationCompat.Builder getNotBuilder() { | ||||
|         return notBuilder; | ||||
|     } | ||||
|  | ||||
|     RemoteViews getBigNotRemoteView() { | ||||
|         return bigNotRemoteView; | ||||
|     } | ||||
|  | ||||
|     RemoteViews getNotRemoteView() { | ||||
|         return notRemoteView; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     public class LocalBinder extends Binder { | ||||
|  | ||||
|         public MainPlayer getService() { | ||||
|             return MainPlayer.this; | ||||
|         } | ||||
|  | ||||
|         public VideoPlayerImpl getPlayer() { | ||||
|             return MainPlayer.this.playerImpl; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -546,7 +546,7 @@ public final class MainVideoPlayer extends AppCompatActivity | ||||
|                     onPlayBackgroundButtonClicked(); | ||||
|                     break; | ||||
|                 case PlayerHelper.MinimizeMode.MINIMIZE_ON_EXIT_MODE_POPUP: | ||||
|                     onFullScreenButtonClicked(); | ||||
|                     toggleFullscreen(); | ||||
|                     break; | ||||
|                 case PlayerHelper.MinimizeMode.MINIMIZE_ON_EXIT_MODE_NONE: | ||||
|                 default: | ||||
| @@ -593,8 +593,8 @@ public final class MainVideoPlayer extends AppCompatActivity | ||||
|         //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|         @Override | ||||
|         public void onFullScreenButtonClicked() { | ||||
|             super.onFullScreenButtonClicked(); | ||||
|         public void toggleFullscreen() { | ||||
|             super.toggleFullscreen(); | ||||
|  | ||||
|             if (DEBUG) Log.d(TAG, "onFullScreenButtonClicked() called"); | ||||
|             if (simpleExoPlayer == null) return; | ||||
| @@ -678,7 +678,7 @@ public final class MainVideoPlayer extends AppCompatActivity | ||||
|                 onScreenRotationClicked(); | ||||
|  | ||||
|             } else if (v.getId() == switchPopupButton.getId()) { | ||||
|                 onFullScreenButtonClicked(); | ||||
|                 toggleFullscreen(); | ||||
|  | ||||
|             } else if (v.getId() == switchBackgroundButton.getId()) { | ||||
|                 onPlayBackgroundButtonClicked(); | ||||
| @@ -746,7 +746,7 @@ public final class MainVideoPlayer extends AppCompatActivity | ||||
|         @Override | ||||
|         public void onPlaybackSpeedClicked() { | ||||
|             PlaybackParameterDialog | ||||
|                     .newInstance(getPlaybackSpeed(), getPlaybackPitch(), getPlaybackSkipSilence()) | ||||
|                     .newInstance(getPlaybackSpeed(), getPlaybackPitch(), getPlaybackSkipSilence(), MainVideoPlayer.this) | ||||
|                     .show(getSupportFragmentManager(), TAG); | ||||
|         } | ||||
|  | ||||
| @@ -909,6 +909,9 @@ public final class MainVideoPlayer extends AppCompatActivity | ||||
|             ); | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public void hideSystemUIIfNeeded() { } | ||||
|  | ||||
|         private void updatePlaybackButtons() { | ||||
|             if (repeatButton == null || shuffleButton == null || | ||||
|                     simpleExoPlayer == null || playQueue == null) return; | ||||
|   | ||||
| @@ -285,7 +285,7 @@ public final class PopupVideoPlayer extends Service { | ||||
|                 PendingIntent.getBroadcast(this, NOTIFICATION_ID, new Intent(ACTION_REPEAT), PendingIntent.FLAG_UPDATE_CURRENT)); | ||||
|  | ||||
|         // Starts popup player activity -- attempts to unlock lockscreen | ||||
|         final Intent intent = NavigationHelper.getPopupPlayerActivityIntent(this); | ||||
|         final Intent intent = NavigationHelper.getBackgroundPlayerActivityIntent(this); | ||||
|         notRemoteView.setOnClickPendingIntent(R.id.notificationContent, | ||||
|                 PendingIntent.getActivity(this, NOTIFICATION_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT)); | ||||
|  | ||||
| @@ -513,7 +513,7 @@ public final class PopupVideoPlayer extends Service { | ||||
|             super.initViews(rootView); | ||||
|             resizingIndicator = rootView.findViewById(R.id.resizing_indicator); | ||||
|             fullScreenButton = rootView.findViewById(R.id.fullScreenButton); | ||||
|             fullScreenButton.setOnClickListener(v -> onFullScreenButtonClicked()); | ||||
|             fullScreenButton.setOnClickListener(v -> toggleFullscreen()); | ||||
|             videoPlayPause = rootView.findViewById(R.id.videoPlayPause); | ||||
|  | ||||
|             extraOptionsView = rootView.findViewById(R.id.extraOptionsView); | ||||
| @@ -552,8 +552,8 @@ public final class PopupVideoPlayer extends Service { | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public void onFullScreenButtonClicked() { | ||||
|             super.onFullScreenButtonClicked(); | ||||
|         public void toggleFullscreen() { | ||||
|             super.toggleFullscreen(); | ||||
|  | ||||
|             if (DEBUG) Log.d(TAG, "onFullScreenButtonClicked() called"); | ||||
|  | ||||
| @@ -867,6 +867,9 @@ public final class PopupVideoPlayer extends Service { | ||||
|             super.hideControlsAndButton(duration, delay, videoPlayPause); | ||||
|         } | ||||
|  | ||||
|         @Override | ||||
|         public void hideSystemUIIfNeeded() { } | ||||
|  | ||||
|         /*////////////////////////////////////////////////////////////////////////// | ||||
|         // Utils | ||||
|         //////////////////////////////////////////////////////////////////////////*/ | ||||
|   | ||||
| @@ -1,6 +1,7 @@ | ||||
| package org.schabi.newpipe.player; | ||||
|  | ||||
| import android.content.Intent; | ||||
| import android.view.Menu; | ||||
| import android.view.MenuItem; | ||||
|  | ||||
| import org.schabi.newpipe.R; | ||||
| @@ -50,13 +51,18 @@ public final class PopupVideoPlayerActivity extends ServicePlayerActivity { | ||||
|         if (item.getItemId() == R.id.action_switch_background) { | ||||
|             this.player.setRecovery(); | ||||
|             getApplicationContext().sendBroadcast(getPlayerShutdownIntent()); | ||||
|             getApplicationContext().startService(getSwitchIntent(BackgroundPlayer.class)); | ||||
|             getApplicationContext().startService(getSwitchIntent(MainPlayer.class, MainPlayer.PlayerType.AUDIO)); | ||||
|             return true; | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void setupMenu(Menu menu) { | ||||
|  | ||||
|     } | ||||
|  | ||||
|     //@Override | ||||
|     public Intent getPlayerShutdownIntent() { | ||||
|         return new Intent(ACTION_CLOSE); | ||||
|     } | ||||
|   | ||||
| @@ -26,17 +26,17 @@ import android.widget.TextView; | ||||
| import com.google.android.exoplayer2.PlaybackParameters; | ||||
| import com.google.android.exoplayer2.Player; | ||||
|  | ||||
| import org.schabi.newpipe.MainActivity; | ||||
| import org.schabi.newpipe.R; | ||||
| import org.schabi.newpipe.extractor.StreamingService; | ||||
| import org.schabi.newpipe.extractor.stream.StreamInfo; | ||||
| import org.schabi.newpipe.fragments.OnScrollBelowItemsListener; | ||||
| import org.schabi.newpipe.fragments.detail.VideoDetailFragment; | ||||
| import org.schabi.newpipe.local.dialog.PlaylistAppendDialog; | ||||
| import org.schabi.newpipe.player.event.PlayerEventListener; | ||||
| import org.schabi.newpipe.player.helper.PlaybackParameterDialog; | ||||
| import org.schabi.newpipe.player.playqueue.PlayQueueAdapter; | ||||
| import org.schabi.newpipe.player.playqueue.PlayQueueItem; | ||||
| import org.schabi.newpipe.player.playqueue.PlayQueueItemBuilder; | ||||
| import org.schabi.newpipe.player.playqueue.PlayQueueItemHolder; | ||||
| import org.schabi.newpipe.player.playqueue.PlayQueueItemTouchCallback; | ||||
| import org.schabi.newpipe.player.playqueue.*; | ||||
| import org.schabi.newpipe.util.Constants; | ||||
| import org.schabi.newpipe.util.Localization; | ||||
| import org.schabi.newpipe.util.NavigationHelper; | ||||
| import org.schabi.newpipe.util.ThemeHelper; | ||||
| @@ -109,7 +109,7 @@ public abstract class ServicePlayerActivity extends AppCompatActivity | ||||
|  | ||||
|     public abstract boolean onPlayerOptionSelected(MenuItem item); | ||||
|  | ||||
|     public abstract Intent getPlayerShutdownIntent(); | ||||
|     public abstract void setupMenu(Menu menu); | ||||
|     //////////////////////////////////////////////////////////////////////////// | ||||
|     // Activity Lifecycle | ||||
|     //////////////////////////////////////////////////////////////////////////// | ||||
| @@ -148,6 +148,13 @@ public abstract class ServicePlayerActivity extends AppCompatActivity | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     // Allow to setup visibility of menuItems | ||||
|     @Override | ||||
|     public boolean onPrepareOptionsMenu(Menu menu) { | ||||
|         setupMenu(menu); | ||||
|         return super.onPrepareOptionsMenu(menu); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean onOptionsItemSelected(MenuItem item) { | ||||
|         switch (item.getItemId()) { | ||||
| @@ -166,8 +173,7 @@ public abstract class ServicePlayerActivity extends AppCompatActivity | ||||
|                 return true; | ||||
|             case R.id.action_switch_main: | ||||
|                 this.player.setRecovery(); | ||||
|                 getApplicationContext().sendBroadcast(getPlayerShutdownIntent()); | ||||
|                 getApplicationContext().startActivity(getSwitchIntent(MainVideoPlayer.class)); | ||||
|                 getApplicationContext().startActivity(getSwitchIntent(MainActivity.class, MainPlayer.PlayerType.VIDEO)); | ||||
|                 return true; | ||||
|         } | ||||
|         return onPlayerOptionSelected(item) || super.onOptionsItemSelected(item); | ||||
| @@ -179,8 +185,8 @@ public abstract class ServicePlayerActivity extends AppCompatActivity | ||||
|         unbind(); | ||||
|     } | ||||
|  | ||||
|     protected Intent getSwitchIntent(final Class clazz) { | ||||
|         return NavigationHelper.getPlayerIntent( | ||||
|     Intent getSwitchIntent(final Class clazz, final MainPlayer.PlayerType playerType) { | ||||
|         Intent intent = NavigationHelper.getPlayerIntent( | ||||
|                 getApplicationContext(), | ||||
|                 clazz, | ||||
|                 this.player.getPlayQueue(), | ||||
| @@ -189,8 +195,15 @@ public abstract class ServicePlayerActivity extends AppCompatActivity | ||||
|                 this.player.getPlaybackPitch(), | ||||
|                 this.player.getPlaybackSkipSilence(), | ||||
|                 null, | ||||
|                 false | ||||
|         ).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | ||||
|                 true); | ||||
|         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | ||||
|         intent.putExtra(Constants.KEY_LINK_TYPE, StreamingService.LinkType.STREAM); | ||||
|         intent.putExtra(Constants.KEY_URL, this.player.getVideoUrl()); | ||||
|         intent.putExtra(Constants.KEY_TITLE, this.player.getVideoTitle()); | ||||
|         intent.putExtra(VideoDetailFragment.AUTO_PLAY, true); | ||||
|         intent.putExtra(Constants.KEY_SERVICE_ID, this.player.getCurrentMetadata().getMetadata().getServiceId()); | ||||
|         intent.putExtra(VideoPlayer.PLAYER_TYPE, playerType); | ||||
|         return intent; | ||||
|     } | ||||
|  | ||||
|     //////////////////////////////////////////////////////////////////////////// | ||||
| @@ -236,6 +249,8 @@ public abstract class ServicePlayerActivity extends AppCompatActivity | ||||
|  | ||||
|                 if (service instanceof PlayerServiceBinder) { | ||||
|                     player = ((PlayerServiceBinder) service).getPlayerInstance(); | ||||
|                 } else if (service instanceof MainPlayer.LocalBinder) { | ||||
|                     player = ((MainPlayer.LocalBinder) service).getPlayer(); | ||||
|                 } | ||||
|  | ||||
|                 if (player == null || player.getPlayQueue() == null || | ||||
| @@ -474,7 +489,7 @@ public abstract class ServicePlayerActivity extends AppCompatActivity | ||||
|     private void openPlaybackParameterDialog() { | ||||
|         if (player == null) return; | ||||
|         PlaybackParameterDialog.newInstance(player.getPlaybackSpeed(), player.getPlaybackPitch(), | ||||
|                 player.getPlaybackSkipSilence()).show(getSupportFragmentManager(), getTag()); | ||||
|                 player.getPlaybackSkipSilence(), this).show(getSupportFragmentManager(), getTag()); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|   | ||||
| @@ -478,7 +478,6 @@ public abstract class VideoPlayer extends BasePlayer | ||||
|         super.onCompleted(); | ||||
|  | ||||
|         showControls(500); | ||||
|         animateView(endScreen, true, 800); | ||||
|         animateView(currentDisplaySeek, AnimationUtils.Type.SCALE_AND_ALPHA, false, 200); | ||||
|         loadingPanel.setVisibility(View.GONE); | ||||
|  | ||||
| @@ -575,11 +574,6 @@ public abstract class VideoPlayer extends BasePlayer | ||||
|         playbackSpeedTextView.setText(formatSpeed(getPlaybackSpeed())); | ||||
|  | ||||
|         super.onPrepared(playWhenReady); | ||||
|  | ||||
|         if (simpleExoPlayer.getCurrentPosition() != 0 && !isControlsVisible()) { | ||||
|             controlsVisibilityHandler.removeCallbacksAndMessages(null); | ||||
|             controlsVisibilityHandler.postDelayed(this::showControlsThenHide, DEFAULT_CONTROLS_DURATION); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
| @@ -615,7 +609,7 @@ public abstract class VideoPlayer extends BasePlayer | ||||
|         if (loadedImage != null) endScreen.setImageBitmap(loadedImage); | ||||
|     } | ||||
|  | ||||
|     protected void onFullScreenButtonClicked() { | ||||
|     protected void toggleFullscreen() { | ||||
|         changeState(STATE_BLOCKED); | ||||
|     } | ||||
|  | ||||
| @@ -724,7 +718,7 @@ public abstract class VideoPlayer extends BasePlayer | ||||
|         showControls(DEFAULT_CONTROLS_DURATION); | ||||
|     } | ||||
|  | ||||
|     private void onResizeClicked() { | ||||
|     void onResizeClicked() { | ||||
|         if (getAspectRatioFrameLayout() != null) { | ||||
|             final int currentResizeMode = getAspectRatioFrameLayout().getResizeMode(); | ||||
|             final int newResizeMode = nextResizeMode(currentResizeMode); | ||||
| @@ -888,6 +882,9 @@ public abstract class VideoPlayer extends BasePlayer | ||||
|             animateView(controlsRoot, false,duration); | ||||
|         }; | ||||
|     } | ||||
|  | ||||
|     public abstract void hideSystemUIIfNeeded(); | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Getters and Setters | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|   | ||||
							
								
								
									
										1552
									
								
								app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1552
									
								
								app/src/main/java/org/schabi/newpipe/player/VideoPlayerImpl.java
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -0,0 +1,47 @@ | ||||
| package org.schabi.newpipe.player.event; | ||||
|  | ||||
| import android.content.Context; | ||||
| import android.graphics.Rect; | ||||
| import android.util.AttributeSet; | ||||
| import android.view.MotionEvent; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import androidx.coordinatorlayout.widget.CoordinatorLayout; | ||||
| import com.google.android.material.bottomsheet.BottomSheetBehavior; | ||||
| import org.schabi.newpipe.R; | ||||
|  | ||||
| public class CustomBottomSheetBehavior extends BottomSheetBehavior { | ||||
|  | ||||
|     public CustomBottomSheetBehavior(Context context, AttributeSet attrs) { | ||||
|         super(context, attrs); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean onInterceptTouchEvent(CoordinatorLayout parent, View child, MotionEvent event) { | ||||
|         // Without overriding scrolling will not work in detail_content_root_layout | ||||
|         ViewGroup controls = child.findViewById(R.id.detail_content_root_layout); | ||||
|         if (controls != null) { | ||||
|             Rect rect = new Rect(); | ||||
|             controls.getGlobalVisibleRect(rect); | ||||
|             if (rect.contains((int) event.getX(), (int) event.getY())) return false; | ||||
|         } | ||||
|  | ||||
|         // Without overriding scrolling will not work on relatedStreamsLayout | ||||
|         ViewGroup relatedStreamsLayout = child.findViewById(R.id.relatedStreamsLayout); | ||||
|         if (relatedStreamsLayout != null) { | ||||
|             Rect rect = new Rect(); | ||||
|             relatedStreamsLayout.getGlobalVisibleRect(rect); | ||||
|             if (rect.contains((int) event.getX(), (int) event.getY())) return false; | ||||
|         } | ||||
|  | ||||
|         ViewGroup playQueue = child.findViewById(R.id.playQueue); | ||||
|         if (playQueue != null) { | ||||
|             Rect rect = new Rect(); | ||||
|             playQueue.getGlobalVisibleRect(rect); | ||||
|             if (rect.contains((int) event.getX(), (int) event.getY())) return false; | ||||
|         } | ||||
|  | ||||
|         return super.onInterceptTouchEvent(parent, child, event); | ||||
|     } | ||||
|  | ||||
| } | ||||
| @@ -0,0 +1,462 @@ | ||||
| package org.schabi.newpipe.player.event; | ||||
|  | ||||
| import android.app.Activity; | ||||
| import android.util.Log; | ||||
| import android.view.*; | ||||
| import androidx.appcompat.content.res.AppCompatResources; | ||||
| import org.schabi.newpipe.R; | ||||
| import org.schabi.newpipe.player.BasePlayer; | ||||
| import org.schabi.newpipe.player.MainPlayer; | ||||
| import org.schabi.newpipe.player.VideoPlayerImpl; | ||||
| import org.schabi.newpipe.player.helper.PlayerHelper; | ||||
|  | ||||
| import static org.schabi.newpipe.player.BasePlayer.*; | ||||
| import static org.schabi.newpipe.player.VideoPlayer.DEFAULT_CONTROLS_DURATION; | ||||
| import static org.schabi.newpipe.player.VideoPlayer.DEFAULT_CONTROLS_HIDE_TIME; | ||||
| import static org.schabi.newpipe.util.AnimationUtils.Type.SCALE_AND_ALPHA; | ||||
| import static org.schabi.newpipe.util.AnimationUtils.animateView; | ||||
|  | ||||
| public class PlayerGestureListener extends GestureDetector.SimpleOnGestureListener implements View.OnTouchListener { | ||||
|     private static final String TAG = ".PlayerGestureListener"; | ||||
|     private static final boolean DEBUG = BasePlayer.DEBUG; | ||||
|  | ||||
|     private VideoPlayerImpl playerImpl; | ||||
|     private MainPlayer service; | ||||
|  | ||||
|     private int initialPopupX, initialPopupY; | ||||
|  | ||||
|     private boolean isMovingInMain, isMovingInPopup; | ||||
|  | ||||
|     private boolean isResizing; | ||||
|  | ||||
|     private int tossFlingVelocity; | ||||
|  | ||||
|     private final boolean isVolumeGestureEnabled; | ||||
|     private final boolean isBrightnessGestureEnabled; | ||||
|     private final int maxVolume; | ||||
|     private static final int MOVEMENT_THRESHOLD = 40; | ||||
|  | ||||
|  | ||||
|     public PlayerGestureListener(final VideoPlayerImpl playerImpl, final MainPlayer service) { | ||||
|         this.playerImpl = playerImpl; | ||||
|         this.service = service; | ||||
|         this.tossFlingVelocity = PlayerHelper.getTossFlingVelocity(service); | ||||
|  | ||||
|         isVolumeGestureEnabled = PlayerHelper.isVolumeGestureEnabled(service); | ||||
|         isBrightnessGestureEnabled = PlayerHelper.isBrightnessGestureEnabled(service); | ||||
|         maxVolume = playerImpl.getAudioReactor().getMaxVolume(); | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Helpers | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     /* | ||||
|     * Main and popup players' gesture listeners is too different. | ||||
|     * So it will be better to have different implementations of them | ||||
|     * */ | ||||
|     @Override | ||||
|     public boolean onDoubleTap(MotionEvent e) { | ||||
|         if (DEBUG) Log.d(TAG, "onDoubleTap() called with: e = [" + e + "]" + "rawXy = " + e.getRawX() + ", " + e.getRawY() + ", xy = " + e.getX() + ", " + e.getY()); | ||||
|  | ||||
|         if (playerImpl.popupPlayerSelected()) return onDoubleTapInPopup(e); | ||||
|         else return onDoubleTapInMain(e); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean onSingleTapConfirmed(MotionEvent e) { | ||||
|         if (DEBUG) Log.d(TAG, "onSingleTapConfirmed() called with: e = [" + e + "]"); | ||||
|  | ||||
|         if (playerImpl.popupPlayerSelected()) return onSingleTapConfirmedInPopup(e); | ||||
|         else return onSingleTapConfirmedInMain(e); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean onDown(MotionEvent e) { | ||||
|         if (DEBUG) Log.d(TAG, "onDown() called with: e = [" + e + "]"); | ||||
|  | ||||
|         if (playerImpl.popupPlayerSelected()) return onDownInPopup(e); | ||||
|         else return true; | ||||
|     } | ||||
|     @Override | ||||
|     public void onLongPress(MotionEvent e) { | ||||
|         if (DEBUG) Log.d(TAG, "onLongPress() called with: e = [" + e + "]"); | ||||
|  | ||||
|         if (playerImpl.popupPlayerSelected()) onLongPressInPopup(e); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean onScroll(MotionEvent initialEvent, MotionEvent movingEvent, float distanceX, float distanceY) { | ||||
|         if (playerImpl.popupPlayerSelected()) return onScrollInPopup(initialEvent, movingEvent, distanceX, distanceY); | ||||
|         else return onScrollInMain(initialEvent, movingEvent, distanceX, distanceY); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { | ||||
|         if (DEBUG) Log.d(TAG, "onFling() called with velocity: dX=[" + velocityX + "], dY=[" + velocityY + "]"); | ||||
|  | ||||
|         if (playerImpl.popupPlayerSelected()) return onFlingInPopup(e1, e2, velocityX, velocityY); | ||||
|         else return true; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean onTouch(View v, MotionEvent event) { | ||||
|         //noinspection PointlessBooleanExpression,ConstantConditions | ||||
|         if (DEBUG && false) Log.d(TAG, "onTouch() called with: v = [" + v + "], event = [" + event + "]"); | ||||
|  | ||||
|         if (playerImpl.popupPlayerSelected()) return onTouchInPopup(v, event); | ||||
|         else return onTouchInMain(v, event); | ||||
|     } | ||||
|  | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Main player listener | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private boolean onDoubleTapInMain(MotionEvent e) { | ||||
|         if (e.getX() > playerImpl.getRootView().getWidth() * 2 / 3) { | ||||
|             playerImpl.onFastForward(); | ||||
|         } else if (e.getX() < playerImpl.getRootView().getWidth() / 3) { | ||||
|             playerImpl.onFastRewind(); | ||||
|         } else { | ||||
|             playerImpl.getPlayPauseButton().performClick(); | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|  | ||||
|     private boolean onSingleTapConfirmedInMain(MotionEvent e) { | ||||
|         if (DEBUG) Log.d(TAG, "onSingleTapConfirmed() called with: e = [" + e + "]"); | ||||
|  | ||||
|         if (playerImpl.getCurrentState() == BasePlayer.STATE_BLOCKED) return true; | ||||
|  | ||||
|         if (playerImpl.isControlsVisible()) { | ||||
|             playerImpl.hideControls(150, 0); | ||||
|         } else { | ||||
|             if (playerImpl.getCurrentState() == BasePlayer.STATE_COMPLETED) { | ||||
|                 playerImpl.showControls(0); | ||||
|             } else { | ||||
|                 playerImpl.showControlsThenHide(); | ||||
|             } | ||||
|             if (playerImpl.isInFullscreen()) { | ||||
|                 int visibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE | ||||
|                         | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | ||||
|                         | View.SYSTEM_UI_FLAG_FULLSCREEN; | ||||
|                 playerImpl.getParentActivity().getWindow().getDecorView().setSystemUiVisibility(visibility); | ||||
|                 playerImpl.getParentActivity().getWindow().clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS); | ||||
|                 playerImpl.getParentActivity().getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); | ||||
|             } | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     private boolean onScrollInMain(MotionEvent initialEvent, MotionEvent movingEvent, float distanceX, float distanceY) { | ||||
|         if (!isVolumeGestureEnabled && !isBrightnessGestureEnabled) return false; | ||||
|  | ||||
|         //noinspection PointlessBooleanExpression | ||||
|         if (DEBUG && false) Log.d(TAG, "MainVideoPlayer.onScroll = " + | ||||
|                 ", e1.getRaw = [" + initialEvent.getRawX() + ", " + initialEvent.getRawY() + "]" + | ||||
|                 ", e2.getRaw = [" + movingEvent.getRawX() + ", " + movingEvent.getRawY() + "]" + | ||||
|                 ", distanceXy = [" + distanceX + ", " + distanceY + "]"); | ||||
|  | ||||
|         final boolean insideThreshold = Math.abs(movingEvent.getY() - initialEvent.getY()) <= MOVEMENT_THRESHOLD; | ||||
|         if (!isMovingInMain && (insideThreshold || Math.abs(distanceX) > Math.abs(distanceY)) | ||||
|                 || playerImpl.getCurrentState() == BasePlayer.STATE_COMPLETED) { | ||||
|             return false; | ||||
|         } | ||||
|  | ||||
|         isMovingInMain = true; | ||||
|  | ||||
|         boolean acceptAnyArea = isVolumeGestureEnabled != isBrightnessGestureEnabled; | ||||
|         boolean acceptVolumeArea = acceptAnyArea || initialEvent.getX() > playerImpl.getRootView().getWidth() / 2; | ||||
|         boolean acceptBrightnessArea = acceptAnyArea || !acceptVolumeArea; | ||||
|  | ||||
|         if (isVolumeGestureEnabled && acceptVolumeArea) { | ||||
|             playerImpl.getVolumeProgressBar().incrementProgressBy((int) distanceY); | ||||
|             float currentProgressPercent = | ||||
|                     (float) playerImpl.getVolumeProgressBar().getProgress() / playerImpl.getMaxGestureLength(); | ||||
|             int currentVolume = (int) (maxVolume * currentProgressPercent); | ||||
|             playerImpl.getAudioReactor().setVolume(currentVolume); | ||||
|  | ||||
|             if (DEBUG) Log.d(TAG, "onScroll().volumeControl, currentVolume = " + currentVolume); | ||||
|  | ||||
|             final int resId = | ||||
|                     currentProgressPercent <= 0 ? R.drawable.ic_volume_off_white_72dp | ||||
|                             : currentProgressPercent < 0.25 ? R.drawable.ic_volume_mute_white_72dp | ||||
|                             : currentProgressPercent < 0.75 ? R.drawable.ic_volume_down_white_72dp | ||||
|                             : R.drawable.ic_volume_up_white_72dp; | ||||
|  | ||||
|             playerImpl.getVolumeImageView().setImageDrawable( | ||||
|                     AppCompatResources.getDrawable(service, resId) | ||||
|             ); | ||||
|  | ||||
|             if (playerImpl.getVolumeRelativeLayout().getVisibility() != View.VISIBLE) { | ||||
|                 animateView(playerImpl.getVolumeRelativeLayout(), SCALE_AND_ALPHA, true, 200); | ||||
|             } | ||||
|             if (playerImpl.getBrightnessRelativeLayout().getVisibility() == View.VISIBLE) { | ||||
|                 playerImpl.getBrightnessRelativeLayout().setVisibility(View.GONE); | ||||
|             } | ||||
|         } else if (isBrightnessGestureEnabled && acceptBrightnessArea) { | ||||
|             Activity parent = playerImpl.getParentActivity(); | ||||
|             if (parent == null) return true; | ||||
|  | ||||
|             Window window = parent.getWindow(); | ||||
|  | ||||
|             playerImpl.getBrightnessProgressBar().incrementProgressBy((int) distanceY); | ||||
|             float currentProgressPercent = | ||||
|                     (float) playerImpl.getBrightnessProgressBar().getProgress() / playerImpl.getMaxGestureLength(); | ||||
|             WindowManager.LayoutParams layoutParams = window.getAttributes(); | ||||
|             layoutParams.screenBrightness = currentProgressPercent; | ||||
|             window.setAttributes(layoutParams); | ||||
|  | ||||
|             if (DEBUG) Log.d(TAG, "onScroll().brightnessControl, currentBrightness = " + currentProgressPercent); | ||||
|  | ||||
|             final int resId = | ||||
|                     currentProgressPercent < 0.25 ? R.drawable.ic_brightness_low_white_72dp | ||||
|                             : currentProgressPercent < 0.75 ? R.drawable.ic_brightness_medium_white_72dp | ||||
|                             : R.drawable.ic_brightness_high_white_72dp; | ||||
|  | ||||
|             playerImpl.getBrightnessImageView().setImageDrawable( | ||||
|                     AppCompatResources.getDrawable(service, resId) | ||||
|             ); | ||||
|  | ||||
|             if (playerImpl.getBrightnessRelativeLayout().getVisibility() != View.VISIBLE) { | ||||
|                 animateView(playerImpl.getBrightnessRelativeLayout(), SCALE_AND_ALPHA, true, 200); | ||||
|             } | ||||
|             if (playerImpl.getVolumeRelativeLayout().getVisibility() == View.VISIBLE) { | ||||
|                 playerImpl.getVolumeRelativeLayout().setVisibility(View.GONE); | ||||
|             } | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     private void onScrollEndInMain() { | ||||
|         if (DEBUG) Log.d(TAG, "onScrollEnd() called"); | ||||
|  | ||||
|         if (playerImpl.getVolumeRelativeLayout().getVisibility() == View.VISIBLE) { | ||||
|             animateView(playerImpl.getVolumeRelativeLayout(), SCALE_AND_ALPHA, false, 200, 200); | ||||
|         } | ||||
|         if (playerImpl.getBrightnessRelativeLayout().getVisibility() == View.VISIBLE) { | ||||
|             animateView(playerImpl.getBrightnessRelativeLayout(), SCALE_AND_ALPHA, false, 200, 200); | ||||
|         } | ||||
|  | ||||
|         if (playerImpl.isControlsVisible() && playerImpl.getCurrentState() == STATE_PLAYING) { | ||||
|             playerImpl.hideControls(DEFAULT_CONTROLS_DURATION, DEFAULT_CONTROLS_HIDE_TIME); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private boolean onTouchInMain(View v, MotionEvent event) { | ||||
|         playerImpl.getGestureDetector().onTouchEvent(event); | ||||
|         if (event.getAction() == MotionEvent.ACTION_UP && isMovingInMain) { | ||||
|             isMovingInMain = false; | ||||
|             onScrollEndInMain(); | ||||
|         } | ||||
|         // This hack allows to stop receiving touch events on appbar while touching video player view | ||||
|         switch (event.getAction()) { | ||||
|             case MotionEvent.ACTION_DOWN: | ||||
|             case MotionEvent.ACTION_MOVE: | ||||
|                 v.getParent().requestDisallowInterceptTouchEvent(playerImpl.isInFullscreen()); | ||||
|                 return true; | ||||
|             case MotionEvent.ACTION_UP: | ||||
|                 v.getParent().requestDisallowInterceptTouchEvent(false); | ||||
|                 return false; | ||||
|             default: | ||||
|                 return true; | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /*////////////////////////////////////////////////////////////////////////// | ||||
|     // Popup player listener | ||||
|     //////////////////////////////////////////////////////////////////////////*/ | ||||
|  | ||||
|     private boolean onDoubleTapInPopup(MotionEvent e) { | ||||
|         if (playerImpl == null || !playerImpl.isPlaying()) return false; | ||||
|  | ||||
|         playerImpl.hideControls(0, 0); | ||||
|  | ||||
|         if (e.getX() > playerImpl.getPopupWidth() / 2) { | ||||
|             playerImpl.onFastForward(); | ||||
|         } else { | ||||
|             playerImpl.onFastRewind(); | ||||
|         } | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     private boolean onSingleTapConfirmedInPopup(MotionEvent e) { | ||||
|         if (playerImpl == null || playerImpl.getPlayer() == null) return false; | ||||
|         if (playerImpl.isControlsVisible()) { | ||||
|             playerImpl.hideControls(100, 100); | ||||
|         } else { | ||||
|             playerImpl.showControlsThenHide(); | ||||
|  | ||||
|         } | ||||
|         playerImpl.onPlayPause(); | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     private boolean onDownInPopup(MotionEvent e) { | ||||
|         // Fix popup position when the user touch it, it may have the wrong one | ||||
|         // because the soft input is visible (the draggable area is currently resized). | ||||
|         playerImpl.checkPopupPositionBounds(playerImpl.getCloseOverlayView().getWidth(), playerImpl.getCloseOverlayView().getHeight()); | ||||
|  | ||||
|         initialPopupX = playerImpl.getPopupLayoutParams().x; | ||||
|         initialPopupY = playerImpl.getPopupLayoutParams().y; | ||||
|         playerImpl.setPopupWidth(playerImpl.getPopupLayoutParams().width); | ||||
|         playerImpl.setPopupHeight(playerImpl.getPopupLayoutParams().height); | ||||
|         return super.onDown(e); | ||||
|     } | ||||
|  | ||||
|     private void onLongPressInPopup(MotionEvent e) { | ||||
|         playerImpl.updateScreenSize(); | ||||
|         playerImpl.checkPopupPositionBounds(); | ||||
|         playerImpl.updatePopupSize((int) playerImpl.getScreenWidth(), -1); | ||||
|     } | ||||
|  | ||||
|     private boolean onScrollInPopup(MotionEvent initialEvent, MotionEvent movingEvent, float distanceX, float distanceY) { | ||||
|         if (isResizing || playerImpl == null) return super.onScroll(initialEvent, movingEvent, distanceX, distanceY); | ||||
|  | ||||
|         if (!isMovingInPopup) { | ||||
|             animateView(playerImpl.getCloseOverlayButton(), true, 200); | ||||
|         } | ||||
|  | ||||
|         isMovingInPopup = true; | ||||
|  | ||||
|         float diffX = (int) (movingEvent.getRawX() - initialEvent.getRawX()), posX = (int) (initialPopupX + diffX); | ||||
|         float diffY = (int) (movingEvent.getRawY() - initialEvent.getRawY()), posY = (int) (initialPopupY + diffY); | ||||
|  | ||||
|         if (posX > (playerImpl.getScreenWidth() - playerImpl.getPopupWidth())) posX = (int) (playerImpl.getScreenWidth() - playerImpl.getPopupWidth()); | ||||
|         else if (posX < 0) posX = 0; | ||||
|  | ||||
|         if (posY > (playerImpl.getScreenHeight() - playerImpl.getPopupHeight())) posY = (int) (playerImpl.getScreenHeight() - playerImpl.getPopupHeight()); | ||||
|         else if (posY < 0) posY = 0; | ||||
|  | ||||
|         playerImpl.getPopupLayoutParams().x = (int) posX; | ||||
|         playerImpl.getPopupLayoutParams().y = (int) posY; | ||||
|  | ||||
|         final View closingOverlayView = playerImpl.getClosingOverlayView(); | ||||
|         if (playerImpl.isInsideClosingRadius(movingEvent)) { | ||||
|             if (closingOverlayView.getVisibility() == View.GONE) { | ||||
|                 animateView(closingOverlayView, true, 250); | ||||
|             } | ||||
|         } else { | ||||
|             if (closingOverlayView.getVisibility() == View.VISIBLE) { | ||||
|                 animateView(closingOverlayView, false, 0); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         //noinspection PointlessBooleanExpression | ||||
|         if (DEBUG && false) { | ||||
|             Log.d(TAG, "PopupVideoPlayer.onScroll = " + | ||||
|                     ", e1.getRaw = [" + initialEvent.getRawX() + ", " + initialEvent.getRawY() + "]" + ", e1.getX,Y = [" + initialEvent.getX() + ", " + initialEvent.getY() + "]" + | ||||
|                     ", e2.getRaw = [" + movingEvent.getRawX() + ", " + movingEvent.getRawY() + "]" + ", e2.getX,Y = [" + movingEvent.getX() + ", " + movingEvent.getY() + "]" + | ||||
|                     ", distanceX,Y = [" + distanceX + ", " + distanceY + "]" + | ||||
|                     ", posX,Y = [" + posX + ", " + posY + "]" + | ||||
|                     ", popupW,H = [" + playerImpl.getPopupWidth() + " x " + playerImpl.getPopupHeight() + "]"); | ||||
|         } | ||||
|         playerImpl.windowManager.updateViewLayout(playerImpl.getRootView(), playerImpl.getPopupLayoutParams()); | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     private void onScrollEndInPopup(MotionEvent event) { | ||||
|         if (playerImpl == null) return; | ||||
|         if (playerImpl.isControlsVisible() && playerImpl.getCurrentState() == STATE_PLAYING) { | ||||
|             playerImpl.hideControls(DEFAULT_CONTROLS_DURATION, DEFAULT_CONTROLS_HIDE_TIME); | ||||
|         } | ||||
|  | ||||
|         if (playerImpl.isInsideClosingRadius(event)) { | ||||
|             playerImpl.closePopup(); | ||||
|         } else { | ||||
|             animateView(playerImpl.getClosingOverlayView(), false, 0); | ||||
|  | ||||
|             if (!playerImpl.isPopupClosing) { | ||||
|                 animateView(playerImpl.getCloseOverlayButton(), false, 200); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     private boolean onFlingInPopup(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { | ||||
|         if (playerImpl == null) return false; | ||||
|  | ||||
|         final float absVelocityX = Math.abs(velocityX); | ||||
|         final float absVelocityY = Math.abs(velocityY); | ||||
|         if (Math.max(absVelocityX, absVelocityY) > tossFlingVelocity) { | ||||
|             if (absVelocityX > tossFlingVelocity) playerImpl.getPopupLayoutParams().x = (int) velocityX; | ||||
|             if (absVelocityY > tossFlingVelocity) playerImpl.getPopupLayoutParams().y = (int) velocityY; | ||||
|             playerImpl.checkPopupPositionBounds(); | ||||
|             playerImpl.windowManager.updateViewLayout(playerImpl.getRootView(), playerImpl.getPopupLayoutParams()); | ||||
|             return true; | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|  | ||||
|     private boolean onTouchInPopup(View v, MotionEvent event) { | ||||
|         playerImpl.getGestureDetector().onTouchEvent(event); | ||||
|         if (playerImpl == null) return false; | ||||
|         if (event.getPointerCount() == 2 && !isMovingInPopup && !isResizing) { | ||||
|             if (DEBUG) Log.d(TAG, "onTouch() 2 finger pointer detected, enabling resizing."); | ||||
|             playerImpl.showAndAnimateControl(-1, true); | ||||
|             playerImpl.getLoadingPanel().setVisibility(View.GONE); | ||||
|  | ||||
|             playerImpl.hideControls(0, 0); | ||||
|             animateView(playerImpl.getCurrentDisplaySeek(), false, 0, 0); | ||||
|             animateView(playerImpl.getResizingIndicator(), true, 200, 0); | ||||
|             isResizing = true; | ||||
|         } | ||||
|  | ||||
|         if (event.getAction() == MotionEvent.ACTION_MOVE && !isMovingInPopup && isResizing) { | ||||
|             if (DEBUG) Log.d(TAG, "onTouch() ACTION_MOVE > v = [" + v + "],  e1.getRaw = [" + event.getRawX() + ", " + event.getRawY() + "]"); | ||||
|             return handleMultiDrag(event); | ||||
|         } | ||||
|  | ||||
|         if (event.getAction() == MotionEvent.ACTION_UP) { | ||||
|             if (DEBUG) | ||||
|                 Log.d(TAG, "onTouch() ACTION_UP > v = [" + v + "],  e1.getRaw = [" + event.getRawX() + ", " + event.getRawY() + "]"); | ||||
|             if (isMovingInPopup) { | ||||
|                 isMovingInPopup = false; | ||||
|                 onScrollEndInPopup(event); | ||||
|             } | ||||
|  | ||||
|             if (isResizing) { | ||||
|                 isResizing = false; | ||||
|                 animateView(playerImpl.getResizingIndicator(), false, 100, 0); | ||||
|                 playerImpl.changeState(playerImpl.getCurrentState()); | ||||
|             } | ||||
|  | ||||
|             if (!playerImpl.isPopupClosing) { | ||||
|                 playerImpl.savePositionAndSize(); | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         v.performClick(); | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
|     private boolean handleMultiDrag(final MotionEvent event) { | ||||
|         if (event.getPointerCount() != 2) return false; | ||||
|  | ||||
|         final float firstPointerX = event.getX(0); | ||||
|         final float secondPointerX = event.getX(1); | ||||
|  | ||||
|         final float diff = Math.abs(firstPointerX - secondPointerX); | ||||
|         if (firstPointerX > secondPointerX) { | ||||
|             // second pointer is the anchor (the leftmost pointer) | ||||
|             playerImpl.getPopupLayoutParams().x = (int) (event.getRawX() - diff); | ||||
|         } else { | ||||
|             // first pointer is the anchor | ||||
|             playerImpl.getPopupLayoutParams().x = (int) event.getRawX(); | ||||
|         } | ||||
|  | ||||
|         playerImpl.checkPopupPositionBounds(); | ||||
|         playerImpl.updateScreenSize(); | ||||
|  | ||||
|         final int width = (int) Math.min(playerImpl.getScreenWidth(), diff); | ||||
|         playerImpl.updatePopupSize(width, -1); | ||||
|  | ||||
|         return true; | ||||
|     } | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -0,0 +1,15 @@ | ||||
| package org.schabi.newpipe.player.event; | ||||
|  | ||||
| import com.google.android.exoplayer2.ExoPlaybackException; | ||||
|  | ||||
| public interface PlayerServiceEventListener extends PlayerEventListener { | ||||
|     void onFullscreenStateChanged(boolean fullscreen); | ||||
|  | ||||
|     void onMoreOptionsLongClicked(); | ||||
|  | ||||
|     void onPlayerError(ExoPlaybackException error); | ||||
|  | ||||
|     boolean isFragmentStopped(); | ||||
|  | ||||
|     void hideSystemUIIfNeeded(); | ||||
| } | ||||
| @@ -80,8 +80,10 @@ public class PlaybackParameterDialog extends DialogFragment { | ||||
|  | ||||
|     public static PlaybackParameterDialog newInstance(final double playbackTempo, | ||||
|                                                       final double playbackPitch, | ||||
|                                                       final boolean playbackSkipSilence) { | ||||
|                                                       final boolean playbackSkipSilence, | ||||
|                                                       Callback callback) { | ||||
|         PlaybackParameterDialog dialog = new PlaybackParameterDialog(); | ||||
|         dialog.callback = callback; | ||||
|         dialog.initialTempo = playbackTempo; | ||||
|         dialog.initialPitch = playbackPitch; | ||||
|  | ||||
| @@ -99,9 +101,9 @@ public class PlaybackParameterDialog extends DialogFragment { | ||||
|     @Override | ||||
|     public void onAttach(Context context) { | ||||
|         super.onAttach(context); | ||||
|         if (context != null && context instanceof Callback) { | ||||
|         if (context instanceof Callback) { | ||||
|             callback = (Callback) context; | ||||
|         } else { | ||||
|         } else if (callback == null) { | ||||
|             dismiss(); | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -0,0 +1,29 @@ | ||||
| package org.schabi.newpipe.settings; | ||||
|  | ||||
| import android.database.ContentObserver; | ||||
| import android.os.Handler; | ||||
|  | ||||
| public class SettingsContentObserver extends ContentObserver { | ||||
|     private OnChangeListener listener; | ||||
|  | ||||
|     public interface OnChangeListener { | ||||
|         void onSettingsChanged(); | ||||
|     } | ||||
|  | ||||
|     public SettingsContentObserver(Handler handler, OnChangeListener listener) { | ||||
|         super(handler); | ||||
|         this.listener = listener; | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public boolean deliverSelfNotifications() { | ||||
|         return super.deliverSelfNotifications(); | ||||
|     } | ||||
|  | ||||
|     @Override | ||||
|     public void onChange(boolean selfChange) { | ||||
|         super.onChange(selfChange); | ||||
|         if (listener != null) | ||||
|             listener.onSettingsChanged(); | ||||
|     } | ||||
| } | ||||
| @@ -10,6 +10,7 @@ import android.os.Build; | ||||
| import android.preference.PreferenceManager; | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.Nullable; | ||||
| import androidx.appcompat.app.AppCompatActivity; | ||||
| import androidx.fragment.app.Fragment; | ||||
| import androidx.fragment.app.FragmentManager; | ||||
| import androidx.fragment.app.FragmentTransaction; | ||||
| @@ -44,14 +45,9 @@ import org.schabi.newpipe.local.history.StatisticsPlaylistFragment; | ||||
| import org.schabi.newpipe.local.playlist.LocalPlaylistFragment; | ||||
| import org.schabi.newpipe.local.subscription.SubscriptionFragment; | ||||
| import org.schabi.newpipe.local.subscription.SubscriptionsImportFragment; | ||||
| import org.schabi.newpipe.player.BackgroundPlayer; | ||||
| import org.schabi.newpipe.player.BackgroundPlayerActivity; | ||||
| import org.schabi.newpipe.player.BasePlayer; | ||||
| import org.schabi.newpipe.player.MainVideoPlayer; | ||||
| import org.schabi.newpipe.player.PopupVideoPlayer; | ||||
| import org.schabi.newpipe.player.PopupVideoPlayerActivity; | ||||
| import org.schabi.newpipe.player.VideoPlayer; | ||||
| import org.schabi.newpipe.player.*; | ||||
| import org.schabi.newpipe.player.playqueue.PlayQueue; | ||||
| import org.schabi.newpipe.player.playqueue.PlayQueueItem; | ||||
| import org.schabi.newpipe.settings.SettingsActivity; | ||||
|  | ||||
| import java.util.ArrayList; | ||||
| @@ -78,6 +74,9 @@ public class NavigationHelper { | ||||
|         if (quality != null) intent.putExtra(VideoPlayer.PLAYBACK_QUALITY, quality); | ||||
|         intent.putExtra(VideoPlayer.RESUME_PLAYBACK, resumePlayback); | ||||
|  | ||||
|         int playerType = intent.getIntExtra(VideoPlayer.PLAYER_TYPE, VideoPlayer.PLAYER_TYPE_VIDEO); | ||||
|         intent.putExtra(VideoPlayer.PLAYER_TYPE, playerType); | ||||
|  | ||||
|         return intent; | ||||
|     } | ||||
|  | ||||
| @@ -117,10 +116,13 @@ public class NavigationHelper { | ||||
|                 .putExtra(BasePlayer.PLAYBACK_SKIP_SILENCE, playbackSkipSilence); | ||||
|     } | ||||
|  | ||||
|     public static void playOnMainPlayer(final Context context, final PlayQueue queue, final boolean resumePlayback) { | ||||
|         final Intent playerIntent = getPlayerIntent(context, MainVideoPlayer.class, queue, resumePlayback); | ||||
|         playerIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); | ||||
|         context.startActivity(playerIntent); | ||||
|     public static void playOnMainPlayer(final AppCompatActivity activity, final PlayQueue queue, final boolean resumePlayback) { | ||||
|         playOnMainPlayer(activity.getSupportFragmentManager(), queue, resumePlayback); | ||||
|     } | ||||
|  | ||||
|     public static void playOnMainPlayer(final FragmentManager fragmentManager, final PlayQueue queue, boolean autoPlay) { | ||||
|         PlayQueueItem currentStream = queue.getItem(); | ||||
|         NavigationHelper.openVideoDetailFragment(fragmentManager, currentStream.getServiceId(), currentStream.getUrl(), currentStream.getTitle(), autoPlay, queue); | ||||
|     } | ||||
|  | ||||
|     public static void playOnPopupPlayer(final Context context, final PlayQueue queue, final boolean resumePlayback) { | ||||
| @@ -130,12 +132,16 @@ public class NavigationHelper { | ||||
|         } | ||||
|  | ||||
|         Toast.makeText(context, R.string.popup_playing_toast, Toast.LENGTH_SHORT).show(); | ||||
|         startService(context, getPlayerIntent(context, PopupVideoPlayer.class, queue, resumePlayback)); | ||||
|         Intent intent = getPlayerIntent(context, MainPlayer.class, queue, resumePlayback); | ||||
|         intent.putExtra(VideoPlayer.PLAYER_TYPE, VideoPlayer.PLAYER_TYPE_POPUP); | ||||
|         startService(context, intent); | ||||
|     } | ||||
|  | ||||
|     public static void playOnBackgroundPlayer(final Context context, final PlayQueue queue, final boolean resumePlayback) { | ||||
|         Toast.makeText(context, R.string.background_player_playing_toast, Toast.LENGTH_SHORT).show(); | ||||
|         startService(context, getPlayerIntent(context, BackgroundPlayer.class, queue, resumePlayback)); | ||||
|         Intent intent = getPlayerIntent(context, MainPlayer.class, queue, resumePlayback); | ||||
|         intent.putExtra(VideoPlayer.PLAYER_TYPE, VideoPlayer.PLAYER_TYPE_AUDIO); | ||||
|         startService(context, intent); | ||||
|     } | ||||
|  | ||||
|     public static void enqueueOnPopupPlayer(final Context context, final PlayQueue queue, final boolean resumePlayback) { | ||||
| @@ -149,8 +155,9 @@ public class NavigationHelper { | ||||
|         } | ||||
|  | ||||
|         Toast.makeText(context, R.string.popup_playing_append, Toast.LENGTH_SHORT).show(); | ||||
|         startService(context, | ||||
|                 getPlayerEnqueueIntent(context, PopupVideoPlayer.class, queue, selectOnAppend, resumePlayback)); | ||||
|         Intent intent = getPlayerEnqueueIntent(context, MainPlayer.class, queue, selectOnAppend, resumePlayback); | ||||
|         intent.putExtra(VideoPlayer.PLAYER_TYPE, VideoPlayer.PLAYER_TYPE_POPUP); | ||||
|         startService(context, intent); | ||||
|     } | ||||
|  | ||||
|     public static void enqueueOnBackgroundPlayer(final Context context, final PlayQueue queue, final boolean resumePlayback) { | ||||
| @@ -159,8 +166,9 @@ public class NavigationHelper { | ||||
|  | ||||
|     public static void enqueueOnBackgroundPlayer(final Context context, final PlayQueue queue, boolean selectOnAppend, final boolean resumePlayback) { | ||||
|         Toast.makeText(context, R.string.background_player_append, Toast.LENGTH_SHORT).show(); | ||||
|         startService(context, | ||||
|                 getPlayerEnqueueIntent(context, BackgroundPlayer.class, queue, selectOnAppend, resumePlayback)); | ||||
|         Intent intent = getPlayerEnqueueIntent(context, MainPlayer.class, queue, selectOnAppend, resumePlayback); | ||||
|         intent.putExtra(VideoPlayer.PLAYER_TYPE, VideoPlayer.PLAYER_TYPE_AUDIO); | ||||
|         startService(context, intent); | ||||
|     } | ||||
|  | ||||
|     public static void startService(@NonNull final Context context, @NonNull final Intent intent) { | ||||
| @@ -281,29 +289,35 @@ public class NavigationHelper { | ||||
|     } | ||||
|  | ||||
|     public static void openVideoDetailFragment(FragmentManager fragmentManager, int serviceId, String url, String title) { | ||||
|         openVideoDetailFragment(fragmentManager, serviceId, url, title, false); | ||||
|         openVideoDetailFragment(fragmentManager, serviceId, url, title, true, null); | ||||
|     } | ||||
|  | ||||
|     public static void openVideoDetailFragment(FragmentManager fragmentManager, int serviceId, String url, String title, boolean autoPlay) { | ||||
|         Fragment fragment = fragmentManager.findFragmentById(R.id.fragment_holder); | ||||
|     public static void openVideoDetailFragment(FragmentManager fragmentManager, int serviceId, String url, String title, boolean autoPlay, PlayQueue playQueue) { | ||||
|         Fragment fragment = fragmentManager.findFragmentById(R.id.fragment_player_holder); | ||||
|         if (title == null) title = ""; | ||||
|  | ||||
|         if (fragment instanceof VideoDetailFragment && fragment.isVisible()) { | ||||
|             expandMainPlayer(fragment.getActivity()); | ||||
|             VideoDetailFragment detailFragment = (VideoDetailFragment) fragment; | ||||
|             detailFragment.setAutoplay(autoPlay); | ||||
|             detailFragment.selectAndLoadVideo(serviceId, url, title); | ||||
|             detailFragment.selectAndLoadVideo(serviceId, url, title, playQueue); | ||||
|             return; | ||||
|         } | ||||
|  | ||||
|         VideoDetailFragment instance = VideoDetailFragment.getInstance(serviceId, url, title); | ||||
|         VideoDetailFragment instance = VideoDetailFragment.getInstance(serviceId, url, title, playQueue); | ||||
|         instance.setAutoplay(autoPlay); | ||||
|  | ||||
|         defaultTransaction(fragmentManager) | ||||
|                 .replace(R.id.fragment_holder, instance) | ||||
|                 .addToBackStack(null) | ||||
|                 .replace(R.id.fragment_player_holder, instance) | ||||
|                 .runOnCommit(() -> expandMainPlayer(instance.getActivity())) | ||||
|                 .commit(); | ||||
|     } | ||||
|  | ||||
|     public static void expandMainPlayer(Context context) { | ||||
|         final Intent intent = new Intent(VideoDetailFragment.ACTION_SHOW_MAIN_PLAYER); | ||||
|         context.sendBroadcast(intent); | ||||
|     } | ||||
|  | ||||
|     public static void openChannelFragment( | ||||
|             FragmentManager fragmentManager, | ||||
|             int serviceId, | ||||
| @@ -458,10 +472,6 @@ public class NavigationHelper { | ||||
|         return getServicePlayerActivityIntent(context, BackgroundPlayerActivity.class); | ||||
|     } | ||||
|  | ||||
|     public static Intent getPopupPlayerActivityIntent(final Context context) { | ||||
|         return getServicePlayerActivityIntent(context, PopupVideoPlayerActivity.class); | ||||
|     } | ||||
|  | ||||
|     private static Intent getServicePlayerActivityIntent(final Context context, | ||||
|                                                          final Class activityClass) { | ||||
|         Intent intent = new Intent(context, activityClass); | ||||
|   | ||||
| @@ -17,6 +17,6 @@ public class ShareUtils { | ||||
|         intent.setType("text/plain"); | ||||
|         intent.putExtra(Intent.EXTRA_SUBJECT, subject); | ||||
|         intent.putExtra(Intent.EXTRA_TEXT, url); | ||||
|         context.startActivity(Intent.createChooser(intent, context.getString(R.string.share_dialog_title))); | ||||
|         context.startActivity(Intent.createChooser(intent, context.getString(R.string.share_dialog_title)).setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -2,9 +2,10 @@ | ||||
| <RelativeLayout | ||||
|     xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:tools="http://schemas.android.com/tools" | ||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="match_parent" | ||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" | ||||
|     android:background="@color/black" | ||||
|     android:gravity="center"> | ||||
|  | ||||
|     <com.google.android.exoplayer2.ui.AspectRatioFrameLayout | ||||
| @@ -49,9 +50,9 @@ | ||||
|     <RelativeLayout | ||||
|         android:id="@+id/playQueuePanel" | ||||
|         android:layout_width="380dp" | ||||
|         android:layout_height="match_parent" | ||||
|         android:layout_alignParentEnd="true" | ||||
|         android:layout_alignParentRight="true" | ||||
|         android:layout_height="match_parent" | ||||
|         android:visibility="invisible" | ||||
|         android:background="?attr/queue_background_color" | ||||
|         tools:visibility="visible"> | ||||
| @@ -120,7 +121,7 @@ | ||||
|             android:layout_height="match_parent" | ||||
|             android:layout_below="@id/playQueueControl" | ||||
|             android:scrollbars="vertical" | ||||
|             app:layoutManager="LinearLayoutManager" | ||||
|             app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" | ||||
|             tools:listitem="@layout/play_queue_item"/> | ||||
|  | ||||
|     </RelativeLayout> | ||||
| @@ -133,37 +134,46 @@ | ||||
|         android:visibility="gone" | ||||
|         tools:visibility="visible"> | ||||
|  | ||||
|         <!-- All top controls in this layout --> | ||||
|         <RelativeLayout | ||||
|             android:id="@+id/playbackWindowRoot" | ||||
|             android:layout_width="match_parent" | ||||
|             android:layout_height="match_parent" | ||||
|             android:fitsSystemWindows="true"> | ||||
|  | ||||
|             <RelativeLayout | ||||
|             <LinearLayout | ||||
|                 android:id="@+id/topControls" | ||||
|                 android:layout_width="match_parent" | ||||
|                 android:layout_height="wrap_content" | ||||
|                 android:layout_alignParentTop="true" | ||||
|                 android:background="@drawable/player_top_controls_bg" | ||||
|                 android:orientation="vertical" | ||||
|                 android:gravity="top" | ||||
|                 android:paddingTop="10dp" | ||||
|                 android:paddingBottom="10dp" | ||||
|                 android:paddingLeft="5dp" | ||||
|                 android:paddingRight="5dp" | ||||
|                 android:baselineAligned="false" | ||||
|                 android:layout_toStartOf="@id/fullScreenButton"> | ||||
|  | ||||
|             <LinearLayout | ||||
|                 android:id="@+id/primaryControls" | ||||
|                 android:layout_width="match_parent" | ||||
|                 android:layout_height="wrap_content" | ||||
|                 android:baselineAligned="false" | ||||
|                 android:gravity="top" | ||||
|                 android:paddingTop="4dp" | ||||
|                 android:paddingBottom="7dp" | ||||
|                 android:paddingLeft="2dp" | ||||
|                 android:paddingRight="6dp" | ||||
|                 tools:ignore="RtlHardcoded"> | ||||
|  | ||||
|                 <LinearLayout | ||||
|                     android:id="@+id/metadataView" | ||||
|                     android:layout_width="wrap_content" | ||||
|                     android:layout_width="0dp" | ||||
|                     android:layout_height="wrap_content" | ||||
|                     android:layout_alignParentLeft="true" | ||||
|                     android:layout_alignParentTop="true" | ||||
|                     android:layout_toLeftOf="@+id/qualityTextView" | ||||
|                     android:gravity="top" | ||||
|                     android:orientation="vertical" | ||||
|                     android:paddingLeft="8dp" | ||||
|                     android:paddingRight="8dp" | ||||
|                     tools:ignore="RtlHardcoded"> | ||||
|                     tools:ignore="RtlHardcoded" | ||||
|                     android:layout_weight="1"> | ||||
|  | ||||
|                     <TextView | ||||
|                         android:id="@+id/titleTextView" | ||||
| @@ -204,13 +214,13 @@ | ||||
|                     android:layout_height="35dp" | ||||
|                     android:layout_marginLeft="2dp" | ||||
|                     android:layout_marginRight="2dp" | ||||
|                     android:layout_toLeftOf="@+id/playbackSpeed" | ||||
|                     android:gravity="center" | ||||
|                     android:minWidth="50dp" | ||||
|                     android:text="720p" | ||||
|                     android:textColor="@android:color/white" | ||||
|                     android:textStyle="bold" | ||||
|                     android:background="?attr/selectableItemBackground" | ||||
|                     android:visibility="visible" | ||||
|                     tools:ignore="HardcodedText,RtlHardcoded"/> | ||||
|  | ||||
|                 <TextView | ||||
| @@ -218,7 +228,6 @@ | ||||
|                     android:layout_width="wrap_content" | ||||
|                     android:layout_height="wrap_content" | ||||
|                     android:layout_marginRight="2dp" | ||||
|                     android:layout_toLeftOf="@+id/queueButton" | ||||
|                     android:gravity="center" | ||||
|                     android:minHeight="35dp" | ||||
|                     android:minWidth="40dp" | ||||
| @@ -226,7 +235,7 @@ | ||||
|                     android:textStyle="bold" | ||||
|                     android:background="?attr/selectableItemBackground" | ||||
|                     tools:ignore="RtlHardcoded,RtlSymmetry" | ||||
|                     tools:text="1x" /> | ||||
|                     tools:text="1x"/> | ||||
|  | ||||
|                 <ImageButton | ||||
|                     android:id="@+id/queueButton" | ||||
| @@ -234,20 +243,19 @@ | ||||
|                     android:layout_height="35dp" | ||||
|                     android:layout_marginLeft="2dp" | ||||
|                     android:layout_marginRight="2dp" | ||||
|                     android:layout_toLeftOf="@+id/moreOptionsButton" | ||||
|                     android:clickable="true" | ||||
|                     android:focusable="true" | ||||
|                     android:padding="5dp" | ||||
|                     android:scaleType="fitXY" | ||||
|                     android:src="@drawable/ic_list_white_24dp" | ||||
|                     android:background="?attr/selectableItemBackground" | ||||
|                     tools:ignore="ContentDescription,RtlHardcoded"/> | ||||
|                     tools:ignore="ContentDescription,RtlHardcoded" | ||||
|                     android:visibility="gone"/> | ||||
|  | ||||
|                 <ImageButton | ||||
|                     android:id="@+id/moreOptionsButton" | ||||
|                     android:layout_width="wrap_content" | ||||
|                     android:layout_height="wrap_content" | ||||
|                     android:layout_alignParentRight="true" | ||||
|                     android:layout_marginLeft="2dp" | ||||
|                     android:padding="5dp" | ||||
|                     android:clickable="true" | ||||
| @@ -256,16 +264,18 @@ | ||||
|                     android:src="@drawable/ic_expand_more_white_24dp" | ||||
|                     android:background="?attr/selectableItemBackgroundBorderless" | ||||
|                     tools:ignore="ContentDescription,RtlHardcoded"/> | ||||
|             </RelativeLayout> | ||||
|  | ||||
|             <RelativeLayout | ||||
|                 </LinearLayout> | ||||
|  | ||||
|             <LinearLayout | ||||
|                 android:id="@+id/secondaryControls" | ||||
|                 android:layout_width="match_parent" | ||||
|                 android:layout_height="wrap_content" | ||||
|                 android:layout_below="@id/topControls" | ||||
|                 android:gravity="top" | ||||
|                 android:paddingLeft="5dp" | ||||
|                 android:paddingRight="5dp" | ||||
|                 android:paddingTop="4dp" | ||||
|                 android:paddingBottom="7dp" | ||||
|                 android:paddingStart="6dp" | ||||
|                 android:paddingEnd="6dp" | ||||
|                 android:visibility="invisible" | ||||
|                 tools:ignore="RtlHardcoded" | ||||
|                 tools:visibility="visible"> | ||||
| @@ -274,9 +284,7 @@ | ||||
|                     android:id="@+id/resizeTextView" | ||||
|                     android:layout_width="wrap_content" | ||||
|                     android:layout_height="35dp" | ||||
|                     android:layout_marginLeft="8dp" | ||||
|                     android:layout_marginRight="8dp" | ||||
|                     android:layout_alignParentLeft="true" | ||||
|                     android:gravity="center" | ||||
|                     android:minWidth="50dp" | ||||
|                     android:textColor="@android:color/white" | ||||
| @@ -291,18 +299,50 @@ | ||||
|                     android:layout_height="wrap_content" | ||||
|                     android:layout_marginLeft="8dp" | ||||
|                     android:layout_marginRight="8dp" | ||||
|                     android:layout_toLeftOf="@id/switchBackground" | ||||
|                     android:layout_toRightOf="@id/resizeTextView" | ||||
|                     android:gravity="center|left" | ||||
|                     android:minHeight="35dp" | ||||
|                     android:minWidth="40dp" | ||||
|                     android:paddingLeft="2dp" | ||||
|                     android:paddingRight="2dp" | ||||
|                     android:minWidth="50dp" | ||||
|                     android:textColor="@android:color/white" | ||||
|                     android:textStyle="bold" | ||||
|                     android:background="?attr/selectableItemBackground" | ||||
|                     tools:ignore="RelativeOverlap,RtlHardcoded" | ||||
|                     tools:text="English" /> | ||||
|                     tools:text="English"/> | ||||
|  | ||||
|                 <Space | ||||
|                     android:id="@+id/spaceBeforeButton" | ||||
|                     android:layout_width="0dp" | ||||
|                     android:layout_height="0dp" | ||||
|                     android:layout_weight="3"/> | ||||
|  | ||||
|                 <ImageButton | ||||
|                     android:id="@+id/playWithKodi" | ||||
|                     android:layout_width="30dp" | ||||
|                     android:layout_height="30dp" | ||||
|                     android:layout_marginLeft="4dp" | ||||
|                     android:layout_marginRight="2dp" | ||||
|                     android:clickable="true" | ||||
|                     android:focusable="true" | ||||
|                     android:padding="5dp" | ||||
|                     android:scaleType="fitXY" | ||||
|                     android:src="@drawable/ic_channel_white_24dp" | ||||
|                     android:background="?attr/selectableItemBackground" | ||||
|                     android:contentDescription="@string/play_with_kodi_title" | ||||
|                     tools:ignore="RtlHardcoded"/> | ||||
|  | ||||
|                 <ImageButton | ||||
|                     android:id="@+id/openInBrowser" | ||||
|                     android:layout_width="30dp" | ||||
|                     android:layout_height="30dp" | ||||
|                     android:layout_marginLeft="4dp" | ||||
|                     android:layout_marginRight="2dp" | ||||
|                     android:clickable="true" | ||||
|                     android:focusable="true" | ||||
|                     android:padding="5dp" | ||||
|                     android:scaleType="fitXY" | ||||
|                     android:src="@drawable/ic_language_white_24dp" | ||||
|                     android:background="?attr/selectableItemBackground" | ||||
|                     android:contentDescription="@string/open_in_browser" | ||||
|                     tools:ignore="RtlHardcoded"/> | ||||
|  | ||||
|                 <ImageButton | ||||
|                     android:id="@+id/share" | ||||
| @@ -310,8 +350,6 @@ | ||||
|                     android:layout_height="30dp" | ||||
|                     android:layout_marginLeft="4dp" | ||||
|                     android:layout_marginRight="2dp" | ||||
|                     android:layout_alignParentRight="true" | ||||
|                     android:layout_centerVertical="true" | ||||
|                     android:clickable="true" | ||||
|                     android:focusable="true" | ||||
|                     android:padding="5dp" | ||||
| @@ -321,56 +359,25 @@ | ||||
|                     android:contentDescription="@string/share" | ||||
|                     tools:ignore="RtlHardcoded"/> | ||||
|  | ||||
|                 <ImageButton | ||||
|                     android:id="@+id/toggleOrientation" | ||||
|                     android:layout_width="30dp" | ||||
|                     android:layout_height="30dp" | ||||
|                     android:layout_marginLeft="4dp" | ||||
|                     android:layout_marginRight="4dp" | ||||
|                     android:layout_toLeftOf="@id/share" | ||||
|                     android:layout_centerVertical="true" | ||||
|                     android:clickable="true" | ||||
|                     android:focusable="true" | ||||
|                     android:padding="5dp" | ||||
|                     android:scaleType="fitXY" | ||||
|                     android:src="@drawable/ic_screen_rotation_white" | ||||
|                     android:background="?attr/selectableItemBackground" | ||||
|                     android:contentDescription="@string/toggle_orientation" | ||||
|                     tools:ignore="RtlHardcoded"/> | ||||
|                 </LinearLayout> | ||||
|  | ||||
|             </LinearLayout> | ||||
|  | ||||
|                 <ImageButton | ||||
|                     android:id="@+id/switchPopup" | ||||
|                     android:layout_width="30dp" | ||||
|                     android:layout_height="30dp" | ||||
|                     android:layout_marginLeft="4dp" | ||||
|                     android:id="@+id/fullScreenButton" | ||||
|                     android:layout_width="40dp" | ||||
|                     android:layout_height="40dp" | ||||
|                     android:layout_marginRight="4dp" | ||||
|                     android:layout_toLeftOf="@id/toggleOrientation" | ||||
|                     android:layout_centerVertical="true" | ||||
|                     android:padding="4dp" | ||||
|                     android:layout_alignParentRight="true" | ||||
|                     android:background="@drawable/player_top_controls_bg" | ||||
|                     android:clickable="true" | ||||
|                     android:focusable="true" | ||||
|                     android:scaleType="fitXY" | ||||
|                     android:src="@drawable/ic_fullscreen_exit_white" | ||||
|                     android:background="?attr/selectableItemBackground" | ||||
|                     android:contentDescription="@string/switch_to_popup" | ||||
|                     tools:ignore="RtlHardcoded"/> | ||||
|  | ||||
|                 <ImageButton | ||||
|                     android:id="@+id/switchBackground" | ||||
|                     android:layout_width="30dp" | ||||
|                     android:layout_height="30dp" | ||||
|                     android:layout_marginLeft="4dp" | ||||
|                     android:layout_marginRight="4dp" | ||||
|                     android:layout_toLeftOf="@id/switchPopup" | ||||
|                     android:layout_centerVertical="true" | ||||
|                     android:clickable="true" | ||||
|                     android:focusable="true" | ||||
|                     android:padding="5dp" | ||||
|                     android:scaleType="fitXY" | ||||
|                     android:src="@drawable/ic_headset_white_24dp" | ||||
|                     android:background="?attr/selectableItemBackground" | ||||
|                     android:contentDescription="@string/switch_to_background" | ||||
|                     tools:ignore="RtlHardcoded"/> | ||||
|             </RelativeLayout> | ||||
|                     android:scaleType="fitCenter" | ||||
|                     android:src="@drawable/ic_fullscreen_white" | ||||
|                     tools:ignore="ContentDescription,RtlHardcoded" | ||||
|                     android:visibility="gone" | ||||
|                     tools:visibility="visible"/> | ||||
|  | ||||
|             <LinearLayout | ||||
|                 android:id="@+id/bottomControls" | ||||
| @@ -380,6 +387,7 @@ | ||||
|                 android:background="@drawable/player_controls_bg" | ||||
|                 android:gravity="center" | ||||
|                 android:orientation="horizontal" | ||||
|                 android:paddingBottom="2dp" | ||||
|                 android:paddingLeft="16dp" | ||||
|                 android:paddingRight="16dp"> | ||||
|  | ||||
| @@ -388,7 +396,7 @@ | ||||
|                     android:layout_width="wrap_content" | ||||
|                     android:layout_height="match_parent" | ||||
|                     android:gravity="center" | ||||
|                     android:minHeight="40dp" | ||||
|                     android:minHeight="30dp" | ||||
|                     android:text="-:--:--" | ||||
|                     android:textColor="@android:color/white" | ||||
|                     tools:ignore="HardcodedText" | ||||
| @@ -433,65 +441,52 @@ | ||||
|             </LinearLayout> | ||||
|         </RelativeLayout> | ||||
|  | ||||
|         <LinearLayout | ||||
|             android:layout_width="match_parent" | ||||
|             android:layout_height="match_parent" | ||||
|             android:gravity="center" | ||||
|             android:orientation="horizontal" | ||||
|             android:weightSum="5.5"> | ||||
|             <!--tools:visibility="gone">--> | ||||
|  | ||||
|         <ImageButton | ||||
|             android:id="@+id/playPauseButton" | ||||
|             android:layout_width="100dp" | ||||
|             android:id="@+id/playPreviousButton" | ||||
|             android:layout_width="0dp" | ||||
|             android:layout_height="100dp" | ||||
|             android:layout_centerInParent="true" | ||||
|             android:layout_weight="1" | ||||
|             android:layout_marginEnd="30dp" | ||||
|             android:clickable="true" | ||||
|             android:focusable="true" | ||||
|             android:background="?attr/selectableItemBackgroundBorderless" | ||||
|             android:scaleType="fitXY" | ||||
|             android:scaleType="fitCenter" | ||||
|             android:src="@drawable/exo_controls_previous" | ||||
|             tools:ignore="ContentDescription"/> | ||||
|  | ||||
|  | ||||
|         <ImageButton | ||||
|             android:id="@+id/playPauseButton" | ||||
|             android:layout_width="0dp" | ||||
|             android:layout_height="100dp" | ||||
|             android:layout_weight="1" | ||||
|             android:background="?attr/selectableItemBackgroundBorderless" | ||||
|             android:scaleType="fitCenter" | ||||
|             android:src="@drawable/ic_pause_white" | ||||
|             tools:ignore="ContentDescription"/> | ||||
|  | ||||
|         <ImageButton | ||||
|             android:id="@+id/playPreviousButton" | ||||
|             android:layout_width="50dp" | ||||
|             android:layout_height="50dp" | ||||
|             android:layout_marginRight="30dp" | ||||
|             android:layout_marginEnd="30dp" | ||||
|             android:layout_centerInParent="true" | ||||
|             android:layout_toLeftOf="@id/playPauseButton" | ||||
|             android:layout_toStartOf="@id/playPauseButton" | ||||
|             android:clickable="true" | ||||
|             android:focusable="true" | ||||
|             android:background="?attr/selectableItemBackgroundBorderless" | ||||
|             android:scaleType="fitXY" | ||||
|             android:src="@drawable/exo_controls_previous" | ||||
|             tools:ignore="ContentDescription"/> | ||||
|  | ||||
|         <ImageButton | ||||
|             android:id="@+id/playNextButton" | ||||
|             android:layout_width="50dp" | ||||
|             android:layout_height="50dp" | ||||
|             android:layout_marginLeft="30dp" | ||||
|             android:layout_width="0dp" | ||||
|             android:layout_height="100dp" | ||||
|             android:layout_weight="1" | ||||
|             android:layout_marginStart="30dp" | ||||
|             android:layout_centerInParent="true" | ||||
|             android:layout_toRightOf="@id/playPauseButton" | ||||
|             android:layout_toEndOf="@id/playPauseButton" | ||||
|             android:clickable="true" | ||||
|             android:focusable="true" | ||||
|             android:background="?attr/selectableItemBackgroundBorderless" | ||||
|             android:scaleType="fitXY" | ||||
|             android:scaleType="fitCenter" | ||||
|             android:src="@drawable/exo_controls_next" | ||||
|             tools:ignore="ContentDescription"/> | ||||
|  | ||||
|         <Button | ||||
|             android:id="@+id/closeButton" | ||||
|             style="@style/Widget.AppCompat.Button.Borderless" | ||||
|             android:layout_width="wrap_content" | ||||
|             android:layout_height="wrap_content" | ||||
|             android:layout_below="@id/playPauseButton" | ||||
|             android:layout_centerInParent="true" | ||||
|             android:layout_marginTop="10dp" | ||||
|             android:background="?attr/selectableItemBackgroundBorderless" | ||||
|             android:clickable="true" | ||||
|             android:focusable="true" | ||||
|             android:text="@string/close" | ||||
|             android:textAllCaps="true" | ||||
|             android:textColor="@color/white" | ||||
|             android:visibility="invisible" /> | ||||
|         </LinearLayout> | ||||
|  | ||||
|     </RelativeLayout> | ||||
|  | ||||
| @@ -533,10 +528,7 @@ | ||||
|     <RelativeLayout | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="match_parent" | ||||
|         android:layout_alignParentTop="true" | ||||
|         android:layout_gravity="center" | ||||
|         android:layout_toEndOf="@+id/loading_panel" | ||||
|         android:layout_toRightOf="@+id/loading_panel" | ||||
|         tools:ignore="RtlHardcoded"> | ||||
|  | ||||
|         <RelativeLayout | ||||
| @@ -611,4 +603,42 @@ | ||||
|             tools:visibility="visible" /> | ||||
|     </RelativeLayout> | ||||
|  | ||||
|     <TextView | ||||
|         android:id="@+id/resizing_indicator" | ||||
|         android:layout_width="wrap_content" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:layout_gravity="left|top" | ||||
|         android:background="#6e000000" | ||||
|         android:gravity="center" | ||||
|         android:padding="5dp" | ||||
|         android:text="@string/popup_resizing_indicator_title" | ||||
|         android:textColor="@android:color/white" | ||||
|         android:textSize="18sp" | ||||
|         android:textStyle="bold" | ||||
|         android:visibility="gone" | ||||
|         tools:ignore="RtlHardcoded" | ||||
|         tools:visibility="gone" /> | ||||
|  | ||||
|     <View | ||||
|         android:id="@+id/closingOverlay" | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="match_parent" | ||||
|         android:background="#AAFF0000" | ||||
|         android:visibility="gone" /> | ||||
|  | ||||
|     <Button | ||||
|         android:id="@+id/closeButton" | ||||
|         style="@style/Widget.AppCompat.Button.Borderless" | ||||
|         android:layout_width="wrap_content" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:layout_centerInParent="true" | ||||
|         android:layout_marginTop="10dp" | ||||
|         android:background="?attr/selectableItemBackgroundBorderless" | ||||
|         android:clickable="true" | ||||
|         android:focusable="true" | ||||
|         android:text="@string/close" | ||||
|         android:textAllCaps="true" | ||||
|         android:textColor="@color/white" | ||||
|         android:visibility="gone" /> | ||||
|  | ||||
| </RelativeLayout> | ||||
|   | ||||
| @@ -1,7 +1,12 @@ | ||||
| <?xml version="1.0" encoding="utf-8"?> | ||||
| <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||
| <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" | ||||
| 	xmlns:app="http://schemas.android.com/apk/res-auto" | ||||
| 	xmlns:tools="http://schemas.android.com/tools" | ||||
| 	android:layout_width="match_parent" | ||||
| 	android:layout_height="match_parent" | ||||
| 	android:background="?attr/windowBackground"> | ||||
|  | ||||
| <LinearLayout | ||||
| 	android:id="@+id/video_item_detail" | ||||
| 	android:layout_width="match_parent" | ||||
| 	android:layout_height="match_parent" | ||||
| @@ -17,6 +22,30 @@ | ||||
| 		android:layout_weight="5" | ||||
| 		android:fitsSystemWindows="true"> | ||||
|  | ||||
| 		<androidx.core.widget.NestedScrollView | ||||
| 				android:layout_width="match_parent" | ||||
| 				android:layout_height="match_parent" | ||||
| 				app:layout_behavior="@string/appbar_scrolling_view_behavior" | ||||
| 				android:fillViewport="true"> | ||||
|  | ||||
| 			<androidx.viewpager.widget.ViewPager | ||||
| 					android:id="@+id/viewpager" | ||||
| 					android:layout_width="match_parent" | ||||
| 					android:layout_height="match_parent" > | ||||
|  | ||||
| 				<com.google.android.material.tabs.TabLayout | ||||
| 						android:id="@+id/tablayout" | ||||
| 						android:layout_width="match_parent" | ||||
| 						android:layout_height="30dp" | ||||
| 						android:layout_gravity="bottom|center" | ||||
| 						app:tabBackground="@drawable/tab_selector" | ||||
| 						app:tabGravity="center" | ||||
| 						app:tabIndicatorHeight="0dp"/> | ||||
|  | ||||
| 			</androidx.viewpager.widget.ViewPager> | ||||
|  | ||||
| 		</androidx.core.widget.NestedScrollView> | ||||
|  | ||||
| 		<com.google.android.material.appbar.AppBarLayout | ||||
| 			android:id="@+id/appbarlayout" | ||||
| 			android:layout_width="match_parent" | ||||
| @@ -144,6 +173,13 @@ | ||||
| 						tools:progress="40" | ||||
| 						tools:visibility="visible" /> | ||||
|  | ||||
| 					<!-- Player will be inserted here in realtime --> | ||||
| 					<FrameLayout | ||||
| 							android:id="@+id/player_placeholder" | ||||
| 							android:layout_width="match_parent" | ||||
| 							android:layout_height="match_parent" | ||||
| 					/> | ||||
|  | ||||
| 				</FrameLayout> | ||||
|  | ||||
| 			</com.google.android.material.appbar.CollapsingToolbarLayout> | ||||
| @@ -153,7 +189,6 @@ | ||||
| 				android:id="@+id/detail_content_root_layout" | ||||
| 				android:layout_width="match_parent" | ||||
| 				android:layout_height="wrap_content" | ||||
| 				android:background="?android:windowBackground" | ||||
| 				app:layout_scrollFlags="scroll"> | ||||
|  | ||||
| 				<!-- TITLE --> | ||||
| @@ -506,26 +541,6 @@ | ||||
|  | ||||
| 		</com.google.android.material.appbar.AppBarLayout> | ||||
|  | ||||
|  | ||||
| 		<androidx.viewpager.widget.ViewPager | ||||
| 			android:id="@+id/viewpager" | ||||
| 			android:layout_width="match_parent" | ||||
| 			android:layout_height="match_parent" | ||||
| 			app:layout_behavior="@string/appbar_scrolling_view_behavior" /> | ||||
|  | ||||
|  | ||||
| 		<com.google.android.material.tabs.TabLayout | ||||
| 			android:id="@+id/tablayout" | ||||
| 			android:layout_width="match_parent" | ||||
| 			android:layout_height="wrap_content" | ||||
| 			android:layout_gravity="bottom|center" | ||||
| 			android:background="@color/transparent_background_color" | ||||
| 			app:tabBackground="@drawable/tab_selector" | ||||
| 			app:tabGravity="center" | ||||
| 			app:tabIndicatorHeight="0dp"> | ||||
|  | ||||
| 		</com.google.android.material.tabs.TabLayout> | ||||
|  | ||||
| 	</androidx.coordinatorlayout.widget.CoordinatorLayout> | ||||
|  | ||||
| 	<FrameLayout | ||||
| @@ -537,3 +552,105 @@ | ||||
|  | ||||
| 	</FrameLayout> | ||||
| </LinearLayout> | ||||
|  | ||||
| 	<RelativeLayout | ||||
| 			android:id="@+id/overlay_layout" | ||||
| 			android:layout_width="match_parent" | ||||
| 			android:layout_height="match_parent" | ||||
| 			android:alpha="0" | ||||
| 			tools:alpha="1" | ||||
| 			android:paddingLeft="@dimen/video_item_search_padding" | ||||
| 			android:paddingRight="@dimen/video_item_search_padding" | ||||
| 			android:background="?attr/windowBackground" > | ||||
|  | ||||
| 		<ImageButton | ||||
| 				android:id="@+id/overlay_thumbnail" | ||||
| 				android:layout_width="50dp" | ||||
| 				android:layout_height="60dp" | ||||
| 				android:layout_alignParentStart="true" | ||||
| 				android:scaleType="fitCenter" | ||||
| 				android:gravity="center_vertical" | ||||
| 				android:contentDescription="@string/list_thumbnail_view_description" | ||||
| 				android:src="@drawable/dummy_thumbnail" | ||||
| 				android:background="@color/transparent_background_color"/> | ||||
|  | ||||
| 		<LinearLayout | ||||
| 				android:id="@+id/overlay_metadata_layout" | ||||
| 				android:layout_width="match_parent" | ||||
| 				android:layout_height="60dp" | ||||
| 				android:gravity="center_vertical" | ||||
| 				android:orientation="vertical" | ||||
| 				android:paddingLeft="@dimen/video_item_search_padding" | ||||
| 				android:paddingRight="@dimen/video_item_search_padding" | ||||
| 				android:clickable="true" | ||||
| 				android:focusable="true" | ||||
| 				android:layout_toEndOf="@+id/overlay_thumbnail" | ||||
| 				android:layout_toStartOf="@+id/overlay_buttons_layout" | ||||
| 				tools:ignore="RtlHardcoded"> | ||||
|  | ||||
| 			<TextView | ||||
| 					android:id="@+id/overlay_title_text_view" | ||||
| 					android:layout_width="match_parent" | ||||
| 					android:layout_height="wrap_content" | ||||
| 					android:ellipsize="marquee" | ||||
| 					android:fadingEdge="horizontal" | ||||
| 					android:marqueeRepeatLimit="marquee_forever" | ||||
| 					android:scrollHorizontally="true" | ||||
| 					android:singleLine="true" | ||||
| 					android:textAppearance="?android:attr/textAppearanceLarge" | ||||
| 					android:textSize="@dimen/video_item_search_title_text_size" | ||||
| 					tools:ignore="RtlHardcoded" | ||||
| 					tools:text="The Video Title LONG very LONVideo Title LONG very LONG"/> | ||||
|  | ||||
| 			<TextView | ||||
| 					android:id="@+id/overlay_channel_text_view" | ||||
| 					android:layout_width="match_parent" | ||||
| 					android:layout_height="wrap_content" | ||||
| 					android:ellipsize="marquee" | ||||
| 					android:fadingEdge="horizontal" | ||||
| 					android:marqueeRepeatLimit="marquee_forever" | ||||
| 					android:scrollHorizontally="true" | ||||
| 					android:singleLine="true" | ||||
| 					android:textAppearance="?android:attr/textAppearanceSmall" | ||||
| 					android:textSize="@dimen/video_item_search_uploader_text_size" | ||||
| 					tools:text="The Video Artist  LONG very LONG very Long"/> | ||||
|  | ||||
| 		</LinearLayout> | ||||
|  | ||||
| 		<LinearLayout | ||||
| 				android:id="@+id/overlay_buttons_layout" | ||||
| 				android:layout_width="wrap_content" | ||||
| 				android:layout_height="60dp" | ||||
| 				android:gravity="center_vertical" | ||||
| 				android:paddingLeft="@dimen/video_item_search_padding" | ||||
| 				android:layout_alignParentEnd="true" | ||||
| 				tools:ignore="RtlHardcoded"> | ||||
|  | ||||
| 			<ImageButton | ||||
| 					android:id="@+id/overlay_play_pause_button" | ||||
| 					android:layout_width="36dp" | ||||
| 					android:layout_height="36dp" | ||||
| 					android:layout_marginLeft="2dp" | ||||
| 					android:layout_marginRight="2dp" | ||||
| 					android:padding="10dp" | ||||
| 					android:scaleType="center" | ||||
| 					android:src="@drawable/ic_pause_white_24dp" | ||||
| 					android:background="?attr/selectableItemBackground" | ||||
| 					tools:ignore="ContentDescription,RtlHardcoded"/> | ||||
|  | ||||
| 			<ImageButton | ||||
| 					android:id="@+id/overlay_close_button" | ||||
| 					android:layout_width="36dp" | ||||
| 					android:layout_height="36dp" | ||||
| 					android:layout_marginLeft="2dp" | ||||
| 					android:padding="10dp" | ||||
| 					android:scaleType="center" | ||||
| 					android:src="?attr/close" | ||||
| 					android:background="?attr/selectableItemBackground" | ||||
| 					tools:ignore="ContentDescription,RtlHardcoded"/> | ||||
|  | ||||
| 		</LinearLayout> | ||||
|  | ||||
| 	</RelativeLayout> | ||||
|  | ||||
| </FrameLayout> | ||||
| @@ -2,6 +2,7 @@ | ||||
| <androidx.drawerlayout.widget.DrawerLayout | ||||
|     xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:tools="http://schemas.android.com/tools" | ||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" | ||||
|     android:id="@+id/drawer_layout" | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="match_parent" | ||||
| @@ -22,6 +23,23 @@ | ||||
|         <include layout="@layout/toolbar_layout" /> | ||||
|     </FrameLayout> | ||||
|  | ||||
|     <androidx.coordinatorlayout.widget.CoordinatorLayout | ||||
|             android:layout_width="match_parent" | ||||
|             android:layout_height="wrap_content"> | ||||
|  | ||||
|         <FrameLayout | ||||
|                 android:id="@+id/fragment_player_holder" | ||||
|                 android:layout_width="match_parent" | ||||
|                 android:layout_height="match_parent" | ||||
|                 android:layout_gravity="center_horizontal" | ||||
|                 app:behavior_hideable="true" | ||||
|                 app:behavior_peekHeight="0dp" | ||||
|                 app:layout_behavior="org.schabi.newpipe.player.event.CustomBottomSheetBehavior"> | ||||
|         </FrameLayout> | ||||
|  | ||||
|     </androidx.coordinatorlayout.widget.CoordinatorLayout> | ||||
|  | ||||
|  | ||||
|     <include layout="@layout/drawer_layout"/> | ||||
|  | ||||
| </androidx.drawerlayout.widget.DrawerLayout> | ||||
|   | ||||
| @@ -2,9 +2,10 @@ | ||||
| <RelativeLayout | ||||
|     xmlns:android="http://schemas.android.com/apk/res/android" | ||||
|     xmlns:tools="http://schemas.android.com/tools" | ||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" | ||||
|     android:layout_width="match_parent" | ||||
|     android:layout_height="match_parent" | ||||
|     xmlns:app="http://schemas.android.com/apk/res-auto" | ||||
|     android:background="@color/black" | ||||
|     android:gravity="center"> | ||||
|  | ||||
|     <com.google.android.exoplayer2.ui.AspectRatioFrameLayout | ||||
| @@ -118,7 +119,7 @@ | ||||
|             android:layout_height="match_parent" | ||||
|             android:layout_below="@id/playQueueControl" | ||||
|             android:scrollbars="vertical" | ||||
|             app:layoutManager="LinearLayoutManager" | ||||
|             app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager" | ||||
|             tools:listitem="@layout/play_queue_item"/> | ||||
|  | ||||
|     </RelativeLayout> | ||||
| @@ -131,37 +132,46 @@ | ||||
|         android:visibility="gone" | ||||
|         tools:visibility="visible"> | ||||
|  | ||||
|         <!-- All top controls in this layout --> | ||||
|         <RelativeLayout | ||||
|             android:id="@+id/playbackWindowRoot" | ||||
|             android:layout_width="match_parent" | ||||
|             android:layout_height="match_parent" | ||||
|             android:fitsSystemWindows="true"> | ||||
|  | ||||
|             <RelativeLayout | ||||
|             <LinearLayout | ||||
|                 android:id="@+id/topControls" | ||||
|                 android:layout_width="match_parent" | ||||
|                 android:layout_height="wrap_content" | ||||
|                 android:layout_alignParentTop="true" | ||||
|                 android:background="@drawable/player_top_controls_bg" | ||||
|                 android:orientation="vertical" | ||||
|                 android:gravity="top" | ||||
|                 android:paddingTop="10dp" | ||||
|                 android:paddingBottom="10dp" | ||||
|                 android:paddingLeft="5dp" | ||||
|                 android:paddingRight="5dp" | ||||
|                 android:baselineAligned="false" | ||||
|                 android:layout_toStartOf="@id/fullScreenButton"> | ||||
|  | ||||
|             <LinearLayout | ||||
|                 android:id="@+id/primaryControls" | ||||
|                 android:layout_width="match_parent" | ||||
|                 android:layout_height="wrap_content" | ||||
|                 android:baselineAligned="false" | ||||
|                 android:gravity="top" | ||||
|                 android:paddingTop="4dp" | ||||
|                 android:paddingBottom="7dp" | ||||
|                 android:paddingLeft="2dp" | ||||
|                 android:paddingRight="6dp" | ||||
|                 tools:ignore="RtlHardcoded"> | ||||
|  | ||||
|                 <LinearLayout | ||||
|                     android:id="@+id/metadataView" | ||||
|                     android:layout_width="wrap_content" | ||||
|                     android:layout_width="0dp" | ||||
|                     android:layout_height="wrap_content" | ||||
|                     android:layout_alignParentLeft="true" | ||||
|                     android:layout_alignParentTop="true" | ||||
|                     android:layout_toLeftOf="@+id/qualityTextView" | ||||
|                     android:gravity="top" | ||||
|                     android:orientation="vertical" | ||||
|                     android:paddingLeft="8dp" | ||||
|                     android:paddingRight="8dp" | ||||
|                     tools:ignore="RtlHardcoded"> | ||||
|                     tools:ignore="RtlHardcoded" | ||||
|                     android:layout_weight="1"> | ||||
|  | ||||
|                     <TextView | ||||
|                         android:id="@+id/titleTextView" | ||||
| @@ -202,13 +212,13 @@ | ||||
|                     android:layout_height="35dp" | ||||
|                     android:layout_marginLeft="2dp" | ||||
|                     android:layout_marginRight="2dp" | ||||
|                     android:layout_toLeftOf="@+id/playbackSpeed" | ||||
|                     android:gravity="center" | ||||
|                     android:minWidth="50dp" | ||||
|                     android:text="720p" | ||||
|                     android:textColor="@android:color/white" | ||||
|                     android:textStyle="bold" | ||||
|                     android:background="?attr/selectableItemBackground" | ||||
|                     android:visibility="visible" | ||||
|                     tools:ignore="HardcodedText,RtlHardcoded"/> | ||||
|  | ||||
|                 <TextView | ||||
| @@ -216,7 +226,6 @@ | ||||
|                     android:layout_width="wrap_content" | ||||
|                     android:layout_height="wrap_content" | ||||
|                     android:layout_marginRight="2dp" | ||||
|                     android:layout_toLeftOf="@+id/queueButton" | ||||
|                     android:gravity="center" | ||||
|                     android:minHeight="35dp" | ||||
|                     android:minWidth="40dp" | ||||
| @@ -224,7 +233,7 @@ | ||||
|                     android:textStyle="bold" | ||||
|                     android:background="?attr/selectableItemBackground" | ||||
|                     tools:ignore="RtlHardcoded,RtlSymmetry" | ||||
|                     tools:text="1x" /> | ||||
|                     tools:text="1x"/> | ||||
|  | ||||
|                 <ImageButton | ||||
|                     android:id="@+id/queueButton" | ||||
| @@ -232,20 +241,19 @@ | ||||
|                     android:layout_height="35dp" | ||||
|                     android:layout_marginLeft="2dp" | ||||
|                     android:layout_marginRight="2dp" | ||||
|                     android:layout_toLeftOf="@+id/moreOptionsButton" | ||||
|                     android:clickable="true" | ||||
|                     android:focusable="true" | ||||
|                     android:padding="5dp" | ||||
|                     android:scaleType="fitXY" | ||||
|                     android:src="@drawable/ic_list_white_24dp" | ||||
|                     android:background="?attr/selectableItemBackground" | ||||
|                     tools:ignore="ContentDescription,RtlHardcoded"/> | ||||
|                     tools:ignore="ContentDescription,RtlHardcoded" | ||||
|                     android:visibility="gone"/> | ||||
|  | ||||
|                 <ImageButton | ||||
|                     android:id="@+id/moreOptionsButton" | ||||
|                     android:layout_width="wrap_content" | ||||
|                     android:layout_height="wrap_content" | ||||
|                     android:layout_alignParentRight="true" | ||||
|                     android:layout_marginLeft="2dp" | ||||
|                     android:padding="5dp" | ||||
|                     android:clickable="true" | ||||
| @@ -254,16 +262,18 @@ | ||||
|                     android:src="@drawable/ic_expand_more_white_24dp" | ||||
|                     android:background="?attr/selectableItemBackgroundBorderless" | ||||
|                     tools:ignore="ContentDescription,RtlHardcoded"/> | ||||
|             </RelativeLayout> | ||||
|  | ||||
|             <RelativeLayout | ||||
|                 </LinearLayout> | ||||
|  | ||||
|             <LinearLayout | ||||
|                 android:id="@+id/secondaryControls" | ||||
|                 android:layout_width="match_parent" | ||||
|                 android:layout_height="wrap_content" | ||||
|                 android:layout_below="@id/topControls" | ||||
|                 android:gravity="top" | ||||
|                 android:paddingLeft="5dp" | ||||
|                 android:paddingRight="5dp" | ||||
|                 android:paddingTop="4dp" | ||||
|                 android:paddingBottom="7dp" | ||||
|                 android:paddingStart="6dp" | ||||
|                 android:paddingEnd="6dp" | ||||
|                 android:visibility="invisible" | ||||
|                 tools:ignore="RtlHardcoded" | ||||
|                 tools:visibility="visible"> | ||||
| @@ -272,9 +282,7 @@ | ||||
|                     android:id="@+id/resizeTextView" | ||||
|                     android:layout_width="wrap_content" | ||||
|                     android:layout_height="35dp" | ||||
|                     android:layout_marginLeft="8dp" | ||||
|                     android:layout_marginRight="8dp" | ||||
|                     android:layout_alignParentLeft="true" | ||||
|                     android:gravity="center" | ||||
|                     android:minWidth="50dp" | ||||
|                     android:textColor="@android:color/white" | ||||
| @@ -289,18 +297,50 @@ | ||||
|                     android:layout_height="wrap_content" | ||||
|                     android:layout_marginLeft="8dp" | ||||
|                     android:layout_marginRight="8dp" | ||||
|                     android:layout_toLeftOf="@id/switchBackground" | ||||
|                     android:layout_toRightOf="@id/resizeTextView" | ||||
|                     android:gravity="center|left" | ||||
|                     android:minHeight="35dp" | ||||
|                     android:minWidth="40dp" | ||||
|                     android:paddingLeft="2dp" | ||||
|                     android:paddingRight="2dp" | ||||
|                     android:minWidth="50dp" | ||||
|                     android:textColor="@android:color/white" | ||||
|                     android:textStyle="bold" | ||||
|                     android:background="?attr/selectableItemBackground" | ||||
|                     tools:ignore="RelativeOverlap,RtlHardcoded" | ||||
|                     tools:text="English" /> | ||||
|                     tools:text="English"/> | ||||
|  | ||||
|                 <Space | ||||
|                     android:id="@+id/spaceBeforeButton" | ||||
|                     android:layout_width="0dp" | ||||
|                     android:layout_height="0dp" | ||||
|                     android:layout_weight="3"/> | ||||
|  | ||||
|                 <ImageButton | ||||
|                     android:id="@+id/playWithKodi" | ||||
|                     android:layout_width="30dp" | ||||
|                     android:layout_height="30dp" | ||||
|                     android:layout_marginLeft="4dp" | ||||
|                     android:layout_marginRight="2dp" | ||||
|                     android:clickable="true" | ||||
|                     android:focusable="true" | ||||
|                     android:padding="5dp" | ||||
|                     android:scaleType="fitXY" | ||||
|                     android:src="@drawable/ic_channel_white_24dp" | ||||
|                     android:background="?attr/selectableItemBackground" | ||||
|                     android:contentDescription="@string/play_with_kodi_title" | ||||
|                     tools:ignore="RtlHardcoded"/> | ||||
|  | ||||
|                 <ImageButton | ||||
|                     android:id="@+id/openInBrowser" | ||||
|                     android:layout_width="30dp" | ||||
|                     android:layout_height="30dp" | ||||
|                     android:layout_marginLeft="4dp" | ||||
|                     android:layout_marginRight="2dp" | ||||
|                     android:clickable="true" | ||||
|                     android:focusable="true" | ||||
|                     android:padding="5dp" | ||||
|                     android:scaleType="fitXY" | ||||
|                     android:src="@drawable/ic_language_white_24dp" | ||||
|                     android:background="?attr/selectableItemBackground" | ||||
|                     android:contentDescription="@string/open_in_browser" | ||||
|                     tools:ignore="RtlHardcoded"/> | ||||
|  | ||||
|                 <ImageButton | ||||
|                     android:id="@+id/share" | ||||
| @@ -308,8 +348,6 @@ | ||||
|                     android:layout_height="30dp" | ||||
|                     android:layout_marginLeft="4dp" | ||||
|                     android:layout_marginRight="2dp" | ||||
|                     android:layout_alignParentRight="true" | ||||
|                     android:layout_centerVertical="true" | ||||
|                     android:clickable="true" | ||||
|                     android:focusable="true" | ||||
|                     android:padding="5dp" | ||||
| @@ -319,56 +357,25 @@ | ||||
|                     android:contentDescription="@string/share" | ||||
|                     tools:ignore="RtlHardcoded"/> | ||||
|  | ||||
|                 <ImageButton | ||||
|                     android:id="@+id/toggleOrientation" | ||||
|                     android:layout_width="30dp" | ||||
|                     android:layout_height="30dp" | ||||
|                     android:layout_marginLeft="4dp" | ||||
|                     android:layout_marginRight="4dp" | ||||
|                     android:layout_toLeftOf="@id/share" | ||||
|                     android:layout_centerVertical="true" | ||||
|                     android:clickable="true" | ||||
|                     android:focusable="true" | ||||
|                     android:padding="5dp" | ||||
|                     android:scaleType="fitXY" | ||||
|                     android:src="@drawable/ic_screen_rotation_white" | ||||
|                     android:background="?attr/selectableItemBackground" | ||||
|                     android:contentDescription="@string/toggle_orientation" | ||||
|                     tools:ignore="RtlHardcoded"/> | ||||
|                 </LinearLayout> | ||||
|  | ||||
|             </LinearLayout> | ||||
|  | ||||
|                 <ImageButton | ||||
|                     android:id="@+id/switchPopup" | ||||
|                     android:layout_width="30dp" | ||||
|                     android:layout_height="30dp" | ||||
|                     android:layout_marginLeft="4dp" | ||||
|                     android:id="@+id/fullScreenButton" | ||||
|                     android:layout_width="40dp" | ||||
|                     android:layout_height="40dp" | ||||
|                     android:layout_marginRight="4dp" | ||||
|                     android:layout_toLeftOf="@id/toggleOrientation" | ||||
|                     android:layout_centerVertical="true" | ||||
|                     android:padding="4dp" | ||||
|                     android:layout_alignParentRight="true" | ||||
|                     android:background="@drawable/player_top_controls_bg" | ||||
|                     android:clickable="true" | ||||
|                     android:focusable="true" | ||||
|                     android:scaleType="fitXY" | ||||
|                     android:src="@drawable/ic_fullscreen_exit_white" | ||||
|                     android:background="?attr/selectableItemBackground" | ||||
|                     android:contentDescription="@string/switch_to_popup" | ||||
|                     tools:ignore="RtlHardcoded"/> | ||||
|  | ||||
|                 <ImageButton | ||||
|                     android:id="@+id/switchBackground" | ||||
|                     android:layout_width="30dp" | ||||
|                     android:layout_height="30dp" | ||||
|                     android:layout_marginLeft="4dp" | ||||
|                     android:layout_marginRight="4dp" | ||||
|                     android:layout_toLeftOf="@id/switchPopup" | ||||
|                     android:layout_centerVertical="true" | ||||
|                     android:clickable="true" | ||||
|                     android:focusable="true" | ||||
|                     android:padding="5dp" | ||||
|                     android:scaleType="fitXY" | ||||
|                     android:src="@drawable/ic_headset_white_24dp" | ||||
|                     android:background="?attr/selectableItemBackground" | ||||
|                     android:contentDescription="@string/switch_to_background" | ||||
|                     tools:ignore="RtlHardcoded"/> | ||||
|             </RelativeLayout> | ||||
|                     android:scaleType="fitCenter" | ||||
|                     android:src="@drawable/ic_fullscreen_white" | ||||
|                     tools:ignore="ContentDescription,RtlHardcoded" | ||||
|                     android:visibility="gone" | ||||
|                     tools:visibility="visible"/> | ||||
|  | ||||
|             <LinearLayout | ||||
|                 android:id="@+id/bottomControls" | ||||
| @@ -378,6 +385,7 @@ | ||||
|                 android:background="@drawable/player_controls_bg" | ||||
|                 android:gravity="center" | ||||
|                 android:orientation="horizontal" | ||||
|                 android:paddingBottom="2dp" | ||||
|                 android:paddingLeft="16dp" | ||||
|                 android:paddingRight="16dp"> | ||||
|  | ||||
| @@ -386,7 +394,7 @@ | ||||
|                     android:layout_width="wrap_content" | ||||
|                     android:layout_height="match_parent" | ||||
|                     android:gravity="center" | ||||
|                     android:minHeight="40dp" | ||||
|                     android:minHeight="30dp" | ||||
|                     android:text="-:--:--" | ||||
|                     android:textColor="@android:color/white" | ||||
|                     tools:ignore="HardcodedText" | ||||
| @@ -431,65 +439,52 @@ | ||||
|             </LinearLayout> | ||||
|         </RelativeLayout> | ||||
|  | ||||
|         <LinearLayout | ||||
|             android:layout_width="match_parent" | ||||
|             android:layout_height="match_parent" | ||||
|             android:gravity="center" | ||||
|             android:orientation="horizontal" | ||||
|             android:weightSum="5.5"> | ||||
|             <!--tools:visibility="gone">--> | ||||
|  | ||||
|         <ImageButton | ||||
|             android:id="@+id/playPauseButton" | ||||
|             android:layout_width="100dp" | ||||
|             android:id="@+id/playPreviousButton" | ||||
|             android:layout_width="0dp" | ||||
|             android:layout_height="100dp" | ||||
|             android:layout_centerInParent="true" | ||||
|             android:layout_weight="1" | ||||
|             android:layout_marginEnd="30dp" | ||||
|             android:clickable="true" | ||||
|             android:focusable="true" | ||||
|             android:background="?attr/selectableItemBackgroundBorderless" | ||||
|             android:scaleType="fitXY" | ||||
|             android:scaleType="fitCenter" | ||||
|             android:src="@drawable/exo_controls_previous" | ||||
|             tools:ignore="ContentDescription"/> | ||||
|  | ||||
|  | ||||
|         <ImageButton | ||||
|             android:id="@+id/playPauseButton" | ||||
|             android:layout_width="0dp" | ||||
|             android:layout_height="100dp" | ||||
|             android:layout_weight="1" | ||||
|             android:background="?attr/selectableItemBackgroundBorderless" | ||||
|             android:scaleType="fitCenter" | ||||
|             android:src="@drawable/ic_pause_white" | ||||
|             tools:ignore="ContentDescription"/> | ||||
|  | ||||
|         <ImageButton | ||||
|             android:id="@+id/playPreviousButton" | ||||
|             android:layout_width="50dp" | ||||
|             android:layout_height="50dp" | ||||
|             android:layout_marginRight="30dp" | ||||
|             android:layout_marginEnd="30dp" | ||||
|             android:layout_centerInParent="true" | ||||
|             android:layout_toLeftOf="@id/playPauseButton" | ||||
|             android:layout_toStartOf="@id/playPauseButton" | ||||
|             android:clickable="true" | ||||
|             android:focusable="true" | ||||
|             android:background="?attr/selectableItemBackgroundBorderless" | ||||
|             android:scaleType="fitXY" | ||||
|             android:src="@drawable/exo_controls_previous" | ||||
|             tools:ignore="ContentDescription"/> | ||||
|  | ||||
|         <ImageButton | ||||
|             android:id="@+id/playNextButton" | ||||
|             android:layout_width="50dp" | ||||
|             android:layout_height="50dp" | ||||
|             android:layout_marginLeft="30dp" | ||||
|             android:layout_width="0dp" | ||||
|             android:layout_height="100dp" | ||||
|             android:layout_weight="1" | ||||
|             android:layout_marginStart="30dp" | ||||
|             android:layout_centerInParent="true" | ||||
|             android:layout_toRightOf="@id/playPauseButton" | ||||
|             android:layout_toEndOf="@id/playPauseButton" | ||||
|             android:clickable="true" | ||||
|             android:focusable="true" | ||||
|             android:background="?attr/selectableItemBackgroundBorderless" | ||||
|             android:scaleType="fitXY" | ||||
|             android:scaleType="fitCenter" | ||||
|             android:src="@drawable/exo_controls_next" | ||||
|             tools:ignore="ContentDescription"/> | ||||
|  | ||||
|         <Button | ||||
|             android:id="@+id/closeButton" | ||||
|             style="@style/Widget.AppCompat.Button.Borderless" | ||||
|             android:layout_width="wrap_content" | ||||
|             android:layout_height="wrap_content" | ||||
|             android:layout_below="@id/playPauseButton" | ||||
|             android:layout_centerInParent="true" | ||||
|             android:layout_marginTop="10dp" | ||||
|             android:background="?attr/selectableItemBackgroundBorderless" | ||||
|             android:clickable="true" | ||||
|             android:focusable="true" | ||||
|             android:text="@string/close" | ||||
|             android:textAllCaps="true" | ||||
|             android:textColor="@color/white" | ||||
|             android:visibility="invisible" /> | ||||
|         </LinearLayout> | ||||
|  | ||||
|     </RelativeLayout> | ||||
|  | ||||
| @@ -531,10 +526,7 @@ | ||||
|     <RelativeLayout | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="match_parent" | ||||
|         android:layout_alignParentTop="true" | ||||
|         android:layout_gravity="center" | ||||
|         android:layout_toEndOf="@+id/loading_panel" | ||||
|         android:layout_toRightOf="@+id/loading_panel" | ||||
|         tools:ignore="RtlHardcoded"> | ||||
|  | ||||
|         <RelativeLayout | ||||
| @@ -609,4 +601,42 @@ | ||||
|             tools:visibility="visible" /> | ||||
|     </RelativeLayout> | ||||
|  | ||||
|     <TextView | ||||
|         android:id="@+id/resizing_indicator" | ||||
|         android:layout_width="wrap_content" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:layout_gravity="left|top" | ||||
|         android:background="#6e000000" | ||||
|         android:gravity="center" | ||||
|         android:padding="5dp" | ||||
|         android:text="@string/popup_resizing_indicator_title" | ||||
|         android:textColor="@android:color/white" | ||||
|         android:textSize="18sp" | ||||
|         android:textStyle="bold" | ||||
|         android:visibility="gone" | ||||
|         tools:ignore="RtlHardcoded" | ||||
|         tools:visibility="gone" /> | ||||
|  | ||||
|     <View | ||||
|         android:id="@+id/closingOverlay" | ||||
|         android:layout_width="match_parent" | ||||
|         android:layout_height="match_parent" | ||||
|         android:background="#AAFF0000" | ||||
|         android:visibility="gone" /> | ||||
|  | ||||
|     <Button | ||||
|         android:id="@+id/closeButton" | ||||
|         style="@style/Widget.AppCompat.Button.Borderless" | ||||
|         android:layout_width="wrap_content" | ||||
|         android:layout_height="wrap_content" | ||||
|         android:layout_centerInParent="true" | ||||
|         android:layout_marginTop="10dp" | ||||
|         android:background="?attr/selectableItemBackgroundBorderless" | ||||
|         android:clickable="true" | ||||
|         android:focusable="true" | ||||
|         android:text="@string/close" | ||||
|         android:textAllCaps="true" | ||||
|         android:textColor="@color/white" | ||||
|         android:visibility="gone" /> | ||||
|  | ||||
| </RelativeLayout> | ||||
|   | ||||
| @@ -5,13 +5,44 @@ | ||||
| 	android:id="@+id/video_item_detail" | ||||
| 	android:layout_width="match_parent" | ||||
| 	android:layout_height="match_parent" | ||||
| 	android:background="?attr/windowBackground" | ||||
| 	android:focusableInTouchMode="true"> | ||||
|  | ||||
| 	<androidx.coordinatorlayout.widget.CoordinatorLayout | ||||
| 		android:id="@+id/detail_main_content" | ||||
| 		android:layout_width="match_parent" | ||||
| 		android:layout_height="match_parent" | ||||
| 		android:fitsSystemWindows="true"> | ||||
| 		android:layout_height="wrap_content"> | ||||
|  | ||||
| 		<!-- | ||||
| 		You may wonder why this NestedScrollView locates on top of the file. That's because BottomSheetBehavior | ||||
| 		can work well only with one element that implements nested scrolling. But we have two: | ||||
| 		one NestedScrollView for ViewPager with id = viewpager here | ||||
| 		second ViewPager with id = playQueue inside activity_main_player.xml. | ||||
| 		By placing this NestedScrollView on top of the file we are getting this ViewPager working --> | ||||
|  | ||||
| 		<androidx.core.widget.NestedScrollView | ||||
| 				android:layout_width="match_parent" | ||||
| 				android:layout_height="match_parent" | ||||
| 				app:layout_behavior="@string/appbar_scrolling_view_behavior" | ||||
| 				android:fillViewport="true"> | ||||
|  | ||||
| 			<androidx.viewpager.widget.ViewPager | ||||
| 					android:id="@+id/viewpager" | ||||
| 					android:layout_width="match_parent" | ||||
| 					android:layout_height="match_parent" > | ||||
|  | ||||
| 				<com.google.android.material.tabs.TabLayout | ||||
| 						android:id="@+id/tablayout" | ||||
| 						android:layout_width="match_parent" | ||||
| 						android:layout_height="30dp" | ||||
| 						android:layout_gravity="bottom|center" | ||||
| 						app:tabBackground="@drawable/tab_selector" | ||||
| 						app:tabGravity="center" | ||||
| 						app:tabIndicatorHeight="0dp"/> | ||||
|  | ||||
| 			</androidx.viewpager.widget.ViewPager> | ||||
|  | ||||
| 		</androidx.core.widget.NestedScrollView> | ||||
|  | ||||
| 		<com.google.android.material.appbar.AppBarLayout | ||||
| 			android:id="@+id/appbarlayout" | ||||
| @@ -140,6 +171,13 @@ | ||||
| 						tools:progress="40" | ||||
| 						tools:visibility="visible" /> | ||||
|  | ||||
| 					<!-- Player will be inserted here in realtime --> | ||||
| 					<FrameLayout | ||||
| 						android:id="@+id/player_placeholder" | ||||
| 						android:layout_width="match_parent" | ||||
| 						android:layout_height="match_parent" | ||||
| 					/> | ||||
|  | ||||
| 				</FrameLayout> | ||||
|  | ||||
| 			</com.google.android.material.appbar.CollapsingToolbarLayout> | ||||
| @@ -149,7 +187,6 @@ | ||||
| 				android:id="@+id/detail_content_root_layout" | ||||
| 				android:layout_width="match_parent" | ||||
| 				android:layout_height="wrap_content" | ||||
| 				android:background="?android:windowBackground" | ||||
| 				app:layout_scrollFlags="scroll"> | ||||
|  | ||||
| 				<!-- TITLE --> | ||||
| @@ -504,25 +541,106 @@ | ||||
|  | ||||
| 		</com.google.android.material.appbar.AppBarLayout> | ||||
|  | ||||
|  | ||||
| 		<androidx.viewpager.widget.ViewPager | ||||
| 			android:id="@+id/viewpager" | ||||
| 			android:layout_width="match_parent" | ||||
| 			android:layout_height="match_parent" | ||||
| 			app:layout_behavior="@string/appbar_scrolling_view_behavior" /> | ||||
|  | ||||
|  | ||||
| 		<com.google.android.material.tabs.TabLayout | ||||
| 			android:id="@+id/tablayout" | ||||
| 			android:layout_width="match_parent" | ||||
| 			android:layout_height="wrap_content" | ||||
| 			android:layout_gravity="bottom|center" | ||||
| 			android:background="@color/transparent_background_color" | ||||
| 			app:tabBackground="@drawable/tab_selector" | ||||
| 			app:tabGravity="center" | ||||
| 			app:tabIndicatorHeight="0dp"> | ||||
|  | ||||
| 		</com.google.android.material.tabs.TabLayout> | ||||
|  | ||||
| 	</androidx.coordinatorlayout.widget.CoordinatorLayout> | ||||
|  | ||||
| 	<RelativeLayout | ||||
| 		android:id="@+id/overlay_layout" | ||||
| 		android:layout_width="match_parent" | ||||
| 		android:layout_height="match_parent" | ||||
| 		android:alpha="0" | ||||
| 		tools:alpha="1" | ||||
| 		android:paddingLeft="@dimen/video_item_search_padding" | ||||
| 		android:paddingRight="@dimen/video_item_search_padding" | ||||
| 		android:background="?attr/windowBackground" > | ||||
|  | ||||
| 		<ImageButton | ||||
| 			android:id="@+id/overlay_thumbnail" | ||||
| 			android:layout_width="50dp" | ||||
| 			android:layout_height="60dp" | ||||
| 			android:layout_alignParentStart="true" | ||||
| 			android:scaleType="fitCenter" | ||||
| 			android:gravity="center_vertical" | ||||
| 			android:contentDescription="@string/list_thumbnail_view_description" | ||||
| 			android:src="@drawable/dummy_thumbnail" | ||||
| 			android:background="@color/transparent_background_color"/> | ||||
|  | ||||
| 		<LinearLayout | ||||
| 			android:id="@+id/overlay_metadata_layout" | ||||
| 			android:layout_width="match_parent" | ||||
| 			android:layout_height="60dp" | ||||
| 			android:gravity="center_vertical" | ||||
| 			android:orientation="vertical" | ||||
| 			android:paddingLeft="@dimen/video_item_search_padding" | ||||
| 			android:paddingRight="@dimen/video_item_search_padding" | ||||
| 			android:clickable="true" | ||||
| 			android:focusable="true" | ||||
| 			android:layout_toEndOf="@+id/overlay_thumbnail" | ||||
| 			android:layout_toStartOf="@+id/overlay_buttons_layout" | ||||
| 			tools:ignore="RtlHardcoded"> | ||||
|  | ||||
| 			<TextView | ||||
| 				android:id="@+id/overlay_title_text_view" | ||||
| 				android:layout_width="match_parent" | ||||
| 				android:layout_height="wrap_content" | ||||
| 				android:ellipsize="marquee" | ||||
| 				android:fadingEdge="horizontal" | ||||
| 				android:marqueeRepeatLimit="marquee_forever" | ||||
| 				android:scrollHorizontally="true" | ||||
| 				android:singleLine="true" | ||||
| 				android:textAppearance="?android:attr/textAppearanceLarge" | ||||
| 				android:textSize="@dimen/video_item_search_title_text_size" | ||||
| 				tools:ignore="RtlHardcoded" | ||||
| 				tools:text="The Video Title LONG very LONVideo Title LONG very LONG"/> | ||||
|  | ||||
| 			<TextView | ||||
| 				android:id="@+id/overlay_channel_text_view" | ||||
| 				android:layout_width="match_parent" | ||||
| 				android:layout_height="wrap_content" | ||||
| 				android:ellipsize="marquee" | ||||
| 				android:fadingEdge="horizontal" | ||||
| 				android:marqueeRepeatLimit="marquee_forever" | ||||
| 				android:scrollHorizontally="true" | ||||
| 				android:singleLine="true" | ||||
| 				android:textAppearance="?android:attr/textAppearanceSmall" | ||||
| 				android:textSize="@dimen/video_item_search_uploader_text_size" | ||||
| 				tools:text="The Video Artist  LONG very LONG very Long"/> | ||||
|  | ||||
| 		</LinearLayout> | ||||
|  | ||||
| 		<LinearLayout | ||||
| 			android:id="@+id/overlay_buttons_layout" | ||||
| 			android:layout_width="wrap_content" | ||||
| 			android:layout_height="60dp" | ||||
| 			android:gravity="center_vertical" | ||||
| 			android:paddingLeft="@dimen/video_item_search_padding" | ||||
| 			android:layout_alignParentEnd="true" | ||||
| 			tools:ignore="RtlHardcoded"> | ||||
|  | ||||
| 		<ImageButton | ||||
| 			android:id="@+id/overlay_play_pause_button" | ||||
| 			android:layout_width="36dp" | ||||
| 			android:layout_height="36dp" | ||||
| 			android:layout_marginLeft="2dp" | ||||
| 			android:layout_marginRight="2dp" | ||||
| 			android:padding="10dp" | ||||
| 			android:scaleType="center" | ||||
| 			android:src="@drawable/ic_pause_white_24dp" | ||||
| 			android:background="?attr/selectableItemBackground" | ||||
| 			tools:ignore="ContentDescription,RtlHardcoded"/> | ||||
|  | ||||
| 		<ImageButton | ||||
| 			android:id="@+id/overlay_close_button" | ||||
| 			android:layout_width="36dp" | ||||
| 			android:layout_height="36dp" | ||||
| 			android:layout_marginLeft="2dp" | ||||
| 			android:padding="10dp" | ||||
| 			android:scaleType="center" | ||||
| 			android:src="?attr/close" | ||||
| 			android:background="?attr/selectableItemBackground" | ||||
| 			tools:ignore="ContentDescription,RtlHardcoded"/> | ||||
|  | ||||
| 		</LinearLayout> | ||||
|  | ||||
| 	</RelativeLayout> | ||||
|  | ||||
| </FrameLayout> | ||||
|   | ||||
| @@ -7,4 +7,9 @@ | ||||
|         android:orderInCategory="1999" | ||||
|         android:title="@string/switch_to_popup" | ||||
|         app:showAsAction="never"/> | ||||
|  | ||||
|     <item android:id="@+id/action_switch_background" | ||||
|           android:orderInCategory="999" | ||||
|           android:title="@string/switch_to_background" | ||||
|           app:showAsAction="never"/> | ||||
| </menu> | ||||
|   | ||||
| @@ -48,6 +48,7 @@ | ||||
|     <dimen name="video_item_detail_like_image_height">18sp</dimen> | ||||
|     <dimen name="video_item_detail_like_image_width">18sp</dimen> | ||||
|     <dimen name="channel_avatar_size">70dp</dimen> | ||||
|     <dimen name="mini_player_height">60dp</dimen> | ||||
|     <!-- Paddings & Margins --> | ||||
|     <dimen name="video_item_detail_like_margin">5dp</dimen> | ||||
|     <dimen name="video_item_detail_error_panel_margin">50dp</dimen> | ||||
|   | ||||
| @@ -21,7 +21,6 @@ | ||||
|  | ||||
|     <SwitchPreference | ||||
|         android:defaultValue="true" | ||||
|         android:dependency="@string/enable_playback_resume_key" | ||||
|         android:key="@string/enable_playback_state_lists_key" | ||||
|         android:summary="@string/enable_playback_state_lists_summary" | ||||
|         android:title="@string/enable_playback_state_lists_title" | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Avently
					Avently