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