/* * Created by Christian Schabesberger on 02.08.16. *

* Copyright (C) Christian Schabesberger 2016 * DownloadActivity.java is part of NewPipe. *

* NewPipe is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. *

* NewPipe is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. *

* You should have received a copy of the GNU General Public License * along with NewPipe. If not, see . */ package org.schabi.newpipe; import static org.schabi.newpipe.util.Localization.assureCorrectAppLanguage; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.util.Log; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.FrameLayout; import android.widget.Spinner; import androidx.annotation.NonNull; import androidx.appcompat.app.ActionBar; import androidx.appcompat.app.ActionBarDrawerToggle; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.view.GravityCompat; import androidx.drawerlayout.widget.DrawerLayout; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; import androidx.preference.PreferenceManager; import com.google.android.material.bottomsheet.BottomSheetBehavior; import org.schabi.newpipe.databinding.ActivityMainBinding; import org.schabi.newpipe.databinding.DrawerHeaderBinding; import org.schabi.newpipe.databinding.DrawerLayoutBinding; import org.schabi.newpipe.databinding.InstanceSpinnerLayoutBinding; import org.schabi.newpipe.databinding.ToolbarLayoutBinding; import org.schabi.newpipe.error.ErrorUtil; import org.schabi.newpipe.extractor.NewPipe; import org.schabi.newpipe.extractor.StreamingService; import org.schabi.newpipe.extractor.exceptions.ExtractionException; import org.schabi.newpipe.extractor.services.peertube.PeertubeInstance; 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.local.feed.notifications.NotificationWorker; import org.schabi.newpipe.player.Player; import org.schabi.newpipe.player.event.OnKeyDownListener; import org.schabi.newpipe.player.helper.PlayerHolder; import org.schabi.newpipe.player.playqueue.PlayQueue; import org.schabi.newpipe.util.Constants; import org.schabi.newpipe.util.DeviceUtils; import org.schabi.newpipe.util.KioskTranslator; import org.schabi.newpipe.util.Localization; import org.schabi.newpipe.util.NavigationHelper; import org.schabi.newpipe.util.PeertubeHelper; import org.schabi.newpipe.util.PermissionHelper; import org.schabi.newpipe.util.SerializedCache; import org.schabi.newpipe.util.ServiceHelper; import org.schabi.newpipe.util.StateSaver; import org.schabi.newpipe.util.ThemeHelper; import org.schabi.newpipe.views.FocusOverlayView; import java.util.ArrayList; import java.util.List; import java.util.Objects; public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; @SuppressWarnings("ConstantConditions") public static final boolean DEBUG = !BuildConfig.BUILD_TYPE.equals("release"); private ActivityMainBinding mainBinding; private DrawerHeaderBinding drawerHeaderBinding; private DrawerLayoutBinding drawerLayoutBinding; private ToolbarLayoutBinding toolbarLayoutBinding; private ActionBarDrawerToggle toggle; private boolean servicesShown = false; private BroadcastReceiver broadcastReceiver; private static final int ITEM_ID_SUBSCRIPTIONS = -1; private static final int ITEM_ID_FEED = -2; private static final int ITEM_ID_BOOKMARKS = -3; private static final int ITEM_ID_DOWNLOADS = -4; private static final int ITEM_ID_HISTORY = -5; private static final int ITEM_ID_SETTINGS = 0; private static final int ITEM_ID_ABOUT = 1; private static final int ORDER = 0; /*////////////////////////////////////////////////////////////////////////// // Activity's LifeCycle //////////////////////////////////////////////////////////////////////////*/ @Override protected void onCreate(final Bundle savedInstanceState) { if (DEBUG) { Log.d(TAG, "onCreate() called with: " + "savedInstanceState = [" + savedInstanceState + "]"); } ThemeHelper.setDayNightMode(this); ThemeHelper.setTheme(this, ServiceHelper.getSelectedServiceId(this)); assureCorrectAppLanguage(this); super.onCreate(savedInstanceState); mainBinding = ActivityMainBinding.inflate(getLayoutInflater()); drawerLayoutBinding = mainBinding.drawerLayout; drawerHeaderBinding = DrawerHeaderBinding.bind(drawerLayoutBinding.navigation .getHeaderView(0)); toolbarLayoutBinding = mainBinding.toolbarLayout; setContentView(mainBinding.getRoot()); if (getSupportFragmentManager().getBackStackEntryCount() == 0) { initFragments(); } setSupportActionBar(toolbarLayoutBinding.toolbar); try { setupDrawer(); } catch (final Exception e) { ErrorUtil.showUiErrorSnackbar(this, "Setting up drawer", e); } if (DeviceUtils.isTv(this)) { FocusOverlayView.setupFocusObserver(this); } openMiniPlayerUponPlayerStarted(); if (PermissionHelper.checkPostNotificationsPermission(this, PermissionHelper.POST_NOTIFICATIONS_REQUEST_CODE)) { // Schedule worker for checking for new streams and creating corresponding notifications // if this is enabled by the user. NotificationWorker.initialize(this); } } @Override protected void onPostCreate(final Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); final App app = App.getApp(); final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(app); if (prefs.getBoolean(app.getString(R.string.update_app_key), true)) { // Start the worker which is checking all conditions // and eventually searching for a new version. NewVersionWorker.enqueueNewVersionCheckingWork(app, false); } } private void setupDrawer() throws ExtractionException { addDrawerMenuForCurrentService(); toggle = new ActionBarDrawerToggle(this, mainBinding.getRoot(), toolbarLayoutBinding.toolbar, R.string.drawer_open, R.string.drawer_close); toggle.syncState(); mainBinding.getRoot().addDrawerListener(toggle); mainBinding.getRoot().addDrawerListener(new DrawerLayout.SimpleDrawerListener() { private int lastService; @Override public void onDrawerOpened(final View drawerView) { lastService = ServiceHelper.getSelectedServiceId(MainActivity.this); } @Override public void onDrawerClosed(final View drawerView) { if (servicesShown) { toggleServices(); } if (lastService != ServiceHelper.getSelectedServiceId(MainActivity.this)) { ActivityCompat.recreate(MainActivity.this); } } }); drawerLayoutBinding.navigation.setNavigationItemSelectedListener(this::drawerItemSelected); setupDrawerHeader(); } /** * Builds the drawer menu for the current service. * * @throws ExtractionException if the service didn't provide available kiosks */ private void addDrawerMenuForCurrentService() throws ExtractionException { //Tabs final int currentServiceId = ServiceHelper.getSelectedServiceId(this); final StreamingService service = NewPipe.getService(currentServiceId); int kioskId = 0; for (final String ks : service.getKioskList().getAvailableKiosks()) { drawerLayoutBinding.navigation.getMenu() .add(R.id.menu_tabs_group, kioskId, 0, KioskTranslator .getTranslatedKioskName(ks, this)) .setIcon(KioskTranslator.getKioskIcon(ks)); kioskId++; } drawerLayoutBinding.navigation.getMenu() .add(R.id.menu_tabs_group, ITEM_ID_SUBSCRIPTIONS, ORDER, R.string.tab_subscriptions) .setIcon(R.drawable.ic_tv); drawerLayoutBinding.navigation.getMenu() .add(R.id.menu_tabs_group, ITEM_ID_FEED, ORDER, R.string.fragment_feed_title) .setIcon(R.drawable.ic_subscriptions); drawerLayoutBinding.navigation.getMenu() .add(R.id.menu_tabs_group, ITEM_ID_BOOKMARKS, ORDER, R.string.tab_bookmarks) .setIcon(R.drawable.ic_bookmark); drawerLayoutBinding.navigation.getMenu() .add(R.id.menu_tabs_group, ITEM_ID_DOWNLOADS, ORDER, R.string.downloads) .setIcon(R.drawable.ic_file_download); drawerLayoutBinding.navigation.getMenu() .add(R.id.menu_tabs_group, ITEM_ID_HISTORY, ORDER, R.string.action_history) .setIcon(R.drawable.ic_history); //Settings and About drawerLayoutBinding.navigation.getMenu() .add(R.id.menu_options_about_group, ITEM_ID_SETTINGS, ORDER, R.string.settings) .setIcon(R.drawable.ic_settings); drawerLayoutBinding.navigation.getMenu() .add(R.id.menu_options_about_group, ITEM_ID_ABOUT, ORDER, R.string.tab_about) .setIcon(R.drawable.ic_info_outline); } private boolean drawerItemSelected(final MenuItem item) { switch (item.getGroupId()) { case R.id.menu_services_group: changeService(item); break; case R.id.menu_tabs_group: try { tabSelected(item); } catch (final Exception e) { ErrorUtil.showUiErrorSnackbar(this, "Selecting main page tab", e); } break; case R.id.menu_options_about_group: optionsAboutSelected(item); break; default: return false; } mainBinding.getRoot().closeDrawers(); return true; } private void changeService(final MenuItem item) { drawerLayoutBinding.navigation.getMenu() .getItem(ServiceHelper.getSelectedServiceId(this)) .setChecked(false); ServiceHelper.setSelectedServiceId(this, item.getItemId()); drawerLayoutBinding.navigation.getMenu() .getItem(ServiceHelper.getSelectedServiceId(this)) .setChecked(true); } private void tabSelected(final MenuItem item) throws ExtractionException { switch (item.getItemId()) { case ITEM_ID_SUBSCRIPTIONS: NavigationHelper.openSubscriptionFragment(getSupportFragmentManager()); break; case ITEM_ID_FEED: NavigationHelper.openFeedFragment(getSupportFragmentManager()); break; case ITEM_ID_BOOKMARKS: NavigationHelper.openBookmarksFragment(getSupportFragmentManager()); break; case ITEM_ID_DOWNLOADS: NavigationHelper.openDownloads(this); break; case ITEM_ID_HISTORY: NavigationHelper.openStatisticFragment(getSupportFragmentManager()); break; default: final int currentServiceId = ServiceHelper.getSelectedServiceId(this); final StreamingService service = NewPipe.getService(currentServiceId); String serviceName = ""; int kioskId = 0; for (final String ks : service.getKioskList().getAvailableKiosks()) { if (kioskId == item.getItemId()) { serviceName = ks; } kioskId++; } NavigationHelper.openKioskFragment(getSupportFragmentManager(), currentServiceId, serviceName); break; } } private void optionsAboutSelected(final MenuItem item) { switch (item.getItemId()) { case ITEM_ID_SETTINGS: NavigationHelper.openSettings(this); break; case ITEM_ID_ABOUT: NavigationHelper.openAbout(this); break; } } private void setupDrawerHeader() { drawerHeaderBinding.drawerHeaderActionButton.setOnClickListener(view -> toggleServices()); // If the current app name is bigger than the default "NewPipe" (7 chars), // let the text view grow a little more as well. if (getString(R.string.app_name).length() > "NewPipe".length()) { final ViewGroup.LayoutParams layoutParams = drawerHeaderBinding.drawerHeaderNewpipeTitle.getLayoutParams(); layoutParams.width = ViewGroup.LayoutParams.WRAP_CONTENT; drawerHeaderBinding.drawerHeaderNewpipeTitle.setLayoutParams(layoutParams); drawerHeaderBinding.drawerHeaderNewpipeTitle.setMaxLines(2); drawerHeaderBinding.drawerHeaderNewpipeTitle.setMinWidth(getResources() .getDimensionPixelSize(R.dimen.drawer_header_newpipe_title_default_width)); drawerHeaderBinding.drawerHeaderNewpipeTitle.setMaxWidth(getResources() .getDimensionPixelSize(R.dimen.drawer_header_newpipe_title_max_width)); } } private void toggleServices() { servicesShown = !servicesShown; drawerLayoutBinding.navigation.getMenu().removeGroup(R.id.menu_services_group); drawerLayoutBinding.navigation.getMenu().removeGroup(R.id.menu_tabs_group); drawerLayoutBinding.navigation.getMenu().removeGroup(R.id.menu_options_about_group); // Show up or down arrow drawerHeaderBinding.drawerArrow.setImageResource( servicesShown ? R.drawable.ic_arrow_drop_up : R.drawable.ic_arrow_drop_down); if (servicesShown) { showServices(); } else { try { addDrawerMenuForCurrentService(); } catch (final Exception e) { ErrorUtil.showUiErrorSnackbar(this, "Showing main page tabs", e); } } } private void showServices() { for (final StreamingService s : NewPipe.getServices()) { final String title = s.getServiceInfo().getName(); final MenuItem menuItem = drawerLayoutBinding.navigation.getMenu() .add(R.id.menu_services_group, s.getServiceId(), ORDER, title) .setIcon(ServiceHelper.getIcon(s.getServiceId())); // peertube specifics if (s.getServiceId() == 3) { enhancePeertubeMenu(menuItem); } } drawerLayoutBinding.navigation.getMenu() .getItem(ServiceHelper.getSelectedServiceId(this)) .setChecked(true); } private void enhancePeertubeMenu(final MenuItem menuItem) { final PeertubeInstance currentInstance = PeertubeHelper.getCurrentInstance(); menuItem.setTitle(currentInstance.getName()); final Spinner spinner = InstanceSpinnerLayoutBinding.inflate(LayoutInflater.from(this)) .getRoot(); final List instances = PeertubeHelper.getInstanceList(this); final List items = new ArrayList<>(); int defaultSelect = 0; for (final PeertubeInstance instance : instances) { items.add(instance.getName()); if (instance.getUrl().equals(currentInstance.getUrl())) { defaultSelect = items.size() - 1; } } final ArrayAdapter adapter = new ArrayAdapter<>(this, R.layout.instance_spinner_item, items); adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item); spinner.setAdapter(adapter); spinner.setSelection(defaultSelect, false); spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(final AdapterView parent, final View view, final int position, final long id) { final PeertubeInstance newInstance = instances.get(position); if (newInstance.getUrl().equals(PeertubeHelper.getCurrentInstance().getUrl())) { return; } PeertubeHelper.selectInstance(newInstance, getApplicationContext()); changeService(menuItem); mainBinding.getRoot().closeDrawers(); new Handler(Looper.getMainLooper()).postDelayed(() -> { getSupportFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE); ActivityCompat.recreate(MainActivity.this); }, 300); } @Override public void onNothingSelected(final AdapterView parent) { } }); menuItem.setActionView(spinner); } @Override protected void onDestroy() { super.onDestroy(); if (!isChangingConfigurations()) { StateSaver.clearStateFiles(); } if (broadcastReceiver != null) { unregisterReceiver(broadcastReceiver); } } @Override protected void onResume() { assureCorrectAppLanguage(this); // Change the date format to match the selected language on resume Localization.initPrettyTime(Localization.resolvePrettyTime(getApplicationContext())); super.onResume(); // Close drawer on return, and don't show animation, // so it looks like the drawer isn't open when the user returns to MainActivity mainBinding.getRoot().closeDrawer(GravityCompat.START, false); try { final int selectedServiceId = ServiceHelper.getSelectedServiceId(this); final String selectedServiceName = NewPipe.getService(selectedServiceId) .getServiceInfo().getName(); drawerHeaderBinding.drawerHeaderServiceView.setText(selectedServiceName); drawerHeaderBinding.drawerHeaderServiceIcon.setImageResource(ServiceHelper .getIcon(selectedServiceId)); drawerHeaderBinding.drawerHeaderServiceView.post(() -> drawerHeaderBinding .drawerHeaderServiceView.setSelected(true)); drawerHeaderBinding.drawerHeaderActionButton.setContentDescription( getString(R.string.drawer_header_description) + selectedServiceName); } catch (final Exception e) { ErrorUtil.showUiErrorSnackbar(this, "Setting up service toggle", e); } final SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); if (sharedPreferences.getBoolean(Constants.KEY_THEME_CHANGE, false)) { if (DEBUG) { Log.d(TAG, "Theme has changed, recreating activity..."); } sharedPreferences.edit().putBoolean(Constants.KEY_THEME_CHANGE, false).apply(); ActivityCompat.recreate(this); } if (sharedPreferences.getBoolean(Constants.KEY_MAIN_PAGE_CHANGE, false)) { if (DEBUG) { Log.d(TAG, "main page has changed, recreating main fragment..."); } sharedPreferences.edit().putBoolean(Constants.KEY_MAIN_PAGE_CHANGE, false).apply(); NavigationHelper.openMainActivity(this); } final boolean isHistoryEnabled = sharedPreferences.getBoolean( getString(R.string.enable_watch_history_key), true); drawerLayoutBinding.navigation.getMenu().findItem(ITEM_ID_HISTORY) .setVisible(isHistoryEnabled); } @Override protected void onNewIntent(final Intent intent) { if (DEBUG) { Log.d(TAG, "onNewIntent() called with: intent = [" + intent + "]"); } if (intent != null) { // Return if launched from a launcher (e.g. Nova Launcher, Pixel Launcher ...) // to not destroy the already created backstack final String action = intent.getAction(); if ((action != null && action.equals(Intent.ACTION_MAIN)) && intent.hasCategory(Intent.CATEGORY_LAUNCHER)) { return; } } super.onNewIntent(intent); setIntent(intent); handleIntent(intent); } @Override public boolean onKeyDown(final int keyCode, final KeyEvent event) { final Fragment fragment = getSupportFragmentManager() .findFragmentById(R.id.fragment_player_holder); if (fragment instanceof OnKeyDownListener && !bottomSheetHiddenOrCollapsed()) { // Provide keyDown event to fragment which then sends this event // to the main player service return ((OnKeyDownListener) fragment).onKeyDown(keyCode) || super.onKeyDown(keyCode, event); } return super.onKeyDown(keyCode, event); } @Override public void onBackPressed() { if (DEBUG) { Log.d(TAG, "onBackPressed() called"); } if (DeviceUtils.isTv(this)) { if (mainBinding.getRoot().isDrawerOpen(drawerLayoutBinding.navigation)) { mainBinding.getRoot().closeDrawers(); return; } } // In case bottomSheet is not visible on the screen or collapsed we can assume that the user // interacts with a fragment inside fragment_holder so all back presses should be // handled by it if (bottomSheetHiddenOrCollapsed()) { final 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 { final 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.from(mainBinding.fragmentPlayerHolder) .setState(BottomSheetBehavior.STATE_COLLAPSED); } return; } } if (getSupportFragmentManager().getBackStackEntryCount() == 1) { finish(); } else { super.onBackPressed(); } } @Override public void onRequestPermissionsResult(final int requestCode, @NonNull final String[] permissions, @NonNull final int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); for (final int i : grantResults) { if (i == PackageManager.PERMISSION_DENIED) { return; } } switch (requestCode) { case PermissionHelper.DOWNLOADS_REQUEST_CODE: NavigationHelper.openDownloads(this); break; case PermissionHelper.DOWNLOAD_DIALOG_REQUEST_CODE: final Fragment fragment = getSupportFragmentManager() .findFragmentById(R.id.fragment_player_holder); if (fragment instanceof VideoDetailFragment) { ((VideoDetailFragment) fragment).openDownloadDialog(); } break; case PermissionHelper.POST_NOTIFICATIONS_REQUEST_CODE: NotificationWorker.initialize(this); break; } } /** * Implement the following diagram behavior for the up button: *

     *              +---------------+
     *              |  Main Screen  +----+
     *              +-------+-------+    |
     *                      |            |
     *                      ▲ Up         | Search Button
     *                      |            |
     *                 +----+-----+      |
     *    +------------+  Search  |◄-----+
     *    |            +----+-----+
     *    |   Open          |
     *    |  something      ▲ Up
     *    |                 |
     *    |    +------------+-------------+
     *    |    |                          |
     *    |    |  Video    <->  Channel   |
     *    +---►|  Channel  <->  Playlist  |
     *         |  Video    <->  ....      |
     *         |                          |
     *         +--------------------------+
     * 
*/ private void onHomeButtonPressed() { // If search fragment wasn't found in the backstack... if (!NavigationHelper.tryGotoSearchFragment(getSupportFragmentManager())) { // ...go to the main fragment NavigationHelper.gotoMainFragment(getSupportFragmentManager()); } } /*////////////////////////////////////////////////////////////////////////// // Menu //////////////////////////////////////////////////////////////////////////*/ @Override public boolean onCreateOptionsMenu(final Menu menu) { if (DEBUG) { Log.d(TAG, "onCreateOptionsMenu() called with: menu = [" + menu + "]"); } super.onCreateOptionsMenu(menu); final Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment_holder); if (!(fragment instanceof SearchFragment)) { toolbarLayoutBinding.toolbarSearchContainer.getRoot().setVisibility(View.GONE); } final ActionBar actionBar = getSupportActionBar(); if (actionBar != null) { actionBar.setDisplayHomeAsUpEnabled(false); } updateDrawerNavigation(); return true; } @Override public boolean onOptionsItemSelected(@NonNull final MenuItem item) { if (DEBUG) { Log.d(TAG, "onOptionsItemSelected() called with: item = [" + item + "]"); } if (item.getItemId() == android.R.id.home) { onHomeButtonPressed(); return true; } return super.onOptionsItemSelected(item); } /*////////////////////////////////////////////////////////////////////////// // Init //////////////////////////////////////////////////////////////////////////*/ private void initFragments() { 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()); } } /*////////////////////////////////////////////////////////////////////////// // Utils //////////////////////////////////////////////////////////////////////////*/ private void updateDrawerNavigation() { if (getSupportActionBar() == null) { return; } final Fragment fragment = getSupportFragmentManager() .findFragmentById(R.id.fragment_holder); if (fragment instanceof MainFragment) { getSupportActionBar().setDisplayHomeAsUpEnabled(false); if (toggle != null) { toggle.syncState(); toolbarLayoutBinding.toolbar.setNavigationOnClickListener(v -> mainBinding.getRoot() .open()); mainBinding.getRoot().setDrawerLockMode(DrawerLayout.LOCK_MODE_UNDEFINED); } } else { mainBinding.getRoot().setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED); getSupportActionBar().setDisplayHomeAsUpEnabled(true); toolbarLayoutBinding.toolbar.setNavigationOnClickListener(v -> onHomeButtonPressed()); } } private void handleIntent(final Intent intent) { try { if (DEBUG) { Log.d(TAG, "handleIntent() called with: intent = [" + intent + "]"); } if (intent.hasExtra(Constants.KEY_LINK_TYPE)) { final String url = intent.getStringExtra(Constants.KEY_URL); final int serviceId = intent.getIntExtra(Constants.KEY_SERVICE_ID, 0); String title = intent.getStringExtra(Constants.KEY_TITLE); if (title == null) { title = ""; } final StreamingService.LinkType linkType = ((StreamingService.LinkType) intent .getSerializableExtra(Constants.KEY_LINK_TYPE)); assert linkType != null; switch (linkType) { case STREAM: final String intentCacheKey = intent.getStringExtra( Player.PLAY_QUEUE_KEY); final PlayQueue playQueue = intentCacheKey != null ? SerializedCache.getInstance() .take(intentCacheKey, PlayQueue.class) : null; final boolean switchingPlayers = intent.getBooleanExtra( VideoDetailFragment.KEY_SWITCHING_PLAYERS, false); NavigationHelper.openVideoDetailFragment( getApplicationContext(), getSupportFragmentManager(), serviceId, url, title, playQueue, switchingPlayers); break; case CHANNEL: NavigationHelper.openChannelFragment(getSupportFragmentManager(), serviceId, url, title); break; case PLAYLIST: NavigationHelper.openPlaylistFragment(getSupportFragmentManager(), serviceId, url, title); break; } } else if (intent.hasExtra(Constants.KEY_OPEN_SEARCH)) { String searchString = intent.getStringExtra(Constants.KEY_SEARCH_STRING); if (searchString == null) { searchString = ""; } final int serviceId = intent.getIntExtra(Constants.KEY_SERVICE_ID, 0); NavigationHelper.openSearchFragment( getSupportFragmentManager(), serviceId, searchString); } else { NavigationHelper.gotoMainFragment(getSupportFragmentManager()); } } catch (final Exception e) { ErrorUtil.showUiErrorSnackbar(this, "Handling intent", e); } } private void openMiniPlayerIfMissing() { final Fragment fragmentPlayer = getSupportFragmentManager() .findFragmentById(R.id.fragment_player_holder); if (fragmentPlayer == null) { // We still don't have a fragment attached to the activity. It can happen when a user // started popup or background players without opening a stream inside the fragment. // Adding it in a collapsed state (only mini player will be visible). NavigationHelper.showMiniPlayer(getSupportFragmentManager()); } } private void openMiniPlayerUponPlayerStarted() { if (getIntent().getSerializableExtra(Constants.KEY_LINK_TYPE) == StreamingService.LinkType.STREAM) { // handleIntent() already takes care of opening video detail fragment // due to an intent containing a STREAM link return; } if (PlayerHolder.getInstance().isPlayerOpen()) { // if the player is already open, no need for a broadcast receiver openMiniPlayerIfMissing(); } else { // listen for player start intent being sent around broadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(final Context context, final Intent intent) { if (Objects.equals(intent.getAction(), VideoDetailFragment.ACTION_PLAYER_STARTED)) { openMiniPlayerIfMissing(); // At this point the player is added 100%, we can unregister. Other actions // are useless since the fragment will not be removed after that. unregisterReceiver(broadcastReceiver); broadcastReceiver = null; } } }; final IntentFilter intentFilter = new IntentFilter(); intentFilter.addAction(VideoDetailFragment.ACTION_PLAYER_STARTED); registerReceiver(broadcastReceiver, intentFilter); } } private boolean bottomSheetHiddenOrCollapsed() { final BottomSheetBehavior bottomSheetBehavior = BottomSheetBehavior.from(mainBinding.fragmentPlayerHolder); final int sheetState = bottomSheetBehavior.getState(); return sheetState == BottomSheetBehavior.STATE_HIDDEN || sheetState == BottomSheetBehavior.STATE_COLLAPSED; } }