Merge pull request #3943 from ByteHamster/nav-list-fragment

Nav list fragment
This commit is contained in:
H. Lehmann 2020-03-19 21:56:39 +01:00 committed by GitHub
commit d7cfa07c44
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 512 additions and 743 deletions

View File

@ -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();
}

View File

@ -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));
}
}

View File

@ -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));
}
}

View File

@ -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

View File

@ -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 android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
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 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 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);
}
}
@ -168,12 +114,6 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem
pager.setCurrentItem(lastPosition);
}
@Override
protected void onStart() {
super.onStart();
loadData();
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(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";

View File

@ -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];
}

View File

@ -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);

View File

@ -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;
}
}

View File

@ -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;
}
}

View File

@ -1,9 +0,0 @@
package de.danoeh.antennapod.menuhandler;
/**
* Defines useful methods for activities that have a navigation drawer
*/
public interface NavDrawerActivity {
boolean isDrawerOpen();
}

View File

@ -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>

View File

@ -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>

View File

@ -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"