Merge branch 'develop' into follow-system-theme
@ -24,7 +24,7 @@ trans.gl = core/src/main/res/values-gl-rES/strings.xml
|
|||||||
trans.he_IL = core/src/main/res/values-iw-rIL/strings.xml
|
trans.he_IL = core/src/main/res/values-iw-rIL/strings.xml
|
||||||
trans.hi_IN = core/src/main/res/values-hi-rIN/strings.xml
|
trans.hi_IN = core/src/main/res/values-hi-rIN/strings.xml
|
||||||
trans.hu = core/src/main/res/values-hu/strings.xml
|
trans.hu = core/src/main/res/values-hu/strings.xml
|
||||||
trans.id = core/src/main/res/values-id/strings.xml
|
trans.id = core/src/main/res/values-in/strings.xml
|
||||||
trans.it_IT = core/src/main/res/values-it/strings.xml
|
trans.it_IT = core/src/main/res/values-it/strings.xml
|
||||||
trans.is = core/src/main/res/values-is-rIS/strings.xml
|
trans.is = core/src/main/res/values-is-rIS/strings.xml
|
||||||
trans.ja = core/src/main/res/values-ja/strings.xml
|
trans.ja = core/src/main/res/values-ja/strings.xml
|
||||||
|
11
CHANGELOG.md
@ -1,6 +1,17 @@
|
|||||||
Change Log
|
Change Log
|
||||||
==========
|
==========
|
||||||
|
|
||||||
|
Version 1.7.3
|
||||||
|
-------------
|
||||||
|
* Display episode image on widget (by @brad)
|
||||||
|
* Added checkbox to keep queue sorted (by @damoasda)
|
||||||
|
* New UI for "Add podcast" screen (by @ByteHamster)
|
||||||
|
* Added batch editing to the queue (by @ByteHamster)
|
||||||
|
* Added option to adapt remaining time to playback speed (by @CedricCabessa)
|
||||||
|
* Removed broken Flattr integration (by @ByteHamster)
|
||||||
|
* Added filter to "All episodes" list (by @jhunnius)
|
||||||
|
* Tons of bug fixes and performance improvements
|
||||||
|
|
||||||
Version 1.7.2
|
Version 1.7.2
|
||||||
-------------
|
-------------
|
||||||
* Added configurable behavior of the back button
|
* Added configurable behavior of the back button
|
||||||
|
@ -18,8 +18,8 @@ android {
|
|||||||
// "1.2.3-SNAPSHOT" -> 1020300
|
// "1.2.3-SNAPSHOT" -> 1020300
|
||||||
// "1.2.3-RC4" -> 1020304
|
// "1.2.3-RC4" -> 1020304
|
||||||
// "1.2.3" -> 1020395
|
// "1.2.3" -> 1020395
|
||||||
versionCode 1070305
|
versionCode 1070395
|
||||||
versionName "1.7.3-RC5"
|
versionName "1.7.3"
|
||||||
testApplicationId "de.test.antennapod"
|
testApplicationId "de.test.antennapod"
|
||||||
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
|
||||||
generatedDensities = []
|
generatedDensities = []
|
||||||
|
@ -72,20 +72,6 @@ public class HTTPBin extends NanoHTTPD {
|
|||||||
return servedFiles.size() - 1;
|
return servedFiles.size() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the file with the given ID from the server.
|
|
||||||
*
|
|
||||||
* @return True if a file was removed, false otherwise
|
|
||||||
*/
|
|
||||||
public synchronized boolean removeFile(int id) {
|
|
||||||
if (id < 0) throw new IllegalArgumentException("ID < 0");
|
|
||||||
if (id >= servedFiles.size()) {
|
|
||||||
return false;
|
|
||||||
} else {
|
|
||||||
return servedFiles.remove(id) != null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public synchronized File accessFile(int id) {
|
public synchronized File accessFile(int id) {
|
||||||
if (id < 0 || id >= servedFiles.size()) {
|
if (id < 0 || id >= servedFiles.size()) {
|
||||||
return null;
|
return null;
|
||||||
|
@ -7,212 +7,9 @@ import android.support.v7.app.AppCompatActivity;
|
|||||||
* network.
|
* network.
|
||||||
*/
|
*/
|
||||||
public abstract class CastEnabledActivity extends AppCompatActivity {
|
public abstract class CastEnabledActivity extends AppCompatActivity {
|
||||||
// implements SharedPreferences.OnSharedPreferenceChangeListener {
|
|
||||||
public static final String TAG = "CastEnabledActivity";
|
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) {
|
public final void requestCastButton(int showAsAction) {
|
||||||
// no-op
|
// no-op
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}
|
}
|
||||||
|
@ -36,7 +36,7 @@
|
|||||||
android:usesCleartextTraffic="true"
|
android:usesCleartextTraffic="true"
|
||||||
android:logo="@mipmap/ic_launcher">
|
android:logo="@mipmap/ic_launcher">
|
||||||
<meta-data android:name="com.google.android.gms.car.notification.SmallIcon"
|
<meta-data android:name="com.google.android.gms.car.notification.SmallIcon"
|
||||||
android:resource="@drawable/ic_notification" />
|
android:resource="@drawable/ic_antenna" />
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="com.google.android.backup.api_key"
|
android:name="com.google.android.backup.api_key"
|
||||||
android:value="AEdPqrEAAAAI3a05VToCTlqBymJrbFGaKQMvF-bBAuLsOdavBA"/>
|
android:value="AEdPqrEAAAAI3a05VToCTlqBymJrbFGaKQMvF-bBAuLsOdavBA"/>
|
||||||
|
@ -240,7 +240,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi
|
|||||||
|
|
||||||
SharedPreferences.Editor edit = prefs.edit();
|
SharedPreferences.Editor edit = prefs.edit();
|
||||||
edit.putBoolean(PREF_IS_FIRST_LAUNCH, false);
|
edit.putBoolean(PREF_IS_FIRST_LAUNCH, false);
|
||||||
edit.commit();
|
edit.apply();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package de.danoeh.antennapod.adapter;
|
package de.danoeh.antennapod.adapter;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.v4.content.ContextCompat;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@ -15,20 +14,15 @@ import de.danoeh.antennapod.core.service.download.DownloadRequest;
|
|||||||
import de.danoeh.antennapod.core.service.download.DownloadStatus;
|
import de.danoeh.antennapod.core.service.download.DownloadStatus;
|
||||||
import de.danoeh.antennapod.core.service.download.Downloader;
|
import de.danoeh.antennapod.core.service.download.Downloader;
|
||||||
import de.danoeh.antennapod.core.util.Converter;
|
import de.danoeh.antennapod.core.util.Converter;
|
||||||
import de.danoeh.antennapod.core.util.ThemeUtils;
|
|
||||||
|
|
||||||
public class DownloadlistAdapter extends BaseAdapter {
|
public class DownloadlistAdapter extends BaseAdapter {
|
||||||
|
|
||||||
private static final int SELECTION_NONE = -1;
|
|
||||||
|
|
||||||
private int selectedItemIndex;
|
|
||||||
private final ItemAccess itemAccess;
|
private final ItemAccess itemAccess;
|
||||||
private final Context context;
|
private final Context context;
|
||||||
|
|
||||||
public DownloadlistAdapter(Context context,
|
public DownloadlistAdapter(Context context,
|
||||||
ItemAccess itemAccess) {
|
ItemAccess itemAccess) {
|
||||||
super();
|
super();
|
||||||
this.selectedItemIndex = SELECTION_NONE;
|
|
||||||
this.context = context;
|
this.context = context;
|
||||||
this.itemAccess = itemAccess;
|
this.itemAccess = itemAccess;
|
||||||
}
|
}
|
||||||
@ -74,13 +68,6 @@ public class DownloadlistAdapter extends BaseAdapter {
|
|||||||
holder = (Holder) convertView.getTag();
|
holder = (Holder) convertView.getTag();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (position == selectedItemIndex) {
|
|
||||||
convertView.setBackgroundColor(ContextCompat.getColor(convertView.getContext(),
|
|
||||||
ThemeUtils.getSelectionBackgroundColor()));
|
|
||||||
} else {
|
|
||||||
convertView.setBackgroundResource(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
holder.title.setText(request.getTitle());
|
holder.title.setText(request.getTitle());
|
||||||
|
|
||||||
holder.progbar.setIndeterminate(request.getSoFar() <= 0);
|
holder.progbar.setIndeterminate(request.getSoFar() <= 0);
|
||||||
|
@ -34,12 +34,8 @@ public class FeedItemlistAdapter extends BaseAdapter {
|
|||||||
private final ItemAccess itemAccess;
|
private final ItemAccess itemAccess;
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final boolean showFeedtitle;
|
private final boolean showFeedtitle;
|
||||||
private final int selectedItemIndex;
|
|
||||||
/** true if played items should be made partially transparent */
|
/** true if played items should be made partially transparent */
|
||||||
private final boolean makePlayedItemsTransparent;
|
private final boolean makePlayedItemsTransparent;
|
||||||
|
|
||||||
private static final int SELECTION_NONE = -1;
|
|
||||||
|
|
||||||
private final int playingBackGroundColor;
|
private final int playingBackGroundColor;
|
||||||
private final int normalBackGroundColor;
|
private final int normalBackGroundColor;
|
||||||
|
|
||||||
@ -51,7 +47,6 @@ public class FeedItemlistAdapter extends BaseAdapter {
|
|||||||
this.context = context;
|
this.context = context;
|
||||||
this.itemAccess = itemAccess;
|
this.itemAccess = itemAccess;
|
||||||
this.showFeedtitle = showFeedtitle;
|
this.showFeedtitle = showFeedtitle;
|
||||||
this.selectedItemIndex = SELECTION_NONE;
|
|
||||||
this.makePlayedItemsTransparent = makePlayedItemsTransparent;
|
this.makePlayedItemsTransparent = makePlayedItemsTransparent;
|
||||||
|
|
||||||
playingBackGroundColor = ThemeUtils.getColorFromAttr(context, R.attr.currently_playing_background);
|
playingBackGroundColor = ThemeUtils.getColorFromAttr(context, R.attr.currently_playing_background);
|
||||||
@ -112,12 +107,6 @@ public class FeedItemlistAdapter extends BaseAdapter {
|
|||||||
|
|
||||||
if (!(getItemViewType(position) == Adapter.IGNORE_ITEM_VIEW_TYPE)) {
|
if (!(getItemViewType(position) == Adapter.IGNORE_ITEM_VIEW_TYPE)) {
|
||||||
convertView.setVisibility(View.VISIBLE);
|
convertView.setVisibility(View.VISIBLE);
|
||||||
if (position == selectedItemIndex) {
|
|
||||||
convertView.setBackgroundColor(ContextCompat.getColor(convertView.getContext(),
|
|
||||||
ThemeUtils.getSelectionBackgroundColor()));
|
|
||||||
} else {
|
|
||||||
convertView.setBackgroundResource(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
StringBuilder buffer = new StringBuilder(item.getTitle());
|
StringBuilder buffer = new StringBuilder(item.getTitle());
|
||||||
if (showFeedtitle) {
|
if (showFeedtitle) {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package de.danoeh.antennapod.adapter;
|
package de.danoeh.antennapod.adapter;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.os.Build;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@ -51,6 +52,17 @@ public class FeedItemlistDescriptionAdapter extends ArrayAdapter<FeedItem> {
|
|||||||
.replaceAll("\\s+", " ")
|
.replaceAll("\\s+", " ")
|
||||||
.trim();
|
.trim();
|
||||||
holder.description.setText(description);
|
holder.description.setText(description);
|
||||||
|
|
||||||
|
final int MAX_LINES_COLLAPSED = 3;
|
||||||
|
holder.description.setMaxLines(MAX_LINES_COLLAPSED);
|
||||||
|
holder.description.setOnClickListener(v -> {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN
|
||||||
|
&& holder.description.getMaxLines() > MAX_LINES_COLLAPSED) {
|
||||||
|
holder.description.setMaxLines(MAX_LINES_COLLAPSED);
|
||||||
|
} else {
|
||||||
|
holder.description.setMaxLines(2000);
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
return convertView;
|
return convertView;
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,72 @@
|
|||||||
|
package de.danoeh.antennapod.asynctask;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.v4.provider.DocumentFile;
|
||||||
|
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.io.OutputStreamWriter;
|
||||||
|
|
||||||
|
import de.danoeh.antennapod.core.export.ExportWriter;
|
||||||
|
import de.danoeh.antennapod.core.storage.DBReader;
|
||||||
|
import de.danoeh.antennapod.core.util.LangUtils;
|
||||||
|
import io.reactivex.Observable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Writes an OPML file into the user selected export directory in the background.
|
||||||
|
*/
|
||||||
|
public class DocumentFileExportWorker {
|
||||||
|
|
||||||
|
private final @NonNull ExportWriter exportWriter;
|
||||||
|
private @NonNull Context context;
|
||||||
|
private @NonNull Uri outputFileUri;
|
||||||
|
|
||||||
|
public DocumentFileExportWorker(@NonNull ExportWriter exportWriter, @NonNull Context context, @NonNull Uri outputFileUri) {
|
||||||
|
this.exportWriter = exportWriter;
|
||||||
|
this.context = context;
|
||||||
|
this.outputFileUri = outputFileUri;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Observable<DocumentFile> exportObservable() {
|
||||||
|
DocumentFile output = DocumentFile.fromSingleUri(context, outputFileUri);
|
||||||
|
return Observable.create(subscriber -> {
|
||||||
|
OutputStream outputStream = null;
|
||||||
|
OutputStreamWriter writer = null;
|
||||||
|
try {
|
||||||
|
Uri uri = output.getUri();
|
||||||
|
if (uri == null) {
|
||||||
|
throw new FileNotFoundException("Export file not found.");
|
||||||
|
}
|
||||||
|
outputStream = context.getContentResolver().openOutputStream(uri);
|
||||||
|
if (outputStream == null) {
|
||||||
|
throw new IOException();
|
||||||
|
}
|
||||||
|
writer = new OutputStreamWriter(outputStream, LangUtils.UTF_8);
|
||||||
|
exportWriter.writeDocument(DBReader.getFeedList(), writer);
|
||||||
|
subscriber.onNext(output);
|
||||||
|
} catch (IOException e) {
|
||||||
|
subscriber.onError(e);
|
||||||
|
} finally {
|
||||||
|
if (writer != null) {
|
||||||
|
try {
|
||||||
|
writer.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
subscriber.onError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (outputStream != null) {
|
||||||
|
try {
|
||||||
|
outputStream.close();
|
||||||
|
} catch (IOException e) {
|
||||||
|
subscriber.onError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
subscriber.onComplete();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -35,6 +35,6 @@ public class PlaybackServiceCallbacksImpl implements PlaybackServiceCallbacks {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getNotificationIconResource(Context context) {
|
public int getNotificationIconResource(Context context) {
|
||||||
return R.drawable.ic_stat_antenna_default;
|
return R.drawable.ic_antenna;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -87,11 +87,10 @@ public class ChaptersFragment extends ListFragment {
|
|||||||
controller = null;
|
controller = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void scrollTo(int position) {
|
|
||||||
getListView().setSelection(position);
|
|
||||||
}
|
|
||||||
|
|
||||||
private int getCurrentChapter(Playable media) {
|
private int getCurrentChapter(Playable media) {
|
||||||
|
if (media == null || media.getChapters() == null || media.getChapters().size() == 0 || controller == null) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
int currentPosition = controller.getPosition();
|
int currentPosition = controller.getPosition();
|
||||||
|
|
||||||
List<Chapter> chapters = media.getChapters();
|
List<Chapter> chapters = media.getChapters();
|
||||||
@ -126,8 +125,10 @@ public class ChaptersFragment extends ListFragment {
|
|||||||
if (adapter != null) {
|
if (adapter != null) {
|
||||||
adapter.setMedia(media);
|
adapter.setMedia(media);
|
||||||
adapter.notifyDataSetChanged();
|
adapter.notifyDataSetChanged();
|
||||||
if (media != null && media.getChapters() != null && media.getChapters().size() != 0) {
|
|
||||||
scrollTo(getCurrentChapter(media));
|
int positionOfCurrentChapter = getCurrentChapter(media);
|
||||||
|
if (positionOfCurrentChapter != -1) {
|
||||||
|
getListView().setSelection(positionOfCurrentChapter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -149,7 +149,7 @@ public abstract class EpisodesListFragment extends Fragment {
|
|||||||
SharedPreferences.Editor editor = prefs.edit();
|
SharedPreferences.Editor editor = prefs.edit();
|
||||||
editor.putInt(PREF_SCROLL_POSITION, firstItem);
|
editor.putInt(PREF_SCROLL_POSITION, firstItem);
|
||||||
editor.putFloat(PREF_SCROLL_OFFSET, topOffset);
|
editor.putFloat(PREF_SCROLL_OFFSET, topOffset);
|
||||||
editor.commit();
|
editor.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void restoreScrollPosition() {
|
private void restoreScrollPosition() {
|
||||||
@ -162,7 +162,7 @@ public abstract class EpisodesListFragment extends Fragment {
|
|||||||
SharedPreferences.Editor editor = prefs.edit();
|
SharedPreferences.Editor editor = prefs.edit();
|
||||||
editor.putInt(PREF_SCROLL_POSITION, 0);
|
editor.putInt(PREF_SCROLL_POSITION, 0);
|
||||||
editor.putFloat(PREF_SCROLL_OFFSET, 0.0f);
|
editor.putFloat(PREF_SCROLL_OFFSET, 0.0f);
|
||||||
editor.commit();
|
editor.apply();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,7 +17,6 @@ import android.view.Menu;
|
|||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
|
||||||
import android.widget.AdapterView;
|
import android.widget.AdapterView;
|
||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
@ -140,13 +139,6 @@ public class FeedItemlistFragment extends ListFragment {
|
|||||||
feedID = args.getLong(ARGUMENT_FEED_ID);
|
feedID = args.getLong(ARGUMENT_FEED_ID);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
|
||||||
View view = super.onCreateView(inflater, container, savedInstanceState);
|
|
||||||
((ListView) view.findViewById(android.R.id.list)).setFastScrollEnabled(true);
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStart() {
|
public void onStart() {
|
||||||
super.onStart();
|
super.onStart();
|
||||||
|
@ -7,6 +7,7 @@ import android.os.Bundle;
|
|||||||
import android.support.design.widget.Snackbar;
|
import android.support.design.widget.Snackbar;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.Fragment;
|
||||||
import android.support.v4.view.MenuItemCompat;
|
import android.support.v4.view.MenuItemCompat;
|
||||||
|
import android.support.v7.app.AlertDialog;
|
||||||
import android.support.v7.widget.LinearLayoutManager;
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
import android.support.v7.widget.SearchView;
|
import android.support.v7.widget.SearchView;
|
||||||
@ -19,6 +20,8 @@ import android.view.MenuInflater;
|
|||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.CheckBox;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
import android.widget.ProgressBar;
|
import android.widget.ProgressBar;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
@ -91,10 +94,12 @@ public class QueueFragment extends Fragment {
|
|||||||
private static final String PREFS = "QueueFragment";
|
private static final String PREFS = "QueueFragment";
|
||||||
private static final String PREF_SCROLL_POSITION = "scroll_position";
|
private static final String PREF_SCROLL_POSITION = "scroll_position";
|
||||||
private static final String PREF_SCROLL_OFFSET = "scroll_offset";
|
private static final String PREF_SCROLL_OFFSET = "scroll_offset";
|
||||||
|
private static final String PREF_SHOW_LOCK_WARNING = "show_lock_warning";
|
||||||
|
|
||||||
private Disposable disposable;
|
private Disposable disposable;
|
||||||
private LinearLayoutManager layoutManager;
|
private LinearLayoutManager layoutManager;
|
||||||
private ItemTouchHelper itemTouchHelper;
|
private ItemTouchHelper itemTouchHelper;
|
||||||
|
private SharedPreferences prefs;
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -102,6 +107,7 @@ public class QueueFragment extends Fragment {
|
|||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
setRetainInstance(true);
|
setRetainInstance(true);
|
||||||
setHasOptionsMenu(true);
|
setHasOptionsMenu(true);
|
||||||
|
prefs = getActivity().getSharedPreferences(PREFS, Context.MODE_PRIVATE);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -219,15 +225,13 @@ public class QueueFragment extends Fragment {
|
|||||||
topOffset = firstItemView.getTop();
|
topOffset = firstItemView.getTop();
|
||||||
}
|
}
|
||||||
|
|
||||||
SharedPreferences prefs = getActivity().getSharedPreferences(PREFS, Context.MODE_PRIVATE);
|
prefs.edit()
|
||||||
SharedPreferences.Editor editor = prefs.edit();
|
.putInt(PREF_SCROLL_POSITION, firstItem)
|
||||||
editor.putInt(PREF_SCROLL_POSITION, firstItem);
|
.putFloat(PREF_SCROLL_OFFSET, topOffset)
|
||||||
editor.putFloat(PREF_SCROLL_OFFSET, topOffset);
|
.apply();
|
||||||
editor.commit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void restoreScrollPosition() {
|
private void restoreScrollPosition() {
|
||||||
SharedPreferences prefs = getActivity().getSharedPreferences(PREFS, Context.MODE_PRIVATE);
|
|
||||||
int position = prefs.getInt(PREF_SCROLL_POSITION, 0);
|
int position = prefs.getInt(PREF_SCROLL_POSITION, 0);
|
||||||
float offset = prefs.getFloat(PREF_SCROLL_OFFSET, 0.0f);
|
float offset = prefs.getFloat(PREF_SCROLL_OFFSET, 0.0f);
|
||||||
if (position > 0 || offset > 0) {
|
if (position > 0 || offset > 0) {
|
||||||
@ -299,19 +303,7 @@ public class QueueFragment extends Fragment {
|
|||||||
if (!super.onOptionsItemSelected(item)) {
|
if (!super.onOptionsItemSelected(item)) {
|
||||||
switch (item.getItemId()) {
|
switch (item.getItemId()) {
|
||||||
case R.id.queue_lock:
|
case R.id.queue_lock:
|
||||||
boolean newLockState = !UserPreferences.isQueueLocked();
|
toggleQueueLock();
|
||||||
UserPreferences.setQueueLocked(newLockState);
|
|
||||||
getActivity().supportInvalidateOptionsMenu();
|
|
||||||
if (recyclerAdapter != null) {
|
|
||||||
recyclerAdapter.setLocked(newLockState);
|
|
||||||
}
|
|
||||||
if (newLockState) {
|
|
||||||
Snackbar.make(getActivity().findViewById(R.id.content), R.string
|
|
||||||
.queue_locked, Snackbar.LENGTH_SHORT).show();
|
|
||||||
} else {
|
|
||||||
Snackbar.make(getActivity().findViewById(R.id.content), R.string
|
|
||||||
.queue_unlocked, Snackbar.LENGTH_SHORT).show();
|
|
||||||
}
|
|
||||||
return true;
|
return true;
|
||||||
case R.id.refresh_item:
|
case R.id.refresh_item:
|
||||||
List<Feed> feeds = ((MainActivity) getActivity()).getFeeds();
|
List<Feed> feeds = ((MainActivity) getActivity()).getFeeds();
|
||||||
@ -394,6 +386,48 @@ public class QueueFragment extends Fragment {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void toggleQueueLock() {
|
||||||
|
boolean isLocked = UserPreferences.isQueueLocked();
|
||||||
|
if (isLocked) {
|
||||||
|
setQueueLocked(false);
|
||||||
|
} else {
|
||||||
|
boolean shouldShowLockWarning = prefs.getBoolean(PREF_SHOW_LOCK_WARNING, true);
|
||||||
|
if (!shouldShowLockWarning) {
|
||||||
|
setQueueLocked(true);
|
||||||
|
} else {
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
|
||||||
|
builder.setTitle(R.string.lock_queue);
|
||||||
|
builder.setMessage(R.string.queue_lock_warning);
|
||||||
|
|
||||||
|
View view = View.inflate(getContext(), R.layout.checkbox_do_not_show_again, null);
|
||||||
|
CheckBox checkDoNotShowAgain = view.findViewById(R.id.checkbox_do_not_show_again);
|
||||||
|
builder.setView(view);
|
||||||
|
|
||||||
|
builder.setPositiveButton(R.string.lock_queue, (dialog, which) -> {
|
||||||
|
prefs.edit().putBoolean(PREF_SHOW_LOCK_WARNING, !checkDoNotShowAgain.isChecked()).apply();
|
||||||
|
setQueueLocked(true);
|
||||||
|
});
|
||||||
|
builder.setNegativeButton(R.string.cancel_label, null);
|
||||||
|
builder.show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setQueueLocked(boolean locked) {
|
||||||
|
UserPreferences.setQueueLocked(locked);
|
||||||
|
getActivity().supportInvalidateOptionsMenu();
|
||||||
|
if (recyclerAdapter != null) {
|
||||||
|
recyclerAdapter.setLocked(locked);
|
||||||
|
}
|
||||||
|
if (locked) {
|
||||||
|
Snackbar.make(getActivity().findViewById(R.id.content), R.string
|
||||||
|
.queue_locked, Snackbar.LENGTH_SHORT).show();
|
||||||
|
} else {
|
||||||
|
Snackbar.make(getActivity().findViewById(R.id.content), R.string
|
||||||
|
.queue_unlocked, Snackbar.LENGTH_SHORT).show();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is called if the user clicks on a sort order menu item.
|
* This method is called if the user clicks on a sort order menu item.
|
||||||
*
|
*
|
||||||
|
@ -4,6 +4,7 @@ import android.Manifest;
|
|||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.app.ProgressDialog;
|
import android.app.ProgressDialog;
|
||||||
|
import android.content.ActivityNotFoundException;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
@ -13,13 +14,16 @@ import android.os.Build;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v4.app.ActivityCompat;
|
import android.support.v4.app.ActivityCompat;
|
||||||
import android.support.v4.content.FileProvider;
|
import android.support.v4.content.FileProvider;
|
||||||
|
import android.support.v4.provider.DocumentFile;
|
||||||
import android.support.v7.app.AlertDialog;
|
import android.support.v7.app.AlertDialog;
|
||||||
import android.support.v7.preference.PreferenceFragmentCompat;
|
import android.support.v7.preference.PreferenceFragmentCompat;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import de.danoeh.antennapod.R;
|
import de.danoeh.antennapod.R;
|
||||||
import de.danoeh.antennapod.activity.DirectoryChooserActivity;
|
import de.danoeh.antennapod.activity.DirectoryChooserActivity;
|
||||||
import de.danoeh.antennapod.activity.ImportExportActivity;
|
import de.danoeh.antennapod.activity.ImportExportActivity;
|
||||||
import de.danoeh.antennapod.activity.OpmlImportFromPathActivity;
|
import de.danoeh.antennapod.activity.OpmlImportFromPathActivity;
|
||||||
|
import de.danoeh.antennapod.asynctask.DocumentFileExportWorker;
|
||||||
import de.danoeh.antennapod.asynctask.ExportWorker;
|
import de.danoeh.antennapod.asynctask.ExportWorker;
|
||||||
import de.danoeh.antennapod.core.export.ExportWriter;
|
import de.danoeh.antennapod.core.export.ExportWriter;
|
||||||
import de.danoeh.antennapod.core.export.html.HtmlWriter;
|
import de.danoeh.antennapod.core.export.html.HtmlWriter;
|
||||||
@ -45,6 +49,12 @@ public class StoragePreferencesFragment extends PreferenceFragmentCompat {
|
|||||||
Manifest.permission.READ_EXTERNAL_STORAGE,
|
Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||||
Manifest.permission.WRITE_EXTERNAL_STORAGE };
|
Manifest.permission.WRITE_EXTERNAL_STORAGE };
|
||||||
private static final int PERMISSION_REQUEST_EXTERNAL_STORAGE = 41;
|
private static final int PERMISSION_REQUEST_EXTERNAL_STORAGE = 41;
|
||||||
|
private static final int CHOOSE_OPML_EXPORT_PATH = 1;
|
||||||
|
private static final String DEFAULT_OPML_OUTPUT_NAME = "antennapod-feeds.opml";
|
||||||
|
private static final String CONTENT_TYPE_OPML = "text/x-opml";
|
||||||
|
private static final int CHOOSE_HTML_EXPORT_PATH = 2;
|
||||||
|
private static final String DEFAULT_HTML_OUTPUT_NAME = "antennapod-feeds.html";
|
||||||
|
private static final String CONTENT_TYPE_HTML = "text/html";
|
||||||
private Disposable disposable;
|
private Disposable disposable;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -59,6 +69,14 @@ public class StoragePreferencesFragment extends PreferenceFragmentCompat {
|
|||||||
setDataFolderText();
|
setDataFolderText();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStop() {
|
||||||
|
super.onStop();
|
||||||
|
if (disposable != null) {
|
||||||
|
disposable.dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void setupStorageScreen() {
|
private void setupStorageScreen() {
|
||||||
final Activity activity = getActivity();
|
final Activity activity = getActivity();
|
||||||
|
|
||||||
@ -69,9 +87,16 @@ public class StoragePreferencesFragment extends PreferenceFragmentCompat {
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
findPreference(PREF_OPML_EXPORT).setOnPreferenceClickListener(
|
findPreference(PREF_OPML_EXPORT).setOnPreferenceClickListener(
|
||||||
preference -> export(new OpmlWriter()));
|
preference -> {
|
||||||
|
openOpmlExportPathPicker();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
);
|
||||||
findPreference(PREF_HTML_EXPORT).setOnPreferenceClickListener(
|
findPreference(PREF_HTML_EXPORT).setOnPreferenceClickListener(
|
||||||
preference -> export(new HtmlWriter()));
|
preference -> {
|
||||||
|
openHtmlExportPathPicker();
|
||||||
|
return true;
|
||||||
|
});
|
||||||
findPreference(PREF_OPML_IMPORT).setOnPreferenceClickListener(
|
findPreference(PREF_OPML_IMPORT).setOnPreferenceClickListener(
|
||||||
preference -> {
|
preference -> {
|
||||||
activity.startActivity(new Intent(activity, OpmlImportFromPathActivity.class));
|
activity.startActivity(new Intent(activity, OpmlImportFromPathActivity.class));
|
||||||
@ -129,52 +154,65 @@ public class StoragePreferencesFragment extends PreferenceFragmentCompat {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private boolean export(ExportWriter exportWriter) {
|
private boolean export(ExportWriter exportWriter) {
|
||||||
|
return export(exportWriter, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean export(ExportWriter exportWriter, final Uri uri) {
|
||||||
Context context = getActivity();
|
Context context = getActivity();
|
||||||
final ProgressDialog progressDialog = new ProgressDialog(context);
|
final ProgressDialog progressDialog = new ProgressDialog(context);
|
||||||
progressDialog.setMessage(context.getString(R.string.exporting_label));
|
progressDialog.setMessage(context.getString(R.string.exporting_label));
|
||||||
progressDialog.setIndeterminate(true);
|
progressDialog.setIndeterminate(true);
|
||||||
progressDialog.show();
|
progressDialog.show();
|
||||||
final AlertDialog.Builder alert = new AlertDialog.Builder(context)
|
if (uri == null) {
|
||||||
.setNeutralButton(android.R.string.ok, (dialog, which) -> dialog.dismiss());
|
Observable<File> observable = new ExportWorker(exportWriter).exportObservable();
|
||||||
Observable<File> observable = new ExportWorker(exportWriter).exportObservable();
|
disposable = observable.subscribeOn(Schedulers.io())
|
||||||
disposable = observable.subscribeOn(Schedulers.io())
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
.observeOn(AndroidSchedulers.mainThread())
|
.subscribe(output -> {
|
||||||
.subscribe(output -> {
|
|
||||||
alert.setTitle(R.string.export_success_title);
|
|
||||||
String message = context.getString(R.string.export_success_sum, output.toString());
|
|
||||||
alert.setMessage(message);
|
|
||||||
alert.setPositiveButton(R.string.send_label, (dialog, which) -> {
|
|
||||||
Uri fileUri = FileProvider.getUriForFile(context.getApplicationContext(),
|
Uri fileUri = FileProvider.getUriForFile(context.getApplicationContext(),
|
||||||
context.getString(R.string.provider_authority), output);
|
context.getString(R.string.provider_authority), output);
|
||||||
Intent sendIntent = new Intent(Intent.ACTION_SEND);
|
showExportSuccessDialog(context.getString(R.string.export_success_sum, output.toString()), fileUri);
|
||||||
sendIntent.putExtra(Intent.EXTRA_SUBJECT,
|
}, this::showExportErrorDialog, progressDialog::dismiss);
|
||||||
context.getResources().getText(R.string.opml_export_label));
|
} else {
|
||||||
sendIntent.putExtra(Intent.EXTRA_STREAM, fileUri);
|
Observable<DocumentFile> observable = new DocumentFileExportWorker(exportWriter, context, uri).exportObservable();
|
||||||
sendIntent.setType("text/plain");
|
disposable = observable.subscribeOn(Schedulers.io())
|
||||||
sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
|
.subscribe(output -> {
|
||||||
List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(sendIntent, PackageManager.MATCH_DEFAULT_ONLY);
|
showExportSuccessDialog(context.getString(R.string.export_success_sum, output.getUri()), output.getUri());
|
||||||
for (ResolveInfo resolveInfo : resInfoList) {
|
}, this::showExportErrorDialog, progressDialog::dismiss);
|
||||||
String packageName = resolveInfo.activityInfo.packageName;
|
}
|
||||||
context.grantUriPermission(packageName, fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
context.startActivity(Intent.createChooser(sendIntent,
|
|
||||||
context.getResources().getText(R.string.send_label)));
|
|
||||||
});
|
|
||||||
alert.create().show();
|
|
||||||
}, error -> {
|
|
||||||
alert.setTitle(R.string.export_error_label);
|
|
||||||
alert.setMessage(error.getMessage());
|
|
||||||
alert.show();
|
|
||||||
}, progressDialog::dismiss);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unsubscribeExportSubscription() {
|
private void showExportSuccessDialog(final String message, final Uri streamUri) {
|
||||||
if (disposable != null) {
|
final AlertDialog.Builder alert = new AlertDialog.Builder(getContext())
|
||||||
disposable.dispose();
|
.setNeutralButton(android.R.string.ok, (dialog, which) -> dialog.dismiss());
|
||||||
}
|
alert.setTitle(R.string.export_success_title);
|
||||||
|
alert.setMessage(message);
|
||||||
|
alert.setPositiveButton(R.string.send_label, (dialog, which) -> {
|
||||||
|
Intent sendIntent = new Intent(Intent.ACTION_SEND);
|
||||||
|
sendIntent.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.opml_export_label));
|
||||||
|
sendIntent.putExtra(Intent.EXTRA_STREAM, streamUri);
|
||||||
|
sendIntent.setType("text/plain");
|
||||||
|
sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||||
|
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
|
||||||
|
List<ResolveInfo> resInfoList = getContext().getPackageManager()
|
||||||
|
.queryIntentActivities(sendIntent, PackageManager.MATCH_DEFAULT_ONLY);
|
||||||
|
for (ResolveInfo resolveInfo : resInfoList) {
|
||||||
|
String packageName = resolveInfo.activityInfo.packageName;
|
||||||
|
getContext().grantUriPermission(packageName, streamUri, Intent.FLAG_GRANT_READ_URI_PERMISSION);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
getContext().startActivity(Intent.createChooser(sendIntent, getString(R.string.send_label)));
|
||||||
|
});
|
||||||
|
alert.create().show();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showExportErrorDialog(final Throwable error) {
|
||||||
|
final AlertDialog.Builder alert = new AlertDialog.Builder(getContext())
|
||||||
|
.setNeutralButton(android.R.string.ok, (dialog, which) -> dialog.dismiss());
|
||||||
|
alert.setTitle(R.string.export_error_label);
|
||||||
|
alert.setMessage(error.getMessage());
|
||||||
|
alert.show();
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressLint("NewApi")
|
@SuppressLint("NewApi")
|
||||||
@ -184,22 +222,22 @@ public class StoragePreferencesFragment extends PreferenceFragmentCompat {
|
|||||||
String dir = data.getStringExtra(DirectoryChooserActivity.RESULT_SELECTED_DIR);
|
String dir = data.getStringExtra(DirectoryChooserActivity.RESULT_SELECTED_DIR);
|
||||||
|
|
||||||
File path;
|
File path;
|
||||||
if(dir != null) {
|
if (dir != null) {
|
||||||
path = new File(dir);
|
path = new File(dir);
|
||||||
} else {
|
} else {
|
||||||
path = getActivity().getExternalFilesDir(null);
|
path = getActivity().getExternalFilesDir(null);
|
||||||
}
|
}
|
||||||
String message = null;
|
String message = null;
|
||||||
final Context context= getActivity().getApplicationContext();
|
final Context context = getActivity().getApplicationContext();
|
||||||
if(!path.exists()) {
|
if (!path.exists()) {
|
||||||
message = String.format(context.getString(R.string.folder_does_not_exist_error), dir);
|
message = String.format(context.getString(R.string.folder_does_not_exist_error), dir);
|
||||||
} else if(!path.canRead()) {
|
} else if (!path.canRead()) {
|
||||||
message = String.format(context.getString(R.string.folder_not_readable_error), dir);
|
message = String.format(context.getString(R.string.folder_not_readable_error), dir);
|
||||||
} else if(!path.canWrite()) {
|
} else if (!path.canWrite()) {
|
||||||
message = String.format(context.getString(R.string.folder_not_writable_error), dir);
|
message = String.format(context.getString(R.string.folder_not_writable_error), dir);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(message == null) {
|
if (message == null) {
|
||||||
Log.d(TAG, "Setting data folder: " + dir);
|
Log.d(TAG, "Setting data folder: " + dir);
|
||||||
UserPreferences.setDataFolder(dir);
|
UserPreferences.setDataFolder(dir);
|
||||||
setDataFolderText();
|
setDataFolderText();
|
||||||
@ -210,6 +248,16 @@ public class StoragePreferencesFragment extends PreferenceFragmentCompat {
|
|||||||
ab.show();
|
ab.show();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (resultCode == Activity.RESULT_OK && requestCode == CHOOSE_OPML_EXPORT_PATH) {
|
||||||
|
Uri uri = data.getData();
|
||||||
|
export(new OpmlWriter(), uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (resultCode == Activity.RESULT_OK && requestCode == CHOOSE_HTML_EXPORT_PATH) {
|
||||||
|
Uri uri = data.getData();
|
||||||
|
export(new HtmlWriter(), uri);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setDataFolderText() {
|
private void setDataFolderText() {
|
||||||
@ -231,6 +279,50 @@ public class StoragePreferencesFragment extends PreferenceFragmentCompat {
|
|||||||
activity.startActivityForResult(intent, DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED);
|
activity.startActivityForResult(intent, DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void openOpmlExportPathPicker() {
|
||||||
|
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2) {
|
||||||
|
Intent intentPickAction = new Intent(Intent.ACTION_CREATE_DOCUMENT)
|
||||||
|
.addCategory(Intent.CATEGORY_OPENABLE)
|
||||||
|
.setType(CONTENT_TYPE_OPML)
|
||||||
|
.putExtra(Intent.EXTRA_TITLE, DEFAULT_OPML_OUTPUT_NAME);
|
||||||
|
|
||||||
|
// Creates an implicit intent to launch a file manager which lets
|
||||||
|
// the user choose a specific directory to export to.
|
||||||
|
try {
|
||||||
|
startActivityForResult(intentPickAction, CHOOSE_OPML_EXPORT_PATH);
|
||||||
|
return;
|
||||||
|
} catch (ActivityNotFoundException e) {
|
||||||
|
Log.e(TAG, "No activity found. Should never happen...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are using a SDK lower than API 21 or the implicit intent failed
|
||||||
|
// fallback to the legacy export process
|
||||||
|
export(new OpmlWriter());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void openHtmlExportPathPicker() {
|
||||||
|
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2) {
|
||||||
|
Intent intentPickAction = new Intent(Intent.ACTION_CREATE_DOCUMENT)
|
||||||
|
.addCategory(Intent.CATEGORY_OPENABLE)
|
||||||
|
.setType(CONTENT_TYPE_HTML)
|
||||||
|
.putExtra(Intent.EXTRA_TITLE, DEFAULT_HTML_OUTPUT_NAME);
|
||||||
|
|
||||||
|
// Creates an implicit intent to launch a file manager which lets
|
||||||
|
// the user choose a specific directory to export to.
|
||||||
|
try {
|
||||||
|
startActivityForResult(intentPickAction, CHOOSE_HTML_EXPORT_PATH);
|
||||||
|
return;
|
||||||
|
} catch (ActivityNotFoundException e) {
|
||||||
|
Log.e(TAG, "No activity found. Should never happen...");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we are using a SDK lower than API 21 or the implicit intent failed
|
||||||
|
// fallback to the legacy export process
|
||||||
|
export(new HtmlWriter());
|
||||||
|
}
|
||||||
|
|
||||||
private void showChooseDataFolderDialog() {
|
private void showChooseDataFolderDialog() {
|
||||||
ChooseDataFolderDialog.showDialog(
|
ChooseDataFolderDialog.showDialog(
|
||||||
getActivity(), new ChooseDataFolderDialog.RunnableWithString() {
|
getActivity(), new ChooseDataFolderDialog.RunnableWithString() {
|
||||||
|
@ -43,10 +43,8 @@ public class PreferenceUpgrader {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (oldVersion < 1070300) {
|
if (oldVersion < 1070300) {
|
||||||
if (UserPreferences.getMediaPlayer().equals("builtin")) {
|
prefs.edit().putString(UserPreferences.PREF_MEDIA_PLAYER,
|
||||||
prefs.edit().putString(UserPreferences.PREF_MEDIA_PLAYER,
|
UserPreferences.PREF_MEDIA_PLAYER_EXOPLAYER).apply();
|
||||||
UserPreferences.PREF_MEDIA_PLAYER_EXOPLAYER).apply();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prefs.getBoolean("prefEnableAutoDownloadOnMobile", false)) {
|
if (prefs.getBoolean("prefEnableAutoDownloadOnMobile", false)) {
|
||||||
UserPreferences.setAllowMobileAutoDownload(true);
|
UserPreferences.setAllowMobileAutoDownload(true);
|
||||||
@ -70,6 +68,8 @@ public class PreferenceUpgrader {
|
|||||||
if (theme == R.style.Theme_AntennaPod_Light) {
|
if (theme == R.style.Theme_AntennaPod_Light) {
|
||||||
prefs.edit().putString(UserPreferences.PREF_THEME, "system").apply();
|
prefs.edit().putString(UserPreferences.PREF_THEME, "system").apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UserPreferences.setQueueLocked(false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,7 @@ public class SPAUtil {
|
|||||||
|
|
||||||
SharedPreferences.Editor editor = prefs.edit();
|
SharedPreferences.Editor editor = prefs.edit();
|
||||||
editor.putBoolean(PREF_HAS_QUERIED_SP_APPS, true);
|
editor.putBoolean(PREF_HAS_QUERIED_SP_APPS, true);
|
||||||
editor.commit();
|
editor.apply();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
@ -63,7 +63,7 @@ public class SPAUtil {
|
|||||||
SharedPreferences.Editor editor = PreferenceManager
|
SharedPreferences.Editor editor = PreferenceManager
|
||||||
.getDefaultSharedPreferences(c.getApplicationContext()).edit();
|
.getDefaultSharedPreferences(c.getApplicationContext()).edit();
|
||||||
editor.putBoolean(PREF_HAS_QUERIED_SP_APPS, false);
|
editor.putBoolean(PREF_HAS_QUERIED_SP_APPS, false);
|
||||||
editor.commit();
|
editor.apply();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
17
app/src/main/res/layout/checkbox_do_not_show_again.xml
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingLeft="16dp"
|
||||||
|
android:paddingRight="16dp"
|
||||||
|
android:paddingTop="8dp"
|
||||||
|
android:paddingBottom="8dp">
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/checkbox_do_not_show_again"
|
||||||
|
android:text="@string/checkbox_do_not_show_again"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
@ -17,7 +17,7 @@
|
|||||||
android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
|
android:layout_marginTop="@dimen/listitem_threeline_verticalpadding"
|
||||||
android:contentDescription="@string/cover_label"
|
android:contentDescription="@string/cover_label"
|
||||||
android:scaleType="centerCrop"
|
android:scaleType="centerCrop"
|
||||||
tools:src="@drawable/ic_stat_antenna_default"
|
tools:src="@drawable/ic_antenna"
|
||||||
tools:background="@android:color/holo_green_dark"/>
|
tools:background="@android:color/holo_green_dark"/>
|
||||||
|
|
||||||
|
|
||||||
|
@ -36,7 +36,8 @@
|
|||||||
android:layout_marginBottom="16dp"
|
android:layout_marginBottom="16dp"
|
||||||
android:contentDescription="@string/cover_label"
|
android:contentDescription="@string/cover_label"
|
||||||
android:gravity="center_vertical"
|
android:gravity="center_vertical"
|
||||||
tools:src="@drawable/ic_stat_antenna_default"
|
android:foreground="?attr/selectableItemBackground"
|
||||||
|
tools:src="@drawable/ic_antenna"
|
||||||
tools:background="@android:color/holo_green_dark" />
|
tools:background="@android:color/holo_green_dark" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
@ -47,6 +48,7 @@
|
|||||||
android:layout_alignTop="@id/imgvCover"
|
android:layout_alignTop="@id/imgvCover"
|
||||||
android:layout_toRightOf="@id/imgvCover"
|
android:layout_toRightOf="@id/imgvCover"
|
||||||
android:layout_toEndOf="@id/imgvCover"
|
android:layout_toEndOf="@id/imgvCover"
|
||||||
|
android:foreground="?attr/selectableItemBackground"
|
||||||
tools:text="Podcast title"
|
tools:text="Podcast title"
|
||||||
tools:background="@android:color/holo_green_dark" />
|
tools:background="@android:color/holo_green_dark" />
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@
|
|||||||
android:layout_marginStart="16dp"
|
android:layout_marginStart="16dp"
|
||||||
android:layout_marginTop="16dp"
|
android:layout_marginTop="16dp"
|
||||||
android:contentDescription="@string/cover_label"
|
android:contentDescription="@string/cover_label"
|
||||||
tools:src="@drawable/ic_stat_antenna_default"
|
tools:src="@drawable/ic_antenna"
|
||||||
tools:background="@android:color/holo_green_dark"/>
|
tools:background="@android:color/holo_green_dark"/>
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
android:contentDescription="@string/cover_label"
|
android:contentDescription="@string/cover_label"
|
||||||
android:cropToPadding="true"
|
android:cropToPadding="true"
|
||||||
android:scaleType="fitXY"
|
android:scaleType="fitXY"
|
||||||
tools:src="@drawable/ic_stat_antenna_default"
|
tools:src="@drawable/ic_antenna"
|
||||||
tools:background="@android:color/holo_green_dark" />
|
tools:background="@android:color/holo_green_dark" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
android:cropToPadding="true"
|
android:cropToPadding="true"
|
||||||
android:scaleType="fitXY"
|
android:scaleType="fitXY"
|
||||||
tools:background="@android:color/holo_green_dark"
|
tools:background="@android:color/holo_green_dark"
|
||||||
tools:src="@drawable/ic_stat_antenna_default" />
|
tools:src="@drawable/ic_antenna" />
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
@ -25,7 +25,7 @@
|
|||||||
android:scaleType="centerCrop"
|
android:scaleType="centerCrop"
|
||||||
android:layout_marginTop="4dp"
|
android:layout_marginTop="4dp"
|
||||||
android:layout_marginBottom="4dp"
|
android:layout_marginBottom="4dp"
|
||||||
tools:src="@drawable/ic_stat_antenna_default"
|
tools:src="@drawable/ic_antenna"
|
||||||
tools:background="@android:color/holo_green_dark"/>
|
tools:background="@android:color/holo_green_dark"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
@ -51,7 +51,7 @@
|
|||||||
android:layout_height="@dimen/thumbnail_length_queue_item"
|
android:layout_height="@dimen/thumbnail_length_queue_item"
|
||||||
android:layout_centerVertical="true"
|
android:layout_centerVertical="true"
|
||||||
android:contentDescription="@string/cover_label"
|
android:contentDescription="@string/cover_label"
|
||||||
tools:src="@drawable/ic_stat_antenna_default"
|
tools:src="@drawable/ic_antenna"
|
||||||
tools:background="@android:color/holo_green_dark"/>
|
tools:background="@android:color/holo_green_dark"/>
|
||||||
</RelativeLayout>
|
</RelativeLayout>
|
||||||
|
|
||||||
|
@ -18,7 +18,7 @@
|
|||||||
android:layout_marginStart="@dimen/listitem_threeline_horizontalpadding"
|
android:layout_marginStart="@dimen/listitem_threeline_horizontalpadding"
|
||||||
android:contentDescription="@string/cover_label"
|
android:contentDescription="@string/cover_label"
|
||||||
android:scaleType="centerCrop"
|
android:scaleType="centerCrop"
|
||||||
tools:src="@drawable/ic_stat_antenna_default"
|
tools:src="@drawable/ic_antenna"
|
||||||
tools:background="@android:color/holo_green_dark"/>
|
tools:background="@android:color/holo_green_dark"/>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
android:scaleType="centerCrop"
|
android:scaleType="centerCrop"
|
||||||
android:layout_marginTop="4dp"
|
android:layout_marginTop="4dp"
|
||||||
android:layout_marginBottom="4dp"
|
android:layout_marginBottom="4dp"
|
||||||
tools:src="@drawable/ic_stat_antenna_default"
|
tools:src="@drawable/ic_antenna"
|
||||||
tools:background="@android:color/holo_green_dark"/>
|
tools:background="@android:color/holo_green_dark"/>
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
@ -9,6 +9,11 @@
|
|||||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||||
<uses-permission android:name="android.permission.BLUETOOTH" />
|
<uses-permission android:name="android.permission.BLUETOOTH" />
|
||||||
<uses-permission android:name="android.permission.VIBRATE" />
|
<uses-permission android:name="android.permission.VIBRATE" />
|
||||||
|
<!-- ACCESS_FINE_LOCATION is needed only on Android 10+,
|
||||||
|
for Automatic Download Wifi filter's UI, which uses
|
||||||
|
WifiManager.WifiManager.getConfiguredNetworks()
|
||||||
|
-->
|
||||||
|
<uses-permission-sdk-23 android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
|
@ -1,23 +0,0 @@
|
|||||||
package de.danoeh.antennapod.core.event;
|
|
||||||
|
|
||||||
import de.danoeh.antennapod.core.feed.FeedMedia;
|
|
||||||
|
|
||||||
public class FeedMediaEvent {
|
|
||||||
|
|
||||||
public enum Action {
|
|
||||||
UPDATE
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Action action;
|
|
||||||
private final FeedMedia media;
|
|
||||||
|
|
||||||
private FeedMediaEvent(Action action, FeedMedia media) {
|
|
||||||
this.action = action;
|
|
||||||
this.media = media;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static FeedMediaEvent update(FeedMedia media) {
|
|
||||||
return new FeedMediaEvent(Action.UPDATE, media);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -895,13 +895,6 @@ public class UserPreferences {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Reads episode cache size as it is saved in the episode_cache_size_values array.
|
|
||||||
*/
|
|
||||||
public static int readEpisodeCacheSize(String valueFromPrefs) {
|
|
||||||
return readEpisodeCacheSizeInternal(valueFromPrefs);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Evaluates whether Cast support (Chromecast, Audio Cast, etc) is enabled on the preferences.
|
* Evaluates whether Cast support (Chromecast, Audio Cast, etc) is enabled on the preferences.
|
||||||
*/
|
*/
|
||||||
|
@ -23,7 +23,9 @@ import com.bumptech.glide.request.RequestOptions;
|
|||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import de.danoeh.antennapod.core.R;
|
import de.danoeh.antennapod.core.R;
|
||||||
|
import de.danoeh.antennapod.core.feed.MediaType;
|
||||||
import de.danoeh.antennapod.core.glide.ApGlideSettings;
|
import de.danoeh.antennapod.core.glide.ApGlideSettings;
|
||||||
|
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
||||||
import de.danoeh.antennapod.core.receiver.MediaButtonReceiver;
|
import de.danoeh.antennapod.core.receiver.MediaButtonReceiver;
|
||||||
import de.danoeh.antennapod.core.receiver.PlayerWidget;
|
import de.danoeh.antennapod.core.receiver.PlayerWidget;
|
||||||
import de.danoeh.antennapod.core.service.playback.PlaybackService;
|
import de.danoeh.antennapod.core.service.playback.PlaybackService;
|
||||||
@ -69,9 +71,7 @@ public class PlayerWidgetJobService extends SafeJobIntentService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
synchronized (waitUsingService) {
|
synchronized (waitUsingService) {
|
||||||
if (playbackService != null) {
|
updateViews();
|
||||||
updateViews();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (playbackService != null) {
|
if (playbackService != null) {
|
||||||
@ -145,9 +145,12 @@ public class PlayerWidgetJobService extends SafeJobIntentService {
|
|||||||
|
|
||||||
String progressString;
|
String progressString;
|
||||||
if (playbackService != null) {
|
if (playbackService != null) {
|
||||||
progressString = getProgressString(playbackService.getCurrentPosition(), playbackService.getDuration());
|
progressString = getProgressString(playbackService.getCurrentPosition(),
|
||||||
|
playbackService.getDuration(), playbackService.getCurrentPlaybackSpeed());
|
||||||
} else {
|
} else {
|
||||||
progressString = getProgressString(media.getPosition(), media.getDuration());
|
float speed = media.getMediaType() == MediaType.VIDEO ?
|
||||||
|
UserPreferences.getVideoPlaybackSpeed() : UserPreferences.getPlaybackSpeed();
|
||||||
|
progressString = getProgressString(media.getPosition(), media.getDuration(), speed);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (progressString != null) {
|
if (progressString != null) {
|
||||||
@ -211,9 +214,9 @@ public class PlayerWidgetJobService extends SafeJobIntentService {
|
|||||||
return PendingIntent.getBroadcast(this, 0, startingIntent, 0);
|
return PendingIntent.getBroadcast(this, 0, startingIntent, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getProgressString(int position, int duration) {
|
private String getProgressString(int position, int duration, float speed) {
|
||||||
if (position > 0 && duration > 0) {
|
if (position > 0 && duration > 0) {
|
||||||
TimeSpeedConverter converter = new TimeSpeedConverter(playbackService.getCurrentPlaybackSpeed());
|
TimeSpeedConverter converter = new TimeSpeedConverter(speed);
|
||||||
position = converter.convert(position);
|
position = converter.convert(position);
|
||||||
duration = converter.convert(duration);
|
duration = converter.convert(duration);
|
||||||
return Converter.getDurationStringLong(position) + " / "
|
return Converter.getDurationStringLong(position) + " / "
|
||||||
|
@ -546,7 +546,7 @@ public class DownloadService extends Service {
|
|||||||
.setContentText(getText(R.string.authentication_notification_msg))
|
.setContentText(getText(R.string.authentication_notification_msg))
|
||||||
.setStyle(new NotificationCompat.BigTextStyle().bigText(getText(R.string.authentication_notification_msg)
|
.setStyle(new NotificationCompat.BigTextStyle().bigText(getText(R.string.authentication_notification_msg)
|
||||||
+ ": " + resourceTitle))
|
+ ": " + resourceTitle))
|
||||||
.setSmallIcon(R.drawable.ic_stat_authentication)
|
.setSmallIcon(R.drawable.ic_notification_key)
|
||||||
.setAutoCancel(true)
|
.setAutoCancel(true)
|
||||||
.setContentIntent(ClientConfig.downloadServiceCallbacks.getAuthentificationNotificationContentIntent(DownloadService.this, downloadRequest));
|
.setContentIntent(ClientConfig.downloadServiceCallbacks.getAuthentificationNotificationContentIntent(DownloadService.this, downloadRequest));
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
|
@ -193,10 +193,6 @@ public class DownloadStatus {
|
|||||||
this.cancelled = true;
|
this.cancelled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setCompletionDate(Date completionDate) {
|
|
||||||
this.completionDate = (Date) completionDate.clone();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setId(long id) {
|
public void setId(long id) {
|
||||||
this.id = id;
|
this.id = id;
|
||||||
}
|
}
|
||||||
|
@ -12,6 +12,7 @@ import android.util.Log;
|
|||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
import android.view.SurfaceHolder;
|
import android.view.SurfaceHolder;
|
||||||
|
|
||||||
|
import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter;
|
||||||
import org.antennapod.audio.MediaPlayer;
|
import org.antennapod.audio.MediaPlayer;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
@ -705,7 +706,6 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void shutdown() {
|
public void shutdown() {
|
||||||
abandonAudioFocus();
|
|
||||||
executor.shutdown();
|
executor.shutdown();
|
||||||
if (mediaPlayer != null) {
|
if (mediaPlayer != null) {
|
||||||
try {
|
try {
|
||||||
@ -839,6 +839,19 @@ public class LocalPSMP extends PlaybackServiceMediaPlayer {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAudioFocusChange(final int focusChange) {
|
public void onAudioFocusChange(final int focusChange) {
|
||||||
|
if (!PlaybackService.isRunning) {
|
||||||
|
abandonAudioFocus();
|
||||||
|
Log.d(TAG, "onAudioFocusChange: PlaybackService is no longer running");
|
||||||
|
if (focusChange == AudioManager.AUDIOFOCUS_GAIN && pausedBecauseOfTransientAudiofocusLoss) {
|
||||||
|
new PlaybackServiceStarter(context, getPlayable())
|
||||||
|
.startWhenPrepared(true)
|
||||||
|
.streamIfLastWasStream()
|
||||||
|
.callEvenIfRunning(false)
|
||||||
|
.start();
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
executor.submit(() -> {
|
executor.submit(() -> {
|
||||||
playerLock.lock();
|
playerLock.lock();
|
||||||
|
|
||||||
|
@ -1086,7 +1086,7 @@ public class PlaybackService extends MediaBrowserServiceCompat {
|
|||||||
editor.putInt(
|
editor.putInt(
|
||||||
PlaybackPreferences.PREF_CURRENT_PLAYER_STATUS, playerStatus);
|
PlaybackPreferences.PREF_CURRENT_PLAYER_STATUS, playerStatus);
|
||||||
|
|
||||||
editor.commit();
|
editor.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writePlayerStatusPlaybackPreferences() {
|
private void writePlayerStatusPlaybackPreferences() {
|
||||||
@ -1095,11 +1095,8 @@ public class PlaybackService extends MediaBrowserServiceCompat {
|
|||||||
SharedPreferences.Editor editor = PreferenceManager
|
SharedPreferences.Editor editor = PreferenceManager
|
||||||
.getDefaultSharedPreferences(getApplicationContext()).edit();
|
.getDefaultSharedPreferences(getApplicationContext()).edit();
|
||||||
int playerStatus = getCurrentPlayerStatusAsInt(mediaPlayer.getPlayerStatus());
|
int playerStatus = getCurrentPlayerStatusAsInt(mediaPlayer.getPlayerStatus());
|
||||||
|
editor.putInt(PlaybackPreferences.PREF_CURRENT_PLAYER_STATUS, playerStatus);
|
||||||
editor.putInt(
|
editor.apply();
|
||||||
PlaybackPreferences.PREF_CURRENT_PLAYER_STATUS, playerStatus);
|
|
||||||
|
|
||||||
editor.commit();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void sendNotificationBroadcast(int type, int code) {
|
private void sendNotificationBroadcast(int type, int code) {
|
||||||
|
@ -193,11 +193,6 @@ public final class DBTasks {
|
|||||||
}).start();
|
}).start();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static long getLastRefreshAllFeedsTimeMillis(final Context context) {
|
|
||||||
SharedPreferences prefs = context.getSharedPreferences(DBTasks.PREF_NAME, MODE_PRIVATE);
|
|
||||||
return prefs.getLong(DBTasks.PREF_LAST_REFRESH, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param context
|
* @param context
|
||||||
* @param feedList the list of feeds to refresh
|
* @param feedList the list of feeds to refresh
|
||||||
|
@ -34,7 +34,6 @@ import de.danoeh.antennapod.core.util.URLChecker;
|
|||||||
public class DownloadRequester {
|
public class DownloadRequester {
|
||||||
private static final String TAG = "DownloadRequester";
|
private static final String TAG = "DownloadRequester";
|
||||||
|
|
||||||
public static final String IMAGE_DOWNLOADPATH = "images/";
|
|
||||||
private static final String FEED_DOWNLOADPATH = "cache/";
|
private static final String FEED_DOWNLOADPATH = "cache/";
|
||||||
private static final String MEDIA_DOWNLOADPATH = "media/";
|
private static final String MEDIA_DOWNLOADPATH = "media/";
|
||||||
|
|
||||||
@ -274,10 +273,6 @@ public class DownloadRequester {
|
|||||||
return item.getDownload_url() != null && downloads.containsKey(item.getDownload_url());
|
return item.getDownload_url() != null && downloads.containsKey(item.getDownload_url());
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized DownloadRequest getDownload(String downloadUrl) {
|
|
||||||
return downloads.get(downloadUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if feedfile with the given download url is in the downloads list
|
* Checks if feedfile with the given download url is in the downloads list
|
||||||
*/
|
*/
|
||||||
|
@ -55,16 +55,10 @@ public class PodDBAdapter {
|
|||||||
*/
|
*/
|
||||||
private static final int IN_OPERATOR_MAXIMUM = 800;
|
private static final int IN_OPERATOR_MAXIMUM = 800;
|
||||||
|
|
||||||
/**
|
|
||||||
* Maximum number of entries per search request.
|
|
||||||
*/
|
|
||||||
public static final int SEARCH_LIMIT = 30;
|
|
||||||
|
|
||||||
// Key-constants
|
// Key-constants
|
||||||
public static final String KEY_ID = "id";
|
public static final String KEY_ID = "id";
|
||||||
public static final String KEY_TITLE = "title";
|
public static final String KEY_TITLE = "title";
|
||||||
public static final String KEY_CUSTOM_TITLE = "custom_title";
|
public static final String KEY_CUSTOM_TITLE = "custom_title";
|
||||||
public static final String KEY_NAME = "name";
|
|
||||||
public static final String KEY_LINK = "link";
|
public static final String KEY_LINK = "link";
|
||||||
public static final String KEY_DESCRIPTION = "description";
|
public static final String KEY_DESCRIPTION = "description";
|
||||||
public static final String KEY_FILE_URL = "file_url";
|
public static final String KEY_FILE_URL = "file_url";
|
||||||
@ -1400,13 +1394,6 @@ public class PodDBAdapter {
|
|||||||
return db.rawQuery(query, null);
|
return db.rawQuery(query, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static final int IDX_FEEDSTATISTICS_FEED = 0;
|
|
||||||
public static final int IDX_FEEDSTATISTICS_NUM_ITEMS = 1;
|
|
||||||
public static final int IDX_FEEDSTATISTICS_NEW_ITEMS = 2;
|
|
||||||
public static final int IDX_FEEDSTATISTICS_LATEST_EPISODE = 3;
|
|
||||||
public static final int IDX_FEEDSTATISTICS_IN_PROGRESS_EPISODES = 4;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Select number of items, new items, the date of the latest episode and the number of episodes in progress. The result
|
* Select number of items, new items, the date of the latest episode and the number of episodes in progress. The result
|
||||||
* is sorted by the title of the feed.
|
* is sorted by the title of the feed.
|
||||||
|
@ -1,117 +0,0 @@
|
|||||||
/* Adapted from: http://thinking-in-code.blogspot.com/2008/11/duck-typing-in-java-using-dynamic.html */
|
|
||||||
|
|
||||||
package de.danoeh.antennapod.core.util;
|
|
||||||
|
|
||||||
import java.lang.reflect.InvocationHandler;
|
|
||||||
import java.lang.reflect.Method;
|
|
||||||
import java.lang.reflect.Proxy;
|
|
||||||
|
|
||||||
import de.danoeh.antennapod.core.BuildConfig;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Allows "duck typing" or dynamic invocation based on method signature rather
|
|
||||||
* than type hierarchy. In other words, rather than checking whether something
|
|
||||||
* IS-a duck, check whether it WALKS-like-a duck or QUACKS-like a duck.
|
|
||||||
*
|
|
||||||
* To use first use the coerce static method to indicate the object you want to
|
|
||||||
* do Duck Typing for, then specify an interface to the to method which you want
|
|
||||||
* to coerce the type to, e.g:
|
|
||||||
*
|
|
||||||
* public interface Foo { void aMethod(); } class Bar { ... public void
|
|
||||||
* aMethod() { ... } ... } Bar bar = ...; Foo foo =
|
|
||||||
* DuckType.coerce(bar).to(Foo.class); foo.aMethod();
|
|
||||||
*
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
public class DuckType {
|
|
||||||
|
|
||||||
private final Object objectToCoerce;
|
|
||||||
|
|
||||||
private DuckType(Object objectToCoerce) {
|
|
||||||
this.objectToCoerce = objectToCoerce;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class CoercedProxy implements InvocationHandler {
|
|
||||||
@Override
|
|
||||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
|
||||||
Method delegateMethod = findMethodBySignature(method);
|
|
||||||
assert delegateMethod != null;
|
|
||||||
return delegateMethod.invoke(DuckType.this.objectToCoerce, args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specify the duck typed object to coerce.
|
|
||||||
*
|
|
||||||
* @param object
|
|
||||||
* the object to coerce
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public static DuckType coerce(Object object) {
|
|
||||||
return new DuckType(object);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Coerce the Duck Typed object to the given interface providing it
|
|
||||||
* implements all the necessary methods.
|
|
||||||
*
|
|
||||||
* @param
|
|
||||||
* @param iface
|
|
||||||
* @return an instance of the given interface that wraps the duck typed
|
|
||||||
* class
|
|
||||||
* @throws ClassCastException
|
|
||||||
* if the object being coerced does not implement all the
|
|
||||||
* methods in the given interface.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings({ "rawtypes", "unchecked" })
|
|
||||||
public <T> T to(Class iface) {
|
|
||||||
if (BuildConfig.DEBUG && !iface.isInterface()) throw new AssertionError("cannot coerce object to a class, must be an interface");
|
|
||||||
if (isA(iface)) {
|
|
||||||
return (T) iface.cast(objectToCoerce);
|
|
||||||
}
|
|
||||||
if (quacksLikeA(iface)) {
|
|
||||||
return generateProxy(iface);
|
|
||||||
}
|
|
||||||
throw new ClassCastException("Could not coerce object of type " + objectToCoerce.getClass() + " to " + iface);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
private boolean isA(Class iface) {
|
|
||||||
return objectToCoerce.getClass().isInstance(iface);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Determine whether the duck typed object can be used with the given
|
|
||||||
* interface.
|
|
||||||
*
|
|
||||||
* @param Type
|
|
||||||
* of the interface to check.
|
|
||||||
* @param iface
|
|
||||||
* Interface class to check
|
|
||||||
* @return true if the object will support all the methods in the interface,
|
|
||||||
* false otherwise.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings("rawtypes")
|
|
||||||
private boolean quacksLikeA(Class iface) {
|
|
||||||
for (Method method : iface.getMethods()) {
|
|
||||||
if (findMethodBySignature(method) == null) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings({ "unchecked", "rawtypes" })
|
|
||||||
private <T> T generateProxy(Class iface) {
|
|
||||||
return (T) Proxy.newProxyInstance(iface.getClassLoader(), new Class[] { iface }, new CoercedProxy());
|
|
||||||
}
|
|
||||||
|
|
||||||
private Method findMethodBySignature(Method method) {
|
|
||||||
try {
|
|
||||||
return objectToCoerce.getClass().getMethod(method.getName(), method.getParameterTypes());
|
|
||||||
} catch (NoSuchMethodException e) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -7,19 +7,6 @@ import de.danoeh.antennapod.core.feed.FeedItem;
|
|||||||
public class FeedItemUtil {
|
public class FeedItemUtil {
|
||||||
private FeedItemUtil(){}
|
private FeedItemUtil(){}
|
||||||
|
|
||||||
public static int indexOfItemWithDownloadUrl(List<FeedItem> items, String downloadUrl) {
|
|
||||||
if(items == null) {
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
for(int i=0; i < items.size(); i++) {
|
|
||||||
FeedItem item = items.get(i);
|
|
||||||
if(item.hasMedia() && item.getMedia().getDownload_url().equals(downloadUrl)) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static int indexOfItemWithId(List<FeedItem> items, long id) {
|
public static int indexOfItemWithId(List<FeedItem> items, long id) {
|
||||||
for(int i=0; i < items.size(); i++) {
|
for(int i=0; i < items.size(); i++) {
|
||||||
FeedItem item = items.get(i);
|
FeedItem item = items.get(i);
|
||||||
@ -40,17 +27,6 @@ public class FeedItemUtil {
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static long[] getIds(FeedItem... items) {
|
|
||||||
if(items == null || items.length == 0) {
|
|
||||||
return new long[0];
|
|
||||||
}
|
|
||||||
long[] result = new long[items.length];
|
|
||||||
for(int i=0; i < items.length; i++) {
|
|
||||||
result[i] = items[i].getId();
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static long[] getIds(List<FeedItem> items) {
|
public static long[] getIds(List<FeedItem> items) {
|
||||||
if(items == null || items.size() == 0) {
|
if(items == null || items.size() == 0) {
|
||||||
return new long[0];
|
return new long[0];
|
||||||
@ -62,20 +38,6 @@ public class FeedItemUtil {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean containsAnyId(List<FeedItem> items, long[] ids) {
|
|
||||||
if(items == null || items.size() == 0) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
for(FeedItem item : items) {
|
|
||||||
for(long id : ids) {
|
|
||||||
if(item.getId() == id) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Get the link for the feed item for the purpose of Share. It fallbacks to
|
* Get the link for the feed item for the purpose of Share. It fallbacks to
|
||||||
* use the feed's link if the named feed item has no link.
|
* use the feed's link if the named feed item has no link.
|
||||||
|
@ -1,15 +0,0 @@
|
|||||||
package de.danoeh.antennapod.core.util;
|
|
||||||
|
|
||||||
import java.util.Comparator;
|
|
||||||
|
|
||||||
import de.danoeh.antennapod.core.feed.Feed;
|
|
||||||
|
|
||||||
/** Compares the title of two feeds for sorting. */
|
|
||||||
class FeedtitleComparator implements Comparator<Feed> {
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int compare(Feed lhs, Feed rhs) {
|
|
||||||
return lhs.getTitle().compareToIgnoreCase(rhs.getTitle());
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -3,30 +3,11 @@ package de.danoeh.antennapod.core.util;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.annotation.AttrRes;
|
import android.support.annotation.AttrRes;
|
||||||
import android.support.annotation.ColorInt;
|
import android.support.annotation.ColorInt;
|
||||||
import android.util.Log;
|
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
|
|
||||||
import de.danoeh.antennapod.core.R;
|
|
||||||
import de.danoeh.antennapod.core.preferences.UserPreferences;
|
|
||||||
|
|
||||||
public class ThemeUtils {
|
public class ThemeUtils {
|
||||||
private ThemeUtils(){}
|
private ThemeUtils() {
|
||||||
|
|
||||||
private static final String TAG = "ThemeUtils";
|
|
||||||
|
|
||||||
public static int getSelectionBackgroundColor() {
|
|
||||||
int theme = UserPreferences.getTheme();
|
|
||||||
if (theme == R.style.Theme_AntennaPod_Dark) {
|
|
||||||
return R.color.selection_background_color_dark;
|
|
||||||
} else if (theme == R.style.Theme_AntennaPod_TrueBlack){
|
|
||||||
return R.color.selection_background_color_trueblack;
|
|
||||||
} else if (theme == R.style.Theme_AntennaPod_Light) {
|
|
||||||
return R.color.selection_background_color_light;
|
|
||||||
} else {
|
|
||||||
Log.e(TAG,
|
|
||||||
"getSelectionBackgroundColor could not match the current theme to any color!");
|
|
||||||
return R.color.selection_background_color_light;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static @ColorInt int getColorFromAttr(Context context, @AttrRes int attr) {
|
public static @ColorInt int getColorFromAttr(Context context, @AttrRes int attr) {
|
||||||
|
@ -1,27 +0,0 @@
|
|||||||
package de.danoeh.antennapod.core.util.comparator;
|
|
||||||
|
|
||||||
import java.util.Comparator;
|
|
||||||
|
|
||||||
import de.danoeh.antennapod.core.feed.FeedItem;
|
|
||||||
import de.danoeh.antennapod.core.feed.SearchResult;
|
|
||||||
|
|
||||||
public class SearchResultValueComparator implements Comparator<SearchResult> {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Compare items based, first, on where they were found (ie. title, chapters, or show notes).
|
|
||||||
* If they were found in the same section, then compare based on the title, in lexicographic
|
|
||||||
* order. This is still not ideal since, for example, "#12 Example A" would be considered
|
|
||||||
* before "#8 Example B" due to the fact that "8" has a larger unicode value than "1"
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public int compare(SearchResult lhs, SearchResult rhs) {
|
|
||||||
int value = rhs.getValue() - lhs.getValue();
|
|
||||||
if (value == 0 && lhs.getComponent() instanceof FeedItem && rhs.getComponent() instanceof FeedItem) {
|
|
||||||
String lhsTitle = ((FeedItem) lhs.getComponent()).getTitle();
|
|
||||||
String rhsTitle = ((FeedItem) rhs.getComponent()).getTitle();
|
|
||||||
return lhsTitle.compareTo(rhsTitle);
|
|
||||||
}
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -182,7 +182,7 @@ public class ExternalMedia implements Playable {
|
|||||||
editor.putLong(PREF_LAST_PLAYED_TIME, timestamp);
|
editor.putLong(PREF_LAST_PLAYED_TIME, timestamp);
|
||||||
position = newPosition;
|
position = newPosition;
|
||||||
lastPlayedTime = timestamp;
|
lastPlayedTime = timestamp;
|
||||||
editor.commit();
|
editor.apply();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -728,6 +728,8 @@ public class PlaybackController {
|
|||||||
public void setPlaybackSpeed(float speed) {
|
public void setPlaybackSpeed(float speed) {
|
||||||
if (playbackService != null) {
|
if (playbackService != null) {
|
||||||
playbackService.setSpeed(speed);
|
playbackService.setSpeed(speed);
|
||||||
|
} else {
|
||||||
|
onPlaybackSpeedChange();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
public void setSkipSilence(boolean skipSilence) {
|
public void setSkipSilence(boolean skipSilence) {
|
||||||
|
Before Width: | Height: | Size: 770 B |
Before Width: | Height: | Size: 975 B |
Before Width: | Height: | Size: 867 B |
Before Width: | Height: | Size: 961 B |
Before Width: | Height: | Size: 979 B |
Before Width: | Height: | Size: 976 B |
Before Width: | Height: | Size: 982 B |
Before Width: | Height: | Size: 493 B |
Before Width: | Height: | Size: 191 B |
Before Width: | Height: | Size: 562 B |
Before Width: | Height: | Size: 541 B |
Before Width: | Height: | Size: 371 B |
Before Width: | Height: | Size: 259 B |
Before Width: | Height: | Size: 536 B |
Before Width: | Height: | Size: 693 B |
Before Width: | Height: | Size: 635 B |
Before Width: | Height: | Size: 684 B |
Before Width: | Height: | Size: 696 B |
Before Width: | Height: | Size: 690 B |
Before Width: | Height: | Size: 694 B |
Before Width: | Height: | Size: 379 B |
Before Width: | Height: | Size: 280 B |
Before Width: | Height: | Size: 447 B |
Before Width: | Height: | Size: 349 B |
Before Width: | Height: | Size: 244 B |
Before Width: | Height: | Size: 976 B |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 1.3 KiB |
Before Width: | Height: | Size: 526 B |
Before Width: | Height: | Size: 221 B |
Before Width: | Height: | Size: 678 B |
Before Width: | Height: | Size: 783 B |
Before Width: | Height: | Size: 418 B |
Before Width: | Height: | Size: 1.4 KiB |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 673 B |
Before Width: | Height: | Size: 317 B |
Before Width: | Height: | Size: 955 B |
Before Width: | Height: | Size: 848 B |
Before Width: | Height: | Size: 310 B After Width: | Height: | Size: 310 B |
Before Width: | Height: | Size: 805 B |
Before Width: | Height: | Size: 400 B |
Before Width: | Height: | Size: 1.2 KiB |
6
core/src/main/res/drawable/ic_antenna.xml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<vector android:height="24dp" android:viewportHeight="12.7"
|
||||||
|
android:viewportWidth="12.7" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillAlpha="1" android:fillColor="#ffffff"
|
||||||
|
android:pathData="m6.0631,0.4728v0.3274c1.1582,0.0249 1.911,0.4225 2.5991,1.1189 0.6881,0.6964 1.0924,1.7043 1.1125,2.9246h0.3211c0.0078,-1.3792 -0.5291,-2.4905 -1.1981,-3.1576C8.2288,1.019 7.3415,0.4734 6.0631,0.4728ZM6.0631,1.4283v0.3453c0.859,0.0361 1.3465,0.2123 1.9398,0.8081 0.5933,0.5957 0.843,1.3669 0.8598,2.2621L9.2029,4.8438c-0.0088,-1.2333 -0.5414,-2.0907 -0.9568,-2.5047 -0.4154,-0.4139 -0.9948,-0.9065 -2.183,-0.9108zM6.0625,2.4323 L6.0631,2.7495c0.3968,0.007 0.8308,0.1395 1.2089,0.5642 0.3781,0.4247 0.495,1.0244 0.51,1.53h0.3255c-0.0016,-0.669 -0.2787,-1.3891 -0.6153,-1.747 -0.3366,-0.358 -0.7368,-0.6621 -1.4298,-0.6645zM6.0906,3.7766c-0.4059,0.0002 -0.7349,0.3294 -0.7347,0.7353 0.0001,0.2677 0.1459,0.5142 0.3804,0.6434l-3.0102,6.2227 0.5151,0.3351 0.607,-1.2485 5.3821,1.5453 0.083,0.1609 0.5732,-0.2508 -3.4927,-6.7397c0.2624,-0.1189 0.4311,-0.3802 0.4315,-0.6683 0.0002,-0.4059 -0.3287,-0.7352 -0.7347,-0.7353zM6.065,5.8631 L6.5929,6.8882 5.2882,7.4761zM6.6976,7.0918 L7.6065,8.8561 5.137,7.8016zM5.0259,8.0199 L7.611,9.1184 4.0314,10.0854zM7.8395,9.3086 L9.081,11.7201 4.1489,10.3069z"
|
||||||
|
android:strokeAlpha="1" android:strokeColor="#00000000" android:strokeWidth="0.32680494"/>
|
||||||
|
</vector>
|