summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMartin Fietz <Martin.Fietz@gmail.com>2016-06-09 00:00:47 +0200
committerMartin Fietz <Martin.Fietz@gmail.com>2016-06-09 00:00:47 +0200
commit405630ee502819f7214f77ab49c484f4c7fd918c (patch)
tree8ae0c8917711da636d9625a71bcd1b044fd1e80a
parent89a1851f57f7a3357ceeef20fa18538febbd2760 (diff)
parent186791b22bd850133b52e9f544c03f5282d477c8 (diff)
downloadAntennaPod-405630ee502819f7214f77ab49c484f4c7fd918c.zip
Merge pull request #2001 from domingos86/flavors-optimization
Flavors code optimization
-rw-r--r--app/src/free/java/de/danoeh/antennapod/activity/CastEnabledActivity.java8
-rw-r--r--app/src/free/java/de/danoeh/antennapod/activity/MainActivity.java773
-rw-r--r--app/src/free/java/de/danoeh/antennapod/fragment/ItemFragment.java592
-rw-r--r--app/src/free/java/de/danoeh/antennapod/preferences/PreferenceControllerFlavorHelper.java13
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java (renamed from app/src/play/java/de/danoeh/antennapod/activity/MainActivity.java)32
-rw-r--r--app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java (renamed from app/src/free/java/de/danoeh/antennapod/activity/MediaplayerActivity.java)5
-rw-r--r--app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java (renamed from app/src/play/java/de/danoeh/antennapod/fragment/ItemFragment.java)6
-rw-r--r--app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java (renamed from app/src/free/java/de/danoeh/antennapod/preferences/PreferenceController.java)17
-rw-r--r--app/src/play/java/de/danoeh/antennapod/activity/MediaplayerActivity.java876
-rw-r--r--app/src/play/java/de/danoeh/antennapod/preferences/PreferenceController.java953
-rw-r--r--app/src/play/java/de/danoeh/antennapod/preferences/PreferenceControllerFlavorHelper.java31
-rw-r--r--core/src/free/java/de/danoeh/antennapod/core/feed/FeedMedia.java567
-rw-r--r--core/src/free/java/de/danoeh/antennapod/core/feed/FeedMediaFlavorHelper.java10
-rw-r--r--core/src/free/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java44
-rw-r--r--core/src/free/java/de/danoeh/antennapod/core/service/playback/RemotePSMP.java592
-rw-r--r--core/src/free/res/values/strings.xml4
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java (renamed from core/src/play/java/de/danoeh/antennapod/core/feed/FeedMedia.java)3
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java (renamed from core/src/free/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java)279
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/Flavors.java24
-rw-r--r--core/src/main/java/de/danoeh/antennapod/core/util/playback/Playable.java (renamed from core/src/free/java/de/danoeh/antennapod/core/util/playback/Playable.java)9
-rw-r--r--core/src/main/res/values/strings.xml3
-rw-r--r--core/src/play/java/de/danoeh/antennapod/core/feed/FeedMediaFlavorHelper.java12
-rw-r--r--core/src/play/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java1780
-rw-r--r--core/src/play/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java252
-rw-r--r--core/src/play/java/de/danoeh/antennapod/core/util/playback/Playable.java246
-rw-r--r--core/src/play/res/values/strings.xml4
26 files changed, 514 insertions, 6621 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
index d82b199db..79d7e02f2 100644
--- a/app/src/free/java/de/danoeh/antennapod/activity/CastEnabledActivity.java
+++ b/app/src/free/java/de/danoeh/antennapod/activity/CastEnabledActivity.java
@@ -106,10 +106,10 @@ public abstract class CastEnabledActivity extends AppCompatActivity {
// *
// * @param showAsAction refer to {@link MenuItem#setShowAsAction(int)}
// */
-// public final void requestCastButton(int showAsAction) {
-// castButtonVisibilityManager.requestCastButton(showAsAction);
-// }
-//
+ public final void requestCastButton(int showAsAction) {
+ // no-op
+ }
+
// private class CastButtonVisibilityManager {
// private volatile boolean prefEnabled = false;
// private volatile boolean viewRequested = false;
diff --git a/app/src/free/java/de/danoeh/antennapod/activity/MainActivity.java b/app/src/free/java/de/danoeh/antennapod/activity/MainActivity.java
deleted file mode 100644
index c4771a583..000000000
--- a/app/src/free/java/de/danoeh/antennapod/activity/MainActivity.java
+++ /dev/null
@@ -1,773 +0,0 @@
-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/fragment/ItemFragment.java b/app/src/free/java/de/danoeh/antennapod/fragment/ItemFragment.java
deleted file mode 100644
index 0e11a5a17..000000000
--- a/app/src/free/java/de/danoeh/antennapod/fragment/ItemFragment.java
+++ /dev/null
@@ -1,592 +0,0 @@
-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.LongList;
-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, item.isTagged(FeedItem.TAG_QUEUE) ?
- LongList.of(item.getId()) : new LongList(0));
- 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/PreferenceControllerFlavorHelper.java b/app/src/free/java/de/danoeh/antennapod/preferences/PreferenceControllerFlavorHelper.java
new file mode 100644
index 000000000..de604c7c4
--- /dev/null
+++ b/app/src/free/java/de/danoeh/antennapod/preferences/PreferenceControllerFlavorHelper.java
@@ -0,0 +1,13 @@
+package de.danoeh.antennapod.preferences;
+
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+
+/**
+ * Implements functions from PreferenceController that are flavor dependent.
+ */
+public class PreferenceControllerFlavorHelper {
+
+ static void setupFlavoredUI(PreferenceController.PreferenceUI ui) {
+ ui.findPreference(UserPreferences.PREF_CAST_ENABLED).setEnabled(false);
+ }
+}
diff --git a/app/src/play/java/de/danoeh/antennapod/activity/MainActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
index 17965ca8e..447edc567 100644
--- a/app/src/play/java/de/danoeh/antennapod/activity/MainActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java
@@ -48,6 +48,7 @@ import de.danoeh.antennapod.core.service.playback.PlaybackService;
import de.danoeh.antennapod.core.storage.DBReader;
import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.FeedItemUtil;
+import de.danoeh.antennapod.core.util.Flavors;
import de.danoeh.antennapod.core.util.StorageUtils;
import de.danoeh.antennapod.dialog.RatingDialog;
import de.danoeh.antennapod.fragment.AddFeedFragment;
@@ -507,21 +508,24 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
@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;
+ if (Flavors.FLAVOR == Flavors.PLAY) {
+ 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;
+ }
+ } else {
+ return retVal;
}
-
}
@Override
diff --git a/app/src/free/java/de/danoeh/antennapod/activity/MediaplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
index 0d6e5504a..ce8672b59 100644
--- a/app/src/free/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
+++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
@@ -39,6 +39,7 @@ 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.Flavors;
import de.danoeh.antennapod.core.util.ShareUtils;
import de.danoeh.antennapod.core.util.StorageUtils;
import de.danoeh.antennapod.core.util.playback.MediaPlayerError;
@@ -281,7 +282,9 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
-// requestCastButton(MenuItem.SHOW_AS_ACTION_ALWAYS);
+ if (Flavors.FLAVOR == Flavors.PLAY) {
+ requestCastButton(MenuItem.SHOW_AS_ACTION_ALWAYS);
+ }
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.mediaplayer, menu);
return true;
diff --git a/app/src/play/java/de/danoeh/antennapod/fragment/ItemFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
index 524b78ed2..6c2e608dc 100644
--- a/app/src/play/java/de/danoeh/antennapod/fragment/ItemFragment.java
+++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java
@@ -58,6 +58,7 @@ 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.Flavors;
import de.danoeh.antennapod.core.util.IntentUtils;
import de.danoeh.antennapod.core.util.LongList;
import de.danoeh.antennapod.core.util.ShareUtils;
@@ -312,7 +313,10 @@ public class ItemFragment extends Fragment implements OnSwipeGesture {
if(!isAdded() || item == null) {
return;
}
- ((CastEnabledActivity) getActivity()).requestCastButton(MenuItem.SHOW_AS_ACTION_ALWAYS);
+ super.onCreateOptionsMenu(menu, inflater);
+ if (Flavors.FLAVOR == Flavors.PLAY) {
+ ((CastEnabledActivity) getActivity()).requestCastButton(MenuItem.SHOW_AS_ACTION_ALWAYS);
+ }
inflater.inflate(R.menu.feeditem_options, menu);
popupMenu = menu;
if (item.hasMedia()) {
diff --git a/app/src/free/java/de/danoeh/antennapod/preferences/PreferenceController.java b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java
index 5cd44c0a7..e1ef24afd 100644
--- a/app/src/free/java/de/danoeh/antennapod/preferences/PreferenceController.java
+++ b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java
@@ -410,22 +410,7 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc
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;
-// });
+ PreferenceControllerFlavorHelper.setupFlavoredUI(ui);
buildEpisodeCleanupPreference();
buildSmartMarkAsPlayedPreference();
buildAutodownloadSelectedNetworsPreference();
diff --git a/app/src/play/java/de/danoeh/antennapod/activity/MediaplayerActivity.java b/app/src/play/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
deleted file mode 100644
index 71d288725..000000000
--- a/app/src/play/java/de/danoeh/antennapod/activity/MediaplayerActivity.java
+++ /dev/null
@@ -1,876 +0,0 @@
-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/play/java/de/danoeh/antennapod/preferences/PreferenceController.java b/app/src/play/java/de/danoeh/antennapod/preferences/PreferenceController.java
deleted file mode 100644
index 183ef338a..000000000
--- a/app/src/play/java/de/danoeh/antennapod/preferences/PreferenceController.java
+++ /dev/null
@@ -1,953 +0,0 @@
-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 com.google.android.gms.common.ConnectionResult;
-import com.google.android.gms.common.GoogleApiAvailability;
-
-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();
- }
-}
diff --git a/app/src/play/java/de/danoeh/antennapod/preferences/PreferenceControllerFlavorHelper.java b/app/src/play/java/de/danoeh/antennapod/preferences/PreferenceControllerFlavorHelper.java
new file mode 100644
index 000000000..b9b4e891f
--- /dev/null
+++ b/app/src/play/java/de/danoeh/antennapod/preferences/PreferenceControllerFlavorHelper.java
@@ -0,0 +1,31 @@
+package de.danoeh.antennapod.preferences;
+
+import com.google.android.gms.common.ConnectionResult;
+import com.google.android.gms.common.GoogleApiAvailability;
+
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+
+/**
+ * Implements functions from PreferenceController that are flavor dependent.
+ */
+public class PreferenceControllerFlavorHelper {
+
+ static void setupFlavoredUI(PreferenceController.PreferenceUI ui) {
+ //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;
+ });
+ }
+}
diff --git a/core/src/free/java/de/danoeh/antennapod/core/feed/FeedMedia.java b/core/src/free/java/de/danoeh/antennapod/core/feed/FeedMedia.java
deleted file mode 100644
index cde66835a..000000000
--- a/core/src/free/java/de/danoeh/antennapod/core/feed/FeedMedia.java
+++ /dev/null
@@ -1,567 +0,0 @@
-package de.danoeh.antennapod.core.feed;
-
-import android.content.SharedPreferences;
-import android.content.SharedPreferences.Editor;
-import android.database.Cursor;
-import android.media.MediaMetadataRetriever;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.support.annotation.Nullable;
-
-import java.util.Date;
-import java.util.List;
-import java.util.concurrent.Callable;
-
-import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.storage.DBReader;
-import de.danoeh.antennapod.core.storage.DBWriter;
-import de.danoeh.antennapod.core.storage.PodDBAdapter;
-import de.danoeh.antennapod.core.util.ChapterUtils;
-import de.danoeh.antennapod.core.util.playback.Playable;
-
-public class FeedMedia extends FeedFile implements Playable {
- private static final String TAG = "FeedMedia";
-
- public static final int FEEDFILETYPE_FEEDMEDIA = 2;
- public static final int PLAYABLE_TYPE_FEEDMEDIA = 1;
-
- public static final String PREF_MEDIA_ID = "FeedMedia.PrefMediaId";
- public static final String PREF_FEED_ID = "FeedMedia.PrefFeedId";
-
- /**
- * Indicates we've checked on the size of the item via the network
- * and got an invalid response. Using Integer.MIN_VALUE because
- * 1) we'll still check on it in case it gets downloaded (it's <= 0)
- * 2) By default all FeedMedia have a size of 0 if we don't know it,
- * so this won't conflict with existing practice.
- */
- private static final int CHECKED_ON_SIZE_BUT_UNKNOWN = Integer.MIN_VALUE;
-
- private int duration;
- private int position; // Current position in file
- private long lastPlayedTime; // Last time this media was played (in ms)
- private int played_duration; // How many ms of this file have been played (for autoflattring)
- private long size; // File size in Byte
- private String mime_type;
- @Nullable private volatile FeedItem item;
- private Date playbackCompletionDate;
-
- // if null: unknown, will be checked
- private Boolean hasEmbeddedPicture;
-
- /* Used for loading item when restoring from parcel. */
- private long itemID;
-
- public FeedMedia(FeedItem i, String download_url, long size,
- String mime_type) {
- super(null, download_url, false);
- this.item = i;
- this.size = size;
- this.mime_type = mime_type;
- }
-
- public FeedMedia(long id, FeedItem item, int duration, int position,
- long size, String mime_type, String file_url, String download_url,
- boolean downloaded, Date playbackCompletionDate, int played_duration,
- long lastPlayedTime) {
- super(file_url, download_url, downloaded);
- this.id = id;
- this.item = item;
- this.duration = duration;
- this.position = position;
- this.played_duration = played_duration;
- this.size = size;
- this.mime_type = mime_type;
- this.playbackCompletionDate = playbackCompletionDate == null
- ? null : (Date) playbackCompletionDate.clone();
- this.lastPlayedTime = lastPlayedTime;
- }
-
- public FeedMedia(long id, FeedItem item, int duration, int position,
- long size, String mime_type, String file_url, String download_url,
- boolean downloaded, Date playbackCompletionDate, int played_duration,
- Boolean hasEmbeddedPicture, long lastPlayedTime) {
- this(id, item, duration, position, size, mime_type, file_url, download_url, downloaded,
- playbackCompletionDate, played_duration, lastPlayedTime);
- this.hasEmbeddedPicture = hasEmbeddedPicture;
- }
-
- public static FeedMedia fromCursor(Cursor cursor) {
- int indexId = cursor.getColumnIndex(PodDBAdapter.KEY_ID);
- int indexPlaybackCompletionDate = cursor.getColumnIndex(PodDBAdapter.KEY_PLAYBACK_COMPLETION_DATE);
- int indexDuration = cursor.getColumnIndex(PodDBAdapter.KEY_DURATION);
- int indexPosition = cursor.getColumnIndex(PodDBAdapter.KEY_POSITION);
- int indexSize = cursor.getColumnIndex(PodDBAdapter.KEY_SIZE);
- int indexMimeType = cursor.getColumnIndex(PodDBAdapter.KEY_MIME_TYPE);
- int indexFileUrl = cursor.getColumnIndex(PodDBAdapter.KEY_FILE_URL);
- int indexDownloadUrl = cursor.getColumnIndex(PodDBAdapter.KEY_DOWNLOAD_URL);
- int indexDownloaded = cursor.getColumnIndex(PodDBAdapter.KEY_DOWNLOADED);
- int indexPlayedDuration = cursor.getColumnIndex(PodDBAdapter.KEY_PLAYED_DURATION);
- int indexLastPlayedTime = cursor.getColumnIndex(PodDBAdapter.KEY_LAST_PLAYED_TIME);
-
- long mediaId = cursor.getLong(indexId);
- Date playbackCompletionDate = null;
- long playbackCompletionTime = cursor.getLong(indexPlaybackCompletionDate);
- if (playbackCompletionTime > 0) {
- playbackCompletionDate = new Date(playbackCompletionTime);
- }
-
- Boolean hasEmbeddedPicture;
- switch(cursor.getInt(cursor.getColumnIndex(PodDBAdapter.KEY_HAS_EMBEDDED_PICTURE))) {
- case 1:
- hasEmbeddedPicture = Boolean.TRUE;
- break;
- case 0:
- hasEmbeddedPicture = Boolean.FALSE;
- break;
- default:
- hasEmbeddedPicture = null;
- break;
- }
-
- return new FeedMedia(
- mediaId,
- null,
- cursor.getInt(indexDuration),
- cursor.getInt(indexPosition),
- cursor.getLong(indexSize),
- cursor.getString(indexMimeType),
- cursor.getString(indexFileUrl),
- cursor.getString(indexDownloadUrl),
- cursor.getInt(indexDownloaded) > 0,
- playbackCompletionDate,
- cursor.getInt(indexPlayedDuration),
- hasEmbeddedPicture,
- cursor.getLong(indexLastPlayedTime)
- );
- }
-
-
- @Override
- public String getHumanReadableIdentifier() {
- if (item != null && item.getTitle() != null) {
- return item.getTitle();
- } else {
- return download_url;
- }
- }
-
- /**
- * Uses mimetype to determine the type of media.
- */
- public MediaType getMediaType() {
- return MediaType.fromMimeType(mime_type);
- }
-
- public void updateFromOther(FeedMedia other) {
- super.updateFromOther(other);
- if (other.size > 0) {
- size = other.size;
- }
- if (other.mime_type != null) {
- mime_type = other.mime_type;
- }
- }
-
- public boolean compareWithOther(FeedMedia other) {
- if (super.compareWithOther(other)) {
- return true;
- }
- if (other.mime_type != null) {
- if (mime_type == null || !mime_type.equals(other.mime_type)) {
- return true;
- }
- }
- if (other.size > 0 && other.size != size) {
- return true;
- }
- return false;
- }
-
- /**
- * Reads playback preferences to determine whether this FeedMedia object is
- * currently being played.
- */
- public boolean isPlaying() {
- return PlaybackPreferences.getCurrentlyPlayingMedia() == FeedMedia.PLAYABLE_TYPE_FEEDMEDIA
- && PlaybackPreferences.getCurrentlyPlayingFeedMediaId() == id;
- }
-
- /**
- * Reads playback preferences to determine whether this FeedMedia object is
- * currently being played and the current player status is playing.
- */
- public boolean isCurrentlyPlaying() {
- return isPlaying() &&
- ((PlaybackPreferences.getCurrentPlayerStatus() == PlaybackPreferences.PLAYER_STATUS_PLAYING));
- }
-
- /**
- * Reads playback preferences to determine whether this FeedMedia object is
- * currently being played and the current player status is paused.
- */
- public boolean isCurrentlyPaused() {
- return isPlaying() &&
- ((PlaybackPreferences.getCurrentPlayerStatus() == PlaybackPreferences.PLAYER_STATUS_PAUSED));
- }
-
-
- public boolean hasAlmostEnded() {
- int smartMarkAsPlayedSecs = UserPreferences.getSmartMarkAsPlayedSecs();
- return this.position >= this.duration - smartMarkAsPlayedSecs * 1000;
- }
-
- @Override
- public int getTypeAsInt() {
- return FEEDFILETYPE_FEEDMEDIA;
- }
-
- public int getDuration() {
- return duration;
- }
-
- public void setDuration(int duration) {
- this.duration = duration;
- }
-
- @Override
- public void setLastPlayedTime(long lastPlayedTime) {
- this.lastPlayedTime = lastPlayedTime;
- }
-
- public int getPlayedDuration() {
- return played_duration;
- }
-
- public void setPlayedDuration(int played_duration) {
- this.played_duration = played_duration;
- }
-
- public int getPosition() {
- return position;
- }
-
- @Override
- public long getLastPlayedTime() {
- return lastPlayedTime;
- }
-
- public void setPosition(int position) {
- this.position = position;
- if(position > 0 && item != null && item.isNew()) {
- this.item.setPlayed(false);
- }
- }
-
- public long getSize() {
- return size;
- }
-
- public void setSize(long size) {
- this.size = size;
- }
-
- /**
- * Indicates we asked the service what the size was, but didn't
- * get a valid answer and we shoudln't check using the network again.
- */
- public void setCheckedOnSizeButUnknown() {
- this.size = CHECKED_ON_SIZE_BUT_UNKNOWN;
- }
-
- public boolean checkedOnSizeButUnknown() {
- return (CHECKED_ON_SIZE_BUT_UNKNOWN == this.size);
- }
-
- public String getMime_type() {
- return mime_type;
- }
-
- public void setMime_type(String mime_type) {
- this.mime_type = mime_type;
- }
-
- @Nullable
- public FeedItem getItem() {
- return item;
- }
-
- /**
- * Sets the item object of this FeedMedia. If the given
- * FeedItem object is not null, it's 'media'-attribute value
- * will also be set to this media object.
- */
- public void setItem(FeedItem item) {
- this.item = item;
- if (item != null && item.getMedia() != this) {
- item.setMedia(this);
- }
- }
-
- public Date getPlaybackCompletionDate() {
- return playbackCompletionDate == null
- ? null : (Date) playbackCompletionDate.clone();
- }
-
- public void setPlaybackCompletionDate(Date playbackCompletionDate) {
- this.playbackCompletionDate = playbackCompletionDate == null
- ? null : (Date) playbackCompletionDate.clone();
- }
-
- public boolean isInProgress() {
- return (this.position > 0);
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- public boolean hasEmbeddedPicture() {
- if(hasEmbeddedPicture == null) {
- checkEmbeddedPicture();
- }
- return hasEmbeddedPicture;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeLong(id);
- dest.writeLong(item != null ? item.getId() : 0L);
-
- dest.writeInt(duration);
- dest.writeInt(position);
- dest.writeLong(size);
- dest.writeString(mime_type);
- dest.writeString(file_url);
- dest.writeString(download_url);
- dest.writeByte((byte) ((downloaded) ? 1 : 0));
- dest.writeLong((playbackCompletionDate != null) ? playbackCompletionDate.getTime() : 0);
- dest.writeInt(played_duration);
- dest.writeLong(lastPlayedTime);
- }
-
- @Override
- public void writeToPreferences(Editor prefEditor) {
- if(item != null && item.getFeed() != null) {
- prefEditor.putLong(PREF_FEED_ID, item.getFeed().getId());
- } else {
- prefEditor.putLong(PREF_FEED_ID, 0L);
- }
- prefEditor.putLong(PREF_MEDIA_ID, id);
- }
-
- @Override
- public void loadMetadata() throws PlayableException {
- if (item == null && itemID != 0) {
- item = DBReader.getFeedItem(itemID);
- }
- }
-
- @Override
- public void loadChapterMarks() {
- if (item == null && itemID != 0) {
- item = DBReader.getFeedItem(itemID);
- }
- // check if chapters are stored in db and not loaded yet.
- if (item != null && item.hasChapters() && item.getChapters() == null) {
- DBReader.loadChaptersOfFeedItem(item);
- } else if (item != null && item.getChapters() == null) {
- if(localFileAvailable()) {
- ChapterUtils.loadChaptersFromFileUrl(this);
- } else {
- ChapterUtils.loadChaptersFromStreamUrl(this);
- }
- if (getChapters() != null && item != null) {
- DBWriter.setFeedItem(item);
- }
- }
- }
-
- @Override
- public String getEpisodeTitle() {
- if (item == null) {
- return null;
- }
- if (item.getTitle() != null) {
- return item.getTitle();
- } else {
- return item.getIdentifyingValue();
- }
- }
-
- @Override
- public List<Chapter> getChapters() {
- if (item == null) {
- return null;
- }
- return item.getChapters();
- }
-
- @Override
- public String getWebsiteLink() {
- if (item == null) {
- return null;
- }
- return item.getLink();
- }
-
- @Override
- public String getFeedTitle() {
- if (item == null || item.getFeed() == null) {
- return null;
- }
- return item.getFeed().getTitle();
- }
-
- @Override
- public Object getIdentifier() {
- return id;
- }
-
- @Override
- public String getLocalMediaUrl() {
- return file_url;
- }
-
- @Override
- public String getStreamUrl() {
- return download_url;
- }
-
- @Override
- public String getPaymentLink() {
- if (item == null) {
- return null;
- }
- return item.getPaymentLink();
- }
-
- @Override
- public boolean localFileAvailable() {
- return isDownloaded() && file_url != null;
- }
-
- @Override
- public boolean streamAvailable() {
- return download_url != null;
- }
-
- @Override
- public void saveCurrentPosition(SharedPreferences pref, int newPosition, long timeStamp) {
- if(item != null && item.isNew()) {
- DBWriter.markItemPlayed(FeedItem.UNPLAYED, item.getId());
- }
- setPosition(newPosition);
- setLastPlayedTime(timeStamp);
- DBWriter.setFeedMediaPlaybackInformation(this);
- }
-
- @Override
- public void onPlaybackStart() {
- }
- @Override
- public void onPlaybackCompleted() {
-
- }
-
- @Override
- public int getPlayableType() {
- return PLAYABLE_TYPE_FEEDMEDIA;
- }
-
- @Override
- public void setChapters(List<Chapter> chapters) {
- if(item != null) {
- item.setChapters(chapters);
- }
- }
-
- @Override
- public Callable<String> loadShownotes() {
- return () -> {
- if (item == null) {
- item = DBReader.getFeedItem(
- itemID);
- }
- if (item.getContentEncoded() == null || item.getDescription() == null) {
- DBReader.loadExtraInformationOfFeedItem(
- item);
-
- }
- return (item.getContentEncoded() != null) ? item.getContentEncoded() : item.getDescription();
- };
- }
-
- public static final Parcelable.Creator<FeedMedia> CREATOR = new Parcelable.Creator<FeedMedia>() {
- public FeedMedia createFromParcel(Parcel in) {
- final long id = in.readLong();
- final long itemID = in.readLong();
- FeedMedia result = new FeedMedia(id, null, in.readInt(), in.readInt(), in.readLong(), in.readString(), in.readString(),
- in.readString(), in.readByte() != 0, new Date(in.readLong()), in.readInt(), in.readLong());
- result.itemID = itemID;
- return result;
- }
-
- public FeedMedia[] newArray(int size) {
- return new FeedMedia[size];
- }
- };
-
- @Override
- public String getImageLocation() {
- if (hasEmbeddedPicture()) {
- return getLocalMediaUrl();
- } else if(item != null) {
- return item.getImageLocation();
- } else {
- return null;
- }
- }
-
- public void setHasEmbeddedPicture(Boolean hasEmbeddedPicture) {
- this.hasEmbeddedPicture = hasEmbeddedPicture;
- }
-
- @Override
- public void setDownloaded(boolean downloaded) {
- super.setDownloaded(downloaded);
- if(item != null && downloaded) {
- item.setPlayed(false);
- }
- }
-
- @Override
- public void setFile_url(String file_url) {
- super.setFile_url(file_url);
- }
-
- public void checkEmbeddedPicture() {
- if (!localFileAvailable()) {
- hasEmbeddedPicture = Boolean.FALSE;
- return;
- }
- MediaMetadataRetriever mmr = new MediaMetadataRetriever();
- try {
- mmr.setDataSource(getLocalMediaUrl());
- byte[] image = mmr.getEmbeddedPicture();
- if(image != null) {
- hasEmbeddedPicture = Boolean.TRUE;
- } else {
- hasEmbeddedPicture = Boolean.FALSE;
- }
- } catch (Exception e) {
- e.printStackTrace();
- hasEmbeddedPicture = Boolean.FALSE;
- }
- }
-
-// @Override
-// public boolean equals(Object o) {
-// if (o instanceof RemoteMedia) {
-// return o.equals(this);
-// }
-// return super.equals(o);
-// }
-}
diff --git a/core/src/free/java/de/danoeh/antennapod/core/feed/FeedMediaFlavorHelper.java b/core/src/free/java/de/danoeh/antennapod/core/feed/FeedMediaFlavorHelper.java
new file mode 100644
index 000000000..7595d9d0b
--- /dev/null
+++ b/core/src/free/java/de/danoeh/antennapod/core/feed/FeedMediaFlavorHelper.java
@@ -0,0 +1,10 @@
+package de.danoeh.antennapod.core.feed;
+
+/**
+ * Implements methods for FeedMedia that are flavor dependent.
+ */
+public class FeedMediaFlavorHelper {
+ static boolean instanceOfRemoteMedia(Object o) {
+ return false;
+ }
+}
diff --git a/core/src/free/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java b/core/src/free/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java
new file mode 100644
index 000000000..6bc3ed7c5
--- /dev/null
+++ b/core/src/free/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java
@@ -0,0 +1,44 @@
+package de.danoeh.antennapod.core.service.playback;
+
+import android.content.Context;
+import android.support.annotation.StringRes;
+
+/**
+ * Class intended to work along PlaybackService and provide support for different flavors.
+ */
+public class PlaybackServiceFlavorHelper {
+
+ private PlaybackService.FlavorHelperCallback callback;
+
+ PlaybackServiceFlavorHelper(Context context, PlaybackService.FlavorHelperCallback callback) {
+ this.callback = callback;
+ }
+
+ void initializeMediaPlayer(Context context) {
+ callback.setMediaPlayer(new LocalPSMP(context, callback.getMediaPlayerCallback()));
+ }
+
+ void removeCastConsumer() {
+ // no-op
+ }
+
+ boolean castDisconnect(boolean castDisconnect) {
+ return false;
+ }
+
+ boolean onMediaPlayerInfo(Context context, int code, @StringRes int resourceId) {
+ return false;
+ }
+
+ void registerWifiBroadcastReceiver() {
+ // no-op
+ }
+
+ void unregisterWifiBroadcastReceiver() {
+ // no-op
+ }
+
+ boolean onSharedPreference(String key) {
+ return false;
+ }
+}
diff --git a/core/src/free/java/de/danoeh/antennapod/core/service/playback/RemotePSMP.java b/core/src/free/java/de/danoeh/antennapod/core/service/playback/RemotePSMP.java
deleted file mode 100644
index abf787ce8..000000000
--- a/core/src/free/java/de/danoeh/antennapod/core/service/playback/RemotePSMP.java
+++ /dev/null
@@ -1,592 +0,0 @@
-//package de.danoeh.antennapod.core.service.playback;
-//
-//import android.content.Context;
-//import android.media.MediaPlayer;
-//import android.support.annotation.NonNull;
-//import android.util.Log;
-//import android.util.Pair;
-//import android.view.SurfaceHolder;
-//
-//import com.google.android.gms.cast.Cast;
-//import com.google.android.gms.cast.CastStatusCodes;
-//import com.google.android.gms.cast.MediaInfo;
-//import com.google.android.gms.cast.MediaStatus;
-//import com.google.android.libraries.cast.companionlibrary.cast.exceptions.CastException;
-//import com.google.android.libraries.cast.companionlibrary.cast.exceptions.NoConnectionException;
-//import com.google.android.libraries.cast.companionlibrary.cast.exceptions.TransientNetworkDisconnectionException;
-//
-//import java.util.concurrent.atomic.AtomicBoolean;
-//
-//import de.danoeh.antennapod.core.R;
-//import de.danoeh.antennapod.core.cast.CastConsumer;
-//import de.danoeh.antennapod.core.cast.CastManager;
-//import de.danoeh.antennapod.core.cast.CastUtils;
-//import de.danoeh.antennapod.core.cast.DefaultCastConsumer;
-//import de.danoeh.antennapod.core.cast.RemoteMedia;
-//import de.danoeh.antennapod.core.feed.FeedMedia;
-//import de.danoeh.antennapod.core.feed.MediaType;
-//import de.danoeh.antennapod.core.util.RewindAfterPauseUtils;
-//import de.danoeh.antennapod.core.util.playback.Playable;
-//
-///**
-// * Implementation of PlaybackServiceMediaPlayer suitable for remote playback on Cast Devices.
-// */
-//public class RemotePSMP extends PlaybackServiceMediaPlayer {
-//
-// public static final String TAG = "RemotePSMP";
-//
-// public static final int CAST_ERROR = 3001;
-//
-// public static final int CAST_ERROR_PRIORITY_HIGH = 3005;
-//
-// private final CastManager castMgr;
-//
-// private volatile Playable media;
-// private volatile MediaInfo remoteMedia;
-// private volatile MediaType mediaType;
-//
-// private final AtomicBoolean isBuffering;
-//
-// private final AtomicBoolean startWhenPrepared;
-//
-// public RemotePSMP(@NonNull Context context, @NonNull PSMPCallback callback) {
-// super(context, callback);
-//
-// castMgr = CastManager.getInstance();
-// media = null;
-// mediaType = null;
-// startWhenPrepared = new AtomicBoolean(false);
-// isBuffering = new AtomicBoolean(false);
-//
-// try {
-// if (castMgr.isConnected() && castMgr.isRemoteMediaLoaded()) {
-// // updates the state, but does not start playing new media if it was going to
-// onRemoteMediaPlayerStatusUpdated(
-// ((p, playNextEpisode, wasSkipped, switchingPlayers) ->
-// this.callback.endPlayback(p, false, wasSkipped, switchingPlayers)));
-// }
-// } catch (TransientNetworkDisconnectionException | NoConnectionException e) {
-// Log.e(TAG, "Unable to do initial check for loaded media", e);
-// }
-//
-// castMgr.addCastConsumer(castConsumer);
-// //TODO
-// }
-//
-// private CastConsumer castConsumer = new DefaultCastConsumer() {
-// @Override
-// public void onRemoteMediaPlayerMetadataUpdated() {
-// RemotePSMP.this.onRemoteMediaPlayerStatusUpdated(callback::endPlayback);
-// }
-//
-// @Override
-// public void onRemoteMediaPlayerStatusUpdated() {
-// RemotePSMP.this.onRemoteMediaPlayerStatusUpdated(callback::endPlayback);
-// }
-//
-// @Override
-// public void onMediaLoadResult(int statusCode) {
-// if (playerStatus == PlayerStatus.PREPARING) {
-// if (statusCode == CastStatusCodes.SUCCESS) {
-// setPlayerStatus(PlayerStatus.PREPARED, media);
-// if (media.getDuration() == 0) {
-// Log.d(TAG, "Setting duration of media");
-// try {
-// media.setDuration((int) castMgr.getMediaDuration());
-// } catch (TransientNetworkDisconnectionException | NoConnectionException e) {
-// Log.e(TAG, "Unable to get remote media's duration");
-// }
-// }
-// } else if (statusCode != CastStatusCodes.REPLACED){
-// Log.d(TAG, "Remote media failed to load");
-// setPlayerStatus(PlayerStatus.INITIALIZED, media);
-// }
-// } else {
-// Log.d(TAG, "onMediaLoadResult called, but Player Status wasn't in preparing state, so we ignore the result");
-// }
-// }
-//
-// @Override
-// public void onApplicationStatusChanged(String appStatus) {
-// if (playerStatus != PlayerStatus.PLAYING) {
-// Log.d(TAG, "onApplicationStatusChanged, but no media was playing");
-// return;
-// }
-// boolean playbackEnded = false;
-// try {
-// int standbyState = castMgr.getApplicationStandbyState();
-// Log.d(TAG, "standbyState: " + standbyState);
-// playbackEnded = standbyState == Cast.STANDBY_STATE_YES;
-// } catch (IllegalStateException e) {
-// Log.d(TAG, "unable to get standbyState on onApplicationStatusChanged()");
-// }
-// if (playbackEnded) {
-// setPlayerStatus(PlayerStatus.INDETERMINATE, media);
-// callback.endPlayback(media, true, false, false);
-// }
-// }
-//
-// @Override
-// public void onFailed(int resourceId, int statusCode) {
-// callback.onMediaPlayerInfo(CAST_ERROR, resourceId);
-// }
-// };
-//
-// private void setBuffering(boolean buffering) {
-// if (buffering && isBuffering.compareAndSet(false, true)) {
-// callback.onMediaPlayerInfo(MediaPlayer.MEDIA_INFO_BUFFERING_START, 0);
-// } else if (!buffering && isBuffering.compareAndSet(true, false)) {
-// callback.onMediaPlayerInfo(MediaPlayer.MEDIA_INFO_BUFFERING_END, 0);
-// }
-// }
-//
-// private Playable localVersion(MediaInfo info){
-// if (info == null) {
-// return null;
-// }
-// if (CastUtils.matches(info, media)) {
-// return media;
-// }
-// return CastUtils.getPlayable(info, true);
-// }
-//
-// private MediaInfo remoteVersion(Playable playable) {
-// if (playable == null) {
-// return null;
-// }
-// if (CastUtils.matches(remoteMedia, playable)) {
-// return remoteMedia;
-// }
-// if (playable instanceof FeedMedia) {
-// return CastUtils.convertFromFeedMedia((FeedMedia) playable);
-// }
-// if (playable instanceof RemoteMedia) {
-// return ((RemoteMedia) playable).extractMediaInfo();
-// }
-// return null;
-// }
-//
-// private void onRemoteMediaPlayerStatusUpdated(@NonNull EndPlaybackCall endPlaybackCall) {
-// MediaStatus status = castMgr.getMediaStatus();
-// if (status == null) {
-// Log.d(TAG, "Received null MediaStatus");
-// //setBuffering(false);
-// //setPlayerStatus(PlayerStatus.INDETERMINATE, null);
-// return;
-// } else {
-// Log.d(TAG, "Received remote status/media update. New state=" + status.getPlayerState());
-// }
-// Playable currentMedia = localVersion(status.getMediaInfo());
-// boolean updateUI = currentMedia != media;
-// if (currentMedia != null) {
-// long position = status.getStreamPosition();
-// if (position > 0 && currentMedia.getPosition() == 0) {
-// currentMedia.setPosition((int) position);
-// }
-// }
-// int state = status.getPlayerState();
-// setBuffering(state == MediaStatus.PLAYER_STATE_BUFFERING);
-// switch (state) {
-// case MediaStatus.PLAYER_STATE_PLAYING:
-// setPlayerStatus(PlayerStatus.PLAYING, currentMedia);
-// break;
-// case MediaStatus.PLAYER_STATE_PAUSED:
-// setPlayerStatus(PlayerStatus.PAUSED, currentMedia);
-// break;
-// case MediaStatus.PLAYER_STATE_BUFFERING:
-// setPlayerStatus(playerStatus, currentMedia);
-// break;
-// case MediaStatus.PLAYER_STATE_IDLE:
-// int reason = status.getIdleReason();
-// switch (reason) {
-// case MediaStatus.IDLE_REASON_CANCELED:
-// // check if we're already loading something else
-// if (!updateUI || media == null) {
-// setPlayerStatus(PlayerStatus.STOPPED, currentMedia);
-// } else {
-// updateUI = false;
-// }
-// break;
-// case MediaStatus.IDLE_REASON_INTERRUPTED:
-// // check if we're already loading something else
-// if (!updateUI || media == null) {
-// setPlayerStatus(PlayerStatus.PREPARING, currentMedia);
-// } else {
-// updateUI = false;
-// }
-// break;
-// case MediaStatus.IDLE_REASON_NONE:
-// setPlayerStatus(PlayerStatus.INITIALIZED, currentMedia);
-// break;
-// case MediaStatus.IDLE_REASON_FINISHED:
-// boolean playing = playerStatus == PlayerStatus.PLAYING;
-// setPlayerStatus(PlayerStatus.INDETERMINATE, currentMedia);
-// endPlaybackCall.endPlayback(currentMedia,playing, false, false);
-// // endPlayback already updates the UI, so no need to trigger it ourselves
-// updateUI = false;
-// break;
-// case MediaStatus.IDLE_REASON_ERROR:
-// Log.w(TAG, "Got an error status from the Chromecast. Skipping, if possible, to the next episode...");
-// setPlayerStatus(PlayerStatus.INDETERMINATE, currentMedia);
-// callback.onMediaPlayerInfo(CAST_ERROR_PRIORITY_HIGH,
-// R.string.cast_failed_media_error_skipping);
-// endPlaybackCall.endPlayback(currentMedia, startWhenPrepared.get(), true, false);
-// // endPlayback already updates the UI, so no need to trigger it ourselves
-// updateUI = false;
-// }
-// break;
-// case MediaStatus.PLAYER_STATE_UNKNOWN:
-// //is this right?
-// setPlayerStatus(PlayerStatus.INDETERMINATE, currentMedia);
-// break;
-// default:
-// Log.e(TAG, "Remote media state undetermined!");
-// setPlayerStatus(PlayerStatus.INDETERMINATE, currentMedia);
-// }
-// if (updateUI) {
-// callback.onMediaChanged(true);
-// }
-// }
-//
-// @Override
-// public void playMediaObject(@NonNull final Playable playable, final boolean stream, final boolean startWhenPrepared, final boolean prepareImmediately) {
-// Log.d(TAG, "playMediaObject() called");
-// playMediaObject(playable, false, stream, startWhenPrepared, prepareImmediately);
-// }
-//
-// /**
-// * Internal implementation of playMediaObject. This method has an additional parameter that allows the caller to force a media player reset even if
-// * the given playable parameter is the same object as the currently playing media.
-// *
-// * @see #playMediaObject(de.danoeh.antennapod.core.util.playback.Playable, boolean, boolean, boolean)
-// */
-// private void playMediaObject(@NonNull final Playable playable, final boolean forceReset, final boolean stream, final boolean startWhenPrepared, final boolean prepareImmediately) {
-// if (!CastUtils.isCastable(playable)) {
-// Log.d(TAG, "media provided is not compatible with cast device");
-// callback.onMediaPlayerInfo(CAST_ERROR_PRIORITY_HIGH, R.string.cast_not_castable);
-// try {
-// playable.loadMetadata();
-// } catch (Playable.PlayableException e) {
-// Log.e(TAG, "Unable to load metadata of playable", e);
-// }
-// callback.endPlayback(playable, startWhenPrepared, true, false);
-// return;
-// }
-//
-// if (media != null) {
-// if (!forceReset && media.getIdentifier().equals(playable.getIdentifier())
-// && playerStatus == PlayerStatus.PLAYING) {
-// // episode is already playing -> ignore method call
-// Log.d(TAG, "Method call to playMediaObject was ignored: media file already playing.");
-// return;
-// } else {
-// // set temporarily to pause in order to update list with current position
-// try {
-// if (castMgr.isRemoteMediaPlaying()) {
-// setPlayerStatus(PlayerStatus.PAUSED, media);
-// }
-// } catch (TransientNetworkDisconnectionException | NoConnectionException e) {
-// Log.e(TAG, "Unable to determine whether media was playing, falling back to stored player status", e);
-// // this might end up just being pointless if we need to query the remote device for the position
-// if (playerStatus == PlayerStatus.PLAYING) {
-// setPlayerStatus(PlayerStatus.PAUSED, media);
-// }
-// }
-// smartMarkAsPlayed(media);
-//
-//
-// setPlayerStatus(PlayerStatus.INDETERMINATE, null);
-// }
-// }
-//
-// this.media = playable;
-// remoteMedia = remoteVersion(playable);
-// //this.stream = stream;
-// this.mediaType = media.getMediaType();
-// this.startWhenPrepared.set(startWhenPrepared);
-// setPlayerStatus(PlayerStatus.INITIALIZING, media);
-// try {
-// media.loadMetadata();
-// callback.onMediaChanged(true);
-// setPlayerStatus(PlayerStatus.INITIALIZED, media);
-// if (prepareImmediately) {
-// prepare();
-// }
-// } catch (Playable.PlayableException e) {
-// Log.e(TAG, "Error while loading media metadata", e);
-// setPlayerStatus(PlayerStatus.STOPPED, null);
-// }
-// }
-//
-// @Override
-// public void resume() {
-// try {
-// // TODO see comment on prepare()
-// // setVolume(UserPreferences.getLeftVolume(), UserPreferences.getRightVolume());
-// if (playerStatus == PlayerStatus.PREPARED && media.getPosition() > 0) {
-// int newPosition = RewindAfterPauseUtils.calculatePositionWithRewind(
-// media.getPosition(),
-// media.getLastPlayedTime());
-// castMgr.play(newPosition);
-// }
-// castMgr.play();
-// } catch (CastException | TransientNetworkDisconnectionException | NoConnectionException e) {
-// Log.e(TAG, "Unable to resume remote playback", e);
-// }
-// }
-//
-// @Override
-// public void pause(boolean abandonFocus, boolean reinit) {
-// try {
-// if (castMgr.isRemoteMediaPlaying()) {
-// castMgr.pause();
-// }
-// } catch (CastException | TransientNetworkDisconnectionException | NoConnectionException e) {
-// Log.e(TAG, "Unable to pause", e);
-// }
-// }
-//
-// @Override
-// public void prepare() {
-// if (playerStatus == PlayerStatus.INITIALIZED) {
-// Log.d(TAG, "Preparing media player");
-// setPlayerStatus(PlayerStatus.PREPARING, media);
-// try {
-// int position = media.getPosition();
-// if (position > 0) {
-// position = RewindAfterPauseUtils.calculatePositionWithRewind(
-// position,
-// media.getLastPlayedTime());
-// }
-// // TODO We're not supporting user set stream volume yet, as we need to make a UI
-// // that doesn't allow changing playback speed or have different values for left/right
-// //setVolume(UserPreferences.getLeftVolume(), UserPreferences.getRightVolume());
-// castMgr.loadMedia(remoteMedia, startWhenPrepared.get(), position);
-// } catch (TransientNetworkDisconnectionException | NoConnectionException e) {
-// Log.e(TAG, "Error loading media", e);
-// setPlayerStatus(PlayerStatus.INITIALIZED, media);
-// }
-// }
-// }
-//
-// @Override
-// public void reinit() {
-// Log.d(TAG, "reinit() called");
-// if (media != null) {
-// playMediaObject(media, true, false, startWhenPrepared.get(), false);
-// } else {
-// Log.d(TAG, "Call to reinit was ignored: media was null");
-// }
-// }
-//
-// @Override
-// public void seekTo(int t) {
-// //TODO check other seek implementations and see if there's no issue with sending too many seek commands to the remote media player
-// try {
-// if (castMgr.isRemoteMediaLoaded()) {
-// setPlayerStatus(PlayerStatus.SEEKING, media);
-// castMgr.seek(t);
-// } else if (media != null && playerStatus == PlayerStatus.INITIALIZED){
-// media.setPosition(t);
-// startWhenPrepared.set(false);
-// prepare();
-// }
-// } catch (TransientNetworkDisconnectionException | NoConnectionException e) {
-// Log.e(TAG, "Unable to seek", e);
-// }
-// }
-//
-// @Override
-// public void seekDelta(int d) {
-// int position = getPosition();
-// if (position != INVALID_TIME) {
-// seekTo(position + d);
-// } else {
-// Log.e(TAG, "getPosition() returned INVALID_TIME in seekDelta");
-// }
-// }
-//
-// @Override
-// public int getDuration() {
-// int retVal = INVALID_TIME;
-// boolean prepared;
-// try {
-// prepared = castMgr.isRemoteMediaLoaded();
-// } catch (TransientNetworkDisconnectionException | NoConnectionException e) {
-// Log.e(TAG, "Unable to check if remote media is loaded", e);
-// prepared = playerStatus.isAtLeast(PlayerStatus.PREPARED);
-// }
-// if (prepared) {
-// try {
-// retVal = (int) castMgr.getMediaDuration();
-// } catch (TransientNetworkDisconnectionException | NoConnectionException e) {
-// Log.e(TAG, "Unable to determine remote media's duration", e);
-// }
-// }
-// if(retVal == INVALID_TIME && media != null && media.getDuration() > 0) {
-// retVal = media.getDuration();
-// }
-// Log.d(TAG, "getDuration() -> " + retVal);
-// return retVal;
-// }
-//
-// @Override
-// public int getPosition() {
-// int retVal = INVALID_TIME;
-// boolean prepared;
-// try {
-// prepared = castMgr.isRemoteMediaLoaded();
-// } catch (TransientNetworkDisconnectionException | NoConnectionException e) {
-// Log.e(TAG, "Unable to check if remote media is loaded", e);
-// prepared = playerStatus.isAtLeast(PlayerStatus.PREPARED);
-// }
-// if (prepared) {
-// try {
-// retVal = (int) castMgr.getCurrentMediaPosition();
-// } catch (TransientNetworkDisconnectionException | NoConnectionException e) {
-// Log.e(TAG, "Unable to determine remote media's position", e);
-// }
-// }
-// if(retVal <= 0 && media != null && media.getPosition() >= 0) {
-// retVal = media.getPosition();
-// }
-// Log.d(TAG, "getPosition() -> " + retVal);
-// return retVal;
-// }
-//
-// @Override
-// public boolean isStartWhenPrepared() {
-// return startWhenPrepared.get();
-// }
-//
-// @Override
-// public void setStartWhenPrepared(boolean startWhenPrepared) {
-// this.startWhenPrepared.set(startWhenPrepared);
-// }
-//
-// //TODO I believe some parts of the code make the same decision skipping this check, so that
-// //should be changed as well
-// @Override
-// public boolean canSetSpeed() {
-// return false;
-// }
-//
-// @Override
-// public void setSpeed(float speed) {
-// throw new UnsupportedOperationException("Setting playback speed unsupported for Remote Playback");
-// }
-//
-// @Override
-// public float getPlaybackSpeed() {
-// return 1;
-// }
-//
-// @Override
-// public void setVolume(float volumeLeft, float volumeRight) {
-// Log.d(TAG, "Setting the Stream volume on Remote Media Player");
-// double volume = (volumeLeft+volumeRight)/2;
-// if (volume > 1.0) {
-// volume = 1.0;
-// }
-// if (volume < 0.0) {
-// volume = 0.0;
-// }
-// try {
-// castMgr.setStreamVolume(volume);
-// } catch (TransientNetworkDisconnectionException | NoConnectionException | CastException e) {
-// Log.e(TAG, "Unable to set the volume", e);
-// }
-// }
-//
-// @Override
-// public boolean canDownmix() {
-// return false;
-// }
-//
-// @Override
-// public void setDownmix(boolean enable) {
-// throw new UnsupportedOperationException("Setting downmix unsupported in Remote Media Player");
-// }
-//
-// @Override
-// public MediaType getCurrentMediaType() {
-// return mediaType;
-// }
-//
-// @Override
-// public boolean isStreaming() {
-// return true;
-// }
-//
-// @Override
-// public void shutdown() {
-// castMgr.removeCastConsumer(castConsumer);
-// }
-//
-// @Override
-// public void shutdownQuietly() {
-// shutdown();
-// }
-//
-// @Override
-// public void setVideoSurface(SurfaceHolder surface) {
-// throw new UnsupportedOperationException("Setting Video Surface unsupported in Remote Media Player");
-// }
-//
-// @Override
-// public void resetVideoSurface() {
-// Log.e(TAG, "Resetting Video Surface unsupported in Remote Media Player");
-// }
-//
-// @Override
-// public Pair<Integer, Integer> getVideoSize() {
-// return null;
-// }
-//
-// @Override
-// public Playable getPlayable() {
-// return media;
-// }
-//
-// @Override
-// protected void setPlayable(Playable playable) {
-// if (playable != media) {
-// media = playable;
-// remoteMedia = remoteVersion(playable);
-// }
-// }
-//
-// @Override
-// public void endPlayback(boolean wasSkipped, boolean switchingPlayers) {
-// Log.d(TAG, "endPlayback() called");
-// boolean isPlaying = playerStatus == PlayerStatus.PLAYING;
-// try {
-// isPlaying = castMgr.isRemoteMediaPlaying();
-// } catch (TransientNetworkDisconnectionException | NoConnectionException e) {
-// Log.e(TAG, "Could not determine if media is playing", e);
-// }
-// // TODO make sure we stop playback whenever there's no next episode.
-// if (playerStatus != PlayerStatus.INDETERMINATE) {
-// setPlayerStatus(PlayerStatus.INDETERMINATE, media);
-// }
-// callback.endPlayback(media, isPlaying, wasSkipped, switchingPlayers);
-// }
-//
-// @Override
-// public void stop() {
-// if (playerStatus == PlayerStatus.INDETERMINATE) {
-// setPlayerStatus(PlayerStatus.STOPPED, null);
-// } else {
-// Log.d(TAG, "Ignored call to stop: Current player state is: " + playerStatus);
-// }
-// }
-//
-// @Override
-// protected boolean shouldLockWifi() {
-// return false;
-// }
-//
-// private interface EndPlaybackCall {
-// boolean endPlayback(Playable media, boolean playNextEpisode, boolean wasSkipped, boolean switchingPlayers);
-// }
-//}
diff --git a/core/src/free/res/values/strings.xml b/core/src/free/res/values/strings.xml
new file mode 100644
index 000000000..579b12e12
--- /dev/null
+++ b/core/src/free/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="pref_cast_message">@string/pref_cast_message_free_flavor</string>
+</resources>
diff --git a/core/src/play/java/de/danoeh/antennapod/core/feed/FeedMedia.java b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java
index 068669af9..b8b154ff0 100644
--- a/core/src/play/java/de/danoeh/antennapod/core/feed/FeedMedia.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/feed/FeedMedia.java
@@ -12,7 +12,6 @@ import java.util.Date;
import java.util.List;
import java.util.concurrent.Callable;
-import de.danoeh.antennapod.core.cast.RemoteMedia;
import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
import de.danoeh.antennapod.core.preferences.UserPreferences;
import de.danoeh.antennapod.core.storage.DBReader;
@@ -560,7 +559,7 @@ public class FeedMedia extends FeedFile implements Playable {
@Override
public boolean equals(Object o) {
- if (o instanceof RemoteMedia) {
+ if (FeedMediaFlavorHelper.instanceOfRemoteMedia(o)) {
return o.equals(this);
}
return super.equals(o);
diff --git a/core/src/free/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
index 01b803d80..76c960607 100644
--- a/core/src/free/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
@@ -15,14 +15,11 @@ import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.AudioManager;
import android.media.MediaPlayer;
-import android.net.NetworkInfo;
-import android.net.wifi.WifiManager;
import android.os.Binder;
import android.os.Build;
import android.os.IBinder;
import android.os.Vibrator;
import android.preference.PreferenceManager;
-import android.support.annotation.NonNull;
import android.support.annotation.StringRes;
import android.support.v4.media.MediaMetadataCompat;
import android.support.v4.media.session.MediaSessionCompat;
@@ -61,6 +58,7 @@ import de.danoeh.antennapod.core.storage.DBWriter;
import de.danoeh.antennapod.core.util.IntList;
import de.danoeh.antennapod.core.util.QueueAccess;
import de.danoeh.antennapod.core.util.flattr.FlattrUtils;
+import de.danoeh.antennapod.core.util.playback.ExternalMedia;
import de.danoeh.antennapod.core.util.playback.Playable;
/**
@@ -175,12 +173,6 @@ public class PlaybackService extends Service {
public static final int INVALID_TIME = -1;
/**
- * Time in seconds during which the CastManager will try to reconnect to the Cast Device after
- * the Wifi Connection is regained.
- */
- private static final int RECONNECTION_ATTEMPT_PERIOD_S = 15;
-
- /**
* Is true if service is running.
*/
public static boolean isRunning = false;
@@ -196,21 +188,13 @@ public class PlaybackService extends Service {
* Is true if a Cast Device is connected to the service.
*/
private static volatile boolean isCasting = false;
- /**
- * Stores the state of the cast playback just before it disconnects.
- */
- private volatile PlaybackServiceMediaPlayer.PSMPInfo infoBeforeCastDisconnection;
-
- private boolean wifiConnectivity = true;
- private BroadcastReceiver wifiBroadcastReceiver;
private static final int NOTIFICATION_ID = 1;
private PlaybackServiceMediaPlayer mediaPlayer;
private PlaybackServiceTaskManager taskManager;
+ private PlaybackServiceFlavorHelper flavorHelper;
-// private CastManager castManager;
-// private MediaRouter mediaRouter;
/**
* Only used for Lollipop notifications.
*/
@@ -284,7 +268,7 @@ public class PlaybackService extends Service {
ACTION_RESUME_PLAY_CURRENT_EPISODE));
taskManager = new PlaybackServiceTaskManager(this, taskManagerCallback);
-// mediaRouter = MediaRouter.getInstance(getApplicationContext());
+ flavorHelper = new PlaybackServiceFlavorHelper(PlaybackService.this, flavorHelperCallback);
PreferenceManager.getDefaultSharedPreferences(this)
.registerOnSharedPreferenceChangeListener(prefListener);
@@ -308,18 +292,7 @@ public class PlaybackService extends Service {
npe.printStackTrace();
}
-// castManager = CastManager.getInstance();
-// castManager.addCastConsumer(castConsumer);
-// isCasting = castManager.isConnected();
-// if (isCasting) {
-// if (UserPreferences.isCastEnabled()) {
-// onCastAppConnected(false);
-// } else {
-// castManager.disconnect();
-// }
-// } else {
- mediaPlayer = new LocalPSMP(this, mediaPlayerCallback);
-// }
+ flavorHelper.initializeMediaPlayer(PlaybackService.this);
mediaSession.setActive(true);
}
@@ -346,8 +319,8 @@ public class PlaybackService extends Service {
unregisterReceiver(skipCurrentEpisodeReceiver);
unregisterReceiver(pausePlayCurrentEpisodeReceiver);
unregisterReceiver(pauseResumeCurrentEpisodeReceiver);
-// castManager.removeCastConsumer(castConsumer);
- unregisterWifiBroadcastReceiver();
+ flavorHelper.removeCastConsumer();
+ flavorHelper.unregisterWifiBroadcastReceiver();
mediaPlayer.shutdown();
taskManager.shutdown();
}
@@ -381,9 +354,7 @@ public class PlaybackService extends Service {
Log.d(TAG, "Received media button event");
handleKeycode(keycode, intent.getIntExtra(MediaButtonReceiver.EXTRA_SOURCE,
InputDevice.SOURCE_CLASS_NONE));
-// } else if (castDisconnect) {
-// castManager.disconnect();
- } else {
+ } else if (!flavorHelper.castDisconnect(castDisconnect)) {
started = true;
boolean stream = intent.getBooleanExtra(EXTRA_SHOULD_STREAM,
true);
@@ -391,9 +362,7 @@ public class PlaybackService extends Service {
boolean prepareImmediately = intent.getBooleanExtra(EXTRA_PREPARE_IMMEDIATELY, false);
sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD, 0);
//If the user asks to play External Media, the casting session, if on, should end.
-// if (playable instanceof ExternalMedia) {
-// castManager.disconnect();
-// }
+ flavorHelper.castDisconnect(playable instanceof ExternalMedia);
mediaPlayer.playMediaObject(playable, stream, startWhenPrepared, prepareImmediately);
}
}
@@ -649,14 +618,8 @@ public class PlaybackService extends Service {
case MediaPlayer.MEDIA_INFO_BUFFERING_END:
sendNotificationBroadcast(NOTIFICATION_TYPE_BUFFER_END, 0);
return true;
-// case RemotePSMP.CAST_ERROR:
-// sendNotificationBroadcast(NOTIFICATION_TYPE_SHOW_TOAST, resourceId);
-// return true;
-// case RemotePSMP.CAST_ERROR_PRIORITY_HIGH:
-// Toast.makeText(PlaybackService.this, resourceId, Toast.LENGTH_SHORT).show();
-// return true;
default:
- return false;
+ return flavorHelper.onMediaPlayerInfo(PlaybackService.this, code, resourceId);
}
}
@@ -1527,67 +1490,6 @@ public class PlaybackService extends Service {
}
}
-// private CastConsumer castConsumer = new DefaultCastConsumer() {
-// @Override
-// public void onApplicationConnected(ApplicationMetadata appMetadata, String sessionId, boolean wasLaunched) {
-// PlaybackService.this.onCastAppConnected(wasLaunched);
-// }
-//
-// @Override
-// public void onDisconnectionReason(int reason) {
-// Log.d(TAG, "onDisconnectionReason() with code " + reason);
-// // This is our final chance to update the underlying stream position
-// // In onDisconnected(), the underlying CastPlayback#mVideoCastConsumer
-// // is disconnected and hence we update our local value of stream position
-// // to the latest position.
-// if (mediaPlayer != null) {
-// saveCurrentPosition(false, 0);
-// infoBeforeCastDisconnection = mediaPlayer.getPSMPInfo();
-// if (reason != BaseCastManager.DISCONNECT_REASON_EXPLICIT &&
-// infoBeforeCastDisconnection.playerStatus == PlayerStatus.PLAYING) {
-// // If it's NOT based on user action, we shouldn't automatically resume local playback
-// infoBeforeCastDisconnection.playerStatus = PlayerStatus.PAUSED;
-// }
-// }
-// }
-//
-// @Override
-// public void onDisconnected() {
-// Log.d(TAG, "onDisconnected()");
-// isCasting = false;
-// PlaybackServiceMediaPlayer.PSMPInfo info = infoBeforeCastDisconnection;
-// infoBeforeCastDisconnection = null;
-// if (info == null && mediaPlayer != null) {
-// info = mediaPlayer.getPSMPInfo();
-// }
-// if (info == null) {
-// info = new PlaybackServiceMediaPlayer.PSMPInfo(PlayerStatus.STOPPED, null);
-// }
-// switchMediaPlayer(new LocalPSMP(PlaybackService.this, mediaPlayerCallback),
-// info, true);
-// if (info.playable != null) {
-// sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD,
-// info.playable.getMediaType() == MediaType.AUDIO ? EXTRA_CODE_AUDIO : EXTRA_CODE_VIDEO);
-// } else {
-// Log.d(TAG, "Cast session disconnected, but no current media");
-// sendNotificationBroadcast(NOTIFICATION_TYPE_PLAYBACK_END, 0);
-// }
-// // hardware volume buttons control the local device volume
-// mediaRouter.setMediaSessionCompat(null);
-// unregisterWifiBroadcastReceiver();
-// PlayerStatus status = info.playerStatus;
-// if ((status == PlayerStatus.PLAYING ||
-// status == PlayerStatus.SEEKING ||
-// status == PlayerStatus.PREPARING ||
-// UserPreferences.isPersistNotify()) &&
-// android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
-// setupNotification(info);
-// } else if (!UserPreferences.isPersistNotify()){
-// stopForeground(true);
-// }
-// }
-// };
-
private final MediaSessionCompat.Callback sessionCallback = new MediaSessionCompat.Callback() {
private static final String TAG = "MediaSessionCompat";
@@ -1673,101 +1575,90 @@ public class PlaybackService extends Service {
}
};
-// private void onCastAppConnected(boolean wasLaunched) {
-// Log.d(TAG, "A cast device application was " + (wasLaunched ? "launched" : "joined"));
-// isCasting = true;
-// PlaybackServiceMediaPlayer.PSMPInfo info = null;
-// if (mediaPlayer != null) {
-// info = mediaPlayer.getPSMPInfo();
-// if (info.playerStatus == PlayerStatus.PLAYING) {
-// // could be pause, but this way we make sure the new player will get the correct position,
-// // since pause runs asynchronously and we could be directing the new player to play even before
-// // the old player gives us back the position.
-// saveCurrentPosition(false, 0);
-// }
-// }
-// if (info == null) {
-// info = new PlaybackServiceMediaPlayer.PSMPInfo(PlayerStatus.STOPPED, null);
-// }
-// sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD, EXTRA_CODE_CAST);
-// switchMediaPlayer(new RemotePSMP(PlaybackService.this, mediaPlayerCallback),
-// info,
-// wasLaunched);
-// // hardware volume buttons control the remote device volume
-// mediaRouter.setMediaSessionCompat(mediaSession);
-// registerWifiBroadcastReceiver();
-// setupNotification(info);
-// }
-
- private void switchMediaPlayer(@NonNull PlaybackServiceMediaPlayer newPlayer,
- @NonNull PlaybackServiceMediaPlayer.PSMPInfo info,
- boolean wasLaunched) {
- if (mediaPlayer != null) {
- mediaPlayer.endPlayback(true, true);
- mediaPlayer.shutdownQuietly();
- }
- mediaPlayer = newPlayer;
- Log.d(TAG, "switched to " + mediaPlayer.getClass().getSimpleName());
- if (!wasLaunched) {
- PlaybackServiceMediaPlayer.PSMPInfo candidate = mediaPlayer.getPSMPInfo();
- if (candidate.playable != null &&
- candidate.playerStatus.isAtLeast(PlayerStatus.PREPARING)) {
- // do not automatically send new media to cast device
- info.playable = null;
- }
+ private SharedPreferences.OnSharedPreferenceChangeListener prefListener =
+ (sharedPreferences, key) -> {
+ if (UserPreferences.PREF_LOCKSCREEN_BACKGROUND.equals(key)) {
+ updateMediaSessionMetadata(getPlayable());
+ } else {
+ flavorHelper.onSharedPreference(key);
+ }
+ };
+
+ interface FlavorHelperCallback {
+ PlaybackServiceMediaPlayer.PSMPCallback getMediaPlayerCallback();
+ void setMediaPlayer(PlaybackServiceMediaPlayer mediaPlayer);
+ PlaybackServiceMediaPlayer getMediaPlayer();
+ void setIsCasting(boolean isCasting);
+ void sendNotificationBroadcast(int type, int code);
+ void saveCurrentPosition(boolean updatePlayedDuration, int deltaPlayedDuration);
+ void setupNotification(boolean connected, PlaybackServiceMediaPlayer.PSMPInfo info);
+ MediaSessionCompat getMediaSession();
+ Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter);
+ void unregisterReceiver(BroadcastReceiver receiver);
+ }
+
+ private FlavorHelperCallback flavorHelperCallback = new FlavorHelperCallback() {
+ @Override
+ public PlaybackServiceMediaPlayer.PSMPCallback getMediaPlayerCallback() {
+ return PlaybackService.this.mediaPlayerCallback;
}
- if (info.playable != null) {
- mediaPlayer.playMediaObject(info.playable,
- !info.playable.localFileAvailable(),
- info.playerStatus == PlayerStatus.PLAYING,
- info.playerStatus.isAtLeast(PlayerStatus.PREPARING));
+
+ @Override
+ public void setMediaPlayer(PlaybackServiceMediaPlayer mediaPlayer) {
+ PlaybackService.this.mediaPlayer = mediaPlayer;
}
- }
- private void registerWifiBroadcastReceiver() {
- if (wifiBroadcastReceiver != null) {
- return;
+ @Override
+ public PlaybackServiceMediaPlayer getMediaPlayer() {
+ return PlaybackService.this.mediaPlayer;
}
- wifiBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
- NetworkInfo info = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
- boolean isConnected = info.isConnected();
- //apparently this method gets called twice when a change happens, but one run is enough.
- if (isConnected && !wifiConnectivity) {
- wifiConnectivity = true;
-// castManager.startCastDiscovery();
-// castManager.reconnectSessionIfPossible(RECONNECTION_ATTEMPT_PERIOD_S, NetworkUtils.getWifiSsid());
- } else {
- wifiConnectivity = isConnected;
- }
+
+ @Override
+ public void setIsCasting(boolean isCasting) {
+ PlaybackService.isCasting = isCasting;
+ }
+
+ @Override
+ public void sendNotificationBroadcast(int type, int code) {
+ PlaybackService.this.sendNotificationBroadcast(type, code);
+ }
+
+ @Override
+ public void saveCurrentPosition(boolean updatePlayedDuration, int deltaPlayedDuration) {
+ PlaybackService.this.saveCurrentPosition(updatePlayedDuration, deltaPlayedDuration);
+ }
+
+ @Override
+ public void setupNotification(boolean connected, PlaybackServiceMediaPlayer.PSMPInfo info) {
+ if (connected) {
+ PlaybackService.this.setupNotification(info);
+ } else {
+ PlayerStatus status = info.playerStatus;
+ if ((status == PlayerStatus.PLAYING ||
+ status == PlayerStatus.SEEKING ||
+ status == PlayerStatus.PREPARING ||
+ UserPreferences.isPersistNotify()) &&
+ android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ PlaybackService.this.setupNotification(info);
+ } else if (!UserPreferences.isPersistNotify()){
+ PlaybackService.this.stopForeground(true);
}
}
- };
- registerReceiver(wifiBroadcastReceiver,
- new IntentFilter(WifiManager.NETWORK_STATE_CHANGED_ACTION));
- }
+ }
- private void unregisterWifiBroadcastReceiver() {
- if (wifiBroadcastReceiver != null) {
- unregisterReceiver(wifiBroadcastReceiver);
- wifiBroadcastReceiver = null;
+ @Override
+ public MediaSessionCompat getMediaSession() {
+ return PlaybackService.this.mediaSession;
}
- }
- private SharedPreferences.OnSharedPreferenceChangeListener prefListener =
- (sharedPreferences, key) -> {
-// if (UserPreferences.PREF_CAST_ENABLED.equals(key)) {
-// if (!UserPreferences.isCastEnabled()) {
-// if (castManager.isConnecting() || castManager.isConnected()) {
-// Log.d(TAG, "Disconnecting cast device due to a change in user preferences");
-// castManager.disconnect();
-// }
-// }
-// } else
- if (UserPreferences.PREF_LOCKSCREEN_BACKGROUND.equals(key)) {
- updateMediaSessionMetadata(getPlayable());
- }
+ @Override
+ public Intent registerReceiver(BroadcastReceiver receiver, IntentFilter filter) {
+ return PlaybackService.this.registerReceiver(receiver, filter);
+ }
+
+ @Override
+ public void unregisterReceiver(BroadcastReceiver receiver) {
+ PlaybackService.this.unregisterReceiver(receiver);
+ }
};
}
diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/Flavors.java b/core/src/main/java/de/danoeh/antennapod/core/util/Flavors.java
new file mode 100644
index 000000000..5feb232e7
--- /dev/null
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/Flavors.java
@@ -0,0 +1,24 @@
+package de.danoeh.antennapod.core.util;
+
+import de.danoeh.antennapod.core.BuildConfig;
+
+/**
+ * Helper class to handle the different build flavors.
+ */
+public enum Flavors {
+ FREE,
+ PLAY,
+ UNKNOWN;
+
+ public static final Flavors FLAVOR;
+
+ static {
+ if (BuildConfig.FLAVOR.equals("free")) {
+ FLAVOR = FREE;
+ } else if (BuildConfig.FLAVOR.equals("play")) {
+ FLAVOR = PLAY;
+ } else {
+ FLAVOR = UNKNOWN;
+ }
+ }
+}
diff --git a/core/src/free/java/de/danoeh/antennapod/core/util/playback/Playable.java b/core/src/main/java/de/danoeh/antennapod/core/util/playback/Playable.java
index bc22e063c..6459d86ed 100644
--- a/core/src/free/java/de/danoeh/antennapod/core/util/playback/Playable.java
+++ b/core/src/main/java/de/danoeh/antennapod/core/util/playback/Playable.java
@@ -183,9 +183,6 @@ public interface Playable extends Parcelable,
case ExternalMedia.PLAYABLE_TYPE_EXTERNAL_MEDIA:
result = createExternalMediaInstance(pref);
break;
-// case RemoteMedia.PLAYABLE_TYPE_REMOTE_MEDIA:
-// result = createRemoteMediaInstance(pref);
-// break;
}
if (result == null) {
Log.e(TAG, "Could not restore Playable object from preferences");
@@ -214,12 +211,6 @@ public interface Playable extends Parcelable,
}
return result;
}
-
- private static Playable createRemoteMediaInstance(SharedPreferences pref) {
- //TODO there's probably no point in restoring RemoteMedia from preferences, because we
- //only care about it while it's playing on the cast device.
- return null;
- }
}
class PlayableException extends Exception {
diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml
index f4ed79937..d775c6fe5 100644
--- a/core/src/main/res/values/strings.xml
+++ b/core/src/main/res/values/strings.xml
@@ -403,7 +403,8 @@
<string name="pref_known_issues">Known issues</string>
<string name="pref_no_browser_found">No web browser found.</string>
<string name="pref_cast_title">Chromecast support</string>
- <string name="pref_cast_message">Enable support for remote media playback on Cast devices (such as Chromecast, Audio Speakers or Android TV)</string>
+ <string name="pref_cast_message_play_flavor">Enable support for remote media playback on Cast devices (such as Chromecast, Audio Speakers or Android TV)</string>
+ <string name="pref_cast_message_free_flavor">Chromecast requires third party proprietary libraries that are disabled in this version of AntennaPod</string>
<!-- Auto-Flattr dialog -->
<string name="auto_flattr_enable">Enable automatic flattring</string>
diff --git a/core/src/play/java/de/danoeh/antennapod/core/feed/FeedMediaFlavorHelper.java b/core/src/play/java/de/danoeh/antennapod/core/feed/FeedMediaFlavorHelper.java
new file mode 100644
index 000000000..b89904af9
--- /dev/null
+++ b/core/src/play/java/de/danoeh/antennapod/core/feed/FeedMediaFlavorHelper.java
@@ -0,0 +1,12 @@
+package de.danoeh.antennapod.core.feed;
+
+import de.danoeh.antennapod.core.cast.RemoteMedia;
+
+/**
+ * Implements methods for FeedMedia that are flavor dependent.
+ */
+public class FeedMediaFlavorHelper {
+ static boolean instanceOfRemoteMedia(Object o) {
+ return o instanceof RemoteMedia;
+ }
+}
diff --git a/core/src/play/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java b/core/src/play/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
deleted file mode 100644
index e2d63a385..000000000
--- a/core/src/play/java/de/danoeh/antennapod/core/service/playback/PlaybackService.java
+++ /dev/null
@@ -1,1780 +0,0 @@
-package de.danoeh.antennapod.core.service.playback;
-
-import android.app.Notification;
-import android.app.NotificationManager;
-import android.app.PendingIntent;
-import android.app.Service;
-import android.bluetooth.BluetoothA2dp;
-import android.content.BroadcastReceiver;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
-import android.content.SharedPreferences;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.media.AudioManager;
-import android.media.MediaPlayer;
-import android.net.NetworkInfo;
-import android.net.wifi.WifiManager;
-import android.os.Binder;
-import android.os.Build;
-import android.os.IBinder;
-import android.os.Vibrator;
-import android.preference.PreferenceManager;
-import android.support.annotation.NonNull;
-import android.support.annotation.StringRes;
-import android.support.v4.media.MediaMetadataCompat;
-import android.support.v4.media.session.MediaSessionCompat;
-import android.support.v4.media.session.PlaybackStateCompat;
-import android.support.v7.app.NotificationCompat;
-import android.support.v7.media.MediaRouter;
-import android.text.TextUtils;
-import android.util.Log;
-import android.util.Pair;
-import android.view.Display;
-import android.view.InputDevice;
-import android.view.KeyEvent;
-import android.view.SurfaceHolder;
-import android.view.WindowManager;
-import android.widget.Toast;
-
-import com.bumptech.glide.Glide;
-import com.bumptech.glide.request.target.Target;
-import com.google.android.gms.cast.ApplicationMetadata;
-import com.google.android.libraries.cast.companionlibrary.cast.BaseCastManager;
-
-import java.util.List;
-
-import de.danoeh.antennapod.core.ClientConfig;
-import de.danoeh.antennapod.core.R;
-import de.danoeh.antennapod.core.cast.CastConsumer;
-import de.danoeh.antennapod.core.cast.CastManager;
-import de.danoeh.antennapod.core.cast.DefaultCastConsumer;
-import de.danoeh.antennapod.core.feed.Chapter;
-import de.danoeh.antennapod.core.feed.FeedItem;
-import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.feed.MediaType;
-import de.danoeh.antennapod.core.glide.ApGlideSettings;
-import de.danoeh.antennapod.core.gpoddernet.model.GpodnetEpisodeAction;
-import de.danoeh.antennapod.core.gpoddernet.model.GpodnetEpisodeAction.Action;
-import de.danoeh.antennapod.core.preferences.GpodnetPreferences;
-import de.danoeh.antennapod.core.preferences.PlaybackPreferences;
-import de.danoeh.antennapod.core.preferences.UserPreferences;
-import de.danoeh.antennapod.core.receiver.MediaButtonReceiver;
-import de.danoeh.antennapod.core.storage.DBTasks;
-import de.danoeh.antennapod.core.storage.DBWriter;
-import de.danoeh.antennapod.core.util.IntList;
-import de.danoeh.antennapod.core.util.NetworkUtils;
-import de.danoeh.antennapod.core.util.QueueAccess;
-import de.danoeh.antennapod.core.util.flattr.FlattrUtils;
-import de.danoeh.antennapod.core.util.playback.ExternalMedia;
-import de.danoeh.antennapod.core.util.playback.Playable;
-
-/**
- * Controls the MediaPlayer that plays a FeedMedia-file
- */
-public class PlaybackService extends Service {
- public static final String FORCE_WIDGET_UPDATE = "de.danoeh.antennapod.FORCE_WIDGET_UPDATE";
- public static final String STOP_WIDGET_UPDATE = "de.danoeh.antennapod.STOP_WIDGET_UPDATE";
- /**
- * Logging tag
- */
- private static final String TAG = "PlaybackService";
-
- /**
- * Parcelable of type Playable.
- */
- public static final String EXTRA_PLAYABLE = "PlaybackService.PlayableExtra";
- /**
- * True if cast session should disconnect.
- */
- public static final String EXTRA_CAST_DISCONNECT = "extra.de.danoeh.antennapod.core.service.castDisconnect";
- /**
- * True if media should be streamed.
- */
- public static final String EXTRA_SHOULD_STREAM = "extra.de.danoeh.antennapod.core.service.shouldStream";
- /**
- * True if playback should be started immediately after media has been
- * prepared.
- */
- public static final String EXTRA_START_WHEN_PREPARED = "extra.de.danoeh.antennapod.core.service.startWhenPrepared";
-
- public static final String EXTRA_PREPARE_IMMEDIATELY = "extra.de.danoeh.antennapod.core.service.prepareImmediately";
-
- public static final String ACTION_PLAYER_STATUS_CHANGED = "action.de.danoeh.antennapod.core.service.playerStatusChanged";
- public static final String EXTRA_NEW_PLAYER_STATUS = "extra.de.danoeh.antennapod.service.playerStatusChanged.newStatus";
- private static final String AVRCP_ACTION_PLAYER_STATUS_CHANGED = "com.android.music.playstatechanged";
- private static final String AVRCP_ACTION_META_CHANGED = "com.android.music.metachanged";
-
- public static final String ACTION_PLAYER_NOTIFICATION = "action.de.danoeh.antennapod.core.service.playerNotification";
- public static final String EXTRA_NOTIFICATION_CODE = "extra.de.danoeh.antennapod.core.service.notificationCode";
- public static final String EXTRA_NOTIFICATION_TYPE = "extra.de.danoeh.antennapod.core.service.notificationType";
-
- /**
- * If the PlaybackService receives this action, it will stop playback and
- * try to shutdown.
- */
- public static final String ACTION_SHUTDOWN_PLAYBACK_SERVICE = "action.de.danoeh.antennapod.core.service.actionShutdownPlaybackService";
-
- /**
- * If the PlaybackService receives this action, it will end playback of the
- * current episode and load the next episode if there is one available.
- */
- public static final String ACTION_SKIP_CURRENT_EPISODE = "action.de.danoeh.antennapod.core.service.skipCurrentEpisode";
-
- /**
- * If the PlaybackService receives this action, it will pause playback.
- */
- public static final String ACTION_PAUSE_PLAY_CURRENT_EPISODE = "action.de.danoeh.antennapod.core.service.pausePlayCurrentEpisode";
-
-
- /**
- * If the PlaybackService receives this action, it will resume playback.
- */
- public static final String ACTION_RESUME_PLAY_CURRENT_EPISODE = "action.de.danoeh.antennapod.core.service.resumePlayCurrentEpisode";
-
-
- /**
- * Used in NOTIFICATION_TYPE_RELOAD.
- */
- public static final int EXTRA_CODE_AUDIO = 1;
- public static final int EXTRA_CODE_VIDEO = 2;
- public static final int EXTRA_CODE_CAST = 3;
-
- public static final int NOTIFICATION_TYPE_ERROR = 0;
- public static final int NOTIFICATION_TYPE_INFO = 1;
- public static final int NOTIFICATION_TYPE_BUFFER_UPDATE = 2;
-
- /**
- * Receivers of this intent should update their information about the curently playing media
- */
- public static final int NOTIFICATION_TYPE_RELOAD = 3;
- /**
- * The state of the sleeptimer changed.
- */
- public static final int NOTIFICATION_TYPE_SLEEPTIMER_UPDATE = 4;
- public static final int NOTIFICATION_TYPE_BUFFER_START = 5;
- public static final int NOTIFICATION_TYPE_BUFFER_END = 6;
- /**
- * No more episodes are going to be played.
- */
- public static final int NOTIFICATION_TYPE_PLAYBACK_END = 7;
-
- /**
- * Playback speed has changed
- */
- public static final int NOTIFICATION_TYPE_PLAYBACK_SPEED_CHANGE = 8;
-
- /**
- * Ability to set the playback speed has changed
- */
- public static final int NOTIFICATION_TYPE_SET_SPEED_ABILITY_CHANGED = 9;
-
- /**
- * Send a message to the user (with provided String resource id)
- */
- public static final int NOTIFICATION_TYPE_SHOW_TOAST = 10;
-
- /**
- * Returned by getPositionSafe() or getDurationSafe() if the playbackService
- * is in an invalid state.
- */
- public static final int INVALID_TIME = -1;
-
- /**
- * Time in seconds during which the CastManager will try to reconnect to the Cast Device after
- * the Wifi Connection is regained.
- */
- private static final int RECONNECTION_ATTEMPT_PERIOD_S = 15;
-
- /**
- * Is true if service is running.
- */
- public static boolean isRunning = false;
- /**
- * Is true if service has received a valid start command.
- */
- public static boolean started = false;
- /**
- * Is true if the service was running, but paused due to headphone disconnect
- */
- public static boolean transientPause = false;
- /**
- * Is true if a Cast Device is connected to the service.
- */
- private static volatile boolean isCasting = false;
- /**
- * Stores the state of the cast playback just before it disconnects.
- */
- private volatile PlaybackServiceMediaPlayer.PSMPInfo infoBeforeCastDisconnection;
-
- private boolean wifiConnectivity = true;
- private BroadcastReceiver wifiBroadcastReceiver;
-
- private static final int NOTIFICATION_ID = 1;
-
- private PlaybackServiceMediaPlayer mediaPlayer;
- private PlaybackServiceTaskManager taskManager;
-
- private CastManager castManager;
- private MediaRouter mediaRouter;
- /**
- * Only used for Lollipop notifications.
- */
- private MediaSessionCompat mediaSession;
-
- private int startPosition;
-
- private static volatile MediaType currentMediaType = MediaType.UNKNOWN;
-
- private final IBinder mBinder = new LocalBinder();
-
- public class LocalBinder extends Binder {
- public PlaybackService getService() {
- return PlaybackService.this;
- }
- }
-
- @Override
- public boolean onUnbind(Intent intent) {
- Log.d(TAG, "Received onUnbind event");
- return super.onUnbind(intent);
- }
-
- /**
- * Returns an intent which starts an audio- or videoplayer, depending on the
- * type of media that is being played. If the playbackservice is not
- * running, the type of the last played media will be looked up.
- */
- public static Intent getPlayerActivityIntent(Context context) {
- if (isRunning) {
- return ClientConfig.playbackServiceCallbacks.getPlayerActivityIntent(context, currentMediaType, isCasting);
- } else {
- if (PlaybackPreferences.getCurrentEpisodeIsVideo()) {
- return ClientConfig.playbackServiceCallbacks.getPlayerActivityIntent(context, MediaType.VIDEO, isCasting);
- } else {
- return ClientConfig.playbackServiceCallbacks.getPlayerActivityIntent(context, MediaType.AUDIO, isCasting);
- }
- }
- }
-
- /**
- * Same as getPlayerActivityIntent(context), but here the type of activity
- * depends on the FeedMedia that is provided as an argument.
- */
- public static Intent getPlayerActivityIntent(Context context, Playable media) {
- MediaType mt = media.getMediaType();
- return ClientConfig.playbackServiceCallbacks.getPlayerActivityIntent(context, mt, isCasting);
- }
-
- @Override
- public void onCreate() {
- super.onCreate();
- Log.d(TAG, "Service created.");
- isRunning = true;
-
- registerReceiver(headsetDisconnected, new IntentFilter(
- Intent.ACTION_HEADSET_PLUG));
- registerReceiver(shutdownReceiver, new IntentFilter(
- ACTION_SHUTDOWN_PLAYBACK_SERVICE));
- if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- registerReceiver(bluetoothStateUpdated, new IntentFilter(
- BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED));
- }
- registerReceiver(audioBecomingNoisy, new IntentFilter(
- AudioManager.ACTION_AUDIO_BECOMING_NOISY));
- registerReceiver(skipCurrentEpisodeReceiver, new IntentFilter(
- ACTION_SKIP_CURRENT_EPISODE));
- registerReceiver(pausePlayCurrentEpisodeReceiver, new IntentFilter(
- ACTION_PAUSE_PLAY_CURRENT_EPISODE));
- registerReceiver(pauseResumeCurrentEpisodeReceiver, new IntentFilter(
- ACTION_RESUME_PLAY_CURRENT_EPISODE));
- taskManager = new PlaybackServiceTaskManager(this, taskManagerCallback);
-
- mediaRouter = MediaRouter.getInstance(getApplicationContext());
- PreferenceManager.getDefaultSharedPreferences(this)
- .registerOnSharedPreferenceChangeListener(prefListener);
-
- ComponentName eventReceiver = new ComponentName(getApplicationContext(),
- MediaButtonReceiver.class);
- Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
- mediaButtonIntent.setComponent(eventReceiver);
- PendingIntent buttonReceiverIntent = PendingIntent.getBroadcast(this, 0, mediaButtonIntent, PendingIntent.FLAG_UPDATE_CURRENT);
-
- mediaSession = new MediaSessionCompat(getApplicationContext(), TAG, eventReceiver, buttonReceiverIntent);
-
- try {
- mediaSession.setCallback(sessionCallback);
- mediaSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
- } catch (NullPointerException npe) {
- // on some devices (Huawei) setting active can cause a NullPointerException
- // even with correct use of the api.
- // See http://stackoverflow.com/questions/31556679/android-huawei-mediassessioncompat
- // and https://plus.google.com/+IanLake/posts/YgdTkKFxz7d
- Log.e(TAG, "NullPointerException while setting up MediaSession");
- npe.printStackTrace();
- }
-
- castManager = CastManager.getInstance();
- castManager.addCastConsumer(castConsumer);
- isCasting = castManager.isConnected();
- if (isCasting) {
- if (UserPreferences.isCastEnabled()) {
- onCastAppConnected(false);
- } else {
- castManager.disconnect();
- }
- } else {
- mediaPlayer = new LocalPSMP(this, mediaPlayerCallback);
- }
-
- mediaSession.setActive(true);
- }
-
- @Override
- public void onDestroy() {
- super.onDestroy();
- Log.d(TAG, "Service is about to be destroyed");
- isRunning = false;
- started = false;
- currentMediaType = MediaType.UNKNOWN;
-
- PreferenceManager.getDefaultSharedPreferences(this)
- .unregisterOnSharedPreferenceChangeListener(prefListener);
- if (mediaSession != null) {
- mediaSession.release();
- }
- unregisterReceiver(headsetDisconnected);
- unregisterReceiver(shutdownReceiver);
- if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- unregisterReceiver(bluetoothStateUpdated);
- }
- unregisterReceiver(audioBecomingNoisy);
- unregisterReceiver(skipCurrentEpisodeReceiver);
- unregisterReceiver(pausePlayCurrentEpisodeReceiver);
- unregisterReceiver(pauseResumeCurrentEpisodeReceiver);
- castManager.removeCastConsumer(castConsumer);
- unregisterWifiBroadcastReceiver();
- mediaPlayer.shutdown();
- taskManager.shutdown();
- }
-
- @Override
- public IBinder onBind(Intent intent) {
- Log.d(TAG, "Received onBind event");
- return mBinder;
- }
-
- @Override
- public int onStartCommand(Intent intent, int flags, int startId) {
- super.onStartCommand(intent, flags, startId);
-
- Log.d(TAG, "OnStartCommand called");
- final int keycode = intent.getIntExtra(MediaButtonReceiver.EXTRA_KEYCODE, -1);
- final boolean castDisconnect = intent.getBooleanExtra(EXTRA_CAST_DISCONNECT, false);
- final Playable playable = intent.getParcelableExtra(EXTRA_PLAYABLE);
- if (keycode == -1 && playable == null && !castDisconnect) {
- Log.e(TAG, "PlaybackService was started with no arguments");
- stopSelf();
- return Service.START_REDELIVER_INTENT;
- }
-
- if ((flags & Service.START_FLAG_REDELIVERY) != 0) {
- Log.d(TAG, "onStartCommand is a redelivered intent, calling stopForeground now.");
- stopForeground(true);
- } else {
-
- if (keycode != -1) {
- Log.d(TAG, "Received media button event");
- handleKeycode(keycode, intent.getIntExtra(MediaButtonReceiver.EXTRA_SOURCE,
- InputDevice.SOURCE_CLASS_NONE));
- } else if (castDisconnect) {
- castManager.disconnect();
- } else {
- started = true;
- boolean stream = intent.getBooleanExtra(EXTRA_SHOULD_STREAM,
- true);
- boolean startWhenPrepared = intent.getBooleanExtra(EXTRA_START_WHEN_PREPARED, false);
- boolean prepareImmediately = intent.getBooleanExtra(EXTRA_PREPARE_IMMEDIATELY, false);
- sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD, 0);
- //If the user asks to play External Media, the casting session, if on, should end.
- if (playable instanceof ExternalMedia) {
- castManager.disconnect();
- }
- mediaPlayer.playMediaObject(playable, stream, startWhenPrepared, prepareImmediately);
- }
- }
-
- return Service.START_REDELIVER_INTENT;
- }
-
- /**
- * Handles media button events
- */
- private void handleKeycode(int keycode, int source) {
- Log.d(TAG, "Handling keycode: " + keycode);
- final PlaybackServiceMediaPlayer.PSMPInfo info = mediaPlayer.getPSMPInfo();
- final PlayerStatus status = info.playerStatus;
- switch (keycode) {
- case KeyEvent.KEYCODE_HEADSETHOOK:
- case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
- if (status == PlayerStatus.PLAYING) {
- mediaPlayer.pause(!UserPreferences.isPersistNotify(), true);
- } else if (status == PlayerStatus.PAUSED || status == PlayerStatus.PREPARED) {
- mediaPlayer.resume();
- } else if (status == PlayerStatus.PREPARING) {
- mediaPlayer.setStartWhenPrepared(!mediaPlayer.isStartWhenPrepared());
- } else if (status == PlayerStatus.INITIALIZED) {
- mediaPlayer.setStartWhenPrepared(true);
- mediaPlayer.prepare();
- }
- break;
- case KeyEvent.KEYCODE_MEDIA_PLAY:
- if (status == PlayerStatus.PAUSED || status == PlayerStatus.PREPARED) {
- mediaPlayer.resume();
- } else if (status == PlayerStatus.INITIALIZED) {
- mediaPlayer.setStartWhenPrepared(true);
- mediaPlayer.prepare();
- }
- break;
- case KeyEvent.KEYCODE_MEDIA_PAUSE:
- if (status == PlayerStatus.PLAYING) {
- mediaPlayer.pause(!UserPreferences.isPersistNotify(), true);
- }
-
- break;
- case KeyEvent.KEYCODE_MEDIA_NEXT:
- if(source == InputDevice.SOURCE_CLASS_NONE ||
- UserPreferences.shouldHardwareButtonSkip()) {
- // assume the skip command comes from a notification or the lockscreen
- // a >| skip button should actually skip
- mediaPlayer.endPlayback(true, false);
- } else {
- // assume skip command comes from a (bluetooth) media button
- // user actually wants to fast-forward
- seekDelta(UserPreferences.getFastFowardSecs() * 1000);
- }
- break;
- case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
- mediaPlayer.seekDelta(UserPreferences.getFastFowardSecs() * 1000);
- break;
- case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
- case KeyEvent.KEYCODE_MEDIA_REWIND:
- mediaPlayer.seekDelta(-UserPreferences.getRewindSecs() * 1000);
- break;
- case KeyEvent.KEYCODE_MEDIA_STOP:
- if (status == PlayerStatus.PLAYING) {
- mediaPlayer.pause(true, true);
- started = false;
- }
-
- stopForeground(true); // gets rid of persistent notification
- break;
- default:
- Log.d(TAG, "Unhandled key code: " + keycode);
- if (info.playable != null && info.playerStatus == PlayerStatus.PLAYING) { // only notify the user about an unknown key event if it is actually doing something
- String message = String.format(getResources().getString(R.string.unknown_media_key), keycode);
- Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
- }
- break;
- }
- }
-
- /**
- * Called by a mediaplayer Activity as soon as it has prepared its
- * mediaplayer.
- */
- public void setVideoSurface(SurfaceHolder sh) {
- Log.d(TAG, "Setting display");
- mediaPlayer.setVideoSurface(sh);
- }
-
- /**
- * Called when the surface holder of the mediaplayer has to be changed.
- */
- private void resetVideoSurface() {
- taskManager.cancelPositionSaver();
- mediaPlayer.resetVideoSurface();
- }
-
- public void notifyVideoSurfaceAbandoned() {
- stopForeground(!UserPreferences.isPersistNotify());
- mediaPlayer.resetVideoSurface();
- }
-
- private final PlaybackServiceTaskManager.PSTMCallback taskManagerCallback = new PlaybackServiceTaskManager.PSTMCallback() {
- @Override
- public void positionSaverTick() {
- saveCurrentPosition(true, PlaybackServiceTaskManager.POSITION_SAVER_WAITING_INTERVAL);
- }
-
- @Override
- public void onSleepTimerAlmostExpired() {
- float leftVolume = 0.1f * UserPreferences.getLeftVolume();
- float rightVolume = 0.1f * UserPreferences.getRightVolume();
- mediaPlayer.setVolume(leftVolume, rightVolume);
- }
-
- @Override
- public void onSleepTimerExpired() {
- mediaPlayer.pause(true, true);
- float leftVolume = UserPreferences.getLeftVolume();
- float rightVolume = UserPreferences.getRightVolume();
- mediaPlayer.setVolume(leftVolume, rightVolume);
- sendNotificationBroadcast(NOTIFICATION_TYPE_SLEEPTIMER_UPDATE, 0);
- }
-
- @Override
- public void onSleepTimerReset() {
- float leftVolume = UserPreferences.getLeftVolume();
- float rightVolume = UserPreferences.getRightVolume();
- mediaPlayer.setVolume(leftVolume, rightVolume);
- }
-
- @Override
- public void onWidgetUpdaterTick() {
- updateWidget();
- }
-
- @Override
- public void onChapterLoaded(Playable media) {
- sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD, 0);
- }
- };
-
- private final PlaybackServiceMediaPlayer.PSMPCallback mediaPlayerCallback = new PlaybackServiceMediaPlayer.PSMPCallback() {
- @Override
- public void statusChanged(PlaybackServiceMediaPlayer.PSMPInfo newInfo) {
- currentMediaType = mediaPlayer.getCurrentMediaType();
- updateMediaSession(newInfo.playerStatus);
- switch (newInfo.playerStatus) {
- case INITIALIZED:
- writePlaybackPreferences();
- break;
-
- case PREPARED:
- taskManager.startChapterLoader(newInfo.playable);
- break;
-
- case PAUSED:
- taskManager.cancelPositionSaver();
- saveCurrentPosition(false, 0);
- taskManager.cancelWidgetUpdater();
- if ((UserPreferences.isPersistNotify() || isCasting) &&
- android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
- // do not remove notification on pause based on user pref and whether android version supports expanded notifications
- // Change [Play] button to [Pause]
- setupNotification(newInfo);
- } else if (!UserPreferences.isPersistNotify() && !isCasting) {
- // remove notification on pause
- stopForeground(true);
- }
- writePlayerStatusPlaybackPreferences();
-
- final Playable playable = newInfo.playable;
-
- // Gpodder: send play action
- if(GpodnetPreferences.loggedIn() && playable instanceof FeedMedia) {
- FeedMedia media = (FeedMedia) playable;
- FeedItem item = media.getItem();
- GpodnetEpisodeAction action = new GpodnetEpisodeAction.Builder(item, Action.PLAY)
- .currentDeviceId()
- .currentTimestamp()
- .started(startPosition / 1000)
- .position(getCurrentPosition() / 1000)
- .total(getDuration() / 1000)
- .build();
- GpodnetPreferences.enqueueEpisodeAction(action);
- }
- break;
-
- case STOPPED:
- //setCurrentlyPlayingMedia(PlaybackPreferences.NO_MEDIA_PLAYING);
- //stopSelf();
- break;
-
- case PLAYING:
- Log.d(TAG, "Audiofocus successfully requested");
- Log.d(TAG, "Resuming/Starting playback");
-
- taskManager.startPositionSaver();
- taskManager.startWidgetUpdater();
- writePlayerStatusPlaybackPreferences();
- setupNotification(newInfo);
- started = true;
- startPosition = mediaPlayer.getPosition();
- break;
-
- case ERROR:
- writePlaybackPreferencesNoMediaPlaying();
- break;
-
- }
-
- Intent statusUpdate = new Intent(ACTION_PLAYER_STATUS_CHANGED);
- // statusUpdate.putExtra(EXTRA_NEW_PLAYER_STATUS, newInfo.playerStatus.ordinal());
- sendBroadcast(statusUpdate);
- updateWidget();
- bluetoothNotifyChange(newInfo, AVRCP_ACTION_PLAYER_STATUS_CHANGED);
- bluetoothNotifyChange(newInfo, AVRCP_ACTION_META_CHANGED);
- }
-
- @Override
- public void shouldStop() {
- stopSelf();
- }
-
- @Override
- public void playbackSpeedChanged(float s) {
- sendNotificationBroadcast(NOTIFICATION_TYPE_PLAYBACK_SPEED_CHANGE, 0);
- }
-
- public void setSpeedAbilityChanged() {
- sendNotificationBroadcast(NOTIFICATION_TYPE_SET_SPEED_ABILITY_CHANGED, 0);
- }
-
- @Override
- public void onBufferingUpdate(int percent) {
- sendNotificationBroadcast(NOTIFICATION_TYPE_BUFFER_UPDATE, percent);
- }
-
- @Override
- public void onMediaChanged(boolean reloadUI) {
- Log.d(TAG, "reloadUI callback reached");
- if (reloadUI) {
- sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD, 0);
- }
- PlaybackService.this.updateMediaSessionMetadata(getPlayable());
- }
-
- @Override
- public boolean onMediaPlayerInfo(int code, @StringRes int resourceId) {
- switch (code) {
- case MediaPlayer.MEDIA_INFO_BUFFERING_START:
- sendNotificationBroadcast(NOTIFICATION_TYPE_BUFFER_START, 0);
- return true;
- case MediaPlayer.MEDIA_INFO_BUFFERING_END:
- sendNotificationBroadcast(NOTIFICATION_TYPE_BUFFER_END, 0);
- return true;
- case RemotePSMP.CAST_ERROR:
- sendNotificationBroadcast(NOTIFICATION_TYPE_SHOW_TOAST, resourceId);
- return true;
- case RemotePSMP.CAST_ERROR_PRIORITY_HIGH:
- Toast.makeText(PlaybackService.this, resourceId, Toast.LENGTH_SHORT).show();
- return true;
- default:
- return false;
- }
- }
-
- @Override
- public boolean onMediaPlayerError(Object inObj, int what, int extra) {
- final String TAG = "PlaybackSvc.onErrorLtsn";
- Log.w(TAG, "An error has occured: " + what + " " + extra);
- if (mediaPlayer.getPlayerStatus() == PlayerStatus.PLAYING) {
- mediaPlayer.pause(true, false);
- }
- sendNotificationBroadcast(NOTIFICATION_TYPE_ERROR, what);
- writePlaybackPreferencesNoMediaPlaying();
- stopSelf();
- return true;
- }
-
- @Override
- public boolean endPlayback(Playable media, boolean playNextEpisode, boolean wasSkipped, boolean switchingPlayers) {
- PlaybackService.this.endPlayback(media, playNextEpisode, wasSkipped, switchingPlayers);
- return true;
- }
- };
-
- private void endPlayback(final Playable playable, boolean playNextEpisode, boolean wasSkipped, boolean switchingPlayers) {
- Log.d(TAG, "Playback ended" + (switchingPlayers ? " from switching players": ""));
-
- if (playable == null) {
- Log.e(TAG, "Cannot end playback: media was null");
- return;
- }
-
- taskManager.cancelPositionSaver();
-
- boolean isInQueue = false;
- FeedItem nextItem = null;
-
- if (playable instanceof FeedMedia && ((FeedMedia) playable).getItem() != null) {
- FeedMedia media = (FeedMedia) playable;
- FeedItem item = media.getItem();
-
- if (!switchingPlayers) {
- try {
- final List<FeedItem> queue = taskManager.getQueue();
- isInQueue = QueueAccess.ItemListAccess(queue).contains(item.getId());
- nextItem = DBTasks.getQueueSuccessorOfItem(item.getId(), queue);
- } catch (InterruptedException e) {
- e.printStackTrace();
- // isInQueue remains false
- }
-
- boolean shouldKeep = wasSkipped && UserPreferences.shouldSkipKeepEpisode();
-
- if (!shouldKeep) {
- // only mark the item as played if we're not keeping it anyways
- DBWriter.markItemPlayed(item, FeedItem.PLAYED, true);
-
- if (isInQueue) {
- DBWriter.removeQueueItem(PlaybackService.this, item, true);
- }
-
- // Delete episode if enabled
- if (item.getFeed().getPreferences().getCurrentAutoDelete()) {
- DBWriter.deleteFeedMediaOfItem(PlaybackService.this, media.getId());
- Log.d(TAG, "Episode Deleted");
- }
- }
- }
-
-
- DBWriter.addItemToPlaybackHistory(media);
-
- // auto-flattr if enabled
- if (isAutoFlattrable(media) && UserPreferences.getAutoFlattrPlayedDurationThreshold() == 1.0f) {
- DBTasks.flattrItemIfLoggedIn(PlaybackService.this, item);
- }
-
- // gpodder play action
- if(GpodnetPreferences.loggedIn()) {
- GpodnetEpisodeAction action = new GpodnetEpisodeAction.Builder(item, Action.PLAY)
- .currentDeviceId()
- .currentTimestamp()
- .started(startPosition / 1000)
- .position(getDuration() / 1000)
- .total(getDuration() / 1000)
- .build();
- GpodnetPreferences.enqueueEpisodeAction(action);
- }
- }
-
- if (!switchingPlayers) {
- // Load next episode if previous episode was in the queue and if there
- // is an episode in the queue left.
- // Start playback immediately if continuous playback is enabled
- Playable nextMedia = null;
- boolean loadNextItem = ClientConfig.playbackServiceCallbacks.useQueue() &&
- isInQueue &&
- nextItem != null;
-
- playNextEpisode = playNextEpisode &&
- loadNextItem &&
- UserPreferences.isFollowQueue();
-
- if (loadNextItem) {
- Log.d(TAG, "Loading next item in queue");
- nextMedia = nextItem.getMedia();
- }
- final boolean prepareImmediately;
- final boolean startWhenPrepared;
- final boolean stream;
-
- if (playNextEpisode) {
- Log.d(TAG, "Playback of next episode will start immediately.");
- prepareImmediately = startWhenPrepared = true;
- } else {
- Log.d(TAG, "No more episodes available to play");
- prepareImmediately = startWhenPrepared = false;
- stopForeground(true);
- stopWidgetUpdater();
- }
-
- writePlaybackPreferencesNoMediaPlaying();
- if (nextMedia != null) {
- stream = !nextMedia.localFileAvailable();
- mediaPlayer.playMediaObject(nextMedia, stream, startWhenPrepared, prepareImmediately);
- sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD,
- isCasting ? EXTRA_CODE_CAST :
- (nextMedia.getMediaType() == MediaType.VIDEO) ? EXTRA_CODE_VIDEO : EXTRA_CODE_AUDIO);
- } else {
- sendNotificationBroadcast(NOTIFICATION_TYPE_PLAYBACK_END, 0);
- mediaPlayer.stop();
- //stopSelf();
- }
- }
- }
-
- public void setSleepTimer(long waitingTime, boolean shakeToReset, boolean vibrate) {
- Log.d(TAG, "Setting sleep timer to " + Long.toString(waitingTime) + " milliseconds");
- taskManager.setSleepTimer(waitingTime, shakeToReset, vibrate);
- sendNotificationBroadcast(NOTIFICATION_TYPE_SLEEPTIMER_UPDATE, 0);
- }
-
- public void disableSleepTimer() {
- taskManager.disableSleepTimer();
- sendNotificationBroadcast(NOTIFICATION_TYPE_SLEEPTIMER_UPDATE, 0);
- }
-
- private void writePlaybackPreferencesNoMediaPlaying() {
- SharedPreferences.Editor editor = PreferenceManager
- .getDefaultSharedPreferences(getApplicationContext()).edit();
- editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_MEDIA,
- PlaybackPreferences.NO_MEDIA_PLAYING);
- editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEED_ID,
- PlaybackPreferences.NO_MEDIA_PLAYING);
- editor.putLong(
- PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID,
- PlaybackPreferences.NO_MEDIA_PLAYING);
- editor.putInt(
- PlaybackPreferences.PREF_CURRENT_PLAYER_STATUS,
- PlaybackPreferences.PLAYER_STATUS_OTHER);
- editor.commit();
- }
-
- private int getCurrentPlayerStatusAsInt(PlayerStatus playerStatus) {
- int playerStatusAsInt;
- switch (playerStatus) {
- case PLAYING:
- playerStatusAsInt = PlaybackPreferences.PLAYER_STATUS_PLAYING;
- break;
- case PAUSED:
- playerStatusAsInt = PlaybackPreferences.PLAYER_STATUS_PAUSED;
- break;
- default:
- playerStatusAsInt = PlaybackPreferences.PLAYER_STATUS_OTHER;
- }
- return playerStatusAsInt;
- }
-
- private void writePlaybackPreferences() {
- Log.d(TAG, "Writing playback preferences");
-
- SharedPreferences.Editor editor = PreferenceManager
- .getDefaultSharedPreferences(getApplicationContext()).edit();
- PlaybackServiceMediaPlayer.PSMPInfo info = mediaPlayer.getPSMPInfo();
- MediaType mediaType = mediaPlayer.getCurrentMediaType();
- boolean stream = mediaPlayer.isStreaming();
- int playerStatus = getCurrentPlayerStatusAsInt(info.playerStatus);
-
- if (info.playable != null) {
- editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_MEDIA,
- info.playable.getPlayableType());
- editor.putBoolean(
- PlaybackPreferences.PREF_CURRENT_EPISODE_IS_STREAM,
- stream);
- editor.putBoolean(
- PlaybackPreferences.PREF_CURRENT_EPISODE_IS_VIDEO,
- mediaType == MediaType.VIDEO);
- if (info.playable instanceof FeedMedia) {
- FeedMedia fMedia = (FeedMedia) info.playable;
- editor.putLong(
- PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEED_ID,
- fMedia.getItem().getFeed().getId());
- editor.putLong(
- PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID,
- fMedia.getId());
- } else {
- editor.putLong(
- PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEED_ID,
- PlaybackPreferences.NO_MEDIA_PLAYING);
- editor.putLong(
- PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID,
- PlaybackPreferences.NO_MEDIA_PLAYING);
- }
- info.playable.writeToPreferences(editor);
- } else {
- editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_MEDIA,
- PlaybackPreferences.NO_MEDIA_PLAYING);
- editor.putLong(PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEED_ID,
- PlaybackPreferences.NO_MEDIA_PLAYING);
- editor.putLong(
- PlaybackPreferences.PREF_CURRENTLY_PLAYING_FEEDMEDIA_ID,
- PlaybackPreferences.NO_MEDIA_PLAYING);
- }
- editor.putInt(
- PlaybackPreferences.PREF_CURRENT_PLAYER_STATUS, playerStatus);
-
- editor.commit();
- }
-
- private void writePlayerStatusPlaybackPreferences() {
- Log.d(TAG, "Writing player status playback preferences");
-
- SharedPreferences.Editor editor = PreferenceManager
- .getDefaultSharedPreferences(getApplicationContext()).edit();
- int playerStatus = getCurrentPlayerStatusAsInt(mediaPlayer.getPlayerStatus());
-
- editor.putInt(
- PlaybackPreferences.PREF_CURRENT_PLAYER_STATUS, playerStatus);
-
- editor.commit();
- }
-
- /**
- * Send ACTION_PLAYER_STATUS_CHANGED without changing the status attribute.
- */
- private void postStatusUpdateIntent() {
- sendBroadcast(new Intent(ACTION_PLAYER_STATUS_CHANGED));
- }
-
- private void sendNotificationBroadcast(int type, int code) {
- Intent intent = new Intent(ACTION_PLAYER_NOTIFICATION);
- intent.putExtra(EXTRA_NOTIFICATION_TYPE, type);
- intent.putExtra(EXTRA_NOTIFICATION_CODE, code);
- sendBroadcast(intent);
- }
-
- /**
- * Updates the Media Session for the corresponding status.
- * @param playerStatus the current {@link PlayerStatus}
- */
- private void updateMediaSession(final PlayerStatus playerStatus) {
- PlaybackStateCompat.Builder sessionState = new PlaybackStateCompat.Builder();
-
- int state;
- if (playerStatus != null) {
- switch (playerStatus) {
- case PLAYING:
- state = PlaybackStateCompat.STATE_PLAYING;
- break;
- case PREPARED:
- case PAUSED:
- state = PlaybackStateCompat.STATE_PAUSED;
- break;
- case STOPPED:
- state = PlaybackStateCompat.STATE_STOPPED;
- break;
- case SEEKING:
- state = PlaybackStateCompat.STATE_FAST_FORWARDING;
- break;
- case PREPARING:
- case INITIALIZING:
- state = PlaybackStateCompat.STATE_CONNECTING;
- break;
- case INITIALIZED:
- case INDETERMINATE:
- state = PlaybackStateCompat.STATE_NONE;
- break;
- case ERROR:
- state = PlaybackStateCompat.STATE_ERROR;
- break;
- default:
- state = PlaybackStateCompat.STATE_NONE;
- break;
- }
- } else {
- state = PlaybackStateCompat.STATE_NONE;
- }
- sessionState.setState(state, mediaPlayer.getPosition(), mediaPlayer.getPlaybackSpeed());
- sessionState.setActions(PlaybackStateCompat.ACTION_PLAY_PAUSE
- | PlaybackStateCompat.ACTION_REWIND
- | PlaybackStateCompat.ACTION_FAST_FORWARD
- | PlaybackStateCompat.ACTION_SKIP_TO_NEXT);
- mediaSession.setPlaybackState(sessionState.build());
- }
-
- /**
- * Used by updateMediaSessionMetadata to load notification data in another thread.
- */
- private Thread mediaSessionSetupThread;
-
- private void updateMediaSessionMetadata(final Playable p) {
- if (p == null || mediaSession == null) {
- return;
- }
- if (mediaSessionSetupThread != null) {
- mediaSessionSetupThread.interrupt();
- }
-
- Runnable mediaSessionSetupTask = () -> {
- MediaMetadataCompat.Builder builder = new MediaMetadataCompat.Builder();
- builder.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, p.getFeedTitle());
- builder.putString(MediaMetadataCompat.METADATA_KEY_TITLE, p.getEpisodeTitle());
- builder.putLong(MediaMetadataCompat.METADATA_KEY_DURATION, p.getDuration());
- builder.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE, p.getEpisodeTitle());
- builder.putString(MediaMetadataCompat.METADATA_KEY_ALBUM, p.getFeedTitle());
-
- if (p.getImageLocation() != null && UserPreferences.setLockscreenBackground()) {
- builder.putString(MediaMetadataCompat.METADATA_KEY_ART_URI, p.getImageLocation().toString());
- try {
- if (isCasting) {
- Bitmap art = Glide.with(this)
- .load(p.getImageLocation())
- .asBitmap()
- .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
- .into(Target.SIZE_ORIGINAL, Target.SIZE_ORIGINAL)
- .get();
- builder.putBitmap(MediaMetadataCompat.METADATA_KEY_ART, art);
- } else {
- WindowManager wm = (WindowManager) getSystemService(Context.WINDOW_SERVICE);
- Display display = wm.getDefaultDisplay();
- Bitmap art = Glide.with(this)
- .load(p.getImageLocation())
- .asBitmap()
- .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
- .centerCrop()
- .into(display.getWidth(), display.getHeight())
- .get();
- builder.putBitmap(MediaMetadataCompat.METADATA_KEY_ART, art);
- }
- } catch (Throwable tr) {
- Log.e(TAG, Log.getStackTraceString(tr));
- }
- }
- if (!Thread.currentThread().isInterrupted() && started) {
- mediaSession.setMetadata(builder.build());
- }
- };
-
- mediaSessionSetupThread = new Thread(mediaSessionSetupTask);
- mediaSessionSetupThread.start();
- }
-
- /**
- * Used by setupNotification to load notification data in another thread.
- */
- private Thread notificationSetupThread;
-
- /**
- * Prepares notification and starts the service in the foreground.
- */
- private void setupNotification(final PlaybackServiceMediaPlayer.PSMPInfo info) {
- final PendingIntent pIntent = PendingIntent.getActivity(this, 0,
- PlaybackService.getPlayerActivityIntent(this),
- PendingIntent.FLAG_UPDATE_CURRENT);
-
- if (notificationSetupThread != null) {
- notificationSetupThread.interrupt();
- }
- Runnable notificationSetupTask = new Runnable() {
- Bitmap icon = null;
-
- @Override
- public void run() {
- Log.d(TAG, "Starting background work");
- if (android.os.Build.VERSION.SDK_INT >= 11) {
- if (info.playable != null) {
- int iconSize = getResources().getDimensionPixelSize(
- android.R.dimen.notification_large_icon_width);
- try {
- icon = Glide.with(PlaybackService.this)
- .load(info.playable.getImageLocation())
- .asBitmap()
- .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY)
- .centerCrop()
- .into(iconSize, iconSize)
- .get();
- } catch (Throwable tr) {
- Log.e(TAG, "Error loading the media icon for the notification", tr);
- }
- }
- }
- if (icon == null) {
- icon = BitmapFactory.decodeResource(getApplicationContext().getResources(),
- ClientConfig.playbackServiceCallbacks.getNotificationIconResource(getApplicationContext()));
- }
-
- if (mediaPlayer == null) {
- return;
- }
- PlayerStatus playerStatus = mediaPlayer.getPlayerStatus();
- final int smallIcon = ClientConfig.playbackServiceCallbacks.getNotificationIconResource(getApplicationContext());
-
- if (!Thread.currentThread().isInterrupted() && started && info.playable != null) {
- String contentText = info.playable.getEpisodeTitle();
- String contentTitle = info.playable.getFeedTitle();
- Notification notification;
-
- // Builder is v7, even if some not overwritten methods return its parent's v4 interface
- NotificationCompat.Builder notificationBuilder = (NotificationCompat.Builder) new NotificationCompat.Builder(
- PlaybackService.this)
- .setContentTitle(contentTitle)
- .setContentText(contentText)
- .setOngoing(false)
- .setContentIntent(pIntent)
- .setLargeIcon(icon)
- .setSmallIcon(smallIcon)
- .setWhen(0) // we don't need the time
- .setPriority(UserPreferences.getNotifyPriority()); // set notification priority
- IntList compactActionList = new IntList();
-
- int numActions = 0; // we start and 0 and then increment by 1 for each call to addAction
-
- if (isCasting) {
- Intent stopCastingIntent = new Intent(PlaybackService.this, PlaybackService.class);
- stopCastingIntent.putExtra(EXTRA_CAST_DISCONNECT, true);
- PendingIntent stopCastingPendingIntent = PendingIntent.getService(PlaybackService.this,
- numActions, stopCastingIntent, PendingIntent.FLAG_UPDATE_CURRENT);
- notificationBuilder.addAction(R.drawable.ic_media_cast_disconnect,
- getString(R.string.cast_disconnect_label),
- stopCastingPendingIntent);
- numActions++;
- }
-
- // always let them rewind
- PendingIntent rewindButtonPendingIntent = getPendingIntentForMediaAction(
- KeyEvent.KEYCODE_MEDIA_REWIND, numActions);
- notificationBuilder.addAction(android.R.drawable.ic_media_rew,
- getString(R.string.rewind_label),
- rewindButtonPendingIntent);
- if(UserPreferences.showRewindOnCompactNotification()) {
- compactActionList.add(numActions);
- }
- numActions++;
-
- if (playerStatus == PlayerStatus.PLAYING) {
- PendingIntent pauseButtonPendingIntent = getPendingIntentForMediaAction(
- KeyEvent.KEYCODE_MEDIA_PAUSE, numActions);
- notificationBuilder.addAction(android.R.drawable.ic_media_pause, //pause action
- getString(R.string.pause_label),
- pauseButtonPendingIntent);
- compactActionList.add(numActions++);
- } else {
- PendingIntent playButtonPendingIntent = getPendingIntentForMediaAction(
- KeyEvent.KEYCODE_MEDIA_PLAY, numActions);
- notificationBuilder.addAction(android.R.drawable.ic_media_play, //play action
- getString(R.string.play_label),
- playButtonPendingIntent);
- compactActionList.add(numActions++);
- }
-
- // ff follows play, then we have skip (if it's present)
- PendingIntent ffButtonPendingIntent = getPendingIntentForMediaAction(
- KeyEvent.KEYCODE_MEDIA_FAST_FORWARD, numActions);
- notificationBuilder.addAction(android.R.drawable.ic_media_ff,
- getString(R.string.fast_forward_label),
- ffButtonPendingIntent);
- if(UserPreferences.showFastForwardOnCompactNotification()) {
- compactActionList.add(numActions);
- }
- numActions++;
-
- if (UserPreferences.isFollowQueue()) {
- PendingIntent skipButtonPendingIntent = getPendingIntentForMediaAction(
- KeyEvent.KEYCODE_MEDIA_NEXT, numActions);
- notificationBuilder.addAction(android.R.drawable.ic_media_next,
- getString(R.string.skip_episode_label),
- skipButtonPendingIntent);
- if(UserPreferences.showSkipOnCompactNotification()) {
- compactActionList.add(numActions);
- }
- numActions++;
- }
-
- PendingIntent stopButtonPendingIntent = getPendingIntentForMediaAction(
- KeyEvent.KEYCODE_MEDIA_STOP, numActions);
- notificationBuilder.setStyle(new android.support.v7.app.NotificationCompat.MediaStyle()
- .setMediaSession(mediaSession.getSessionToken())
- .setShowActionsInCompactView(compactActionList.toArray())
- .setShowCancelButton(true)
- .setCancelButtonIntent(stopButtonPendingIntent))
- .setVisibility(Notification.VISIBILITY_PUBLIC)
- .setColor(Notification.COLOR_DEFAULT);
-
- notification = notificationBuilder.build();
-
- if (playerStatus == PlayerStatus.PLAYING ||
- playerStatus == PlayerStatus.PREPARING ||
- playerStatus == PlayerStatus.SEEKING ||
- isCasting) {
- startForeground(NOTIFICATION_ID, notification);
- } else {
- stopForeground(false);
- NotificationManager mNotificationManager = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
- mNotificationManager.notify(NOTIFICATION_ID, notification);
- }
- Log.d(TAG, "Notification set up");
- }
- }
- };
- notificationSetupThread = new Thread(notificationSetupTask);
- notificationSetupThread.start();
- }
-
- private PendingIntent getPendingIntentForMediaAction(int keycodeValue, int requestCode) {
- Intent intent = new Intent(
- PlaybackService.this, PlaybackService.class);
- intent.putExtra(
- MediaButtonReceiver.EXTRA_KEYCODE,
- keycodeValue);
- return PendingIntent
- .getService(PlaybackService.this, requestCode,
- intent,
- PendingIntent.FLAG_UPDATE_CURRENT);
- }
-
- /**
- * Persists the current position and last played time of the media file.
- *
- * @param updatePlayedDuration true if played_duration should be updated. This applies only to FeedMedia objects
- * @param deltaPlayedDuration value by which played_duration should be increased.
- */
- private synchronized void saveCurrentPosition(boolean updatePlayedDuration, int deltaPlayedDuration) {
- int position = getCurrentPosition();
- int duration = getDuration();
- float playbackSpeed = getCurrentPlaybackSpeed();
- final Playable playable = mediaPlayer.getPlayable();
- if (position != INVALID_TIME && duration != INVALID_TIME && playable != null) {
- Log.d(TAG, "Saving current position to " + position);
- if (updatePlayedDuration && playable instanceof FeedMedia) {
- FeedMedia media = (FeedMedia) playable;
- FeedItem item = media.getItem();
- media.setPlayedDuration(media.getPlayedDuration() + ((int) (deltaPlayedDuration * playbackSpeed)));
- // Auto flattr
- if (isAutoFlattrable(media) &&
- (media.getPlayedDuration() > UserPreferences.getAutoFlattrPlayedDurationThreshold() * duration)) {
- Log.d(TAG, "saveCurrentPosition: performing auto flattr since played duration " + Integer.toString(media.getPlayedDuration())
- + " is " + UserPreferences.getAutoFlattrPlayedDurationThreshold() * 100 + "% of file duration " + Integer.toString(duration));
- DBTasks.flattrItemIfLoggedIn(this, item);
- }
- }
- playable.saveCurrentPosition(
- PreferenceManager.getDefaultSharedPreferences(getApplicationContext()),
- position,
- System.currentTimeMillis());
- }
- }
-
- private void stopWidgetUpdater() {
- taskManager.cancelWidgetUpdater();
- sendBroadcast(new Intent(STOP_WIDGET_UPDATE));
- }
-
- private void updateWidget() {
- PlaybackService.this.sendBroadcast(new Intent(
- FORCE_WIDGET_UPDATE));
- }
-
- public boolean sleepTimerActive() {
- return taskManager.isSleepTimerActive();
- }
-
- public long getSleepTimerTimeLeft() {
- return taskManager.getSleepTimerTimeLeft();
- }
-
- private void bluetoothNotifyChange(PlaybackServiceMediaPlayer.PSMPInfo info, String whatChanged) {
- boolean isPlaying = false;
-
- if (info.playerStatus == PlayerStatus.PLAYING) {
- isPlaying = true;
- }
-
- if (info.playable != null) {
- Intent i = new Intent(whatChanged);
- i.putExtra("id", 1);
- i.putExtra("artist", "");
- i.putExtra("album", info.playable.getFeedTitle());
- i.putExtra("track", info.playable.getEpisodeTitle());
- i.putExtra("playing", isPlaying);
- final List<FeedItem> queue = taskManager.getQueueIfLoaded();
- if (queue != null) {
- i.putExtra("ListSize", queue.size());
- }
- i.putExtra("duration", info.playable.getDuration());
- i.putExtra("position", info.playable.getPosition());
- sendBroadcast(i);
- }
- }
-
- /**
- * Pauses playback when the headset is disconnected and the preference is
- * set
- */
- private final BroadcastReceiver headsetDisconnected = new BroadcastReceiver() {
- private static final String TAG = "headsetDisconnected";
- private static final int UNPLUGGED = 0;
- private static final int PLUGGED = 1;
-
- @Override
- public void onReceive(Context context, Intent intent) {
- if (TextUtils.equals(intent.getAction(), Intent.ACTION_HEADSET_PLUG)) {
- int state = intent.getIntExtra("state", -1);
- if (state != -1) {
- Log.d(TAG, "Headset plug event. State is " + state);
- if (state == UNPLUGGED) {
- Log.d(TAG, "Headset was unplugged during playback.");
- pauseIfPauseOnDisconnect();
- } else if (state == PLUGGED) {
- Log.d(TAG, "Headset was plugged in during playback.");
- unpauseIfPauseOnDisconnect(false);
- }
- } else {
- Log.e(TAG, "Received invalid ACTION_HEADSET_PLUG intent");
- }
- }
- }
- };
-
- private final BroadcastReceiver bluetoothStateUpdated = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
- if (TextUtils.equals(intent.getAction(), BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED)) {
- int state = intent.getIntExtra(BluetoothA2dp.EXTRA_STATE, -1);
- if (state == BluetoothA2dp.STATE_CONNECTED) {
- Log.d(TAG, "Received bluetooth connection intent");
- unpauseIfPauseOnDisconnect(true);
- }
- }
- }
- }
- };
-
- private final BroadcastReceiver audioBecomingNoisy = new BroadcastReceiver() {
-
- @Override
- public void onReceive(Context context, Intent intent) {
- // sound is about to change, eg. bluetooth -> speaker
- Log.d(TAG, "Pausing playback because audio is becoming noisy");
- pauseIfPauseOnDisconnect();
- }
- // android.media.AUDIO_BECOMING_NOISY
- };
-
- /**
- * Pauses playback if PREF_PAUSE_ON_HEADSET_DISCONNECT was set to true.
- */
- private void pauseIfPauseOnDisconnect() {
- if (UserPreferences.isPauseOnHeadsetDisconnect()) {
- if (mediaPlayer.getPlayerStatus() == PlayerStatus.PLAYING) {
- transientPause = true;
- }
- mediaPlayer.pause(!UserPreferences.isPersistNotify(), true);
- }
- }
-
- /**
- * @param bluetooth true if the event for unpausing came from bluetooth
- */
- private void unpauseIfPauseOnDisconnect(boolean bluetooth) {
- if (transientPause) {
- transientPause = false;
- if (!bluetooth && UserPreferences.isUnpauseOnHeadsetReconnect()) {
- mediaPlayer.resume();
- } else if (bluetooth && UserPreferences.isUnpauseOnBluetoothReconnect()){
- // let the user know we've started playback again...
- Vibrator v = (Vibrator) getApplicationContext().getSystemService(Context.VIBRATOR_SERVICE);
- if(v != null) {
- v.vibrate(500);
- }
- mediaPlayer.resume();
- }
- }
- }
-
- private final BroadcastReceiver shutdownReceiver = new BroadcastReceiver() {
-
- @Override
- public void onReceive(Context context, Intent intent) {
- if (TextUtils.equals(intent.getAction(), ACTION_SHUTDOWN_PLAYBACK_SERVICE)) {
- stopSelf();
- }
- }
-
- };
-
- private final BroadcastReceiver skipCurrentEpisodeReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (TextUtils.equals(intent.getAction(), ACTION_SKIP_CURRENT_EPISODE)) {
- Log.d(TAG, "Received SKIP_CURRENT_EPISODE intent");
- mediaPlayer.endPlayback(true, false);
- }
- }
- };
-
- private final BroadcastReceiver pauseResumeCurrentEpisodeReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (TextUtils.equals(intent.getAction(), ACTION_RESUME_PLAY_CURRENT_EPISODE)) {
- Log.d(TAG, "Received RESUME_PLAY_CURRENT_EPISODE intent");
- mediaPlayer.resume();
- }
- }
- };
-
- private final BroadcastReceiver pausePlayCurrentEpisodeReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (TextUtils.equals(intent.getAction(), ACTION_PAUSE_PLAY_CURRENT_EPISODE)) {
- Log.d(TAG, "Received PAUSE_PLAY_CURRENT_EPISODE intent");
- mediaPlayer.pause(false, false);
- }
- }
- };
-
- public static MediaType getCurrentMediaType() {
- return currentMediaType;
- }
-
- public static boolean isCasting() {
- return isCasting;
- }
-
- public void resume() {
- mediaPlayer.resume();
- }
-
- public void prepare() {
- mediaPlayer.prepare();
- }
-
- public void pause(boolean abandonAudioFocus, boolean reinit) {
- mediaPlayer.pause(abandonAudioFocus, reinit);
- }
-
- public void reinit() {
- mediaPlayer.reinit();
- }
-
- public PlaybackServiceMediaPlayer.PSMPInfo getPSMPInfo() {
- return mediaPlayer.getPSMPInfo();
- }
-
- public PlayerStatus getStatus() {
- return mediaPlayer.getPlayerStatus();
- }
-
- public Playable getPlayable() { return mediaPlayer.getPlayable(); }
-
- public boolean canSetSpeed() {
- return mediaPlayer.canSetSpeed();
- }
-
- public void setSpeed(float speed) {
- mediaPlayer.setSpeed(speed);
- }
-
- public void setVolume(float leftVolume, float rightVolume) {
- mediaPlayer.setVolume(leftVolume, rightVolume);
- }
-
- public float getCurrentPlaybackSpeed() {
- return mediaPlayer.getPlaybackSpeed();
- }
-
- public boolean canDownmix() {
- return mediaPlayer.canDownmix();
- }
-
- public void setDownmix(boolean enable) {
- mediaPlayer.setDownmix(enable);
- }
-
- public boolean isStartWhenPrepared() {
- return mediaPlayer.isStartWhenPrepared();
- }
-
- public void setStartWhenPrepared(boolean s) {
- mediaPlayer.setStartWhenPrepared(s);
- }
-
-
- public void seekTo(final int t) {
- if(mediaPlayer.getPlayerStatus() == PlayerStatus.PLAYING
- && GpodnetPreferences.loggedIn()) {
- final Playable playable = mediaPlayer.getPlayable();
- if (playable instanceof FeedMedia) {
- FeedMedia media = (FeedMedia) playable;
- FeedItem item = media.getItem();
- GpodnetEpisodeAction action = new GpodnetEpisodeAction.Builder(item, Action.PLAY)
- .currentDeviceId()
- .currentTimestamp()
- .started(startPosition / 1000)
- .position(getCurrentPosition() / 1000)
- .total(getDuration() / 1000)
- .build();
- GpodnetPreferences.enqueueEpisodeAction(action);
- }
- }
- mediaPlayer.seekTo(t);
- if(mediaPlayer.getPlayerStatus() == PlayerStatus.PLAYING ) {
- startPosition = t;
- }
- }
-
-
- public void seekDelta(final int d) {
- mediaPlayer.seekDelta(d);
- }
-
- /**
- * @see LocalPSMP#seekToChapter(de.danoeh.antennapod.core.feed.Chapter)
- */
- public void seekToChapter(Chapter c) {
- mediaPlayer.seekToChapter(c);
- }
-
- /**
- * call getDuration() on mediaplayer or return INVALID_TIME if player is in
- * an invalid state.
- */
- public int getDuration() {
- return mediaPlayer.getDuration();
- }
-
- /**
- * call getCurrentPosition() on mediaplayer or return INVALID_TIME if player
- * is in an invalid state.
- */
- public int getCurrentPosition() {
- return mediaPlayer.getPosition();
- }
-
- public boolean isStreaming() {
- return mediaPlayer.isStreaming();
- }
-
- public Pair<Integer, Integer> getVideoSize() {
- return mediaPlayer.getVideoSize();
- }
-
- private boolean isAutoFlattrable(FeedMedia media) {
- if (media != null) {
- FeedItem item = media.getItem();
- return item != null && FlattrUtils.hasToken() && UserPreferences.isAutoFlattr() && item.getPaymentLink() != null && item.getFlattrStatus().getUnflattred();
- } else {
- return false;
- }
- }
-
- private CastConsumer castConsumer = new DefaultCastConsumer() {
- @Override
- public void onApplicationConnected(ApplicationMetadata appMetadata, String sessionId, boolean wasLaunched) {
- PlaybackService.this.onCastAppConnected(wasLaunched);
- }
-
- @Override
- public void onDisconnectionReason(int reason) {
- Log.d(TAG, "onDisconnectionReason() with code " + reason);
- // This is our final chance to update the underlying stream position
- // In onDisconnected(), the underlying CastPlayback#mVideoCastConsumer
- // is disconnected and hence we update our local value of stream position
- // to the latest position.
- if (mediaPlayer != null) {
- saveCurrentPosition(false, 0);
- infoBeforeCastDisconnection = mediaPlayer.getPSMPInfo();
- if (reason != BaseCastManager.DISCONNECT_REASON_EXPLICIT &&
- infoBeforeCastDisconnection.playerStatus == PlayerStatus.PLAYING) {
- // If it's NOT based on user action, we shouldn't automatically resume local playback
- infoBeforeCastDisconnection.playerStatus = PlayerStatus.PAUSED;
- }
- }
- }
-
- @Override
- public void onDisconnected() {
- Log.d(TAG, "onDisconnected()");
- isCasting = false;
- PlaybackServiceMediaPlayer.PSMPInfo info = infoBeforeCastDisconnection;
- infoBeforeCastDisconnection = null;
- if (info == null && mediaPlayer != null) {
- info = mediaPlayer.getPSMPInfo();
- }
- if (info == null) {
- info = new PlaybackServiceMediaPlayer.PSMPInfo(PlayerStatus.STOPPED, null);
- }
- switchMediaPlayer(new LocalPSMP(PlaybackService.this, mediaPlayerCallback),
- info, true);
- if (info.playable != null) {
- sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD,
- info.playable.getMediaType() == MediaType.AUDIO ? EXTRA_CODE_AUDIO : EXTRA_CODE_VIDEO);
- } else {
- Log.d(TAG, "Cast session disconnected, but no current media");
- sendNotificationBroadcast(NOTIFICATION_TYPE_PLAYBACK_END, 0);
- }
- // hardware volume buttons control the local device volume
- mediaRouter.setMediaSessionCompat(null);
- unregisterWifiBroadcastReceiver();
- PlayerStatus status = info.playerStatus;
- if ((status == PlayerStatus.PLAYING ||
- status == PlayerStatus.SEEKING ||
- status == PlayerStatus.PREPARING ||
- UserPreferences.isPersistNotify()) &&
- android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
- setupNotification(info);
- } else if (!UserPreferences.isPersistNotify()){
- stopForeground(true);
- }
- }
- };
-
- private final MediaSessionCompat.Callback sessionCallback = new MediaSessionCompat.Callback() {
-
- private static final String TAG = "MediaSessionCompat";
-
- @Override
- public void onPlay() {
- Log.d(TAG, "onPlay()");
- PlayerStatus status = getStatus();
- if (status == PlayerStatus.PAUSED || status == PlayerStatus.PREPARED) {
- resume();
- } else if (status == PlayerStatus.INITIALIZED) {
- setStartWhenPrepared(true);
- prepare();
- }
- }
-
- @Override
- public void onPause() {
- Log.d(TAG, "onPause()");
- if (getStatus() == PlayerStatus.PLAYING) {
- pause(false, true);
- }
- if (UserPreferences.isPersistNotify()) {
- pause(false, true);
- } else {
- pause(true, true);
- }
- }
-
- @Override
- public void onStop() {
- Log.d(TAG, "onStop()");
- mediaPlayer.stop();
- }
-
- @Override
- public void onSkipToPrevious() {
- Log.d(TAG, "onSkipToPrevious()");
- seekDelta(-UserPreferences.getRewindSecs() * 1000);
- }
-
- @Override
- public void onRewind() {
- Log.d(TAG, "onRewind()");
- seekDelta(-UserPreferences.getRewindSecs() * 1000);
- }
-
- @Override
- public void onFastForward() {
- Log.d(TAG, "onFastForward()");
- seekDelta(UserPreferences.getFastFowardSecs() * 1000);
- }
-
- @Override
- public void onSkipToNext() {
- Log.d(TAG, "onSkipToNext()");
- if(UserPreferences.shouldHardwareButtonSkip()) {
- mediaPlayer.endPlayback(true, false);
- } else {
- seekDelta(UserPreferences.getFastFowardSecs() * 1000);
- }
- }
-
-
- @Override
- public void onSeekTo(long pos) {
- Log.d(TAG, "onSeekTo()");
- seekTo((int) pos);
- }
-
- @Override
- public boolean onMediaButtonEvent(final Intent mediaButton) {
- Log.d(TAG, "onMediaButtonEvent(" + mediaButton + ")");
- if (mediaButton != null) {
- KeyEvent keyEvent = (KeyEvent) mediaButton.getExtras().get(Intent.EXTRA_KEY_EVENT);
- if (keyEvent != null &&
- keyEvent.getAction() == KeyEvent.ACTION_DOWN &&
- keyEvent.getRepeatCount() == 0){
- handleKeycode(keyEvent.getKeyCode(), keyEvent.getSource());
- }
- }
- return false;
- }
- };
-
- private void onCastAppConnected(boolean wasLaunched) {
- Log.d(TAG, "A cast device application was " + (wasLaunched ? "launched" : "joined"));
- isCasting = true;
- PlaybackServiceMediaPlayer.PSMPInfo info = null;
- if (mediaPlayer != null) {
- info = mediaPlayer.getPSMPInfo();
- if (info.playerStatus == PlayerStatus.PLAYING) {
- // could be pause, but this way we make sure the new player will get the correct position,
- // since pause runs asynchronously and we could be directing the new player to play even before
- // the old player gives us back the position.
- saveCurrentPosition(false, 0);
- }
- }
- if (info == null) {
- info = new PlaybackServiceMediaPlayer.PSMPInfo(PlayerStatus.STOPPED, null);
- }
- sendNotificationBroadcast(NOTIFICATION_TYPE_RELOAD, EXTRA_CODE_CAST);
- switchMediaPlayer(new RemotePSMP(PlaybackService.this, mediaPlayerCallback),
- info,
- wasLaunched);
- // hardware volume buttons control the remote device volume
- mediaRouter.setMediaSessionCompat(mediaSession);
- registerWifiBroadcastReceiver();
- setupNotification(info);
- }
-
- private void switchMediaPlayer(@NonNull PlaybackServiceMediaPlayer newPlayer,
- @NonNull PlaybackServiceMediaPlayer.PSMPInfo info,
- boolean wasLaunched) {
- if (mediaPlayer != null) {
- mediaPlayer.endPlayback(true, true);
- mediaPlayer.shutdownQuietly();
- }
- mediaPlayer = newPlayer;
- Log.d(TAG, "switched to " + mediaPlayer.getClass().getSimpleName());
- if (!wasLaunched) {
- PlaybackServiceMediaPlayer.PSMPInfo candidate = mediaPlayer.getPSMPInfo();
- if (candidate.playable != null &&
- candidate.playerStatus.isAtLeast(PlayerStatus.PREPARING)) {
- // do not automatically send new media to cast device
- info.playable = null;
- }
- }
- if (info.playable != null) {
- mediaPlayer.playMediaObject(info.playable,
- !info.playable.localFileAvailable(),
- info.playerStatus == PlayerStatus.PLAYING,
- info.playerStatus.isAtLeast(PlayerStatus.PREPARING));
- }
- }
-
- private void registerWifiBroadcastReceiver() {
- if (wifiBroadcastReceiver != null) {
- return;
- }
- wifiBroadcastReceiver = new BroadcastReceiver() {
- @Override
- public void onReceive(Context context, Intent intent) {
- if (intent.getAction().equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
- NetworkInfo info = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
- boolean isConnected = info.isConnected();
- //apparently this method gets called twice when a change happens, but one run is enough.
- if (isConnected && !wifiConnectivity) {
- wifiConnectivity = true;
- castManager.startCastDiscovery();
- castManager.reconnectSessionIfPossible(RECONNECTION_ATTEMPT_PERIOD_S, NetworkUtils.getWifiSsid());
- } else {
- wifiConnectivity = isConnected;
- }
- }
- }
- };
- registerReceiver(wifiBroadcastReceiver,
- new IntentFilter(WifiManager.NETWORK_STATE_CHANGED_ACTION));
- }
-
- private void unregisterWifiBroadcastReceiver() {
- if (wifiBroadcastReceiver != null) {
- unregisterReceiver(wifiBroadcastReceiver);
- wifiBroadcastReceiver = null;
- }
- }
-
- private SharedPreferences.OnSharedPreferenceChangeListener prefListener =
- (sharedPreferences, key) -> {
- if (UserPreferences.PREF_CAST_ENABLED.equals(key)) {
- if (!UserPreferences.isCastEnabled()) {
- if (castManager.isConnecting() || castManager.isConnected()) {
- Log.d(TAG, "Disconnecting cast device due to a change in user preferences");
- castManager.disconnect();
- }
- }
- } else if (UserPreferences.PREF_LOCKSCREEN_BACKGROUND.equals(key)) {
- updateMediaSessionMetadata(getPlayable());
- }
- };
-}
diff --git a/core/src/play/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java b/core/src/play/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java
new file mode 100644
index 000000000..aef3e3c2b
--- /dev/null
+++ b/core/src/play/java/de/danoeh/antennapod/core/service/playback/PlaybackServiceFlavorHelper.java
@@ -0,0 +1,252 @@
+package de.danoeh.antennapod.core.service.playback;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.NetworkInfo;
+import android.net.wifi.WifiManager;
+import android.support.annotation.NonNull;
+import android.support.annotation.StringRes;
+import android.support.v7.media.MediaRouter;
+import android.util.Log;
+import android.widget.Toast;
+
+import com.google.android.gms.cast.ApplicationMetadata;
+import com.google.android.libraries.cast.companionlibrary.cast.BaseCastManager;
+
+import de.danoeh.antennapod.core.cast.CastConsumer;
+import de.danoeh.antennapod.core.cast.CastManager;
+import de.danoeh.antennapod.core.cast.DefaultCastConsumer;
+import de.danoeh.antennapod.core.feed.MediaType;
+import de.danoeh.antennapod.core.preferences.UserPreferences;
+import de.danoeh.antennapod.core.util.NetworkUtils;
+
+/**
+ * Class intended to work along PlaybackService and provide support for different flavors.
+ */
+public class PlaybackServiceFlavorHelper {
+ public static final String TAG = "PlaybackSrvFlavorHelper";
+
+ /**
+ * Time in seconds during which the CastManager will try to reconnect to the Cast Device after
+ * the Wifi Connection is regained.
+ */
+ private static final int RECONNECTION_ATTEMPT_PERIOD_S = 15;
+ /**
+ * Stores the state of the cast playback just before it disconnects.
+ */
+ private volatile PlaybackServiceMediaPlayer.PSMPInfo infoBeforeCastDisconnection;
+
+ private boolean wifiConnectivity = true;
+ private BroadcastReceiver wifiBroadcastReceiver;
+
+ private CastManager castManager;
+ private MediaRouter mediaRouter;
+ private PlaybackService.FlavorHelperCallback callback;
+ private CastConsumer castConsumer;
+
+ PlaybackServiceFlavorHelper(Context context, PlaybackService.FlavorHelperCallback callback) {
+ this.callback = callback;
+ mediaRouter = MediaRouter.getInstance(context.getApplicationContext());
+ setCastConsumer(context);
+ }
+
+ void initializeMediaPlayer(Context context) {
+ castManager = CastManager.getInstance();
+ castManager.addCastConsumer(castConsumer);
+ boolean isCasting = castManager.isConnected();
+ callback.setIsCasting(isCasting);
+ if (isCasting) {
+ if (UserPreferences.isCastEnabled()) {
+ onCastAppConnected(context, false);
+ } else {
+ castManager.disconnect();
+ }
+ } else {
+ callback.setMediaPlayer(new LocalPSMP(context, callback.getMediaPlayerCallback()));
+ }
+ }
+
+ void removeCastConsumer() {
+ castManager.removeCastConsumer(castConsumer);
+ }
+
+ boolean castDisconnect(boolean castDisconnect) {
+ if (castDisconnect) {
+ castManager.disconnect();
+ return true;
+ }
+ return false;
+ }
+
+ boolean onMediaPlayerInfo(Context context, int code, @StringRes int resourceId) {
+ switch (code) {
+ case RemotePSMP.CAST_ERROR:
+ callback.sendNotificationBroadcast(PlaybackService.NOTIFICATION_TYPE_SHOW_TOAST, resourceId);
+ return true;
+ case RemotePSMP.CAST_ERROR_PRIORITY_HIGH:
+ Toast.makeText(context, resourceId, Toast.LENGTH_SHORT).show();
+ return true;
+ default:
+ return false;
+ }
+ }
+
+ private void setCastConsumer(Context context) {
+ castConsumer = new DefaultCastConsumer() {
+ @Override
+ public void onApplicationConnected(ApplicationMetadata appMetadata, String sessionId, boolean wasLaunched) {
+ onCastAppConnected(context, wasLaunched);
+ }
+
+ @Override
+ public void onDisconnectionReason(int reason) {
+ Log.d(TAG, "onDisconnectionReason() with code " + reason);
+ // This is our final chance to update the underlying stream position
+ // In onDisconnected(), the underlying CastPlayback#mVideoCastConsumer
+ // is disconnected and hence we update our local value of stream position
+ // to the latest position.
+ PlaybackServiceMediaPlayer mediaPlayer = callback.getMediaPlayer();
+ if (mediaPlayer != null) {
+ callback.saveCurrentPosition(false, 0);
+ infoBeforeCastDisconnection = mediaPlayer.getPSMPInfo();
+ if (reason != BaseCastManager.DISCONNECT_REASON_EXPLICIT &&
+ infoBeforeCastDisconnection.playerStatus == PlayerStatus.PLAYING) {
+ // If it's NOT based on user action, we shouldn't automatically resume local playback
+ infoBeforeCastDisconnection.playerStatus = PlayerStatus.PAUSED;
+ }
+ }
+ }
+
+ @Override
+ public void onDisconnected() {
+ Log.d(TAG, "onDisconnected()");
+ callback.setIsCasting(false);
+ PlaybackServiceMediaPlayer.PSMPInfo info = infoBeforeCastDisconnection;
+ infoBeforeCastDisconnection = null;
+ PlaybackServiceMediaPlayer mediaPlayer = callback.getMediaPlayer();
+ if (info == null && mediaPlayer != null) {
+ info = mediaPlayer.getPSMPInfo();
+ }
+ if (info == null) {
+ info = new PlaybackServiceMediaPlayer.PSMPInfo(PlayerStatus.STOPPED, null);
+ }
+ switchMediaPlayer(new LocalPSMP(context, callback.getMediaPlayerCallback()),
+ info, true);
+ if (info.playable != null) {
+ callback.sendNotificationBroadcast(PlaybackService.NOTIFICATION_TYPE_RELOAD,
+ info.playable.getMediaType() == MediaType.AUDIO ?
+ PlaybackService.EXTRA_CODE_AUDIO : PlaybackService.EXTRA_CODE_VIDEO);
+ } else {
+ Log.d(TAG, "Cast session disconnected, but no current media");
+ callback.sendNotificationBroadcast(PlaybackService.NOTIFICATION_TYPE_PLAYBACK_END, 0);
+ }
+ // hardware volume buttons control the local device volume
+ mediaRouter.setMediaSessionCompat(null);
+ unregisterWifiBroadcastReceiver();
+ callback.setupNotification(false, info);
+ }
+ };
+ }
+
+ private void onCastAppConnected(Context context, boolean wasLaunched) {
+ Log.d(TAG, "A cast device application was " + (wasLaunched ? "launched" : "joined"));
+ callback.setIsCasting(true);
+ PlaybackServiceMediaPlayer.PSMPInfo info = null;
+ PlaybackServiceMediaPlayer mediaPlayer = callback.getMediaPlayer();
+ if (mediaPlayer != null) {
+ info = mediaPlayer.getPSMPInfo();
+ if (info.playerStatus == PlayerStatus.PLAYING) {
+ // could be pause, but this way we make sure the new player will get the correct position,
+ // since pause runs asynchronously and we could be directing the new player to play even before
+ // the old player gives us back the position.
+ callback.saveCurrentPosition(false, 0);
+ }
+ }
+ if (info == null) {
+ info = new PlaybackServiceMediaPlayer.PSMPInfo(PlayerStatus.STOPPED, null);
+ }
+ callback.sendNotificationBroadcast(PlaybackService.NOTIFICATION_TYPE_RELOAD,
+ PlaybackService.EXTRA_CODE_CAST);
+ switchMediaPlayer(new RemotePSMP(context, callback.getMediaPlayerCallback()),
+ info,
+ wasLaunched);
+ // hardware volume buttons control the remote device volume
+ mediaRouter.setMediaSessionCompat(callback.getMediaSession());
+ registerWifiBroadcastReceiver();
+ callback.setupNotification(true, info);
+ }
+
+ private void switchMediaPlayer(@NonNull PlaybackServiceMediaPlayer newPlayer,
+ @NonNull PlaybackServiceMediaPlayer.PSMPInfo info,
+ boolean wasLaunched) {
+ PlaybackServiceMediaPlayer mediaPlayer = callback.getMediaPlayer();
+ if (mediaPlayer != null) {
+ mediaPlayer.endPlayback(true, true);
+ mediaPlayer.shutdownQuietly();
+ }
+ mediaPlayer = newPlayer;
+ callback.setMediaPlayer(mediaPlayer);
+ Log.d(TAG, "switched to " + mediaPlayer.getClass().getSimpleName());
+ if (!wasLaunched) {
+ PlaybackServiceMediaPlayer.PSMPInfo candidate = mediaPlayer.getPSMPInfo();
+ if (candidate.playable != null &&
+ candidate.playerStatus.isAtLeast(PlayerStatus.PREPARING)) {
+ // do not automatically send new media to cast device
+ info.playable = null;
+ }
+ }
+ if (info.playable != null) {
+ mediaPlayer.playMediaObject(info.playable,
+ !info.playable.localFileAvailable(),
+ info.playerStatus == PlayerStatus.PLAYING,
+ info.playerStatus.isAtLeast(PlayerStatus.PREPARING));
+ }
+ }
+
+ void registerWifiBroadcastReceiver() {
+ if (wifiBroadcastReceiver != null) {
+ return;
+ }
+ wifiBroadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (intent.getAction().equals(WifiManager.NETWORK_STATE_CHANGED_ACTION)) {
+ NetworkInfo info = intent.getParcelableExtra(WifiManager.EXTRA_NETWORK_INFO);
+ boolean isConnected = info.isConnected();
+ //apparently this method gets called twice when a change happens, but one run is enough.
+ if (isConnected && !wifiConnectivity) {
+ wifiConnectivity = true;
+ castManager.startCastDiscovery();
+ castManager.reconnectSessionIfPossible(RECONNECTION_ATTEMPT_PERIOD_S, NetworkUtils.getWifiSsid());
+ } else {
+ wifiConnectivity = isConnected;
+ }
+ }
+ }
+ };
+ callback.registerReceiver(wifiBroadcastReceiver,
+ new IntentFilter(WifiManager.NETWORK_STATE_CHANGED_ACTION));
+ }
+
+ void unregisterWifiBroadcastReceiver() {
+ if (wifiBroadcastReceiver != null) {
+ callback.unregisterReceiver(wifiBroadcastReceiver);
+ wifiBroadcastReceiver = null;
+ }
+ }
+
+ boolean onSharedPreference(String key) {
+ if (UserPreferences.PREF_CAST_ENABLED.equals(key)) {
+ if (!UserPreferences.isCastEnabled()) {
+ if (castManager.isConnecting() || castManager.isConnected()) {
+ Log.d(TAG, "Disconnecting cast device due to a change in user preferences");
+ castManager.disconnect();
+ }
+ }
+ return true;
+ }
+ return false;
+ }
+}
diff --git a/core/src/play/java/de/danoeh/antennapod/core/util/playback/Playable.java b/core/src/play/java/de/danoeh/antennapod/core/util/playback/Playable.java
deleted file mode 100644
index 201efbc81..000000000
--- a/core/src/play/java/de/danoeh/antennapod/core/util/playback/Playable.java
+++ /dev/null
@@ -1,246 +0,0 @@
-package de.danoeh.antennapod.core.util.playback;
-
-import android.content.Context;
-import android.content.SharedPreferences;
-import android.os.Parcelable;
-import android.util.Log;
-
-import java.util.List;
-
-import de.danoeh.antennapod.core.asynctask.ImageResource;
-import de.danoeh.antennapod.core.cast.RemoteMedia;
-import de.danoeh.antennapod.core.feed.Chapter;
-import de.danoeh.antennapod.core.feed.FeedMedia;
-import de.danoeh.antennapod.core.feed.MediaType;
-import de.danoeh.antennapod.core.storage.DBReader;
-import de.danoeh.antennapod.core.util.ShownotesProvider;
-
-/**
- * Interface for objects that can be played by the PlaybackService.
- */
-public interface Playable extends Parcelable,
- ShownotesProvider, ImageResource {
-
- /**
- * Save information about the playable in a preference so that it can be
- * restored later via PlayableUtils.createInstanceFromPreferences.
- * Implementations must NOT call commit() after they have written the values
- * to the preferences file.
- */
- void writeToPreferences(SharedPreferences.Editor prefEditor);
-
- /**
- * This method is called from a separate thread by the PlaybackService.
- * Playable objects should load their metadata in this method. This method
- * should execute as quickly as possible and NOT load chapter marks if no
- * local file is available.
- */
- void loadMetadata() throws PlayableException;
-
- /**
- * This method is called from a separate thread by the PlaybackService.
- * Playable objects should load their chapter marks in this method if no
- * local file was available when loadMetadata() was called.
- */
- void loadChapterMarks();
-
- /**
- * Returns the title of the episode that this playable represents
- */
- String getEpisodeTitle();
-
- /**
- * Returns a list of chapter marks or null if this Playable has no chapters.
- */
- List<Chapter> getChapters();
-
- /**
- * Returns a link to a website that is meant to be shown in a browser
- */
- String getWebsiteLink();
-
- String getPaymentLink();
-
- /**
- * Returns the title of the feed this Playable belongs to.
- */
- String getFeedTitle();
-
- /**
- * Returns a unique identifier, for example a file url or an ID from a
- * database.
- */
- Object getIdentifier();
-
- /**
- * Return duration of object or 0 if duration is unknown.
- */
- int getDuration();
-
- /**
- * Return position of object or 0 if position is unknown.
- */
- int getPosition();
-
- /**
- * Returns last time (in ms) when this playable was played or 0
- * if last played time is unknown.
- */
- long getLastPlayedTime();
-
- /**
- * Returns the type of media. This method should return the correct value
- * BEFORE loadMetadata() is called.
- */
- MediaType getMediaType();
-
- /**
- * Returns an url to a local file that can be played or null if this file
- * does not exist.
- */
- String getLocalMediaUrl();
-
- /**
- * Returns an url to a file that can be streamed by the player or null if
- * this url is not known.
- */
- String getStreamUrl();
-
- /**
- * Returns true if a local file that can be played is available. getFileUrl
- * MUST return a non-null string if this method returns true.
- */
- boolean localFileAvailable();
-
- /**
- * Returns true if a streamable file is available. getStreamUrl MUST return
- * a non-null string if this method returns true.
- */
- boolean streamAvailable();
-
- /**
- * Saves the current position of this object. Implementations can use the
- * provided SharedPreference to save this information and retrieve it later
- * via PlayableUtils.createInstanceFromPreferences.
- *
- * @param pref shared prefs that might be used to store this object
- * @param newPosition new playback position in ms
- * @param timestamp current time in ms
- */
- void saveCurrentPosition(SharedPreferences pref, int newPosition, long timestamp);
-
- void setPosition(int newPosition);
-
- void setDuration(int newDuration);
-
- /**
- * @param lastPlayedTimestamp timestamp in ms
- */
- void setLastPlayedTime(long lastPlayedTimestamp);
-
- /**
- * Is called by the PlaybackService when playback starts.
- */
- void onPlaybackStart();
-
- /**
- * Is called by the PlaybackService when playback is completed.
- */
- void onPlaybackCompleted();
-
- /**
- * Returns an integer that must be unique among all Playable classes. The
- * return value is later used by PlayableUtils to determine the type of the
- * Playable object that is restored.
- */
- int getPlayableType();
-
- void setChapters(List<Chapter> chapters);
-
- /**
- * Provides utility methods for Playable objects.
- */
- class PlayableUtils {
- private static final String TAG = "PlayableUtils";
-
- /**
- * Restores a playable object from a sharedPreferences file. This method might load data from the database,
- * depending on the type of playable that was restored.
- *
- * @param type An integer that represents the type of the Playable object
- * that is restored.
- * @param pref The SharedPreferences file from which the Playable object
- * is restored
- * @return The restored Playable object
- */
- public static Playable createInstanceFromPreferences(Context context, int type,
- SharedPreferences pref) {
- Playable result = null;
- // ADD new Playable types here:
- switch (type) {
- case FeedMedia.PLAYABLE_TYPE_FEEDMEDIA:
- result = createFeedMediaInstance(pref);
- break;
- case ExternalMedia.PLAYABLE_TYPE_EXTERNAL_MEDIA:
- result = createExternalMediaInstance(pref);
- break;
- case RemoteMedia.PLAYABLE_TYPE_REMOTE_MEDIA:
- result = createRemoteMediaInstance(pref);
- break;
- }
- if (result == null) {
- Log.e(TAG, "Could not restore Playable object from preferences");
- }
- return result;
- }
-
- private static Playable createFeedMediaInstance(SharedPreferences pref) {
- Playable result = null;
- long mediaId = pref.getLong(FeedMedia.PREF_MEDIA_ID, -1);
- if (mediaId != -1) {
- result = DBReader.getFeedMedia(mediaId);
- }
- return result;
- }
-
- private static Playable createExternalMediaInstance(SharedPreferences pref) {
- Playable result = null;
- String source = pref.getString(ExternalMedia.PREF_SOURCE_URL, null);
- String mediaType = pref.getString(ExternalMedia.PREF_MEDIA_TYPE, null);
- if (source != null && mediaType != null) {
- int position = pref.getInt(ExternalMedia.PREF_POSITION, 0);
- long lastPlayedTime = pref.getLong(ExternalMedia.PREF_LAST_PLAYED_TIME, 0);
- result = new ExternalMedia(source, MediaType.valueOf(mediaType),
- position, lastPlayedTime);
- }
- return result;
- }
-
- private static Playable createRemoteMediaInstance(SharedPreferences pref) {
- //TODO there's probably no point in restoring RemoteMedia from preferences, because we
- //only care about it while it's playing on the cast device.
- return null;
- }
- }
-
- class PlayableException extends Exception {
- private static final long serialVersionUID = 1L;
-
- public PlayableException() {
- super();
- }
-
- public PlayableException(String detailMessage, Throwable throwable) {
- super(detailMessage, throwable);
- }
-
- public PlayableException(String detailMessage) {
- super(detailMessage);
- }
-
- public PlayableException(Throwable throwable) {
- super(throwable);
- }
-
- }
-}
diff --git a/core/src/play/res/values/strings.xml b/core/src/play/res/values/strings.xml
new file mode 100644
index 000000000..4fd13d2de
--- /dev/null
+++ b/core/src/play/res/values/strings.xml
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+ <string name="pref_cast_message">@string/pref_cast_message_play_flavor</string>
+</resources>