diff options
author | H. Lehmann <ByteHamster@users.noreply.github.com> | 2020-03-19 21:56:39 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-03-19 21:56:39 +0100 |
commit | d7cfa07c449666caa321df3c0894e5de816ffafa (patch) | |
tree | 366955c8b62de0be1805389be54ece561b29dda1 | |
parent | 0ed4f808c3877506b3db0f2279fd4168b0cdec02 (diff) | |
parent | 260bb8f5ccca9136a1520443252ad352f3d0b6ac (diff) | |
download | AntennaPod-d7cfa07c449666caa321df3c0894e5de816ffafa.zip |
Merge pull request #3943 from ByteHamster/nav-list-fragment
Nav list fragment
13 files changed, 511 insertions, 742 deletions
diff --git a/app/src/androidTest/java/de/test/antennapod/EspressoTestUtils.java b/app/src/androidTest/java/de/test/antennapod/EspressoTestUtils.java index a9d8483fa..225f07dfd 100644 --- a/app/src/androidTest/java/de/test/antennapod/EspressoTestUtils.java +++ b/app/src/androidTest/java/de/test/antennapod/EspressoTestUtils.java @@ -20,6 +20,7 @@ import de.danoeh.antennapod.core.service.download.DownloadService; import de.danoeh.antennapod.core.service.playback.PlaybackService; import de.danoeh.antennapod.core.storage.PodDBAdapter; import de.danoeh.antennapod.dialog.RatingDialog; +import de.danoeh.antennapod.fragment.NavDrawerFragment; import org.awaitility.Awaitility; import org.awaitility.core.ConditionTimeoutException; import org.hamcrest.Matcher; @@ -131,9 +132,10 @@ public class EspressoTestUtils { } public static void setLastNavFragment(String tag) { - InstrumentationRegistry.getTargetContext().getSharedPreferences(MainActivity.PREF_NAME, Context.MODE_PRIVATE) + InstrumentationRegistry.getTargetContext().getSharedPreferences( + NavDrawerFragment.PREF_NAME, Context.MODE_PRIVATE) .edit() - .putString(MainActivity.PREF_LAST_FRAGMENT_TAG, tag) + .putString(NavDrawerFragment.PREF_LAST_FRAGMENT_TAG, tag) .commit(); } diff --git a/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java b/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java index d0b2cb4b4..72e4c29d9 100644 --- a/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java +++ b/app/src/androidTest/java/de/test/antennapod/ui/MainActivityTest.java @@ -99,6 +99,7 @@ public class MainActivityTest { solo.goBackToActivity(MainActivity.class.getSimpleName()); solo.goBack(); + solo.goBack(); assertEquals(solo.getString(R.string.subscriptions_label), getActionbarTitle()); } @@ -111,6 +112,7 @@ public class MainActivityTest { onView(withText(R.string.back_button_open_drawer)).perform(click()); solo.goBackToActivity(MainActivity.class.getSimpleName()); solo.goBack(); + solo.goBack(); assertTrue(((MainActivity)solo.getCurrentActivity()).isDrawerOpen()); } @@ -124,6 +126,7 @@ public class MainActivityTest { solo.goBackToActivity(MainActivity.class.getSimpleName()); solo.goBack(); solo.goBack(); + solo.goBack(); assertThat(mActivityRule.getActivityResult(), hasResultCode(Activity.RESULT_CANCELED)); } @@ -136,6 +139,7 @@ public class MainActivityTest { onView(withText(R.string.back_button_show_prompt)).perform(click()); solo.goBackToActivity(MainActivity.class.getSimpleName()); solo.goBack(); + solo.goBack(); onView(withText(R.string.yes)).perform(click()); Thread.sleep(100); assertThat(mActivityRule.getActivityResult(), hasResultCode(Activity.RESULT_CANCELED)); @@ -150,6 +154,7 @@ public class MainActivityTest { onView(withText(R.string.back_button_default)).perform(click()); solo.goBackToActivity(MainActivity.class.getSimpleName()); solo.goBack(); + solo.goBack(); assertThat(mActivityRule.getActivityResult(), hasResultCode(Activity.RESULT_CANCELED)); } } diff --git a/app/src/androidTest/java/de/test/antennapod/ui/NavigationDrawerTest.java b/app/src/androidTest/java/de/test/antennapod/ui/NavigationDrawerTest.java index e7fbbcb89..86b167db2 100644 --- a/app/src/androidTest/java/de/test/antennapod/ui/NavigationDrawerTest.java +++ b/app/src/androidTest/java/de/test/antennapod/ui/NavigationDrawerTest.java @@ -12,6 +12,7 @@ import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.fragment.DownloadsFragment; import de.danoeh.antennapod.fragment.EpisodesFragment; +import de.danoeh.antennapod.fragment.NavDrawerFragment; import de.danoeh.antennapod.fragment.PlaybackHistoryFragment; import de.danoeh.antennapod.fragment.QueueFragment; import de.test.antennapod.EspressoTestUtils; @@ -26,7 +27,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; -import static androidx.test.espresso.Espresso.onData; import static androidx.test.espresso.Espresso.onView; import static androidx.test.espresso.action.ViewActions.click; import static androidx.test.espresso.action.ViewActions.longClick; @@ -194,7 +194,7 @@ public class NavigationDrawerTest { List<String> hidden = UserPreferences.getHiddenDrawerItems(); assertEquals(titles.length, hidden.size()); - for (String tag : MainActivity.NAV_DRAWER_TAGS) { + for (String tag : NavDrawerFragment.NAV_DRAWER_TAGS) { assertTrue(hidden.contains(tag)); } } diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java index 87cc20567..9ec043828 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java @@ -1,24 +1,17 @@ package de.danoeh.antennapod.activity; import android.annotation.TargetApi; -import android.app.ActionBar; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Configuration; -import android.database.DataSetObserver; import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.util.Log; -import android.view.ContextMenu; import android.view.Menu; -import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; -import android.widget.AdapterView; -import android.widget.ListView; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -32,83 +25,48 @@ import androidx.fragment.app.FragmentTransaction; import com.bumptech.glide.Glide; import com.google.android.material.snackbar.Snackbar; import de.danoeh.antennapod.R; -import de.danoeh.antennapod.adapter.NavListAdapter; -import de.danoeh.antennapod.core.asynctask.FeedRemover; -import de.danoeh.antennapod.core.dialog.ConfirmationDialog; -import de.danoeh.antennapod.core.event.FeedListUpdateEvent; import de.danoeh.antennapod.core.event.MessageEvent; -import de.danoeh.antennapod.core.event.QueueEvent; -import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent; -import de.danoeh.antennapod.core.feed.Feed; -import de.danoeh.antennapod.core.preferences.PlaybackPreferences; import de.danoeh.antennapod.core.preferences.UserPreferences; -import de.danoeh.antennapod.core.service.playback.PlaybackService; -import de.danoeh.antennapod.core.storage.DBReader; -import de.danoeh.antennapod.core.storage.DBWriter; -import de.danoeh.antennapod.core.util.FeedItemUtil; import de.danoeh.antennapod.core.util.Flavors; -import de.danoeh.antennapod.core.util.IntentUtils; import de.danoeh.antennapod.core.util.StorageUtils; import de.danoeh.antennapod.dialog.RatingDialog; -import de.danoeh.antennapod.dialog.RenameFeedDialog; import de.danoeh.antennapod.fragment.AddFeedFragment; import de.danoeh.antennapod.fragment.DownloadsFragment; import de.danoeh.antennapod.fragment.EpisodesFragment; import de.danoeh.antennapod.fragment.ExternalPlayerFragment; import de.danoeh.antennapod.fragment.FeedItemlistFragment; +import de.danoeh.antennapod.fragment.NavDrawerFragment; import de.danoeh.antennapod.fragment.PlaybackHistoryFragment; import de.danoeh.antennapod.fragment.QueueFragment; import de.danoeh.antennapod.fragment.SubscriptionFragment; import de.danoeh.antennapod.fragment.TransitionEffect; -import de.danoeh.antennapod.menuhandler.NavDrawerActivity; import de.danoeh.antennapod.preferences.PreferenceUpgrader; -import io.reactivex.Observable; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.disposables.Disposable; -import io.reactivex.schedulers.Schedulers; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.Validate; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; -import java.util.List; - /** * The activity that is shown when the user launches the app. */ -public class MainActivity extends CastEnabledActivity implements NavDrawerActivity { +public class MainActivity extends CastEnabledActivity { private static final String TAG = "MainActivity"; + public static final String MAIN_FRAGMENT_TAG = "main"; public static final String PREF_NAME = "MainActivityPrefs"; public static final String PREF_IS_FIRST_LAUNCH = "prefMainActivityIsFirstLaunch"; - public static final String PREF_LAST_FRAGMENT_TAG = "prefMainActivityLastFragmentTag"; - public static final String EXTRA_NAV_TYPE = "nav_type"; - public static final String EXTRA_NAV_INDEX = "nav_index"; public static final String EXTRA_FRAGMENT_TAG = "fragment_tag"; public static final String EXTRA_FRAGMENT_ARGS = "fragment_args"; - private static final String EXTRA_FEED_ID = "fragment_feed_id"; + public static final String EXTRA_FEED_ID = "fragment_feed_id"; private static final String SAVE_BACKSTACK_COUNT = "backstackCount"; - public static final String[] NAV_DRAWER_TAGS = { - QueueFragment.TAG, - EpisodesFragment.TAG, - SubscriptionFragment.TAG, - DownloadsFragment.TAG, - PlaybackHistoryFragment.TAG, - AddFeedFragment.TAG, - NavListAdapter.SUBSCRIPTION_LIST_TAG - }; - private DrawerLayout drawerLayout; private View navDrawer; - private NavListAdapter navAdapter; - private int mPosition = -1; private ActionBarDrawerToggle drawerToggle; - private Disposable disposable; private long lastBackButtonPressTime = 0; @NonNull @@ -127,39 +85,20 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi setContentView(R.layout.main); drawerLayout = findViewById(R.id.drawer_layout); - ListView navList = findViewById(R.id.nav_list); - navDrawer = findViewById(R.id.nav_layout); + navDrawer = findViewById(R.id.navDrawerFragment); final FragmentManager fm = getSupportFragmentManager(); fm.addOnBackStackChangedListener(() -> drawerToggle.setDrawerIndicatorEnabled(fm.getBackStackEntryCount() == 0)); - navAdapter = new NavListAdapter(itemAccess, this); - navList.setAdapter(navAdapter); - navList.setOnItemClickListener(navListClickListener); - navList.setOnItemLongClickListener(newListLongClickListener); - registerForContextMenu(navList); - - navAdapter.registerDataSetObserver(new DataSetObserver() { - @Override - public void onChanged() { - selectedNavListIndex = getSelectedNavListIndex(); - } - }); - - findViewById(R.id.nav_settings).setOnClickListener(v -> { - drawerLayout.closeDrawer(navDrawer); - startActivity(new Intent(MainActivity.this, PreferenceActivity.class)); - }); - FragmentTransaction transaction = fm.beginTransaction(); - Fragment mainFragment = fm.findFragmentByTag("main"); + Fragment mainFragment = fm.findFragmentByTag(MAIN_FRAGMENT_TAG); if (mainFragment != null) { transaction.replace(R.id.main_view, mainFragment); } else { - String lastFragment = getLastNavFragment(); - if (ArrayUtils.contains(NAV_DRAWER_TAGS, lastFragment)) { + String lastFragment = NavDrawerFragment.getLastNavFragment(this); + if (ArrayUtils.contains(NavDrawerFragment.NAV_DRAWER_TAGS, lastFragment)) { loadFragment(lastFragment, null); } else { try { @@ -174,6 +113,9 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi } ExternalPlayerFragment externalPlayerFragment = new ExternalPlayerFragment(); transaction.replace(R.id.playerFragment, externalPlayerFragment, ExternalPlayerFragment.TAG); + NavDrawerFragment navDrawerFragment = new NavDrawerFragment(); + transaction.replace(R.id.navDrawerFragment, navDrawerFragment, NavDrawerFragment.TAG); + transaction.commit(); checkFirstLaunch(); @@ -190,25 +132,6 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi super.setSupportActionBar(toolbar); } - private void saveLastNavFragment(String tag) { - Log.d(TAG, "saveLastNavFragment(tag: " + tag + ")"); - SharedPreferences prefs = getSharedPreferences(PREF_NAME, MODE_PRIVATE); - SharedPreferences.Editor edit = prefs.edit(); - if (tag != null) { - edit.putString(PREF_LAST_FRAGMENT_TAG, tag); - } else { - edit.remove(PREF_LAST_FRAGMENT_TAG); - } - edit.apply(); - } - - private String getLastNavFragment() { - SharedPreferences prefs = getSharedPreferences(PREF_NAME, MODE_PRIVATE); - String lastFragment = prefs.getString(PREF_LAST_FRAGMENT_TAG, QueueFragment.TAG); - Log.d(TAG, "getLastNavFragment() -> " + lastFragment); - return lastFragment; - } - private void checkFirstLaunch() { SharedPreferences prefs = getSharedPreferences(PREF_NAME, MODE_PRIVATE); if (prefs.getBoolean(PREF_IS_FIRST_LAUNCH, true)) { @@ -224,50 +147,13 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi } } - private void showDrawerPreferencesDialog() { - final List<String> hiddenDrawerItems = UserPreferences.getHiddenDrawerItems(); - String[] navLabels = new String[NAV_DRAWER_TAGS.length]; - final boolean[] checked = new boolean[NAV_DRAWER_TAGS.length]; - for (int i = 0; i < NAV_DRAWER_TAGS.length; i++) { - String tag = NAV_DRAWER_TAGS[i]; - navLabels[i] = navAdapter.getLabel(tag); - if (!hiddenDrawerItems.contains(tag)) { - checked[i] = true; - } - } - - AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.this); - builder.setTitle(R.string.drawer_preferences); - builder.setMultiChoiceItems(navLabels, checked, (dialog, which, isChecked) -> { - if (isChecked) { - hiddenDrawerItems.remove(NAV_DRAWER_TAGS[which]); - } else { - hiddenDrawerItems.add(NAV_DRAWER_TAGS[which]); - } - }); - builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> UserPreferences.setHiddenDrawerItems(hiddenDrawerItems)); - builder.setNegativeButton(R.string.cancel_label, null); - builder.create().show(); - } - public boolean isDrawerOpen() { return drawerLayout != null && navDrawer != null && drawerLayout.isDrawerOpen(navDrawer); } - private void loadFragment(int index, Bundle args) { - Log.d(TAG, "loadFragment(index: " + index + ", args: " + args + ")"); - if (index < navAdapter.getSubscriptionOffset()) { - String tag = navAdapter.getTags().get(index); - loadFragment(tag, args); - } else { - int pos = index - navAdapter.getSubscriptionOffset(); - loadFeedFragmentByPosition(pos, args); - } - } - public void loadFragment(String tag, Bundle args) { Log.d(TAG, "loadFragment(tag: " + tag + ", args: " + args + ")"); - Fragment fragment = null; + Fragment fragment; switch (tag) { case QueueFragment.TAG: fragment = new QueueFragment(); @@ -289,32 +175,22 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi break; default: // default to the queue - tag = QueueFragment.TAG; fragment = new QueueFragment(); args = null; break; } - saveLastNavFragment(tag); + if (args != null) { fragment.setArguments(args); } loadFragment(fragment); } - private void loadFeedFragmentByPosition(int relPos, Bundle args) { - if(relPos < 0) { - return; - } - Feed feed = itemAccess.getItem(relPos); - loadFeedFragmentById(feed.getId(), args); - } - public void loadFeedFragmentById(long feedId, Bundle args) { Fragment fragment = FeedItemlistFragment.newInstance(feedId); - if(args != null) { + if (args != null) { fragment.setArguments(args); } - saveLastNavFragment(String.valueOf(feedId)); loadFragment(fragment); } @@ -325,7 +201,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi fragmentManager.popBackStack(); } FragmentTransaction t = fragmentManager.beginTransaction(); - t.replace(R.id.main_view, fragment, "main"); + t.replace(R.id.main_view, fragment, MAIN_FRAGMENT_TAG); fragmentManager.popBackStack(); // TODO: we have to allow state loss here // since this function can get called from an AsyncTask which @@ -334,9 +210,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi // not commit anything in an AsyncTask, but that's a bigger // change than we want now. t.commitAllowingStateLoss(); - if (navAdapter != null) { - navAdapter.notifyDataSetChanged(); - } + drawerLayout.closeDrawer(navDrawer); } public void loadChildFragment(Fragment fragment, TransitionEffect transition) { @@ -357,8 +231,8 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi } transaction - .hide(getSupportFragmentManager().findFragmentByTag("main")) - .add(R.id.main_view, fragment, "main") + .hide(getSupportFragmentManager().findFragmentByTag(MAIN_FRAGMENT_TAG)) + .add(R.id.main_view, fragment, MAIN_FRAGMENT_TAG) .addToBackStack(null) .commit(); } @@ -367,64 +241,10 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi loadChildFragment(fragment, TransitionEffect.NONE); } - private int getSelectedNavListIndex() { - String currentFragment = getLastNavFragment(); - if(currentFragment == null) { - // should not happen, but better safe than sorry - return -1; - } - int tagIndex = navAdapter.getTags().indexOf(currentFragment); - if(tagIndex >= 0) { - return tagIndex; - } else if(ArrayUtils.contains(NAV_DRAWER_TAGS, currentFragment)) { - // the fragment was just hidden - return -1; - } else { // last fragment was not a list, but a feed - long feedId = Long.parseLong(currentFragment); - if (navDrawerData != null) { - List<Feed> feeds = navDrawerData.feeds; - for (int i = 0; i < feeds.size(); i++) { - if (feeds.get(i).getId() == feedId) { - return i + navAdapter.getSubscriptionOffset(); - } - } - } - return -1; - } - } - - private final AdapterView.OnItemClickListener navListClickListener = new AdapterView.OnItemClickListener() { - @Override - public void onItemClick(AdapterView<?> parent, View view, int position, long id) { - int viewType = parent.getAdapter().getItemViewType(position); - if (viewType != NavListAdapter.VIEW_TYPE_SECTION_DIVIDER && position != selectedNavListIndex) { - loadFragment(position, null); - } - drawerLayout.closeDrawer(navDrawer); - } - }; - - private final AdapterView.OnItemLongClickListener newListLongClickListener = new AdapterView.OnItemLongClickListener() { - @Override - public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { - if(position < navAdapter.getTags().size()) { - showDrawerPreferencesDialog(); - return true; - } else { - mPosition = position; - return false; - } - } - }; - - @Override protected void onPostCreate(Bundle savedInstanceState) { super.onPostCreate(savedInstanceState); drawerToggle.syncState(); - if (savedInstanceState != null) { - selectedNavListIndex = getSelectedNavListIndex(); - } } @Override @@ -455,14 +275,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi protected void onResume() { super.onResume(); StorageUtils.checkStorageAvailability(this); - - Intent intent = getIntent(); - if (intent.hasExtra(EXTRA_FEED_ID) || - (navDrawerData != null && intent.hasExtra(EXTRA_NAV_TYPE) && - (intent.hasExtra(EXTRA_NAV_INDEX) || intent.hasExtra(EXTRA_FRAGMENT_TAG)))) { - handleNavIntent(); - } - loadData(); + handleNavIntent(); RatingDialog.check(); } @@ -470,12 +283,8 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi protected void onStop() { super.onStop(); EventBus.getDefault().unregister(this); - if (disposable != null) { - disposable.dispose(); - } } - @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) @Override public void onTrimMemory(int level) { @@ -493,7 +302,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi public boolean onCreateOptionsMenu(Menu menu) { boolean retVal = super.onCreateOptionsMenu(menu); if (Flavors.FLAVOR == Flavors.PLAY) { - switch (getLastNavFragment()) { + switch (NavDrawerFragment.getLastNavFragment(this)) { case QueueFragment.TAG: case EpisodesFragment.TAG: requestCastButton(MenuItem.SHOW_AS_ACTION_IF_ROOM); @@ -526,100 +335,6 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi } } - - @Override - public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { - super.onCreateContextMenu(menu, v, menuInfo); - if(v.getId() != R.id.nav_list) { - return; - } - AdapterView.AdapterContextMenuInfo adapterInfo = (AdapterView.AdapterContextMenuInfo) menuInfo; - int position = adapterInfo.position; - if(position < navAdapter.getSubscriptionOffset()) { - return; - } - MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.nav_feed_context, menu); - Feed feed = navDrawerData.feeds.get(position - navAdapter.getSubscriptionOffset()); - menu.setHeaderTitle(feed.getTitle()); - // episodes are not loaded, so we cannot check if the podcast has new or unplayed ones! - } - - - @Override - public boolean onContextItemSelected(MenuItem item) { - final int position = mPosition; - mPosition = -1; // reset - if(position < 0) { - return false; - } - Feed feed = navDrawerData.feeds.get(position - navAdapter.getSubscriptionOffset()); - switch(item.getItemId()) { - case R.id.remove_all_new_flags_item: - ConfirmationDialog removeAllNewFlagsConfirmationDialog = new ConfirmationDialog(this, - R.string.remove_all_new_flags_label, - R.string.remove_all_new_flags_confirmation_msg) { - @Override - public void onConfirmButtonPressed(DialogInterface dialog) { - dialog.dismiss(); - DBWriter.removeFeedNewFlag(feed.getId()); - } - }; - removeAllNewFlagsConfirmationDialog.createNewDialog().show(); - return true; - case R.id.mark_all_read_item: - ConfirmationDialog markAllReadConfirmationDialog = new ConfirmationDialog(this, - R.string.mark_all_read_label, - R.string.mark_all_read_confirmation_msg) { - - @Override - public void onConfirmButtonPressed(DialogInterface dialog) { - dialog.dismiss(); - DBWriter.markFeedRead(feed.getId()); - } - }; - markAllReadConfirmationDialog.createNewDialog().show(); - return true; - case R.id.rename_item: - new RenameFeedDialog(this, feed).show(); - return true; - case R.id.remove_item: - final FeedRemover remover = new FeedRemover(this, feed) { - @Override - protected void onPostExecute(Void result) { - super.onPostExecute(result); - if(getSelectedNavListIndex() == position) { - loadFragment(EpisodesFragment.TAG, null); - } - } - }; - ConfirmationDialog conDialog = new ConfirmationDialog(this, - R.string.remove_feed_label, - getString(R.string.feed_delete_confirmation_msg, feed.getTitle())) { - @Override - public void onConfirmButtonPressed( - DialogInterface dialog) { - dialog.dismiss(); - long mediaId = PlaybackPreferences.getCurrentlyPlayingFeedMediaId(); - if (mediaId > 0 && - FeedItemUtil.indexOfItemWithMediaId(feed.getItems(), mediaId) >= 0) { - Log.d(TAG, "Currently playing episode is about to be deleted, skipping"); - remover.skipOnCompletion = true; - int playerStatus = PlaybackPreferences.getCurrentPlayerStatus(); - if(playerStatus == PlaybackPreferences.PLAYER_STATUS_PLAYING) { - IntentUtils.sendLocalBroadcast(MainActivity.this, PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE); - } - } - remover.executeAsync(); - } - }; - conDialog.createNewDialog().show(); - return true; - default: - return super.onContextItemSelected(item); - } - } - @Override public void onBackPressed() { if (isDrawerOpen()) { @@ -648,7 +363,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi } break; case GO_TO_PAGE: - if (getLastNavFragment().equals(UserPreferences.getBackButtonGoToPage())) { + if (NavDrawerFragment.getLastNavFragment(this).equals(UserPreferences.getBackButtonGoToPage())) { super.onBackPressed(); } else { loadFragment(UserPreferences.getBackButtonGoToPage(), null); @@ -659,141 +374,32 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi } } - private DBReader.NavDrawerData navDrawerData; - private int selectedNavListIndex = 0; - - private final NavListAdapter.ItemAccess itemAccess = new NavListAdapter.ItemAccess() { - @Override - public int getCount() { - if (navDrawerData != null) { - return navDrawerData.feeds.size(); - } else { - return 0; - } - } - - @Override - public Feed getItem(int position) { - if (navDrawerData != null && 0 <= position && position < navDrawerData.feeds.size()) { - return navDrawerData.feeds.get(position); - } else { - return null; - } - } - - @Override - public int getSelectedItemIndex() { - return selectedNavListIndex; - } - - @Override - public int getQueueSize() { - return (navDrawerData != null) ? navDrawerData.queueSize : 0; - } - - @Override - public int getNumberOfNewItems() { - return (navDrawerData != null) ? navDrawerData.numNewItems : 0; - } - - @Override - public int getNumberOfDownloadedItems() { - return (navDrawerData != null) ? navDrawerData.numDownloadedItems : 0; - } - - @Override - public int getReclaimableItems() { - return (navDrawerData != null) ? navDrawerData.reclaimableSpace : 0; - } - - @Override - public int getFeedCounter(long feedId) { - return navDrawerData != null ? navDrawerData.feedCounters.get(feedId) : 0; - } - - @Override - public int getFeedCounterSum() { - if(navDrawerData == null) { - return 0; - } - int sum = 0; - for(int counter : navDrawerData.feedCounters.values()) { - sum += counter; - } - return sum; - } - - }; - - private void loadData() { - disposable = Observable.fromCallable(DBReader::getNavDrawerData) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(result -> { - boolean handleIntent = (navDrawerData == null); - - navDrawerData = result; - navAdapter.notifyDataSetChanged(); - - if (handleIntent) { - handleNavIntent(); - } - }, error -> Log.e(TAG, Log.getStackTraceString(error))); - } - - @Subscribe - public void onEvent(QueueEvent event) { - Log.d(TAG, "onEvent(" + event + ")"); - // we are only interested in the number of queue items, not download status or position - if(event.action == QueueEvent.Action.DELETED_MEDIA || - event.action == QueueEvent.Action.SORTED || - event.action == QueueEvent.Action.MOVED) { - return; - } - loadData(); - } - @Subscribe(threadMode = ThreadMode.MAIN) public void onEventMainThread(MessageEvent event) { Log.d(TAG, "onEvent(" + event + ")"); View parentLayout = findViewById(R.id.drawer_layout); Snackbar snackbar = Snackbar.make(parentLayout, event.message, Snackbar.LENGTH_SHORT); - if(event.action != null) { + if (event.action != null) { snackbar.setAction(getString(R.string.undo), v -> event.action.run()); } snackbar.show(); } - @Subscribe - public void onUnreadItemsChanged(UnreadItemsUpdateEvent event) { - loadData(); - } - - - @Subscribe - public void onFeedListChanged(FeedListUpdateEvent event) { - loadData(); - } - private void handleNavIntent() { - Log.d(TAG, "handleNavIntent()"); Intent intent = getIntent(); - if (intent.hasExtra(EXTRA_FEED_ID) || - (intent.hasExtra(EXTRA_NAV_TYPE) && - (intent.hasExtra(EXTRA_NAV_INDEX) || intent.hasExtra(EXTRA_FRAGMENT_TAG)))) { - int index = intent.getIntExtra(EXTRA_NAV_INDEX, -1); + if (intent.hasExtra(EXTRA_FEED_ID) || intent.hasExtra(EXTRA_FRAGMENT_TAG)) { + Log.d(TAG, "handleNavIntent()"); String tag = intent.getStringExtra(EXTRA_FRAGMENT_TAG); Bundle args = intent.getBundleExtra(EXTRA_FRAGMENT_ARGS); long feedId = intent.getLongExtra(EXTRA_FEED_ID, 0); - if (index >= 0) { - loadFragment(index, args); - } else if (tag != null) { + if (tag != null) { loadFragment(tag, args); - } else if(feedId > 0) { + } else if (feedId > 0) { loadFeedFragmentById(feedId, args); } + // to avoid handling the intent twice when the configuration changes + setIntent(new Intent(MainActivity.this, MainActivity.class)); } - setIntent(new Intent(MainActivity.this, MainActivity.class)); // to avoid handling the intent twice when the configuration changes } @Override diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java index 162d4e489..1a7631813 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java @@ -1,76 +1,43 @@ package de.danoeh.antennapod.activity; -import android.app.AlertDialog; -import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Configuration; -import android.os.Build; import android.os.Bundle; -import com.google.android.material.appbar.AppBarLayout; -import com.google.android.material.snackbar.Snackbar; -import androidx.fragment.app.Fragment; -import androidx.fragment.app.FragmentManager; -import androidx.fragment.app.FragmentStatePagerAdapter; -import androidx.viewpager.widget.ViewPager; -import androidx.drawerlayout.widget.DrawerLayout; -import androidx.appcompat.app.ActionBarDrawerToggle; -import androidx.appcompat.widget.Toolbar; import android.util.Log; -import android.util.TypedValue; -import android.view.ContextMenu; -import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; -import android.widget.AdapterView; -import android.widget.ImageButton; -import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; - -import java.util.List; - +import androidx.appcompat.app.ActionBarDrawerToggle; +import androidx.appcompat.widget.Toolbar; +import androidx.drawerlayout.widget.DrawerLayout; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentStatePagerAdapter; +import androidx.viewpager.widget.ViewPager; +import com.google.android.material.snackbar.Snackbar; import de.danoeh.antennapod.R; -import de.danoeh.antennapod.adapter.NavListAdapter; -import de.danoeh.antennapod.core.asynctask.FeedRemover; -import de.danoeh.antennapod.core.dialog.ConfirmationDialog; -import de.danoeh.antennapod.core.event.FeedListUpdateEvent; import de.danoeh.antennapod.core.event.MessageEvent; import de.danoeh.antennapod.core.feed.Chapter; -import de.danoeh.antennapod.core.feed.Feed; -import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.service.playback.PlaybackService; -import de.danoeh.antennapod.core.service.playback.PlayerStatus; -import de.danoeh.antennapod.core.storage.DBReader; -import de.danoeh.antennapod.core.storage.DBWriter; -import de.danoeh.antennapod.core.util.IntentUtils; -import de.danoeh.antennapod.core.util.playback.Playable; import de.danoeh.antennapod.core.util.playback.PlaybackController; -import de.danoeh.antennapod.dialog.RenameFeedDialog; -import de.danoeh.antennapod.fragment.AddFeedFragment; import de.danoeh.antennapod.fragment.ChaptersFragment; import de.danoeh.antennapod.fragment.CoverFragment; -import de.danoeh.antennapod.fragment.DownloadsFragment; -import de.danoeh.antennapod.fragment.EpisodesFragment; import de.danoeh.antennapod.fragment.ItemDescriptionFragment; -import de.danoeh.antennapod.fragment.PlaybackHistoryFragment; -import de.danoeh.antennapod.fragment.QueueFragment; -import de.danoeh.antennapod.fragment.SubscriptionFragment; -import de.danoeh.antennapod.menuhandler.NavDrawerActivity; +import de.danoeh.antennapod.fragment.NavDrawerFragment; import de.danoeh.antennapod.view.PagerIndicatorView; import de.danoeh.antennapod.view.PlaybackSpeedIndicatorView; -import io.reactivex.Observable; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.disposables.Disposable; -import io.reactivex.schedulers.Schedulers; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; +import java.util.List; + /** * Activity for playing files that do not require a video surface. */ -public abstract class MediaplayerInfoActivity extends MediaplayerActivity implements NavDrawerActivity { +public abstract class MediaplayerInfoActivity extends MediaplayerActivity { private static final String TAG = "MediaplayerInfoActivity"; @@ -82,31 +49,15 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem private static final String PREFS = "AudioPlayerActivityPreferences"; private static final String PREF_KEY_SELECTED_FRAGMENT_POSITION = "selectedFragmentPosition"; - private static final String[] NAV_DRAWER_TAGS = { - QueueFragment.TAG, - EpisodesFragment.TAG, - SubscriptionFragment.TAG, - DownloadsFragment.TAG, - PlaybackHistoryFragment.TAG, - AddFeedFragment.TAG, - NavListAdapter.SUBSCRIPTION_LIST_TAG - }; - PlaybackSpeedIndicatorView butPlaybackSpeed; TextView txtvPlaybackSpeed; private DrawerLayout drawerLayout; - private NavListAdapter navAdapter; - private ListView navList; private View navDrawer; private ActionBarDrawerToggle drawerToggle; - private int mPosition = -1; - private ViewPager pager; private PagerIndicatorView pageIndicator; private MediaplayerInfoPagerAdapter pagerAdapter; - private Disposable disposable; - @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); @@ -117,9 +68,6 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem protected void onStop() { super.onStop(); Log.d(TAG, "onStop()"); - if (disposable != null) { - disposable.dispose(); - } saveCurrentFragment(); } @@ -129,8 +77,6 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem super.onDestroy(); // don't risk creating memory leaks drawerLayout = null; - navAdapter = null; - navList = null; navDrawer = null; drawerToggle = null; pager = null; @@ -143,7 +89,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem } void saveCurrentFragment() { - if(pager == null) { + if (pager == null) { return; } Log.d(TAG, "Saving preferences"); @@ -156,7 +102,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); - if(drawerToggle != null) { + if (drawerToggle != null) { drawerToggle.onConfigurationChanged(newConfig); } } @@ -169,12 +115,6 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem } @Override - protected void onStart() { - super.onStart(); - loadData(); - } - - @Override protected void onNewIntent(Intent intent) { super.onNewIntent(intent); setIntent(intent); @@ -212,44 +152,18 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setTitle(""); drawerLayout = findViewById(R.id.drawer_layout); - navList = findViewById(R.id.nav_list); - navDrawer = findViewById(R.id.nav_layout); + navDrawer = findViewById(R.id.navDrawerFragment); + butPlaybackSpeed = findViewById(R.id.butPlaybackSpeed); + txtvPlaybackSpeed = findViewById(R.id.txtvPlaybackSpeed); drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, R.string.drawer_open, R.string.drawer_close); drawerToggle.setDrawerIndicatorEnabled(false); drawerLayout.addDrawerListener(drawerToggle); - - navAdapter = new NavListAdapter(itemAccess, this); - navList.setAdapter(navAdapter); - navList.setOnItemClickListener((parent, view, position, id) -> { - int viewType = parent.getAdapter().getItemViewType(position); - if (viewType != NavListAdapter.VIEW_TYPE_SECTION_DIVIDER) { - Intent intent = new Intent(MediaplayerInfoActivity.this, MainActivity.class); - intent.putExtra(MainActivity.EXTRA_NAV_TYPE, viewType); - intent.putExtra(MainActivity.EXTRA_NAV_INDEX, position); - startActivity(intent); - } - drawerLayout.closeDrawer(navDrawer); - }); - navList.setOnItemLongClickListener((parent, view, position, id) -> { - if (position < navAdapter.getTags().size()) { - showDrawerPreferencesDialog(); - return true; - } else { - mPosition = position; - return false; - } - }); - registerForContextMenu(navList); drawerToggle.syncState(); - findViewById(R.id.nav_settings).setOnClickListener(v -> { - drawerLayout.closeDrawer(navDrawer); - startActivity(new Intent(MediaplayerInfoActivity.this, PreferenceActivity.class)); - }); - - butPlaybackSpeed = findViewById(R.id.butPlaybackSpeed); - txtvPlaybackSpeed = findViewById(R.id.txtvPlaybackSpeed); + getSupportFragmentManager().beginTransaction() + .replace(R.id.navDrawerFragment, new NavDrawerFragment(), NavDrawerFragment.TAG) + .commit(); pager = findViewById(R.id.pager); pager.setOffscreenPageLimit(3); @@ -262,7 +176,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem loadLastFragment(); pager.onSaveInstanceState(); - navList.post(this::supportStartPostponedEnterTransition); + pager.post(this::supportStartPostponedEnterTransition); } @Override @@ -299,7 +213,6 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem return controller; } - @Override public boolean isDrawerOpen() { return drawerLayout != null && navDrawer != null && drawerLayout.isDrawerOpen(navDrawer); } @@ -309,84 +222,14 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem return R.layout.mediaplayerinfo_activity; } - @Override public boolean onOptionsItemSelected(MenuItem item) { return (drawerToggle != null && drawerToggle.onOptionsItemSelected(item)) || super.onOptionsItemSelected(item); } @Override - public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { - super.onCreateContextMenu(menu, v, menuInfo); - if(v.getId() != R.id.nav_list) { - return; - } - AdapterView.AdapterContextMenuInfo adapterInfo = (AdapterView.AdapterContextMenuInfo) menuInfo; - int position = adapterInfo.position; - if(position < navAdapter.getSubscriptionOffset()) { - return; - } - MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.nav_feed_context, menu); - Feed feed = navDrawerData.feeds.get(position - navAdapter.getSubscriptionOffset()); - menu.setHeaderTitle(feed.getTitle()); - // episodes are not loaded, so we cannot check if the podcast has new or unplayed ones! - } - - @Override - public boolean onContextItemSelected(MenuItem item) { - final int position = mPosition; - mPosition = -1; // reset - if(position < 0) { - return false; - } - Feed feed = navDrawerData.feeds.get(position - navAdapter.getSubscriptionOffset()); - switch(item.getItemId()) { - case R.id.remove_all_new_flags_item: - DBWriter.removeFeedNewFlag(feed.getId()); - return true; - case R.id.mark_all_read_item: - DBWriter.markFeedRead(feed.getId()); - return true; - case R.id.rename_item: - new RenameFeedDialog(this, feed).show(); - return true; - case R.id.remove_item: - final FeedRemover remover = new FeedRemover(this, feed); - ConfirmationDialog conDialog = new ConfirmationDialog(this, - R.string.remove_feed_label, - getString(R.string.feed_delete_confirmation_msg, feed.getTitle())) { - @Override - public void onConfirmButtonPressed( - DialogInterface dialog) { - dialog.dismiss(); - if (controller != null) { - Playable playable = controller.getMedia(); - if (playable != null && playable instanceof FeedMedia) { - FeedMedia media = (FeedMedia) playable; - if (media.getItem() != null && media.getItem().getFeed() != null && - media.getItem().getFeed().getId() == feed.getId()) { - Log.d(TAG, "Currently playing episode is about to be deleted, skipping"); - remover.skipOnCompletion = true; - if(controller.getStatus() == PlayerStatus.PLAYING) { - IntentUtils.sendLocalBroadcast(MediaplayerInfoActivity.this, PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE); - } - } - } - } - remover.executeAsync(); - } - }; - conDialog.createNewDialog().show(); - return true; - default: - return super.onContextItemSelected(item); - } - } - - @Override public void onBackPressed() { - if(isDrawerOpen()) { + if (isDrawerOpen()) { drawerLayout.closeDrawer(navDrawer); } else if (pager == null || pager.getCurrentItem() == 0) { // If the user is currently looking at the first step, allow the system to handle the @@ -398,46 +241,6 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem } } - private void showDrawerPreferencesDialog() { - final List<String> hiddenDrawerItems = UserPreferences.getHiddenDrawerItems(); - String[] navLabels = new String[NAV_DRAWER_TAGS.length]; - final boolean[] checked = new boolean[NAV_DRAWER_TAGS.length]; - for (int i = 0; i < NAV_DRAWER_TAGS.length; i++) { - String tag = NAV_DRAWER_TAGS[i]; - navLabels[i] = navAdapter.getLabel(tag); - if (!hiddenDrawerItems.contains(tag)) { - checked[i] = true; - } - } - - AlertDialog.Builder builder = new AlertDialog.Builder(this); - builder.setTitle(R.string.drawer_preferences); - builder.setMultiChoiceItems(navLabels, checked, (dialog, which, isChecked) -> { - if (isChecked) { - hiddenDrawerItems.remove(NAV_DRAWER_TAGS[which]); - } else { - hiddenDrawerItems.add(NAV_DRAWER_TAGS[which]); - } - }); - builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> UserPreferences.setHiddenDrawerItems(hiddenDrawerItems)); - builder.setNegativeButton(R.string.cancel_label, null); - builder.create().show(); - } - - private DBReader.NavDrawerData navDrawerData; - - private void loadData() { - disposable = Observable.fromCallable(DBReader::getNavDrawerData) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(result -> { - navDrawerData = result; - if (navAdapter != null) { - navAdapter.notifyDataSetChanged(); - } - }, error -> Log.e(TAG, Log.getStackTraceString(error))); - } - @Subscribe(threadMode = ThreadMode.MAIN) public void onEventMainThread(MessageEvent event) { Log.d(TAG, "onEvent(" + event + ")"); @@ -449,73 +252,6 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem snackbar.show(); } - @Subscribe - public void onFeedListChanged(FeedListUpdateEvent event) { - loadData(); - } - - private final NavListAdapter.ItemAccess itemAccess = new NavListAdapter.ItemAccess() { - @Override - public int getCount() { - if (navDrawerData != null) { - return navDrawerData.feeds.size(); - } else { - return 0; - } - } - - @Override - public Feed getItem(int position) { - if (navDrawerData != null && 0 <= position && position < navDrawerData.feeds.size()) { - return navDrawerData.feeds.get(position); - } else { - return null; - } - } - - @Override - public int getSelectedItemIndex() { - return -1; - } - - @Override - public int getQueueSize() { - return (navDrawerData != null) ? navDrawerData.queueSize : 0; - } - - @Override - public int getNumberOfNewItems() { - return (navDrawerData != null) ? navDrawerData.numNewItems : 0; - } - - @Override - public int getNumberOfDownloadedItems() { - return (navDrawerData != null) ? navDrawerData.numDownloadedItems : 0; - } - - @Override - public int getReclaimableItems() { - return (navDrawerData != null) ? navDrawerData.reclaimableSpace : 0; - } - - @Override - public int getFeedCounter(long feedId) { - return navDrawerData != null ? navDrawerData.feedCounters.get(feedId) : 0; - } - - @Override - public int getFeedCounterSum() { - if(navDrawerData == null) { - return 0; - } - int sum = 0; - for(int counter : navDrawerData.feedCounters.values()) { - sum += counter; - } - return sum; - } - }; - private static class MediaplayerInfoPagerAdapter extends FragmentStatePagerAdapter { private static final String TAG = "MPInfoPagerAdapter"; diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java index 59cde717e..3c45829ef 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java @@ -4,7 +4,6 @@ import android.app.Activity; import android.content.Context; import android.content.SharedPreferences; import android.content.res.TypedArray; -import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.preference.PreferenceManager; import android.util.TypedValue; @@ -21,22 +20,23 @@ import com.bumptech.glide.request.RequestOptions; import com.joanzapata.iconify.Iconify; import com.joanzapata.iconify.widget.IconTextView; import de.danoeh.antennapod.R; -import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.glide.ApGlideSettings; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.fragment.AddFeedFragment; import de.danoeh.antennapod.fragment.DownloadsFragment; import de.danoeh.antennapod.fragment.EpisodesFragment; +import de.danoeh.antennapod.fragment.NavDrawerFragment; import de.danoeh.antennapod.fragment.PlaybackHistoryFragment; import de.danoeh.antennapod.fragment.QueueFragment; import de.danoeh.antennapod.fragment.SubscriptionFragment; +import org.apache.commons.lang3.ArrayUtils; + import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; -import org.apache.commons.lang3.ArrayUtils; /** * BaseAdapter for the navigation drawer @@ -80,7 +80,7 @@ public class NavListAdapter extends BaseAdapter } private void loadItems() { - List<String> newTags = new ArrayList<>(Arrays.asList(MainActivity.NAV_DRAWER_TAGS)); + List<String> newTags = new ArrayList<>(Arrays.asList(NavDrawerFragment.NAV_DRAWER_TAGS)); List<String> hiddenFragments = UserPreferences.getHiddenDrawerItems(); newTags.removeAll(hiddenFragments); @@ -100,7 +100,7 @@ public class NavListAdapter extends BaseAdapter } public String getLabel(String tag) { - int index = ArrayUtils.indexOf(MainActivity.NAV_DRAWER_TAGS, tag); + int index = ArrayUtils.indexOf(NavDrawerFragment.NAV_DRAWER_TAGS, tag); return titles[index]; } diff --git a/app/src/main/java/de/danoeh/antennapod/config/DownloadServiceCallbacksImpl.java b/app/src/main/java/de/danoeh/antennapod/config/DownloadServiceCallbacksImpl.java index 998bfa6c9..f50b4143b 100644 --- a/app/src/main/java/de/danoeh/antennapod/config/DownloadServiceCallbacksImpl.java +++ b/app/src/main/java/de/danoeh/antennapod/config/DownloadServiceCallbacksImpl.java @@ -19,14 +19,11 @@ public class DownloadServiceCallbacksImpl implements DownloadServiceCallbacks { @Override public PendingIntent getNotificationContentIntent(Context context) { Intent intent = new Intent(context, MainActivity.class); - intent.putExtra(MainActivity.EXTRA_NAV_TYPE, NavListAdapter.VIEW_TYPE_NAV); intent.putExtra(MainActivity.EXTRA_FRAGMENT_TAG, DownloadsFragment.TAG); Bundle args = new Bundle(); args.putInt(DownloadsFragment.ARG_SELECTED_TAB, DownloadsFragment.POS_RUNNING); intent.putExtra(MainActivity.EXTRA_FRAGMENT_ARGS, args); - - return PendingIntent.getActivity(context, 0, intent, - PendingIntent.FLAG_UPDATE_CURRENT); + return PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); } @Override @@ -40,7 +37,6 @@ public class DownloadServiceCallbacksImpl implements DownloadServiceCallbacks { @Override public PendingIntent getReportNotificationContentIntent(Context context) { Intent intent = new Intent(context, MainActivity.class); - intent.putExtra(MainActivity.EXTRA_NAV_TYPE, NavListAdapter.VIEW_TYPE_NAV); intent.putExtra(MainActivity.EXTRA_FRAGMENT_TAG, DownloadsFragment.TAG); Bundle args = new Bundle(); args.putInt(DownloadsFragment.ARG_SELECTED_TAB, DownloadsFragment.POS_LOG); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/NavDrawerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/NavDrawerFragment.java new file mode 100644 index 000000000..b3492d79c --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/fragment/NavDrawerFragment.java @@ -0,0 +1,421 @@ +package de.danoeh.antennapod.fragment; + +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.content.SharedPreferences; +import android.os.Bundle; +import android.util.Log; +import android.view.ContextMenu; +import android.view.LayoutInflater; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.AdapterView; +import android.widget.ListView; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; +import androidx.appcompat.app.AlertDialog; +import androidx.fragment.app.Fragment; +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.activity.MainActivity; +import de.danoeh.antennapod.activity.PreferenceActivity; +import de.danoeh.antennapod.adapter.NavListAdapter; +import de.danoeh.antennapod.core.asynctask.FeedRemover; +import de.danoeh.antennapod.core.dialog.ConfirmationDialog; +import de.danoeh.antennapod.core.event.FeedListUpdateEvent; +import de.danoeh.antennapod.core.event.QueueEvent; +import de.danoeh.antennapod.core.event.UnreadItemsUpdateEvent; +import de.danoeh.antennapod.core.feed.Feed; +import de.danoeh.antennapod.core.preferences.PlaybackPreferences; +import de.danoeh.antennapod.core.preferences.UserPreferences; +import de.danoeh.antennapod.core.service.playback.PlaybackService; +import de.danoeh.antennapod.core.storage.DBReader; +import de.danoeh.antennapod.core.storage.DBWriter; +import de.danoeh.antennapod.core.util.FeedItemUtil; +import de.danoeh.antennapod.core.util.IntentUtils; +import de.danoeh.antennapod.dialog.RenameFeedDialog; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; +import org.apache.commons.lang3.StringUtils; +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; + +import java.util.List; + +public class NavDrawerFragment extends Fragment implements AdapterView.OnItemClickListener, + AdapterView.OnItemLongClickListener { + @VisibleForTesting + public static final String PREF_LAST_FRAGMENT_TAG = "prefLastFragmentTag"; + @VisibleForTesting + public static final String PREF_NAME = "NavDrawerPrefs"; + public static final String TAG = "NavDrawerFragment"; + + public static final String[] NAV_DRAWER_TAGS = { + QueueFragment.TAG, + EpisodesFragment.TAG, + SubscriptionFragment.TAG, + DownloadsFragment.TAG, + PlaybackHistoryFragment.TAG, + AddFeedFragment.TAG, + NavListAdapter.SUBSCRIPTION_LIST_TAG + }; + + private DBReader.NavDrawerData navDrawerData; + private int selectedNavListIndex = -1; + private int position = -1; + private NavListAdapter navAdapter; + private Disposable disposable; + + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + super.onCreateView(inflater, container, savedInstanceState); + View root = inflater.inflate(R.layout.nav_list, container, false); + + ListView navList = root.findViewById(R.id.nav_list); + navAdapter = new NavListAdapter(itemAccess, getActivity()); + navList.setAdapter(navAdapter); + navList.setOnItemClickListener(this); + navList.setOnItemLongClickListener(this); + registerForContextMenu(navList); + updateSelection(); + + root.findViewById(R.id.nav_settings).setOnClickListener(v -> { + startActivity(new Intent(getActivity(), PreferenceActivity.class)); + }); + return root; + } + + private void updateSelection() { + String lastNavFragment = getLastNavFragment(getContext()); + int tagIndex = navAdapter.getTags().indexOf(lastNavFragment); + if (tagIndex >= 0) { + selectedNavListIndex = tagIndex; + } else if (StringUtils.isNumeric(lastNavFragment)) { // last fragment was not a list, but a feed + long feedId = Long.parseLong(lastNavFragment); + if (navDrawerData != null) { + List<Feed> feeds = navDrawerData.feeds; + for (int i = 0; i < feeds.size(); i++) { + if (feeds.get(i).getId() == feedId) { + selectedNavListIndex = navAdapter.getSubscriptionOffset() + i; + break; + } + } + } + } + navAdapter.notifyDataSetChanged(); + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + EventBus.getDefault().register(this); + } + + @Override + public void onDestroyView() { + super.onDestroyView(); + EventBus.getDefault().unregister(this); + if (disposable != null) { + disposable.dispose(); + } + } + + @Override + public void onCreateContextMenu(@NonNull ContextMenu menu, @NonNull View v, ContextMenu.ContextMenuInfo menuInfo) { + super.onCreateContextMenu(menu, v, menuInfo); + if (v.getId() != R.id.nav_list) { + return; + } + AdapterView.AdapterContextMenuInfo adapterInfo = (AdapterView.AdapterContextMenuInfo) menuInfo; + int position = adapterInfo.position; + if (position < navAdapter.getSubscriptionOffset()) { + return; + } + MenuInflater inflater = getActivity().getMenuInflater(); + inflater.inflate(R.menu.nav_feed_context, menu); + Feed feed = navDrawerData.feeds.get(position - navAdapter.getSubscriptionOffset()); + menu.setHeaderTitle(feed.getTitle()); + // episodes are not loaded, so we cannot check if the podcast has new or unplayed ones! + } + + @Override + public boolean onContextItemSelected(@NonNull MenuItem item) { + final int position = this.position; + this.position = -1; // reset + if (position < 0) { + return false; + } + Feed feed = navDrawerData.feeds.get(position - navAdapter.getSubscriptionOffset()); + switch (item.getItemId()) { + case R.id.remove_all_new_flags_item: + ConfirmationDialog removeAllNewFlagsConfirmationDialog = new ConfirmationDialog(getContext(), + R.string.remove_all_new_flags_label, + R.string.remove_all_new_flags_confirmation_msg) { + @Override + public void onConfirmButtonPressed(DialogInterface dialog) { + dialog.dismiss(); + DBWriter.removeFeedNewFlag(feed.getId()); + } + }; + removeAllNewFlagsConfirmationDialog.createNewDialog().show(); + return true; + case R.id.mark_all_read_item: + ConfirmationDialog markAllReadConfirmationDialog = new ConfirmationDialog(getContext(), + R.string.mark_all_read_label, + R.string.mark_all_read_confirmation_msg) { + + @Override + public void onConfirmButtonPressed(DialogInterface dialog) { + dialog.dismiss(); + DBWriter.markFeedRead(feed.getId()); + } + }; + markAllReadConfirmationDialog.createNewDialog().show(); + return true; + case R.id.rename_item: + new RenameFeedDialog(getActivity(), feed).show(); + return true; + case R.id.remove_item: + final FeedRemover remover = new FeedRemover(getContext(), feed) { + @Override + protected void onPostExecute(Void result) { + super.onPostExecute(result); + if (selectedNavListIndex == position) { + if (getActivity() instanceof MainActivity) { + ((MainActivity) getActivity()).loadFragment(EpisodesFragment.TAG, null); + } else { + showMainActivity(EpisodesFragment.TAG); + } + saveLastNavFragment(EpisodesFragment.TAG); + } + } + }; + ConfirmationDialog conDialog = new ConfirmationDialog(getContext(), + R.string.remove_feed_label, + getString(R.string.feed_delete_confirmation_msg, feed.getTitle())) { + @Override + public void onConfirmButtonPressed(DialogInterface dialog) { + dialog.dismiss(); + long mediaId = PlaybackPreferences.getCurrentlyPlayingFeedMediaId(); + if (mediaId > 0 && FeedItemUtil.indexOfItemWithMediaId(feed.getItems(), mediaId) >= 0) { + Log.d(TAG, "Currently playing episode is about to be deleted, skipping"); + remover.skipOnCompletion = true; + int playerStatus = PlaybackPreferences.getCurrentPlayerStatus(); + if (playerStatus == PlaybackPreferences.PLAYER_STATUS_PLAYING) { + IntentUtils.sendLocalBroadcast(getContext(), + PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE); + } + } + remover.executeAsync(); + } + }; + conDialog.createNewDialog().show(); + return true; + default: + return super.onContextItemSelected(item); + } + } + + private void showMainActivity(String tag) { + Intent intent = new Intent(getActivity(), MainActivity.class); + intent.putExtra(MainActivity.EXTRA_FRAGMENT_TAG, tag); + startActivity(intent); + } + + @Subscribe + public void onUnreadItemsChanged(UnreadItemsUpdateEvent event) { + loadData(); + } + + + @Subscribe + public void onFeedListChanged(FeedListUpdateEvent event) { + loadData(); + } + + @Subscribe + public void onQueueChanged(QueueEvent event) { + Log.d(TAG, "onQueueChanged(" + event + ")"); + // we are only interested in the number of queue items, not download status or position + if (event.action == QueueEvent.Action.DELETED_MEDIA + || event.action == QueueEvent.Action.SORTED + || event.action == QueueEvent.Action.MOVED) { + return; + } + loadData(); + } + + @Override + public void onResume() { + super.onResume(); + loadData(); + } + + private void showDrawerPreferencesDialog() { + final List<String> hiddenDrawerItems = UserPreferences.getHiddenDrawerItems(); + String[] navLabels = new String[NAV_DRAWER_TAGS.length]; + final boolean[] checked = new boolean[NAV_DRAWER_TAGS.length]; + for (int i = 0; i < NAV_DRAWER_TAGS.length; i++) { + String tag = NAV_DRAWER_TAGS[i]; + navLabels[i] = navAdapter.getLabel(tag); + if (!hiddenDrawerItems.contains(tag)) { + checked[i] = true; + } + } + + AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); + builder.setTitle(R.string.drawer_preferences); + builder.setMultiChoiceItems(navLabels, checked, (dialog, which, isChecked) -> { + if (isChecked) { + hiddenDrawerItems.remove(NAV_DRAWER_TAGS[which]); + } else { + hiddenDrawerItems.add(NAV_DRAWER_TAGS[which]); + } + }); + builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> { + UserPreferences.setHiddenDrawerItems(hiddenDrawerItems); + updateSelection(); + }); + builder.setNegativeButton(R.string.cancel_label, null); + builder.create().show(); + } + + private final NavListAdapter.ItemAccess itemAccess = new NavListAdapter.ItemAccess() { + @Override + public int getCount() { + if (navDrawerData != null) { + return navDrawerData.feeds.size(); + } else { + return 0; + } + } + + @Override + public Feed getItem(int position) { + if (navDrawerData != null && 0 <= position && position < navDrawerData.feeds.size()) { + return navDrawerData.feeds.get(position); + } else { + return null; + } + } + + @Override + public int getSelectedItemIndex() { + return selectedNavListIndex; + } + + @Override + public int getQueueSize() { + return (navDrawerData != null) ? navDrawerData.queueSize : 0; + } + + @Override + public int getNumberOfNewItems() { + return (navDrawerData != null) ? navDrawerData.numNewItems : 0; + } + + @Override + public int getNumberOfDownloadedItems() { + return (navDrawerData != null) ? navDrawerData.numDownloadedItems : 0; + } + + @Override + public int getReclaimableItems() { + return (navDrawerData != null) ? navDrawerData.reclaimableSpace : 0; + } + + @Override + public int getFeedCounter(long feedId) { + return navDrawerData != null ? navDrawerData.feedCounters.get(feedId) : 0; + } + + @Override + public int getFeedCounterSum() { + if (navDrawerData == null) { + return 0; + } + int sum = 0; + for (int counter : navDrawerData.feedCounters.values()) { + sum += counter; + } + return sum; + } + + }; + + private void loadData() { + disposable = Observable.fromCallable(DBReader::getNavDrawerData) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(result -> { + navDrawerData = result; + updateSelection(); // Selected item might be a feed + navAdapter.notifyDataSetChanged(); + }, error -> Log.e(TAG, Log.getStackTraceString(error))); + } + + @Override + public void onItemClick(AdapterView<?> parent, View view, int position, long id) { + int viewType = parent.getAdapter().getItemViewType(position); + if (viewType != NavListAdapter.VIEW_TYPE_SECTION_DIVIDER) { + if (position < navAdapter.getSubscriptionOffset()) { + String tag = navAdapter.getTags().get(position); + if (getActivity() instanceof MainActivity) { + ((MainActivity) getActivity()).loadFragment(tag, null); + } else { + showMainActivity(tag); + } + saveLastNavFragment(tag); + } else { + int pos = position - navAdapter.getSubscriptionOffset(); + long feedId = navDrawerData.feeds.get(pos).getId(); + if (getActivity() instanceof MainActivity) { + ((MainActivity) getActivity()).loadFeedFragmentById(feedId, null); + } else { + Intent intent = new Intent(getActivity(), MainActivity.class); + intent.putExtra(MainActivity.EXTRA_FEED_ID, feedId); + startActivity(intent); + } + saveLastNavFragment(String.valueOf(feedId)); + } + selectedNavListIndex = position; + navAdapter.notifyDataSetChanged(); + } + } + + @Override + public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { + if (position < navAdapter.getTags().size()) { + showDrawerPreferencesDialog(); + return true; + } else { + this.position = position; + return false; + } + } + + private void saveLastNavFragment(String tag) { + Log.d(TAG, "saveLastNavFragment(tag: " + tag + ")"); + SharedPreferences prefs = getContext().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE); + SharedPreferences.Editor edit = prefs.edit(); + if (tag != null) { + edit.putString(PREF_LAST_FRAGMENT_TAG, tag); + } else { + edit.remove(PREF_LAST_FRAGMENT_TAG); + } + edit.apply(); + } + + public static String getLastNavFragment(Context context) { + SharedPreferences prefs = context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE); + String lastFragment = prefs.getString(PREF_LAST_FRAGMENT_TAG, QueueFragment.TAG); + Log.d(TAG, "getLastNavFragment() -> " + lastFragment); + return lastFragment; + } +} diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/UserInterfacePreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/UserInterfacePreferencesFragment.java index fe2baf92b..c3611d683 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/UserInterfacePreferencesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/UserInterfacePreferencesFragment.java @@ -13,6 +13,7 @@ import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.activity.PreferenceActivity; import de.danoeh.antennapod.core.preferences.UserPreferences; +import de.danoeh.antennapod.fragment.NavDrawerFragment; import org.apache.commons.lang3.ArrayUtils; import java.util.List; @@ -89,11 +90,11 @@ public class UserInterfacePreferencesFragment extends PreferenceFragmentCompat { final Context context = getActivity(); final List<String> hiddenDrawerItems = UserPreferences.getHiddenDrawerItems(); final String[] navTitles = context.getResources().getStringArray(R.array.nav_drawer_titles); - final String[] NAV_DRAWER_TAGS = MainActivity.NAV_DRAWER_TAGS; - boolean[] checked = new boolean[MainActivity.NAV_DRAWER_TAGS.length]; - for(int i=0; i < NAV_DRAWER_TAGS.length; i++) { + final String[] NAV_DRAWER_TAGS = NavDrawerFragment.NAV_DRAWER_TAGS; + boolean[] checked = new boolean[NavDrawerFragment.NAV_DRAWER_TAGS.length]; + for (int i = 0; i < NAV_DRAWER_TAGS.length; i++) { String tag = NAV_DRAWER_TAGS[i]; - if(!hiddenDrawerItems.contains(tag)) { + if (!hiddenDrawerItems.contains(tag)) { checked[i] = true; } } diff --git a/app/src/main/java/de/danoeh/antennapod/menuhandler/NavDrawerActivity.java b/app/src/main/java/de/danoeh/antennapod/menuhandler/NavDrawerActivity.java deleted file mode 100644 index c973990c9..000000000 --- a/app/src/main/java/de/danoeh/antennapod/menuhandler/NavDrawerActivity.java +++ /dev/null @@ -1,9 +0,0 @@ -package de.danoeh.antennapod.menuhandler; - -/** - * Defines useful methods for activities that have a navigation drawer - */ -public interface NavDrawerActivity { - - boolean isDrawerOpen(); -} diff --git a/app/src/main/res/layout/main.xml b/app/src/main/res/layout/main.xml index 5c06dc027..89b7e0c47 100644 --- a/app/src/main/res/layout/main.xml +++ b/app/src/main/res/layout/main.xml @@ -30,6 +30,13 @@ </RelativeLayout> - <include layout="@layout/nav_list" /> + <FrameLayout + android:id="@+id/navDrawerFragment" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_marginEnd="24dp" + android:layout_marginRight="24dp" + android:layout_gravity="start" + android:orientation="vertical" /> </androidx.drawerlayout.widget.DrawerLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/mediaplayerinfo_activity.xml b/app/src/main/res/layout/mediaplayerinfo_activity.xml index c46672ceb..526994752 100644 --- a/app/src/main/res/layout/mediaplayerinfo_activity.xml +++ b/app/src/main/res/layout/mediaplayerinfo_activity.xml @@ -236,6 +236,13 @@ </RelativeLayout> - <include layout="@layout/nav_list" /> + <FrameLayout + android:id="@+id/navDrawerFragment" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:layout_marginEnd="24dp" + android:layout_marginRight="24dp" + android:layout_gravity="start" + android:orientation="vertical" /> </androidx.drawerlayout.widget.DrawerLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/nav_list.xml b/app/src/main/res/layout/nav_list.xml index d6b8a540d..504952b08 100644 --- a/app/src/main/res/layout/nav_list.xml +++ b/app/src/main/res/layout/nav_list.xml @@ -4,11 +4,7 @@ android:id="@+id/nav_layout" android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_marginEnd="24dp" - android:layout_marginRight="24dp" - android:layout_gravity="start" - android:background="?android:attr/windowBackground" - android:orientation="vertical"> + android:background="?android:attr/windowBackground"> <LinearLayout android:id="@+id/nav_settings" @@ -68,6 +64,7 @@ android:choiceMode="singleChoice" android:clipToPadding="false" android:divider="@android:color/transparent" + android:layout_alignParentTop="true" android:dividerHeight="0dp" android:paddingBottom="@dimen/list_vertical_padding" android:paddingTop="8dp" |