summaryrefslogtreecommitdiff
path: root/app/src/free
diff options
context:
space:
mode:
Diffstat (limited to 'app/src/free')
-rw-r--r--app/src/free/java/de/danoeh/antennapod/activity/CastEnabledActivity.java218
-rw-r--r--app/src/free/java/de/danoeh/antennapod/activity/MainActivity.java773
-rw-r--r--app/src/free/java/de/danoeh/antennapod/activity/MediaplayerActivity.java876
-rw-r--r--app/src/free/java/de/danoeh/antennapod/fragment/ItemFragment.java590
-rw-r--r--app/src/free/java/de/danoeh/antennapod/preferences/PreferenceController.java951
5 files changed, 3408 insertions, 0 deletions
diff --git a/app/src/free/java/de/danoeh/antennapod/activity/CastEnabledActivity.java b/app/src/free/java/de/danoeh/antennapod/activity/CastEnabledActivity.java
new file mode 100644
index 000000000..d82b199db
--- /dev/null
+++ b/app/src/free/java/de/danoeh/antennapod/activity/CastEnabledActivity.java
@@ -0,0 +1,218 @@
+package de.danoeh.antennapod.activity;
+
+import android.support.v7.app.AppCompatActivity;
+
+/**
+ * Activity that allows for showing the MediaRouter button whenever there's a cast device in the
+ * network.
+ */
+public abstract class CastEnabledActivity extends AppCompatActivity {
+// implements SharedPreferences.OnSharedPreferenceChangeListener {
+ public static final String TAG = "CastEnabledActivity";
+
+// protected CastManager castManager;
+// protected SwitchableMediaRouteActionProvider mediaRouteActionProvider;
+// private final CastButtonVisibilityManager castButtonVisibilityManager = new CastButtonVisibilityManager();
+//
+// @Override
+// protected void onCreate(Bundle savedInstanceState) {
+// super.onCreate(savedInstanceState);
+//
+// PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).
+// registerOnSharedPreferenceChangeListener(this);
+//
+// castManager = CastManager.getInstance();
+// castManager.addCastConsumer(castConsumer);
+// castButtonVisibilityManager.setPrefEnabled(UserPreferences.isCastEnabled());
+// onCastConnectionChanged(castManager.isConnected());
+// }
+//
+// @Override
+// protected void onDestroy() {
+// PreferenceManager.getDefaultSharedPreferences(getApplicationContext())
+// .unregisterOnSharedPreferenceChangeListener(this);
+// castManager.removeCastConsumer(castConsumer);
+// super.onDestroy();
+// }
+//
+// @Override
+// @CallSuper
+// public boolean onCreateOptionsMenu(Menu menu) {
+// super.onCreateOptionsMenu(menu);
+// getMenuInflater().inflate(R.menu.cast_enabled, menu);
+// castButtonVisibilityManager.setMenu(menu);
+// return true;
+// }
+//
+// @Override
+// @CallSuper
+// public boolean onPrepareOptionsMenu(Menu menu) {
+// super.onPrepareOptionsMenu(menu);
+// mediaRouteActionProvider = castManager
+// .addMediaRouterButton(menu.findItem(R.id.media_route_menu_item));
+// mediaRouteActionProvider.setEnabled(castButtonVisibilityManager.shouldEnable());
+// return true;
+// }
+//
+// @Override
+// protected void onResume() {
+// super.onResume();
+// castButtonVisibilityManager.setResumed(true);
+// }
+//
+// @Override
+// protected void onPause() {
+// super.onPause();
+// castButtonVisibilityManager.setResumed(false);
+// }
+//
+// @Override
+// public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+// if (UserPreferences.PREF_CAST_ENABLED.equals(key)) {
+// boolean newValue = UserPreferences.isCastEnabled();
+// Log.d(TAG, "onSharedPreferenceChanged(), isCastEnabled set to " + newValue);
+// castButtonVisibilityManager.setPrefEnabled(newValue);
+// // PlaybackService has its own listener, so if it's active we don't have to take action here.
+// if (!newValue && !PlaybackService.isRunning) {
+// CastManager.getInstance().disconnect();
+// }
+// }
+// }
+//
+// CastConsumer castConsumer = new DefaultCastConsumer() {
+// @Override
+// public void onApplicationConnected(ApplicationMetadata appMetadata, String sessionId, boolean wasLaunched) {
+// onCastConnectionChanged(true);
+// }
+//
+// @Override
+// public void onDisconnected() {
+// onCastConnectionChanged(false);
+// }
+// };
+//
+// private void onCastConnectionChanged(boolean connected) {
+// if (connected) {
+// castButtonVisibilityManager.onConnected();
+// setVolumeControlStream(AudioManager.USE_DEFAULT_STREAM_TYPE);
+// } else {
+// castButtonVisibilityManager.onDisconnected();
+// setVolumeControlStream(AudioManager.STREAM_MUSIC);
+// }
+// }
+//
+// /**
+// * Should be called by any activity or fragment for which the cast button should be shown.
+// *
+// * @param showAsAction refer to {@link MenuItem#setShowAsAction(int)}
+// */
+// public final void requestCastButton(int showAsAction) {
+// castButtonVisibilityManager.requestCastButton(showAsAction);
+// }
+//
+// private class CastButtonVisibilityManager {
+// private volatile boolean prefEnabled = false;
+// private volatile boolean viewRequested = false;
+// private volatile boolean resumed = false;
+// private volatile boolean connected = false;
+// private volatile int showAsAction = MenuItem.SHOW_AS_ACTION_IF_ROOM;
+// private Menu menu;
+//
+// public synchronized void setPrefEnabled(boolean newValue) {
+// if (prefEnabled != newValue && resumed && (viewRequested || connected)) {
+// if (newValue) {
+// castManager.incrementUiCounter();
+// } else {
+// castManager.decrementUiCounter();
+// }
+// }
+// prefEnabled = newValue;
+// if (mediaRouteActionProvider != null) {
+// mediaRouteActionProvider.setEnabled(prefEnabled && (viewRequested || connected));
+// }
+// }
+//
+// public synchronized void setResumed(boolean newValue) {
+// if (resumed == newValue) {
+// Log.e(TAG, "resumed should never change to the same value");
+// return;
+// }
+// resumed = newValue;
+// if (prefEnabled && (viewRequested || connected)) {
+// if (resumed) {
+// castManager.incrementUiCounter();
+// } else {
+// castManager.decrementUiCounter();
+// }
+// }
+// }
+//
+// public synchronized void setViewRequested(boolean newValue) {
+// if (viewRequested != newValue && resumed && prefEnabled && !connected) {
+// if (newValue) {
+// castManager.incrementUiCounter();
+// } else {
+// castManager.decrementUiCounter();
+// }
+// }
+// viewRequested = newValue;
+// if (mediaRouteActionProvider != null) {
+// mediaRouteActionProvider.setEnabled(prefEnabled && (viewRequested || connected));
+// }
+// }
+//
+// public synchronized void setConnected(boolean newValue) {
+// if (connected != newValue && resumed && prefEnabled && !prefEnabled) {
+// if (newValue) {
+// castManager.incrementUiCounter();
+// } else {
+// castManager.decrementUiCounter();
+// }
+// }
+// connected = newValue;
+// if (mediaRouteActionProvider != null) {
+// mediaRouteActionProvider.setEnabled(prefEnabled && (viewRequested || connected));
+// }
+// }
+//
+// public synchronized boolean shouldEnable() {
+// return prefEnabled && viewRequested;
+// }
+//
+// public void setMenu(Menu menu) {
+// setViewRequested(false);
+// showAsAction = MenuItem.SHOW_AS_ACTION_IF_ROOM;
+// this.menu = menu;
+// setShowAsAction();
+// }
+//
+// public void requestCastButton(int showAsAction) {
+// setViewRequested(true);
+// this.showAsAction = showAsAction;
+// setShowAsAction();
+// }
+//
+// public void onConnected() {
+// setConnected(true);
+// setShowAsAction();
+// }
+//
+// public void onDisconnected() {
+// setConnected(false);
+// setShowAsAction();
+// }
+//
+// private void setShowAsAction() {
+// if (menu == null) {
+// Log.d(TAG, "setShowAsAction() without a menu");
+// return;
+// }
+// MenuItem item = menu.findItem(R.id.media_route_menu_item);
+// if (item == null) {
+// Log.e(TAG, "setShowAsAction(), but cast button not inflated");
+// return;
+// }
+// MenuItemCompat.setShowAsAction(item, connected? MenuItem.SHOW_AS_ACTION_ALWAYS : showAsAction);
+// }
+// }
+}
diff --git a/app/src/free/java/de/danoeh/antennapod/activity/MainActivity.java b/app/src/free/java/de/danoeh/antennapod/activity/MainActivity.java
new file mode 100644
index 000000000..c4771a583
--- /dev/null
+++ b/app/src/free/java/de/danoeh/antennapod/activity/MainActivity.java
@@ -0,0 +1,773 @@
+package de.danoeh.antennapod.activity;
+
+import android.annotation.TargetApi;
+import android.app.ProgressDialog;
+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.support.v4.app.Fragment;
+import android.support.v4.app.FragmentManager;
+import android.support.v4.app.FragmentTransaction;
+import android.support.v4.widget.DrawerLayout;
+import android.support.v7.app.ActionBarDrawerToggle;
+import android.support.v7.app.AlertDialog;
+import android.support.v7.widget.Toolbar;
+import android.util.Log;
+import android.util.TypedValue;
+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 com.bumptech.glide.Glide;
+
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.Validate;
+
+import java.util.List;
+
+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.ProgressEvent;
+import de.danoeh.antennapod.core.event.QueueEvent;
+import de.danoeh.antennapod.core.feed.EventDistributor;
+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.StorageUtils;
+import de.danoeh.antennapod.dialog.RatingDialog;
+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.ItemlistFragment;
+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.preferences.PreferenceController;
+import de.greenrobot.event.EventBus;
+import rx.Observable;
+import rx.Subscription;
+import rx.android.schedulers.AndroidSchedulers;
+import rx.schedulers.Schedulers;
+
+/**
+ * The activity that is shown when the user launches the app.
+ */
+public class MainActivity extends CastEnabledActivity implements NavDrawerActivity {
+
+ private static final String TAG = "MainActivity";
+
+ private static final int EVENTS = EventDistributor.FEED_LIST_UPDATE
+ | EventDistributor.UNREAD_ITEMS_UPDATE;
+
+ 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";
+ public static final String EXTRA_FEED_ID = "fragment_feed_id";
+
+ public static final String SAVE_BACKSTACK_COUNT = "backstackCount";
+ public static final String SAVE_TITLE = "title";
+
+ public static final String[] NAV_DRAWER_TAGS = {
+ QueueFragment.TAG,
+ EpisodesFragment.TAG,
+ SubscriptionFragment.TAG,
+ DownloadsFragment.TAG,
+ PlaybackHistoryFragment.TAG,
+ AddFeedFragment.TAG,
+ NavListAdapter.SUBSCRIPTION_LIST_TAG
+ };
+
+ private Toolbar toolbar;
+ private ExternalPlayerFragment externalPlayerFragment;
+ private DrawerLayout drawerLayout;
+
+ private View navDrawer;
+ private ListView navList;
+ private NavListAdapter navAdapter;
+ private int mPosition = -1;
+
+ private ActionBarDrawerToggle drawerToggle;
+
+ private CharSequence currentTitle;
+
+ private ProgressDialog pd;
+
+ private Subscription subscription;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ setTheme(UserPreferences.getNoTitleTheme());
+ super.onCreate(savedInstanceState);
+ StorageUtils.checkStorageAvailability(this);
+ setContentView(R.layout.main);
+
+ toolbar = (Toolbar) findViewById(R.id.toolbar);
+ setSupportActionBar(toolbar);
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ findViewById(R.id.shadow).setVisibility(View.GONE);
+ int elevation = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4,
+ getResources().getDisplayMetrics());
+ getSupportActionBar().setElevation(elevation);
+ }
+
+ currentTitle = getTitle();
+
+ drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
+ navList = (ListView) findViewById(R.id.nav_list);
+ navDrawer = findViewById(R.id.nav_layout);
+
+ drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, R.string.drawer_open, R.string.drawer_close);
+ if (savedInstanceState != null) {
+ int backstackCount = savedInstanceState.getInt(SAVE_BACKSTACK_COUNT, 0);
+ drawerToggle.setDrawerIndicatorEnabled(backstackCount == 0);
+ }
+ drawerLayout.setDrawerListener(drawerToggle);
+
+ final FragmentManager fm = getSupportFragmentManager();
+
+ fm.addOnBackStackChangedListener(() -> {
+ drawerToggle.setDrawerIndicatorEnabled(fm.getBackStackEntryCount() == 0);
+ });
+
+ getSupportActionBar().setDisplayHomeAsUpEnabled(true);
+ getSupportActionBar().setHomeButtonEnabled(true);
+
+ 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, PreferenceController.getPreferenceActivity()));
+ });
+
+ FragmentTransaction transaction = fm.beginTransaction();
+
+ Fragment mainFragment = fm.findFragmentByTag("main");
+ if (mainFragment != null) {
+ transaction.replace(R.id.main_view, mainFragment);
+ } else {
+ String lastFragment = getLastNavFragment();
+ if (ArrayUtils.contains(NAV_DRAWER_TAGS, lastFragment)) {
+ loadFragment(lastFragment, null);
+ } else {
+ try {
+ loadFeedFragmentById(Integer.parseInt(lastFragment), null);
+ } catch (NumberFormatException e) {
+ // it's not a number, this happens if we removed
+ // a label from the NAV_DRAWER_TAGS
+ // give them a nice default...
+ loadFragment(QueueFragment.TAG, null);
+ }
+ }
+ }
+ externalPlayerFragment = new ExternalPlayerFragment();
+ transaction.replace(R.id.playerFragment, externalPlayerFragment, ExternalPlayerFragment.TAG);
+ transaction.commit();
+
+ checkFirstLaunch();
+ }
+
+ 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.commit();
+ }
+
+ 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)) {
+ new Handler().postDelayed(() -> drawerLayout.openDrawer(navDrawer), 1500);
+
+ // for backward compatibility, we only change defaults for fresh installs
+ UserPreferences.setUpdateInterval(12);
+
+ SharedPreferences.Editor edit = prefs.edit();
+ edit.putBoolean(PREF_IS_FIRST_LAUNCH, false);
+ edit.commit();
+ }
+ }
+
+ public 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);
+ }
+
+ public List<Feed> getFeeds() {
+ return (navDrawerData != null) ? navDrawerData.feeds : null;
+ }
+
+ public 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;
+ switch (tag) {
+ case QueueFragment.TAG:
+ fragment = new QueueFragment();
+ break;
+ case EpisodesFragment.TAG:
+ fragment = new EpisodesFragment();
+ break;
+ case DownloadsFragment.TAG:
+ fragment = new DownloadsFragment();
+ break;
+ case PlaybackHistoryFragment.TAG:
+ fragment = new PlaybackHistoryFragment();
+ break;
+ case AddFeedFragment.TAG:
+ fragment = new AddFeedFragment();
+ break;
+ case SubscriptionFragment.TAG:
+ SubscriptionFragment subscriptionFragment = new SubscriptionFragment();
+ fragment = subscriptionFragment;
+ break;
+ default:
+ // default to the queue
+ tag = QueueFragment.TAG;
+ fragment = new QueueFragment();
+ args = null;
+ break;
+ }
+ currentTitle = navAdapter.getLabel(tag);
+ getSupportActionBar().setTitle(currentTitle);
+ 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 = ItemlistFragment.newInstance(feedId);
+ if(args != null) {
+ fragment.setArguments(args);
+ }
+ saveLastNavFragment(String.valueOf(feedId));
+ currentTitle = "";
+ getSupportActionBar().setTitle(currentTitle);
+ loadFragment(fragment);
+ }
+
+ private void loadFragment(Fragment fragment) {
+ FragmentManager fragmentManager = getSupportFragmentManager();
+ // clear back stack
+ for (int i = 0; i < fragmentManager.getBackStackEntryCount(); i++) {
+ fragmentManager.popBackStack();
+ }
+ FragmentTransaction t = fragmentManager.beginTransaction();
+ t.replace(R.id.main_view, fragment, "main");
+ fragmentManager.popBackStack();
+ // TODO: we have to allow state loss here
+ // since this function can get called from an AsyncTask which
+ // could be finishing after our app has already committed state
+ // and is about to get shutdown. What we *should* do is
+ // not commit anything in an AsyncTask, but that's a bigger
+ // change than we want now.
+ t.commitAllowingStateLoss();
+ if (navAdapter != null) {
+ navAdapter.notifyDataSetChanged();
+ }
+ }
+
+ public void loadChildFragment(Fragment fragment) {
+ Validate.notNull(fragment);
+ FragmentManager fm = getSupportFragmentManager();
+ fm.beginTransaction()
+ .replace(R.id.main_view, fragment, "main")
+ .addToBackStack(null)
+ .commit();
+ }
+
+ public void dismissChildFragment() {
+ getSupportFragmentManager().popBackStack();
+ }
+
+ 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 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 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) {
+ currentTitle = savedInstanceState.getString(SAVE_TITLE);
+ if (!drawerLayout.isDrawerOpen(navDrawer)) {
+ getSupportActionBar().setTitle(currentTitle);
+ }
+ selectedNavListIndex = getSelectedNavListIndex();
+ }
+ }
+
+ @Override
+ public void onConfigurationChanged(Configuration newConfig) {
+ super.onConfigurationChanged(newConfig);
+ drawerToggle.onConfigurationChanged(newConfig);
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putString(SAVE_TITLE, getSupportActionBar().getTitle().toString());
+ outState.putInt(SAVE_BACKSTACK_COUNT, getSupportFragmentManager().getBackStackEntryCount());
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ EventDistributor.getInstance().register(contentUpdate);
+ EventBus.getDefault().register(this);
+ RatingDialog.init(this);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+ }
+
+ @Override
+ 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();
+ RatingDialog.check();
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+ EventDistributor.getInstance().unregister(contentUpdate);
+ EventBus.getDefault().unregister(this);
+ if(subscription != null) {
+ subscription.unsubscribe();
+ }
+ if(pd != null) {
+ pd.dismiss();
+ }
+ }
+
+
+ @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+ @Override
+ public void onTrimMemory(int level) {
+ super.onTrimMemory(level);
+ Glide.get(this).trimMemory(level);
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ Glide.get(this).clearMemory();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ boolean retVal = super.onCreateOptionsMenu(menu);
+ switch (getLastNavFragment()) {
+ case QueueFragment.TAG:
+ case EpisodesFragment.TAG:
+// requestCastButton(MenuItem.SHOW_AS_ACTION_IF_ROOM);
+ return retVal;
+ case DownloadsFragment.TAG:
+ case PlaybackHistoryFragment.TAG:
+ case AddFeedFragment.TAG:
+ case SubscriptionFragment.TAG:
+ return retVal;
+ default:
+// requestCastButton(MenuItem.SHOW_AS_ACTION_NEVER);
+ return retVal;
+ }
+
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (drawerToggle.onOptionsItemSelected(item)) {
+ return true;
+ } else if (item.getItemId() == android.R.id.home) {
+ if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
+ dismissChildFragment();
+ }
+ return true;
+ } else {
+ return 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.mark_all_seen_item:
+ DBWriter.markFeedSeen(feed.getId());
+ return true;
+ case R.id.mark_all_read_item:
+ DBWriter.markFeedRead(feed.getId());
+ 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,
+ R.string.feed_delete_confirmation_msg) {
+ @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) {
+ sendBroadcast(new Intent(
+ PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE));
+ }
+ }
+ remover.executeAsync();
+ }
+ };
+ conDialog.createNewDialog().show();
+ return true;
+ default:
+ return super.onContextItemSelected(item);
+ }
+ }
+
+ @Override
+ public void onBackPressed() {
+ if(isDrawerOpen()) {
+ drawerLayout.closeDrawer(navDrawer);
+ } else {
+ super.onBackPressed();
+ }
+ }
+
+ private DBReader.NavDrawerData navDrawerData;
+ private int selectedNavListIndex = 0;
+
+ private 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() {
+ subscription = Observable.fromCallable(DBReader::getNavDrawerData)
+ .subscribeOn(Schedulers.newThread())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(result -> {
+ boolean handleIntent = (navDrawerData == null);
+
+ navDrawerData = result;
+ navAdapter.notifyDataSetChanged();
+
+ if (handleIntent) {
+ handleNavIntent();
+ }
+ }, error -> {
+ Log.e(TAG, Log.getStackTraceString(error));
+ });
+ }
+
+ 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();
+ }
+
+ public void onEventMainThread(ProgressEvent event) {
+ Log.d(TAG, "onEvent(" + event + ")");
+ switch(event.action) {
+ case START:
+ pd = new ProgressDialog(this);
+ pd.setMessage(event.message);
+ pd.setIndeterminate(true);
+ pd.setCancelable(false);
+ pd.show();
+ break;
+ case END:
+ if(pd != null) {
+ pd.dismiss();
+ }
+ break;
+ }
+ }
+
+ private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
+
+ @Override
+ public void update(EventDistributor eventDistributor, Integer arg) {
+ if ((EVENTS & arg) != 0) {
+ Log.d(TAG, "Received contentUpdate Intent.");
+ 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);
+ 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) {
+ loadFragment(tag, args);
+ } else if(feedId > 0) {
+ loadFeedFragmentById(feedId, args);
+ }
+ }
+ setIntent(new Intent(MainActivity.this, MainActivity.class)); // to avoid handling the intent twice when the configuration changes
+ }
+
+ @Override
+ protected void onNewIntent(Intent intent) {
+ super.onNewIntent(intent);
+ setIntent(intent);
+ }
+}
diff --git a/app/src/free/java/de/danoeh/antennapod/activity/MediaplayerActivity.java b/app/src/free/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
new file mode 100644
index 000000000..0d6e5504a
--- /dev/null
+++ b/app/src/free/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
@@ -0,0 +1,876 @@
+package de.danoeh.antennapod.activity;
+
+import android.annotation.TargetApi;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.res.TypedArray;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.v7.app.AlertDialog;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.ImageButton;
+import android.widget.SeekBar;
+import android.widget.SeekBar.OnSeekBarChangeListener;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.afollestad.materialdialogs.MaterialDialog;
+import com.bumptech.glide.Glide;
+import com.joanzapata.iconify.IconDrawable;
+import com.joanzapata.iconify.fonts.FontAwesomeIcons;
+
+import java.util.Locale;
+
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.core.feed.FeedItem;
+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.storage.DBReader;
+import de.danoeh.antennapod.core.storage.DBTasks;
+import de.danoeh.antennapod.core.storage.DBWriter;
+import de.danoeh.antennapod.core.util.Converter;
+import de.danoeh.antennapod.core.util.ShareUtils;
+import de.danoeh.antennapod.core.util.StorageUtils;
+import de.danoeh.antennapod.core.util.playback.MediaPlayerError;
+import de.danoeh.antennapod.core.util.playback.Playable;
+import de.danoeh.antennapod.core.util.playback.PlaybackController;
+import de.danoeh.antennapod.dialog.SleepTimerDialog;
+import de.danoeh.antennapod.dialog.VariableSpeedDialog;
+import rx.Observable;
+import rx.android.schedulers.AndroidSchedulers;
+import rx.schedulers.Schedulers;
+
+
+/**
+ * Provides general features which are both needed for playing audio and video
+ * files.
+ */
+public abstract class MediaplayerActivity extends CastEnabledActivity implements OnSeekBarChangeListener {
+ private static final String TAG = "MediaplayerActivity";
+ private static final String PREFS = "MediaPlayerActivityPreferences";
+ private static final String PREF_SHOW_TIME_LEFT = "showTimeLeft";
+
+ protected PlaybackController controller;
+
+ protected TextView txtvPosition;
+ protected TextView txtvLength;
+ protected SeekBar sbPosition;
+ protected ImageButton butRev;
+ protected TextView txtvRev;
+ protected ImageButton butPlay;
+ protected ImageButton butFF;
+ protected TextView txtvFF;
+ protected ImageButton butSkip;
+
+ protected boolean showTimeLeft = false;
+
+ private boolean isFavorite = false;
+
+ private PlaybackController newPlaybackController() {
+ return new PlaybackController(this, false) {
+
+ @Override
+ public void setupGUI() {
+ MediaplayerActivity.this.setupGUI();
+ }
+
+ @Override
+ public void onPositionObserverUpdate() {
+ MediaplayerActivity.this.onPositionObserverUpdate();
+ }
+
+ @Override
+ public void onBufferStart() {
+ MediaplayerActivity.this.onBufferStart();
+ }
+
+ @Override
+ public void onBufferEnd() {
+ MediaplayerActivity.this.onBufferEnd();
+ }
+
+ @Override
+ public void onBufferUpdate(float progress) {
+ MediaplayerActivity.this.onBufferUpdate(progress);
+ }
+
+ @Override
+ public void handleError(int code) {
+ MediaplayerActivity.this.handleError(code);
+ }
+
+ @Override
+ public void onReloadNotification(int code) {
+ MediaplayerActivity.this.onReloadNotification(code);
+ }
+
+ @Override
+ public void onSleepTimerUpdate() {
+ supportInvalidateOptionsMenu();
+ }
+
+ @Override
+ public ImageButton getPlayButton() {
+ return butPlay;
+ }
+
+ @Override
+ public void postStatusMsg(int msg, boolean showToast) {
+ MediaplayerActivity.this.postStatusMsg(msg, showToast);
+ }
+
+ @Override
+ public void clearStatusMsg() {
+ MediaplayerActivity.this.clearStatusMsg();
+ }
+
+ @Override
+ public boolean loadMediaInfo() {
+ return MediaplayerActivity.this.loadMediaInfo();
+ }
+
+ @Override
+ public void onAwaitingVideoSurface() {
+ MediaplayerActivity.this.onAwaitingVideoSurface();
+ }
+
+ @Override
+ public void onServiceQueried() {
+ MediaplayerActivity.this.onServiceQueried();
+ }
+
+ @Override
+ public void onShutdownNotification() {
+ finish();
+ }
+
+ @Override
+ public void onPlaybackEnd() {
+ finish();
+ }
+
+ @Override
+ public void onPlaybackSpeedChange() {
+ MediaplayerActivity.this.onPlaybackSpeedChange();
+ }
+
+ @Override
+ protected void setScreenOn(boolean enable) {
+ super.setScreenOn(enable);
+ MediaplayerActivity.this.setScreenOn(enable);
+ }
+
+ @Override
+ public void onSetSpeedAbilityChanged() {
+ MediaplayerActivity.this.onSetSpeedAbilityChanged();
+ }
+ };
+ }
+
+ protected void onSetSpeedAbilityChanged() {
+ Log.d(TAG, "onSetSpeedAbilityChanged()");
+ updatePlaybackSpeedButton();
+ }
+
+ protected void onPlaybackSpeedChange() {
+ updatePlaybackSpeedButtonText();
+ }
+
+ protected void onServiceQueried() {
+ supportInvalidateOptionsMenu();
+ }
+
+ protected void chooseTheme() {
+ setTheme(UserPreferences.getTheme());
+ }
+
+ protected void setScreenOn(boolean enable) {
+ }
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ chooseTheme();
+ super.onCreate(savedInstanceState);
+
+ Log.d(TAG, "onCreate()");
+ StorageUtils.checkStorageAvailability(this);
+
+ orientation = getResources().getConfiguration().orientation;
+ getWindow().setFormat(PixelFormat.TRANSPARENT);
+ }
+
+ @Override
+ protected void onPause() {
+ if(controller != null) {
+ controller.reinitServiceIfPaused();
+ controller.pause();
+ }
+ super.onPause();
+ }
+
+ /**
+ * Should be used to switch to another player activity if the mime type is
+ * not the correct one for the current activity.
+ */
+ protected abstract void onReloadNotification(int notificationCode);
+
+ /**
+ * Should be used to inform the user that the PlaybackService is currently
+ * buffering.
+ */
+ protected abstract void onBufferStart();
+
+ /**
+ * Should be used to hide the view that was showing the 'buffering'-message.
+ */
+ protected abstract void onBufferEnd();
+
+ protected void onBufferUpdate(float progress) {
+ if (sbPosition != null) {
+ sbPosition.setSecondaryProgress((int) progress * sbPosition.getMax());
+ }
+ }
+
+ /**
+ * Current screen orientation.
+ */
+ protected int orientation;
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ if (controller != null) {
+ controller.release();
+ }
+ controller = newPlaybackController();
+ }
+
+ @Override
+ protected void onStop() {
+ Log.d(TAG, "onStop()");
+ if (controller != null) {
+ controller.release();
+ controller = null; // prevent leak
+ }
+ super.onStop();
+ }
+
+ @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
+ @Override
+ public void onTrimMemory(int level) {
+ super.onTrimMemory(level);
+ Glide.get(this).trimMemory(level);
+ }
+
+ @Override
+ public void onLowMemory() {
+ super.onLowMemory();
+ Glide.get(this).clearMemory();
+ }
+
+ @Override
+ public boolean onCreateOptionsMenu(Menu menu) {
+ super.onCreateOptionsMenu(menu);
+// requestCastButton(MenuItem.SHOW_AS_ACTION_ALWAYS);
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.mediaplayer, menu);
+ return true;
+ }
+
+ @Override
+ public boolean onPrepareOptionsMenu(Menu menu) {
+ super.onPrepareOptionsMenu(menu);
+ if (controller == null) {
+ return false;
+ }
+ Playable media = controller.getMedia();
+
+ menu.findItem(R.id.support_item).setVisible(
+ media != null && media.getPaymentLink() != null &&
+ (media instanceof FeedMedia) &&
+ ((FeedMedia) media).getItem() != null &&
+ ((FeedMedia) media).getItem().getFlattrStatus().flattrable()
+ );
+
+ boolean hasWebsiteLink = media != null && media.getWebsiteLink() != null;
+ menu.findItem(R.id.visit_website_item).setVisible(hasWebsiteLink);
+
+ boolean isItemAndHasLink = media != null && (media instanceof FeedMedia) &&
+ ((FeedMedia) media).getItem() != null && ((FeedMedia) media).getItem().getLink() != null;
+ menu.findItem(R.id.share_link_item).setVisible(isItemAndHasLink);
+ menu.findItem(R.id.share_link_with_position_item).setVisible(isItemAndHasLink);
+
+ boolean isItemHasDownloadLink = media != null && (media instanceof FeedMedia) && ((FeedMedia) media).getDownload_url() != null;
+ menu.findItem(R.id.share_download_url_item).setVisible(isItemHasDownloadLink);
+ menu.findItem(R.id.share_download_url_with_position_item).setVisible(isItemHasDownloadLink);
+
+ menu.findItem(R.id.share_item).setVisible(hasWebsiteLink || isItemAndHasLink || isItemHasDownloadLink);
+
+ menu.findItem(R.id.add_to_favorites_item).setVisible(false);
+ menu.findItem(R.id.remove_from_favorites_item).setVisible(false);
+ if(media != null && media instanceof FeedMedia) {
+ menu.findItem(R.id.add_to_favorites_item).setVisible(!isFavorite);
+ menu.findItem(R.id.remove_from_favorites_item).setVisible(isFavorite);
+ }
+
+ boolean sleepTimerSet = controller.sleepTimerActive();
+ boolean sleepTimerNotSet = controller.sleepTimerNotActive();
+ menu.findItem(R.id.set_sleeptimer_item).setVisible(sleepTimerNotSet);
+ menu.findItem(R.id.disable_sleeptimer_item).setVisible(sleepTimerSet);
+
+ if (this instanceof AudioplayerActivity) {
+ int[] attrs = {R.attr.action_bar_icon_color};
+ TypedArray ta = obtainStyledAttributes(UserPreferences.getTheme(), attrs);
+ int textColor = ta.getColor(0, Color.GRAY);
+ ta.recycle();
+ menu.findItem(R.id.audio_controls).setIcon(new IconDrawable(this,
+ FontAwesomeIcons.fa_sliders).color(textColor).actionBarSize());
+ } else {
+ menu.findItem(R.id.audio_controls).setVisible(false);
+ }
+
+ return true;
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (controller == null) {
+ return false;
+ }
+ Playable media = controller.getMedia();
+ if (item.getItemId() == android.R.id.home) {
+ Intent intent = new Intent(MediaplayerActivity.this,
+ MainActivity.class);
+ intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP
+ | Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
+ return true;
+ } else {
+ if (media != null) {
+ switch (item.getItemId()) {
+ case R.id.add_to_favorites_item:
+ if(media instanceof FeedMedia) {
+ FeedItem feedItem = ((FeedMedia)media).getItem();
+ if(feedItem != null) {
+ DBWriter.addFavoriteItem(feedItem);
+ isFavorite = true;
+ invalidateOptionsMenu();
+ Toast.makeText(this, R.string.added_to_favorites, Toast.LENGTH_SHORT)
+ .show();
+ }
+ }
+ break;
+ case R.id.remove_from_favorites_item:
+ if(media instanceof FeedMedia) {
+ FeedItem feedItem = ((FeedMedia)media).getItem();
+ if(feedItem != null) {
+ DBWriter.removeFavoriteItem(feedItem);
+ isFavorite = false;
+ invalidateOptionsMenu();
+ Toast.makeText(this, R.string.removed_from_favorites, Toast.LENGTH_SHORT)
+ .show();
+ }
+ }
+ break;
+ case R.id.disable_sleeptimer_item:
+ if (controller.serviceAvailable()) {
+
+ MaterialDialog.Builder stDialog = new MaterialDialog.Builder(this);
+ stDialog.title(R.string.sleep_timer_label);
+ stDialog.content(getString(R.string.time_left_label)
+ + Converter.getDurationStringLong((int) controller
+ .getSleepTimerTimeLeft()));
+ stDialog.positiveText(R.string.disable_sleeptimer_label);
+ stDialog.negativeText(R.string.cancel_label);
+ stDialog.onPositive((dialog, which) -> {
+ dialog.dismiss();
+ controller.disableSleepTimer();
+ });
+ stDialog.onNegative((dialog, which) -> dialog.dismiss());
+ stDialog.build().show();
+ }
+ break;
+ case R.id.set_sleeptimer_item:
+ if (controller.serviceAvailable()) {
+ SleepTimerDialog td = new SleepTimerDialog(this) {
+ @Override
+ public void onTimerSet(long millis, boolean shakeToReset, boolean vibrate) {
+ controller.setSleepTimer(millis, shakeToReset, vibrate);
+ }
+ };
+ td.createNewDialog().show();
+ }
+ break;
+ case R.id.audio_controls:
+ MaterialDialog dialog = new MaterialDialog.Builder(this)
+ .title(R.string.audio_controls)
+ .customView(R.layout.audio_controls, true)
+ .neutralText(R.string.close_label)
+ .onNeutral((dialog1, which) -> {
+ final SeekBar left = (SeekBar) dialog1.findViewById(R.id.volume_left);
+ final SeekBar right = (SeekBar) dialog1.findViewById(R.id.volume_right);
+ UserPreferences.setVolume(left.getProgress(), right.getProgress());
+ })
+ .show();
+ final SeekBar barPlaybackSpeed = (SeekBar) dialog.findViewById(R.id.playback_speed);
+ final Button butDecSpeed = (Button) dialog.findViewById(R.id.butDecSpeed);
+ butDecSpeed.setOnClickListener(v -> {
+ if(controller != null && controller.canSetPlaybackSpeed()) {
+ barPlaybackSpeed.setProgress(barPlaybackSpeed.getProgress() - 2);
+ } else {
+ VariableSpeedDialog.showGetPluginDialog(this);
+ }
+ });
+ final Button butIncSpeed = (Button) dialog.findViewById(R.id.butIncSpeed);
+ butIncSpeed.setOnClickListener(v -> {
+ if(controller != null && controller.canSetPlaybackSpeed()) {
+ barPlaybackSpeed.setProgress(barPlaybackSpeed.getProgress() + 2);
+ } else {
+ VariableSpeedDialog.showGetPluginDialog(this);
+ }
+ });
+
+ final TextView txtvPlaybackSpeed = (TextView) dialog.findViewById(R.id.txtvPlaybackSpeed);
+ float currentSpeed = 1.0f;
+ try {
+ currentSpeed = Float.parseFloat(UserPreferences.getPlaybackSpeed());
+ } catch (NumberFormatException e) {
+ Log.e(TAG, Log.getStackTraceString(e));
+ UserPreferences.setPlaybackSpeed(String.valueOf(currentSpeed));
+ }
+
+ txtvPlaybackSpeed.setText(String.format("%.2fx", currentSpeed));
+ barPlaybackSpeed.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ if(controller != null && controller.canSetPlaybackSpeed()) {
+ float playbackSpeed = (progress + 10) / 20.0f;
+ controller.setPlaybackSpeed(playbackSpeed);
+ String speedPref = String.format(Locale.US, "%.2f", playbackSpeed);
+ UserPreferences.setPlaybackSpeed(speedPref);
+ String speedStr = String.format("%.2fx", playbackSpeed);
+ txtvPlaybackSpeed.setText(speedStr);
+ } else if(fromUser) {
+ float speed = Float.valueOf(UserPreferences.getPlaybackSpeed());
+ barPlaybackSpeed.post(() -> barPlaybackSpeed.setProgress((int) (20 * speed) - 10));
+ }
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ if(controller != null && !controller.canSetPlaybackSpeed()) {
+ VariableSpeedDialog.showGetPluginDialog(MediaplayerActivity.this);
+ }
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ }
+ });
+ barPlaybackSpeed.setProgress((int) (20 * currentSpeed) - 10);
+
+ final SeekBar barLeftVolume = (SeekBar) dialog.findViewById(R.id.volume_left);
+ barLeftVolume.setProgress(UserPreferences.getLeftVolumePercentage());
+ final SeekBar barRightVolume = (SeekBar) dialog.findViewById(R.id.volume_right);
+ barRightVolume.setProgress(UserPreferences.getRightVolumePercentage());
+ final CheckBox stereoToMono = (CheckBox) dialog.findViewById(R.id.stereo_to_mono);
+ stereoToMono.setChecked(UserPreferences.stereoToMono());
+ if (controller != null && !controller.canDownmix()) {
+ stereoToMono.setEnabled(false);
+ String sonicOnly = getString(R.string.sonic_only);
+ stereoToMono.setText(stereoToMono.getText() + " [" + sonicOnly + "]");
+ }
+
+ barLeftVolume.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ controller.setVolume(
+ Converter.getVolumeFromPercentage(progress),
+ Converter.getVolumeFromPercentage(barRightVolume.getProgress()));
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ }
+ });
+ barRightVolume.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
+ @Override
+ public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
+ controller.setVolume(
+ Converter.getVolumeFromPercentage(barLeftVolume.getProgress()),
+ Converter.getVolumeFromPercentage(progress));
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ }
+ });
+ stereoToMono.setOnCheckedChangeListener((buttonView, isChecked) -> {
+ UserPreferences.stereoToMono(isChecked);
+ if (controller != null) {
+ controller.setDownmix(isChecked);
+ }
+ });
+ break;
+ case R.id.visit_website_item:
+ Uri uri = Uri.parse(media.getWebsiteLink());
+ startActivity(new Intent(Intent.ACTION_VIEW, uri));
+ break;
+ case R.id.support_item:
+ if (media instanceof FeedMedia) {
+ DBTasks.flattrItemIfLoggedIn(this, ((FeedMedia) media).getItem());
+ }
+ break;
+ case R.id.share_link_item:
+ if (media instanceof FeedMedia) {
+ ShareUtils.shareFeedItemLink(this, ((FeedMedia) media).getItem());
+ }
+ break;
+ case R.id.share_download_url_item:
+ if (media instanceof FeedMedia) {
+ ShareUtils.shareFeedItemDownloadLink(this, ((FeedMedia) media).getItem());
+ }
+ break;
+ case R.id.share_link_with_position_item:
+ if (media instanceof FeedMedia) {
+ ShareUtils.shareFeedItemLink(this, ((FeedMedia) media).getItem(), true);
+ }
+ break;
+ case R.id.share_download_url_with_position_item:
+ if (media instanceof FeedMedia) {
+ ShareUtils.shareFeedItemDownloadLink(this, ((FeedMedia) media).getItem(), true);
+ }
+ break;
+ default:
+ return false;
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+ Log.d(TAG, "onResume()");
+ StorageUtils.checkStorageAvailability(this);
+ if(controller != null) {
+ controller.init();
+ }
+ }
+
+ /**
+ * Called by 'handleStatus()' when the PlaybackService is waiting for
+ * a video surface.
+ */
+ protected abstract void onAwaitingVideoSurface();
+
+ protected abstract void postStatusMsg(int resId, boolean showToast);
+
+ protected abstract void clearStatusMsg();
+
+ protected void onPositionObserverUpdate() {
+ if (controller == null || txtvPosition == null || txtvLength == null) {
+ return;
+ }
+ int currentPosition = controller.getPosition();
+ int duration = controller.getDuration();
+ Log.d(TAG, "currentPosition " + Converter.getDurationStringLong(currentPosition));
+ if (currentPosition == PlaybackService.INVALID_TIME ||
+ duration == PlaybackService.INVALID_TIME) {
+ Log.w(TAG, "Could not react to position observer update because of invalid time");
+ return;
+ }
+ txtvPosition.setText(Converter.getDurationStringLong(currentPosition));
+ if (showTimeLeft) {
+ txtvLength.setText("-" + Converter.getDurationStringLong(duration - currentPosition));
+ } else {
+ txtvLength.setText(Converter.getDurationStringLong(duration));
+ }
+ updateProgressbarPosition(currentPosition, duration);
+ }
+
+ private void updateProgressbarPosition(int position, int duration) {
+ Log.d(TAG, "updateProgressbarPosition(" + position + ", " + duration + ")");
+ if(sbPosition == null) {
+ return;
+ }
+ float progress = ((float) position) / duration;
+ sbPosition.setProgress((int) (progress * sbPosition.getMax()));
+ }
+
+ /**
+ * Load information about the media that is going to be played or currently
+ * being played. This method will be called when the activity is connected
+ * to the PlaybackService to ensure that the activity has the right
+ * FeedMedia object.
+ */
+ protected boolean loadMediaInfo() {
+ Log.d(TAG, "loadMediaInfo()");
+ Playable media = controller.getMedia();
+ SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE);
+ showTimeLeft = prefs.getBoolean(PREF_SHOW_TIME_LEFT, false);
+ if (media != null) {
+ onPositionObserverUpdate();
+ checkFavorite();
+ updatePlaybackSpeedButton();
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ protected void updatePlaybackSpeedButton() {
+ // Only meaningful on AudioplayerActivity, where it is overridden.
+ }
+
+ protected void updatePlaybackSpeedButtonText() {
+ // Only meaningful on AudioplayerActivity, where it is overridden.
+ }
+
+
+ protected void setupGUI() {
+ setContentView(getContentViewResourceId());
+ sbPosition = (SeekBar) findViewById(R.id.sbPosition);
+ txtvPosition = (TextView) findViewById(R.id.txtvPosition);
+
+ SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE);
+ showTimeLeft = prefs.getBoolean(PREF_SHOW_TIME_LEFT, false);
+ Log.d("timeleft", showTimeLeft ? "true" : "false");
+ txtvLength = (TextView) findViewById(R.id.txtvLength);
+ if (txtvLength != null) {
+ txtvLength.setOnClickListener(v -> {
+ showTimeLeft = !showTimeLeft;
+ Playable media = controller.getMedia();
+ if (media == null) {
+ return;
+ }
+
+ String length;
+ if (showTimeLeft) {
+ length = "-" + Converter.getDurationStringLong(media.getDuration() - media.getPosition());
+ } else {
+ length = Converter.getDurationStringLong(media.getDuration());
+ }
+ txtvLength.setText(length);
+
+ SharedPreferences.Editor editor = prefs.edit();
+ editor.putBoolean(PREF_SHOW_TIME_LEFT, showTimeLeft);
+ editor.apply();
+ Log.d("timeleft on click", showTimeLeft ? "true" : "false");
+ });
+ }
+
+ butRev = (ImageButton) findViewById(R.id.butRev);
+ txtvRev = (TextView) findViewById(R.id.txtvRev);
+ if (txtvRev != null) {
+ txtvRev.setText(String.valueOf(UserPreferences.getRewindSecs()));
+ }
+ butPlay = (ImageButton) findViewById(R.id.butPlay);
+ butFF = (ImageButton) findViewById(R.id.butFF);
+ txtvFF = (TextView) findViewById(R.id.txtvFF);
+ if (txtvFF != null) {
+ txtvFF.setText(String.valueOf(UserPreferences.getFastFowardSecs()));
+ }
+ butSkip = (ImageButton) findViewById(R.id.butSkip);
+
+ // SEEKBAR SETUP
+
+ sbPosition.setOnSeekBarChangeListener(this);
+
+ // BUTTON SETUP
+
+ if (butRev != null) {
+ butRev.setOnClickListener(v -> onRewind());
+ butRev.setOnLongClickListener(new View.OnLongClickListener() {
+
+ int choice;
+
+ @Override
+ public boolean onLongClick(View v) {
+ int checked = 0;
+ int rewindSecs = UserPreferences.getRewindSecs();
+ final int[] values = getResources().getIntArray(R.array.seek_delta_values);
+ final String[] choices = new String[values.length];
+ for (int i = 0; i < values.length; i++) {
+ if (rewindSecs == values[i]) {
+ checked = i;
+ }
+ choices[i] = String.valueOf(values[i]) + " " + getString(R.string.time_seconds);
+ }
+ choice = values[checked];
+ AlertDialog.Builder builder = new AlertDialog.Builder(MediaplayerActivity.this);
+ builder.setTitle(R.string.pref_rewind);
+ builder.setSingleChoiceItems(choices, checked,
+ (dialog, which) -> {
+ choice = values[which];
+ });
+ builder.setNegativeButton(R.string.cancel_label, null);
+ builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> {
+ UserPreferences.setPrefRewindSecs(choice);
+ if(txtvRev != null){
+ txtvRev.setText(String.valueOf(choice));
+ }
+ });
+ builder.create().show();
+ return true;
+ }
+ });
+ }
+
+ butPlay.setOnClickListener(v -> onPlayPause());
+
+ if (butFF != null) {
+ butFF.setOnClickListener(v -> onFastForward());
+ butFF.setOnLongClickListener(new View.OnLongClickListener() {
+
+ int choice;
+
+ @Override
+ public boolean onLongClick(View v) {
+ int checked = 0;
+ int rewindSecs = UserPreferences.getFastFowardSecs();
+ final int[] values = getResources().getIntArray(R.array.seek_delta_values);
+ final String[] choices = new String[values.length];
+ for (int i = 0; i < values.length; i++) {
+ if (rewindSecs == values[i]) {
+ checked = i;
+ }
+ choices[i] = String.valueOf(values[i]) + " " + getString(R.string.time_seconds);
+ }
+ choice = values[checked];
+ AlertDialog.Builder builder = new AlertDialog.Builder(MediaplayerActivity.this);
+ builder.setTitle(R.string.pref_fast_forward);
+ builder.setSingleChoiceItems(choices, checked,
+ (dialog, which) -> {
+ choice = values[which];
+ });
+ builder.setNegativeButton(R.string.cancel_label, null);
+ builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> {
+ UserPreferences.setPrefFastForwardSecs(choice);
+ if(txtvFF != null) {
+ txtvFF.setText(String.valueOf(choice));
+ }
+ });
+ builder.create().show();
+ return true;
+ }
+ });
+ }
+
+ if (butSkip != null) {
+ butSkip.setOnClickListener(v -> sendBroadcast(new Intent(PlaybackService.ACTION_SKIP_CURRENT_EPISODE)));
+ }
+ }
+
+ protected void onRewind() {
+ if (controller == null) {
+ return;
+ }
+ int curr = controller.getPosition();
+ controller.seekTo(curr - UserPreferences.getRewindSecs() * 1000);
+ }
+
+ protected void onPlayPause() {
+ if(controller == null) {
+ return;
+ }
+ controller.playPause();
+ }
+
+ protected void onFastForward() {
+ if (controller == null) {
+ return;
+ }
+ int curr = controller.getPosition();
+ controller.seekTo(curr + UserPreferences.getFastFowardSecs() * 1000);
+ }
+
+ protected abstract int getContentViewResourceId();
+
+ void handleError(int errorCode) {
+ final AlertDialog.Builder errorDialog = new AlertDialog.Builder(this);
+ errorDialog.setTitle(R.string.error_label);
+ errorDialog.setMessage(MediaPlayerError.getErrorString(this, errorCode));
+ errorDialog.setNeutralButton("OK",
+ (dialog, which) -> {
+ dialog.dismiss();
+ finish();
+ }
+ );
+ errorDialog.create().show();
+ }
+
+ float prog;
+
+ @Override
+ public void onProgressChanged (SeekBar seekBar,int progress, boolean fromUser) {
+ if (controller == null || txtvLength == null) {
+ return;
+ }
+ prog = controller.onSeekBarProgressChanged(seekBar, progress, fromUser, txtvPosition);
+ if (showTimeLeft && prog != 0) {
+ int duration = controller.getDuration();
+ String length = "-" + Converter.getDurationStringLong(duration - (int) (prog * duration));
+ txtvLength.setText(length);
+ }
+ }
+
+ @Override
+ public void onStartTrackingTouch(SeekBar seekBar) {
+ if (controller != null) {
+ controller.onSeekBarStartTrackingTouch(seekBar);
+ }
+ }
+
+ @Override
+ public void onStopTrackingTouch(SeekBar seekBar) {
+ if (controller != null) {
+ controller.onSeekBarStopTrackingTouch(seekBar, prog);
+ }
+ }
+
+ private void checkFavorite() {
+ Playable playable = controller.getMedia();
+ if (playable != null && playable instanceof FeedMedia) {
+ FeedItem feedItem = ((FeedMedia) playable).getItem();
+ if (feedItem != null) {
+ Observable.fromCallable(() -> DBReader.getFeedItem(feedItem.getId()))
+ .subscribeOn(Schedulers.newThread())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(
+ item -> {
+ boolean isFav = item.isTagged(FeedItem.TAG_FAVORITE);
+ if (isFavorite != isFav) {
+ isFavorite = isFav;
+ invalidateOptionsMenu();
+ }
+ }, error -> {
+ Log.e(TAG, Log.getStackTraceString(error));
+ }
+ );
+ }
+ }
+ }
+
+}
diff --git a/app/src/free/java/de/danoeh/antennapod/fragment/ItemFragment.java b/app/src/free/java/de/danoeh/antennapod/fragment/ItemFragment.java
new file mode 100644
index 000000000..2b2808adc
--- /dev/null
+++ b/app/src/free/java/de/danoeh/antennapod/fragment/ItemFragment.java
@@ -0,0 +1,590 @@
+package de.danoeh.antennapod.fragment;
+
+import android.content.ClipData;
+import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.support.annotation.Nullable;
+import android.support.v4.app.Fragment;
+import android.support.v4.content.ContextCompat;
+import android.support.v4.view.GestureDetectorCompat;
+import android.text.Layout;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewGroup;
+import android.webkit.WebSettings;
+import android.webkit.WebView;
+import android.webkit.WebViewClient;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import android.widget.Toast;
+
+import com.bumptech.glide.Glide;
+import com.joanzapata.iconify.Iconify;
+import com.joanzapata.iconify.widget.IconButton;
+
+import org.apache.commons.lang3.ArrayUtils;
+
+import java.util.List;
+
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.MainActivity;
+import de.danoeh.antennapod.adapter.DefaultActionButtonCallback;
+import de.danoeh.antennapod.core.event.DownloadEvent;
+import de.danoeh.antennapod.core.event.DownloaderUpdate;
+import de.danoeh.antennapod.core.event.FeedItemEvent;
+import de.danoeh.antennapod.core.feed.EventDistributor;
+import de.danoeh.antennapod.core.feed.FeedItem;
+import de.danoeh.antennapod.core.feed.FeedMedia;
+import de.danoeh.antennapod.core.glide.ApGlideSettings;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.service.download.Downloader;
+import de.danoeh.antennapod.core.storage.DBReader;
+import de.danoeh.antennapod.core.storage.DBTasks;
+import de.danoeh.antennapod.core.storage.DBWriter;
+import de.danoeh.antennapod.core.storage.DownloadRequestException;
+import de.danoeh.antennapod.core.storage.DownloadRequester;
+import de.danoeh.antennapod.core.util.Converter;
+import de.danoeh.antennapod.core.util.DateUtils;
+import de.danoeh.antennapod.core.util.IntentUtils;
+import de.danoeh.antennapod.core.util.ShareUtils;
+import de.danoeh.antennapod.core.util.playback.Timeline;
+import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler;
+import de.danoeh.antennapod.view.OnSwipeGesture;
+import de.danoeh.antennapod.view.SwipeGestureDetector;
+import de.greenrobot.event.EventBus;
+import rx.Observable;
+import rx.Subscription;
+import rx.android.schedulers.AndroidSchedulers;
+import rx.schedulers.Schedulers;
+
+/**
+ * Displays information about a FeedItem and actions.
+ */
+public class ItemFragment extends Fragment implements OnSwipeGesture {
+
+ private static final String TAG = "ItemFragment";
+
+ private static final int EVENTS = EventDistributor.UNREAD_ITEMS_UPDATE;
+
+ private static final String ARG_FEEDITEMS = "feeditems";
+ private static final String ARG_FEEDITEM_POS = "feeditem_pos";
+
+ private GestureDetectorCompat headerGestureDetector;
+ private GestureDetectorCompat webviewGestureDetector;
+
+ /**
+ * Creates a new instance of an ItemFragment
+ *
+ * @param feeditem The ID of the FeedItem that should be displayed.
+ * @return The ItemFragment instance
+ */
+ public static ItemFragment newInstance(long feeditem) {
+ return newInstance(new long[] { feeditem }, 0);
+ }
+
+ /**
+ * Creates a new instance of an ItemFragment
+ *
+ * @param feeditems The IDs of the FeedItems that belong to the same list
+ * @param feedItemPos The position of the FeedItem that is currently shown
+ * @return The ItemFragment instance
+ */
+ public static ItemFragment newInstance(long[] feeditems, int feedItemPos) {
+ ItemFragment fragment = new ItemFragment();
+ Bundle args = new Bundle();
+ args.putLongArray(ARG_FEEDITEMS, feeditems);
+ args.putInt(ARG_FEEDITEM_POS, feedItemPos);
+ fragment.setArguments(args);
+ return fragment;
+ }
+
+ private boolean itemsLoaded = false;
+ private long[] feedItems;
+ private int feedItemPos;
+ private FeedItem item;
+ private String webviewData;
+ private List<Downloader> downloaderList;
+
+ private ViewGroup root;
+ private WebView webvDescription;
+ private TextView txtvPodcast;
+ private TextView txtvTitle;
+ private TextView txtvDuration;
+ private TextView txtvPublished;
+ private ImageView imgvCover;
+ private ProgressBar progbarDownload;
+ private ProgressBar progbarLoading;
+ private IconButton butAction1;
+ private IconButton butAction2;
+ private Menu popupMenu;
+
+ private Subscription subscription;
+
+ /**
+ * URL that was selected via long-press.
+ */
+ private String selectedURL;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setRetainInstance(true);
+ setHasOptionsMenu(true);
+
+ feedItems = getArguments().getLongArray(ARG_FEEDITEMS);
+ feedItemPos = getArguments().getInt(ARG_FEEDITEM_POS);
+
+ headerGestureDetector = new GestureDetectorCompat(getActivity(), new SwipeGestureDetector(this));
+ webviewGestureDetector = new GestureDetectorCompat(getActivity(), new SwipeGestureDetector(this) {
+ // necessary for the longclick context menu to work properly
+ @Override
+ public boolean onDown(MotionEvent e) {
+ return false;
+ }
+ });
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
+ super.onCreateView(inflater, container, savedInstanceState);
+ View layout = inflater.inflate(R.layout.feeditem_fragment, container, false);
+
+ root = (ViewGroup) layout.findViewById(R.id.content_root);
+
+ LinearLayout header = (LinearLayout) root.findViewById(R.id.header);
+ if(feedItems.length > 0) {
+ header.setOnTouchListener((v, event) -> headerGestureDetector.onTouchEvent(event));
+ }
+
+ txtvPodcast = (TextView) layout.findViewById(R.id.txtvPodcast);
+ txtvPodcast.setOnClickListener(v -> openPodcast());
+ txtvTitle = (TextView) layout.findViewById(R.id.txtvTitle);
+ if(Build.VERSION.SDK_INT >= 23) {
+ txtvTitle.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL);
+ }
+ txtvDuration = (TextView) layout.findViewById(R.id.txtvDuration);
+ txtvPublished = (TextView) layout.findViewById(R.id.txtvPublished);
+ if (Build.VERSION.SDK_INT >= 14) { // ellipsize is causing problems on old versions, see #448
+ txtvTitle.setEllipsize(TextUtils.TruncateAt.END);
+ }
+ webvDescription = (WebView) layout.findViewById(R.id.webvDescription);
+ if (UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) {
+ if (Build.VERSION.SDK_INT >= 11
+ && Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
+ webvDescription.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
+ }
+ webvDescription.setBackgroundColor(ContextCompat.getColor(getContext(), R.color.black));
+ }
+ webvDescription.getSettings().setUseWideViewPort(false);
+ webvDescription.getSettings().setLayoutAlgorithm(
+ WebSettings.LayoutAlgorithm.NARROW_COLUMNS);
+ webvDescription.getSettings().setLoadWithOverviewMode(true);
+ if(feedItems.length > 0) {
+ webvDescription.setOnLongClickListener(webViewLongClickListener);
+ }
+ webvDescription.setOnTouchListener((v, event) -> webviewGestureDetector.onTouchEvent(event));
+ webvDescription.setWebViewClient(new WebViewClient() {
+ @Override
+ public boolean shouldOverrideUrlLoading(WebView view, String url) {
+ Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
+ if(IntentUtils.isCallable(getActivity(), intent)) {
+ startActivity(intent);
+ }
+ return true;
+ }
+ });
+ registerForContextMenu(webvDescription);
+
+ imgvCover = (ImageView) layout.findViewById(R.id.imgvCover);
+ imgvCover.setOnClickListener(v -> openPodcast());
+ progbarDownload = (ProgressBar) layout.findViewById(R.id.progbarDownload);
+ progbarLoading = (ProgressBar) layout.findViewById(R.id.progbarLoading);
+ butAction1 = (IconButton) layout.findViewById(R.id.butAction1);
+ butAction2 = (IconButton) layout.findViewById(R.id.butAction2);
+
+ butAction1.setOnClickListener(v -> {
+ if (item == null) {
+ return;
+ }
+ DefaultActionButtonCallback actionButtonCallback = new DefaultActionButtonCallback(getActivity());
+ actionButtonCallback.onActionButtonPressed(item);
+ FeedMedia media = item.getMedia();
+ if (media != null && media.isDownloaded()) {
+ // playback was started, dialog should close itself
+ ((MainActivity) getActivity()).dismissChildFragment();
+ }
+ });
+
+ butAction2.setOnClickListener(v -> {
+ if (item == null) {
+ return;
+ }
+
+ if (item.hasMedia()) {
+ FeedMedia media = item.getMedia();
+ if (!media.isDownloaded()) {
+ DBTasks.playMedia(getActivity(), media, true, true, true);
+ ((MainActivity) getActivity()).dismissChildFragment();
+ } else {
+ DBWriter.deleteFeedMediaOfItem(getActivity(), media.getId());
+ }
+ } else if (item.getLink() != null) {
+ Uri uri = Uri.parse(item.getLink());
+ getActivity().startActivity(new Intent(Intent.ACTION_VIEW, uri));
+ }
+ });
+
+ return layout;
+ }
+
+ @Override
+ public void onActivityCreated(@Nullable Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ load();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ EventDistributor.getInstance().register(contentUpdate);
+ EventBus.getDefault().registerSticky(this);
+ if(itemsLoaded) {
+ progbarLoading.setVisibility(View.GONE);
+ updateAppearance();
+ }
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ EventDistributor.getInstance().unregister(contentUpdate);
+ EventBus.getDefault().unregister(this);
+ }
+
+ @Override
+ public void onDestroyView() {
+ super.onDestroyView();
+ if(subscription != null) {
+ subscription.unsubscribe();
+ }
+ if (webvDescription != null && root != null) {
+ root.removeView(webvDescription);
+ webvDescription.destroy();
+ }
+ }
+
+ @Override
+ public boolean onSwipeLeftToRight() {
+ Log.d(TAG, "onSwipeLeftToRight()");
+ feedItemPos = feedItemPos - 1;
+ if(feedItemPos < 0) {
+ feedItemPos = feedItems.length - 1;
+ }
+ load();
+ return true;
+ }
+
+ @Override
+ public boolean onSwipeRightToLeft() {
+ Log.d(TAG, "onSwipeRightToLeft()");
+ feedItemPos = (feedItemPos + 1) % feedItems.length;
+ load();
+ return true;
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ if(!isAdded() || item == null) {
+ return;
+ }
+// ((CastEnabledActivity) getActivity()).requestCastButton(MenuItem.SHOW_AS_ACTION_ALWAYS);
+ inflater.inflate(R.menu.feeditem_options, menu);
+ popupMenu = menu;
+ if (item.hasMedia()) {
+ FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item, true, null);
+ } else {
+ // these are already available via button1 and button2
+ FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item, true, null,
+ R.id.mark_read_item, R.id.visit_website_item);
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem menuItem) {
+ switch(menuItem.getItemId()) {
+ case R.id.open_podcast:
+ openPodcast();
+ return true;
+ default:
+ try {
+ return FeedItemMenuHandler.onMenuItemClicked(getActivity(), menuItem.getItemId(), item);
+ } catch (DownloadRequestException e) {
+ e.printStackTrace();
+ Toast.makeText(getActivity(), e.getMessage(), Toast.LENGTH_LONG).show();
+ return true;
+ }
+ }
+ }
+
+ private final FeedItemMenuHandler.MenuInterface popupMenuInterface = new FeedItemMenuHandler.MenuInterface() {
+ @Override
+ public void setItemVisibility(int id, boolean visible) {
+ MenuItem item = popupMenu.findItem(id);
+ if (item != null) {
+ item.setVisible(visible);
+ }
+ }
+ };
+
+
+ private void onFragmentLoaded() {
+ if (webviewData != null) {
+ webvDescription.loadDataWithBaseURL(null, webviewData, "text/html", "utf-8", "about:blank");
+ }
+ updateAppearance();
+ }
+
+ private void updateAppearance() {
+ if (item == null) {
+ Log.d(TAG, "updateAppearance item is null");
+ return;
+ }
+ getActivity().supportInvalidateOptionsMenu();
+ txtvPodcast.setText(item.getFeed().getTitle());
+ txtvTitle.setText(item.getTitle());
+
+ if (item.getPubDate() != null) {
+ String pubDateStr = DateUtils.formatAbbrev(getActivity(), item.getPubDate());
+ txtvPublished.setText(pubDateStr);
+ }
+
+ Glide.with(getActivity())
+ .load(item.getImageLocation())
+ .placeholder(R.color.light_gray)
+ .error(R.color.light_gray)
+ .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
+ .fitCenter()
+ .dontAnimate()
+ .into(imgvCover);
+
+ progbarDownload.setVisibility(View.GONE);
+ if (item.hasMedia() && downloaderList != null) {
+ for (Downloader downloader : downloaderList) {
+ if (downloader.getDownloadRequest().getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA
+ && downloader.getDownloadRequest().getFeedfileId() == item.getMedia().getId()) {
+ progbarDownload.setVisibility(View.VISIBLE);
+ progbarDownload.setProgress(downloader.getDownloadRequest().getProgressPercent());
+ }
+ }
+ }
+
+ FeedMedia media = item.getMedia();
+ String butAction1Icon = null;
+ int butAction1Text = 0;
+ String butAction2Icon = null;
+ int butAction2Text = 0;
+ if (media == null) {
+ if (!item.isPlayed()) {
+ butAction1Icon = "{fa-check 24sp}";
+ butAction1Text = R.string.mark_read_label;
+ }
+ if (item.getLink() != null) {
+ butAction2Icon = "{md-web 24sp}";
+ butAction2Text = R.string.visit_website_label;
+ }
+ } else {
+ if(media.getDuration() > 0) {
+ txtvDuration.setText(Converter.getDurationStringLong(media.getDuration()));
+ }
+ boolean isDownloading = DownloadRequester.getInstance().isDownloadingFile(media);
+ if (!media.isDownloaded()) {
+ butAction2Icon = "{md-settings-input-antenna 24sp}";
+ butAction2Text = R.string.stream_label;
+ } else {
+ butAction2Icon = "{md-delete 24sp}";
+ butAction2Text = R.string.remove_label;
+ }
+ if (isDownloading) {
+ butAction1Icon = "{md-cancel 24sp}";
+ butAction1Text = R.string.cancel_label;
+ } else if (media.isDownloaded()) {
+ butAction1Icon = "{md-play-arrow 24sp}";
+ butAction1Text = R.string.play_label;
+ } else {
+ butAction1Icon = "{md-file-download 24sp}";
+ butAction1Text = R.string.download_label;
+ }
+ }
+ if(butAction1Icon != null && butAction1Text != 0) {
+ butAction1.setText(butAction1Icon +"\u0020\u0020" + getActivity().getString(butAction1Text));
+ Iconify.addIcons(butAction1);
+ butAction1.setVisibility(View.VISIBLE);
+ } else {
+ butAction1.setVisibility(View.INVISIBLE);
+ }
+ if(butAction2Icon != null && butAction2Text != 0) {
+ butAction2.setText(butAction2Icon +"\u0020\u0020" + getActivity().getString(butAction2Text));
+ Iconify.addIcons(butAction2);
+ butAction2.setVisibility(View.VISIBLE);
+ } else {
+ butAction2.setVisibility(View.INVISIBLE);
+ }
+ }
+
+ private View.OnLongClickListener webViewLongClickListener = new View.OnLongClickListener() {
+
+ @Override
+ public boolean onLongClick(View v) {
+ WebView.HitTestResult r = webvDescription.getHitTestResult();
+ if (r != null
+ && r.getType() == WebView.HitTestResult.SRC_ANCHOR_TYPE) {
+ Log.d(TAG, "Link of webview was long-pressed. Extra: " + r.getExtra());
+ selectedURL = r.getExtra();
+ webvDescription.showContextMenu();
+ return true;
+ }
+ selectedURL = null;
+ return false;
+ }
+ };
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item) {
+ boolean handled = selectedURL != null;
+ if (selectedURL != null) {
+ switch (item.getItemId()) {
+ case R.id.open_in_browser_item:
+ Uri uri = Uri.parse(selectedURL);
+ final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ if(IntentUtils.isCallable(getActivity(), intent)) {
+ getActivity().startActivity(intent);
+ }
+ break;
+ case R.id.share_url_item:
+ ShareUtils.shareLink(getActivity(), selectedURL);
+ break;
+ case R.id.copy_url_item:
+ if (android.os.Build.VERSION.SDK_INT >= 11) {
+ ClipData clipData = ClipData.newPlainText(selectedURL,
+ selectedURL);
+ android.content.ClipboardManager cm = (android.content.ClipboardManager) getActivity()
+ .getSystemService(Context.CLIPBOARD_SERVICE);
+ cm.setPrimaryClip(clipData);
+ } else {
+ android.text.ClipboardManager cm = (android.text.ClipboardManager) getActivity()
+ .getSystemService(Context.CLIPBOARD_SERVICE);
+ cm.setText(selectedURL);
+ }
+ Toast t = Toast.makeText(getActivity(),
+ R.string.copied_url_msg, Toast.LENGTH_SHORT);
+ t.show();
+ break;
+ default:
+ handled = false;
+ break;
+
+ }
+ selectedURL = null;
+ }
+ return handled;
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v,
+ ContextMenu.ContextMenuInfo menuInfo) {
+ if (selectedURL != null) {
+ super.onCreateContextMenu(menu, v, menuInfo);
+ Uri uri = Uri.parse(selectedURL);
+ final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
+ if(IntentUtils.isCallable(getActivity(), intent)) {
+ menu.add(Menu.NONE, R.id.open_in_browser_item, Menu.NONE,
+ R.string.open_in_browser_label);
+ }
+ menu.add(Menu.NONE, R.id.copy_url_item, Menu.NONE,
+ R.string.copy_url_label);
+ menu.add(Menu.NONE, R.id.share_url_item, Menu.NONE,
+ R.string.share_url_label);
+ menu.setHeaderTitle(selectedURL);
+ }
+ }
+
+ private void openPodcast() {
+ Fragment fragment = ItemlistFragment.newInstance(item.getFeedId());
+ ((MainActivity)getActivity()).loadChildFragment(fragment);
+ }
+
+ public void onEventMainThread(FeedItemEvent event) {
+ Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
+ for(FeedItem item : event.items) {
+ if(feedItems[feedItemPos] == item.getId()) {
+ load();
+ return;
+ }
+ }
+ }
+
+ public void onEventMainThread(DownloadEvent event) {
+ Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]");
+ DownloaderUpdate update = event.update;
+ downloaderList = update.downloaders;
+ if(item == null || item.getMedia() == null) {
+ return;
+ }
+ long mediaId = item.getMedia().getId();
+ if(ArrayUtils.contains(update.mediaIds, mediaId)) {
+ if (itemsLoaded && getActivity() != null) {
+ updateAppearance();
+ }
+ }
+ }
+
+
+ private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() {
+ @Override
+ public void update(EventDistributor eventDistributor, Integer arg) {
+ if ((arg & EVENTS) != 0) {
+ load();
+ }
+ }
+ };
+
+ private void load() {
+ if(subscription != null) {
+ subscription.unsubscribe();
+ }
+ progbarLoading.setVisibility(View.VISIBLE);
+ subscription = Observable.fromCallable(this::loadInBackground)
+ .subscribeOn(Schedulers.newThread())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(result -> {
+ progbarLoading.setVisibility(View.GONE);
+ item = result;
+ itemsLoaded = true;
+ onFragmentLoaded();
+ }, error -> {
+ Log.e(TAG, Log.getStackTraceString(error));
+ });
+ }
+
+ private FeedItem loadInBackground() {
+ FeedItem feedItem = DBReader.getFeedItem(feedItems[feedItemPos]);
+ if (feedItem != null) {
+ Timeline t = new Timeline(getActivity(), feedItem);
+ webviewData = t.processShownotes(false);
+ }
+ return feedItem;
+ }
+
+}
diff --git a/app/src/free/java/de/danoeh/antennapod/preferences/PreferenceController.java b/app/src/free/java/de/danoeh/antennapod/preferences/PreferenceController.java
new file mode 100644
index 000000000..5cd44c0a7
--- /dev/null
+++ b/app/src/free/java/de/danoeh/antennapod/preferences/PreferenceController.java
@@ -0,0 +1,951 @@
+package de.danoeh.antennapod.preferences;
+
+import android.Manifest;
+import android.annotation.SuppressLint;
+import android.app.Activity;
+import android.app.TimePickerDialog;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+import android.os.Build;
+import android.preference.CheckBoxPreference;
+import android.preference.EditTextPreference;
+import android.preference.ListPreference;
+import android.preference.Preference;
+import android.preference.PreferenceManager;
+import android.preference.PreferenceScreen;
+import android.support.design.widget.Snackbar;
+import android.support.v4.app.ActivityCompat;
+import android.support.v4.content.ContextCompat;
+import android.support.v7.app.AlertDialog;
+import android.text.Editable;
+import android.text.Html;
+import android.text.TextWatcher;
+import android.text.format.DateFormat;
+import android.util.Log;
+import android.widget.EditText;
+import android.widget.Toast;
+import android.widget.ListView;
+
+import com.afollestad.materialdialogs.MaterialDialog;
+
+import org.apache.commons.lang3.ArrayUtils;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import de.danoeh.antennapod.CrashReportWriter;
+import de.danoeh.antennapod.R;
+import de.danoeh.antennapod.activity.AboutActivity;
+import de.danoeh.antennapod.activity.DirectoryChooserActivity;
+import de.danoeh.antennapod.activity.MainActivity;
+import de.danoeh.antennapod.activity.PreferenceActivity;
+import de.danoeh.antennapod.activity.PreferenceActivityGingerbread;
+import de.danoeh.antennapod.activity.StatisticsActivity;
+import de.danoeh.antennapod.asynctask.OpmlExportWorker;
+import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.service.GpodnetSyncService;
+import de.danoeh.antennapod.core.util.Converter;
+import de.danoeh.antennapod.core.util.StorageUtils;
+import de.danoeh.antennapod.core.util.flattr.FlattrUtils;
+import de.danoeh.antennapod.dialog.AuthenticationDialog;
+import de.danoeh.antennapod.dialog.AutoFlattrPreferenceDialog;
+import de.danoeh.antennapod.dialog.GpodnetSetHostnameDialog;
+import de.danoeh.antennapod.dialog.ProxyDialog;
+import de.danoeh.antennapod.dialog.VariableSpeedDialog;
+
+/**
+ * Sets up a preference UI that lets the user change user preferences.
+ */
+
+public class PreferenceController implements SharedPreferences.OnSharedPreferenceChangeListener {
+
+ private static final String TAG = "PreferenceController";
+
+ public static final String PREF_FLATTR_SETTINGS = "prefFlattrSettings";
+ public static final String PREF_FLATTR_AUTH = "pref_flattr_authenticate";
+ public static final String PREF_FLATTR_REVOKE = "prefRevokeAccess";
+ public static final String PREF_AUTO_FLATTR_PREFS = "prefAutoFlattrPrefs";
+ public static final String PREF_OPML_EXPORT = "prefOpmlExport";
+ public static final String STATISTICS = "statistics";
+ public static final String PREF_ABOUT = "prefAbout";
+ public static final String PREF_CHOOSE_DATA_DIR = "prefChooseDataDir";
+ public static final String AUTO_DL_PREF_SCREEN = "prefAutoDownloadSettings";
+ public static final String PREF_PLAYBACK_SPEED_LAUNCHER = "prefPlaybackSpeedLauncher";
+ public static final String PREF_GPODNET_LOGIN = "pref_gpodnet_authenticate";
+ public static final String PREF_GPODNET_SETLOGIN_INFORMATION = "pref_gpodnet_setlogin_information";
+ public static final String PREF_GPODNET_SYNC = "pref_gpodnet_sync";
+ public static final String PREF_GPODNET_LOGOUT = "pref_gpodnet_logout";
+ public static final String PREF_GPODNET_HOSTNAME = "pref_gpodnet_hostname";
+ public static final String PREF_EXPANDED_NOTIFICATION = "prefExpandNotify";
+ public static final String PREF_PROXY = "prefProxy";
+ public static final String PREF_KNOWN_ISSUES = "prefKnownIssues";
+ public static final String PREF_FAQ = "prefFaq";
+ public static final String PREF_SEND_CRASH_REPORT = "prefSendCrashReport";
+
+ private final PreferenceUI ui;
+
+ private CheckBoxPreference[] selectedNetworks;
+
+ private static final String[] EXTERNAL_STORAGE_PERMISSIONS = {
+ Manifest.permission.READ_EXTERNAL_STORAGE,
+ Manifest.permission.WRITE_EXTERNAL_STORAGE };
+ private static final int PERMISSION_REQUEST_EXTERNAL_STORAGE = 41;
+
+ public PreferenceController(PreferenceUI ui) {
+ this.ui = ui;
+ PreferenceManager.getDefaultSharedPreferences(ui.getActivity().getApplicationContext())
+ .registerOnSharedPreferenceChangeListener(this);
+ }
+
+ @Override
+ public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
+ if(key.equals(UserPreferences.PREF_SONIC)) {
+ CheckBoxPreference prefSonic = (CheckBoxPreference) ui.findPreference(UserPreferences.PREF_SONIC);
+ if(prefSonic != null) {
+ prefSonic.setChecked(sharedPreferences.getBoolean(UserPreferences.PREF_SONIC, false));
+ }
+ }
+ }
+
+ /**
+ * Returns the preference activity that should be used on this device.
+ *
+ * @return PreferenceActivity if the API level is greater than 10, PreferenceActivityGingerbread otherwise.
+ */
+ public static Class<? extends Activity> getPreferenceActivity() {
+ if (Build.VERSION.SDK_INT > 10) {
+ return PreferenceActivity.class;
+ } else {
+ return PreferenceActivityGingerbread.class;
+ }
+ }
+
+ public void onCreate() {
+ final Activity activity = ui.getActivity();
+
+ if (android.os.Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
+ // disable expanded notification option on unsupported android versions
+ ui.findPreference(PreferenceController.PREF_EXPANDED_NOTIFICATION).setEnabled(false);
+ ui.findPreference(PreferenceController.PREF_EXPANDED_NOTIFICATION).setOnPreferenceClickListener(
+ preference -> {
+ Toast toast = Toast.makeText(activity,
+ R.string.pref_expand_notify_unsupport_toast, Toast.LENGTH_SHORT);
+ toast.show();
+ return true;
+ }
+ );
+ }
+ ui.findPreference(PreferenceController.PREF_FLATTR_REVOKE).setOnPreferenceClickListener(
+ preference -> {
+ FlattrUtils.revokeAccessToken(activity);
+ checkItemVisibility();
+ return true;
+ }
+ );
+ ui.findPreference(PreferenceController.PREF_ABOUT).setOnPreferenceClickListener(
+ preference -> {
+ activity.startActivity(new Intent(activity, AboutActivity.class));
+ return true;
+ }
+ );
+ ui.findPreference(PreferenceController.STATISTICS).setOnPreferenceClickListener(
+ preference -> {
+ activity.startActivity(new Intent(activity, StatisticsActivity.class));
+ return true;
+ }
+ );
+ ui.findPreference(PreferenceController.PREF_OPML_EXPORT).setOnPreferenceClickListener(
+ preference -> {
+ new OpmlExportWorker(activity).executeAsync();
+ return true;
+ }
+ );
+ ui.findPreference(PreferenceController.PREF_CHOOSE_DATA_DIR).setOnPreferenceClickListener(
+ preference -> {
+ if (Build.VERSION_CODES.KITKAT <= Build.VERSION.SDK_INT &&
+ Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) {
+ showChooseDataFolderDialog();
+ } else {
+ int readPermission = ActivityCompat.checkSelfPermission(
+ activity, Manifest.permission.READ_EXTERNAL_STORAGE);
+ int writePermission = ActivityCompat.checkSelfPermission(
+ activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
+ if (readPermission == PackageManager.PERMISSION_GRANTED &&
+ writePermission == PackageManager.PERMISSION_GRANTED) {
+ openDirectoryChooser();
+ } else {
+ requestPermission();
+ }
+ }
+ return true;
+ }
+ );
+ ui.findPreference(PreferenceController.PREF_CHOOSE_DATA_DIR)
+ .setOnPreferenceClickListener(
+ preference -> {
+ if (Build.VERSION.SDK_INT >= 19) {
+ showChooseDataFolderDialog();
+ } else {
+ Intent intent = new Intent(activity, DirectoryChooserActivity.class);
+ activity.startActivityForResult(intent,
+ DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED);
+ }
+ return true;
+ }
+ );
+ ui.findPreference(UserPreferences.PREF_THEME)
+ .setOnPreferenceChangeListener(
+ (preference, newValue) -> {
+ Intent i = new Intent(activity, MainActivity.class);
+ i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK
+ | Intent.FLAG_ACTIVITY_NEW_TASK);
+ activity.finish();
+ activity.startActivity(i);
+ return true;
+ }
+ );
+ ui.findPreference(UserPreferences.PREF_HIDDEN_DRAWER_ITEMS)
+ .setOnPreferenceClickListener(preference -> {
+ showDrawerPreferencesDialog();
+ return true;
+ });
+
+ ui.findPreference(UserPreferences.PREF_COMPACT_NOTIFICATION_BUTTONS)
+ .setOnPreferenceClickListener(preference -> {
+ showNotificationButtonsDialog();
+ return true;
+ });
+
+ ui.findPreference(UserPreferences.PREF_UPDATE_INTERVAL)
+ .setOnPreferenceClickListener(preference -> {
+ showUpdateIntervalTimePreferencesDialog();
+ return true;
+ });
+
+ ui.findPreference(UserPreferences.PREF_ENABLE_AUTODL).setOnPreferenceChangeListener(
+ (preference, newValue) -> {
+ if (newValue instanceof Boolean) {
+ boolean enabled = (Boolean) newValue;
+ ui.findPreference(UserPreferences.PREF_EPISODE_CACHE_SIZE).setEnabled(enabled);
+ ui.findPreference(UserPreferences.PREF_ENABLE_AUTODL_ON_BATTERY).setEnabled(enabled);
+ ui.findPreference(UserPreferences.PREF_ENABLE_AUTODL_WIFI_FILTER).setEnabled(enabled);
+ setSelectedNetworksEnabled(enabled && UserPreferences.isEnableAutodownloadWifiFilter());
+ }
+ return true;
+ });
+ ui.findPreference(UserPreferences.PREF_ENABLE_AUTODL_WIFI_FILTER)
+ .setOnPreferenceChangeListener(
+ (preference, newValue) -> {
+ if (newValue instanceof Boolean) {
+ setSelectedNetworksEnabled((Boolean) newValue);
+ return true;
+ } else {
+ return false;
+ }
+ }
+ );
+ ui.findPreference(UserPreferences.PREF_PARALLEL_DOWNLOADS)
+ .setOnPreferenceChangeListener(
+ (preference, o) -> {
+ if (o instanceof String) {
+ try {
+ int value = Integer.parseInt((String) o);
+ if (1 <= value && value <= 50) {
+ setParallelDownloadsText(value);
+ return true;
+ }
+ } catch (NumberFormatException e) {
+ return false;
+ }
+ }
+ return false;
+ }
+ );
+ // validate and set correct value: number of downloads between 1 and 50 (inclusive)
+ final EditText ev = ((EditTextPreference) ui.findPreference(UserPreferences.PREF_PARALLEL_DOWNLOADS)).getEditText();
+ ev.addTextChangedListener(new TextWatcher() {
+ @Override
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
+ }
+
+ @Override
+ public void onTextChanged(CharSequence s, int start, int before, int count) {
+ }
+
+ @Override
+ public void afterTextChanged(Editable s) {
+ if (s.length() > 0) {
+ try {
+ int value = Integer.parseInt(s.toString());
+ if (value <= 0) {
+ ev.setText("1");
+ } else if (value > 50) {
+ ev.setText("50");
+ }
+ } catch (NumberFormatException e) {
+ ev.setText("6");
+ }
+ ev.setSelection(ev.getText().length());
+ }
+ }
+ });
+ ui.findPreference(UserPreferences.PREF_EPISODE_CACHE_SIZE)
+ .setOnPreferenceChangeListener(
+ (preference, o) -> {
+ if (o instanceof String) {
+ setEpisodeCacheSizeText(UserPreferences.readEpisodeCacheSize((String) o));
+ }
+ return true;
+ }
+ );
+ ui.findPreference(PreferenceController.PREF_PLAYBACK_SPEED_LAUNCHER)
+ .setOnPreferenceClickListener(preference -> {
+ VariableSpeedDialog.showDialog(activity);
+ return true;
+ });
+ ui.findPreference(PreferenceController.PREF_GPODNET_SETLOGIN_INFORMATION)
+ .setOnPreferenceClickListener(preference -> {
+ AuthenticationDialog dialog = new AuthenticationDialog(activity,
+ R.string.pref_gpodnet_setlogin_information_title, false, false, GpodnetPreferences.getUsername(),
+ null) {
+
+ @Override
+ protected void onConfirmed(String username, String password, boolean saveUsernamePassword) {
+ GpodnetPreferences.setPassword(password);
+ }
+ };
+ dialog.show();
+ return true;
+ });
+ ui.findPreference(PreferenceController.PREF_GPODNET_SYNC).
+ setOnPreferenceClickListener(preference -> {
+ GpodnetSyncService.sendSyncIntent(ui.getActivity().getApplicationContext());
+ Toast toast = Toast.makeText(ui.getActivity(), R.string.pref_gpodnet_sync_started,
+ Toast.LENGTH_SHORT);
+ toast.show();
+ return true;
+ });
+ ui.findPreference(PreferenceController.PREF_GPODNET_LOGOUT).setOnPreferenceClickListener(
+ preference -> {
+ GpodnetPreferences.logout();
+ Toast toast = Toast.makeText(activity, R.string.pref_gpodnet_logout_toast, Toast.LENGTH_SHORT);
+ toast.show();
+ updateGpodnetPreferenceScreen();
+ return true;
+ });
+ ui.findPreference(PreferenceController.PREF_GPODNET_HOSTNAME).setOnPreferenceClickListener(
+ preference -> {
+ GpodnetSetHostnameDialog.createDialog(activity).setOnDismissListener(dialog -> updateGpodnetPreferenceScreen());
+ return true;
+ });
+
+ ui.findPreference(PreferenceController.PREF_AUTO_FLATTR_PREFS)
+ .setOnPreferenceClickListener(preference -> {
+ AutoFlattrPreferenceDialog.newAutoFlattrPreferenceDialog(activity,
+ new AutoFlattrPreferenceDialog.AutoFlattrPreferenceDialogInterface() {
+ @Override
+ public void onCancelled() {
+
+ }
+
+ @Override
+ public void onConfirmed(boolean autoFlattrEnabled, float autoFlattrValue) {
+ UserPreferences.setAutoFlattrSettings(autoFlattrEnabled, autoFlattrValue);
+ checkItemVisibility();
+ }
+ });
+ return true;
+ });
+ ui.findPreference(UserPreferences.PREF_IMAGE_CACHE_SIZE).setOnPreferenceChangeListener(
+ (preference, o) -> {
+ if (o instanceof String) {
+ int newValue = Integer.parseInt((String) o) * 1024 * 1024;
+ if (newValue != UserPreferences.getImageCacheSize()) {
+ AlertDialog.Builder dialog = new AlertDialog.Builder(ui.getActivity());
+ dialog.setTitle(android.R.string.dialog_alert_title);
+ dialog.setMessage(R.string.pref_restart_required);
+ dialog.setPositiveButton(android.R.string.ok, null);
+ dialog.show();
+ }
+ return true;
+ }
+ return false;
+ }
+ );
+ ui.findPreference(PREF_PROXY).setOnPreferenceClickListener(preference -> {
+ ProxyDialog dialog = new ProxyDialog(ui.getActivity());
+ dialog.createDialog().show();
+ return true;
+ });
+ ui.findPreference(PREF_KNOWN_ISSUES).setOnPreferenceClickListener(preference -> {
+ openInBrowser("https://github.com/AntennaPod/AntennaPod/labels/bug");
+ return true;
+ });
+ ui.findPreference(PREF_FAQ).setOnPreferenceClickListener(preference -> {
+ openInBrowser("http://antennapod.org/faq.html");
+ return true;
+ });
+ ui.findPreference(PREF_SEND_CRASH_REPORT).setOnPreferenceClickListener(preference -> {
+ Intent emailIntent = new Intent(Intent.ACTION_SEND);
+ emailIntent.setType("text/plain");
+ emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[]{"Martin.Fietz@gmail.com"});
+ emailIntent.putExtra(Intent.EXTRA_SUBJECT, "AntennaPod Crash Report");
+ emailIntent.putExtra(Intent.EXTRA_TEXT, "Please describe what you were doing when the app crashed");
+ // the attachment
+ emailIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(CrashReportWriter.getFile()));
+ String intentTitle = ui.getActivity().getString(R.string.send_email);
+ ui.getActivity().startActivity(Intent.createChooser(emailIntent, intentTitle));
+ return true;
+ });
+ //checks whether Google Play Services is installed on the device (condition necessary for Cast support)
+// ui.findPreference(UserPreferences.PREF_CAST_ENABLED).setOnPreferenceChangeListener((preference, o) -> {
+// if (o instanceof Boolean && ((Boolean) o)) {
+// final int googlePlayServicesCheck = GoogleApiAvailability.getInstance()
+// .isGooglePlayServicesAvailable(ui.getActivity());
+// if (googlePlayServicesCheck == ConnectionResult.SUCCESS) {
+// return true;
+// } else {
+// GoogleApiAvailability.getInstance()
+// .getErrorDialog(ui.getActivity(), googlePlayServicesCheck, 0)
+// .show();
+// return false;
+// }
+// }
+// return true;
+// });
+ buildEpisodeCleanupPreference();
+ buildSmartMarkAsPlayedPreference();
+ buildAutodownloadSelectedNetworsPreference();
+ setSelectedNetworksEnabled(UserPreferences.isEnableAutodownloadWifiFilter());
+ }
+
+ private void openInBrowser(String url) {
+ try {
+ Intent myIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
+ ui.getActivity().startActivity(myIntent);
+ } catch (ActivityNotFoundException e) {
+ Toast.makeText(ui.getActivity(), R.string.pref_no_browser_found, Toast.LENGTH_LONG).show();
+ Log.e(TAG, Log.getStackTraceString(e));
+ }
+ }
+
+ public void onResume() {
+ checkItemVisibility();
+ setUpdateIntervalText();
+ setParallelDownloadsText(UserPreferences.getParallelDownloads());
+ setEpisodeCacheSizeText(UserPreferences.getEpisodeCacheSize());
+ setDataFolderText();
+ updateGpodnetPreferenceScreen();
+ }
+
+ @SuppressLint("NewApi")
+ public void onActivityResult(int requestCode, int resultCode, Intent data) {
+ if (resultCode == Activity.RESULT_OK &&
+ requestCode == DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED) {
+ String dir = data.getStringExtra(DirectoryChooserActivity.RESULT_SELECTED_DIR);
+
+ File path;
+ if(dir != null) {
+ path = new File(dir);
+ } else {
+ path = ui.getActivity().getExternalFilesDir(null);
+ }
+ String message = null;
+ final Context context= ui.getActivity().getApplicationContext();
+ if(!path.exists()) {
+ message = String.format(context.getString(R.string.folder_does_not_exist_error), dir);
+ } else if(!path.canRead()) {
+ message = String.format(context.getString(R.string.folder_not_readable_error), dir);
+ } else if(!path.canWrite()) {
+ message = String.format(context.getString(R.string.folder_not_writable_error), dir);
+ }
+
+ if(message == null) {
+ Log.d(TAG, "Setting data folder: " + dir);
+ UserPreferences.setDataFolder(dir);
+ setDataFolderText();
+ } else {
+ AlertDialog.Builder ab = new AlertDialog.Builder(ui.getActivity());
+ ab.setMessage(message);
+ ab.setPositiveButton(android.R.string.ok, null);
+ ab.show();
+ }
+ }
+ }
+
+
+ private void updateGpodnetPreferenceScreen() {
+ final boolean loggedIn = GpodnetPreferences.loggedIn();
+ ui.findPreference(PreferenceController.PREF_GPODNET_LOGIN).setEnabled(!loggedIn);
+ ui.findPreference(PreferenceController.PREF_GPODNET_SETLOGIN_INFORMATION).setEnabled(loggedIn);
+ ui.findPreference(PreferenceController.PREF_GPODNET_SYNC).setEnabled(loggedIn);
+ ui.findPreference(PreferenceController.PREF_GPODNET_LOGOUT).setEnabled(loggedIn);
+ if(loggedIn) {
+ String format = ui.getActivity().getString(R.string.pref_gpodnet_login_status);
+ String summary = String.format(format, GpodnetPreferences.getUsername(),
+ GpodnetPreferences.getDeviceID());
+ ui.findPreference(PreferenceController.PREF_GPODNET_LOGOUT).setSummary(Html.fromHtml(summary));
+ } else {
+ ui.findPreference(PreferenceController.PREF_GPODNET_LOGOUT).setSummary(null);
+ }
+ ui.findPreference(PreferenceController.PREF_GPODNET_HOSTNAME).setSummary(GpodnetPreferences.getHostname());
+ }
+
+ private String[] getUpdateIntervalEntries(final String[] values) {
+ final Resources res = ui.getActivity().getResources();
+ String[] entries = new String[values.length];
+ for (int x = 0; x < values.length; x++) {
+ Integer v = Integer.parseInt(values[x]);
+ switch (v) {
+ case 0:
+ entries[x] = res.getString(R.string.pref_update_interval_hours_manual);
+ break;
+ case 1:
+ entries[x] = v + " " + res.getString(R.string.pref_update_interval_hours_singular);
+ break;
+ default:
+ entries[x] = v + " " + res.getString(R.string.pref_update_interval_hours_plural);
+ break;
+
+ }
+ }
+ return entries;
+ }
+
+ private void buildEpisodeCleanupPreference() {
+ final Resources res = ui.getActivity().getResources();
+
+ ListPreference pref = (ListPreference) ui.findPreference(UserPreferences.PREF_EPISODE_CLEANUP);
+ String[] values = res.getStringArray(
+ R.array.episode_cleanup_values);
+ String[] entries = new String[values.length];
+ for (int x = 0; x < values.length; x++) {
+ int v = Integer.parseInt(values[x]);
+ if (v == UserPreferences.EPISODE_CLEANUP_QUEUE) {
+ entries[x] = res.getString(R.string.episode_cleanup_queue_removal);
+ } else if (v == UserPreferences.EPISODE_CLEANUP_NULL){
+ entries[x] = res.getString(R.string.episode_cleanup_never);
+ } else if (v == 0) {
+ entries[x] = res.getString(R.string.episode_cleanup_after_listening);
+ } else {
+ entries[x] = res.getQuantityString(R.plurals.episode_cleanup_days_after_listening, v, v);
+ }
+ }
+ pref.setEntries(entries);
+ }
+
+ private void buildSmartMarkAsPlayedPreference() {
+ final Resources res = ui.getActivity().getResources();
+
+ ListPreference pref = (ListPreference) ui.findPreference(UserPreferences.PREF_SMART_MARK_AS_PLAYED_SECS);
+ String[] values = res.getStringArray(R.array.smart_mark_as_played_values);
+ String[] entries = new String[values.length];
+ for (int x = 0; x < values.length; x++) {
+ if(x == 0) {
+ entries[x] = res.getString(R.string.pref_smart_mark_as_played_disabled);
+ } else {
+ Integer v = Integer.parseInt(values[x]);
+ if(v < 60) {
+ entries[x] = res.getQuantityString(R.plurals.time_seconds_quantified, v, v);
+ } else {
+ v /= 60;
+ entries[x] = res.getQuantityString(R.plurals.time_minutes_quantified, v, v);
+ }
+ }
+ }
+ pref.setEntries(entries);
+ }
+
+ private void setSelectedNetworksEnabled(boolean b) {
+ if (selectedNetworks != null) {
+ for (Preference p : selectedNetworks) {
+ p.setEnabled(b);
+ }
+ }
+ }
+
+ @SuppressWarnings("deprecation")
+ private void checkItemVisibility() {
+ boolean hasFlattrToken = FlattrUtils.hasToken();
+ ui.findPreference(PreferenceController.PREF_FLATTR_SETTINGS).setEnabled(FlattrUtils.hasAPICredentials());
+ ui.findPreference(PreferenceController.PREF_FLATTR_AUTH).setEnabled(!hasFlattrToken);
+ ui.findPreference(PreferenceController.PREF_FLATTR_REVOKE).setEnabled(hasFlattrToken);
+ ui.findPreference(PreferenceController.PREF_AUTO_FLATTR_PREFS).setEnabled(hasFlattrToken);
+
+ boolean autoDownload = UserPreferences.isEnableAutodownload();
+ ui.findPreference(UserPreferences.PREF_EPISODE_CACHE_SIZE).setEnabled(autoDownload);
+ ui.findPreference(UserPreferences.PREF_ENABLE_AUTODL_ON_BATTERY).setEnabled(autoDownload);
+ ui.findPreference(UserPreferences.PREF_ENABLE_AUTODL_WIFI_FILTER).setEnabled(autoDownload);
+ setSelectedNetworksEnabled(autoDownload && UserPreferences.isEnableAutodownloadWifiFilter());
+
+ ui.findPreference(PREF_SEND_CRASH_REPORT).setEnabled(CrashReportWriter.getFile().exists());
+
+ if (Build.VERSION.SDK_INT >= 16) {
+ ui.findPreference(UserPreferences.PREF_SONIC).setEnabled(true);
+ } else {
+ Preference prefSonic = ui.findPreference(UserPreferences.PREF_SONIC);
+ prefSonic.setSummary("[Android 4.1+]\n" + prefSonic.getSummary());
+ }
+ }
+
+ private void setUpdateIntervalText() {
+ Context context = ui.getActivity().getApplicationContext();
+ String val;
+ long interval = UserPreferences.getUpdateInterval();
+ if(interval > 0) {
+ int hours = (int) TimeUnit.MILLISECONDS.toHours(interval);
+ String hoursStr = context.getResources().getQuantityString(R.plurals.time_hours_quantified, hours, hours);
+ val = String.format(context.getString(R.string.pref_autoUpdateIntervallOrTime_every), hoursStr);
+ } else {
+ int[] timeOfDay = UserPreferences.getUpdateTimeOfDay();
+ if(timeOfDay.length == 2) {
+ Calendar cal = new GregorianCalendar();
+ cal.set(Calendar.HOUR_OF_DAY, timeOfDay[0]);
+ cal.set(Calendar.MINUTE, timeOfDay[1]);
+ String timeOfDayStr = DateFormat.getTimeFormat(context).format(cal.getTime());
+ val = String.format(context.getString(R.string.pref_autoUpdateIntervallOrTime_at),
+ timeOfDayStr);
+ } else {
+ val = context.getString(R.string.pref_smart_mark_as_played_disabled);
+ }
+ }
+ String summary = context.getString(R.string.pref_autoUpdateIntervallOrTime_sum) + "\n"
+ + String.format(context.getString(R.string.pref_current_value), val);
+ ui.findPreference(UserPreferences.PREF_UPDATE_INTERVAL).setSummary(summary);
+ }
+
+ private void setParallelDownloadsText(int downloads) {
+ final Resources res = ui.getActivity().getResources();
+
+ String s = Integer.toString(downloads)
+ + res.getString(R.string.parallel_downloads_suffix);
+ ui.findPreference(UserPreferences.PREF_PARALLEL_DOWNLOADS).setSummary(s);
+ }
+
+ private void setEpisodeCacheSizeText(int cacheSize) {
+ final Resources res = ui.getActivity().getResources();
+
+ String s;
+ if (cacheSize == res.getInteger(
+ R.integer.episode_cache_size_unlimited)) {
+ s = res.getString(R.string.pref_episode_cache_unlimited);
+ } else {
+ s = Integer.toString(cacheSize)
+ + res.getString(R.string.episodes_suffix);
+ }
+ ui.findPreference(UserPreferences.PREF_EPISODE_CACHE_SIZE).setSummary(s);
+ }
+
+ private void setDataFolderText() {
+ File f = UserPreferences.getDataFolder(null);
+ if (f != null) {
+ ui.findPreference(PreferenceController.PREF_CHOOSE_DATA_DIR)
+ .setSummary(f.getAbsolutePath());
+ }
+ }
+
+ private void buildAutodownloadSelectedNetworsPreference() {
+ final Activity activity = ui.getActivity();
+
+ if (selectedNetworks != null) {
+ clearAutodownloadSelectedNetworsPreference();
+ }
+ // get configured networks
+ WifiManager wifiservice = (WifiManager) activity.getSystemService(Context.WIFI_SERVICE);
+ List<WifiConfiguration> networks = wifiservice.getConfiguredNetworks();
+
+ if (networks != null) {
+ selectedNetworks = new CheckBoxPreference[networks.size()];
+ List<String> prefValues = Arrays.asList(UserPreferences
+ .getAutodownloadSelectedNetworks());
+ PreferenceScreen prefScreen = (PreferenceScreen) ui.findPreference(PreferenceController.AUTO_DL_PREF_SCREEN);
+ Preference.OnPreferenceClickListener clickListener = preference -> {
+ if (preference instanceof CheckBoxPreference) {
+ String key = preference.getKey();
+ List<String> prefValuesList = new ArrayList<>(
+ Arrays.asList(UserPreferences
+ .getAutodownloadSelectedNetworks())
+ );
+ boolean newValue = ((CheckBoxPreference) preference)
+ .isChecked();
+ Log.d(TAG, "Selected network " + key + ". New state: " + newValue);
+
+ int index = prefValuesList.indexOf(key);
+ if (index >= 0 && !newValue) {
+ // remove network
+ prefValuesList.remove(index);
+ } else if (index < 0 && newValue) {
+ prefValuesList.add(key);
+ }
+
+ UserPreferences.setAutodownloadSelectedNetworks(
+ prefValuesList.toArray(new String[prefValuesList.size()])
+ );
+ return true;
+ } else {
+ return false;
+ }
+ };
+ // create preference for each known network. attach listener and set
+ // value
+ for (int i = 0; i < networks.size(); i++) {
+ WifiConfiguration config = networks.get(i);
+
+ CheckBoxPreference pref = new CheckBoxPreference(activity);
+ String key = Integer.toString(config.networkId);
+ pref.setTitle(config.SSID);
+ pref.setKey(key);
+ pref.setOnPreferenceClickListener(clickListener);
+ pref.setPersistent(false);
+ pref.setChecked(prefValues.contains(key));
+ selectedNetworks[i] = pref;
+ prefScreen.addPreference(pref);
+ }
+ } else {
+ Log.e(TAG, "Couldn't get list of configure Wi-Fi networks");
+ }
+ }
+
+ private void clearAutodownloadSelectedNetworsPreference() {
+ if (selectedNetworks != null) {
+ PreferenceScreen prefScreen = (PreferenceScreen) ui.findPreference(PreferenceController.AUTO_DL_PREF_SCREEN);
+
+ for (CheckBoxPreference network : selectedNetworks) {
+ if (network != null) {
+ prefScreen.removePreference(network);
+ }
+ }
+ }
+ }
+
+ private void showDrawerPreferencesDialog() {
+ final Context context = ui.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++) {
+ String tag = NAV_DRAWER_TAGS[i];
+ if(!hiddenDrawerItems.contains(tag)) {
+ checked[i] = true;
+ }
+ }
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(R.string.drawer_preferences);
+ builder.setMultiChoiceItems(navTitles, 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 void showNotificationButtonsDialog() {
+ final Context context = ui.getActivity();
+ final List<Integer> preferredButtons = UserPreferences.getCompactNotificationButtons();
+ final String[] allButtonNames = context.getResources().getStringArray(
+ R.array.compact_notification_buttons_options);
+ boolean[] checked = new boolean[allButtonNames.length]; // booleans default to false in java
+
+ for(int i=0; i < checked.length; i++) {
+ if(preferredButtons.contains(i)) {
+ checked[i] = true;
+ }
+ }
+
+ AlertDialog.Builder builder = new AlertDialog.Builder(context);
+ builder.setTitle(String.format(context.getResources().getString(
+ R.string.pref_compact_notification_buttons_dialog_title), 2));
+ builder.setMultiChoiceItems(allButtonNames, checked, (dialog, which, isChecked) -> {
+ checked[which] = isChecked;
+
+ if (isChecked) {
+ if (preferredButtons.size() < 2) {
+ preferredButtons.add(which);
+ } else {
+ // Only allow a maximum of two selections. This is because the notification
+ // on the lock screen can only display 3 buttons, and the play/pause button
+ // is always included.
+ checked[which] = false;
+ ListView selectionView = ((AlertDialog) dialog).getListView();
+ selectionView.setItemChecked(which, false);
+ Snackbar.make(
+ selectionView,
+ String.format(context.getResources().getString(
+ R.string.pref_compact_notification_buttons_dialog_error), 2),
+ Snackbar.LENGTH_SHORT).show();
+ }
+ } else {
+ preferredButtons.remove((Integer) which);
+ }
+ });
+ builder.setPositiveButton(R.string.confirm_label, (dialog, which) -> {
+ UserPreferences.setCompactNotificationButtons(preferredButtons);
+ });
+ builder.setNegativeButton(R.string.cancel_label, null);
+ builder.create().show();
+ }
+
+ // CHOOSE DATA FOLDER
+
+ private void requestPermission() {
+ ActivityCompat.requestPermissions(ui.getActivity(), EXTERNAL_STORAGE_PERMISSIONS,
+ PERMISSION_REQUEST_EXTERNAL_STORAGE);
+ }
+
+ private void openDirectoryChooser() {
+ Activity activity = ui.getActivity();
+ Intent intent = new Intent(activity, DirectoryChooserActivity.class);
+ activity.startActivityForResult(intent, DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED);
+ }
+
+ private void showChooseDataFolderDialog() {
+ Context context = ui.getActivity();
+ File dataFolder = UserPreferences.getDataFolder(null);
+ if(dataFolder == null) {
+ new MaterialDialog.Builder(ui.getActivity())
+ .title(R.string.error_label)
+ .content(R.string.external_storage_error_msg)
+ .neutralText(android.R.string.ok)
+ .show();
+ return;
+ }
+ String dataFolderPath = dataFolder.getAbsolutePath();
+ int selectedIndex = -1;
+ File[] mediaDirs = ContextCompat.getExternalFilesDirs(context, null);
+ List<String> folders = new ArrayList<>(mediaDirs.length);
+ List<CharSequence> choices = new ArrayList<>(mediaDirs.length);
+ for(int i=0; i < mediaDirs.length; i++) {
+ File dir = mediaDirs[i];
+ if(dir == null || !dir.exists() || !dir.canRead() || !dir.canWrite()) {
+ continue;
+ }
+ String path = mediaDirs[i].getAbsolutePath();
+ folders.add(path);
+ if(dataFolderPath.equals(path)) {
+ selectedIndex = i;
+ }
+ int index = path.indexOf("Android");
+ String choice;
+ if(index >= 0) {
+ choice = path.substring(0, index);
+ } else {
+ choice = path;
+ }
+ long bytes = StorageUtils.getFreeSpaceAvailable(path);
+ String freeSpace = String.format(context.getString(R.string.free_space_label),
+ Converter.byteToString(bytes));
+ choices.add(Html.fromHtml("<html><small>" + choice
+ + " [" + freeSpace + "]" + "</small></html>"));
+ }
+ if(choices.size() == 0) {
+ new MaterialDialog.Builder(ui.getActivity())
+ .title(R.string.error_label)
+ .content(R.string.external_storage_error_msg)
+ .neutralText(android.R.string.ok)
+ .show();
+ return;
+ }
+ MaterialDialog dialog = new MaterialDialog.Builder(ui.getActivity())
+ .title(R.string.choose_data_directory)
+ .content(R.string.choose_data_directory_message)
+ .items(choices.toArray(new CharSequence[choices.size()]))
+ .itemsCallbackSingleChoice(selectedIndex, (dialog1, itemView, which, text) -> {
+ String folder = folders.get(which);
+ Log.d(TAG, "data folder: " + folder);
+ UserPreferences.setDataFolder(folder);
+ setDataFolderText();
+ return true;
+ })
+ .negativeText(R.string.cancel_label)
+ .cancelable(true)
+ .build();
+ dialog.show();
+ }
+
+ // UPDATE TIME/INTERVAL DIALOG
+
+ private void showUpdateIntervalTimePreferencesDialog() {
+ final Context context = ui.getActivity();
+
+ MaterialDialog.Builder builder = new MaterialDialog.Builder(context);
+ builder.title(R.string.pref_autoUpdateIntervallOrTime_title);
+ builder.content(R.string.pref_autoUpdateIntervallOrTime_message);
+ builder.positiveText(R.string.pref_autoUpdateIntervallOrTime_Interval);
+ builder.negativeText(R.string.pref_autoUpdateIntervallOrTime_TimeOfDay);
+ builder.neutralText(R.string.pref_autoUpdateIntervallOrTime_Disable);
+ builder.onPositive((dialog, which) -> {
+ AlertDialog.Builder builder1 = new AlertDialog.Builder(context);
+ builder1.setTitle(context.getString(R.string.pref_autoUpdateIntervallOrTime_Interval));
+ final String[] values = context.getResources().getStringArray(R.array.update_intervall_values);
+ final String[] entries = getUpdateIntervalEntries(values);
+ long currInterval = UserPreferences.getUpdateInterval();
+ int checkedItem = -1;
+ if(currInterval > 0) {
+ String currIntervalStr = String.valueOf(TimeUnit.MILLISECONDS.toHours(currInterval));
+ checkedItem = ArrayUtils.indexOf(values, currIntervalStr);
+ }
+ builder1.setSingleChoiceItems(entries, checkedItem, (dialog1, which1) -> {
+ int hours = Integer.parseInt(values[which1]);
+ UserPreferences.setUpdateInterval(hours);
+ dialog1.dismiss();
+ setUpdateIntervalText();
+ });
+ builder1.setNegativeButton(context.getString(R.string.cancel_label), null);
+ builder1.show();
+ });
+ builder.onNegative((dialog, which) -> {
+ int hourOfDay = 7, minute = 0;
+ int[] updateTime = UserPreferences.getUpdateTimeOfDay();
+ if (updateTime.length == 2) {
+ hourOfDay = updateTime[0];
+ minute = updateTime[1];
+ }
+ TimePickerDialog timePickerDialog = new TimePickerDialog(context,
+ (view, selectedHourOfDay, selectedMinute) -> {
+ if (view.getTag() == null) { // onTimeSet() may get called twice!
+ view.setTag("TAGGED");
+ UserPreferences.setUpdateTimeOfDay(selectedHourOfDay, selectedMinute);
+ setUpdateIntervalText();
+ }
+ }, hourOfDay, minute, DateFormat.is24HourFormat(context));
+ timePickerDialog.setTitle(context.getString(R.string.pref_autoUpdateIntervallOrTime_TimeOfDay));
+ timePickerDialog.show();
+ });
+ builder.onNeutral((dialog, which) -> {
+ UserPreferences.setUpdateInterval(0);
+ setUpdateIntervalText();
+ });
+ builder.show();
+ }
+
+
+ public interface PreferenceUI {
+
+ /**
+ * Finds a preference based on its key.
+ */
+ Preference findPreference(CharSequence key);
+
+ Activity getActivity();
+ }
+}