diff options
Diffstat (limited to 'app/src/main/java/de/danoeh')
96 files changed, 3164 insertions, 2484 deletions
diff --git a/app/src/main/java/de/danoeh/antennapod/PodcastApp.java b/app/src/main/java/de/danoeh/antennapod/PodcastApp.java index f6a8db5fb..fde9af16f 100644 --- a/app/src/main/java/de/danoeh/antennapod/PodcastApp.java +++ b/app/src/main/java/de/danoeh/antennapod/PodcastApp.java @@ -20,7 +20,7 @@ public class PodcastApp extends Application { try { Class.forName("de.danoeh.antennapod.config.ClientConfigurator"); } catch (Exception e) { - throw new RuntimeException("ClientConfigurator not found"); + throw new RuntimeException("ClientConfigurator not found", e); } } @@ -41,10 +41,8 @@ public class PodcastApp extends Application { .detectLeakedSqlLiteObjects() .penaltyLog() .penaltyDropBox(); - if (Build.VERSION.SDK_INT >= 11) { - builder.detectActivityLeaks(); - builder.detectLeakedClosableObjects(); - } + builder.detectActivityLeaks(); + builder.detectLeakedClosableObjects(); if(Build.VERSION.SDK_INT >= 16) { builder.detectLeakedRegistrationObjects(); } diff --git a/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java index 1b42b274c..ecfdf24b0 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java @@ -21,11 +21,10 @@ import java.nio.charset.Charset; import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.preferences.UserPreferences; -import rx.Observable; -import rx.Subscriber; -import rx.Subscription; -import rx.android.schedulers.AndroidSchedulers; -import rx.schedulers.Schedulers; +import io.reactivex.Single; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; /** * Displays the 'about' screen @@ -34,11 +33,9 @@ public class AboutActivity extends AppCompatActivity { private static final String TAG = AboutActivity.class.getSimpleName(); - private WebView webview; - private LinearLayout webviewContainer; - private int depth = 0; - - private Subscription subscription; + private WebView webView; + private LinearLayout webViewContainer; + private Disposable disposable; @Override protected void onCreate(Bundle savedInstanceState) { @@ -46,28 +43,25 @@ public class AboutActivity extends AppCompatActivity { super.onCreate(savedInstanceState); getSupportActionBar().setDisplayShowHomeEnabled(true); setContentView(R.layout.about); - webviewContainer = (LinearLayout) findViewById(R.id.webvContainer); - webview = (WebView) findViewById(R.id.webvAbout); - webview.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE); + webViewContainer = findViewById(R.id.webViewContainer); + webView = findViewById(R.id.webViewAbout); + webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE); 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) { - webview.setLayerType(View.LAYER_TYPE_SOFTWARE, null); + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) { + webView.setLayerType(View.LAYER_TYPE_SOFTWARE, null); } - webview.setBackgroundColor(Color.TRANSPARENT); + webView.setBackgroundColor(Color.TRANSPARENT); } - webview.setWebViewClient(new WebViewClient() { + webView.setWebViewClient(new WebViewClient() { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { - if(url.startsWith("http")) { - depth++; - return false; - } else { + if (!url.startsWith("http")) { url = url.replace("file:///android_asset/", ""); loadAsset(url); return true; } + return false; } }); @@ -75,69 +69,65 @@ public class AboutActivity extends AppCompatActivity { } private void loadAsset(String filename) { - subscription = Observable.create(new Observable.OnSubscribe<String>() { - @Override - public void call(Subscriber<? super String> subscriber) { - InputStream input = null; - try { - TypedArray res = AboutActivity.this.getTheme().obtainStyledAttributes( - new int[] { android.R.attr.textColorPrimary }); - int colorResource = res.getColor(0, 0); - String colorString = String.format("#%06X", 0xFFFFFF & colorResource); - res.recycle(); - input = getAssets().open(filename); - String webViewData = IOUtils.toString(input, Charset.defaultCharset()); - if(!webViewData.startsWith("<!DOCTYPE html>")) { - //webViewData = webViewData.replace("\n\n", "</p><p>"); - webViewData = webViewData.replace("%", "%"); - webViewData = - "<!DOCTYPE html>" + - "<html>" + - "<head>" + - " <meta http-equiv=\"Content-Type\" content=\"text/html;charset=UTF-8\">" + - " <style type=\"text/css\">" + - " @font-face {" + - " font-family: 'Roboto-Light';" + - " src: url('file:///android_asset/Roboto-Light.ttf');" + - " }" + - " * {" + - " color: %s;" + - " font-family: roboto-Light;" + - " font-size: 8pt;" + - " }" + - " </style>" + - "</head><body><p>" + webViewData + "</p></body></html>"; - webViewData = webViewData.replace("\n", "<br/>"); - depth++; - } else { - depth = 0; - } - webViewData = String.format(webViewData, colorString); - subscriber.onNext(webViewData); - } catch (IOException e) { - subscriber.onError(e); - } finally { - IOUtils.closeQuietly(input); - } - subscriber.onCompleted(); - } - }) - .subscribeOn(Schedulers.newThread()) + disposable = Single.create(subscriber -> { + InputStream input = null; + try { + TypedArray res = AboutActivity.this.getTheme().obtainStyledAttributes( + new int[] { R.attr.about_screen_font_color, R.attr.about_screen_background, + R.attr.about_screen_card_background, R.attr.about_screen_card_border}); + String fontColor = String.format("#%06X", 0xFFFFFF & res.getColor(0, 0)); + String backgroundColor = String.format("#%06X", 0xFFFFFF & res.getColor(1, 0)); + String cardBackground = String.format("#%06X", 0xFFFFFF & res.getColor(2, 0)); + String cardBorder = String.format("#%06X", 0xFFFFFF & res.getColor(3, 0)); + res.recycle(); + input = getAssets().open(filename); + String webViewData = IOUtils.toString(input, Charset.defaultCharset()); + if (!webViewData.startsWith("<!DOCTYPE html>")) { + webViewData = webViewData.replace("%", "%"); + webViewData = + "<!DOCTYPE html>" + + "<html>" + + "<head>" + + " <meta http-equiv=\"Content-Type\" content=\"text/html;charset=UTF-8\">" + + " <style type=\"text/css\">" + + " @font-face {" + + " font-family: 'Roboto-Light';" + + " src: url('file:///android_asset/Roboto-Light.ttf');" + + " }" + + " * {" + + " color: @fontcolor@;" + + " font-family: roboto-Light;" + + " font-size: 8pt;" + + " }" + + " </style>" + + "</head><body><p>" + webViewData + "</p></body></html>"; + webViewData = webViewData.replace("\n", "<br/>"); + } + webViewData = webViewData.replace("@fontcolor@", fontColor); + webViewData = webViewData.replace("@background@", backgroundColor); + webViewData = webViewData.replace("@card_background@", cardBackground); + webViewData = webViewData.replace("@card_border@", cardBorder); + subscriber.onSuccess(webViewData); + } catch (IOException e) { + Log.e(TAG, Log.getStackTraceString(e)); + subscriber.onError(e); + } finally { + IOUtils.closeQuietly(input); + } + }) + .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( - webviewData -> - webview.loadDataWithBaseURL("file:///android_asset/", webviewData, "text/html", "utf-8", "about:blank"), + webViewData -> + webView.loadDataWithBaseURL("file:///android_asset/", webViewData.toString(), "text/html", "utf-8", "file:///android_asset/" + filename.toString()), error -> Log.e(TAG, Log.getStackTraceString(error)) ); } @Override public void onBackPressed() { - Log.d(TAG, "depth: " + depth); - if(depth == 1) { - loadAsset("about.html"); - } else if(depth > 1) { - webview.goBack(); + if (webView.canGoBack()) { + webView.goBack(); } else { super.onBackPressed(); } @@ -156,12 +146,12 @@ public class AboutActivity extends AppCompatActivity { @Override protected void onDestroy() { super.onDestroy(); - if(subscription != null) { - subscription.unsubscribe(); + if (disposable != null) { + disposable.dispose(); } - if (webviewContainer != null && webview != null) { - webviewContainer.removeAllViews(); - webview.destroy(); + if (webViewContainer != null && webView != null) { + webViewContainer.removeAllViews(); + webView.destroy(); } } } diff --git a/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java index ca214de9e..67dda01cf 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java @@ -6,41 +6,31 @@ import android.text.TextUtils; import android.util.Log; import android.view.View; +import java.text.DecimalFormat; import java.util.concurrent.atomic.AtomicBoolean; import de.danoeh.antennapod.core.feed.MediaType; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.service.playback.PlaybackService; -import de.danoeh.antennapod.core.util.playback.ExternalMedia; import de.danoeh.antennapod.dialog.VariableSpeedDialog; /** * Activity for playing audio files. */ public class AudioplayerActivity extends MediaplayerInfoActivity { - public static final String TAG = "AudioPlayerActivity"; + private static final String TAG = "AudioPlayerActivity"; - private AtomicBoolean isSetup = new AtomicBoolean(false); + private final AtomicBoolean isSetup = new AtomicBoolean(false); @Override protected void onResume() { super.onResume(); if (TextUtils.equals(getIntent().getAction(), Intent.ACTION_VIEW)) { - Intent intent = getIntent(); - Log.d(TAG, "Received VIEW intent: " + intent.getData().getPath()); - ExternalMedia media = new ExternalMedia(intent.getData().getPath(), - MediaType.AUDIO); - Intent launchIntent = new Intent(this, PlaybackService.class); - launchIntent.putExtra(PlaybackService.EXTRA_PLAYABLE, media); - launchIntent.putExtra(PlaybackService.EXTRA_START_WHEN_PREPARED, - true); - launchIntent.putExtra(PlaybackService.EXTRA_SHOULD_STREAM, false); - launchIntent.putExtra(PlaybackService.EXTRA_PREPARE_IMMEDIATELY, - true); - startService(launchIntent); + playExternalMedia(getIntent(), MediaType.AUDIO); } else if (PlaybackService.isCasting()) { Intent intent = PlaybackService.getPlayerActivityIntent(this); - if (!intent.getComponent().getClassName().equals(AudioplayerActivity.class.getName())) { + if (intent.getComponent() != null && + !intent.getComponent().getClassName().equals(AudioplayerActivity.class.getName())) { saveCurrentFragment(); finish(); startActivity(intent); @@ -95,7 +85,7 @@ public class AudioplayerActivity extends MediaplayerInfoActivity { UserPreferences.setPlaybackSpeed(String.valueOf(speed)); } } - String speedStr = String.format("%.2fx", speed); + String speedStr = new DecimalFormat("0.00x").format(speed); butPlaybackSpeed.setText(speedStr); } diff --git a/app/src/main/java/de/danoeh/antennapod/activity/CastplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/CastplayerActivity.java index a7e9b1e70..871e9c279 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/CastplayerActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/CastplayerActivity.java @@ -13,9 +13,9 @@ import de.danoeh.antennapod.core.service.playback.PlaybackService; * Activity for controlling the remote playback on a Cast device. */ public class CastplayerActivity extends MediaplayerInfoActivity { - public static final String TAG = "CastPlayerActivity"; + private static final String TAG = "CastPlayerActivity"; - private AtomicBoolean isSetup = new AtomicBoolean(false); + private final AtomicBoolean isSetup = new AtomicBoolean(false); @Override protected void onCreate(Bundle savedInstanceState) { diff --git a/app/src/main/java/de/danoeh/antennapod/activity/DirectoryChooserActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/DirectoryChooserActivity.java index 390d4cef8..33def125e 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/DirectoryChooserActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/DirectoryChooserActivity.java @@ -64,11 +64,11 @@ public class DirectoryChooserActivity extends AppCompatActivity { getSupportActionBar().setDisplayHomeAsUpEnabled(true); setContentView(R.layout.directory_chooser); - butConfirm = (Button) findViewById(R.id.butConfirm); - butCancel = (Button) findViewById(R.id.butCancel); - butNavUp = (ImageButton) findViewById(R.id.butNavUp); - txtvSelectedFolder = (TextView) findViewById(R.id.txtvSelectedFolder); - listDirectories = (ListView) findViewById(R.id.directory_list); + butConfirm = findViewById(R.id.butConfirm); + butCancel = findViewById(R.id.butCancel); + butNavUp = findViewById(R.id.butNavUp); + txtvSelectedFolder = findViewById(R.id.txtvSelectedFolder); + listDirectories = findViewById(R.id.directory_list); butConfirm.setOnClickListener(new OnClickListener() { diff --git a/app/src/main/java/de/danoeh/antennapod/activity/DownloadAuthenticationActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/DownloadAuthenticationActivity.java index 41b2debdc..5e04d743d 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/DownloadAuthenticationActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/DownloadAuthenticationActivity.java @@ -3,15 +3,14 @@ package de.danoeh.antennapod.activity; import android.app.Activity; import android.content.Intent; import android.os.Bundle; -import android.support.v7.app.ActionBarActivity; -import android.util.Log; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AppCompatActivity; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; import org.apache.commons.lang3.Validate; -import de.danoeh.antennapod.BuildConfig; import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.service.download.DownloadRequest; @@ -23,8 +22,7 @@ import de.danoeh.antennapod.core.storage.DownloadRequester; * Other arguments are optional. * The activity's result will be the same DownloadRequest with the entered username and password. */ -public class DownloadAuthenticationActivity extends ActionBarActivity { - private static final String TAG = "DownloadAuthenticationActivity"; +public class DownloadAuthenticationActivity extends AppCompatActivity { /** * The download request object that contains information about the resource that requires a username and a password @@ -36,47 +34,39 @@ public class DownloadAuthenticationActivity extends ActionBarActivity { */ public static final String ARG_SEND_TO_DOWNLOAD_REQUESTER_BOOL = "send_to_downloadrequester"; - public static final String RESULT_REQUEST = "request"; + private static final String RESULT_REQUEST = "request"; private EditText etxtUsername; private EditText etxtPassword; - private Button butConfirm; - private Button butCancel; - private TextView txtvDescription; - - private DownloadRequest request; - private boolean sendToDownloadRequester; @Override protected void onCreate(Bundle savedInstanceState) { setTheme(UserPreferences.getTheme()); super.onCreate(savedInstanceState); - getSupportActionBar().hide(); - setContentView(R.layout.download_authentication_activity); + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + actionBar.hide(); + } - etxtUsername = (EditText) findViewById(R.id.etxtUsername); - etxtPassword = (EditText) findViewById(R.id.etxtPassword); - butConfirm = (Button) findViewById(R.id.butConfirm); - butCancel = (Button) findViewById(R.id.butCancel); - txtvDescription = (TextView) findViewById(R.id.txtvDescription); + setContentView(R.layout.download_authentication_activity); + TextView txtvDescription = findViewById(R.id.txtvDescription); + etxtUsername = findViewById(R.id.etxtUsername); + etxtPassword = findViewById(R.id.etxtPassword); + Button butConfirm = findViewById(R.id.butConfirm); + Button butCancel = findViewById(R.id.butCancel); Validate.isTrue(getIntent().hasExtra(ARG_DOWNLOAD_REQUEST), "Download request missing"); + DownloadRequest request = getIntent().getParcelableExtra(ARG_DOWNLOAD_REQUEST); + boolean sendToDownloadRequester = getIntent().getBooleanExtra(ARG_SEND_TO_DOWNLOAD_REQUESTER_BOOL, false); - request = getIntent().getParcelableExtra(ARG_DOWNLOAD_REQUEST); - sendToDownloadRequester = getIntent().getBooleanExtra(ARG_SEND_TO_DOWNLOAD_REQUESTER_BOOL, false); + String newDescription = txtvDescription.getText() + ":\n\n" + request.getTitle(); + txtvDescription.setText(newDescription); if (savedInstanceState != null) { etxtUsername.setText(savedInstanceState.getString("username")); etxtPassword.setText(savedInstanceState.getString("password")); } - txtvDescription.setText(txtvDescription.getText() + ":\n\n" + request.getTitle()); - - butCancel.setOnClickListener(v -> { - setResult(Activity.RESULT_CANCELED); - finish(); - }); - butConfirm.setOnClickListener(v -> { String username = etxtUsername.getText().toString(); String password = etxtPassword.getText().toString(); @@ -87,11 +77,16 @@ public class DownloadAuthenticationActivity extends ActionBarActivity { setResult(Activity.RESULT_OK, result); if (sendToDownloadRequester) { - if (BuildConfig.DEBUG) Log.d(TAG, "Sending request to DownloadRequester"); DownloadRequester.getInstance().download(DownloadAuthenticationActivity.this, request); } finish(); }); + + butCancel.setOnClickListener(v -> { + setResult(Activity.RESULT_CANCELED); + finish(); + }); + } @Override diff --git a/app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java index 2a58d5104..bfa694e5c 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java @@ -2,30 +2,23 @@ package de.danoeh.antennapod.activity; import android.content.ClipData; import android.content.Context; -import android.content.DialogInterface; import android.content.Intent; +import android.graphics.LightingColorFilter; import android.net.Uri; import android.os.Bundle; import android.support.v7.app.AppCompatActivity; -import android.text.Editable; import android.text.TextUtils; -import android.text.TextWatcher; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; -import android.widget.AdapterView; -import android.widget.AdapterView.OnItemSelectedListener; -import android.widget.CheckBox; -import android.widget.EditText; import android.widget.ImageView; -import android.widget.RadioButton; -import android.widget.Spinner; import android.widget.TextView; import android.widget.Toast; import com.bumptech.glide.Glide; +import com.bumptech.glide.request.RequestOptions; import com.joanzapata.iconify.Iconify; import org.apache.commons.lang3.StringUtils; @@ -33,24 +26,22 @@ import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import de.danoeh.antennapod.R; -import de.danoeh.antennapod.core.dialog.ConfirmationDialog; import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator; import de.danoeh.antennapod.core.feed.Feed; -import de.danoeh.antennapod.core.feed.FeedFilter; -import de.danoeh.antennapod.core.feed.FeedPreferences; import de.danoeh.antennapod.core.glide.ApGlideSettings; +import de.danoeh.antennapod.core.glide.FastBlurTransformation; 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.DownloadRequestException; import de.danoeh.antennapod.core.util.IntentUtils; import de.danoeh.antennapod.core.util.LangUtils; import de.danoeh.antennapod.core.util.syndication.HtmlToPlainText; import de.danoeh.antennapod.menuhandler.FeedMenuHandler; -import rx.Observable; -import rx.Subscription; -import rx.android.schedulers.AndroidSchedulers; -import rx.schedulers.Schedulers; +import io.reactivex.Maybe; +import io.reactivex.MaybeOnSubscribe; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; /** * Displays information about a feed. @@ -59,7 +50,6 @@ public class FeedInfoActivity extends AppCompatActivity { public static final String EXTRA_FEED_ID = "de.danoeh.antennapod.extra.feedId"; private static final String TAG = "FeedInfoActivity"; - private boolean autoDeleteChanged = false; private Feed feed; private ImageView imgvCover; @@ -70,17 +60,8 @@ public class FeedInfoActivity extends AppCompatActivity { private TextView lblAuthor; private TextView txtvAuthor; private TextView txtvUrl; - private EditText etxtUsername; - private EditText etxtPassword; - private EditText etxtFilterText; - private RadioButton rdoFilterInclude; - private RadioButton rdoFilterExclude; - private CheckBox cbxAutoDownload; - private CheckBox cbxKeepUpdated; - private Spinner spnAutoDelete; - private boolean filterInclude = true; - private Subscription subscription; + private Disposable disposable; private final View.OnClickListener copyUrlToClipboard = new View.OnClickListener() { @@ -88,56 +69,16 @@ public class FeedInfoActivity extends AppCompatActivity { public void onClick(View v) { if(feed != null && feed.getDownload_url() != null) { String url = feed.getDownload_url(); - if (android.os.Build.VERSION.SDK_INT >= 11) { - ClipData clipData = ClipData.newPlainText(url, url); - android.content.ClipboardManager cm = (android.content.ClipboardManager) FeedInfoActivity.this - .getSystemService(Context.CLIPBOARD_SERVICE); - cm.setPrimaryClip(clipData); - } else { - android.text.ClipboardManager cm = (android.text.ClipboardManager) FeedInfoActivity.this - .getSystemService(Context.CLIPBOARD_SERVICE); - cm.setText(url); - } + ClipData clipData = ClipData.newPlainText(url, url); + android.content.ClipboardManager cm = (android.content.ClipboardManager) FeedInfoActivity.this + .getSystemService(Context.CLIPBOARD_SERVICE); + cm.setPrimaryClip(clipData); Toast t = Toast.makeText(FeedInfoActivity.this, R.string.copied_url_msg, Toast.LENGTH_SHORT); t.show(); } } }; - private boolean authInfoChanged = false; - - private TextWatcher authTextWatcher = 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) { - authInfoChanged = true; - } - }; - - private boolean filterTextChanged = false; - - private TextWatcher filterTextWatcher = 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) { - filterTextChanged = true; - } - }; - @Override protected void onCreate(Bundle savedInstanceState) { setTheme(UserPreferences.getTheme()); @@ -146,54 +87,58 @@ public class FeedInfoActivity extends AppCompatActivity { getSupportActionBar().setDisplayHomeAsUpEnabled(true); long feedId = getIntent().getLongExtra(EXTRA_FEED_ID, -1); - imgvCover = (ImageView) findViewById(R.id.imgvCover); - txtvTitle = (TextView) findViewById(R.id.txtvTitle); - txtvDescription = (TextView) findViewById(R.id.txtvDescription); - lblLanguage = (TextView) findViewById(R.id.lblLanguage); - txtvLanguage = (TextView) findViewById(R.id.txtvLanguage); - lblAuthor = (TextView) findViewById(R.id.lblAuthor); - txtvAuthor = (TextView) findViewById(R.id.txtvAuthor); - txtvUrl = (TextView) findViewById(R.id.txtvUrl); - cbxAutoDownload = (CheckBox) findViewById(R.id.cbxAutoDownload); - cbxKeepUpdated = (CheckBox) findViewById(R.id.cbxKeepUpdated); - spnAutoDelete = (Spinner) findViewById(R.id.spnAutoDelete); - etxtUsername = (EditText) findViewById(R.id.etxtUsername); - etxtPassword = (EditText) findViewById(R.id.etxtPassword); - etxtFilterText = (EditText) findViewById(R.id.etxtEpisodeFilterText); - rdoFilterInclude = (RadioButton) findViewById(R.id.radio_filter_include); - rdoFilterInclude.setOnClickListener(v -> { - filterInclude = true; - filterTextChanged = true; - }); - rdoFilterExclude = (RadioButton) findViewById(R.id.radio_filter_exclude); - rdoFilterExclude.setOnClickListener(v -> { - filterInclude = false; - filterTextChanged = true; - }); + imgvCover = findViewById(R.id.imgvCover); + txtvTitle = findViewById(R.id.txtvTitle); + TextView txtvAuthorHeader = findViewById(R.id.txtvAuthor); + ImageView imgvBackground = findViewById(R.id.imgvBackground); + findViewById(R.id.butShowInfo).setVisibility(View.INVISIBLE); + findViewById(R.id.butShowSettings).setVisibility(View.INVISIBLE); + // https://github.com/bumptech/glide/issues/529 + imgvBackground.setColorFilter(new LightingColorFilter(0xff828282, 0x000000)); + + + txtvDescription = findViewById(R.id.txtvDescription); + lblLanguage = findViewById(R.id.lblLanguage); + txtvLanguage = findViewById(R.id.txtvLanguage); + lblAuthor = findViewById(R.id.lblAuthor); + txtvAuthor = findViewById(R.id.txtvDetailsAuthor); + txtvUrl = findViewById(R.id.txtvUrl); txtvUrl.setOnClickListener(copyUrlToClipboard); - subscription = Observable.fromCallable(()-> DBReader.getFeed(feedId)) - .subscribeOn(Schedulers.newThread()) + disposable = Maybe.create((MaybeOnSubscribe<Feed>) emitter -> { + Feed feed = DBReader.getFeed(feedId); + if (feed != null) { + emitter.onSuccess(feed); + } else { + emitter.onComplete(); + } + }) + .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(result -> { - if (result == null) { - Log.e(TAG, "Activity was started with invalid arguments"); - finish(); - } feed = result; Log.d(TAG, "Language is " + feed.getLanguage()); Log.d(TAG, "Author is " + feed.getAuthor()); Log.d(TAG, "URL is " + feed.getDownload_url()); - FeedPreferences prefs = feed.getPreferences(); Glide.with(FeedInfoActivity.this) .load(feed.getImageLocation()) - .placeholder(R.color.light_gray) - .error(R.color.light_gray) - .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) - .fitCenter() - .dontAnimate() + .apply(new RequestOptions() + .placeholder(R.color.light_gray) + .error(R.color.light_gray) + .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) + .fitCenter() + .dontAnimate()) .into(imgvCover); + Glide.with(FeedInfoActivity.this) + .load(feed.getImageLocation()) + .apply(new RequestOptions() + .placeholder(R.color.image_readability_tint) + .error(R.color.image_readability_tint) + .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) + .transform(new FastBlurTransformation()) + .dontAnimate()) + .into(imgvBackground); txtvTitle.setText(feed.getTitle()); @@ -211,6 +156,7 @@ public class FeedInfoActivity extends AppCompatActivity { if (!TextUtils.isEmpty(feed.getAuthor())) { txtvAuthor.setText(feed.getAuthor()); + txtvAuthorHeader.setText(feed.getAuthor()); } else { lblAuthor.setVisibility(View.GONE); txtvAuthor.setVisibility(View.GONE); @@ -224,118 +170,21 @@ public class FeedInfoActivity extends AppCompatActivity { txtvUrl.setText(feed.getDownload_url() + " {fa-paperclip}"); Iconify.addIcons(txtvUrl); - cbxAutoDownload.setEnabled(UserPreferences.isEnableAutodownload()); - cbxAutoDownload.setChecked(prefs.getAutoDownload()); - cbxAutoDownload.setOnCheckedChangeListener((compoundButton, checked) -> { - feed.getPreferences().setAutoDownload(checked); - feed.savePreferences(); - updateAutoDownloadSettings(); - ApplyToEpisodesDialog dialog = new ApplyToEpisodesDialog(FeedInfoActivity.this, - feed, checked); - dialog.createNewDialog().show(); - }); - cbxKeepUpdated.setChecked(prefs.getKeepUpdated()); - cbxKeepUpdated.setOnCheckedChangeListener((compoundButton, checked) -> { - feed.getPreferences().setKeepUpdated(checked); - feed.savePreferences(); - }); - spnAutoDelete.setOnItemSelectedListener(new OnItemSelectedListener() { - @Override - public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { - FeedPreferences.AutoDeleteAction auto_delete_action; - switch (parent.getSelectedItemPosition()) { - case 0: - auto_delete_action = FeedPreferences.AutoDeleteAction.GLOBAL; - break; - case 1: - auto_delete_action = FeedPreferences.AutoDeleteAction.YES; - break; - case 2: - auto_delete_action = FeedPreferences.AutoDeleteAction.NO; - break; - default: // TODO - add exceptions here - return; - } - feed.getPreferences().setAutoDeleteAction(auto_delete_action);// p - autoDeleteChanged = true; - } - - @Override - public void onNothingSelected(AdapterView<?> parent) { - // Another interface callback - } - }); - spnAutoDelete.setSelection(prefs.getAutoDeleteAction().ordinal()); - - etxtUsername.setText(prefs.getUsername()); - etxtPassword.setText(prefs.getPassword()); - - etxtUsername.addTextChangedListener(authTextWatcher); - etxtPassword.addTextChangedListener(authTextWatcher); - - FeedFilter filter = prefs.getFilter(); - if (filter.includeOnly()) { - etxtFilterText.setText(filter.getIncludeFilter()); - rdoFilterInclude.setChecked(true); - rdoFilterExclude.setChecked(false); - filterInclude = true; - } else if (filter.excludeOnly()) { - etxtFilterText.setText(filter.getExcludeFilter()); - rdoFilterInclude.setChecked(false); - rdoFilterExclude.setChecked(true); - filterInclude = false; - } else { - Log.d(TAG, "No filter set"); - rdoFilterInclude.setChecked(false); - rdoFilterExclude.setChecked(false); - etxtFilterText.setText(""); - } - etxtFilterText.addTextChangedListener(filterTextWatcher); - supportInvalidateOptionsMenu(); - updateAutoDownloadSettings(); }, error -> { Log.d(TAG, Log.getStackTraceString(error)); finish(); + }, () -> { + Log.e(TAG, "Activity was started with invalid arguments"); + finish(); }); } @Override - protected void onPause() { - super.onPause(); - if (feed != null) { - FeedPreferences prefs = feed.getPreferences(); - if (authInfoChanged) { - Log.d(TAG, "Auth info changed, saving credentials"); - prefs.setUsername(etxtUsername.getText().toString()); - prefs.setPassword(etxtPassword.getText().toString()); - } - if (filterTextChanged) { - Log.d(TAG, "Filter info changed, saving..."); - String filterText = etxtFilterText.getText().toString(); - String includeString = ""; - String excludeString = ""; - if (filterInclude) { - includeString = filterText; - } else { - excludeString = filterText; - } - prefs.setFilter(new FeedFilter(includeString, excludeString)); - } - if (authInfoChanged || autoDeleteChanged || filterTextChanged) { - DBWriter.setFeedPreferences(prefs); - } - authInfoChanged = false; - autoDeleteChanged = false; - filterTextChanged = false; - } - } - - @Override public void onDestroy() { super.onDestroy(); - if(subscription != null) { - subscription.unsubscribe(); + if (disposable != null) { + disposable.dispose(); } } @@ -375,34 +224,4 @@ public class FeedInfoActivity extends AppCompatActivity { return super.onOptionsItemSelected(item); } } - - private void updateAutoDownloadSettings() { - if (feed != null && feed.getPreferences() != null) { - boolean enabled = feed.getPreferences().getAutoDownload() && UserPreferences.isEnableAutodownload(); - rdoFilterInclude.setEnabled(enabled); - rdoFilterExclude.setEnabled(enabled); - etxtFilterText.setEnabled(enabled); - } - } - - private class ApplyToEpisodesDialog extends ConfirmationDialog { - - private final Feed feed; - private final boolean autoDownload; - - ApplyToEpisodesDialog(Context context, Feed feed, boolean autoDownload) { - super(context, R.string.auto_download_apply_to_items_title, - R.string.auto_download_apply_to_items_message); - this.feed = feed; - this.autoDownload = autoDownload; - setPositiveText(R.string.yes); - setNegativeText(R.string.no); - } - - @Override - public void onConfirmButtonPressed(DialogInterface dialog) { - DBWriter.setFeedsItemsAutoDownload(feed, autoDownload); - } - } - } diff --git a/app/src/main/java/de/danoeh/antennapod/activity/FeedSettingsActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/FeedSettingsActivity.java new file mode 100644 index 000000000..4698ed90e --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/activity/FeedSettingsActivity.java @@ -0,0 +1,364 @@ +package de.danoeh.antennapod.activity; + +import android.content.Context; +import android.content.DialogInterface; +import android.content.Intent; +import android.graphics.LightingColorFilter; +import android.net.Uri; +import android.os.Bundle; +import android.support.v7.app.AppCompatActivity; +import android.text.Editable; +import android.text.TextUtils; +import android.text.TextWatcher; +import android.util.Log; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.widget.AdapterView; +import android.widget.AdapterView.OnItemSelectedListener; +import android.widget.CheckBox; +import android.widget.EditText; +import android.widget.ImageView; +import android.widget.RadioButton; +import android.widget.Spinner; +import android.widget.TextView; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.request.RequestOptions; + +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.core.dialog.ConfirmationDialog; +import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator; +import de.danoeh.antennapod.core.feed.Feed; +import de.danoeh.antennapod.core.feed.FeedFilter; +import de.danoeh.antennapod.core.feed.FeedPreferences; +import de.danoeh.antennapod.core.glide.ApGlideSettings; +import de.danoeh.antennapod.core.glide.FastBlurTransformation; +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.DownloadRequestException; +import de.danoeh.antennapod.core.util.IntentUtils; +import de.danoeh.antennapod.menuhandler.FeedMenuHandler; +import io.reactivex.Maybe; +import io.reactivex.MaybeOnSubscribe; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; + +/** + * Displays information about a feed. + */ +public class FeedSettingsActivity extends AppCompatActivity { + + public static final String EXTRA_FEED_ID = "de.danoeh.antennapod.extra.feedId"; + private static final String TAG = "FeedSettingsActivity"; + private boolean autoDeleteChanged = false; + private Feed feed; + + private ImageView imgvCover; + private TextView txtvTitle; + private EditText etxtUsername; + private EditText etxtPassword; + private EditText etxtFilterText; + private RadioButton rdoFilterInclude; + private RadioButton rdoFilterExclude; + private CheckBox cbxAutoDownload; + private CheckBox cbxKeepUpdated; + private Spinner spnAutoDelete; + private boolean filterInclude = true; + + private Disposable disposable; + + private boolean authInfoChanged = false; + + private final TextWatcher authTextWatcher = 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) { + authInfoChanged = true; + } + }; + + private boolean filterTextChanged = false; + + private final TextWatcher filterTextWatcher = 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) { + filterTextChanged = true; + } + }; + + @Override + protected void onCreate(Bundle savedInstanceState) { + setTheme(UserPreferences.getTheme()); + super.onCreate(savedInstanceState); + setContentView(R.layout.feedsettings); + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + long feedId = getIntent().getLongExtra(EXTRA_FEED_ID, -1); + + imgvCover = findViewById(R.id.imgvCover); + txtvTitle = findViewById(R.id.txtvTitle); + TextView txtvAuthorHeader = findViewById(R.id.txtvAuthor); + ImageView imgvBackground = findViewById(R.id.imgvBackground); + findViewById(R.id.butShowInfo).setVisibility(View.INVISIBLE); + findViewById(R.id.butShowSettings).setVisibility(View.INVISIBLE); + // https://github.com/bumptech/glide/issues/529 + imgvBackground.setColorFilter(new LightingColorFilter(0xff828282, 0x000000)); + + cbxAutoDownload = findViewById(R.id.cbxAutoDownload); + cbxKeepUpdated = findViewById(R.id.cbxKeepUpdated); + spnAutoDelete = findViewById(R.id.spnAutoDelete); + etxtUsername = findViewById(R.id.etxtUsername); + etxtPassword = findViewById(R.id.etxtPassword); + etxtFilterText = findViewById(R.id.etxtEpisodeFilterText); + rdoFilterInclude = findViewById(R.id.radio_filter_include); + rdoFilterInclude.setOnClickListener(v -> { + filterInclude = true; + filterTextChanged = true; + }); + rdoFilterExclude = findViewById(R.id.radio_filter_exclude); + rdoFilterExclude.setOnClickListener(v -> { + filterInclude = false; + filterTextChanged = true; + }); + + disposable = Maybe.create((MaybeOnSubscribe<Feed>) emitter -> { + Feed feed = DBReader.getFeed(feedId); + if (feed != null) { + emitter.onSuccess(feed); + } else { + emitter.onComplete(); + } + }) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(result -> { + feed = result; + FeedPreferences prefs = feed.getPreferences(); + Glide.with(FeedSettingsActivity.this) + .load(feed.getImageLocation()) + .apply(new RequestOptions() + .placeholder(R.color.light_gray) + .error(R.color.light_gray) + .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) + .fitCenter() + .dontAnimate()) + .into(imgvCover); + Glide.with(FeedSettingsActivity.this) + .load(feed.getImageLocation()) + .apply(new RequestOptions() + .placeholder(R.color.image_readability_tint) + .error(R.color.image_readability_tint) + .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) + .transform(new FastBlurTransformation()) + .dontAnimate()) + .into(imgvBackground); + + txtvTitle.setText(feed.getTitle()); + + if (!TextUtils.isEmpty(feed.getAuthor())) { + txtvAuthorHeader.setText(feed.getAuthor()); + } + + cbxAutoDownload.setEnabled(UserPreferences.isEnableAutodownload()); + cbxAutoDownload.setChecked(prefs.getAutoDownload()); + cbxAutoDownload.setOnCheckedChangeListener((compoundButton, checked) -> { + feed.getPreferences().setAutoDownload(checked); + feed.savePreferences(); + updateAutoDownloadSettings(); + ApplyToEpisodesDialog dialog = new ApplyToEpisodesDialog(FeedSettingsActivity.this, + feed, checked); + dialog.createNewDialog().show(); + }); + cbxKeepUpdated.setChecked(prefs.getKeepUpdated()); + cbxKeepUpdated.setOnCheckedChangeListener((compoundButton, checked) -> { + feed.getPreferences().setKeepUpdated(checked); + feed.savePreferences(); + }); + spnAutoDelete.setOnItemSelectedListener(new OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView<?> parent, View view, int pos, long id) { + FeedPreferences.AutoDeleteAction auto_delete_action; + switch (parent.getSelectedItemPosition()) { + case 0: + auto_delete_action = FeedPreferences.AutoDeleteAction.GLOBAL; + break; + case 1: + auto_delete_action = FeedPreferences.AutoDeleteAction.YES; + break; + case 2: + auto_delete_action = FeedPreferences.AutoDeleteAction.NO; + break; + default: // TODO - add exceptions here + return; + } + feed.getPreferences().setAutoDeleteAction(auto_delete_action);// p + autoDeleteChanged = true; + } + + @Override + public void onNothingSelected(AdapterView<?> parent) { + // Another interface callback + } + }); + spnAutoDelete.setSelection(prefs.getAutoDeleteAction().ordinal()); + + etxtUsername.setText(prefs.getUsername()); + etxtPassword.setText(prefs.getPassword()); + + etxtUsername.addTextChangedListener(authTextWatcher); + etxtPassword.addTextChangedListener(authTextWatcher); + + FeedFilter filter = prefs.getFilter(); + if (filter.includeOnly()) { + etxtFilterText.setText(filter.getIncludeFilter()); + rdoFilterInclude.setChecked(true); + rdoFilterExclude.setChecked(false); + filterInclude = true; + } else if (filter.excludeOnly()) { + etxtFilterText.setText(filter.getExcludeFilter()); + rdoFilterInclude.setChecked(false); + rdoFilterExclude.setChecked(true); + filterInclude = false; + } else { + Log.d(TAG, "No filter set"); + rdoFilterInclude.setChecked(false); + rdoFilterExclude.setChecked(false); + etxtFilterText.setText(""); + } + etxtFilterText.addTextChangedListener(filterTextWatcher); + + supportInvalidateOptionsMenu(); + updateAutoDownloadSettings(); + }, error -> { + Log.d(TAG, Log.getStackTraceString(error)); + finish(); + }, () -> { + Log.e(TAG, "Activity was started with invalid arguments"); + finish(); + }); + } + + @Override + protected void onPause() { + super.onPause(); + if (feed != null) { + FeedPreferences prefs = feed.getPreferences(); + if (authInfoChanged) { + Log.d(TAG, "Auth info changed, saving credentials"); + prefs.setUsername(etxtUsername.getText().toString()); + prefs.setPassword(etxtPassword.getText().toString()); + } + if (filterTextChanged) { + Log.d(TAG, "Filter info changed, saving..."); + String filterText = etxtFilterText.getText().toString(); + String includeString = ""; + String excludeString = ""; + if (filterInclude) { + includeString = filterText; + } else { + excludeString = filterText; + } + prefs.setFilter(new FeedFilter(includeString, excludeString)); + } + if (authInfoChanged || autoDeleteChanged || filterTextChanged) { + DBWriter.setFeedPreferences(prefs); + } + authInfoChanged = false; + autoDeleteChanged = false; + filterTextChanged = false; + } + } + + @Override + public void onDestroy() { + super.onDestroy(); + if (disposable != null) { + disposable.dispose(); + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + super.onCreateOptionsMenu(menu); + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.feedinfo, menu); + return true; + } + + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + super.onPrepareOptionsMenu(menu); + menu.findItem(R.id.support_item).setVisible( + feed != null && feed.getPaymentLink() != null); + menu.findItem(R.id.share_link_item).setVisible(feed != null && feed.getLink() != null); + menu.findItem(R.id.visit_website_item).setVisible(feed != null && feed.getLink() != null && + IntentUtils.isCallable(this, new Intent(Intent.ACTION_VIEW, Uri.parse(feed.getLink())))); + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + switch (item.getItemId()) { + case android.R.id.home: + finish(); + return true; + default: + try { + return FeedMenuHandler.onOptionsItemClicked(this, item, feed); + } catch (DownloadRequestException e) { + e.printStackTrace(); + DownloadRequestErrorDialogCreator.newRequestErrorDialog(this, + e.getMessage()); + } + return super.onOptionsItemSelected(item); + } + } + + private void updateAutoDownloadSettings() { + if (feed != null && feed.getPreferences() != null) { + boolean enabled = feed.getPreferences().getAutoDownload() && UserPreferences.isEnableAutodownload(); + rdoFilterInclude.setEnabled(enabled); + rdoFilterExclude.setEnabled(enabled); + etxtFilterText.setEnabled(enabled); + } + } + + private static class ApplyToEpisodesDialog extends ConfirmationDialog { + + private final Feed feed; + private final boolean autoDownload; + + ApplyToEpisodesDialog(Context context, Feed feed, boolean autoDownload) { + super(context, R.string.auto_download_apply_to_items_title, + R.string.auto_download_apply_to_items_message); + this.feed = feed; + this.autoDownload = autoDownload; + setPositiveText(R.string.yes); + setNegativeText(R.string.no); + } + + @Override + public void onConfirmButtonPressed(DialogInterface dialog) { + DBWriter.setFeedsItemsAutoDownload(feed, autoDownload); + } + } + +} diff --git a/app/src/main/java/de/danoeh/antennapod/activity/FlattrAuthActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/FlattrAuthActivity.java index be1c9f9e6..2b4384a02 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/FlattrAuthActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/FlattrAuthActivity.java @@ -4,7 +4,7 @@ package de.danoeh.antennapod.activity; import android.content.Intent; import android.net.Uri; import android.os.Bundle; -import android.support.v7.app.ActionBarActivity; +import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.Menu; import android.view.MenuItem; @@ -18,11 +18,10 @@ import de.danoeh.antennapod.BuildConfig; import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.util.flattr.FlattrUtils; -import de.danoeh.antennapod.preferences.PreferenceController; /** Guides the user through the authentication process */ -public class FlattrAuthActivity extends ActionBarActivity { +public class FlattrAuthActivity extends AppCompatActivity { private static final String TAG = "FlattrAuthActivity"; private TextView txtvExplanation; @@ -42,9 +41,9 @@ public class FlattrAuthActivity extends ActionBarActivity { if (BuildConfig.DEBUG) Log.d(TAG, "Activity created"); getSupportActionBar().setDisplayHomeAsUpEnabled(true); setContentView(R.layout.flattr_auth); - txtvExplanation = (TextView) findViewById(R.id.txtvExplanation); - butAuthenticate = (Button) findViewById(R.id.but_authenticate); - butReturn = (Button) findViewById(R.id.but_return_home); + txtvExplanation = findViewById(R.id.txtvExplanation); + butAuthenticate = findViewById(R.id.but_authenticate); + butReturn = findViewById(R.id.but_return_home); butReturn.setOnClickListener(v -> { Intent intent = new Intent(FlattrAuthActivity.this, MainActivity.class); @@ -104,7 +103,7 @@ public class FlattrAuthActivity extends ActionBarActivity { switch (item.getItemId()) { case android.R.id.home: if (authSuccessful) { - Intent intent = new Intent(this, PreferenceController.getPreferenceActivity()); + Intent intent = new Intent(this, PreferenceActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); startActivity(intent); } else { diff --git a/app/src/main/java/de/danoeh/antennapod/activity/ImportExportActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/ImportExportActivity.java new file mode 100644 index 000000000..e6c9c37cc --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/activity/ImportExportActivity.java @@ -0,0 +1,190 @@ +package de.danoeh.antennapod.activity; + +import android.content.ComponentName; +import android.content.Intent; +import android.net.Uri; +import android.os.Build; +import android.os.Bundle; +import android.os.Environment; +import android.os.ParcelFileDescriptor; +import android.support.design.widget.Snackbar; +import android.support.v7.app.ActionBar; +import android.support.v7.app.AlertDialog; +import android.support.v7.app.AppCompatActivity; +import android.util.Log; +import android.view.MenuItem; + +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.channels.FileChannel; + +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.core.preferences.UserPreferences; +import de.danoeh.antennapod.core.storage.PodDBAdapter; + +/** + * Displays the 'import/export' screen + */ +public class ImportExportActivity extends AppCompatActivity { + private static final int REQUEST_CODE_RESTORE = 43; + private static final int REQUEST_CODE_BACKUP_DOCUMENT = 44; + private static final String EXPORT_FILENAME = "AntennaPodBackup.db"; + private static final String TAG = ImportExportActivity.class.getSimpleName(); + + @Override + protected void onCreate(Bundle savedInstanceState) { + setTheme(UserPreferences.getTheme()); + super.onCreate(savedInstanceState); + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + actionBar.setDisplayShowHomeEnabled(true); + } + setContentView(R.layout.import_export_activity); + + findViewById(R.id.button_export).setOnClickListener(view -> backup()); + findViewById(R.id.button_import).setOnClickListener(view -> restore()); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == android.R.id.home) { + finish(); + return true; + } else { + return super.onOptionsItemSelected(item); + } + } + + private void backup() { + if (Build.VERSION.SDK_INT >= 19) { + Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT) + .addCategory(Intent.CATEGORY_OPENABLE) + .setType("application/x-sqlite3") + .putExtra(Intent.EXTRA_TITLE, EXPORT_FILENAME); + + startActivityForResult(intent, REQUEST_CODE_BACKUP_DOCUMENT); + } else { + try { + File sd = Environment.getExternalStorageDirectory(); + File backupDB = new File(sd, EXPORT_FILENAME); + writeBackupTo(new FileOutputStream(backupDB)); + } catch (IOException e) { + Log.e(TAG, Log.getStackTraceString(e)); + Snackbar.make(findViewById(R.id.import_export_layout), e.getLocalizedMessage(), Snackbar.LENGTH_SHORT).show(); + } + } + } + + private void restore() { + if (Build.VERSION.SDK_INT >= 19) { + Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT); + intent.setType("*/*"); + startActivityForResult(intent, REQUEST_CODE_RESTORE); + } else { + Intent intent = new Intent(Intent.ACTION_GET_CONTENT); + intent.setType("*/*"); + startActivityForResult(Intent.createChooser(intent, + getString(R.string.import_select_file)), REQUEST_CODE_RESTORE); + } + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent resultData) { + if (resultCode != RESULT_OK || resultData == null) { + return; + } + Uri uri = resultData.getData(); + + if (requestCode == REQUEST_CODE_RESTORE) { + restoreFrom(uri); + } else if (requestCode == REQUEST_CODE_BACKUP_DOCUMENT) { + backupToDocument(uri); + } + } + + private void restoreFrom(Uri inputUri) { + File currentDB = getDatabasePath(PodDBAdapter.DATABASE_NAME); + InputStream inputStream = null; + try { + inputStream = getContentResolver().openInputStream(inputUri); + FileUtils.copyInputStreamToFile(inputStream, currentDB); + displayImportSuccessDialog(); + } catch (IOException e) { + Log.e(TAG, Log.getStackTraceString(e)); + Snackbar.make(findViewById(R.id.import_export_layout), e.getLocalizedMessage(), Snackbar.LENGTH_SHORT).show(); + } finally { + IOUtils.closeQuietly(inputStream); + } + } + + private void displayImportSuccessDialog() { + AlertDialog.Builder d = new AlertDialog.Builder(ImportExportActivity.this); + d.setMessage(R.string.import_ok); + d.setCancelable(false); + d.setPositiveButton(android.R.string.ok, (dialogInterface, i) -> { + Intent intent = new Intent(getApplicationContext(), SplashActivity.class); + ComponentName cn = intent.getComponent(); + Intent mainIntent = Intent.makeRestartActivityTask(cn); + startActivity(mainIntent); + }); + d.show(); + } + + private void backupToDocument(Uri uri) { + ParcelFileDescriptor pfd = null; + FileOutputStream fileOutputStream = null; + try { + pfd = getContentResolver().openFileDescriptor(uri, "w"); + fileOutputStream = new FileOutputStream(pfd.getFileDescriptor()); + writeBackupTo(fileOutputStream); + + Snackbar.make(findViewById(R.id.import_export_layout), + R.string.export_ok, Snackbar.LENGTH_SHORT).show(); + } catch (IOException e) { + Log.e(TAG, Log.getStackTraceString(e)); + Snackbar.make(findViewById(R.id.import_export_layout), e.getLocalizedMessage(), Snackbar.LENGTH_SHORT).show(); + } finally { + IOUtils.closeQuietly(fileOutputStream); + + if (pfd != null) { + try { + pfd.close(); + } catch (IOException e) { + Log.d(TAG, "Unable to close ParcelFileDescriptor"); + } + } + } + } + + private void writeBackupTo(FileOutputStream outFileStream) { + FileChannel src = null; + FileChannel dst = null; + try { + File currentDB = getDatabasePath(PodDBAdapter.DATABASE_NAME); + + if (currentDB.exists()) { + src = new FileInputStream(currentDB).getChannel(); + dst = outFileStream.getChannel(); + dst.transferFrom(src, 0, src.size()); + + Snackbar.make(findViewById(R.id.import_export_layout), + R.string.export_ok, Snackbar.LENGTH_SHORT).show(); + } else { + Snackbar.make(findViewById(R.id.import_export_layout), + "Can not access current database", Snackbar.LENGTH_SHORT).show(); + } + } catch (IOException e) { + Log.e(TAG, Log.getStackTraceString(e)); + Snackbar.make(findViewById(R.id.import_export_layout), e.getLocalizedMessage(), Snackbar.LENGTH_SHORT).show(); + } finally { + IOUtils.closeQuietly(src); + IOUtils.closeQuietly(dst); + } + } +} diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java index f0fcdca90..9f4fbe271 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java @@ -27,6 +27,7 @@ import android.view.MenuItem; import android.view.View; import android.widget.AdapterView; import android.widget.ListView; +import android.widget.Toast; import com.bumptech.glide.Glide; @@ -42,17 +43,20 @@ import de.danoeh.antennapod.core.dialog.ConfirmationDialog; import de.danoeh.antennapod.core.event.MessageEvent; import de.danoeh.antennapod.core.event.ProgressEvent; import de.danoeh.antennapod.core.event.QueueEvent; +import de.danoeh.antennapod.core.event.ServiceEvent; 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.DBTasks; import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.util.FeedItemUtil; import de.danoeh.antennapod.core.util.Flavors; +import de.danoeh.antennapod.core.util.IntentUtils; import de.danoeh.antennapod.core.util.StorageUtils; +import de.danoeh.antennapod.core.util.download.AutoUpdateManager; +import de.danoeh.antennapod.core.util.gui.NotificationUtils; import de.danoeh.antennapod.dialog.RatingDialog; import de.danoeh.antennapod.dialog.RenameFeedDialog; import de.danoeh.antennapod.fragment.AddFeedFragment; @@ -64,12 +68,11 @@ 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; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; /** * The activity that is shown when the user launches the app. @@ -83,7 +86,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi 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"; + private 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"; @@ -91,8 +94,8 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi 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"; + private static final String SAVE_BACKSTACK_COUNT = "backstackCount"; + private static final String SAVE_TITLE = "title"; public static final String[] NAV_DRAWER_TAGS = { QueueFragment.TAG, @@ -119,7 +122,9 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi private ProgressDialog pd; - private Subscription subscription; + private Disposable disposable; + + private long lastBackButtonPressTime = 0; @Override public void onCreate(Bundle savedInstanceState) { @@ -128,7 +133,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi StorageUtils.checkStorageAvailability(this); setContentView(R.layout.main); - toolbar = (Toolbar) findViewById(R.id.toolbar); + toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { @@ -140,8 +145,8 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi currentTitle = getTitle(); - drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); - navList = (ListView) findViewById(R.id.nav_list); + drawerLayout = findViewById(R.id.drawer_layout); + navList = findViewById(R.id.nav_list); navDrawer = findViewById(R.id.nav_layout); drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, R.string.drawer_open, R.string.drawer_close); @@ -173,7 +178,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi findViewById(R.id.nav_settings).setOnClickListener(v -> { drawerLayout.closeDrawer(navDrawer); - startActivity(new Intent(MainActivity.this, PreferenceController.getPreferenceActivity())); + startActivity(new Intent(MainActivity.this, PreferenceActivity.class)); }); FragmentTransaction transaction = fm.beginTransaction(); @@ -201,6 +206,8 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi transaction.commit(); checkFirstLaunch(); + NotificationUtils.createChannels(this); + UserPreferences.restartUpdateAlarm(false); } private void saveLastNavFragment(String tag) { @@ -236,7 +243,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi } } - public void showDrawerPreferencesDialog() { + private void showDrawerPreferencesDialog() { final List<String> hiddenDrawerItems = UserPreferences.getHiddenDrawerItems(); String[] navLabels = new String[NAV_DRAWER_TAGS.length]; final boolean[] checked = new boolean[NAV_DRAWER_TAGS.length]; @@ -270,7 +277,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi return (navDrawerData != null) ? navDrawerData.feeds : null; } - public void loadFragment(int index, Bundle args) { + private void loadFragment(int index, Bundle args) { Log.d(TAG, "loadFragment(index: " + index + ", args: " + args + ")"); if (index < navAdapter.getSubscriptionOffset()) { String tag = navAdapter.getTags().get(index); @@ -399,7 +406,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi } } - private AdapterView.OnItemClickListener navListClickListener = new AdapterView.OnItemClickListener() { + private final AdapterView.OnItemClickListener navListClickListener = new AdapterView.OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { int viewType = parent.getAdapter().getItemViewType(position); @@ -410,7 +417,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi } }; - private AdapterView.OnItemLongClickListener newListLongClickListener = new AdapterView.OnItemLongClickListener() { + private final AdapterView.OnItemLongClickListener newListLongClickListener = new AdapterView.OnItemLongClickListener() { @Override public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) { if(position < navAdapter.getTags().size()) { @@ -467,7 +474,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi protected void onResume() { super.onResume(); StorageUtils.checkStorageAvailability(this); - DBTasks.checkShouldRefreshFeeds(getApplicationContext()); + AutoUpdateManager.checkShouldRefreshFeeds(getApplicationContext()); Intent intent = getIntent(); if (intent.hasExtra(EXTRA_FEED_ID) || @@ -484,8 +491,8 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi super.onStop(); EventDistributor.getInstance().unregister(contentUpdate); EventBus.getDefault().unregister(this); - if(subscription != null) { - subscription.unsubscribe(); + if (disposable != null) { + disposable.dispose(); } if(pd != null) { pd.dismiss(); @@ -573,10 +580,29 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi Feed feed = navDrawerData.feeds.get(position - navAdapter.getSubscriptionOffset()); switch(item.getItemId()) { case R.id.mark_all_seen_item: - DBWriter.markFeedSeen(feed.getId()); + ConfirmationDialog markAllSeenConfirmationDialog = new ConfirmationDialog(this, + R.string.mark_all_seen_label, + R.string.mark_all_seen_confirmation_msg) { + @Override + public void onConfirmButtonPressed(DialogInterface dialog) { + dialog.dismiss(); + DBWriter.markFeedSeen(feed.getId()); + } + }; + markAllSeenConfirmationDialog.createNewDialog().show(); return true; case R.id.mark_all_read_item: - DBWriter.markFeedRead(feed.getId()); + ConfirmationDialog markAllReadConfirmationDialog = new ConfirmationDialog(this, + R.string.mark_all_read_label, + R.string.mark_all_read_confirmation_msg) { + + @Override + public void onConfirmButtonPressed(DialogInterface dialog) { + dialog.dismiss(); + DBWriter.markFeedRead(feed.getId()); + } + }; + markAllReadConfirmationDialog.createNewDialog().show(); return true; case R.id.rename_item: new RenameFeedDialog(this, feed).show(); @@ -605,8 +631,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi remover.skipOnCompletion = true; int playerStatus = PlaybackPreferences.getCurrentPlayerStatus(); if(playerStatus == PlaybackPreferences.PLAYER_STATUS_PLAYING) { - sendBroadcast(new Intent( - PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE)); + IntentUtils.sendLocalBroadcast(MainActivity.this, PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE); } } remover.executeAsync(); @@ -621,17 +646,47 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi @Override public void onBackPressed() { - if(isDrawerOpen()) { + if (isDrawerOpen()) { drawerLayout.closeDrawer(navDrawer); - } else { + } else if (getSupportFragmentManager().getBackStackEntryCount() != 0) { super.onBackPressed(); + } else { + switch (UserPreferences.getBackButtonBehavior()) { + case OPEN_DRAWER: + drawerLayout.openDrawer(navDrawer); + break; + case SHOW_PROMPT: + new AlertDialog.Builder(this) + .setMessage(R.string.close_prompt) + .setPositiveButton(R.string.yes, (dialogInterface, i) -> MainActivity.super.onBackPressed()) + .setNegativeButton(R.string.no, null) + .setCancelable(false) + .show(); + break; + case DOUBLE_TAP: + if (lastBackButtonPressTime < System.currentTimeMillis() - 2000) { + Toast.makeText(this, R.string.double_tap_toast, Toast.LENGTH_SHORT).show(); + lastBackButtonPressTime = System.currentTimeMillis(); + } else { + super.onBackPressed(); + } + break; + case GO_TO_PAGE: + if (getLastNavFragment().equals(UserPreferences.getBackButtonGoToPage())) { + super.onBackPressed(); + } else { + loadFragment(UserPreferences.getBackButtonGoToPage(), null); + } + break; + default: super.onBackPressed(); + } } } private DBReader.NavDrawerData navDrawerData; private int selectedNavListIndex = 0; - private NavListAdapter.ItemAccess itemAccess = new NavListAdapter.ItemAccess() { + private final NavListAdapter.ItemAccess itemAccess = new NavListAdapter.ItemAccess() { @Override public int getCount() { if (navDrawerData != null) { @@ -695,8 +750,8 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi }; private void loadData() { - subscription = Observable.fromCallable(DBReader::getNavDrawerData) - .subscribeOn(Schedulers.newThread()) + disposable = Observable.fromCallable(DBReader::getNavDrawerData) + .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(result -> { boolean handleIntent = (navDrawerData == null); @@ -721,6 +776,15 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi loadData(); } + public void onEventMainThread(ServiceEvent event) { + Log.d(TAG, "onEvent(" + event + ")"); + switch(event.action) { + case SERVICE_STARTED: + externalPlayerFragment.connectToPlaybackService(); + break; + } + } + public void onEventMainThread(ProgressEvent event) { Log.d(TAG, "onEvent(" + event + ")"); switch(event.action) { @@ -744,14 +808,12 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi View parentLayout = findViewById(R.id.drawer_layout); Snackbar snackbar = Snackbar.make(parentLayout, event.message, Snackbar.LENGTH_SHORT); if(event.action != null) { - snackbar.setAction(getString(R.string.undo), v -> { - event.action.run(); - }); + snackbar.setAction(getString(R.string.undo), v -> event.action.run()); } snackbar.show(); } - private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { + private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { @Override public void update(EventDistributor eventDistributor, Integer arg) { diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java index 21a0fa66f..1cddaa655 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java @@ -1,9 +1,11 @@ package de.danoeh.antennapod.activity; +import android.Manifest; import android.annotation.TargetApi; import android.app.Activity; import android.content.Intent; import android.content.SharedPreferences; +import android.content.pm.PackageManager; import android.content.res.TypedArray; import android.graphics.Color; import android.graphics.PixelFormat; @@ -11,6 +13,9 @@ import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.support.annotation.Nullable; +import android.support.v4.app.ActivityCompat; +import android.support.v4.app.ActivityOptionsCompat; +import android.support.v4.content.ContextCompat; import android.support.v7.app.AlertDialog; import android.util.Log; import android.view.Menu; @@ -33,28 +38,36 @@ import com.joanzapata.iconify.fonts.FontAwesomeIcons; import java.util.Locale; import de.danoeh.antennapod.R; +import de.danoeh.antennapod.core.event.ServiceEvent; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedMedia; +import de.danoeh.antennapod.core.feed.MediaType; import de.danoeh.antennapod.core.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.Consumer; import de.danoeh.antennapod.core.util.Converter; +import de.danoeh.antennapod.core.util.FeedItemUtil; import de.danoeh.antennapod.core.util.Flavors; +import de.danoeh.antennapod.core.util.Function; +import de.danoeh.antennapod.core.util.IntentUtils; import de.danoeh.antennapod.core.util.ShareUtils; import de.danoeh.antennapod.core.util.StorageUtils; import de.danoeh.antennapod.core.util.Supplier; +import de.danoeh.antennapod.core.util.gui.PictureInPictureUtil; +import de.danoeh.antennapod.core.util.playback.ExternalMedia; 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.core.util.playback.PlaybackServiceStarter; import de.danoeh.antennapod.dialog.SleepTimerDialog; import de.danoeh.antennapod.dialog.VariableSpeedDialog; -import rx.Observable; -import rx.android.schedulers.AndroidSchedulers; -import rx.functions.Action1; -import rx.functions.Func1; -import rx.schedulers.Schedulers; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; /** @@ -65,23 +78,26 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements private static final String TAG = "MediaplayerActivity"; private static final String PREFS = "MediaPlayerActivityPreferences"; private static final String PREF_SHOW_TIME_LEFT = "showTimeLeft"; + private static final int REQUEST_CODE_STORAGE = 42; - protected PlaybackController controller; + 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; + private TextView txtvPosition; + private TextView txtvLength; + SeekBar sbPosition; + private ImageButton butRev; + private TextView txtvRev; + private ImageButton butPlay; + private ImageButton butFF; + private TextView txtvFF; + private ImageButton butSkip; - protected boolean showTimeLeft = false; + private boolean showTimeLeft = false; private boolean isFavorite = false; + private Disposable disposable; + private PlaybackController newPlaybackController() { return new PlaybackController(this, false) { @@ -183,31 +199,31 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements }; } - protected static TextView getTxtvFFFromActivity(MediaplayerActivity activity) { + private static TextView getTxtvFFFromActivity(MediaplayerActivity activity) { return activity.txtvFF; } - protected static TextView getTxtvRevFromActivity(MediaplayerActivity activity) { + private static TextView getTxtvRevFromActivity(MediaplayerActivity activity) { return activity.txtvRev; } - protected void onSetSpeedAbilityChanged() { + private void onSetSpeedAbilityChanged() { Log.d(TAG, "onSetSpeedAbilityChanged()"); updatePlaybackSpeedButton(); } - protected void onPlaybackSpeedChange() { + private void onPlaybackSpeedChange() { updatePlaybackSpeedButtonText(); } - protected void onServiceQueried() { + private void onServiceQueried() { supportInvalidateOptionsMenu(); } - protected void chooseTheme() { + void chooseTheme() { setTheme(UserPreferences.getTheme()); } - protected void setScreenOn(boolean enable) { + void setScreenOn(boolean enable) { } @Override @@ -218,15 +234,16 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements 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(); + if (!PictureInPictureUtil.isInPictureInPictureMode(this)) { + if (controller != null) { + controller.reinitServiceIfPaused(); + controller.pause(); + } } super.onPause(); } @@ -248,17 +265,12 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements */ protected abstract void onBufferEnd(); - protected void onBufferUpdate(float progress) { + private void onBufferUpdate(float progress) { if (sbPosition != null) { - sbPosition.setSecondaryProgress((int) progress * sbPosition.getMax()); + sbPosition.setSecondaryProgress((int) (progress * sbPosition.getMax())); } } - /** - * Current screen orientation. - */ - protected int orientation; - @Override protected void onStart() { super.onStart(); @@ -266,6 +278,9 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements controller.release(); } controller = newPlaybackController(); + setupGUI(); + loadMediaInfo(); + onPositionObserverUpdate(); } @Override @@ -275,6 +290,9 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements controller.release(); controller = null; // prevent leak } + if (disposable != null) { + disposable.dispose(); + } super.onStop(); } @@ -316,11 +334,11 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements ((FeedMedia) media).getItem().getFlattrStatus().flattrable() ); - boolean hasWebsiteLink = media != null && media.getWebsiteLink() != null; + boolean hasWebsiteLink = ( getWebsiteLinkWithFallback(media) != null ); menu.findItem(R.id.visit_website_item).setVisible(hasWebsiteLink); boolean isItemAndHasLink = isFeedMedia && - ((FeedMedia) media).getItem() != null && ((FeedMedia) media).getItem().getLink() != null; + ShareUtils.hasLinkToShare(((FeedMedia) media).getItem()); menu.findItem(R.id.share_link_item).setVisible(isItemAndHasLink); menu.findItem(R.id.share_link_with_position_item).setVisible(isItemAndHasLink); @@ -368,7 +386,17 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements MainActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK); - startActivity(intent); + + View cover = findViewById(R.id.imgvCover); + if (cover != null && Build.VERSION.SDK_INT >= 16) { + ActivityOptionsCompat options = ActivityOptionsCompat. + makeSceneTransitionAnimation(MediaplayerActivity.this, + cover, "coverTransition"); + startActivity(intent, options.toBundle()); + } else { + startActivity(intent); + } + finish(); return true; } else { if (media != null) { @@ -546,7 +574,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements }); break; case R.id.visit_website_item: - Uri uri = Uri.parse(media.getWebsiteLink()); + Uri uri = Uri.parse(getWebsiteLinkWithFallback(media)); startActivity(new Intent(Intent.ACTION_VIEW, uri)); break; case R.id.support_item: @@ -589,16 +617,36 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements } } + private static String getWebsiteLinkWithFallback(Playable media) { + if (media == null) { + return null; + } else if (media.getWebsiteLink() != null) { + return media.getWebsiteLink(); + } else if (media instanceof FeedMedia) { + return FeedItemUtil.getLinkWithFallback(((FeedMedia)media).getItem()); + } + return null; + } + @Override protected void onResume() { super.onResume(); Log.d(TAG, "onResume()"); StorageUtils.checkStorageAvailability(this); - if(controller != null) { + if (controller != null) { controller.init(); } } + public void onEventMainThread(ServiceEvent event) { + Log.d(TAG, "onEvent(" + event + ")"); + if (event.action == ServiceEvent.Action.SERVICE_STARTED) { + if (controller != null) { + controller.init(); + } + } + } + /** * Called by 'handleStatus()' when the PlaybackService is waiting for * a video surface. @@ -609,7 +657,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements protected abstract void clearStatusMsg(); - protected void onPositionObserverUpdate() { + void onPositionObserverUpdate() { if (controller == null || txtvPosition == null || txtvLength == null) { return; } @@ -645,12 +693,11 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements * to the PlaybackService to ensure that the activity has the right * FeedMedia object. */ - protected boolean loadMediaInfo() { + boolean loadMediaInfo() { Log.d(TAG, "loadMediaInfo()"); if(controller == null || controller.getMedia() == null) { return false; } - Playable media = controller.getMedia(); SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE); showTimeLeft = prefs.getBoolean(PREF_SHOW_TIME_LEFT, false); onPositionObserverUpdate(); @@ -659,18 +706,18 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements return true; } - protected void updatePlaybackSpeedButton() { + void updatePlaybackSpeedButton() { // Only meaningful on AudioplayerActivity, where it is overridden. } - protected void updatePlaybackSpeedButtonText() { + void updatePlaybackSpeedButtonText() { // Only meaningful on AudioplayerActivity, where it is overridden. } /** * Abstract directions to skip forward or back (rewind) and encapsulates behavior to get or set preference (including update of UI on the skip buttons). */ - static public enum SkipDirection { + public enum SkipDirection { SKIP_FORWARD( UserPreferences::getFastForwardSecs, MediaplayerActivity::getTxtvFFFromActivity, @@ -682,8 +729,8 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements R.string.pref_rewind); private final Supplier<Integer> getPrefSecsFn; - private final Func1<MediaplayerActivity, TextView> getTextViewFn; - private final Action1<Integer> setPrefSecsFn; + private final Function<MediaplayerActivity, TextView> getTextViewFn; + private final Consumer<Integer> setPrefSecsFn; private final int titleResourceID; /** @@ -695,7 +742,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements * @param setPrefSecsFn Handle to function that sets the preference (setting) for the skip delta value (and optionally updates the button label with the current values) * @param titleResourceID ID of the resource string with the title for a view */ - SkipDirection(Supplier<Integer> getPrefSecsFn, Func1<MediaplayerActivity, TextView> getTextViewFn, Action1<Integer> setPrefSecsFn, int titleResourceID) { + SkipDirection(Supplier<Integer> getPrefSecsFn, Function<MediaplayerActivity, TextView> getTextViewFn, Consumer<Integer> setPrefSecsFn, int titleResourceID) { this.getPrefSecsFn = getPrefSecsFn; this.getTextViewFn = getTextViewFn; this.setPrefSecsFn = setPrefSecsFn; @@ -714,10 +761,10 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements * @param activity MediaplyerActivity that contains textview to update the display of the skip delta setting (or null if nothing to update) */ public void setPrefSkipSeconds(int seconds, @Nullable Activity activity) { - setPrefSecsFn.call(seconds); + setPrefSecsFn.accept(seconds); if (activity != null && activity instanceof MediaplayerActivity) { - TextView tv = getTextViewFn.call((MediaplayerActivity)activity); + TextView tv = getTextViewFn.apply((MediaplayerActivity)activity); if (tv != null) tv.setText(String.valueOf(seconds)); } } @@ -753,15 +800,15 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements builder.create().show(); } - protected void setupGUI() { + void setupGUI() { setContentView(getContentViewResourceId()); - sbPosition = (SeekBar) findViewById(R.id.sbPosition); - txtvPosition = (TextView) findViewById(R.id.txtvPosition); + sbPosition = findViewById(R.id.sbPosition); + txtvPosition = 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); + txtvLength = findViewById(R.id.txtvLength); if (txtvLength != null) { txtvLength.setOnClickListener(v -> { showTimeLeft = !showTimeLeft; @@ -785,18 +832,18 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements }); } - butRev = (ImageButton) findViewById(R.id.butRev); - txtvRev = (TextView) findViewById(R.id.txtvRev); + butRev = findViewById(R.id.butRev); + txtvRev = 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); + butPlay = findViewById(R.id.butPlay); + butFF = findViewById(R.id.butFF); + txtvFF = findViewById(R.id.txtvFF); if (txtvFF != null) { txtvFF.setText(String.valueOf(UserPreferences.getFastForwardSecs())); } - butSkip = (ImageButton) findViewById(R.id.butSkip); + butSkip = findViewById(R.id.butSkip); // SEEKBAR SETUP @@ -823,11 +870,12 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements } if (butSkip != null) { - butSkip.setOnClickListener(v -> sendBroadcast(new Intent(PlaybackService.ACTION_SKIP_CURRENT_EPISODE))); + butSkip.setOnClickListener(v -> + IntentUtils.sendLocalBroadcast(MediaplayerActivity.this, PlaybackService.ACTION_SKIP_CURRENT_EPISODE)); } } - protected void onRewind() { + void onRewind() { if (controller == null) { return; } @@ -835,14 +883,15 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements controller.seekTo(curr - UserPreferences.getRewindSecs() * 1000); } - protected void onPlayPause() { + void onPlayPause() { if(controller == null) { return; } + controller.init(); controller.playPause(); } - protected void onFastForward() { + void onFastForward() { if (controller == null) { return; } @@ -852,7 +901,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements protected abstract int getContentViewResourceId(); - void handleError(int errorCode) { + private void handleError(int errorCode) { final AlertDialog.Builder errorDialog = new AlertDialog.Builder(this); errorDialog.setTitle(R.string.error_label); errorDialog.setMessage(MediaPlayerError.getErrorString(this, errorCode)); @@ -865,7 +914,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements errorDialog.create().show(); } - float prog; + private float prog; @Override public void onProgressChanged (SeekBar seekBar,int progress, boolean fromUser) { @@ -896,22 +945,62 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements 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))); + if (!(playable instanceof FeedMedia)) { + return; + } + FeedItem feedItem = ((FeedMedia) playable).getItem(); + if (feedItem == null) { + return; + } + if (disposable != null) { + disposable.dispose(); + } + disposable = Observable.fromCallable(() -> DBReader.getFeedItem(feedItem.getId())) + .subscribeOn(Schedulers.io()) + .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))); + } + + void playExternalMedia(Intent intent, MediaType type) { + if (intent == null || intent.getData() == null) { + return; + } + if (Build.VERSION.SDK_INT >= 23 + && ContextCompat.checkSelfPermission(this, Manifest.permission.READ_EXTERNAL_STORAGE) + != PackageManager.PERMISSION_GRANTED) { + + if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_EXTERNAL_STORAGE)) { + Toast.makeText(this, R.string.needs_storage_permission, Toast.LENGTH_LONG).show(); + } else { + ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE}, + REQUEST_CODE_STORAGE); } + return; } + + Log.d(TAG, "Received VIEW intent: " + intent.getData().getPath()); + ExternalMedia media = new ExternalMedia(intent.getData().getPath(), type); + + new PlaybackServiceStarter(this, media) + .startWhenPrepared(true) + .shouldStream(false) + .prepareImmediately(true) + .start(); } + @Override + public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { + if (requestCode == REQUEST_CODE_STORAGE) { + if (grantResults.length <= 0 || grantResults[0] != PackageManager.PERMISSION_GRANTED) { + Toast.makeText(this, R.string.needs_storage_permission, Toast.LENGTH_LONG).show(); + } + } + } } diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java index b3cda69d3..a2389dabd 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java @@ -6,6 +6,7 @@ import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Configuration; import android.os.Build; +import android.os.Bundle; import android.support.annotation.Nullable; import android.support.design.widget.AppBarLayout; import android.support.design.widget.Snackbar; @@ -45,8 +46,9 @@ import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.service.playback.PlaybackService; import de.danoeh.antennapod.core.service.playback.PlayerStatus; import de.danoeh.antennapod.core.storage.DBReader; -import de.danoeh.antennapod.core.storage.DBTasks; import de.danoeh.antennapod.core.storage.DBWriter; +import de.danoeh.antennapod.core.util.IntentUtils; +import de.danoeh.antennapod.core.util.download.AutoUpdateManager; import de.danoeh.antennapod.core.util.playback.Playable; import de.danoeh.antennapod.core.util.playback.PlaybackController; import de.danoeh.antennapod.dialog.RenameFeedDialog; @@ -60,28 +62,28 @@ 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; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; /** * Activity for playing files that do not require a video surface. */ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implements NavDrawerActivity { + private static final String TAG = "MediaplayerInfoActivity"; + private static final int POS_COVER = 0; private static final int POS_DESCR = 1; private static final int POS_CHAPTERS = 2; private static final int NUM_CONTENT_FRAGMENTS = 3; - final String TAG = "MediaplayerInfoActivity"; private static final String PREFS = "AudioPlayerActivityPreferences"; private static final String PREF_KEY_SELECTED_FRAGMENT_POSITION = "selectedFragmentPosition"; - public static final String[] NAV_DRAWER_TAGS = { + private static final String[] NAV_DRAWER_TAGS = { QueueFragment.TAG, EpisodesFragment.TAG, SubscriptionFragment.TAG, @@ -91,8 +93,8 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem NavListAdapter.SUBSCRIPTION_LIST_TAG }; - protected Button butPlaybackSpeed; - protected ImageButton butCastDisconnect; + Button butPlaybackSpeed; + ImageButton butCastDisconnect; private DrawerLayout drawerLayout; private NavListAdapter navAdapter; private ListView navList; @@ -104,7 +106,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem private ViewPager pager; private MediaplayerInfoPagerAdapter pagerAdapter; - private Subscription subscription; + private Disposable disposable; @Override protected void onPause() { @@ -113,14 +115,20 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem } @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + supportPostponeEnterTransition(); + } + + @Override protected void onStop() { super.onStop(); Log.d(TAG, "onStop()"); if(pagerAdapter != null) { pagerAdapter.setController(null); } - if(subscription != null) { - subscription.unsubscribe(); + if (disposable != null) { + disposable.dispose(); } EventDistributor.getInstance().unregister(contentUpdate); saveCurrentFragment(); @@ -145,7 +153,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem setTheme(UserPreferences.getNoTitleTheme()); } - protected void saveCurrentFragment() { + void saveCurrentFragment() { if(pager == null) { return; } @@ -153,7 +161,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem SharedPreferences prefs = getSharedPreferences(PREFS, MODE_PRIVATE); prefs.edit() .putInt(PREF_KEY_SELECTED_FRAGMENT_POSITION, pager.getCurrentItem()) - .commit(); + .apply(); } @Override @@ -179,7 +187,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem pagerAdapter.onMediaChanged(media); pagerAdapter.setController(controller); } - DBTasks.checkShouldRefreshFeeds(getApplicationContext()); + AutoUpdateManager.checkShouldRefreshFeeds(getApplicationContext()); EventDistributor.getInstance().register(contentUpdate); EventBus.getDefault().register(this); @@ -219,23 +227,23 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem @Override protected void setupGUI() { super.setupGUI(); - Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); + Toolbar toolbar = findViewById(R.id.toolbar); setSupportActionBar(toolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setTitle(""); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { findViewById(R.id.shadow).setVisibility(View.GONE); - AppBarLayout appBarLayout = (AppBarLayout) findViewById(R.id.appBar); + AppBarLayout appBarLayout = findViewById(R.id.appBar); float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 4, getResources().getDisplayMetrics()); appBarLayout.setElevation(px); } - drawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout); - navList = (ListView) findViewById(R.id.nav_list); + drawerLayout = findViewById(R.id.drawer_layout); + navList = findViewById(R.id.nav_list); navDrawer = findViewById(R.id.nav_layout); drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, R.string.drawer_open, R.string.drawer_close); drawerToggle.setDrawerIndicatorEnabled(false); - drawerLayout.setDrawerListener(drawerToggle); + drawerLayout.addDrawerListener(drawerToggle); navAdapter = new NavListAdapter(itemAccess, this); navList.setAdapter(navAdapter); @@ -263,20 +271,22 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem findViewById(R.id.nav_settings).setOnClickListener(v -> { drawerLayout.closeDrawer(navDrawer); - startActivity(new Intent(MediaplayerInfoActivity.this, PreferenceController.getPreferenceActivity())); + startActivity(new Intent(MediaplayerInfoActivity.this, PreferenceActivity.class)); }); - butPlaybackSpeed = (Button) findViewById(R.id.butPlaybackSpeed); - butCastDisconnect = (ImageButton) findViewById(R.id.butCastDisconnect); + butPlaybackSpeed = findViewById(R.id.butPlaybackSpeed); + butCastDisconnect = findViewById(R.id.butCastDisconnect); - pager = (ViewPager) findViewById(R.id.pager); + pager = findViewById(R.id.pager); pagerAdapter = new MediaplayerInfoPagerAdapter(getSupportFragmentManager(), media); pagerAdapter.setController(controller); pager.setAdapter(pagerAdapter); - CirclePageIndicator pageIndicator = (CirclePageIndicator) findViewById(R.id.page_indicator); + CirclePageIndicator pageIndicator = findViewById(R.id.page_indicator); pageIndicator.setViewPager(pager); loadLastFragment(); pager.onSaveInstanceState(); + + navList.post(this::supportStartPostponedEnterTransition); } @Override @@ -297,7 +307,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem return true; } - public void notifyMediaPositionChanged() { + private void notifyMediaPositionChanged() { if(pagerAdapter == null) { return; } @@ -347,7 +357,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem @Override public boolean onOptionsItemSelected(MenuItem item) { - return drawerToggle != null && drawerToggle.onOptionsItemSelected(item) || super.onOptionsItemSelected(item); + return (drawerToggle != null && drawerToggle.onOptionsItemSelected(item)) || super.onOptionsItemSelected(item); } @Override @@ -387,12 +397,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem new RenameFeedDialog(this, feed).show(); return true; case R.id.remove_item: - final FeedRemover remover = new FeedRemover(this, feed) { - @Override - protected void onPostExecute(Void result) { - super.onPostExecute(result); - } - }; + final FeedRemover remover = new FeedRemover(this, feed); ConfirmationDialog conDialog = new ConfirmationDialog(this, R.string.remove_feed_label, getString(R.string.feed_delete_confirmation_msg, feed.getTitle())) { @@ -404,12 +409,12 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem Playable playable = controller.getMedia(); if (playable != null && playable instanceof FeedMedia) { FeedMedia media = (FeedMedia) playable; - if (media.getItem().getFeed().getId() == feed.getId()) { + if (media.getItem() != null && media.getItem().getFeed() != null && + media.getItem().getFeed().getId() == feed.getId()) { Log.d(TAG, "Currently playing episode is about to be deleted, skipping"); remover.skipOnCompletion = true; if(controller.getStatus() == PlayerStatus.PLAYING) { - sendBroadcast(new Intent( - PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE)); + IntentUtils.sendLocalBroadcast(MediaplayerInfoActivity.this, PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE); } } } @@ -438,7 +443,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem } } - public void showDrawerPreferencesDialog() { + private void showDrawerPreferencesDialog() { final List<String> hiddenDrawerItems = UserPreferences.getHiddenDrawerItems(); String[] navLabels = new String[NAV_DRAWER_TAGS.length]; final boolean[] checked = new boolean[NAV_DRAWER_TAGS.length]; @@ -467,8 +472,8 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem private DBReader.NavDrawerData navDrawerData; private void loadData() { - subscription = Observable.fromCallable(DBReader::getNavDrawerData) - .subscribeOn(Schedulers.newThread()) + disposable = Observable.fromCallable(DBReader::getNavDrawerData) + .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(result -> { navDrawerData = result; @@ -483,14 +488,12 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem View parentLayout = findViewById(R.id.drawer_layout); Snackbar snackbar = Snackbar.make(parentLayout, event.message, Snackbar.LENGTH_SHORT); if (event.action != null) { - snackbar.setAction(getString(R.string.undo), v -> { - event.action.run(); - }); + snackbar.setAction(getString(R.string.undo), v -> event.action.run()); } snackbar.show(); } - private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { + private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { @Override public void update(EventDistributor eventDistributor, Integer arg) { diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java index f6bf11e66..73da9a834 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java @@ -5,8 +5,9 @@ import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; -import android.os.Looper; +import android.support.annotation.UiThread; import android.support.v4.app.NavUtils; +import android.support.v7.app.ActionBar; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.text.TextUtils; @@ -27,6 +28,7 @@ import android.widget.TextView; import com.bumptech.glide.Glide; +import com.bumptech.glide.request.RequestOptions; import org.apache.commons.lang3.StringUtils; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; @@ -55,7 +57,6 @@ import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DownloadRequestException; import de.danoeh.antennapod.core.storage.DownloadRequester; import de.danoeh.antennapod.core.syndication.handler.FeedHandler; -import de.danoeh.antennapod.core.syndication.handler.FeedHandlerResult; import de.danoeh.antennapod.core.syndication.handler.UnsupportedFeedtypeException; import de.danoeh.antennapod.core.util.DownloadError; import de.danoeh.antennapod.core.util.FileNameGenerator; @@ -65,11 +66,10 @@ import de.danoeh.antennapod.core.util.syndication.FeedDiscoverer; import de.danoeh.antennapod.core.util.syndication.HtmlToPlainText; import de.danoeh.antennapod.dialog.AuthenticationDialog; import de.greenrobot.event.EventBus; -import rx.Observable; -import rx.Subscriber; -import rx.Subscription; -import rx.android.schedulers.AndroidSchedulers; -import rx.schedulers.Schedulers; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; /** * Downloads a feed from a feed URL and parses it. Subclasses can display the @@ -84,7 +84,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity { public static final String ARG_FEEDURL = "arg.feedurl"; // Optional argument: specify a title for the actionbar. public static final String ARG_TITLE = "title"; - public static final int RESULT_ERROR = 2; + private static final int RESULT_ERROR = 2; private static final String TAG = "OnlineFeedViewActivity"; private static final int EVENTS = EventDistributor.FEED_LIST_UPDATE; private volatile List<Feed> feeds; @@ -98,23 +98,21 @@ public class OnlineFeedViewActivity extends AppCompatActivity { private Button subscribeButton; - private Subscription download; - private Subscription parser; - private Subscription updater; - private EventDistributor.EventListener listener = new EventDistributor.EventListener() { + private Disposable download; + private Disposable parser; + private Disposable updater; + private final EventDistributor.EventListener listener = new EventDistributor.EventListener() { @Override public void update(EventDistributor eventDistributor, Integer arg) { if ((arg & EventDistributor.FEED_LIST_UPDATE) != 0) { updater = Observable.fromCallable(DBReader::getFeedList) - .subscribeOn(Schedulers.newThread()) + .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( feeds -> { OnlineFeedViewActivity.this.feeds = feeds; setSubscribeButtonState(feed); - }, error -> { - Log.e(TAG, Log.getStackTraceString(error)); - } + }, error -> Log.e(TAG, Log.getStackTraceString(error)) ); } else if ((arg & EVENTS) != 0) { setSubscribeButtonState(feed); @@ -131,10 +129,13 @@ public class OnlineFeedViewActivity extends AppCompatActivity { protected void onCreate(Bundle savedInstanceState) { setTheme(UserPreferences.getTheme()); super.onCreate(savedInstanceState); - getSupportActionBar().setDisplayHomeAsUpEnabled(true); + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + actionBar.setDisplayHomeAsUpEnabled(true); + } - if (getIntent() != null && getIntent().hasExtra(ARG_TITLE)) { - getSupportActionBar().setTitle(getIntent().getStringExtra(ARG_TITLE)); + if (actionBar != null && getIntent() != null && getIntent().hasExtra(ARG_TITLE)) { + actionBar.setTitle(getIntent().getStringExtra(ARG_TITLE)); } StorageUtils.checkStorageAvailability(this); @@ -144,9 +145,11 @@ public class OnlineFeedViewActivity extends AppCompatActivity { feedUrl = getIntent().getStringExtra(ARG_FEEDURL); } else if (TextUtils.equals(getIntent().getAction(), Intent.ACTION_SEND) || TextUtils.equals(getIntent().getAction(), Intent.ACTION_VIEW)) { - feedUrl = (TextUtils.equals(getIntent().getAction(), Intent.ACTION_SEND)) + feedUrl = TextUtils.equals(getIntent().getAction(), Intent.ACTION_SEND) ? getIntent().getStringExtra(Intent.EXTRA_TEXT) : getIntent().getDataString(); - getSupportActionBar().setTitle(R.string.add_feed_label); + if (actionBar != null) { + actionBar.setTitle(R.string.add_feed_label); + } } else { throw new IllegalArgumentException("Activity must be started with feedurl argument!"); } @@ -210,13 +213,13 @@ public class OnlineFeedViewActivity extends AppCompatActivity { public void onDestroy() { super.onDestroy(); if(updater != null) { - updater.unsubscribe(); + updater.dispose(); } if(download != null) { - download.unsubscribe(); + download.dispose(); } if(parser != null) { - parser.unsubscribe(); + parser.dispose(); } } @@ -265,18 +268,13 @@ public class OnlineFeedViewActivity extends AppCompatActivity { feed.getDownload_url(), "OnlineFeed", 0, Feed.FEEDFILETYPE_FEED, username, password, true, null); - download = Observable.create(new Observable.OnSubscribe<DownloadStatus>() { - @Override - public void call(Subscriber<? super DownloadStatus> subscriber) { - feeds = DBReader.getFeedList(); - downloader = new HttpDownloader(request); - downloader.call(); - Log.d(TAG, "Download was completed"); - subscriber.onNext(downloader.getResult()); - subscriber.onCompleted(); - } + download = Observable.fromCallable(() -> { + feeds = DBReader.getFeedList(); + downloader = new HttpDownloader(request); + downloader.call(); + return downloader.getResult(); }) - .subscribeOn(Schedulers.newThread()) + .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(this::checkDownloadResult, error -> Log.e(TAG, Log.getStackTraceString(error))); @@ -286,6 +284,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity { if (status == null) { Log.wtf(TAG, "DownloadStatus returned by Downloader was null"); finish(); + return; } if (status.isCancelled()) { return; @@ -300,7 +299,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity { } } else { String errorMsg = status.getReason().getErrorString(OnlineFeedViewActivity.this); - if (errorMsg != null && status.getReasonDetailed() != null) { + if (status.getReasonDetailed() != null) { errorMsg += " (" + status.getReasonDetailed() + ")"; } showErrorDialog(errorMsg); @@ -308,44 +307,43 @@ public class OnlineFeedViewActivity extends AppCompatActivity { } private void parseFeed() { - if (feed == null || feed.getFile_url() == null && feed.isDownloaded()) { + if (feed == null || (feed.getFile_url() == null && feed.isDownloaded())) { throw new IllegalStateException("feed must be non-null and downloaded when parseFeed is called"); } Log.d(TAG, "Parsing feed"); - parser = Observable.create(new Observable.OnSubscribe<FeedHandlerResult>() { - @Override - public void call(Subscriber<? super FeedHandlerResult> subscriber) { - FeedHandler handler = new FeedHandler(); - try { - FeedHandlerResult result = handler.parseFeed(feed); - subscriber.onNext(result); - } catch (UnsupportedFeedtypeException e) { - Log.d(TAG, "Unsupported feed type detected"); - if (TextUtils.equals("html", e.getRootElement().toLowerCase())) { - showFeedDiscoveryDialog(new File(feed.getFile_url()), feed.getDownload_url()); - } else { - subscriber.onError(e); - } - } catch (Exception e) { - Log.e(TAG, Log.getStackTraceString(e)); - subscriber.onError(e); - } finally { - boolean rc = new File(feed.getFile_url()).delete(); - Log.d(TAG, "Deleted feed source file. Result: " + rc); - subscriber.onCompleted(); + parser = Observable.fromCallable(() -> { + FeedHandler handler = new FeedHandler(); + try { + return handler.parseFeed(feed); + } catch (UnsupportedFeedtypeException e) { + Log.d(TAG, "Unsupported feed type detected"); + if ("html".equalsIgnoreCase(e.getRootElement())) { + showFeedDiscoveryDialog(new File(feed.getFile_url()), feed.getDownload_url()); + return null; + } else { + throw e; } + } catch (Exception e) { + Log.e(TAG, Log.getStackTraceString(e)); + throw e; + } finally { + boolean rc = new File(feed.getFile_url()).delete(); + Log.d(TAG, "Deleted feed source file. Result: " + rc); } }) - .subscribeOn(Schedulers.newThread()) + .subscribeOn(Schedulers.computation()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(result -> { - beforeShowFeedInformation(result.feed); - showFeedInformation(result.feed, result.alternateFeedUrls); + if(result != null) { + beforeShowFeedInformation(result.feed); + showFeedInformation(result.feed, result.alternateFeedUrls); + } }, error -> { String errorMsg = DownloadError.ERROR_PARSER_EXCEPTION.getErrorString( OnlineFeedViewActivity.this) + " (" + error.getMessage() + ")"; showErrorDialog(errorMsg); + Log.d(TAG, "Feed parser exception: " + Log.getStackTraceString(error)); }); } @@ -382,30 +380,30 @@ public class OnlineFeedViewActivity extends AppCompatActivity { this.feed = feed; this.selectedDownloadUrl = feed.getDownload_url(); EventDistributor.getInstance().register(listener); - ListView listView = (ListView) findViewById(R.id.listview); - LayoutInflater inflater = (LayoutInflater) - getSystemService(Context.LAYOUT_INFLATER_SERVICE); + ListView listView = findViewById(R.id.listview); + LayoutInflater inflater = LayoutInflater.from(this); View header = inflater.inflate(R.layout.onlinefeedview_header, listView, false); listView.addHeaderView(header); listView.setAdapter(new FeedItemlistDescriptionAdapter(this, 0, feed.getItems())); - ImageView cover = (ImageView) header.findViewById(R.id.imgvCover); - TextView title = (TextView) header.findViewById(R.id.txtvTitle); - TextView author = (TextView) header.findViewById(R.id.txtvAuthor); - TextView description = (TextView) header.findViewById(R.id.txtvDescription); - Spinner spAlternateUrls = (Spinner) header.findViewById(R.id.spinnerAlternateUrls); + ImageView cover = header.findViewById(R.id.imgvCover); + TextView title = header.findViewById(R.id.txtvTitle); + TextView author = header.findViewById(R.id.txtvAuthor); + TextView description = header.findViewById(R.id.txtvDescription); + Spinner spAlternateUrls = header.findViewById(R.id.spinnerAlternateUrls); - subscribeButton = (Button) header.findViewById(R.id.butSubscribe); + subscribeButton = header.findViewById(R.id.butSubscribe); - if (feed.getImage() != null && StringUtils.isNotBlank(feed.getImage().getDownload_url())) { + if (StringUtils.isNotBlank(feed.getImageUrl())) { Glide.with(this) - .load(feed.getImage().getDownload_url()) - .placeholder(R.color.light_gray) - .error(R.color.light_gray) - .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) - .fitCenter() - .dontAnimate() + .load(feed.getImageUrl()) + .apply(new RequestOptions() + .placeholder(R.color.light_gray) + .error(R.color.light_gray) + .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) + .fitCenter() + .dontAnimate()) .into(cover); } @@ -414,7 +412,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity { description.setText(feed.getDescription()); subscribeButton.setOnClickListener(v -> { - if(feed != null && feedInFeedlist(feed)) { + if(feedInFeedlist(feed)) { Intent intent = new Intent(OnlineFeedViewActivity.this, MainActivity.class); // feed.getId() is always 0, we have to retrieve the id from the feed list from // the database @@ -508,8 +506,8 @@ public class OnlineFeedViewActivity extends AppCompatActivity { return 0; } + @UiThread private void showErrorDialog(String errorMsg) { - assert(Looper.myLooper() == Looper.getMainLooper()); // run on UI thread if (!isFinishing() && !isPaused) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(R.string.error_label); @@ -586,7 +584,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity { private class FeedViewAuthenticationDialog extends AuthenticationDialog { - private String feedUrl; + private final String feedUrl; FeedViewAuthenticationDialog(Context context, int titleRes, String feedUrl) { super(context, titleRes, true, false, null, null); diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OpmlFeedChooserActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OpmlFeedChooserActivity.java index 355e0f372..72759c59c 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/OpmlFeedChooserActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/OpmlFeedChooserActivity.java @@ -39,9 +39,9 @@ public class OpmlFeedChooserActivity extends AppCompatActivity { super.onCreate(savedInstanceState); setContentView(R.layout.opml_selection); - butConfirm = (Button) findViewById(R.id.butConfirm); - butCancel = (Button) findViewById(R.id.butCancel); - feedlist = (ListView) findViewById(R.id.feedlist); + butConfirm = findViewById(R.id.butConfirm); + butCancel = findViewById(R.id.butCancel); + feedlist = findViewById(R.id.feedlist); feedlist.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); listAdapter = new ArrayAdapter<>(this, @@ -97,7 +97,7 @@ public class OpmlFeedChooserActivity extends AppCompatActivity { } private List<String> getTitleList() { - List<String> result = new ArrayList<String>(); + List<String> result = new ArrayList<>(); if (OpmlImportHolder.getReadElements() != null) { for (OpmlElement element : OpmlImportHolder.getReadElements()) { result.add(element.getText()); diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportBaseActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportBaseActivity.java index 07b0b3cdb..c04ae051e 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportBaseActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportBaseActivity.java @@ -68,7 +68,7 @@ public class OpmlImportBaseActivity extends AppCompatActivity { } } - protected void importUri(@Nullable Uri uri) { + void importUri(@Nullable Uri uri) { if(uri == null) { new MaterialDialog.Builder(this) .content(R.string.opml_import_error_no_file) @@ -114,7 +114,7 @@ public class OpmlImportBaseActivity extends AppCompatActivity { } /** Starts the import process. */ - protected void startImport() { + private void startImport() { try { Reader mReader = new InputStreamReader(getContentResolver().openInputStream(uri), LangUtils.UTF_8); importWorker = new OpmlImportWorker(this, mReader) { @@ -144,7 +144,7 @@ public class OpmlImportBaseActivity extends AppCompatActivity { } } - protected boolean finishWhenCanceled() { + boolean finishWhenCanceled() { return false; } diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromPathActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromPathActivity.java index eb6b473d2..a63d3b735 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromPathActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportFromPathActivity.java @@ -36,26 +36,25 @@ public class OpmlImportFromPathActivity extends OpmlImportBaseActivity { getSupportActionBar().setDisplayHomeAsUpEnabled(true); setContentView(R.layout.opml_import); - final TextView txtvHeaderExplanation1 = (TextView) findViewById(R.id.txtvHeadingExplanation1); - final TextView txtvExplanation1 = (TextView) findViewById(R.id.txtvExplanation1); - final TextView txtvHeaderExplanation2 = (TextView) findViewById(R.id.txtvHeadingExplanation2); - final TextView txtvExplanation2 = (TextView) findViewById(R.id.txtvExplanation2); - final TextView txtvHeaderExplanation3 = (TextView) findViewById(R.id.txtvHeadingExplanation3); + final TextView txtvHeaderExplanation1 = findViewById(R.id.txtvHeadingExplanation1); + final TextView txtvExplanation1 = findViewById(R.id.txtvExplanation1); + final TextView txtvHeaderExplanation2 = findViewById(R.id.txtvHeadingExplanation2); + final TextView txtvExplanation2 = findViewById(R.id.txtvExplanation2); + final TextView txtvHeaderExplanation3 = findViewById(R.id.txtvHeadingExplanation3); - Button butChooseFilesystem = (Button) findViewById(R.id.butChooseFileFromFilesystem); + Button butChooseFilesystem = findViewById(R.id.butChooseFileFromFilesystem); butChooseFilesystem.setOnClickListener(v -> chooseFileFromFilesystem()); - Button butChooseExternal = (Button) findViewById(R.id.butChooseFileFromExternal); + Button butChooseExternal = findViewById(R.id.butChooseFileFromExternal); butChooseExternal.setOnClickListener(v -> chooseFileFromExternal()); int nextOption = 1; String optionLabel = getString(R.string.opml_import_option); intentPickAction = new Intent(Intent.ACTION_PICK); - intentPickAction.setData(Uri.parse("file://")); if(!IntentUtils.isCallable(getApplicationContext(), intentPickAction)) { intentPickAction.setData(null); - if(false == IntentUtils.isCallable(getApplicationContext(), intentPickAction)) { + if(!IntentUtils.isCallable(getApplicationContext(), intentPickAction)) { txtvHeaderExplanation1.setVisibility(View.GONE); txtvExplanation1.setVisibility(View.GONE); findViewById(R.id.divider1).setVisibility(View.GONE); diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportHolder.java b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportHolder.java index b01cf43e4..dc5570dc0 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportHolder.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportHolder.java @@ -14,6 +14,8 @@ import de.danoeh.antennapod.core.export.opml.OpmlElement; */ public class OpmlImportHolder { + private OpmlImportHolder(){} + private static ArrayList<OpmlElement> readElements; public static ArrayList<OpmlElement> getReadElements() { diff --git a/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java index dd932814f..452e91bd3 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java @@ -1,19 +1,20 @@ package de.danoeh.antennapod.activity; -import android.annotation.TargetApi; -import android.app.Activity; import android.content.Intent; -import android.os.Build; import android.os.Bundle; -import android.preference.Preference; -import android.preference.PreferenceFragment; import android.support.v7.app.ActionBar; import android.support.v7.app.AppCompatActivity; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceFragmentCompat; +import android.support.v7.preference.PreferenceScreen; import android.view.Menu; import android.view.MenuItem; import android.view.ViewGroup; import android.widget.FrameLayout; +import com.bytehamster.lib.preferencesearch.SearchPreferenceResult; +import com.bytehamster.lib.preferencesearch.SearchPreferenceResultListener; + import java.lang.ref.WeakReference; import de.danoeh.antennapod.R; @@ -24,25 +25,40 @@ import de.danoeh.antennapod.preferences.PreferenceController; * PreferenceActivity for API 11+. In order to change the behavior of the preference UI, see * PreferenceController. */ -public class PreferenceActivity extends AppCompatActivity { +public class PreferenceActivity extends AppCompatActivity implements SearchPreferenceResultListener { + public static final String PARAM_RESOURCE = "resource"; private static WeakReference<PreferenceActivity> instance; private PreferenceController preferenceController; - private MainFragment prefFragment; private final PreferenceController.PreferenceUI preferenceUI = new PreferenceController.PreferenceUI() { - @TargetApi(Build.VERSION_CODES.HONEYCOMB) + private PreferenceFragmentCompat fragment; + + @Override + public void setFragment(PreferenceFragmentCompat fragment) { + this.fragment = fragment; + } + + @Override + public PreferenceFragmentCompat getFragment() { + return fragment; + } + @Override public Preference findPreference(CharSequence key) { - return prefFragment.findPreference(key); + return fragment.findPreference(key); + } + + @Override + public PreferenceScreen getPreferenceScreen() { + return fragment.getPreferenceScreen(); } @Override - public Activity getActivity() { + public AppCompatActivity getActivity() { return PreferenceActivity.this; } }; - @TargetApi(Build.VERSION_CODES.HONEYCOMB) @Override protected void onCreate(Bundle savedInstanceState) { // This must be the FIRST thing we do, otherwise other code may not have the @@ -68,8 +84,21 @@ public class PreferenceActivity extends AppCompatActivity { // since the MainFragment depends on the preferenceController already being created preferenceController = new PreferenceController(preferenceUI); - prefFragment = new MainFragment(); - getFragmentManager().beginTransaction().replace(R.id.content, prefFragment).commit(); + showPreferenceScreen(R.xml.preferences, false); + } + + private void showPreferenceScreen(int screen, boolean addHistory) { + PreferenceFragmentCompat prefFragment = new MainFragment(); + preferenceUI.setFragment(prefFragment); + Bundle args = new Bundle(); + args.putInt(PARAM_RESOURCE, screen); + prefFragment.setArguments(args); + if (addHistory) { + getSupportFragmentManager().beginTransaction().replace(R.id.content, prefFragment) + .addToBackStack(getString(PreferenceController.getTitleOfPage(screen))).commit(); + } else { + getSupportFragmentManager().beginTransaction().replace(R.id.content, prefFragment).commit(); + } } @Override @@ -88,24 +117,40 @@ public class PreferenceActivity extends AppCompatActivity { public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case android.R.id.home: - finish(); + if (getSupportFragmentManager().getBackStackEntryCount() == 0) { + finish(); + } else { + getSupportFragmentManager().popBackStack(); + } return true; default: return false; } } - @TargetApi(Build.VERSION_CODES.HONEYCOMB) - public static class MainFragment extends PreferenceFragment { + @Override + public void onSearchResultClicked(SearchPreferenceResult result) { + showPreferenceScreen(result.getResourceFile(), true); + result.highlight(preferenceUI.getFragment()); + } + + public static class MainFragment extends PreferenceFragmentCompat { + private int screen; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setRetainInstance(true); - addPreferencesFromResource(R.xml.preferences); + } + + @Override + public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { + screen = getArguments().getInt(PARAM_RESOURCE); + addPreferencesFromResource(screen); PreferenceActivity activity = instance.get(); - if(activity != null && activity.preferenceController != null) { - activity.preferenceController.onCreate(); + if (activity != null && activity.preferenceController != null) { + activity.preferenceUI.setFragment(this); + activity.preferenceController.onCreate(screen); } } @@ -114,15 +159,17 @@ public class PreferenceActivity extends AppCompatActivity { super.onResume(); PreferenceActivity activity = instance.get(); if(activity != null && activity.preferenceController != null) { - activity.preferenceController.onResume(); + activity.setTitle(PreferenceController.getTitleOfPage(screen)); + activity.preferenceUI.setFragment(this); + activity.preferenceController.onResume(screen); } } @Override public void onPause() { PreferenceActivity activity = instance.get(); - if(activity != null && activity.preferenceController != null) { - activity.preferenceController.onPause(); + if (screen == R.xml.preferences_gpodder) { + activity.preferenceController.unregisterGpodnet(); } super.onPause(); } @@ -130,8 +177,8 @@ public class PreferenceActivity extends AppCompatActivity { @Override public void onStop() { PreferenceActivity activity = instance.get(); - if(activity != null && activity.preferenceController != null) { - activity.preferenceController.onStop(); + if (screen == R.xml.preferences_storage) { + activity.preferenceController.unsubscribeExportSubscription(); } super.onStop(); } diff --git a/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivityGingerbread.java b/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivityGingerbread.java deleted file mode 100644 index 390bec15c..000000000 --- a/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivityGingerbread.java +++ /dev/null @@ -1,97 +0,0 @@ -package de.danoeh.antennapod.activity; - -import android.annotation.SuppressLint; -import android.app.Activity; -import android.content.Intent; -import android.content.res.Resources.Theme; -import android.os.Bundle; -import android.preference.Preference; -import android.preference.PreferenceScreen; - -import de.danoeh.antennapod.R; -import de.danoeh.antennapod.core.preferences.UserPreferences; -import de.danoeh.antennapod.preferences.PreferenceController; - -/** - * PreferenceActivity for API 10. In order to change the behavior of the preference UI, see - * PreferenceController. - */ -public class PreferenceActivityGingerbread extends android.preference.PreferenceActivity { - private static final String TAG = "PreferenceActivity"; - private final PreferenceController.PreferenceUI preferenceUI = new PreferenceController.PreferenceUI() { - - @SuppressWarnings("deprecation") - @Override - public Preference findPreference(CharSequence key) { - return PreferenceActivityGingerbread.this.findPreference(key); - } - - @Override - public Activity getActivity() { - return PreferenceActivityGingerbread.this; - } - }; - private PreferenceController preferenceController; - - @SuppressLint("NewApi") - @SuppressWarnings("deprecation") - @Override - public void onCreate(Bundle savedInstanceState) { - setTheme(UserPreferences.getTheme()); - super.onCreate(savedInstanceState); - - addPreferencesFromResource(R.xml.preferences); - preferenceController = new PreferenceController(preferenceUI); - preferenceController.onCreate(); - } - - - @Override - protected void onResume() { - super.onResume(); - preferenceController.onResume(); - } - - @Override - protected void onPause() { - preferenceController.onPause(); - super.onPause(); - } - - @Override - protected void onStop() { - preferenceController.onStop(); - super.onStop(); - } - - @Override - protected void onApplyThemeResource(Theme theme, int resid, boolean first) { - theme.applyStyle(UserPreferences.getTheme(), true); - } - - @Override - protected void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - preferenceController.onActivityResult(requestCode, resultCode, data); - } - - @SuppressWarnings("deprecation") - @Override - public boolean onPreferenceTreeClick(PreferenceScreen preferenceScreen, - Preference preference) { - super.onPreferenceTreeClick(preferenceScreen, preference); - if (preference != null) - if (preference instanceof PreferenceScreen) - if (((PreferenceScreen) preference).getDialog() != null) - ((PreferenceScreen) preference) - .getDialog() - .getWindow() - .getDecorView() - .setBackgroundDrawable( - this.getWindow().getDecorView() - .getBackground().getConstantState() - .newDrawable() - ); - return false; - } -} diff --git a/app/src/main/java/de/danoeh/antennapod/activity/SplashActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/SplashActivity.java index b92ac8577..52102eee1 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/SplashActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/SplashActivity.java @@ -1,23 +1,51 @@ package de.danoeh.antennapod.activity; import android.content.Intent; +import android.graphics.PorterDuff; +import android.graphics.drawable.Drawable; +import android.os.Build; import android.os.Bundle; import android.support.annotation.Nullable; +import android.support.v4.graphics.drawable.DrawableCompat; import android.support.v7.app.AppCompatActivity; +import android.widget.ProgressBar; + +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.core.storage.PodDBAdapter; +import io.reactivex.Completable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.schedulers.Schedulers; /** - * Creator: vbarad - * Date: 2016-12-03 - * Project: AntennaPod + * Shows the AntennaPod logo while waiting for the main activity to start */ - public class SplashActivity extends AppCompatActivity { - @Override - protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); + @Override + protected void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.splash); + + ProgressBar progressBar = findViewById(R.id.progressBar); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) { + Drawable wrapDrawable = DrawableCompat.wrap(progressBar.getIndeterminateDrawable()); + DrawableCompat.setTint(wrapDrawable, 0xffffffff); + progressBar.setIndeterminateDrawable(DrawableCompat.unwrap(wrapDrawable)); + } else { + progressBar.getIndeterminateDrawable().setColorFilter(0xffffffff, PorterDuff.Mode.SRC_IN); + } - Intent intent = new Intent(this, MainActivity.class); - startActivity(intent); - finish(); - } + Completable.create(subscriber -> { + // Trigger schema updates + PodDBAdapter.getInstance().open(); + PodDBAdapter.getInstance().close(); + subscriber.onComplete(); + }) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(() -> { + Intent intent = new Intent(SplashActivity.this, MainActivity.class); + startActivity(intent); + finish(); + }); + } } diff --git a/app/src/main/java/de/danoeh/antennapod/activity/StatisticsActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/StatisticsActivity.java index b2ff43c43..37199ccf7 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/StatisticsActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/StatisticsActivity.java @@ -20,10 +20,10 @@ import de.danoeh.antennapod.adapter.StatisticsListAdapter; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.util.Converter; -import rx.Observable; -import rx.Subscription; -import rx.android.schedulers.AndroidSchedulers; -import rx.schedulers.Schedulers; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; /** * Displays the 'statistics' screen @@ -35,7 +35,7 @@ public class StatisticsActivity extends AppCompatActivity private static final String PREF_NAME = "StatisticsActivityPrefs"; private static final String PREF_COUNT_ALL = "countAll"; - private Subscription subscription; + private Disposable disposable; private TextView totalTimeTextView; private ListView feedStatisticsList; private ProgressBar progressBar; @@ -53,9 +53,9 @@ public class StatisticsActivity extends AppCompatActivity prefs = getSharedPreferences(PREF_NAME, MODE_PRIVATE); countAll = prefs.getBoolean(PREF_COUNT_ALL, false); - totalTimeTextView = (TextView) findViewById(R.id.total_time); - feedStatisticsList = (ListView) findViewById(R.id.statistics_list); - progressBar = (ProgressBar) findViewById(R.id.progressBar); + totalTimeTextView = findViewById(R.id.total_time); + feedStatisticsList = findViewById(R.id.statistics_list); + progressBar = findViewById(R.id.progressBar); listAdapter = new StatisticsListAdapter(this); listAdapter.setCountAll(countAll); feedStatisticsList.setAdapter(listAdapter); @@ -119,21 +119,19 @@ public class StatisticsActivity extends AppCompatActivity } private void loadStatistics() { - if (subscription != null) { - subscription.unsubscribe(); + if (disposable != null) { + disposable.dispose(); } - subscription = Observable.fromCallable(() -> DBReader.getStatistics(countAll)) - .subscribeOn(Schedulers.newThread()) + disposable = Observable.fromCallable(() -> DBReader.getStatistics(countAll)) + .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(result -> { - if (result != null) { - totalTimeTextView.setText(Converter - .shortLocalizedDuration(this, countAll ? result.totalTimeCountAll : result.totalTime)); - listAdapter.update(result.feedTime); - progressBar.setVisibility(View.GONE); - totalTimeTextView.setVisibility(View.VISIBLE); - feedStatisticsList.setVisibility(View.VISIBLE); - } + totalTimeTextView.setText(Converter + .shortLocalizedDuration(this, countAll ? result.totalTimeCountAll : result.totalTime)); + listAdapter.update(result.feedTime); + progressBar.setVisibility(View.GONE); + totalTimeTextView.setVisibility(View.VISIBLE); + feedStatisticsList.setVisibility(View.VISIBLE); }, error -> Log.e(TAG, Log.getStackTraceString(error))); } diff --git a/app/src/main/java/de/danoeh/antennapod/activity/StorageErrorActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/StorageErrorActivity.java index d104a9e93..20e34cc52 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/StorageErrorActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/StorageErrorActivity.java @@ -42,7 +42,7 @@ public class StorageErrorActivity extends AppCompatActivity { setContentView(R.layout.storage_error); - Button btnChooseDataFolder = (Button) findViewById(R.id.btnChooseDataFolder); + Button btnChooseDataFolder = findViewById(R.id.btnChooseDataFolder); btnChooseDataFolder.setOnClickListener(v -> { if (Build.VERSION_CODES.KITKAT <= Build.VERSION.SDK_INT && Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP_MR1) { @@ -164,7 +164,7 @@ public class StorageErrorActivity extends AppCompatActivity { startActivity(new Intent(this, MainActivity.class)); } - private BroadcastReceiver mediaUpdate = new BroadcastReceiver() { + private final BroadcastReceiver mediaUpdate = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { diff --git a/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java index 8531a7356..78cc15b2c 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java @@ -7,14 +7,19 @@ import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.support.v4.view.WindowCompat; +import android.support.v7.app.ActionBar; +import android.text.TextUtils; import android.util.Log; import android.util.Pair; +import android.view.Menu; +import android.view.MenuItem; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.View; import android.view.WindowManager; import android.view.animation.Animation; import android.view.animation.AnimationUtils; +import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.SeekBar; @@ -24,9 +29,10 @@ import java.util.concurrent.atomic.AtomicBoolean; import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.feed.MediaType; +import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.service.playback.PlaybackService; import de.danoeh.antennapod.core.service.playback.PlayerStatus; -import de.danoeh.antennapod.core.util.playback.ExternalMedia; +import de.danoeh.antennapod.core.util.gui.PictureInPictureUtil; import de.danoeh.antennapod.core.util.playback.Playable; import de.danoeh.antennapod.view.AspectRatioVideoView; @@ -45,12 +51,13 @@ public class VideoplayerActivity extends MediaplayerActivity { private VideoControlsHider videoControlsHider = new VideoControlsHider(this); - private AtomicBoolean isSetup = new AtomicBoolean(false); + private final AtomicBoolean isSetup = new AtomicBoolean(false); private LinearLayout controls; private LinearLayout videoOverlay; private AspectRatioVideoView videoview; private ProgressBar progressIndicator; + private FrameLayout videoframe; @Override protected void chooseTheme() { @@ -70,20 +77,8 @@ public class VideoplayerActivity extends MediaplayerActivity { @Override protected void onResume() { super.onResume(); - if (getIntent().getAction() != null - && getIntent().getAction().equals(Intent.ACTION_VIEW)) { - Intent intent = getIntent(); - Log.d(TAG, "Received VIEW intent: " + intent.getData().getPath()); - ExternalMedia media = new ExternalMedia(intent.getData().getPath(), - MediaType.VIDEO); - Intent launchIntent = new Intent(this, PlaybackService.class); - launchIntent.putExtra(PlaybackService.EXTRA_PLAYABLE, media); - launchIntent.putExtra(PlaybackService.EXTRA_START_WHEN_PREPARED, - true); - launchIntent.putExtra(PlaybackService.EXTRA_SHOULD_STREAM, false); - launchIntent.putExtra(PlaybackService.EXTRA_PREPARE_IMMEDIATELY, - true); - startService(launchIntent); + if (TextUtils.equals(getIntent().getAction(), Intent.ACTION_VIEW)) { + playExternalMedia(getIntent(), MediaType.VIDEO); } else if (PlaybackService.isCasting()) { Intent intent = PlaybackService.getPlayerActivityIntent(this); if (!intent.getComponent().getClassName().equals(VideoplayerActivity.class.getName())) { @@ -95,10 +90,27 @@ public class VideoplayerActivity extends MediaplayerActivity { } @Override + protected void onStop() { + super.onStop(); + if (!PictureInPictureUtil.isInPictureInPictureMode(this)) { + videoControlsHider.stop(); + } + } + + @Override + public void onUserLeaveHint () { + if (!PictureInPictureUtil.isInPictureInPictureMode(this) && UserPreferences.getVideoBackgroundBehavior() + == UserPreferences.VideoBackgroundBehavior.PICTURE_IN_PICTURE) { + compatEnterPictureInPicture(); + } + } + + @Override protected void onPause() { - videoControlsHider.stop(); - if (controller != null && controller.getStatus() == PlayerStatus.PLAYING) { - controller.pause(); + if (!PictureInPictureUtil.isInPictureInPictureMode(this)) { + if (controller != null && controller.getStatus() == PlayerStatus.PLAYING) { + controller.pause(); + } } super.onPause(); } @@ -126,42 +138,38 @@ public class VideoplayerActivity extends MediaplayerActivity { @Override protected void setupGUI() { - if(isSetup.getAndSet(true)) { + if (isSetup.getAndSet(true)) { return; } super.setupGUI(); getSupportActionBar().setDisplayHomeAsUpEnabled(true); - controls = (LinearLayout) findViewById(R.id.controls); - videoOverlay = (LinearLayout) findViewById(R.id.overlay); - videoview = (AspectRatioVideoView) findViewById(R.id.videoview); - progressIndicator = (ProgressBar) findViewById(R.id.progressIndicator); + controls = findViewById(R.id.controls); + videoOverlay = findViewById(R.id.overlay); + videoview = findViewById(R.id.videoview); + videoframe = findViewById(R.id.videoframe); + progressIndicator = findViewById(R.id.progressIndicator); videoview.getHolder().addCallback(surfaceHolderCallback); - videoview.setOnTouchListener(onVideoviewTouched); + videoframe.setOnTouchListener(onVideoviewTouched); + videoOverlay.setOnTouchListener((view, motionEvent) -> true); // To suppress touches directly below the slider if (Build.VERSION.SDK_INT >= 16) { videoview.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION); } - if (Build.VERSION.SDK_INT >= 14) { - videoOverlay.setFitsSystemWindows(true); - } + videoOverlay.setFitsSystemWindows(true); setupVideoControlsToggler(); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); + + videoframe.getViewTreeObserver().addOnGlobalLayoutListener(() -> + videoview.setAvailableSize(videoframe.getWidth(), videoframe.getHeight())); } @Override protected void onAwaitingVideoSurface() { + setupVideoAspectRatio(); if (videoSurfaceCreated && controller != null) { Log.d(TAG, "Videosurface already created, setting videosurface now"); - - Pair<Integer, Integer> videoSize = controller.getVideoSize(); - if (videoSize != null && videoSize.first > 0 && videoSize.second > 0) { - Log.d(TAG, "Width,height of video: " + videoSize.first + ", " + videoSize.second); - videoview.setVideoSize(videoSize.first, videoSize.second); - } else { - Log.e(TAG, "Could not determine video size"); - } controller.setVideoSurface(videoview.getHolder()); } } @@ -180,8 +188,11 @@ public class VideoplayerActivity extends MediaplayerActivity { progressIndicator.setVisibility(View.INVISIBLE); } - View.OnTouchListener onVideoviewTouched = (v, event) -> { + private final View.OnTouchListener onVideoviewTouched = (v, event) -> { if (event.getAction() == MotionEvent.ACTION_DOWN) { + if (PictureInPictureUtil.isInPictureInPictureMode(this)) { + return true; + } videoControlsHider.stop(); toggleVideoControlsVisibility(); if (videoControlsShowing) { @@ -194,11 +205,23 @@ public class VideoplayerActivity extends MediaplayerActivity { }; @SuppressLint("NewApi") - void setupVideoControlsToggler() { + private void setupVideoControlsToggler() { videoControlsHider.stop(); videoControlsHider.start(); } + private void setupVideoAspectRatio() { + if (videoSurfaceCreated && controller != null) { + Pair<Integer, Integer> videoSize = controller.getVideoSize(); + if (videoSize != null && videoSize.first > 0 && videoSize.second > 0) { + Log.d(TAG, "Width,height of video: " + videoSize.first + ", " + videoSize.second); + videoview.setVideoSize(videoSize.first, videoSize.second); + } else { + Log.e(TAG, "Could not determine video size"); + } + } + } + private void toggleVideoControlsVisibility() { if (videoControlsShowing) { getSupportActionBar().hide(); @@ -247,14 +270,16 @@ public class VideoplayerActivity extends MediaplayerActivity { Log.e(TAG, "Couldn't attach surface to mediaplayer - reference to service was null"); } } - + setupVideoAspectRatio(); } @Override public void surfaceDestroyed(SurfaceHolder holder) { Log.d(TAG, "Videosurface was destroyed"); videoSurfaceCreated = false; - if (controller != null && !destroyingDueToReload) { + if (controller != null && !destroyingDueToReload + && UserPreferences.getVideoBackgroundBehavior() + != UserPreferences.VideoBackgroundBehavior.CONTINUE_PLAYING) { controller.notifyVideoSurfaceAbandoned(); } } @@ -263,6 +288,13 @@ public class VideoplayerActivity extends MediaplayerActivity { @Override protected void onReloadNotification(int notificationCode) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N && PictureInPictureUtil.isInPictureInPictureMode(this)) { + if (notificationCode == PlaybackService.EXTRA_CODE_AUDIO + || notificationCode == PlaybackService.EXTRA_CODE_CAST) { + finish(); + } + return; + } if (notificationCode == PlaybackService.EXTRA_CODE_AUDIO) { Log.d(TAG, "ReloadNotification received, switching to Audioplayer now"); destroyingDueToReload = true; @@ -307,28 +339,31 @@ public class VideoplayerActivity extends MediaplayerActivity { videoOverlay.startAnimation(animation); controls.startAnimation(animation); } - if (Build.VERSION.SDK_INT >= 14) { - videoview.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE); - } + videoview.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE); } @SuppressLint("NewApi") - private void hideVideoControls() { - final Animation animation = AnimationUtils.loadAnimation(this, R.anim.fade_out); - if (animation != null) { - videoOverlay.startAnimation(animation); - controls.startAnimation(animation); - } - if (Build.VERSION.SDK_INT >= 14) { - int videoviewFlag = (Build.VERSION.SDK_INT >= 16) ? View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION : 0; - getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE | View.SYSTEM_UI_FLAG_FULLSCREEN - | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | videoviewFlag); - videoOverlay.setFitsSystemWindows(true); + private void hideVideoControls(boolean showAnimation) { + if (showAnimation) { + final Animation animation = AnimationUtils.loadAnimation(this, R.anim.fade_out); + if (animation != null) { + videoOverlay.startAnimation(animation); + controls.startAnimation(animation); + } } + int videoviewFlag = (Build.VERSION.SDK_INT >= 16) ? View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION : 0; + getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE | View.SYSTEM_UI_FLAG_FULLSCREEN + | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | videoviewFlag); + videoOverlay.setFitsSystemWindows(true); + videoOverlay.setVisibility(View.GONE); controls.setVisibility(View.GONE); } + private void hideVideoControls() { + hideVideoControls(true); + } + @Override protected int getContentViewResourceId() { return R.layout.videoplayer_activity; @@ -344,24 +379,53 @@ public class VideoplayerActivity extends MediaplayerActivity { } } + @Override + public boolean onPrepareOptionsMenu(Menu menu) { + super.onPrepareOptionsMenu(menu); + if (PictureInPictureUtil.supportsPictureInPicture(this)) { + menu.findItem(R.id.player_go_to_picture_in_picture).setVisible(true); + } + return true; + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == R.id.player_go_to_picture_in_picture) { + compatEnterPictureInPicture(); + return true; + } + return super.onOptionsItemSelected(item); + } + + private void compatEnterPictureInPicture() { + if (PictureInPictureUtil.supportsPictureInPicture(this) && Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + getSupportActionBar().hide(); + hideVideoControls(false); + enterPictureInPictureMode(); + } + } + private static class VideoControlsHider extends Handler { private static final int DELAY = 2500; private WeakReference<VideoplayerActivity> activity; - public VideoControlsHider(VideoplayerActivity activity) { + VideoControlsHider(VideoplayerActivity activity) { this.activity = new WeakReference<>(activity); } private final Runnable hideVideoControls = () -> { - VideoplayerActivity vpa = activity.get(); - if(vpa == null) { + VideoplayerActivity vpa = activity != null ? activity.get() : null; + if (vpa == null) { return; } if (vpa.videoControlsShowing) { Log.d(TAG, "Hiding video controls"); - vpa.getSupportActionBar().hide(); + ActionBar actionBar = vpa.getSupportActionBar(); + if (actionBar != null) { + actionBar.hide(); + } vpa.hideVideoControls(); vpa.videoControlsShowing = false; } @@ -371,7 +435,7 @@ public class VideoplayerActivity extends MediaplayerActivity { this.postDelayed(hideVideoControls, DELAY); } - public void stop() { + void stop() { this.removeCallbacks(hideVideoControls); } diff --git a/app/src/main/java/de/danoeh/antennapod/activity/gpoddernet/GpodnetAuthenticationActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/gpoddernet/GpodnetAuthenticationActivity.java index 8ede947c5..8fcdb4371 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/gpoddernet/GpodnetAuthenticationActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/gpoddernet/GpodnetAuthenticationActivity.java @@ -45,8 +45,6 @@ import de.danoeh.antennapod.core.service.GpodnetSyncService; public class GpodnetAuthenticationActivity extends AppCompatActivity { private static final String TAG = "GpodnetAuthActivity"; - private static final String CURRENT_STEP = "current_step"; - private ViewFlipper viewFlipper; private static final int STEP_DEFAULT = -1; @@ -61,7 +59,7 @@ public class GpodnetAuthenticationActivity extends AppCompatActivity { private volatile String password; private volatile GpodnetDevice selectedDevice; - View[] views; + private View[] views; @Override protected void onCreate(Bundle savedInstanceState) { @@ -72,7 +70,7 @@ public class GpodnetAuthenticationActivity extends AppCompatActivity { setContentView(R.layout.gpodnetauth_activity); service = new GpodnetService(); - viewFlipper = (ViewFlipper) findViewById(R.id.viewflipper); + viewFlipper = findViewById(R.id.viewflipper); LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE); views = new View[]{ @@ -109,11 +107,11 @@ public class GpodnetAuthenticationActivity extends AppCompatActivity { } private void setupLoginView(View view) { - final EditText username = (EditText) view.findViewById(R.id.etxtUsername); - final EditText password = (EditText) view.findViewById(R.id.etxtPassword); - final Button login = (Button) view.findViewById(R.id.butLogin); - final TextView txtvError = (TextView) view.findViewById(R.id.txtvError); - final ProgressBar progressBar = (ProgressBar) view.findViewById(R.id.progBarLogin); + final EditText username = view.findViewById(R.id.etxtUsername); + final EditText password = view.findViewById(R.id.etxtPassword); + final Button login = view.findViewById(R.id.butLogin); + final TextView txtvError = view.findViewById(R.id.txtvError); + final ProgressBar progressBar = view.findViewById(R.id.progBarLogin); password.setOnEditorActionListener((v, actionID, event) -> actionID == EditorInfo.IME_ACTION_GO && login.performClick()); @@ -171,23 +169,19 @@ public class GpodnetAuthenticationActivity extends AppCompatActivity { return null; } }; - if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) { - authTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, service); - } else { - authTask.execute(); - } + authTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, service); } }); } private void setupDeviceView(View view) { - final EditText deviceID = (EditText) view.findViewById(R.id.etxtDeviceID); - final EditText caption = (EditText) view.findViewById(R.id.etxtCaption); - final Button createNewDevice = (Button) view.findViewById(R.id.butCreateNewDevice); - final Button chooseDevice = (Button) view.findViewById(R.id.butChooseExistingDevice); - final TextView txtvError = (TextView) view.findViewById(R.id.txtvError); - final ProgressBar progBarCreateDevice = (ProgressBar) view.findViewById(R.id.progbarCreateDevice); - final Spinner spinnerDevices = (Spinner) view.findViewById(R.id.spinnerChooseDevice); + final EditText deviceID = view.findViewById(R.id.etxtDeviceID); + final EditText caption = view.findViewById(R.id.etxtCaption); + final Button createNewDevice = view.findViewById(R.id.butCreateNewDevice); + final Button chooseDevice = view.findViewById(R.id.butChooseExistingDevice); + final TextView txtvError = view.findViewById(R.id.txtvError); + final ProgressBar progBarCreateDevice = view.findViewById(R.id.progbarCreateDevice); + final Spinner spinnerDevices = view.findViewById(R.id.spinnerChooseDevice); // load device list @@ -352,8 +346,8 @@ public class GpodnetAuthenticationActivity extends AppCompatActivity { } private void setupFinishView(View view) { - final Button sync = (Button) view.findViewById(R.id.butSyncNow); - final Button back = (Button) view.findViewById(R.id.butGoMainscreen); + final Button sync = view.findViewById(R.id.butSyncNow); + final Button back = view.findViewById(R.id.butGoMainscreen); sync.setOnClickListener(v -> { GpodnetSyncService.sendSyncIntent(GpodnetAuthenticationActivity.this); diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonCallback.java b/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonCallback.java index c18564351..e6b42efcb 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonCallback.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonCallback.java @@ -3,7 +3,7 @@ package de.danoeh.antennapod.adapter; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.util.LongList; -public interface ActionButtonCallback { +interface ActionButtonCallback { /** Is called when the action button of a list item has been pressed. */ void onActionButtonPressed(FeedItem item, LongList queueIds); } diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonUtils.java b/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonUtils.java index f0210f983..a915692d1 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonUtils.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/ActionButtonUtils.java @@ -16,7 +16,7 @@ import de.danoeh.antennapod.core.storage.DownloadRequester; * Utility methods for the action button that is displayed on the right hand side * of a listitem. */ -public class ActionButtonUtils { +class ActionButtonUtils { private final int[] labels; private final TypedArray drawables; diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java index 3e8bbc488..0b2b81edb 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java @@ -1,6 +1,7 @@ package de.danoeh.antennapod.adapter; import android.os.Build; +import android.support.annotation.Nullable; import android.support.v4.content.ContextCompat; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.helper.ItemTouchHelper; @@ -19,9 +20,7 @@ import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.TextView; -import com.bumptech.glide.Glide; import com.joanzapata.iconify.Iconify; -import com.nineoldandroids.view.ViewHelper; import java.lang.ref.WeakReference; @@ -29,13 +28,12 @@ import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; 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.storage.DownloadRequester; import de.danoeh.antennapod.core.util.Converter; import de.danoeh.antennapod.core.util.DateUtils; import de.danoeh.antennapod.core.util.LongList; import de.danoeh.antennapod.core.util.NetworkUtils; +import de.danoeh.antennapod.core.util.ThemeUtils; import de.danoeh.antennapod.fragment.ItemFragment; import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler; @@ -52,7 +50,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR private final ActionButtonUtils actionButtonUtils; private final boolean showOnlyNewEpisodes; - private int position = -1; + private FeedItem selectedItem; private final int playingBackGroundColor; private final int normalBackGroundColor; @@ -68,11 +66,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR this.actionButtonCallback = actionButtonCallback; this.showOnlyNewEpisodes = showOnlyNewEpisodes; - if(UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) { - playingBackGroundColor = ContextCompat.getColor(mainActivity, R.color.highlight_dark); - } else { - playingBackGroundColor = ContextCompat.getColor(mainActivity, R.color.highlight_light); - } + playingBackGroundColor = ThemeUtils.getColorFromAttr(mainActivity, R.attr.currently_playing_background); normalBackGroundColor = ContextCompat.getColor(mainActivity, android.R.color.transparent); } @@ -81,27 +75,26 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR View view = LayoutInflater.from(parent.getContext()) .inflate(R.layout.new_episodes_listitem, parent, false); Holder holder = new Holder(view); - holder.container = (FrameLayout) view.findViewById(R.id.container); - holder.content = (LinearLayout) view.findViewById(R.id.content); - holder.placeholder = (TextView) view.findViewById(R.id.txtvPlaceholder); - holder.title = (TextView) view.findViewById(R.id.txtvTitle); + holder.container = view.findViewById(R.id.container); + holder.content = view.findViewById(R.id.content); + holder.placeholder = view.findViewById(R.id.txtvPlaceholder); + holder.title = view.findViewById(R.id.txtvTitle); if(Build.VERSION.SDK_INT >= 23) { holder.title.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL); } - holder.pubDate = (TextView) view + holder.pubDate = view .findViewById(R.id.txtvPublished); holder.statusUnread = view.findViewById(R.id.statusUnread); - holder.butSecondary = (ImageButton) view + holder.butSecondary = view .findViewById(R.id.butSecondaryAction); - holder.queueStatus = (ImageView) view + holder.queueStatus = view .findViewById(R.id.imgvInPlaylist); - holder.progress = (ProgressBar) view + holder.progress = view .findViewById(R.id.pbar_progress); - holder.cover = (ImageView) view.findViewById(R.id.imgvCover); - holder.txtvDuration = (TextView) view.findViewById(R.id.txtvDuration); + holder.cover = view.findViewById(R.id.imgvCover); + holder.txtvDuration = view.findViewById(R.id.txtvDuration); holder.item = null; holder.mainActivityRef = mainActivityRef; - holder.position = -1; // so we can grab this later view.setTag(holder); @@ -113,11 +106,10 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR final FeedItem item = itemAccess.getItem(position); if (item == null) return; holder.itemView.setOnLongClickListener(v -> { - this.position = position; + this.selectedItem = item; return false; }); holder.item = item; - holder.position = position; holder.placeholder.setVisibility(View.VISIBLE); holder.placeholder.setText(item.getFeed().getTitle()); holder.title.setText(item.getTitle()); @@ -129,9 +121,9 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR holder.statusUnread.setVisibility(View.VISIBLE); } if(item.isPlayed()) { - ViewHelper.setAlpha(holder.content, 0.5f); + holder.content.setAlpha(0.5f); } else { - ViewHelper.setAlpha(holder.content, 1.0f); + holder.content.setAlpha(1.0f); } FeedMedia media = item.getMedia(); @@ -174,7 +166,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR holder.progress.setVisibility(View.VISIBLE); } } else { - holder.progress.setVisibility(View.GONE); + holder.progress.setVisibility(View.INVISIBLE); } if(media.isCurrentlyPlaying()) { @@ -183,7 +175,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR holder.container.setBackgroundColor(normalBackGroundColor); } } else { - holder.progress.setVisibility(View.GONE); + holder.progress.setVisibility(View.INVISIBLE); holder.txtvDuration.setVisibility(View.GONE); } @@ -199,12 +191,17 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR holder.butSecondary.setTag(item); holder.butSecondary.setOnClickListener(secondaryActionListener); - Glide.with(mainActivityRef.get()) - .load(item.getImageLocation()) - .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) - .fitCenter() - .dontAnimate() - .into(new CoverTarget(item.getFeed().getImageLocation(), holder.placeholder, holder.cover, mainActivityRef.get())); + new CoverLoader(mainActivityRef.get()) + .withUri(item.getImageLocation()) + .withFallbackUri(item.getFeed().getImageLocation()) + .withPlaceholderView(holder.placeholder) + .withCoverView(holder.cover) + .load(); + } + + @Nullable + public FeedItem getSelectedItem() { + return selectedItem; } @Override @@ -218,17 +215,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR return itemAccess.getCount(); } - public FeedItem getItem(int position) { - return itemAccess.getItem(position); - } - - public int getPosition() { - int pos = position; - position = -1; // reset - return pos; - } - - private View.OnClickListener secondaryActionListener = new View.OnClickListener() { + private final View.OnClickListener secondaryActionListener = new View.OnClickListener() { @Override public void onClick(View v) { FeedItem item = (FeedItem) v.getTag(); @@ -253,7 +240,6 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR ImageButton butSecondary; FeedItem item; WeakReference<MainActivity> mainActivityRef; - int position; public Holder(View itemView) { super(itemView); @@ -266,18 +252,18 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR MainActivity mainActivity = mainActivityRef.get(); if (mainActivity != null) { long[] ids = itemAccess.getItemsIds().toArray(); - mainActivity.loadChildFragment(ItemFragment.newInstance(ids, position)); + mainActivity.loadChildFragment(ItemFragment.newInstance(ids, getAdapterPosition())); } } @Override public void onItemSelected() { - ViewHelper.setAlpha(itemView, 0.5f); + itemView.setAlpha(0.5f); } @Override public void onItemClear() { - ViewHelper.setAlpha(itemView, 1.0f); + itemView.setAlpha(1.0f); } public FeedItem getFeedItem() { return item; } @@ -303,6 +289,8 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR } }; FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item, true, null); + + contextMenuInterface.setItemVisibility(R.id.mark_as_seen_item, item.isNew()); } } diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java index d7bebb672..c3fac7e18 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java @@ -1,6 +1,7 @@ package de.danoeh.antennapod.adapter; import android.content.Context; +import android.support.annotation.NonNull; import android.support.v4.content.ContextCompat; import android.text.Layout; import android.text.Selection; @@ -8,7 +9,6 @@ import android.text.Spannable; import android.text.Spanned; import android.text.style.ClickableSpan; import android.text.util.Linkify; -import android.util.Log; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; @@ -19,9 +19,9 @@ import android.widget.TextView; import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.feed.Chapter; -import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.util.ChapterUtils; import de.danoeh.antennapod.core.util.Converter; +import de.danoeh.antennapod.core.util.ThemeUtils; import de.danoeh.antennapod.core.util.playback.Playable; public class ChaptersListAdapter extends ArrayAdapter<Chapter> { @@ -42,8 +42,9 @@ public class ChaptersListAdapter extends ArrayAdapter<Chapter> { this.media = media; } + @NonNull @Override - public View getView(final int position, View convertView, ViewGroup parent) { + public View getView(final int position, View convertView, @NonNull ViewGroup parent) { Holder holder; Chapter sc = getItem(position); @@ -56,12 +57,12 @@ public class ChaptersListAdapter extends ArrayAdapter<Chapter> { convertView = inflater.inflate(R.layout.simplechapter_item, parent, false); holder.view = convertView; - holder.title = (TextView) convertView.findViewById(R.id.txtvTitle); + holder.title = convertView.findViewById(R.id.txtvTitle); defaultTextColor = holder.title.getTextColors().getDefaultColor(); - holder.start = (TextView) convertView.findViewById(R.id.txtvStart); - holder.link = (TextView) convertView.findViewById(R.id.txtvLink); - holder.duration = (TextView) convertView.findViewById(R.id.txtvDuration); - holder.butPlayChapter = (ImageButton) convertView.findViewById(R.id.butPlayChapter); + holder.start = convertView.findViewById(R.id.txtvStart); + holder.link = convertView.findViewById(R.id.txtvLink); + holder.duration = convertView.findViewById(R.id.txtvDuration); + holder.butPlayChapter = convertView.findViewById(R.id.butPlayChapter); convertView.setTag(holder); } else { holder = (Holder) convertView.getTag(); @@ -120,7 +121,7 @@ public class ChaptersListAdapter extends ArrayAdapter<Chapter> { if (link.length != 0) { if (action == MotionEvent.ACTION_UP) { link[0].onClick(widget); - } else if (action == MotionEvent.ACTION_DOWN){ + } else if (action == MotionEvent.ACTION_DOWN) { Selection.setSelection(buffer, buffer.getSpanStart(link[0]), buffer.getSpanEnd(link[0])); @@ -139,23 +140,15 @@ public class ChaptersListAdapter extends ArrayAdapter<Chapter> { callback.onPlayChapterButtonClicked(position); } }); + Chapter current = ChapterUtils.getCurrentChapter(media); - if (current != null) { - if (current == sc) { - int playingBackGroundColor; - if(UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) { - playingBackGroundColor = ContextCompat.getColor(getContext(), R.color.highlight_dark); - } else { - playingBackGroundColor = ContextCompat.getColor(getContext(), R.color.highlight_light); - } - holder.view.setBackgroundColor(playingBackGroundColor); - } else { - holder.view.setBackgroundColor(ContextCompat.getColor(getContext(), android.R.color.transparent)); - holder.title.setTextColor(defaultTextColor); - holder.start.setTextColor(defaultTextColor); - } + if (current == sc) { + int playingBackGroundColor = ThemeUtils.getColorFromAttr(getContext(), R.attr.currently_playing_background); + holder.view.setBackgroundColor(playingBackGroundColor); } else { - Log.w(TAG, "Could not find out what the current chapter is."); + holder.view.setBackgroundColor(ContextCompat.getColor(getContext(), android.R.color.transparent)); + holder.title.setTextColor(defaultTextColor); + holder.start.setTextColor(defaultTextColor); } return convertView; @@ -172,7 +165,7 @@ public class ChaptersListAdapter extends ArrayAdapter<Chapter> { @Override public int getCount() { - if(media == null || media.getChapters() == null) { + if (media == null || media.getChapters() == null) { return 0; } // ignore invalid chapters diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/CoverLoader.java b/app/src/main/java/de/danoeh/antennapod/adapter/CoverLoader.java new file mode 100644 index 000000000..54ecdae77 --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/adapter/CoverLoader.java @@ -0,0 +1,113 @@ +package de.danoeh.antennapod.adapter; + +import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.view.View; +import android.widget.ImageView; +import android.widget.TextView; + +import com.bumptech.glide.Glide; +import com.bumptech.glide.RequestBuilder; +import com.bumptech.glide.request.RequestOptions; +import com.bumptech.glide.request.target.CustomViewTarget; + +import java.lang.ref.WeakReference; + +import com.bumptech.glide.request.transition.Transition; +import de.danoeh.antennapod.activity.MainActivity; +import de.danoeh.antennapod.core.glide.ApGlideSettings; + +public class CoverLoader { + private String uri; + private String fallbackUri; + private TextView txtvPlaceholder; + private ImageView imgvCover; + private MainActivity activity; + private int errorResource = -1; + + public CoverLoader(MainActivity activity) { + this.activity = activity; + } + + public CoverLoader withUri(String uri) { + this.uri = uri; + return this; + } + + public CoverLoader withFallbackUri(String uri) { + fallbackUri = uri; + return this; + } + + public CoverLoader withCoverView(ImageView coverView) { + imgvCover = coverView; + return this; + } + + public CoverLoader withError(int errorResource) { + this.errorResource = errorResource; + return this; + } + + public CoverLoader withPlaceholderView(TextView placeholderView) { + txtvPlaceholder = placeholderView; + return this; + } + + public void load() { + RequestOptions options = new RequestOptions() + .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) + .fitCenter() + .dontAnimate(); + + if (errorResource != -1) { + options = options.error(errorResource); + } + + RequestBuilder builder = Glide.with(activity) + .load(uri) + .apply(options); + + if (fallbackUri != null && txtvPlaceholder != null && imgvCover != null) { + builder = builder.error(Glide.with(activity) + .load(fallbackUri) + .apply(options)); + } + + builder.into(new CoverTarget(txtvPlaceholder, imgvCover)); + } + + class CoverTarget extends CustomViewTarget<ImageView, Drawable> { + private final WeakReference<TextView> placeholder; + private final WeakReference<ImageView> cover; + + public CoverTarget(TextView txtvPlaceholder, ImageView imgvCover) { + super(imgvCover); + placeholder = new WeakReference<>(txtvPlaceholder); + cover = new WeakReference<>(imgvCover); + } + + @Override + public void onLoadFailed(Drawable errorDrawable) { + + } + + @Override + public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) { + TextView txtvPlaceholder = placeholder.get(); + if (txtvPlaceholder != null) { + txtvPlaceholder.setVisibility(View.INVISIBLE); + } + ImageView ivCover = cover.get(); + ivCover.setImageDrawable(resource); + } + + @Override + protected void onResourceCleared(@Nullable Drawable placeholder) { + ImageView ivCover = cover.get(); + ivCover.setImageDrawable(placeholder); + } + } +}
\ No newline at end of file diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/CoverTarget.java b/app/src/main/java/de/danoeh/antennapod/adapter/CoverTarget.java deleted file mode 100644 index 538af8c79..000000000 --- a/app/src/main/java/de/danoeh/antennapod/adapter/CoverTarget.java +++ /dev/null @@ -1,58 +0,0 @@ -package de.danoeh.antennapod.adapter; - -import android.graphics.drawable.Drawable; -import android.net.Uri; -import android.view.View; -import android.widget.ImageView; -import android.widget.TextView; - -import com.bumptech.glide.Glide; -import com.bumptech.glide.load.resource.drawable.GlideDrawable; -import com.bumptech.glide.request.animation.GlideAnimation; -import com.bumptech.glide.request.target.GlideDrawableImageViewTarget; - -import java.lang.ref.WeakReference; - -import de.danoeh.antennapod.activity.MainActivity; -import de.danoeh.antennapod.core.glide.ApGlideSettings; - -class CoverTarget extends GlideDrawableImageViewTarget { - - private final WeakReference<String> fallback; - private final WeakReference<TextView> placeholder; - private final WeakReference<ImageView> cover; - private final WeakReference<MainActivity> mainActivity; - - public CoverTarget(String fallbackUri, TextView txtvPlaceholder, ImageView imgvCover, MainActivity activity) { - super(imgvCover); - fallback = new WeakReference<>(fallbackUri); - placeholder = new WeakReference<>(txtvPlaceholder); - cover = new WeakReference<>(imgvCover); - mainActivity = new WeakReference<>(activity); - } - - @Override - public void onLoadFailed(Exception e, Drawable errorDrawable) { - String fallbackUri = fallback.get(); - TextView txtvPlaceholder = placeholder.get(); - ImageView imgvCover = cover.get(); - if (fallbackUri != null && txtvPlaceholder != null && imgvCover != null) { - MainActivity activity = mainActivity.get(); - Glide.with(activity) - .load(fallbackUri) - .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) - .fitCenter() - .dontAnimate() - .into(new CoverTarget(null, txtvPlaceholder, imgvCover, activity)); - } - } - - @Override - public void onResourceReady(GlideDrawable drawable, GlideAnimation<? super GlideDrawable> anim) { - super.onResourceReady(drawable, anim); - TextView txtvPlaceholder = placeholder.get(); - if (txtvPlaceholder != null) { - txtvPlaceholder.setVisibility(View.INVISIBLE); - } - } -} diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java b/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java index 4a53be9dc..1286d9dc7 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java @@ -1,6 +1,7 @@ package de.danoeh.antennapod.adapter; import android.content.Context; +import android.support.annotation.NonNull; import android.content.Intent; import android.widget.Toast; @@ -19,8 +20,10 @@ 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.IntentUtils; import de.danoeh.antennapod.core.util.LongList; import de.danoeh.antennapod.core.util.NetworkUtils; +import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter; /** * Default implementation of an ActionButtonCallback @@ -80,13 +83,19 @@ public class DefaultActionButtonCallback implements ActionButtonCallback { Toast.makeText(context, R.string.download_canceled_msg, Toast.LENGTH_LONG).show(); } } else { // media is downloaded - if (item.hasMedia() && item.getMedia().isCurrentlyPlaying()) { - context.sendBroadcast(new Intent(PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE)); - } - else if (item.hasMedia() && item.getMedia().isCurrentlyPaused()) { - context.sendBroadcast(new Intent(PlaybackService.ACTION_RESUME_PLAY_CURRENT_EPISODE)); - } - else { + if (media.isCurrentlyPlaying()) { + new PlaybackServiceStarter(context, media) + .startWhenPrepared(true) + .shouldStream(false) + .start(); + IntentUtils.sendLocalBroadcast(context, PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE); + } else if (media.isCurrentlyPaused()) { + new PlaybackServiceStarter(context, media) + .startWhenPrepared(true) + .shouldStream(false) + .start(); + IntentUtils.sendLocalBroadcast(context, PlaybackService.ACTION_RESUME_PLAY_CURRENT_EPISODE); + } else { DBTasks.playMedia(context, media, false, true, false); } } diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java index e271b5eed..789c01a26 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java @@ -19,7 +19,6 @@ import com.joanzapata.iconify.widget.IconTextView; import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator; import de.danoeh.antennapod.core.feed.Feed; -import de.danoeh.antennapod.core.feed.FeedImage; import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.service.download.DownloadStatus; import de.danoeh.antennapod.core.storage.DBReader; @@ -29,11 +28,11 @@ import de.danoeh.antennapod.core.storage.DownloadRequestException; /** Displays a list of DownloadStatus entries. */ public class DownloadLogAdapter extends BaseAdapter { - private final String TAG = "DownloadLogAdapter"; + private static final String TAG = "DownloadLogAdapter"; - private Context context; + private final Context context; - private ItemAccess itemAccess; + private final ItemAccess itemAccess; public DownloadLogAdapter(Context context, ItemAccess itemAccess) { super(); @@ -50,15 +49,15 @@ public class DownloadLogAdapter extends BaseAdapter { LayoutInflater inflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = inflater.inflate(R.layout.downloadlog_item, parent, false); - holder.icon = (IconTextView) convertView.findViewById(R.id.txtvIcon); - holder.retry = (IconButton) convertView.findViewById(R.id.btnRetry); - holder.date = (TextView) convertView.findViewById(R.id.txtvDate); - holder.title = (TextView) convertView.findViewById(R.id.txtvTitle); + holder.icon = convertView.findViewById(R.id.txtvIcon); + holder.retry = convertView.findViewById(R.id.btnRetry); + holder.date = convertView.findViewById(R.id.txtvDate); + holder.title = convertView.findViewById(R.id.txtvTitle); if(Build.VERSION.SDK_INT >= 23) { holder.title.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL); } - holder.type = (TextView) convertView.findViewById(R.id.txtvType); - holder.reason = (TextView) convertView.findViewById(R.id.txtvReason); + holder.type = convertView.findViewById(R.id.txtvType); + holder.reason = convertView.findViewById(R.id.txtvReason); convertView.setTag(holder); } else { holder = (Holder) convertView.getTag(); @@ -67,8 +66,6 @@ public class DownloadLogAdapter extends BaseAdapter { holder.type.setText(R.string.download_type_feed); } else if (status.getFeedfileType() == FeedMedia.FEEDFILETYPE_FEEDMEDIA) { holder.type.setText(R.string.download_type_media); - } else if (status.getFeedfileType() == FeedImage.FEEDFILETYPE_FEEDIMAGE) { - holder.type.setText(R.string.download_type_image); } if (status.getTitle() != null) { holder.title.setText(status.getTitle()); @@ -94,8 +91,7 @@ public class DownloadLogAdapter extends BaseAdapter { } holder.reason.setText(reasonText); holder.reason.setVisibility(View.VISIBLE); - if(status.getFeedfileType() != FeedImage.FEEDFILETYPE_FEEDIMAGE && - !newerWasSuccessful(position, status.getFeedfileType(), status.getFeedfileId())) { + if(!newerWasSuccessful(position, status.getFeedfileType(), status.getFeedfileId())) { holder.retry.setVisibility(View.VISIBLE); holder.retry.setOnClickListener(clickListener); ButtonHolder btnHolder; diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java index 6907e467b..cd636af43 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java @@ -12,11 +12,12 @@ import android.widget.ImageView; import android.widget.TextView; import com.bumptech.glide.Glide; -import com.nineoldandroids.view.ViewHelper; +import com.bumptech.glide.request.RequestOptions; import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.glide.ApGlideSettings; +import de.danoeh.antennapod.core.service.playback.PlaybackService; import de.danoeh.antennapod.core.util.Converter; import de.danoeh.antennapod.core.util.DateUtils; @@ -61,16 +62,16 @@ public class DownloadedEpisodesListAdapter extends BaseAdapter { .getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = inflater.inflate(R.layout.downloaded_episodeslist_item, parent, false); - holder.imageView = (ImageView) convertView.findViewById(R.id.imgvImage); - holder.title = (TextView) convertView.findViewById(R.id.txtvTitle); + holder.imageView = convertView.findViewById(R.id.imgvImage); + holder.title = convertView.findViewById(R.id.txtvTitle); if(Build.VERSION.SDK_INT >= 23) { holder.title.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL); } - holder.txtvSize = (TextView) convertView.findViewById(R.id.txtvSize); - holder.queueStatus = (ImageView) convertView.findViewById(R.id.imgvInPlaylist); - holder.pubDate = (TextView) convertView + holder.txtvSize = convertView.findViewById(R.id.txtvSize); + holder.queueStatus = convertView.findViewById(R.id.imgvInPlaylist); + holder.pubDate = convertView .findViewById(R.id.txtvPublished); - holder.butSecondary = (ImageButton) convertView + holder.butSecondary = convertView .findViewById(R.id.butSecondaryAction); convertView.setTag(holder); } else { @@ -79,17 +80,18 @@ public class DownloadedEpisodesListAdapter extends BaseAdapter { Glide.with(context) .load(item.getImageLocation()) - .placeholder(R.color.light_gray) - .error(R.color.light_gray) - .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) - .fitCenter() - .dontAnimate() + .apply(new RequestOptions() + .placeholder(R.color.light_gray) + .error(R.color.light_gray) + .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) + .fitCenter() + .dontAnimate()) .into(holder.imageView); if(item.isPlayed()) { - ViewHelper.setAlpha(convertView, 0.5f); + convertView.setAlpha(0.5f); } else { - ViewHelper.setAlpha(convertView, 1.0f); + convertView.setAlpha(1.0f); } holder.title.setText(item.getTitle()); @@ -99,10 +101,12 @@ public class DownloadedEpisodesListAdapter extends BaseAdapter { holder.pubDate.setText(pubDateStr); FeedItem.State state = item.getState(); - if (state == FeedItem.State.PLAYING) { + if (state == FeedItem.State.PLAYING && PlaybackService.isRunning) { holder.butSecondary.setEnabled(false); + holder.butSecondary.setAlpha(0.5f); } else { holder.butSecondary.setEnabled(true); + holder.butSecondary.setAlpha(1.0f); } holder.butSecondary.setFocusable(false); holder.butSecondary.setTag(item); @@ -111,7 +115,7 @@ public class DownloadedEpisodesListAdapter extends BaseAdapter { return convertView; } - private View.OnClickListener secondaryActionListener = new View.OnClickListener() { + private final View.OnClickListener secondaryActionListener = new View.OnClickListener() { @Override public void onClick(View v) { FeedItem item = (FeedItem) v.getTag(); diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadlistAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadlistAdapter.java index e1efdaa7b..b85d1d35d 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadlistAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadlistAdapter.java @@ -19,11 +19,11 @@ import de.danoeh.antennapod.core.util.ThemeUtils; public class DownloadlistAdapter extends BaseAdapter { - public static final int SELECTION_NONE = -1; + private static final int SELECTION_NONE = -1; private int selectedItemIndex; - private ItemAccess itemAccess; - private Context context; + private final ItemAccess itemAccess; + private final Context context; public DownloadlistAdapter(Context context, ItemAccess itemAccess) { @@ -59,14 +59,14 @@ public class DownloadlistAdapter extends BaseAdapter { LayoutInflater inflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = inflater.inflate(R.layout.downloadlist_item, parent, false); - holder.title = (TextView) convertView.findViewById(R.id.txtvTitle); - holder.downloaded = (TextView) convertView + holder.title = convertView.findViewById(R.id.txtvTitle); + holder.downloaded = convertView .findViewById(R.id.txtvDownloaded); - holder.percent = (TextView) convertView + holder.percent = convertView .findViewById(R.id.txtvPercent); - holder.progbar = (ProgressBar) convertView + holder.progbar = convertView .findViewById(R.id.progProgress); - holder.butSecondary = (ImageButton) convertView + holder.butSecondary = convertView .findViewById(R.id.butSecondaryAction); convertView.setTag(holder); @@ -105,7 +105,7 @@ public class DownloadlistAdapter extends BaseAdapter { return convertView; } - private View.OnClickListener butSecondaryListener = new View.OnClickListener() { + private final View.OnClickListener butSecondaryListener = new View.OnClickListener() { @Override public void onClick(View v) { Downloader downloader = (Downloader) v.getTag(); @@ -121,15 +121,6 @@ public class DownloadlistAdapter extends BaseAdapter { ImageButton butSecondary; } - public int getSelectedItemIndex() { - return selectedItemIndex; - } - - public void setSelectedItemIndex(int selectedItemIndex) { - this.selectedItemIndex = selectedItemIndex; - notifyDataSetChanged(); - } - public interface ItemAccess { int getCount(); diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java index 35c42725c..738a0a636 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java @@ -17,13 +17,10 @@ import android.widget.LinearLayout; import android.widget.ProgressBar; import android.widget.TextView; -import com.nineoldandroids.view.ViewHelper; - import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.feed.MediaType; -import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.storage.DownloadRequester; import de.danoeh.antennapod.core.util.DateUtils; import de.danoeh.antennapod.core.util.LongList; @@ -34,16 +31,16 @@ import de.danoeh.antennapod.core.util.ThemeUtils; */ public class FeedItemlistAdapter extends BaseAdapter { - private ActionButtonCallback callback; + private final ActionButtonCallback callback; private final ItemAccess itemAccess; private final Context context; - private boolean showFeedtitle; - private int selectedItemIndex; + private final boolean showFeedtitle; + private final int selectedItemIndex; /** true if played items should be made partially transparent */ - private boolean makePlayedItemsTransparent; + private final boolean makePlayedItemsTransparent; private final ActionButtonUtils actionButtonUtils; - public static final int SELECTION_NONE = -1; + private static final int SELECTION_NONE = -1; private final int playingBackGroundColor; private final int normalBackGroundColor; @@ -62,11 +59,7 @@ public class FeedItemlistAdapter extends BaseAdapter { this.actionButtonUtils = new ActionButtonUtils(context); this.makePlayedItemsTransparent = makePlayedItemsTransparent; - if(UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) { - playingBackGroundColor = ContextCompat.getColor(context, R.color.highlight_dark); - } else { - playingBackGroundColor = ContextCompat.getColor(context, R.color.highlight_light); - } + playingBackGroundColor = ThemeUtils.getColorFromAttr(context, R.attr.currently_playing_background); normalBackGroundColor = ContextCompat.getColor(context, android.R.color.transparent); } @@ -97,24 +90,24 @@ public class FeedItemlistAdapter extends BaseAdapter { LayoutInflater inflater = (LayoutInflater) context .getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = inflater.inflate(R.layout.feeditemlist_item, parent, false); - holder.container = (LinearLayout) convertView + holder.container = convertView .findViewById(R.id.container); - holder.title = (TextView) convertView.findViewById(R.id.txtvItemname); + holder.title = convertView.findViewById(R.id.txtvItemname); if(Build.VERSION.SDK_INT >= 23) { holder.title.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL); } - holder.lenSize = (TextView) convertView + holder.lenSize = convertView .findViewById(R.id.txtvLenSize); - holder.butAction = (ImageButton) convertView + holder.butAction = convertView .findViewById(R.id.butSecondaryAction); - holder.published = (TextView) convertView + holder.published = convertView .findViewById(R.id.txtvPublished); - holder.inPlaylist = (ImageView) convertView + holder.inPlaylist = convertView .findViewById(R.id.imgvInPlaylist); - holder.type = (ImageView) convertView.findViewById(R.id.imgvType); + holder.type = convertView.findViewById(R.id.imgvType); holder.statusUnread = convertView .findViewById(R.id.statusUnread); - holder.episodeProgress = (ProgressBar) convertView + holder.episodeProgress = convertView .findViewById(R.id.pbar_episode_progress); convertView.setTag(holder); @@ -145,9 +138,9 @@ public class FeedItemlistAdapter extends BaseAdapter { holder.statusUnread.setVisibility(View.INVISIBLE); } if(item.isPlayed() && makePlayedItemsTransparent) { - ViewHelper.setAlpha(convertView, 0.5f); + convertView.setAlpha(0.5f); } else { - ViewHelper.setAlpha(convertView, 1.0f); + convertView.setAlpha(1.0f); } String pubDateStr = DateUtils.formatAbbrev(context, item.getPubDate()); @@ -157,7 +150,7 @@ public class FeedItemlistAdapter extends BaseAdapter { FeedMedia media = item.getMedia(); if (media == null) { - holder.episodeProgress.setVisibility(View.GONE); + holder.episodeProgress.setVisibility(View.INVISIBLE); holder.inPlaylist.setVisibility(View.INVISIBLE); holder.type.setVisibility(View.INVISIBLE); holder.lenSize.setVisibility(View.INVISIBLE); @@ -176,7 +169,7 @@ public class FeedItemlistAdapter extends BaseAdapter { holder.episodeProgress.setProgress(itemAccess.getItemDownloadProgressPercent(item)); } else { if(media.getPosition() == 0) { - holder.episodeProgress.setVisibility(View.GONE); + holder.episodeProgress.setVisibility(View.INVISIBLE); } } diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java index 5b205e91f..c10bb7638 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java @@ -34,9 +34,9 @@ public class FeedItemlistDescriptionAdapter extends ArrayAdapter<FeedItem> { LayoutInflater inflater = (LayoutInflater) getContext() .getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = inflater.inflate(R.layout.itemdescription_listitem, parent, false); - holder.title = (TextView) convertView.findViewById(R.id.txtvTitle); - holder.pubDate = (TextView) convertView.findViewById(R.id.txtvPubDate); - holder.description = (TextView) convertView.findViewById(R.id.txtvDescription); + holder.title = convertView.findViewById(R.id.txtvTitle); + holder.pubDate = convertView.findViewById(R.id.txtvPubDate); + holder.description = convertView.findViewById(R.id.txtvDescription); convertView.setTag(holder); } else { diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java index 465497b55..fbf6b804a 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java @@ -17,6 +17,7 @@ import android.widget.RelativeLayout; import android.widget.TextView; import com.bumptech.glide.Glide; +import com.bumptech.glide.request.RequestOptions; import com.joanzapata.iconify.Iconify; import com.joanzapata.iconify.widget.IconTextView; @@ -48,10 +49,10 @@ import de.danoeh.antennapod.fragment.SubscriptionFragment; public class NavListAdapter extends BaseAdapter implements SharedPreferences.OnSharedPreferenceChangeListener { - public static final int VIEW_TYPE_COUNT = 3; + private static final int VIEW_TYPE_COUNT = 3; public static final int VIEW_TYPE_NAV = 0; public static final int VIEW_TYPE_SECTION_DIVIDER = 1; - public static final int VIEW_TYPE_SUBSCRIPTION = 2; + private static final int VIEW_TYPE_SUBSCRIPTION = 2; /** * a tag used as a placeholder to indicate if the subscription list should be displayed or not @@ -62,8 +63,8 @@ public class NavListAdapter extends BaseAdapter private static List<String> tags; private static String[] titles; - private ItemAccess itemAccess; - private WeakReference<Activity> activity; + private final ItemAccess itemAccess; + private final WeakReference<Activity> activity; private boolean showSubscriptionList = true; public NavListAdapter(ItemAccess itemAccess, Activity context) { @@ -86,9 +87,7 @@ public class NavListAdapter extends BaseAdapter private void loadItems() { List<String> newTags = new ArrayList<>(Arrays.asList(MainActivity.NAV_DRAWER_TAGS)); List<String> hiddenFragments = UserPreferences.getHiddenDrawerItems(); - for(String hidden : hiddenFragments) { - newTags.remove(hidden); - } + newTags.removeAll(hiddenFragments); if (newTags.contains(SUBSCRIPTION_LIST_TAG)) { // we never want SUBSCRIPTION_LIST_TAG to be in 'tags' @@ -214,7 +213,7 @@ public class NavListAdapter extends BaseAdapter v = getFeedView(position, convertView, parent); } if (v != null && viewType != VIEW_TYPE_SECTION_DIVIDER) { - TextView txtvTitle = (TextView) v.findViewById(R.id.txtvTitle); + TextView txtvTitle = v.findViewById(R.id.txtvTitle); if (position == itemAccess.getSelectedItemIndex()) { txtvTitle.setTypeface(null, Typeface.BOLD); } else { @@ -237,9 +236,9 @@ public class NavListAdapter extends BaseAdapter convertView = inflater.inflate(R.layout.nav_listitem, parent, false); - holder.image = (ImageView) convertView.findViewById(R.id.imgvCover); - holder.title = (TextView) convertView.findViewById(R.id.txtvTitle); - holder.count = (TextView) convertView.findViewById(R.id.txtvCount); + holder.image = convertView.findViewById(R.id.imgvCover); + holder.title = convertView.findViewById(R.id.txtvTitle); + holder.count = convertView.findViewById(R.id.txtvCount); convertView.setTag(holder); } else { holder = (NavHolder) convertView.getTag(); @@ -327,10 +326,10 @@ public class NavListAdapter extends BaseAdapter convertView = inflater.inflate(R.layout.nav_feedlistitem, parent, false); - holder.image = (ImageView) convertView.findViewById(R.id.imgvCover); - holder.title = (TextView) convertView.findViewById(R.id.txtvTitle); - holder.failure = (IconTextView) convertView.findViewById(R.id.itxtvFailure); - holder.count = (TextView) convertView.findViewById(R.id.txtvCount); + holder.image = convertView.findViewById(R.id.imgvCover); + holder.title = convertView.findViewById(R.id.txtvTitle); + holder.failure = convertView.findViewById(R.id.itxtvFailure); + holder.count = convertView.findViewById(R.id.txtvCount); convertView.setTag(holder); } else { holder = (FeedHolder) convertView.getTag(); @@ -338,11 +337,12 @@ public class NavListAdapter extends BaseAdapter Glide.with(context) .load(feed.getImageLocation()) - .placeholder(R.color.light_gray) - .error(R.color.light_gray) - .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) - .fitCenter() - .dontAnimate() + .apply(new RequestOptions() + .placeholder(R.color.light_gray) + .error(R.color.light_gray) + .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) + .fitCenter() + .dontAnimate()) .into(holder.image); holder.title.setText(feed.getTitle()); diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java index c6ddc6c86..df8cafb9d 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java @@ -23,8 +23,8 @@ import android.widget.ProgressBar; import android.widget.TextView; import com.bumptech.glide.Glide; +import com.bumptech.glide.request.RequestOptions; import com.joanzapata.iconify.Iconify; -import com.nineoldandroids.view.ViewHelper; import org.apache.commons.lang3.ArrayUtils; @@ -41,6 +41,7 @@ import de.danoeh.antennapod.core.util.Converter; import de.danoeh.antennapod.core.util.DateUtils; import de.danoeh.antennapod.core.util.LongList; import de.danoeh.antennapod.core.util.NetworkUtils; +import de.danoeh.antennapod.core.util.ThemeUtils; import de.danoeh.antennapod.fragment.ItemFragment; import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler; @@ -51,7 +52,7 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap private static final String TAG = QueueRecyclerAdapter.class.getSimpleName(); - private WeakReference<MainActivity> mainActivity; + private final WeakReference<MainActivity> mainActivity; private final ItemAccess itemAccess; private final ActionButtonCallback actionButtonCallback; private final ActionButtonUtils actionButtonUtils; @@ -76,11 +77,7 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap this.itemTouchHelper = itemTouchHelper; locked = UserPreferences.isQueueLocked(); - if(UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark) { - playingBackGroundColor = ContextCompat.getColor(mainActivity, R.color.highlight_dark); - } else { - playingBackGroundColor = ContextCompat.getColor(mainActivity, R.color.highlight_light); - } + playingBackGroundColor = ThemeUtils.getColorFromAttr(mainActivity, R.attr.currently_playing_background); normalBackGroundColor = ContextCompat.getColor(mainActivity, android.R.color.transparent); } @@ -138,19 +135,19 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap public ViewHolder(View v) { super(v); - container = (FrameLayout) v.findViewById(R.id.container); - dragHandle = (ImageView) v.findViewById(R.id.drag_handle); - placeholder = (TextView) v.findViewById(R.id.txtvPlaceholder); - cover = (ImageView) v.findViewById(R.id.imgvCover); - title = (TextView) v.findViewById(R.id.txtvTitle); + container = v.findViewById(R.id.container); + dragHandle = v.findViewById(R.id.drag_handle); + placeholder = v.findViewById(R.id.txtvPlaceholder); + cover = v.findViewById(R.id.imgvCover); + title = v.findViewById(R.id.txtvTitle); if(Build.VERSION.SDK_INT >= 23) { title.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL); } - pubDate = (TextView) v.findViewById(R.id.txtvPubDate); - progressLeft = (TextView) v.findViewById(R.id.txtvProgressLeft); - progressRight = (TextView) v.findViewById(R.id.txtvProgressRight); - butSecondary = (ImageButton) v.findViewById(R.id.butSecondaryAction); - progressBar = (ProgressBar) v.findViewById(R.id.progressBar); + pubDate = v.findViewById(R.id.txtvPubDate); + progressLeft = v.findViewById(R.id.txtvProgressLeft); + progressRight = v.findViewById(R.id.txtvProgressRight); + butSecondary = v.findViewById(R.id.butSecondaryAction); + progressBar = v.findViewById(R.id.progressBar); v.setTag(this); v.setOnClickListener(this); v.setOnCreateContextMenuListener(this); @@ -198,12 +195,12 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap @Override public void onItemSelected() { - ViewHelper.setAlpha(itemView, 0.5f); + itemView.setAlpha(0.5f); } @Override public void onItemClear() { - ViewHelper.setAlpha(itemView, 1.0f); + itemView.setAlpha(1.0f); } public void bind(FeedItem item) { @@ -280,7 +277,7 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap progressLeft.setText(""); } progressRight.setText(Converter.getDurationStringLong(media.getDuration())); - progressBar.setVisibility(View.GONE); + progressBar.setVisibility(View.INVISIBLE); } if(media.isCurrentlyPlaying()) { @@ -295,17 +292,17 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap butSecondary.setTag(item); butSecondary.setOnClickListener(secondaryActionListener); - Glide.with(mainActivity.get()) - .load(item.getImageLocation()) - .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) - .fitCenter() - .dontAnimate() - .into(new CoverTarget(item.getFeed().getImageLocation(), placeholder, cover, mainActivity.get())); + new CoverLoader(mainActivity.get()) + .withUri(item.getImageLocation()) + .withFallbackUri(item.getFeed().getImageLocation()) + .withPlaceholderView(placeholder) + .withCoverView(cover) + .load(); } } - private View.OnClickListener secondaryActionListener = new View.OnClickListener() { + private final View.OnClickListener secondaryActionListener = new View.OnClickListener() { @Override public void onClick(View v) { FeedItem item = (FeedItem) v.getTag(); diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/SearchlistAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/SearchlistAdapter.java index 8e1aa24e0..45cb4af87 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/SearchlistAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/SearchlistAdapter.java @@ -11,8 +11,8 @@ import android.widget.ImageView; import android.widget.TextView; import com.bumptech.glide.Glide; -import com.nineoldandroids.view.ViewHelper; +import com.bumptech.glide.request.RequestOptions; import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.feed.FeedComponent; @@ -62,13 +62,13 @@ public class SearchlistAdapter extends BaseAdapter { .getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = inflater.inflate(R.layout.searchlist_item, parent, false); - holder.title = (TextView) convertView.findViewById(R.id.txtvTitle); + holder.title = convertView.findViewById(R.id.txtvTitle); if(Build.VERSION.SDK_INT >= 23) { holder.title.setHyphenationFrequency(Layout.HYPHENATION_FREQUENCY_FULL); } - holder.cover = (ImageView) convertView + holder.cover = convertView .findViewById(R.id.imgvFeedimage); - holder.subtitle = (TextView) convertView + holder.subtitle = convertView .findViewById(R.id.txtvSubtitle); convertView.setTag(holder); @@ -82,11 +82,12 @@ public class SearchlistAdapter extends BaseAdapter { Glide.with(context) .load(feed.getImageLocation()) - .placeholder(R.color.light_gray) - .error(R.color.light_gray) - .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) - .fitCenter() - .dontAnimate() + .apply(new RequestOptions() + .placeholder(R.color.light_gray) + .error(R.color.light_gray) + .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) + .fitCenter() + .dontAnimate()) .into(holder.cover); } else if (component.getClass() == FeedItem.class) { @@ -97,15 +98,16 @@ public class SearchlistAdapter extends BaseAdapter { holder.subtitle.setText(result.getSubtitle()); } - ViewHelper.setAlpha(convertView, item.isPlayed() ? 0.5f : 1.0f); + convertView.setAlpha(item.isPlayed() ? 0.5f : 1.0f); Glide.with(context) .load(item.getFeed().getImageLocation()) - .placeholder(R.color.light_gray) - .error(R.color.light_gray) - .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) - .fitCenter() - .dontAnimate() + .apply(new RequestOptions() + .placeholder(R.color.light_gray) + .error(R.color.light_gray) + .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) + .fitCenter() + .dontAnimate()) .into(holder.cover); } diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/StatisticsListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/StatisticsListAdapter.java index c060083a6..31e82dbe0 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/StatisticsListAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/StatisticsListAdapter.java @@ -13,6 +13,7 @@ import com.bumptech.glide.Glide; import java.util.ArrayList; import java.util.List; +import com.bumptech.glide.request.RequestOptions; import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.glide.ApGlideSettings; @@ -23,8 +24,8 @@ import de.danoeh.antennapod.core.util.Converter; * Adapter for the statistics list */ public class StatisticsListAdapter extends BaseAdapter { - private Context context; - List<DBReader.StatisticsItem> feedTime = new ArrayList<>(); + private final Context context; + private List<DBReader.StatisticsItem> feedTime = new ArrayList<>(); private boolean countAll = true; public StatisticsListAdapter(Context context) { @@ -62,9 +63,9 @@ public class StatisticsListAdapter extends BaseAdapter { convertView = inflater.inflate(R.layout.statistics_listitem, parent, false); - holder.image = (ImageView) convertView.findViewById(R.id.imgvCover); - holder.title = (TextView) convertView.findViewById(R.id.txtvTitle); - holder.time = (TextView) convertView.findViewById(R.id.txtvTime); + holder.image = convertView.findViewById(R.id.imgvCover); + holder.title = convertView.findViewById(R.id.txtvTitle); + holder.time = convertView.findViewById(R.id.txtvTime); convertView.setTag(holder); } else { holder = (StatisticsHolder) convertView.getTag(); @@ -72,11 +73,12 @@ public class StatisticsListAdapter extends BaseAdapter { Glide.with(context) .load(feed.getImageLocation()) - .placeholder(R.color.light_gray) - .error(R.color.light_gray) - .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) - .fitCenter() - .dontAnimate() + .apply(new RequestOptions() + .placeholder(R.color.light_gray) + .error(R.color.light_gray) + .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) + .fitCenter() + .dontAnimate()) .into(holder.image); holder.title.setText(feed.getTitle()); diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java index 6d19bfa6c..763dcb57d 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java @@ -14,6 +14,7 @@ import com.bumptech.glide.Glide; import java.lang.ref.WeakReference; +import com.bumptech.glide.request.RequestOptions; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.core.feed.Feed; @@ -50,7 +51,6 @@ public class SubscriptionsAdapter extends BaseAdapter implements AdapterView.OnI } private int getAdjustedPosition(int origPosition) { - assert(origPosition != getAddTilePosition()); return origPosition < getAddTilePosition() ? origPosition : origPosition - 1; } @@ -90,9 +90,9 @@ public class SubscriptionsAdapter extends BaseAdapter implements AdapterView.OnI LayoutInflater layoutInflater = (LayoutInflater) mainActivityRef.get().getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = layoutInflater.inflate(R.layout.subscription_item, parent, false); - holder.feedTitle = (TextView) convertView.findViewById(R.id.txtvTitle); - holder.imageView = (ImageView) convertView.findViewById(R.id.imgvCover); - holder.count = (TriangleLabelView) convertView.findViewById(R.id.triangleCountView); + holder.feedTitle = convertView.findViewById(R.id.txtvTitle); + holder.imageView = convertView.findViewById(R.id.imgvCover); + holder.count = convertView.findViewById(R.id.triangleCountView); convertView.setTag(holder); @@ -109,7 +109,7 @@ public class SubscriptionsAdapter extends BaseAdapter implements AdapterView.OnI holder.count.setVisibility(View.INVISIBLE); // when this holder is reused, we could else end up with a cover image - Glide.clear(holder.imageView); + Glide.with(mainActivityRef.get()).clear(holder.imageView); return convertView; } @@ -126,13 +126,13 @@ public class SubscriptionsAdapter extends BaseAdapter implements AdapterView.OnI } else { holder.count.setVisibility(View.GONE); } - Glide.with(mainActivityRef.get()) - .load(feed.getImageLocation()) - .error(R.color.light_gray) - .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) - .fitCenter() - .dontAnimate() - .into(new CoverTarget(null, holder.feedTitle, holder.imageView, mainActivityRef.get())); + + new CoverLoader(mainActivityRef.get()) + .withUri(feed.getImageLocation()) + .withPlaceholderView(holder.feedTitle) + .withCoverView(holder.imageView) + .withError(R.color.light_gray) + .load(); return convertView; } diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java index aea3d583f..06c80e173 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/PodcastListAdapter.java @@ -10,6 +10,7 @@ import android.widget.TextView; import com.bumptech.glide.Glide; +import com.bumptech.glide.request.RequestOptions; import org.apache.commons.lang3.StringUtils; import java.util.List; @@ -40,10 +41,10 @@ public class PodcastListAdapter extends ArrayAdapter<GpodnetPodcast> { .getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = inflater.inflate(R.layout.gpodnet_podcast_listitem, parent, false); - holder.image = (ImageView) convertView.findViewById(R.id.imgvCover); - holder.title = (TextView) convertView.findViewById(R.id.txtvTitle); - holder.subscribers = (TextView) convertView.findViewById(R.id.txtvSubscribers); - holder.url = (TextView) convertView.findViewById(R.id.txtvUrl); + holder.image = convertView.findViewById(R.id.imgvCover); + holder.title = convertView.findViewById(R.id.txtvTitle); + holder.subscribers = convertView.findViewById(R.id.txtvSubscribers); + holder.url = convertView.findViewById(R.id.txtvUrl); convertView.setTag(holder); } else { holder = (Holder) convertView.getTag(); @@ -52,11 +53,12 @@ public class PodcastListAdapter extends ArrayAdapter<GpodnetPodcast> { if (StringUtils.isNotBlank(podcast.getLogoUrl())) { Glide.with(convertView.getContext()) .load(podcast.getLogoUrl()) - .placeholder(R.color.light_gray) - .error(R.color.light_gray) - .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) - .fitCenter() - .dontAnimate() + .apply(new RequestOptions() + .placeholder(R.color.light_gray) + .error(R.color.light_gray) + .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) + .fitCenter() + .dontAnimate()) .into(holder.image); } diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/TagListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/TagListAdapter.java index b4eadefb5..52fca792e 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/TagListAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/gpodnet/TagListAdapter.java @@ -34,8 +34,8 @@ public class TagListAdapter extends ArrayAdapter<GpodnetTag> { .getSystemService(Context.LAYOUT_INFLATER_SERVICE); convertView = inflater.inflate(R.layout.gpodnet_tag_listitem, parent, false); - holder.title = (TextView) convertView.findViewById(R.id.txtvTitle); - holder.usage = (TextView) convertView.findViewById(R.id.txtvUsage); + holder.title = convertView.findViewById(R.id.txtvTitle); + holder.usage = convertView.findViewById(R.id.txtvUsage); convertView.setTag(holder); } else { holder = (Holder) convertView.getTag(); diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java index f1f8be559..2cf17c85f 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java @@ -12,6 +12,8 @@ import android.widget.TextView; import com.bumptech.glide.Glide; import com.bumptech.glide.load.engine.DiskCacheStrategy; +import com.bumptech.glide.request.RequestOptions; +import de.danoeh.antennapod.core.glide.ApGlideSettings; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; @@ -80,10 +82,11 @@ public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> { //Update the empty imageView with the image from the feed Glide.with(context) .load(podcast.imageUrl) - .placeholder(R.color.light_gray) - .diskCacheStrategy(DiskCacheStrategy.NONE) - .fitCenter() - .dontAnimate() + .apply(new RequestOptions() + .placeholder(R.color.light_gray) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .fitCenter() + .dontAnimate()) .into(viewHolder.coverView); //Feed the grid view @@ -124,7 +127,7 @@ public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> { * @param json object holding the podcast information * @throws JSONException */ - public static Podcast fromSearch(JSONObject json) throws JSONException { + public static Podcast fromSearch(JSONObject json) { String title = json.optString("collectionName", ""); String imageUrl = json.optString("artworkUrl100", null); String feedUrl = json.optString("feedUrl", null); @@ -132,7 +135,7 @@ public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> { } public static Podcast fromSearch(SearchHit searchHit) { - return new Podcast(searchHit.getTitle(), searchHit.getImageUrl(), searchHit.getXmlUrl()); + return new Podcast(searchHit.getTitle(), searchHit.getThumbImageURL(), searchHit.getXmlUrl()); } /** @@ -162,7 +165,7 @@ public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> { /** * View holder object for the GridView */ - class PodcastViewHolder { + static class PodcastViewHolder { /** * ImageView holding the Podcast image @@ -182,9 +185,9 @@ public class ItunesAdapter extends ArrayAdapter<ItunesAdapter.Podcast> { * @param view GridView cell */ PodcastViewHolder(View view){ - coverView = (ImageView) view.findViewById(R.id.imgvCover); - titleView = (TextView) view.findViewById(R.id.txtvTitle); - urlView = (TextView) view.findViewById(R.id.txtvUrl); + coverView = view.findViewById(R.id.imgvCover); + titleView = view.findViewById(R.id.txtvTitle); + urlView = view.findViewById(R.id.txtvUrl); } } } diff --git a/app/src/main/java/de/danoeh/antennapod/asynctask/ExportWorker.java b/app/src/main/java/de/danoeh/antennapod/asynctask/ExportWorker.java index 192df8ca5..7c3c570a0 100644 --- a/app/src/main/java/de/danoeh/antennapod/asynctask/ExportWorker.java +++ b/app/src/main/java/de/danoeh/antennapod/asynctask/ExportWorker.java @@ -12,7 +12,7 @@ import de.danoeh.antennapod.core.export.ExportWriter; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.util.LangUtils; -import rx.Observable; +import io.reactivex.Observable; /** * Writes an OPML file into the export directory in the background. @@ -23,15 +23,15 @@ public class ExportWorker { private static final String TAG = "ExportWorker"; private static final String DEFAULT_OUTPUT_NAME = "antennapod-feeds"; - private ExportWriter exportWriter; - private File output; + private final ExportWriter exportWriter; + private final File output; public ExportWorker(ExportWriter exportWriter) { this(exportWriter, new File(UserPreferences.getDataFolder(EXPORT_DIR), DEFAULT_OUTPUT_NAME + "." + exportWriter.fileExtension())); } - public ExportWorker(ExportWriter exportWriter, @NonNull File output) { + private ExportWorker(ExportWriter exportWriter, @NonNull File output) { this.exportWriter = exportWriter; this.output = output; } @@ -57,7 +57,7 @@ public class ExportWorker { subscriber.onError(e); } } - subscriber.onCompleted(); + subscriber.onComplete(); } }); } diff --git a/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlFeedQueuer.java b/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlFeedQueuer.java index 4449d82c2..ea5128102 100644 --- a/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlFeedQueuer.java +++ b/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlFeedQueuer.java @@ -1,6 +1,5 @@ package de.danoeh.antennapod.asynctask; -import android.annotation.SuppressLint; import android.app.ProgressDialog; import android.content.Context; import android.os.AsyncTask; @@ -16,9 +15,9 @@ import de.danoeh.antennapod.core.storage.DownloadRequester; /** Queues items for download in the background. */ public class OpmlFeedQueuer extends AsyncTask<Void, Void, Void> { - private Context context; + private final Context context; private ProgressDialog progDialog; - private int[] selection; + private final int[] selection; public OpmlFeedQueuer(Context context, int[] selection) { super(); @@ -56,13 +55,8 @@ public class OpmlFeedQueuer extends AsyncTask<Void, Void, Void> { return null; } - @SuppressLint("NewApi") public void executeAsync() { - if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) { - executeOnExecutor(THREAD_POOL_EXECUTOR); - } else { - execute(); - } + executeOnExecutor(THREAD_POOL_EXECUTOR); } } diff --git a/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlImportWorker.java b/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlImportWorker.java index 62ea85811..13b95907f 100644 --- a/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlImportWorker.java +++ b/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlImportWorker.java @@ -1,6 +1,5 @@ package de.danoeh.antennapod.asynctask; -import android.annotation.SuppressLint; import android.app.ProgressDialog; import android.content.Context; import android.os.AsyncTask; @@ -21,12 +20,12 @@ public class OpmlImportWorker extends AsyncTask<Void, Void, ArrayList<OpmlElement>> { private static final String TAG = "OpmlImportWorker"; - private Context context; + private final Context context; private Exception exception; private ProgressDialog progDialog; - private Reader mReader; + private final Reader mReader; public OpmlImportWorker(Context context, Reader reader) { super(); @@ -93,13 +92,8 @@ public class OpmlImportWorker extends return exception != null; } - @SuppressLint("NewApi") public void executeAsync() { - if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) { - executeOnExecutor(THREAD_POOL_EXECUTOR); - } else { - execute(); - } + executeOnExecutor(THREAD_POOL_EXECUTOR); } } diff --git a/app/src/main/java/de/danoeh/antennapod/config/ClientConfigurator.java b/app/src/main/java/de/danoeh/antennapod/config/ClientConfigurator.java index f26f2ea76..4138738f6 100644 --- a/app/src/main/java/de/danoeh/antennapod/config/ClientConfigurator.java +++ b/app/src/main/java/de/danoeh/antennapod/config/ClientConfigurator.java @@ -6,7 +6,9 @@ import de.danoeh.antennapod.core.ClientConfig; /** * Configures the ClientConfig class of the core package. */ -public class ClientConfigurator { +class ClientConfigurator { + + private ClientConfigurator(){} static { ClientConfig.USER_AGENT = "AntennaPod/" + BuildConfig.VERSION_NAME; diff --git a/app/src/main/java/de/danoeh/antennapod/config/PlaybackServiceCallbacksImpl.java b/app/src/main/java/de/danoeh/antennapod/config/PlaybackServiceCallbacksImpl.java index 1ab60ef61..eb70d8e0b 100644 --- a/app/src/main/java/de/danoeh/antennapod/config/PlaybackServiceCallbacksImpl.java +++ b/app/src/main/java/de/danoeh/antennapod/config/PlaybackServiceCallbacksImpl.java @@ -2,7 +2,7 @@ package de.danoeh.antennapod.config; import android.content.Context; import android.content.Intent; - +import android.os.Build; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.AudioplayerActivity; import de.danoeh.antennapod.activity.CastplayerActivity; @@ -18,7 +18,11 @@ public class PlaybackServiceCallbacksImpl implements PlaybackServiceCallbacks { return new Intent(context, CastplayerActivity.class); } if (mediaType == MediaType.VIDEO) { - return new Intent(context, VideoplayerActivity.class); + Intent i = new Intent(context, VideoplayerActivity.class); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + i.addFlags(Intent.FLAG_ACTIVITY_NEW_DOCUMENT); + } + return i; } else { return new Intent(context, AudioplayerActivity.class); } diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/AuthenticationDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/AuthenticationDialog.java index 6f9e221ec..2c41b8cb8 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/AuthenticationDialog.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/AuthenticationDialog.java @@ -35,11 +35,11 @@ public abstract class AuthenticationDialog extends Dialog { protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.authentication_dialog); - final EditText etxtUsername = (EditText) findViewById(R.id.etxtUsername); - final EditText etxtPassword = (EditText) findViewById(R.id.etxtPassword); - final CheckBox saveUsernamePassword = (CheckBox) findViewById(R.id.chkSaveUsernamePassword); - final Button butConfirm = (Button) findViewById(R.id.butConfirm); - final Button butCancel = (Button) findViewById(R.id.butCancel); + final EditText etxtUsername = findViewById(R.id.etxtUsername); + final EditText etxtPassword = findViewById(R.id.etxtPassword); + final CheckBox saveUsernamePassword = findViewById(R.id.chkSaveUsernamePassword); + final Button butConfirm = findViewById(R.id.butConfirm); + final Button butCancel = findViewById(R.id.butCancel); if (titleRes != 0) { setTitle(titleRes); diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/AutoFlattrPreferenceDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/AutoFlattrPreferenceDialog.java index 93425949c..c28342374 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/AutoFlattrPreferenceDialog.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/AutoFlattrPreferenceDialog.java @@ -29,9 +29,9 @@ public class AutoFlattrPreferenceDialog { AlertDialog.Builder builder = new AlertDialog.Builder(activity); @SuppressLint("InflateParams") View view = activity.getLayoutInflater().inflate(R.layout.autoflattr_preference_dialog, null); - final CheckBox chkAutoFlattr = (CheckBox) view.findViewById(R.id.chkAutoFlattr); - final SeekBar skbPercent = (SeekBar) view.findViewById(R.id.skbPercent); - final TextView txtvStatus = (TextView) view.findViewById(R.id.txtvStatus); + final CheckBox chkAutoFlattr = view.findViewById(R.id.chkAutoFlattr); + final SeekBar skbPercent = view.findViewById(R.id.skbPercent); + final TextView txtvStatus = view.findViewById(R.id.txtvStatus); chkAutoFlattr.setChecked(UserPreferences.isAutoFlattr()); skbPercent.setEnabled(chkAutoFlattr.isChecked()); diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java index ac073141d..07a64cde8 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java @@ -1,5 +1,6 @@ package de.danoeh.antennapod.dialog; +import android.app.AlertDialog; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.os.Bundle; @@ -32,14 +33,14 @@ import de.danoeh.antennapod.core.util.LongList; public class EpisodesApplyActionFragment extends Fragment { - public String TAG = "EpisodeActionFragment"; + public static final String TAG = "EpisodeActionFragment"; public static final int ACTION_QUEUE = 1; - public static final int ACTION_MARK_PLAYED = 2; - public static final int ACTION_MARK_UNPLAYED = 4; - public static final int ACTION_DOWNLOAD = 8; + private static final int ACTION_MARK_PLAYED = 2; + private static final int ACTION_MARK_UNPLAYED = 4; + private static final int ACTION_DOWNLOAD = 8; public static final int ACTION_REMOVE = 16; - public static final int ACTION_ALL = ACTION_QUEUE | ACTION_MARK_PLAYED | ACTION_MARK_UNPLAYED + private static final int ACTION_ALL = ACTION_QUEUE | ACTION_MARK_PLAYED | ACTION_MARK_UNPLAYED | ACTION_DOWNLOAD | ACTION_REMOVE; private ListView mListView; @@ -84,7 +85,7 @@ public class EpisodesApplyActionFragment extends Fragment { Bundle savedInstanceState) { View view = inflater.inflate(R.layout.episodes_apply_action_fragment, container, false); - mListView = (ListView) view.findViewById(android.R.id.list); + mListView = view.findViewById(android.R.id.list); mListView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); mListView.setOnItemClickListener((ListView, view1, position, rowId) -> { long id = episodes.get(position).getId(); @@ -95,6 +96,28 @@ public class EpisodesApplyActionFragment extends Fragment { } refreshCheckboxes(); }); + mListView.setOnItemLongClickListener((adapterView, view12, position, id) -> { + new AlertDialog.Builder(getActivity()) + .setItems(R.array.batch_long_press_options, (dialogInterface, item) -> { + int direction; + if (item == 0) { + direction = -1; + } else { + direction = 1; + } + + int currentPosition = position + direction; + while (currentPosition >= 0 && currentPosition < episodes.size()) { + long id1 = episodes.get(currentPosition).getId(); + if (!checkedIds.contains(id1)) { + checkedIds.add(id1); + } + currentPosition += direction; + } + refreshCheckboxes(); + }).show(); + return true; + }); for(FeedItem episode : episodes) { titles.add(episode.getTitle()); @@ -106,7 +129,7 @@ public class EpisodesApplyActionFragment extends Fragment { checkAll(); int lastVisibleDiv = 0; - btnAddToQueue = (Button) view.findViewById(R.id.btnAddToQueue); + btnAddToQueue = view.findViewById(R.id.btnAddToQueue); if((actions & ACTION_QUEUE) != 0) { btnAddToQueue.setOnClickListener(v -> queueChecked()); lastVisibleDiv = R.id.divider1; @@ -114,7 +137,7 @@ public class EpisodesApplyActionFragment extends Fragment { btnAddToQueue.setVisibility(View.GONE); view.findViewById(R.id.divider1).setVisibility(View.GONE); } - btnMarkAsPlayed = (Button) view.findViewById(R.id.btnMarkAsPlayed); + btnMarkAsPlayed = view.findViewById(R.id.btnMarkAsPlayed); if((actions & ACTION_MARK_PLAYED) != 0) { btnMarkAsPlayed.setOnClickListener(v -> markedCheckedPlayed()); lastVisibleDiv = R.id.divider2; @@ -122,7 +145,7 @@ public class EpisodesApplyActionFragment extends Fragment { btnMarkAsPlayed.setVisibility(View.GONE); view.findViewById(R.id.divider2).setVisibility(View.GONE); } - btnMarkAsUnplayed = (Button) view.findViewById(R.id.btnMarkAsUnplayed); + btnMarkAsUnplayed = view.findViewById(R.id.btnMarkAsUnplayed); if((actions & ACTION_MARK_UNPLAYED) != 0) { btnMarkAsUnplayed.setOnClickListener(v -> markedCheckedUnplayed()); lastVisibleDiv = R.id.divider3; @@ -130,7 +153,7 @@ public class EpisodesApplyActionFragment extends Fragment { btnMarkAsUnplayed.setVisibility(View.GONE); view.findViewById(R.id.divider3).setVisibility(View.GONE); } - btnDownload = (Button) view.findViewById(R.id.btnDownload); + btnDownload = view.findViewById(R.id.btnDownload); if((actions & ACTION_DOWNLOAD) != 0) { btnDownload.setOnClickListener(v -> downloadChecked()); lastVisibleDiv = R.id.divider4; @@ -138,7 +161,7 @@ public class EpisodesApplyActionFragment extends Fragment { btnDownload.setVisibility(View.GONE); view.findViewById(R.id.divider4).setVisibility(View.GONE); } - btnDelete = (Button) view.findViewById(R.id.btnDelete); + btnDelete = view.findViewById(R.id.btnDelete); if((actions & ACTION_REMOVE) != 0) { btnDelete.setOnClickListener(v -> deleteChecked()); } else { diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/GpodnetSetHostnameDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/GpodnetSetHostnameDialog.java index db6b48735..933ced0f9 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/GpodnetSetHostnameDialog.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/GpodnetSetHostnameDialog.java @@ -8,6 +8,7 @@ import android.view.View; import android.view.ViewGroup; import android.widget.EditText; import android.widget.LinearLayout; + import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.gpoddernet.GpodnetService; import de.danoeh.antennapod.core.preferences.GpodnetPreferences; @@ -16,6 +17,9 @@ import de.danoeh.antennapod.core.preferences.GpodnetPreferences; * Creates a dialog that lets the user change the hostname for the gpodder.net service. */ public class GpodnetSetHostnameDialog { + + private GpodnetSetHostnameDialog(){} + private static final String TAG = "GpodnetSetHostnameDialog"; public static AlertDialog createDialog(final Context context) { diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java index 0bd75b5b0..8f2629b43 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java @@ -29,21 +29,21 @@ import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.service.download.AntennapodHttpClient; import de.danoeh.antennapod.core.service.download.ProxyConfig; +import io.reactivex.Single; +import io.reactivex.SingleOnSubscribe; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; import okhttp3.Credentials; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; -import rx.Observable; -import rx.Subscriber; -import rx.Subscription; -import rx.android.schedulers.AndroidSchedulers; -import rx.schedulers.Schedulers; public class ProxyDialog { private static final String TAG = "ProxyDialog"; - private Context context; + private final Context context; private MaterialDialog dialog; @@ -55,7 +55,7 @@ public class ProxyDialog { private boolean testSuccessful = false; private TextView txtvMessage; - private Subscription subscription; + private Disposable disposable; public ProxyDialog(Context context) { this.context = context; @@ -102,7 +102,7 @@ public class ProxyDialog { .autoDismiss(false) .build(); View view = dialog.getCustomView(); - spType = (Spinner) view.findViewById(R.id.spType); + spType = view.findViewById(R.id.spType); String[] types = { Proxy.Type.DIRECT.name(), Proxy.Type.HTTP.name() }; ArrayAdapter<String> adapter = new ArrayAdapter<>(context, android.R.layout.simple_spinner_item, types); @@ -110,22 +110,22 @@ public class ProxyDialog { spType.setAdapter(adapter); ProxyConfig proxyConfig = UserPreferences.getProxyConfig(); spType.setSelection(adapter.getPosition(proxyConfig.type.name())); - etHost = (EditText) view.findViewById(R.id.etHost); + etHost = view.findViewById(R.id.etHost); if(!TextUtils.isEmpty(proxyConfig.host)) { etHost.setText(proxyConfig.host); } etHost.addTextChangedListener(requireTestOnChange); - etPort = (EditText) view.findViewById(R.id.etPort); + etPort = view.findViewById(R.id.etPort); if(proxyConfig.port > 0) { etPort.setText(String.valueOf(proxyConfig.port)); } etPort.addTextChangedListener(requireTestOnChange); - etUsername = (EditText) view.findViewById(R.id.etUsername); + etUsername = view.findViewById(R.id.etUsername); if(!TextUtils.isEmpty(proxyConfig.username)) { etUsername.setText(proxyConfig.username); } etUsername.addTextChangedListener(requireTestOnChange); - etPassword = (EditText) view.findViewById(R.id.etPassword); + etPassword = view.findViewById(R.id.etPassword); if(!TextUtils.isEmpty(proxyConfig.password)) { etPassword.setText(proxyConfig.username); } @@ -146,7 +146,7 @@ public class ProxyDialog { enableSettings(false); } }); - txtvMessage = (TextView) view.findViewById(R.id.txtvMessage); + txtvMessage = view.findViewById(R.id.txtvMessage); checkValidity(); return dialog; } @@ -229,8 +229,8 @@ public class ProxyDialog { } private void test() { - if(subscription != null) { - subscription.unsubscribe(); + if (disposable != null) { + disposable.dispose(); } if(!checkValidity()) { setTestRequired(true); @@ -243,48 +243,44 @@ public class ProxyDialog { txtvMessage.setTextColor(textColorPrimary); txtvMessage.setText("{fa-circle-o-notch spin} " + checking); txtvMessage.setVisibility(View.VISIBLE); - subscription = Observable.create(new Observable.OnSubscribe<Response>() { - @Override - public void call(Subscriber<? super Response> subscriber) { - String type = (String) spType.getSelectedItem(); - String host = etHost.getText().toString(); - String port = etPort.getText().toString(); - String username = etUsername.getText().toString(); - String password = etPassword.getText().toString(); - int portValue = 8080; - if(!TextUtils.isEmpty(port)) { - portValue = Integer.valueOf(port); - } - SocketAddress address = InetSocketAddress.createUnresolved(host, portValue); - Proxy.Type proxyType = Proxy.Type.valueOf(type.toUpperCase()); - Proxy proxy = new Proxy(proxyType, address); - OkHttpClient.Builder builder = AntennapodHttpClient.newBuilder() - .connectTimeout(10, TimeUnit.SECONDS) - .proxy(proxy); - builder.interceptors().clear(); - OkHttpClient client = builder.build(); - if(!TextUtils.isEmpty(username)) { - String credentials = Credentials.basic(username, password); - client.interceptors().add(chain -> { - Request request = chain.request().newBuilder() - .header("Proxy-Authorization", credentials).build(); - return chain.proceed(request); - }); - } - Request request = new Request.Builder() - .url("http://www.google.com") - .head() - .build(); - try { - Response response = client.newCall(request).execute(); - subscriber.onNext(response); - } catch(IOException e) { - subscriber.onError(e); - } - subscriber.onCompleted(); - } - }) - .subscribeOn(Schedulers.newThread()) + disposable = Single.create((SingleOnSubscribe<Response>) emitter -> { + String type = (String) spType.getSelectedItem(); + String host = etHost.getText().toString(); + String port = etPort.getText().toString(); + String username = etUsername.getText().toString(); + String password = etPassword.getText().toString(); + int portValue = 8080; + if(!TextUtils.isEmpty(port)) { + portValue = Integer.valueOf(port); + } + SocketAddress address = InetSocketAddress.createUnresolved(host, portValue); + Proxy.Type proxyType = Proxy.Type.valueOf(type.toUpperCase()); + Proxy proxy = new Proxy(proxyType, address); + OkHttpClient.Builder builder = AntennapodHttpClient.newBuilder() + .connectTimeout(10, TimeUnit.SECONDS) + .proxy(proxy); + builder.interceptors().clear(); + OkHttpClient client = builder.build(); + if(!TextUtils.isEmpty(username)) { + String credentials = Credentials.basic(username, password); + client.interceptors().add(chain -> { + Request request = chain.request().newBuilder() + .header("Proxy-Authorization", credentials).build(); + return chain.proceed(request); + }); + } + Request request = new Request.Builder() + .url("http://www.google.com") + .head() + .build(); + try { + Response response = client.newCall(request).execute(); + emitter.onSuccess(response); + } catch(IOException e) { + emitter.onError(e); + } + }) + .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe( response -> { diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java index 64fc1fda4..ece184035 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java @@ -17,6 +17,8 @@ import de.danoeh.antennapod.R; public class RatingDialog { + private RatingDialog(){} + private static final String TAG = RatingDialog.class.getSimpleName(); private static final int AFTER_DAYS = 7; @@ -54,7 +56,7 @@ public class RatingDialog { } } - public static void rateNow() { + private static void rateNow() { Context context = mContext.get(); if(context == null) { return; @@ -67,11 +69,11 @@ public class RatingDialog { saveRated(); } - public static boolean rated() { + private static boolean rated() { return mPreferences.getBoolean(KEY_RATED, false); } - public static void saveRated() { + private static void saveRated() { mPreferences .edit() .putBoolean(KEY_RATED, true) diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java index 7d6a66a54..4b8601ec6 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/SleepTimerDialog.java @@ -1,7 +1,6 @@ package de.danoeh.antennapod.dialog; import android.content.Context; -import android.support.design.widget.Snackbar; import android.text.Editable; import android.text.TextWatcher; import android.util.Log; @@ -9,7 +8,6 @@ import android.view.View; import android.view.inputmethod.InputMethodManager; import android.widget.ArrayAdapter; import android.widget.CheckBox; -import android.widget.CompoundButton; import android.widget.EditText; import android.widget.Spinner; import android.widget.Toast; @@ -26,7 +24,7 @@ public abstract class SleepTimerDialog { private static final String TAG = SleepTimerDialog.class.getSimpleName(); - private Context context; + private final Context context; private MaterialDialog dialog; private EditText etxtTime; @@ -63,11 +61,11 @@ public abstract class SleepTimerDialog { dialog = builder.build(); View view = dialog.getView(); - etxtTime = (EditText) view.findViewById(R.id.etxtTime); - spTimeUnit = (Spinner) view.findViewById(R.id.spTimeUnit); - cbShakeToReset = (CheckBox) view.findViewById(R.id.cbShakeToReset); - cbVibrate = (CheckBox) view.findViewById(R.id.cbVibrate); - chAutoEnable = (CheckBox) view.findViewById(R.id.chAutoEnable); + etxtTime = view.findViewById(R.id.etxtTime); + spTimeUnit = view.findViewById(R.id.spTimeUnit); + cbShakeToReset = view.findViewById(R.id.cbShakeToReset); + cbVibrate = view.findViewById(R.id.cbVibrate); + chAutoEnable = view.findViewById(R.id.chAutoEnable); etxtTime.setText(SleepTimerPreferences.lastTimerValue()); etxtTime.addTextChangedListener(new TextWatcher() { diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java index 6a975fe49..cf9a2907b 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java @@ -52,7 +52,7 @@ public class VariableSpeedDialog { builder.neutralText(R.string.close_label); builder.onPositive((dialog, which) -> { if (Build.VERSION.SDK_INT >= 16) { // just to be safe - UserPreferences.enableSonic(true); + UserPreferences.enableSonic(); if(showSpeedSelector) { showSpeedSelectorDialog(context); } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java index f14ebbdaf..ee2373da8 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java @@ -25,25 +25,25 @@ public class AddFeedFragment extends Fragment { /** * Preset value for url text field. */ - public static final String ARG_FEED_URL = "feedurl"; + private static final String ARG_FEED_URL = "feedurl"; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { super.onCreateView(inflater, container, savedInstanceState); View root = inflater.inflate(R.layout.addfeed, container, false); - final EditText etxtFeedurl = (EditText) root.findViewById(R.id.etxtFeedurl); + final EditText etxtFeedurl = root.findViewById(R.id.etxtFeedurl); Bundle args = getArguments(); if (args != null && args.getString(ARG_FEED_URL) != null) { etxtFeedurl.setText(args.getString(ARG_FEED_URL)); } - Button butSearchITunes = (Button) root.findViewById(R.id.butSearchItunes); - Button butBrowserGpoddernet = (Button) root.findViewById(R.id.butBrowseGpoddernet); - Button butSearchFyyd = (Button) root.findViewById(R.id.butSearchFyyd); - Button butOpmlImport = (Button) root.findViewById(R.id.butOpmlImport); - Button butConfirm = (Button) root.findViewById(R.id.butConfirm); + Button butSearchITunes = root.findViewById(R.id.butSearchItunes); + Button butBrowserGpoddernet = root.findViewById(R.id.butBrowseGpoddernet); + Button butSearchFyyd = root.findViewById(R.id.butSearchFyyd); + Button butOpmlImport = root.findViewById(R.id.butOpmlImport); + Button butConfirm = root.findViewById(R.id.butConfirm); final MainActivity activity = (MainActivity) getActivity(); activity.getSupportActionBar().setTitle(R.string.add_feed_label); @@ -66,4 +66,15 @@ public class AddFeedFragment extends Fragment { return root; } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setRetainInstance(true); + + // So, we certainly *don't* have an options menu, + // but unless we say we do, old options menus sometimes + // persist. mfietz thinks this causes the ActionBar to be invalidated + setHasOptionsMenu(true); + } } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java index bbfd1688d..ef522d3b3 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java @@ -4,6 +4,8 @@ import android.content.Context; import android.content.DialogInterface; import android.content.SharedPreferences; import android.os.Bundle; +import android.os.Handler; +import android.support.design.widget.Snackbar; import android.support.v4.app.Fragment; import android.support.v4.view.MenuItemCompat; import android.support.v7.widget.LinearLayoutManager; @@ -36,22 +38,22 @@ import de.danoeh.antennapod.core.feed.EventDistributor; import de.danoeh.antennapod.core.feed.Feed; 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.download.DownloadService; 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.FeedItemUtil; import de.danoeh.antennapod.core.util.LongList; import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler; import de.danoeh.antennapod.menuhandler.MenuItemUtils; import de.greenrobot.event.EventBus; -import rx.Observable; -import rx.Subscription; -import rx.android.schedulers.AndroidSchedulers; -import rx.schedulers.Schedulers; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; /** * Shows unread or recently published episodes @@ -69,24 +71,24 @@ public class AllEpisodesFragment extends Fragment { private static final String PREF_SCROLL_POSITION = "scroll_position"; private static final String PREF_SCROLL_OFFSET = "scroll_offset"; - protected RecyclerView recyclerView; - protected AllEpisodesRecycleAdapter listAdapter; + RecyclerView recyclerView; + AllEpisodesRecycleAdapter listAdapter; private ProgressBar progLoading; - protected List<FeedItem> episodes; + List<FeedItem> episodes; private List<Downloader> downloaderList; private boolean itemsLoaded = false; private boolean viewsCreated = false; private boolean isUpdatingFeeds; - protected boolean isMenuInvalidationAllowed = false; + boolean isMenuInvalidationAllowed = false; - protected Subscription subscription; + Disposable disposable; private LinearLayoutManager layoutManager; - protected boolean showOnlyNewEpisodes() { return false; } - protected String getPrefName() { return DEFAULT_PREF_NAME; } + boolean showOnlyNewEpisodes() { return false; } + String getPrefName() { return DEFAULT_PREF_NAME; } @Override public void onCreate(Bundle savedInstanceState) { @@ -123,8 +125,8 @@ public class AllEpisodesFragment extends Fragment { public void onStop() { super.onStop(); EventDistributor.getInstance().unregister(contentUpdate); - if(subscription != null) { - subscription.unsubscribe(); + if (disposable != null) { + disposable.dispose(); } } @@ -165,7 +167,7 @@ public class AllEpisodesFragment extends Fragment { } } - protected void resetViewState() { + void resetViewState() { viewsCreated = false; listAdapter = null; } @@ -273,24 +275,24 @@ public class AllEpisodesFragment extends Fragment { if(item.getItemId() == R.id.share_item) { return true; // avoids that the position is reset when we need it in the submenu } - int pos = listAdapter.getPosition(); - if(pos < 0) { - return false; - } - FeedItem selectedItem = itemAccess.getItem(pos); + FeedItem selectedItem = listAdapter.getSelectedItem(); if (selectedItem == null) { - Log.i(TAG, "Selected item at position " + pos + " was null, ignoring selection"); + Log.i(TAG, "Selected item was null, ignoring selection"); return super.onContextItemSelected(item); } - try { - return FeedItemMenuHandler.onMenuItemClicked(getActivity(), item.getItemId(), selectedItem); - } catch (DownloadRequestException e) { - e.printStackTrace(); - Toast.makeText(getActivity(), e.getMessage(), Toast.LENGTH_LONG).show(); + // Mark as seen contains UI logic specific to All/New/FavoriteSegments, + // e.g., Undo with Snackbar, + // and is handled by this class rather than the generic FeedItemMenuHandler + // Undo is useful for Mark as seen, given there is no UI to undo it otherwise, + // i.e., there is context menu item for Mark as new + if (R.id.mark_as_seen_item == item.getItemId()) { + markItemAsSeenWithUndo(selectedItem); return true; } + + return FeedItemMenuHandler.onMenuItemClicked(getActivity(), item.getItemId(), selectedItem); } @Override @@ -299,15 +301,15 @@ public class AllEpisodesFragment extends Fragment { R.layout.all_episodes_fragment); } - protected View onCreateViewHelper(LayoutInflater inflater, - ViewGroup container, - Bundle savedInstanceState, - int fragmentResource) { + View onCreateViewHelper(LayoutInflater inflater, + ViewGroup container, + Bundle savedInstanceState, + int fragmentResource) { super.onCreateView(inflater, container, savedInstanceState); View root = inflater.inflate(fragmentResource, container, false); - recyclerView = (RecyclerView) root.findViewById(android.R.id.list); + recyclerView = root.findViewById(android.R.id.list); RecyclerView.ItemAnimator animator = recyclerView.getItemAnimator(); if (animator instanceof SimpleItemAnimator) { ((SimpleItemAnimator) animator).setSupportsChangeAnimations(false); @@ -317,7 +319,7 @@ public class AllEpisodesFragment extends Fragment { recyclerView.setHasFixedSize(true); recyclerView.addItemDecoration(new HorizontalDividerItemDecoration.Builder(getActivity()).build()); - progLoading = (ProgressBar) root.findViewById(R.id.progLoading); + progLoading = root.findViewById(R.id.progLoading); if (!itemsLoaded) { progLoading.setVisibility(View.VISIBLE); @@ -346,7 +348,7 @@ public class AllEpisodesFragment extends Fragment { updateShowOnlyEpisodesListViewState(); } - protected AllEpisodesRecycleAdapter.ItemAccess itemAccess = new AllEpisodesRecycleAdapter.ItemAccess() { + private final AllEpisodesRecycleAdapter.ItemAccess itemAccess = new AllEpisodesRecycleAdapter.ItemAccess() { @Override public int getCount() { @@ -444,7 +446,7 @@ public class AllEpisodesFragment extends Fragment { } } - private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { + private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { @Override public void update(EventDistributor eventDistributor, Integer arg) { if ((arg & EVENTS) != 0) { @@ -459,16 +461,16 @@ public class AllEpisodesFragment extends Fragment { private void updateShowOnlyEpisodesListViewState() { } - protected void loadItems() { - if(subscription != null) { - subscription.unsubscribe(); + void loadItems() { + if (disposable != null) { + disposable.dispose(); } if (viewsCreated && !itemsLoaded) { recyclerView.setVisibility(View.GONE); progLoading.setVisibility(View.VISIBLE); } - subscription = Observable.fromCallable(this::loadData) - .subscribeOn(Schedulers.newThread()) + disposable = Observable.fromCallable(this::loadData) + .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(data -> { recyclerView.setVisibility(View.VISIBLE); @@ -483,8 +485,40 @@ public class AllEpisodesFragment extends Fragment { }, error -> Log.e(TAG, Log.getStackTraceString(error))); } - protected List<FeedItem> loadData() { + List<FeedItem> loadData() { return DBReader.getRecentlyPublishedEpisodes(RECENT_EPISODES_LIMIT); } + void markItemAsSeenWithUndo(FeedItem item) { + if (item == null) { + return; + } + + Log.d(TAG, "markItemAsSeenWithUndo(" + item.getId() + ")"); + if (disposable != null) { + disposable.dispose(); + } + // we're marking it as unplayed since the user didn't actually play it + // but they don't want it considered 'NEW' anymore + DBWriter.markItemPlayed(FeedItem.UNPLAYED, item.getId()); + + final Handler h = new Handler(getActivity().getMainLooper()); + final Runnable r = () -> { + FeedMedia media = item.getMedia(); + if (media != null && media.hasAlmostEnded() && UserPreferences.isAutoDelete()) { + DBWriter.deleteFeedMediaOfItem(getActivity(), media.getId()); + } + }; + + Snackbar snackbar = Snackbar.make(getView(), getString(R.string.marked_as_seen_label), + Snackbar.LENGTH_LONG); + snackbar.setAction(getString(R.string.undo), v -> { + DBWriter.markItemPlayed(FeedItem.NEW, item.getId()); + // don't forget to cancel the thing that's going to remove the media + h.removeCallbacks(r); + }); + snackbar.show(); + h.postDelayed(r, (int)Math.ceil(snackbar.getDuration() * 1.05f)); + } + } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java index 1ba7ed557..4bba9b255 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java @@ -1,8 +1,6 @@ package de.danoeh.antennapod.fragment; import android.content.Context; -import android.content.res.TypedArray; -import android.graphics.Color; import android.os.Bundle; import android.support.v4.app.ListFragment; import android.util.Log; @@ -12,9 +10,6 @@ import android.view.MenuItem; import android.view.View; import android.widget.ListView; -import com.joanzapata.iconify.IconDrawable; -import com.joanzapata.iconify.fonts.FontAwesomeIcons; - import java.util.List; import de.danoeh.antennapod.R; @@ -22,15 +17,14 @@ import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.adapter.DownloadedEpisodesListAdapter; import de.danoeh.antennapod.core.feed.EventDistributor; import de.danoeh.antennapod.core.feed.FeedItem; -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.util.FeedItemUtil; import de.danoeh.antennapod.dialog.EpisodesApplyActionFragment; -import rx.Observable; -import rx.Subscription; -import rx.android.schedulers.AndroidSchedulers; -import rx.schedulers.Schedulers; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; /** * Displays all running downloads and provides a button to delete them @@ -48,7 +42,7 @@ public class CompletedDownloadsFragment extends ListFragment { private boolean viewCreated = false; - private Subscription subscription; + private Disposable disposable; @Override public void onCreate(Bundle savedInstanceState) { @@ -67,16 +61,16 @@ public class CompletedDownloadsFragment extends ListFragment { public void onStop() { super.onStop(); EventDistributor.getInstance().unregister(contentUpdate); - if(subscription != null) { - subscription.unsubscribe(); + if (disposable != null) { + disposable.dispose(); } } @Override public void onDetach() { super.onDetach(); - if(subscription != null) { - subscription.unsubscribe(); + if (disposable != null) { + disposable.dispose(); } } @@ -85,8 +79,8 @@ public class CompletedDownloadsFragment extends ListFragment { super.onDestroyView(); listAdapter = null; viewCreated = false; - if(subscription != null) { - subscription.unsubscribe(); + if (disposable != null) { + disposable.dispose(); } } @@ -140,18 +134,7 @@ public class CompletedDownloadsFragment extends ListFragment { super.onCreateOptionsMenu(menu, inflater); if(items != null) { inflater.inflate(R.menu.downloads_completed, menu); - MenuItem episodeActions = menu.findItem(R.id.episode_actions); - if(items.size() > 0) { - int[] attrs = {R.attr.action_bar_icon_color}; - TypedArray ta = getActivity().obtainStyledAttributes(UserPreferences.getTheme(), attrs); - int textColor = ta.getColor(0, Color.GRAY); - ta.recycle(); - episodeActions.setIcon(new IconDrawable(getActivity(), - FontAwesomeIcons.fa_gears).color(textColor).actionBarSize()); - episodeActions.setVisible(true); - } else { - episodeActions.setVisible(false); - } + menu.findItem(R.id.episode_actions).setVisible(items.size() > 0); } } @@ -160,7 +143,7 @@ public class CompletedDownloadsFragment extends ListFragment { switch (item.getItemId()) { case R.id.episode_actions: EpisodesApplyActionFragment fragment = EpisodesApplyActionFragment - .newInstance(items, EpisodesApplyActionFragment.ACTION_REMOVE); + .newInstance(items, EpisodesApplyActionFragment.ACTION_REMOVE | EpisodesApplyActionFragment.ACTION_QUEUE); ((MainActivity) getActivity()).loadChildFragment(fragment); return true; default: @@ -168,7 +151,7 @@ public class CompletedDownloadsFragment extends ListFragment { } } - private DownloadedEpisodesListAdapter.ItemAccess itemAccess = new DownloadedEpisodesListAdapter.ItemAccess() { + private final DownloadedEpisodesListAdapter.ItemAccess itemAccess = new DownloadedEpisodesListAdapter.ItemAccess() { @Override public int getCount() { return (items != null) ? items.size() : 0; @@ -189,7 +172,7 @@ public class CompletedDownloadsFragment extends ListFragment { } }; - private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { + private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { @Override public void update(EventDistributor eventDistributor, Integer arg) { if ((arg & EVENTS) != 0) { @@ -199,21 +182,19 @@ public class CompletedDownloadsFragment extends ListFragment { }; private void loadItems() { - if(subscription != null) { - subscription.unsubscribe(); + if (disposable != null) { + disposable.dispose(); } if (items == null && viewCreated) { setListShown(false); } - subscription = Observable.fromCallable(DBReader::getDownloadedItems) - .subscribeOn(Schedulers.newThread()) + disposable = Observable.fromCallable(DBReader::getDownloadedItems) + .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(result -> { - if (result != null) { - items = result; - if (viewCreated && getActivity() != null) { - onFragmentLoaded(); - } + items = result; + if (viewCreated && getActivity() != null) { + onFragmentLoaded(); } }, error -> Log.e(TAG, Log.getStackTraceString(error))); } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java index 1d3fcefba..5a061c7e6 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java @@ -11,6 +11,7 @@ import android.widget.TextView; import com.bumptech.glide.Glide; +import com.bumptech.glide.request.RequestOptions; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MediaplayerInfoActivity.MediaplayerInfoContentFragment; import de.danoeh.antennapod.core.glide.ApGlideSettings; @@ -22,7 +23,6 @@ import de.danoeh.antennapod.core.util.playback.Playable; public class CoverFragment extends Fragment implements MediaplayerInfoContentFragment { private static final String TAG = "CoverFragment"; - private static final String ARG_PLAYABLE = "arg.playable"; private Playable media; @@ -49,9 +49,9 @@ public class CoverFragment extends Fragment implements MediaplayerInfoContentFra public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { root = inflater.inflate(R.layout.cover_fragment, container, false); - txtvPodcastTitle = (TextView) root.findViewById(R.id.txtvPodcastTitle); - txtvEpisodeTitle = (TextView) root.findViewById(R.id.txtvEpisodeTitle); - imgvCover = (ImageView) root.findViewById(R.id.imgvCover); + txtvPodcastTitle = root.findViewById(R.id.txtvPodcastTitle); + txtvEpisodeTitle = root.findViewById(R.id.txtvEpisodeTitle); + imgvCover = root.findViewById(R.id.imgvCover); return root; } @@ -61,9 +61,10 @@ public class CoverFragment extends Fragment implements MediaplayerInfoContentFra txtvEpisodeTitle.setText(media.getEpisodeTitle()); Glide.with(this) .load(media.getImageLocation()) - .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) - .dontAnimate() - .fitCenter() + .apply(new RequestOptions() + .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) + .dontAnimate() + .fitCenter()) .into(imgvCover); } else { Log.w(TAG, "loadMediaInfo was called while media was null"); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java index 0a710196a..5ab6bac63 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java @@ -1,5 +1,7 @@ package de.danoeh.antennapod.fragment; +import android.app.AlertDialog; +import android.app.Dialog; import android.content.res.TypedArray; import android.os.Bundle; import android.support.v4.app.ListFragment; @@ -10,19 +12,21 @@ import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.widget.ListView; +import android.widget.TextView; import java.util.List; import de.danoeh.antennapod.R; import de.danoeh.antennapod.adapter.DownloadLogAdapter; import de.danoeh.antennapod.core.feed.EventDistributor; +import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.service.download.DownloadStatus; import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBWriter; -import rx.Observable; -import rx.Subscription; -import rx.android.schedulers.AndroidSchedulers; -import rx.schedulers.Schedulers; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; /** * Shows the download log @@ -37,7 +41,7 @@ public class DownloadLogFragment extends ListFragment { private boolean viewsCreated = false; private boolean itemsLoaded = false; - private Subscription subscription; + private Disposable disposable; @Override public void onStart() { @@ -51,8 +55,8 @@ public class DownloadLogFragment extends ListFragment { public void onStop() { super.onStop(); EventDistributor.getInstance().unregister(contentUpdate); - if(subscription != null) { - subscription.unsubscribe(); + if(disposable != null) { + disposable.dispose(); } } @@ -82,7 +86,30 @@ public class DownloadLogFragment extends ListFragment { getActivity().supportInvalidateOptionsMenu(); } - private DownloadLogAdapter.ItemAccess itemAccess = new DownloadLogAdapter.ItemAccess() { + @Override + public void onListItemClick(ListView l, View v, int position, long id) { + super.onListItemClick(l, v, position, id); + + DownloadStatus status = adapter.getItem(position); + String url = "unknown"; + String message = getString(R.string.download_successful); + FeedMedia media = DBReader.getFeedMedia(status.getFeedfileId()); + if (media != null) { + url = media.getDownload_url(); + } + if (!status.isSuccessful()) { + message = status.getReasonDetailed(); + } + + AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); + builder.setTitle(R.string.download_error_details); + builder.setMessage(getString(R.string.download_error_details_message, message, url)); + builder.setPositiveButton(android.R.string.ok, null); + Dialog dialog = builder.show(); + ((TextView) dialog.findViewById(android.R.id.message)).setTextIsSelectable(true); + } + + private final DownloadLogAdapter.ItemAccess itemAccess = new DownloadLogAdapter.ItemAccess() { @Override public int getCount() { @@ -99,7 +126,7 @@ public class DownloadLogFragment extends ListFragment { } }; - private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { + private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { @Override public void update(EventDistributor eventDistributor, Integer arg) { @@ -151,11 +178,11 @@ public class DownloadLogFragment extends ListFragment { } private void loadItems() { - if(subscription != null) { - subscription.unsubscribe(); + if(disposable != null) { + disposable.dispose(); } - subscription = Observable.fromCallable(DBReader::getDownloadLog) - .subscribeOn(Schedulers.newThread()) + disposable = Observable.fromCallable(DBReader::getDownloadLog) + .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(result -> { if (result != null) { diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java index 52a38ccb9..aa6029c84 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java @@ -25,7 +25,7 @@ public class DownloadsFragment extends Fragment { public static final String ARG_SELECTED_TAB = "selected_tab"; public static final int POS_RUNNING = 0; - public static final int POS_COMPLETED = 1; + private static final int POS_COMPLETED = 1; public static final int POS_LOG = 2; private static final String PREF_LAST_TAB_POSITION = "tab_position"; @@ -38,12 +38,12 @@ public class DownloadsFragment extends Fragment { super.onCreateView(inflater, container, savedInstanceState); View root = inflater.inflate(R.layout.pager_fragment, container, false); - viewPager = (ViewPager)root.findViewById(R.id.viewpager); + viewPager = root.findViewById(R.id.viewpager); DownloadsPagerAdapter pagerAdapter = new DownloadsPagerAdapter(getChildFragmentManager(), getResources()); viewPager.setAdapter(pagerAdapter); // Give the TabLayout the ViewPager - tabLayout = (TabLayout) root.findViewById(R.id.sliding_tabs); + tabLayout = root.findViewById(R.id.sliding_tabs); tabLayout.setupWithViewPager(viewPager); return root; @@ -78,9 +78,9 @@ public class DownloadsFragment extends Fragment { viewPager.setCurrentItem(lastPosition); } - public class DownloadsPagerAdapter extends FragmentPagerAdapter { + public static class DownloadsPagerAdapter extends FragmentPagerAdapter { - Resources resources; + final Resources resources; public DownloadsPagerAdapter(FragmentManager fm, Resources resources) { super(fm); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java index e2fbd91f3..0610bfd24 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java @@ -21,10 +21,10 @@ public class EpisodesFragment extends Fragment { public static final String TAG = "EpisodesFragment"; private static final String PREF_LAST_TAB_POSITION = "tab_position"; - public static final int POS_NEW_EPISODES = 0; - public static final int POS_ALL_EPISODES = 1; - public static final int POS_FAV_EPISODES = 2; - public static final int TOTAL_COUNT = 3; + private static final int POS_NEW_EPISODES = 0; + private static final int POS_ALL_EPISODES = 1; + private static final int POS_FAV_EPISODES = 2; + private static final int TOTAL_COUNT = 3; private TabLayout tabLayout; @@ -46,11 +46,11 @@ public class EpisodesFragment extends Fragment { ((MainActivity) getActivity()).getSupportActionBar().setTitle(R.string.episodes_label); View rootView = inflater.inflate(R.layout.pager_fragment, container, false); - viewPager = (ViewPager)rootView.findViewById(R.id.viewpager); + viewPager = rootView.findViewById(R.id.viewpager); viewPager.setAdapter(new EpisodesPagerAdapter(getChildFragmentManager(), getResources())); // Give the TabLayout the ViewPager - tabLayout = (TabLayout) rootView.findViewById(R.id.sliding_tabs); + tabLayout = rootView.findViewById(R.id.sliding_tabs); tabLayout.setupWithViewPager(viewPager); return rootView; @@ -79,7 +79,7 @@ public class EpisodesFragment extends Fragment { public static class EpisodesPagerAdapter extends FragmentPagerAdapter { private final Resources resources; - private AllEpisodesFragment[] fragments = { + private final AllEpisodesFragment[] fragments = { new NewEpisodesFragment(), new AllEpisodesFragment(), new FavoriteEpisodesFragment() @@ -106,7 +106,7 @@ public class EpisodesFragment extends Fragment { case POS_ALL_EPISODES: return resources.getString(R.string.all_episodes_short_label); case POS_NEW_EPISODES: - return resources.getString(R.string.new_label); + return resources.getString(R.string.new_episodes_label); case POS_FAV_EPISODES: return resources.getString(R.string.favorite_episodes_label); default: diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java index 1e385728a..de2f04590 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java @@ -1,6 +1,9 @@ package de.danoeh.antennapod.fragment; +import android.content.Intent; +import android.os.Build; import android.os.Bundle; +import android.support.v4.app.ActivityOptionsCompat; import android.support.v4.app.Fragment; import android.util.Log; import android.view.LayoutInflater; @@ -13,12 +16,17 @@ import android.widget.TextView; import com.bumptech.glide.Glide; +import com.bumptech.glide.request.RequestOptions; import de.danoeh.antennapod.R; +import de.danoeh.antennapod.core.feed.MediaType; import de.danoeh.antennapod.core.glide.ApGlideSettings; import de.danoeh.antennapod.core.service.playback.PlaybackService; -import de.danoeh.antennapod.core.util.Converter; import de.danoeh.antennapod.core.util.playback.Playable; import de.danoeh.antennapod.core.util.playback.PlaybackController; +import io.reactivex.Maybe; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; /** * Fragment which is supposed to be displayed outside of the MediaplayerActivity @@ -33,8 +41,8 @@ public class ExternalPlayerFragment extends Fragment { private ImageButton butPlay; private TextView mFeedName; private ProgressBar mProgressBar; - private PlaybackController controller; + private Disposable disposable; public ExternalPlayerFragment() { super(); @@ -45,19 +53,26 @@ public class ExternalPlayerFragment extends Fragment { Bundle savedInstanceState) { View root = inflater.inflate(R.layout.external_player_fragment, container, false); - fragmentLayout = (ViewGroup) root.findViewById(R.id.fragmentLayout); - imgvCover = (ImageView) root.findViewById(R.id.imgvCover); - txtvTitle = (TextView) root.findViewById(R.id.txtvTitle); - butPlay = (ImageButton) root.findViewById(R.id.butPlay); - mFeedName = (TextView) root.findViewById(R.id.txtvAuthor); - mProgressBar = (ProgressBar) root.findViewById(R.id.episodeProgress); + fragmentLayout = root.findViewById(R.id.fragmentLayout); + imgvCover = root.findViewById(R.id.imgvCover); + txtvTitle = root.findViewById(R.id.txtvTitle); + butPlay = root.findViewById(R.id.butPlay); + mFeedName = root.findViewById(R.id.txtvAuthor); + mProgressBar = root.findViewById(R.id.episodeProgress); fragmentLayout.setOnClickListener(v -> { Log.d(TAG, "layoutInfo was clicked"); if (controller != null && controller.getMedia() != null) { - startActivity(PlaybackService.getPlayerActivityIntent( - getActivity(), controller.getMedia())); + Intent intent = PlaybackService.getPlayerActivityIntent(getActivity(), controller.getMedia()); + + if (Build.VERSION.SDK_INT >= 16 && controller.getMedia().getMediaType() == MediaType.AUDIO) { + ActivityOptionsCompat options = ActivityOptionsCompat. + makeSceneTransitionAnimation(getActivity(), imgvCover, "coverTransition"); + startActivity(intent, options.toBundle()); + } else { + startActivity(intent); + } } }); return root; @@ -68,10 +83,15 @@ public class ExternalPlayerFragment extends Fragment { super.onActivityCreated(savedInstanceState); controller = setupPlaybackController(); butPlay.setOnClickListener(v -> { - if(controller != null) { + if (controller != null) { controller.playPause(); } }); + loadMediaInfo(); + } + + public void connectToPlaybackService() { + controller.init(); } private PlaybackController setupPlaybackController() { @@ -112,9 +132,9 @@ public class ExternalPlayerFragment extends Fragment { @Override public void onResume() { super.onResume(); + onPositionObserverUpdate(); + controller.init(); - mProgressBar.setProgress((int) - ((double) controller.getPosition() / controller.getDuration() * 100)); } @Override @@ -124,6 +144,9 @@ public class ExternalPlayerFragment extends Fragment { if (controller != null) { controller.release(); } + if (disposable != null) { + disposable.dispose(); + } } @Override @@ -144,7 +167,7 @@ public class ExternalPlayerFragment extends Fragment { controller = setupPlaybackController(); if (butPlay != null) { butPlay.setOnClickListener(v -> { - if(controller != null) { + if (controller != null) { controller.playPause(); } }); @@ -154,50 +177,65 @@ public class ExternalPlayerFragment extends Fragment { private boolean loadMediaInfo() { Log.d(TAG, "Loading media info"); - if (controller != null && controller.serviceAvailable()) { - Playable media = controller.getMedia(); - if (media != null) { - txtvTitle.setText(media.getEpisodeTitle()); - mFeedName.setText(media.getFeedTitle()); - mProgressBar.setProgress((int) - ((double) controller.getPosition() / controller.getDuration() * 100)); - - Glide.with(getActivity()) - .load(media.getImageLocation()) + if (controller == null) { + Log.w(TAG, "loadMediaInfo was called while PlaybackController was null!"); + return false; + } + + if (disposable != null) { + disposable.dispose(); + } + disposable = Maybe.create(emitter -> { + Playable media = controller.getMedia(); + if (media != null) { + emitter.onSuccess(media); + } else { + emitter.onComplete(); + } + }) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(media -> updateUi((Playable) media), + error -> Log.e(TAG, Log.getStackTraceString(error))); + return true; + } + + private void updateUi(Playable media) { + if (media != null) { + txtvTitle.setText(media.getEpisodeTitle()); + mFeedName.setText(media.getFeedTitle()); + onPositionObserverUpdate(); + + Glide.with(getActivity()) + .load(media.getImageLocation()) + .apply(new RequestOptions() .placeholder(R.color.light_gray) .error(R.color.light_gray) .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) .fitCenter() - .dontAnimate() - .into(imgvCover); + .dontAnimate()) + .into(imgvCover); - fragmentLayout.setVisibility(View.VISIBLE); - if (controller.isPlayingVideoLocally()) { - butPlay.setVisibility(View.GONE); - } else { - butPlay.setVisibility(View.VISIBLE); - } - return true; + fragmentLayout.setVisibility(View.VISIBLE); + if (controller.isPlayingVideoLocally()) { + butPlay.setVisibility(View.GONE); } else { - Log.w(TAG, "loadMediaInfo was called while the media object of playbackService was null!"); - return false; + butPlay.setVisibility(View.VISIBLE); } } else { - Log.w(TAG, "loadMediaInfo was called while playbackService was null!"); - return false; + Log.w(TAG, "loadMediaInfo was called while the media object of playbackService was null!"); } } - private String getPositionString(int position, int duration) { - return Converter.getDurationStringLong(position) + " / " - + Converter.getDurationStringLong(duration); - } - public PlaybackController getPlaybackControllerTestingOnly() { return controller; } - public void onPositionObserverUpdate() { + private void onPositionObserverUpdate() { + if (controller.getPosition() == PlaybackService.INVALID_TIME + || controller.getDuration() == PlaybackService.INVALID_TIME) { + return; + } mProgressBar.setProgress((int) ((double) controller.getPosition() / controller.getDuration() * 100)); } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java index 234c8377d..70f82c2ec 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java @@ -26,7 +26,7 @@ import de.danoeh.antennapod.core.storage.DBWriter; public class FavoriteEpisodesFragment extends AllEpisodesFragment { - public static final String TAG = "FavoriteEpisodesFrag"; + private static final String TAG = "FavoriteEpisodesFrag"; private static final String PREF_NAME = "PrefFavoriteEpisodesFragment"; @@ -62,8 +62,8 @@ public class FavoriteEpisodesFragment extends AllEpisodesFragment { AllEpisodesRecycleAdapter.Holder holder = (AllEpisodesRecycleAdapter.Holder)viewHolder; Log.d(TAG, "remove(" + holder.getItemId() + ")"); - if (subscription != null) { - subscription.unsubscribe(); + if (disposable != null) { + disposable.dispose(); } FeedItem item = holder.getFeedItem(); if (item != null) { diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FyydSearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FyydSearchFragment.java index 7c1ec5ec1..dadc596e2 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/FyydSearchFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/FyydSearchFragment.java @@ -28,9 +28,9 @@ import de.danoeh.antennapod.menuhandler.MenuItemUtils; import de.mfietz.fyydlin.FyydClient; import de.mfietz.fyydlin.FyydResponse; import de.mfietz.fyydlin.SearchHit; -import rx.Subscription; -import rx.android.schedulers.AndroidSchedulers; -import rx.schedulers.Schedulers; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; import static de.danoeh.antennapod.adapter.itunes.ItunesAdapter.Podcast; import static java.util.Collections.emptyList; @@ -49,13 +49,13 @@ public class FyydSearchFragment extends Fragment { private Button butRetry; private TextView txtvEmpty; - private FyydClient client = new FyydClient(AntennapodHttpClient.getHttpClient()); + private final FyydClient client = new FyydClient(AntennapodHttpClient.getHttpClient()); /** * List of podcasts retreived from the search */ private List<Podcast> searchResults; - private Subscription subscription; + private Disposable disposable; /** * Constructor @@ -75,7 +75,7 @@ public class FyydSearchFragment extends Fragment { Bundle savedInstanceState) { // Inflate the layout for this fragment View root = inflater.inflate(R.layout.fragment_itunes_search, container, false); - gridView = (GridView) root.findViewById(R.id.gridView); + gridView = root.findViewById(R.id.gridView); adapter = new ItunesAdapter(getActivity(), new ArrayList<>()); gridView.setAdapter(adapter); @@ -87,10 +87,10 @@ public class FyydSearchFragment extends Fragment { intent.putExtra(OnlineFeedViewActivity.ARG_TITLE, podcast.title); startActivity(intent); }); - progressBar = (ProgressBar) root.findViewById(R.id.progressBar); - txtvError = (TextView) root.findViewById(R.id.txtvError); - butRetry = (Button) root.findViewById(R.id.butRetry); - txtvEmpty = (TextView) root.findViewById(android.R.id.empty); + progressBar = root.findViewById(R.id.progressBar); + txtvError = root.findViewById(R.id.txtvError); + butRetry = root.findViewById(R.id.butRetry); + txtvEmpty = root.findViewById(android.R.id.empty); return root; } @@ -98,8 +98,8 @@ public class FyydSearchFragment extends Fragment { @Override public void onDestroy() { super.onDestroy(); - if (subscription != null) { - subscription.unsubscribe(); + if (disposable != null) { + disposable.dispose(); } adapter = null; } @@ -141,12 +141,12 @@ public class FyydSearchFragment extends Fragment { } private void search(String query) { - if (subscription != null) { - subscription.unsubscribe(); + if (disposable != null) { + disposable.dispose(); } showOnlyProgressBar(); - subscription = client.searchPodcasts(query) - .subscribeOn(Schedulers.newThread()) + disposable = client.searchPodcasts(query, 10) + .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(result -> { progressBar.setVisibility(View.GONE); @@ -169,12 +169,12 @@ public class FyydSearchFragment extends Fragment { progressBar.setVisibility(View.VISIBLE); } - void processSearchResult(FyydResponse response) { + private void processSearchResult(FyydResponse response) { adapter.clear(); if (!response.getData().isEmpty()) { adapter.clear(); searchResults = new ArrayList<>(); - for (SearchHit searchHit : response.getData().values()) { + for (SearchHit searchHit : response.getData()) { Podcast podcast = Podcast.fromSearch(searchHit); searchResults.add(podcast); } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java index a0586fe16..4ee7a06ad 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java @@ -10,7 +10,6 @@ import android.content.SharedPreferences; import android.content.res.TypedArray; import android.graphics.Color; import android.net.Uri; -import android.os.Build; import android.os.Bundle; import android.support.v4.app.Fragment; import android.util.Log; @@ -34,15 +33,16 @@ import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.util.Converter; import de.danoeh.antennapod.core.util.IntentUtils; +import de.danoeh.antennapod.core.util.NetworkUtils; import de.danoeh.antennapod.core.util.ShareUtils; import de.danoeh.antennapod.core.util.ShownotesProvider; import de.danoeh.antennapod.core.util.playback.Playable; import de.danoeh.antennapod.core.util.playback.PlaybackController; import de.danoeh.antennapod.core.util.playback.Timeline; -import rx.Observable; -import rx.Subscription; -import rx.android.schedulers.AndroidSchedulers; -import rx.schedulers.Schedulers; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; /** * Displays the description of a Playable object in a Webview. @@ -66,7 +66,7 @@ public class ItemDescriptionFragment extends Fragment implements MediaplayerInfo private ShownotesProvider shownotesProvider; private Playable media; - private Subscription webViewLoader; + private Disposable webViewLoader; /** * URL that was selected via long-press. @@ -112,15 +112,20 @@ public class ItemDescriptionFragment extends Fragment implements MediaplayerInfo Bundle savedInstanceState) { Log.d(TAG, "Creating view"); webvDescription = new WebView(getActivity().getApplicationContext()); - if (Build.VERSION.SDK_INT >= 11) { - webvDescription.setLayerType(View.LAYER_TYPE_SOFTWARE, null); - } + webvDescription.setLayerType(View.LAYER_TYPE_SOFTWARE, null); + TypedArray ta = getActivity().getTheme().obtainStyledAttributes(new int[] {android.R.attr.colorBackground}); - int backgroundColor = ta.getColor(0, UserPreferences.getTheme() == - R.style.Theme_AntennaPod_Dark ? Color.BLACK : Color.WHITE); + boolean black = UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark + || UserPreferences.getTheme() == R.style.Theme_AntennaPod_TrueBlack; + int backgroundColor = ta.getColor(0, black ? Color.BLACK : Color.WHITE); + ta.recycle(); webvDescription.setBackgroundColor(backgroundColor); + if (!NetworkUtils.networkAvailable()) { + webvDescription.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); + // Use cached resources, even if they have expired + } webvDescription.getSettings().setUseWideViewPort(false); webvDescription.getSettings().setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN); webvDescription.getSettings().setLoadWithOverviewMode(true); @@ -162,7 +167,7 @@ public class ItemDescriptionFragment extends Fragment implements MediaplayerInfo super.onDestroy(); Log.d(TAG, "Fragment destroyed"); if (webViewLoader != null) { - webViewLoader.unsubscribe(); + webViewLoader.dispose(); } if (webvDescription != null) { webvDescription.removeAllViews(); @@ -193,7 +198,7 @@ public class ItemDescriptionFragment extends Fragment implements MediaplayerInfo } else if (args.containsKey(ARG_FEEDITEM_ID)) { long id = getArguments().getLong(ARG_FEEDITEM_ID); Observable.defer(() -> Observable.just(DBReader.getFeedItem(id))) - .subscribeOn(Schedulers.newThread()) + .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(feedItem -> { shownotesProvider = feedItem; @@ -203,7 +208,7 @@ public class ItemDescriptionFragment extends Fragment implements MediaplayerInfo } - private View.OnLongClickListener webViewLongClickListener = new View.OnLongClickListener() { + private final View.OnLongClickListener webViewLongClickListener = new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { @@ -238,17 +243,11 @@ public class ItemDescriptionFragment extends Fragment implements MediaplayerInfo 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); - } + ClipData clipData = ClipData.newPlainText(selectedURL, + selectedURL); + android.content.ClipboardManager cm = (android.content.ClipboardManager) getActivity() + .getSystemService(Context.CLIPBOARD_SERVICE); + cm.setPrimaryClip(clipData); Toast t = Toast.makeText(getActivity(), R.string.copied_url_msg, Toast.LENGTH_SHORT); t.show(); @@ -299,13 +298,13 @@ public class ItemDescriptionFragment extends Fragment implements MediaplayerInfo private void load() { Log.d(TAG, "load()"); if(webViewLoader != null) { - webViewLoader.unsubscribe(); + webViewLoader.dispose(); } if(shownotesProvider == null) { return; } - webViewLoader = Observable.defer(() -> Observable.just(loadData())) - .subscribeOn(Schedulers.newThread()) + webViewLoader = Observable.fromCallable(this::loadData) + .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(data -> { webvDescription.loadDataWithBaseURL(null, data, "text/html", diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java index 7939dcb23..bcca281d4 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java @@ -31,6 +31,7 @@ import android.widget.TextView; import android.widget.Toast; import com.bumptech.glide.Glide; +import com.bumptech.glide.request.RequestOptions; import com.joanzapata.iconify.Iconify; import com.joanzapata.iconify.widget.IconButton; @@ -51,26 +52,27 @@ 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.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.storage.DownloadRequestException; import de.danoeh.antennapod.core.storage.DownloadRequester; import de.danoeh.antennapod.core.util.Converter; import de.danoeh.antennapod.core.util.DateUtils; import de.danoeh.antennapod.core.util.Flavors; import de.danoeh.antennapod.core.util.IntentUtils; import de.danoeh.antennapod.core.util.LongList; +import de.danoeh.antennapod.core.util.NetworkUtils; 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; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; /** * Displays information about a FeedItem and actions. @@ -133,7 +135,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture { private IconButton butAction2; private Menu popupMenu; - private Subscription subscription; + private Disposable disposable; /** * URL that was selected via long-press. @@ -164,32 +166,36 @@ public class ItemFragment extends Fragment implements OnSwipeGesture { super.onCreateView(inflater, container, savedInstanceState); View layout = inflater.inflate(R.layout.feeditem_fragment, container, false); - root = (ViewGroup) layout.findViewById(R.id.content_root); + root = layout.findViewById(R.id.content_root); - LinearLayout header = (LinearLayout) root.findViewById(R.id.header); + LinearLayout header = root.findViewById(R.id.header); if(feedItems.length > 0) { header.setOnTouchListener((v, event) -> headerGestureDetector.onTouchEvent(event)); } - txtvPodcast = (TextView) layout.findViewById(R.id.txtvPodcast); + txtvPodcast = layout.findViewById(R.id.txtvPodcast); txtvPodcast.setOnClickListener(v -> openPodcast()); - txtvTitle = (TextView) layout.findViewById(R.id.txtvTitle); + txtvTitle = 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); + txtvDuration = layout.findViewById(R.id.txtvDuration); + txtvPublished = 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 = layout.findViewById(R.id.webvDescription); + if (UserPreferences.getTheme() == R.style.Theme_AntennaPod_Dark || + UserPreferences.getTheme() == R.style.Theme_AntennaPod_TrueBlack) { + if (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)); } + if (!NetworkUtils.networkAvailable()) { + webvDescription.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); + // Use cached resources, even if they have expired + } webvDescription.getSettings().setUseWideViewPort(false); webvDescription.getSettings().setLayoutAlgorithm( WebSettings.LayoutAlgorithm.NARROW_COLUMNS); @@ -210,12 +216,12 @@ public class ItemFragment extends Fragment implements OnSwipeGesture { }); registerForContextMenu(webvDescription); - imgvCover = (ImageView) layout.findViewById(R.id.imgvCover); + imgvCover = 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); + progbarDownload = layout.findViewById(R.id.progbarDownload); + progbarLoading = layout.findViewById(R.id.progbarLoading); + butAction1 = layout.findViewById(R.id.butAction1); + butAction2 = layout.findViewById(R.id.butAction2); butAction1.setOnClickListener(v -> { if (item == null) { @@ -280,8 +286,8 @@ public class ItemFragment extends Fragment implements OnSwipeGesture { @Override public void onDestroyView() { super.onDestroyView(); - if(subscription != null) { - subscription.unsubscribe(); + if(disposable != null) { + disposable.dispose(); } if (webvDescription != null && root != null) { root.removeView(webvDescription); @@ -335,13 +341,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture { 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; - } + return FeedItemMenuHandler.onMenuItemClicked(getActivity(), menuItem.getItemId(), item); } } @@ -379,11 +379,12 @@ public class ItemFragment extends Fragment implements OnSwipeGesture { Glide.with(getActivity()) .load(item.getImageLocation()) - .placeholder(R.color.light_gray) - .error(R.color.light_gray) - .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) - .fitCenter() - .dontAnimate() + .apply(new RequestOptions() + .placeholder(R.color.light_gray) + .error(R.color.light_gray) + .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) + .fitCenter() + .dontAnimate()) .into(imgvCover); progbarDownload.setVisibility(View.GONE); @@ -434,6 +435,16 @@ public class ItemFragment extends Fragment implements OnSwipeGesture { butAction1Text = R.string.download_label; } } + + FeedItem.State state = item.getState(); + if (butAction2Text == R.string.delete_label && state == FeedItem.State.PLAYING && PlaybackService.isRunning) { + butAction2.setEnabled(false); + butAction2.setAlpha(0.5f); + } else { + butAction2.setEnabled(true); + butAction2.setAlpha(1.0f); + } + if(butAction1Icon != null && butAction1Text != 0) { butAction1.setText(butAction1Icon +"\u0020\u0020" + getActivity().getString(butAction1Text)); Iconify.addIcons(butAction1); @@ -450,7 +461,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture { } } - private View.OnLongClickListener webViewLongClickListener = new View.OnLongClickListener() { + private final View.OnLongClickListener webViewLongClickListener = new View.OnLongClickListener() { @Override public boolean onLongClick(View v) { @@ -483,17 +494,11 @@ public class ItemFragment extends Fragment implements OnSwipeGesture { 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); - } + ClipData clipData = ClipData.newPlainText(selectedURL, + selectedURL); + android.content.ClipboardManager cm = (android.content.ClipboardManager) getActivity() + .getSystemService(Context.CLIPBOARD_SERVICE); + cm.setPrimaryClip(clipData); Toast t = Toast.makeText(getActivity(), R.string.copied_url_msg, Toast.LENGTH_SHORT); t.show(); @@ -558,7 +563,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture { } - private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { + private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { @Override public void update(EventDistributor eventDistributor, Integer arg) { if ((arg & EVENTS) != 0) { @@ -568,12 +573,12 @@ public class ItemFragment extends Fragment implements OnSwipeGesture { }; private void load() { - if(subscription != null) { - subscription.unsubscribe(); + if(disposable != null) { + disposable.dispose(); } progbarLoading.setVisibility(View.VISIBLE); - subscription = Observable.fromCallable(this::loadInBackground) - .subscribeOn(Schedulers.newThread()) + disposable = Observable.fromCallable(this::loadInBackground) + .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(result -> { progbarLoading.setVisibility(View.GONE); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java index a118673a6..d9e318069 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java @@ -4,10 +4,7 @@ import android.annotation.SuppressLint; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; -import android.content.res.TypedArray; -import android.graphics.Color; import android.graphics.LightingColorFilter; -import android.os.Build; import android.os.Bundle; import android.support.v4.app.ListFragment; import android.support.v4.view.MenuItemCompat; @@ -22,15 +19,14 @@ import android.view.View; import android.widget.AdapterView; import android.widget.ImageButton; import android.widget.ImageView; -import android.widget.ListAdapter; import android.widget.ListView; import android.widget.RelativeLayout; import android.widget.TextView; import com.bumptech.glide.Glide; +import com.bumptech.glide.request.RequestOptions; import com.joanzapata.iconify.IconDrawable; import com.joanzapata.iconify.Iconify; -import com.joanzapata.iconify.fonts.FontAwesomeIcons; import com.joanzapata.iconify.widget.IconTextView; import org.apache.commons.lang3.Validate; @@ -39,6 +35,7 @@ import java.util.List; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.FeedInfoActivity; +import de.danoeh.antennapod.activity.FeedSettingsActivity; import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.adapter.DefaultActionButtonCallback; import de.danoeh.antennapod.adapter.FeedItemlistAdapter; @@ -56,7 +53,6 @@ import de.danoeh.antennapod.core.feed.FeedItemFilter; import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.glide.ApGlideSettings; import de.danoeh.antennapod.core.glide.FastBlurTransformation; -import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.service.download.DownloadService; import de.danoeh.antennapod.core.service.download.Downloader; import de.danoeh.antennapod.core.storage.DBReader; @@ -72,10 +68,10 @@ import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler; import de.danoeh.antennapod.menuhandler.FeedMenuHandler; import de.danoeh.antennapod.menuhandler.MenuItemUtils; import de.greenrobot.event.EventBus; -import rx.Observable; -import rx.Subscription; -import rx.android.schedulers.AndroidSchedulers; -import rx.schedulers.Schedulers; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; /** * Displays a list of FeedItems. @@ -89,9 +85,9 @@ public class ItemlistFragment extends ListFragment { | EventDistributor.PLAYER_STATUS_UPDATE; public static final String EXTRA_SELECTED_FEEDITEM = "extra.de.danoeh.antennapod.activity.selected_feeditem"; - public static final String ARGUMENT_FEED_ID = "argument.de.danoeh.antennapod.feed_id"; + private static final String ARGUMENT_FEED_ID = "argument.de.danoeh.antennapod.feed_id"; - protected FeedItemlistAdapter adapter; + private FeedItemlistAdapter adapter; private ContextMenu contextMenu; private AdapterView.AdapterContextMenuInfo lastMenuInfo = null; @@ -115,7 +111,7 @@ public class ItemlistFragment extends ListFragment { private TextView txtvInformation; - private Subscription subscription; + private Disposable disposable; /** * Creates new ItemlistFragment which shows the Feeditems of a specific @@ -166,8 +162,8 @@ public class ItemlistFragment extends ListFragment { super.onPause(); EventDistributor.getInstance().unregister(contentUpdate); EventBus.getDefault().unregister(this); - if(subscription != null) { - subscription.unsubscribe(); + if(disposable != null) { + disposable.dispose(); } } @@ -223,13 +219,6 @@ public class ItemlistFragment extends ListFragment { menu.findItem(R.id.share_link_item).setVisible(false); menu.findItem(R.id.visit_website_item).setVisible(false); } - int[] attrs = { R.attr.action_bar_icon_color }; - TypedArray ta = getActivity().obtainStyledAttributes(UserPreferences.getTheme(), attrs); - int textColor = ta.getColor(0, Color.GRAY); - ta.recycle(); - - menu.findItem(R.id.episode_actions).setIcon(new IconDrawable(getActivity(), - FontAwesomeIcons.fa_gears).color(textColor).actionBarSize()); isUpdatingFeed = MenuItemUtils.updateRefreshMenuItem(menu, R.id.refresh_item, updateRefreshMenuItemChecker); } @@ -342,22 +331,7 @@ public class ItemlistFragment extends ListFragment { return super.onContextItemSelected(item); } - try { - return FeedItemMenuHandler.onMenuItemClicked(getActivity(), item.getItemId(), selectedItem); - } catch (DownloadRequestException e) { - // context menu doesn't contain download functionality - return true; - } - } - - - @Override - public void setListAdapter(ListAdapter adapter) { - // This workaround prevents the ListFragment from setting a list adapter when its state is restored. - // This is only necessary on API 10 because addFooterView throws an internal exception in this case. - if (Build.VERSION.SDK_INT > 10 || insideOnFragmentLoaded) { - super.setListAdapter(adapter); - } + return FeedItemMenuHandler.onMenuItemClicked(getActivity(), item.getItemId(), selectedItem); } @Override @@ -417,7 +391,7 @@ public class ItemlistFragment extends ListFragment { } } - private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { + private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { @Override public void update(EventDistributor eventDistributor, Integer arg) { @@ -440,13 +414,10 @@ public class ItemlistFragment extends ListFragment { } - private boolean insideOnFragmentLoaded = false; - private void onFragmentLoaded() { if(!isVisible()) { return; } - insideOnFragmentLoaded = true; if (adapter == null) { setListAdapter(null); setupHeaderView(); @@ -463,9 +434,6 @@ public class ItemlistFragment extends ListFragment { if (feed != null && feed.getNextPageLink() == null && listFooter != null) { getListView().removeFooterView(listFooter.getRoot()); } - - insideOnFragmentLoaded = false; - } private void refreshHeaderView() { @@ -509,13 +477,14 @@ public class ItemlistFragment extends ListFragment { View header = inflater.inflate(R.layout.feeditemlist_header, lv, false); lv.addHeaderView(header); - txtvTitle = (TextView) header.findViewById(R.id.txtvTitle); - TextView txtvAuthor = (TextView) header.findViewById(R.id.txtvAuthor); - imgvBackground = (ImageView) header.findViewById(R.id.imgvBackground); - imgvCover = (ImageView) header.findViewById(R.id.imgvCover); - ImageButton butShowInfo = (ImageButton) header.findViewById(R.id.butShowInfo); - txtvInformation = (TextView) header.findViewById(R.id.txtvInformation); - txtvFailure = (IconTextView) header.findViewById(R.id.txtvFailure); + txtvTitle = header.findViewById(R.id.txtvTitle); + TextView txtvAuthor = header.findViewById(R.id.txtvAuthor); + imgvBackground = header.findViewById(R.id.imgvBackground); + imgvCover = header.findViewById(R.id.imgvCover); + ImageButton butShowInfo = header.findViewById(R.id.butShowInfo); + ImageButton butShowSettings = header.findViewById(R.id.butShowSettings); + txtvInformation = header.findViewById(R.id.txtvInformation); + txtvFailure = header.findViewById(R.id.txtvFailure); txtvTitle.setText(feed.getTitle()); txtvAuthor.setText(feed.getAuthor()); @@ -526,10 +495,12 @@ public class ItemlistFragment extends ListFragment { loadFeedImage(); - butShowInfo.setOnClickListener(v -> { + butShowInfo.setOnClickListener(v -> showFeedInfo()); + imgvCover.setOnClickListener(v -> showFeedInfo()); + butShowSettings.setOnClickListener(v -> { if (viewsCreated && itemsLoaded) { - Intent startIntent = new Intent(getActivity(), FeedInfoActivity.class); - startIntent.putExtra(FeedInfoActivity.EXTRA_FEED_ID, + Intent startIntent = new Intent(getActivity(), FeedSettingsActivity.class); + startIntent.putExtra(FeedSettingsActivity.EXTRA_FEED_ID, feed.getId()); startActivity(startIntent); } @@ -537,23 +508,34 @@ public class ItemlistFragment extends ListFragment { headerCreated = true; } + private void showFeedInfo() { + if (viewsCreated && itemsLoaded) { + Intent startIntent = new Intent(getActivity(), FeedInfoActivity.class); + startIntent.putExtra(FeedInfoActivity.EXTRA_FEED_ID, + feed.getId()); + startActivity(startIntent); + } + } + private void loadFeedImage() { Glide.with(getActivity()) .load(feed.getImageLocation()) - .placeholder(R.color.image_readability_tint) - .error(R.color.image_readability_tint) - .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) - .transform(new FastBlurTransformation(getActivity())) - .dontAnimate() + .apply(new RequestOptions() + .placeholder(R.color.image_readability_tint) + .error(R.color.image_readability_tint) + .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) + .transform(new FastBlurTransformation()) + .dontAnimate()) .into(imgvBackground); Glide.with(getActivity()) .load(feed.getImageLocation()) - .placeholder(R.color.light_gray) - .error(R.color.light_gray) - .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) - .fitCenter() - .dontAnimate() + .apply(new RequestOptions() + .placeholder(R.color.light_gray) + .error(R.color.light_gray) + .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) + .fitCenter() + .dontAnimate()) .into(imgvCover); } @@ -583,7 +565,7 @@ public class ItemlistFragment extends ListFragment { } } - private FeedItemlistAdapter.ItemAccess itemAccess = new FeedItemlistAdapter.ItemAccess() { + private final FeedItemlistAdapter.ItemAccess itemAccess = new FeedItemlistAdapter.ItemAccess() { @Override public FeedItem getItem(int position) { @@ -629,11 +611,11 @@ public class ItemlistFragment extends ListFragment { private void loadItems() { - if(subscription != null) { - subscription.unsubscribe(); + if(disposable != null) { + disposable.dispose(); } - subscription = Observable.fromCallable(this::loadData) - .subscribeOn(Schedulers.newThread()) + disposable = Observable.fromCallable(this::loadData) + .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(result -> { if (result != null) { diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java index 43dedad25..a0e2ca22a 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java @@ -36,13 +36,14 @@ import de.danoeh.antennapod.adapter.itunes.ItunesAdapter; import de.danoeh.antennapod.core.ClientConfig; import de.danoeh.antennapod.core.service.download.AntennapodHttpClient; import de.danoeh.antennapod.menuhandler.MenuItemUtils; +import io.reactivex.Single; +import io.reactivex.SingleOnSubscribe; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; import okhttp3.OkHttpClient; import okhttp3.Request; import okhttp3.Response; -import rx.Observable; -import rx.Subscription; -import rx.android.schedulers.AndroidSchedulers; -import rx.schedulers.Schedulers; import static de.danoeh.antennapod.adapter.itunes.ItunesAdapter.Podcast; @@ -69,13 +70,13 @@ public class ItunesSearchFragment extends Fragment { */ private List<Podcast> searchResults; private List<Podcast> topList; - private Subscription subscription; + private Disposable disposable; /** * Replace adapter data with provided search results from SearchTask. * @param result List of Podcast objects containing search results */ - void updateData(List<Podcast> result) { + private void updateData(List<Podcast> result) { this.searchResults = result; adapter.clear(); if (result != null && result.size() > 0) { @@ -109,7 +110,7 @@ public class ItunesSearchFragment extends Fragment { Bundle savedInstanceState) { // Inflate the layout for this fragment View root = inflater.inflate(R.layout.fragment_itunes_search, container, false); - gridView = (GridView) root.findViewById(R.id.gridView); + gridView = root.findViewById(R.id.gridView); adapter = new ItunesAdapter(getActivity(), new ArrayList<>()); gridView.setAdapter(adapter); @@ -127,7 +128,7 @@ public class ItunesSearchFragment extends Fragment { } else { gridView.setVisibility(View.GONE); progressBar.setVisibility(View.VISIBLE); - subscription = Observable.create((Observable.OnSubscribe<String>) subscriber -> { + disposable = Single.create((SingleOnSubscribe<String>) emitter -> { OkHttpClient client = AntennapodHttpClient.getHttpClient(); Request.Builder httpReq = new Request.Builder() .url(podcast.feedUrl) @@ -139,17 +140,16 @@ public class ItunesSearchFragment extends Fragment { JSONObject result = new JSONObject(resultString); JSONObject results = result.getJSONArray("results").getJSONObject(0); String feedUrl = results.getString("feedUrl"); - subscriber.onNext(feedUrl); + emitter.onSuccess(feedUrl); } else { String prefix = getString(R.string.error_msg_prefix); - subscriber.onError(new IOException(prefix + response)); + emitter.onError(new IOException(prefix + response)); } } catch (IOException | JSONException e) { - subscriber.onError(e); + emitter.onError(e); } - subscriber.onCompleted(); }) - .subscribeOn(Schedulers.newThread()) + .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(feedUrl -> { progressBar.setVisibility(View.GONE); @@ -170,10 +170,10 @@ public class ItunesSearchFragment extends Fragment { }); } }); - progressBar = (ProgressBar) root.findViewById(R.id.progressBar); - txtvError = (TextView) root.findViewById(R.id.txtvError); - butRetry = (Button) root.findViewById(R.id.butRetry); - txtvEmpty = (TextView) root.findViewById(android.R.id.empty); + progressBar = root.findViewById(R.id.progressBar); + txtvError = root.findViewById(R.id.txtvError); + butRetry = root.findViewById(R.id.butRetry); + txtvEmpty = root.findViewById(android.R.id.empty); loadToplist(); @@ -183,8 +183,8 @@ public class ItunesSearchFragment extends Fragment { @Override public void onDestroy() { super.onDestroy(); - if (subscription != null) { - subscription.unsubscribe(); + if (disposable != null) { + disposable.dispose(); } adapter = null; } @@ -228,15 +228,15 @@ public class ItunesSearchFragment extends Fragment { } private void loadToplist() { - if (subscription != null) { - subscription.unsubscribe(); + if (disposable != null) { + disposable.dispose(); } gridView.setVisibility(View.GONE); txtvError.setVisibility(View.GONE); butRetry.setVisibility(View.GONE); txtvEmpty.setVisibility(View.GONE); progressBar.setVisibility(View.VISIBLE); - subscription = Observable.create((Observable.OnSubscribe<List<Podcast>>) subscriber -> { + disposable = Single.create((SingleOnSubscribe<List<Podcast>>) emitter -> { String lang = Locale.getDefault().getLanguage(); String url = "https://itunes.apple.com/" + lang + "/rss/toppodcasts/limit=25/explicit=true/json"; OkHttpClient client = AntennapodHttpClient.getHttpClient(); @@ -268,15 +268,14 @@ public class ItunesSearchFragment extends Fragment { } else { String prefix = getString(R.string.error_msg_prefix); - subscriber.onError(new IOException(prefix + response)); + emitter.onError(new IOException(prefix + response)); } } catch (IOException | JSONException e) { - subscriber.onError(e); + emitter.onError(e); } - subscriber.onNext(results); - subscriber.onCompleted(); + emitter.onSuccess(results); }) - .subscribeOn(Schedulers.newThread()) + .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(podcasts -> { progressBar.setVisibility(View.GONE); @@ -293,15 +292,15 @@ public class ItunesSearchFragment extends Fragment { } private void search(String query) { - if (subscription != null) { - subscription.unsubscribe(); + if (disposable != null) { + disposable.dispose(); } gridView.setVisibility(View.GONE); txtvError.setVisibility(View.GONE); butRetry.setVisibility(View.GONE); txtvEmpty.setVisibility(View.GONE); progressBar.setVisibility(View.VISIBLE); - subscription = rx.Observable.create((Observable.OnSubscribe<List<Podcast>>) subscriber -> { + disposable = Single.create((SingleOnSubscribe<List<Podcast>>) subscriber -> { String encodedQuery = null; try { encodedQuery = URLEncoder.encode(query, "UTF-8"); @@ -341,10 +340,9 @@ public class ItunesSearchFragment extends Fragment { } catch (IOException | JSONException e) { subscriber.onError(e); } - subscriber.onNext(podcasts); - subscriber.onCompleted(); + subscriber.onSuccess(podcasts); }) - .subscribeOn(Schedulers.newThread()) + .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(podcasts -> { progressBar.setVisibility(View.GONE); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java index 183c10f3d..6695ba427 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java @@ -1,14 +1,10 @@ package de.danoeh.antennapod.fragment; import android.os.Bundle; -import android.os.Handler; -import android.support.design.widget.Snackbar; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.helper.ItemTouchHelper; import android.util.Log; import android.view.LayoutInflater; -import android.view.Menu; -import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; @@ -18,10 +14,7 @@ import de.danoeh.antennapod.R; import de.danoeh.antennapod.adapter.AllEpisodesRecycleAdapter; import de.danoeh.antennapod.core.event.FeedItemEvent; 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.storage.DBReader; -import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.util.FeedItemUtil; @@ -76,33 +69,7 @@ public class NewEpisodesFragment extends AllEpisodesFragment { @Override public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) { AllEpisodesRecycleAdapter.Holder holder = (AllEpisodesRecycleAdapter.Holder)viewHolder; - - Log.d(TAG, "remove(" + holder.getItemId() + ")"); - if (subscription != null) { - subscription.unsubscribe(); - } - FeedItem item = holder.getFeedItem(); - // we're marking it as unplayed since the user didn't actually play it - // but they don't want it considered 'NEW' anymore - DBWriter.markItemPlayed(FeedItem.UNPLAYED, item.getId()); - - final Handler h = new Handler(getActivity().getMainLooper()); - final Runnable r = () -> { - FeedMedia media = item.getMedia(); - if (media != null && media.hasAlmostEnded() && UserPreferences.isAutoDelete()) { - DBWriter.deleteFeedMediaOfItem(getActivity(), media.getId()); - } - }; - - Snackbar snackbar = Snackbar.make(root, getString(R.string.marked_as_seen_label), - Snackbar.LENGTH_LONG); - snackbar.setAction(getString(R.string.undo), v -> { - DBWriter.markItemPlayed(FeedItem.NEW, item.getId()); - // don't forget to cancel the thing that's going to remove the media - h.removeCallbacks(r); - }); - snackbar.show(); - h.postDelayed(r, (int)Math.ceil(snackbar.getDuration() * 1.05f)); + markItemAsSeenWithUndo(holder.getFeedItem()); } @Override diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java index 441f0096c..c2a9200c8 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java @@ -30,10 +30,10 @@ import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.util.FeedItemUtil; import de.danoeh.antennapod.core.util.LongList; import de.greenrobot.event.EventBus; -import rx.Observable; -import rx.Subscription; -import rx.android.schedulers.AndroidSchedulers; -import rx.schedulers.Schedulers; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; public class PlaybackHistoryFragment extends ListFragment { @@ -50,7 +50,7 @@ public class PlaybackHistoryFragment extends ListFragment { private List<Downloader> downloaderList; - private Subscription subscription; + private Disposable disposable; @Override public void onAttach(Context context) { @@ -107,16 +107,16 @@ public class PlaybackHistoryFragment extends ListFragment { public void onStop() { super.onStop(); EventDistributor.getInstance().unregister(contentUpdate); - if(subscription != null) { - subscription.unsubscribe(); + if(disposable != null) { + disposable.dispose(); } } @Override public void onDetach() { super.onDetach(); - if(subscription != null) { - subscription.unsubscribe(); + if(disposable != null) { + disposable.dispose(); } } @@ -199,7 +199,7 @@ public class PlaybackHistoryFragment extends ListFragment { } } - private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { + private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { @Override public void update(EventDistributor eventDistributor, Integer arg) { @@ -224,7 +224,7 @@ public class PlaybackHistoryFragment extends ListFragment { getActivity().supportInvalidateOptionsMenu(); } - private FeedItemlistAdapter.ItemAccess itemAccess = new FeedItemlistAdapter.ItemAccess() { + private final FeedItemlistAdapter.ItemAccess itemAccess = new FeedItemlistAdapter.ItemAccess() { @Override public int getItemDownloadProgressPercent(FeedItem item) { @@ -269,11 +269,11 @@ public class PlaybackHistoryFragment extends ListFragment { }; private void loadItems() { - if(subscription != null) { - subscription.unsubscribe(); + if(disposable != null) { + disposable.dispose(); } - subscription = Observable.fromCallable(this::loadData) - .subscribeOn(Schedulers.newThread()) + disposable = Observable.fromCallable(this::loadData) + .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(result -> { if (result != null) { diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java index fccb86076..faeabf75c 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java @@ -21,7 +21,6 @@ import android.view.View; import android.view.ViewGroup; import android.widget.ProgressBar; import android.widget.TextView; -import android.widget.Toast; import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration; @@ -46,7 +45,6 @@ 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.FeedItemUtil; @@ -55,10 +53,10 @@ import de.danoeh.antennapod.core.util.QueueSorter; import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler; import de.danoeh.antennapod.menuhandler.MenuItemUtils; import de.greenrobot.event.EventBus; -import rx.Observable; -import rx.Subscription; -import rx.android.schedulers.AndroidSchedulers; -import rx.schedulers.Schedulers; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; /** * Shows all items in the queue @@ -86,7 +84,7 @@ public class QueueFragment extends Fragment { private static final String PREF_SCROLL_POSITION = "scroll_position"; private static final String PREF_SCROLL_OFFSET = "scroll_offset"; - private Subscription subscription; + private Disposable disposable; private LinearLayoutManager layoutManager; private ItemTouchHelper itemTouchHelper; @@ -109,7 +107,6 @@ public class QueueFragment extends Fragment { @Override public void onResume() { super.onResume(); - recyclerView.setAdapter(recyclerAdapter); loadItems(true); EventDistributor.getInstance().register(contentUpdate); EventBus.getDefault().registerSticky(this); @@ -121,8 +118,8 @@ public class QueueFragment extends Fragment { saveScrollPosition(); EventDistributor.getInstance().unregister(contentUpdate); EventBus.getDefault().unregister(this); - if(subscription != null) { - subscription.unsubscribe(); + if(disposable != null) { + disposable.dispose(); } } @@ -328,6 +325,15 @@ public class QueueFragment extends Fragment { case R.id.queue_sort_feed_title_desc: QueueSorter.sort(getActivity(), QueueSorter.Rule.FEED_TITLE_DESC, true); return true; + case R.id.queue_sort_random: + QueueSorter.sort(getActivity(), QueueSorter.Rule.RANDOM, true); + return true; + case R.id.queue_sort_smart_shuffle_asc: + QueueSorter.sort(getActivity(), QueueSorter.Rule.SMART_SHUFFLE_ASC, true); + return true; + case R.id.queue_sort_smart_shuffle_desc: + QueueSorter.sort(getActivity(), QueueSorter.Rule.SMART_SHUFFLE_DESC, true); + return true; default: return false; } @@ -363,13 +369,7 @@ public class QueueFragment extends Fragment { DBWriter.moveQueueItemToBottom(selectedItem.getId(), true); return true; default: - try { - return FeedItemMenuHandler.onMenuItemClicked(getActivity(), item.getItemId(), selectedItem); - } catch (DownloadRequestException e) { - e.printStackTrace(); - Toast.makeText(getActivity(), e.getMessage(), Toast.LENGTH_LONG).show(); - return true; - } + return FeedItemMenuHandler.onMenuItemClicked(getActivity(), item.getItemId(), selectedItem); } } @@ -380,8 +380,8 @@ public class QueueFragment extends Fragment { ((MainActivity) getActivity()).getSupportActionBar().setTitle(R.string.queue_label); View root = inflater.inflate(R.layout.queue_fragment, container, false); - infoBar = (TextView) root.findViewById(R.id.info_bar); - recyclerView = (RecyclerView) root.findViewById(R.id.recyclerView); + infoBar = root.findViewById(R.id.info_bar); + recyclerView = root.findViewById(R.id.recyclerView); RecyclerView.ItemAnimator animator = recyclerView.getItemAnimator(); if (animator instanceof SimpleItemAnimator) { ((SimpleItemAnimator) animator).setSupportsChangeAnimations(false); @@ -395,24 +395,36 @@ public class QueueFragment extends Fragment { itemTouchHelper = new ItemTouchHelper( new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN, ItemTouchHelper.RIGHT) { + // Position tracking whilst dragging + int dragFrom = -1; + int dragTo = -1; + @Override public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) { + int fromPosition = viewHolder.getAdapterPosition(); + int toPosition = target.getAdapterPosition(); + + // Update tracked position + if(dragFrom == -1) { + dragFrom = fromPosition; + } + dragTo = toPosition; + int from = viewHolder.getAdapterPosition(); int to = target.getAdapterPosition(); - Log.d(TAG, "move(" + from + ", " + to + ")"); + Log.d(TAG, "move(" + from + ", " + to + ") in memory"); if(from >= queue.size() || to >= queue.size()) { return false; } queue.add(to, queue.remove(from)); recyclerAdapter.notifyItemMoved(from, to); - DBWriter.moveQueueItem(from, to, true); return true; } @Override public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) { - if(subscription != null) { - subscription.unsubscribe(); + if(disposable != null) { + disposable.dispose(); } final int position = viewHolder.getAdapterPosition(); Log.d(TAG, "remove(" + position + ")"); @@ -459,19 +471,32 @@ public class QueueFragment extends Fragment { RecyclerView.ViewHolder viewHolder) { super.clearView(recyclerView, viewHolder); + // Check if drag finished + if(dragFrom != -1 && dragTo != -1 && dragFrom != dragTo) { + reallyMoved(dragFrom, dragTo); + } + + dragFrom = dragTo = -1; + if (viewHolder instanceof QueueRecyclerAdapter.ItemTouchHelperViewHolder) { QueueRecyclerAdapter.ItemTouchHelperViewHolder itemViewHolder = (QueueRecyclerAdapter.ItemTouchHelperViewHolder) viewHolder; itemViewHolder.onItemClear(); } } + + private void reallyMoved(int from, int to) { + // Write drag operation to database + Log.d(TAG, "Write to database move(" + from + ", " + to + ")"); + DBWriter.moveQueueItem(from, to, true); + } } ); itemTouchHelper.attachToRecyclerView(recyclerView); - txtvEmpty = (TextView) root.findViewById(android.R.id.empty); + txtvEmpty = root.findViewById(android.R.id.empty); txtvEmpty.setVisibility(View.GONE); - progLoading = (ProgressBar) root.findViewById(R.id.progLoading); + progLoading = root.findViewById(R.id.progLoading); progLoading.setVisibility(View.VISIBLE); return root; @@ -507,19 +532,23 @@ public class QueueFragment extends Fragment { private void refreshInfoBar() { String info = queue.size() + getString(R.string.episodes_suffix); if(queue.size() > 0) { - long duration = 0; + long timeLeft = 0; + float playbackSpeed = Float.valueOf(UserPreferences.getPlaybackSpeed()); for(FeedItem item : queue) { if(item.getMedia() != null) { - duration += item.getMedia().getDuration(); + timeLeft += + (long) ((item.getMedia().getDuration() - item.getMedia().getPosition()) + / playbackSpeed); } } info += " \u2022 "; - info += Converter.getDurationStringLocalized(getActivity(), duration); + info += getString(R.string.time_left_label); + info += Converter.getDurationStringLocalized(getActivity(), timeLeft); } infoBar.setText(info); } - private QueueRecyclerAdapter.ItemAccess itemAccess = new QueueRecyclerAdapter.ItemAccess() { + private final QueueRecyclerAdapter.ItemAccess itemAccess = new QueueRecyclerAdapter.ItemAccess() { @Override public int getCount() { return queue != null ? queue.size() : 0; @@ -579,7 +608,7 @@ public class QueueFragment extends Fragment { } }; - private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { + private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { @Override public void update(EventDistributor eventDistributor, Integer arg) { if ((arg & EVENTS) != 0) { @@ -594,16 +623,16 @@ public class QueueFragment extends Fragment { private void loadItems(final boolean restoreScrollPosition) { Log.d(TAG, "loadItems()"); - if(subscription != null) { - subscription.unsubscribe(); + if(disposable != null) { + disposable.dispose(); } if (queue == null) { recyclerView.setVisibility(View.GONE); txtvEmpty.setVisibility(View.GONE); progLoading.setVisibility(View.VISIBLE); } - subscription = Observable.fromCallable(DBReader::getQueue) - .subscribeOn(Schedulers.newThread()) + disposable = Observable.fromCallable(DBReader::getQueue) + .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(items -> { if(items != null) { diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java index ba526edb3..66c59b7f7 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java @@ -75,7 +75,7 @@ public class RunningDownloadsFragment extends ListFragment { } - private DownloadlistAdapter.ItemAccess itemAccess = new DownloadlistAdapter.ItemAccess() { + private final DownloadlistAdapter.ItemAccess itemAccess = new DownloadlistAdapter.ItemAccess() { @Override public int getCount() { return (downloaderList != null) ? downloaderList.size() : 0; diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java index f64b4c20a..8322a5573 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java @@ -24,10 +24,10 @@ import de.danoeh.antennapod.core.feed.FeedComponent; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.SearchResult; import de.danoeh.antennapod.core.storage.FeedSearcher; -import rx.Observable; -import rx.Subscription; -import rx.android.schedulers.AndroidSchedulers; -import rx.schedulers.Schedulers; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; /** * Performs a search operation on all feeds or one specific feed and displays the search result. @@ -44,7 +44,7 @@ public class SearchFragment extends ListFragment { private boolean viewCreated = false; private boolean itemsLoaded = false; - private Subscription subscription; + private Disposable disposable; /** * Create a new SearchFragment that searches all feeds. @@ -85,8 +85,8 @@ public class SearchFragment extends ListFragment { @Override public void onStop() { super.onStop(); - if(subscription != null) { - subscription.unsubscribe(); + if(disposable != null) { + disposable.dispose(); } EventDistributor.getInstance().unregister(contentUpdate); } @@ -94,8 +94,8 @@ public class SearchFragment extends ListFragment { @Override public void onDetach() { super.onDetach(); - if(subscription != null) { - subscription.unsubscribe(); + if(disposable != null) { + disposable.dispose(); } } @@ -205,14 +205,14 @@ public class SearchFragment extends ListFragment { private void search() { - if(subscription != null) { - subscription.unsubscribe(); + if(disposable != null) { + disposable.dispose(); } if (viewCreated && !itemsLoaded) { setListShown(false); } - subscription = Observable.fromCallable(this::performSearch) - .subscribeOn(Schedulers.newThread()) + disposable = Observable.fromCallable(this::performSearch) + .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(result -> { if (result != null) { diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java index 9626e6c2e..5f09be8ce 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java @@ -1,7 +1,6 @@ package de.danoeh.antennapod.fragment; import android.content.DialogInterface; -import android.content.Intent; import android.os.Bundle; import android.support.v4.app.Fragment; import android.util.Log; @@ -26,11 +25,12 @@ import de.danoeh.antennapod.core.service.playback.PlaybackService; import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.util.FeedItemUtil; +import de.danoeh.antennapod.core.util.IntentUtils; import de.danoeh.antennapod.dialog.RenameFeedDialog; -import rx.Observable; -import rx.Subscription; -import rx.android.schedulers.AndroidSchedulers; -import rx.schedulers.Schedulers; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; /** * Fragment for displaying feed subscriptions @@ -48,7 +48,7 @@ public class SubscriptionFragment extends Fragment { private int mPosition = -1; - private Subscription subscription; + private Disposable disposable; public SubscriptionFragment() { } @@ -68,7 +68,7 @@ public class SubscriptionFragment extends Fragment { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View root = inflater.inflate(R.layout.fragment_subscriptions, container, false); - subscriptionGridLayout = (GridView) root.findViewById(R.id.subscriptions_grid); + subscriptionGridLayout = root.findViewById(R.id.subscriptions_grid); registerForContextMenu(subscriptionGridLayout); return root; } @@ -94,17 +94,17 @@ public class SubscriptionFragment extends Fragment { @Override public void onDestroy() { super.onDestroy(); - if(subscription != null) { - subscription.unsubscribe(); + if(disposable != null) { + disposable.dispose(); } } private void loadSubscriptions() { - if(subscription != null) { - subscription.unsubscribe(); + if(disposable != null) { + disposable.dispose(); } - subscription = Observable.fromCallable(DBReader::getNavDrawerData) - .subscribeOn(Schedulers.newThread()) + disposable = Observable.fromCallable(DBReader::getNavDrawerData) + .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(result -> { navDrawerData = result; @@ -152,18 +152,39 @@ public class SubscriptionFragment extends Fragment { Feed feed = (Feed)selectedObject; switch(item.getItemId()) { case R.id.mark_all_seen_item: - Observable.fromCallable(() -> DBWriter.markFeedSeen(feed.getId())) - .subscribeOn(Schedulers.newThread()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(result -> loadSubscriptions(), - error -> Log.e(TAG, Log.getStackTraceString(error))); + ConfirmationDialog markAllSeenConfirmationDialog = new ConfirmationDialog(getActivity(), + R.string.mark_all_seen_label, + R.string.mark_all_seen_confirmation_msg) { + + @Override + public void onConfirmButtonPressed(DialogInterface dialog) { + dialog.dismiss(); + + Observable.fromCallable(() -> DBWriter.markFeedSeen(feed.getId())) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(result -> loadSubscriptions(), + error -> Log.e(TAG, Log.getStackTraceString(error))); + } + }; + markAllSeenConfirmationDialog.createNewDialog().show(); return true; case R.id.mark_all_read_item: - Observable.fromCallable(() -> DBWriter.markFeedRead(feed.getId())) - .subscribeOn(Schedulers.newThread()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(result -> loadSubscriptions(), - error -> Log.e(TAG, Log.getStackTraceString(error))); + ConfirmationDialog markAllReadConfirmationDialog = new ConfirmationDialog(getActivity(), + R.string.mark_all_read_label, + R.string.mark_all_read_confirmation_msg) { + + @Override + public void onConfirmButtonPressed(DialogInterface dialog) { + dialog.dismiss(); + Observable.fromCallable(() -> DBWriter.markFeedRead(feed.getId())) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(result -> loadSubscriptions(), + error -> Log.e(TAG, Log.getStackTraceString(error))); + } + }; + markAllReadConfirmationDialog.createNewDialog().show(); return true; case R.id.rename_item: new RenameFeedDialog(getActivity(), feed).show(); @@ -190,8 +211,8 @@ public class SubscriptionFragment extends Fragment { remover.skipOnCompletion = true; int playerStatus = PlaybackPreferences.getCurrentPlayerStatus(); if(playerStatus == PlaybackPreferences.PLAYER_STATUS_PLAYING) { - getActivity().sendBroadcast(new Intent( - PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE)); + IntentUtils.sendLocalBroadcast(getContext(), PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE); + } } remover.executeAsync(); @@ -210,7 +231,7 @@ public class SubscriptionFragment extends Fragment { loadSubscriptions(); } - private EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { + private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { @Override public void update(EventDistributor eventDistributor, Integer arg) { if ((EVENTS & arg) != 0) { @@ -220,7 +241,7 @@ public class SubscriptionFragment extends Fragment { } }; - private SubscriptionsAdapter.ItemAccess itemAccess = new SubscriptionsAdapter.ItemAccess() { + private final SubscriptionsAdapter.ItemAccess itemAccess = new SubscriptionsAdapter.ItemAccess() { @Override public int getCount() { if (navDrawerData != null) { diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/GpodnetMainFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/GpodnetMainFragment.java index aff5069c6..4dc114f9b 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/GpodnetMainFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/GpodnetMainFragment.java @@ -20,7 +20,7 @@ import de.danoeh.antennapod.R; */ public class GpodnetMainFragment extends Fragment { - public static final String TAG = "GpodnetMainFragment"; + private static final String TAG = "GpodnetMainFragment"; private static final String PREF_LAST_TAB_POSITION = "tab_position"; private TabLayout tabLayout; @@ -31,12 +31,12 @@ public class GpodnetMainFragment extends Fragment { super.onCreateView(inflater, container, savedInstanceState); View root = inflater.inflate(R.layout.pager_fragment, container, false); - viewPager = (ViewPager)root.findViewById(R.id.viewpager); + viewPager = root.findViewById(R.id.viewpager); GpodnetPagerAdapter pagerAdapter = new GpodnetPagerAdapter(getChildFragmentManager(), getResources()); viewPager.setAdapter(pagerAdapter); // Give the TabLayout the ViewPager - tabLayout = (TabLayout) root.findViewById(R.id.sliding_tabs); + tabLayout = root.findViewById(R.id.sliding_tabs); tabLayout.setupWithViewPager(viewPager); return root; @@ -71,7 +71,7 @@ public class GpodnetMainFragment extends Fragment { private static final int POS_TAGS = 1; private static final int POS_SUGGESTIONS = 2; - Resources resources; + final Resources resources; public GpodnetPagerAdapter(FragmentManager fm, Resources resources) { super(fm); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java index 15e9c9943..49851ebb4 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java @@ -78,10 +78,10 @@ public abstract class PodcastListFragment extends Fragment { public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View root = inflater.inflate(R.layout.gpodnet_podcast_list, container, false); - gridView = (GridView) root.findViewById(R.id.gridView); - progressBar = (ProgressBar) root.findViewById(R.id.progressBar); - txtvError = (TextView) root.findViewById(R.id.txtvError); - butRetry = (Button) root.findViewById(R.id.butRetry); + gridView = root.findViewById(R.id.gridView); + progressBar = root.findViewById(R.id.progressBar); + txtvError = root.findViewById(R.id.txtvError); + butRetry = root.findViewById(R.id.butRetry); gridView.setOnItemClickListener((parent, view, position, id) -> onPodcastSelected((GpodnetPodcast) gridView.getAdapter().getItem(position))); @@ -91,7 +91,7 @@ public abstract class PodcastListFragment extends Fragment { return root; } - protected void onPodcastSelected(GpodnetPodcast selection) { + private void onPodcastSelected(GpodnetPodcast selection) { Log.d(TAG, "Selected podcast: " + selection.toString()); Intent intent = new Intent(getActivity(), OnlineFeedViewActivity.class); intent.putExtra(OnlineFeedViewActivity.ARG_FEEDURL, selection.getUrl()); @@ -101,7 +101,7 @@ public abstract class PodcastListFragment extends Fragment { protected abstract List<GpodnetPodcast> loadPodcastData(GpodnetService service) throws GpodnetServiceException; - protected final void loadData() { + final void loadData() { AsyncTask<Void, Void, List<GpodnetPodcast>> loaderTask = new AsyncTask<Void, Void, List<GpodnetPodcast>>() { volatile Exception exception = null; @@ -160,10 +160,6 @@ public abstract class PodcastListFragment extends Fragment { } }; - if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) { - loaderTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } else { - loaderTask.execute(); - } + loaderTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastTopListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastTopListFragment.java index 33a35fa90..4f963756c 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastTopListFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastTopListFragment.java @@ -1,11 +1,11 @@ package de.danoeh.antennapod.fragment.gpodnet; +import java.util.List; + import de.danoeh.antennapod.core.gpoddernet.GpodnetService; import de.danoeh.antennapod.core.gpoddernet.GpodnetServiceException; import de.danoeh.antennapod.core.gpoddernet.model.GpodnetPodcast; -import java.util.List; - /** * */ diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/SearchListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/SearchListFragment.java index 613e06805..10bd636dd 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/SearchListFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/SearchListFragment.java @@ -73,7 +73,7 @@ public class SearchListFragment extends PodcastListFragment { return service.searchPodcasts(query, 0); } - public void changeQuery(String query) { + private void changeQuery(String query) { Validate.notNull(query); this.query = query; diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java index d2c7f32dd..1e46b1ac5 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java @@ -135,11 +135,7 @@ public class TagListFragment extends ListFragment { } } }; - if (android.os.Build.VERSION.SDK_INT > android.os.Build.VERSION_CODES.GINGERBREAD_MR1) { - loadTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); - } else { - loadTask.execute(); - } + loadTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); } } diff --git a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java index 57b7c359c..ffdfa9516 100644 --- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java +++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java @@ -17,7 +17,7 @@ import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.service.playback.PlaybackService; 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.util.FeedItemUtil; import de.danoeh.antennapod.core.util.IntentUtils; import de.danoeh.antennapod.core.util.LongList; import de.danoeh.antennapod.core.util.ShareUtils; @@ -87,7 +87,7 @@ public class FeedItemMenuHandler { mi.setItemVisibility(R.id.add_to_queue_item, false); } - if (!showExtendedMenu || selectedItem.getLink() == null) { + if (!showExtendedMenu || !ShareUtils.hasLinkToShare(selectedItem)) { mi.setItemVisibility(R.id.visit_website_item, false); mi.setItemVisibility(R.id.share_link_item, false); mi.setItemVisibility(R.id.share_link_with_position_item, false); @@ -155,10 +155,10 @@ public class FeedItemMenuHandler { } public static boolean onMenuItemClicked(Context context, int menuItemId, - FeedItem selectedItem) throws DownloadRequestException { + FeedItem selectedItem) { switch (menuItemId) { case R.id.skip_episode_item: - context.sendBroadcast(new Intent(PlaybackService.ACTION_SKIP_CURRENT_EPISODE)); + IntentUtils.sendLocalBroadcast(context, PlaybackService.ACTION_SKIP_CURRENT_EPISODE); break; case R.id.remove_item: DBWriter.deleteFeedMediaOfItem(context, selectedItem.getMedia().getId()); @@ -217,7 +217,7 @@ public class FeedItemMenuHandler { DBWriter.setFeedItemAutoDownload(selectedItem, false); break; case R.id.visit_website_item: - Uri uri = Uri.parse(selectedItem.getLink()); + Uri uri = Uri.parse(FeedItemUtil.getLinkWithFallback(selectedItem)); Intent intent = new Intent(Intent.ACTION_VIEW, uri); if(IntentUtils.isCallable(context, intent)) { context.startActivity(intent); diff --git a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java index ab7d0e7c6..bd4fe9bcf 100644 --- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java +++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java @@ -30,6 +30,9 @@ import de.danoeh.antennapod.core.util.ShareUtils; * Handles interactions with the FeedItemMenu. */ public class FeedMenuHandler { + + private FeedMenuHandler(){ } + private static final String TAG = "FeedMenuHandler"; public static boolean onCreateOptionsMenu(MenuInflater inflater, Menu menu) { diff --git a/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java b/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java index ac703e13e..7b9fcad9b 100644 --- a/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java +++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java @@ -19,8 +19,9 @@ public class MenuItemUtils extends de.danoeh.antennapod.core.menuhandler.MenuIte public static void adjustTextColor(Context context, SearchView sv) { if(Build.VERSION.SDK_INT < 14) { - EditText searchEditText = (EditText) sv.findViewById(R.id.search_src_text); - if(UserPreferences.getTheme() == de.danoeh.antennapod.R.style.Theme_AntennaPod_Dark) { + EditText searchEditText = sv.findViewById(R.id.search_src_text); + if (UserPreferences.getTheme() == de.danoeh.antennapod.R.style.Theme_AntennaPod_Dark + || UserPreferences.getTheme() == R.style.Theme_AntennaPod_TrueBlack) { searchEditText.setTextColor(Color.WHITE); } else { searchEditText.setTextColor(Color.BLACK); diff --git a/app/src/main/java/de/danoeh/antennapod/preferences/MasterSwitchPreference.java b/app/src/main/java/de/danoeh/antennapod/preferences/MasterSwitchPreference.java new file mode 100644 index 000000000..b810cbfa6 --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/preferences/MasterSwitchPreference.java @@ -0,0 +1,48 @@ +package de.danoeh.antennapod.preferences; + +import android.annotation.TargetApi; +import android.content.Context; +import android.graphics.Typeface; +import android.os.Build; +import android.support.v14.preference.SwitchPreference; +import android.support.v7.preference.PreferenceViewHolder; +import android.util.AttributeSet; +import android.util.TypedValue; +import android.widget.TextView; + +import de.danoeh.antennapod.R; + +public class MasterSwitchPreference extends SwitchPreference { + + public MasterSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + } + + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public MasterSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + } + + public MasterSwitchPreference(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public MasterSwitchPreference(Context context) { + super(context); + } + + + @Override + public void onBindViewHolder(PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + + TypedValue typedValue = new TypedValue(); + getContext().getTheme().resolveAttribute(R.attr.master_switch_background, typedValue, true); + holder.itemView.setBackgroundColor(typedValue.data); + + TextView title = (TextView) holder.findViewById(android.R.id.title); + if (title != null) { + title.setTypeface(title.getTypeface(), Typeface.BOLD); + } + } +}
\ No newline at end of file diff --git a/app/src/main/java/de/danoeh/antennapod/preferences/NumberPickerPreference.java b/app/src/main/java/de/danoeh/antennapod/preferences/NumberPickerPreference.java new file mode 100644 index 000000000..50e76838c --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/preferences/NumberPickerPreference.java @@ -0,0 +1,107 @@ +package de.danoeh.antennapod.preferences; + +import android.app.AlertDialog; +import android.content.Context; +import android.support.v7.preference.Preference; +import android.text.InputFilter; +import android.util.AttributeSet; +import android.view.View; +import android.view.WindowManager; +import android.widget.EditText; + +import de.danoeh.antennapod.R; + +public class NumberPickerPreference extends Preference { + private Context context; + private int defaultValue = 0; + private int minValue = 0; + private int maxValue = Integer.MAX_VALUE; + + public NumberPickerPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { + super(context, attrs, defStyleAttr, defStyleRes); + init(context, attrs); + } + + public NumberPickerPreference(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + init(context, attrs); + } + + public NumberPickerPreference(Context context, AttributeSet attrs) { + super(context, attrs); + init(context, attrs); + } + + public NumberPickerPreference(Context context) { + super(context); + this.context = context; + } + + private void init(Context context, AttributeSet attrs) { + this.context = context; + + for (int i = 0; i < attrs.getAttributeCount(); i++) { + String name = attrs.getAttributeName(i); + String value = attrs.getAttributeValue(i); + switch (name) { + case "defaultValue": + defaultValue = Integer.parseInt(value); + break; + case "minValue": + minValue = Integer.parseInt(value); + break; + case "maxValue": + maxValue = Integer.parseInt(value); + break; + } + } + } + + @Override + protected void onClick() { + super.onClick(); + + View view = View.inflate(context, R.layout.numberpicker, null); + EditText number = view.findViewById(R.id.number); + number.setText(getSharedPreferences().getString(getKey(), ""+defaultValue)); + number.setFilters(new InputFilter[]{(source, start, end, dest, dstart, dend) -> { + try { + String newVal = dest.toString().substring(0, dstart) + dest.toString().substring(dend); + newVal = newVal.substring(0, dstart) + source.toString() + newVal.substring(dstart); + int input = Integer.parseInt(newVal); + if (input >= minValue && input <= maxValue) { + return null; + } + } catch (NumberFormatException nfe) { + nfe.printStackTrace(); + } + return ""; + }}); + + AlertDialog dialog = new AlertDialog.Builder(context) + .setTitle(getTitle()) + .setView(view) + .setNegativeButton(android.R.string.cancel, null) + .setPositiveButton(android.R.string.ok, (dialogInterface, i) -> { + try { + String numberString = number.getText().toString(); + int value = Integer.parseInt(numberString); + + if (value < minValue || value > maxValue) { + return; + } + + getSharedPreferences().edit().putString(getKey(), "" + value).apply(); + + if (getOnPreferenceChangeListener() != null) { + getOnPreferenceChangeListener().onPreferenceChange(this, value); + } + } catch (NumberFormatException e) { + // Do not set value + } + }) + .create(); + dialog.show(); + dialog.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE); + } +} diff --git a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java index 1ca0d0109..31b2cbcb2 100644 --- a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java +++ b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java @@ -16,28 +16,28 @@ 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.os.Bundle; import android.support.design.widget.Snackbar; import android.support.v4.app.ActivityCompat; -import android.support.v4.content.ContextCompat; import android.support.v4.content.FileProvider; import android.support.v7.app.AlertDialog; -import android.text.Editable; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.preference.CheckBoxPreference; +import android.support.v7.preference.ListPreference; +import android.support.v7.preference.Preference; +import android.support.v7.preference.PreferenceFragmentCompat; +import android.support.v7.preference.PreferenceManager; +import android.support.v7.preference.PreferenceScreen; import android.text.Html; -import android.text.TextWatcher; import android.text.format.DateFormat; import android.text.format.DateUtils; import android.util.Log; -import android.widget.EditText; import android.widget.ListView; import android.widget.Toast; import com.afollestad.materialdialogs.MaterialDialog; +import com.bytehamster.lib.preferencesearch.SearchConfiguration; +import com.bytehamster.lib.preferencesearch.SearchPreference; import org.apache.commons.lang3.ArrayUtils; @@ -46,7 +46,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collections; -import java.util.Comparator; import java.util.GregorianCalendar; import java.util.List; import java.util.concurrent.TimeUnit; @@ -55,13 +54,13 @@ 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.ImportExportActivity; import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.activity.MediaplayerActivity; +import de.danoeh.antennapod.activity.OpmlImportFromPathActivity; import de.danoeh.antennapod.activity.PreferenceActivity; -import de.danoeh.antennapod.activity.PreferenceActivityGingerbread; import de.danoeh.antennapod.activity.StatisticsActivity; import de.danoeh.antennapod.asynctask.ExportWorker; -import de.danoeh.antennapod.core.BuildConfig; import de.danoeh.antennapod.core.export.ExportWriter; import de.danoeh.antennapod.core.export.html.HtmlWriter; import de.danoeh.antennapod.core.export.opml.OpmlWriter; @@ -69,16 +68,19 @@ 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.flattr.FlattrUtils; +import de.danoeh.antennapod.core.util.gui.PictureInPictureUtil; import de.danoeh.antennapod.dialog.AuthenticationDialog; import de.danoeh.antennapod.dialog.AutoFlattrPreferenceDialog; import de.danoeh.antennapod.dialog.ChooseDataFolderDialog; import de.danoeh.antennapod.dialog.GpodnetSetHostnameDialog; import de.danoeh.antennapod.dialog.ProxyDialog; import de.danoeh.antennapod.dialog.VariableSpeedDialog; -import rx.Observable; -import rx.Subscription; -import rx.android.schedulers.AndroidSchedulers; -import rx.schedulers.Schedulers; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; + +import static de.danoeh.antennapod.activity.PreferenceActivity.PARAM_RESOURCE; /** * Sets up a preference UI that lets the user change user preferences. @@ -88,19 +90,28 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc private static final String TAG = "PreferenceController"; - private static final String PREF_FLATTR_SETTINGS = "prefFlattrSettings"; + private static final String PREF_SCREEN_USER_INTERFACE = "prefScreenInterface"; + private static final String PREF_SCREEN_PLAYBACK = "prefScreenPlayback"; + private static final String PREF_SCREEN_NETWORK = "prefScreenNetwork"; + private static final String PREF_SCREEN_INTEGRATIONS = "prefScreenIntegrations"; + private static final String PREF_SCREEN_STORAGE = "prefScreenStorage"; + private static final String PREF_SCREEN_AUTODL = "prefAutoDownloadSettings"; + private static final String PREF_SCREEN_FLATTR = "prefFlattrSettings"; + private static final String PREF_SCREEN_GPODDER = "prefGpodderSettings"; + private static final String PREF_FLATTR_AUTH = "pref_flattr_authenticate"; private static final String PREF_FLATTR_REVOKE = "prefRevokeAccess"; private static final String PREF_AUTO_FLATTR_PREFS = "prefAutoFlattrPrefs"; private static final String PREF_OPML_EXPORT = "prefOpmlExport"; + private static final String PREF_OPML_IMPORT = "prefOpmlImport"; private static final String PREF_HTML_EXPORT = "prefHtmlExport"; private static final String STATISTICS = "statistics"; + private static final String IMPORT_EXPORT = "importExport"; private static final String PREF_ABOUT = "prefAbout"; private static final String PREF_CHOOSE_DATA_DIR = "prefChooseDataDir"; - private static final String AUTO_DL_PREF_SCREEN = "prefAutoDownloadSettings"; private static final String PREF_PLAYBACK_SPEED_LAUNCHER = "prefPlaybackSpeedLauncher"; - public static final String PREF_PLAYBACK_REWIND_DELTA_LAUNCHER = "prefPlaybackRewindDeltaLauncher"; - public static final String PREF_PLAYBACK_FAST_FORWARD_DELTA_LAUNCHER = "prefPlaybackFastForwardDeltaLauncher"; + private static final String PREF_PLAYBACK_REWIND_DELTA_LAUNCHER = "prefPlaybackRewindDeltaLauncher"; + private static final String PREF_PLAYBACK_FAST_FORWARD_DELTA_LAUNCHER = "prefPlaybackFastForwardDeltaLauncher"; private static final String PREF_GPODNET_LOGIN = "pref_gpodnet_authenticate"; private static final String PREF_GPODNET_SETLOGIN_INFORMATION = "pref_gpodnet_setlogin_information"; private static final String PREF_GPODNET_SYNC = "pref_gpodnet_sync"; @@ -126,7 +137,7 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc } }; private CheckBoxPreference[] selectedNetworks; - private Subscription subscription; + private Disposable disposable; public PreferenceController(PreferenceUI ui) { this.ui = ui; @@ -134,30 +145,51 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc .registerOnSharedPreferenceChangeListener(this); } - /** - * 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; - } - } - @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)); - } + + } + + + + public void onCreate(int screen) { + switch (screen) { + case R.xml.preferences: + setupMainScreen(); + break; + case R.xml.preferences_network: + setupNetworkScreen(); + break; + case R.xml.preferences_autodownload: + setupAutoDownloadScreen(); + buildAutodownloadSelectedNetworksPreference(); + setSelectedNetworksEnabled(UserPreferences.isEnableAutodownloadWifiFilter()); + buildEpisodeCleanupPreference(); + break; + case R.xml.preferences_playback: + setupPlaybackScreen(); + PreferenceControllerFlavorHelper.setupFlavoredUI(ui); + buildSmartMarkAsPlayedPreference(); + break; + case R.xml.preferences_integrations: + setupIntegrationsScreen(); + break; + case R.xml.preferences_flattr: + setupFlattrScreen(); + break; + case R.xml.preferences_gpodder: + setupGpodderScreen(); + break; + case R.xml.preferences_storage: + setupStorageScreen(); + break; + case R.xml.preferences_user_interface: + setupInterfaceScreen(); + break; } } - public void onCreate() { + private void setupInterfaceScreen() { final Activity activity = ui.getActivity(); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { @@ -172,22 +204,64 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc } ); } - ui.findPreference(PreferenceController.PREF_FLATTR_REVOKE).setOnPreferenceClickListener( - preference -> { - FlattrUtils.revokeAccessToken(activity); - checkItemVisibility(); + 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(PreferenceController.PREF_ABOUT).setOnPreferenceClickListener( - preference -> { - activity.startActivity(new Intent(activity, AboutActivity.class)); + }); + + ui.findPreference(UserPreferences.PREF_COMPACT_NOTIFICATION_BUTTONS) + .setOnPreferenceClickListener(preference -> { + showNotificationButtonsDialog(); return true; - } - ); - ui.findPreference(PreferenceController.STATISTICS).setOnPreferenceClickListener( + }); + + ui.findPreference(UserPreferences.PREF_BACK_BUTTON_BEHAVIOR) + .setOnPreferenceChangeListener((preference, newValue) -> { + if (newValue.equals("page")) { + final Context context = ui.getActivity(); + final String[] navTitles = context.getResources().getStringArray(R.array.back_button_go_to_pages); + final String[] navTags = context.getResources().getStringArray(R.array.back_button_go_to_pages_tags); + final String choice[] = { UserPreferences.getBackButtonGoToPage() }; + + AlertDialog.Builder builder = new AlertDialog.Builder(context); + builder.setTitle(R.string.back_button_go_to_page_title); + builder.setSingleChoiceItems(navTitles, ArrayUtils.indexOf(navTags, UserPreferences.getBackButtonGoToPage()), (dialogInterface, i) -> { + if (i >= 0) { + choice[0] = navTags[i]; + } + }); + builder.setPositiveButton(R.string.confirm_label, (dialogInterface, i) -> UserPreferences.setBackButtonGoToPage(choice[0])); + builder.setNegativeButton(R.string.cancel_label, null); + builder.create().show(); + return true; + } else { + return true; + } + }); + + if (Build.VERSION.SDK_INT >= 26) { + ui.findPreference(UserPreferences.PREF_EXPANDED_NOTIFICATION).setVisible(false); + } + } + + private void setupStorageScreen() { + final Activity activity = ui.getActivity(); + + ui.findPreference(PreferenceController.IMPORT_EXPORT).setOnPreferenceClickListener( preference -> { - activity.startActivity(new Intent(activity, StatisticsActivity.class)); + activity.startActivity(new Intent(activity, ImportExportActivity.class)); return true; } ); @@ -195,6 +269,11 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc preference -> export(new OpmlWriter())); ui.findPreference(PreferenceController.PREF_HTML_EXPORT).setOnPreferenceClickListener( preference -> export(new HtmlWriter())); + ui.findPreference(PreferenceController.PREF_OPML_IMPORT).setOnPreferenceClickListener( + preference -> { + activity.startActivity(new Intent(activity, OpmlImportFromPathActivity.class)); + return true; + }); ui.findPreference(PreferenceController.PREF_CHOOSE_DATA_DIR).setOnPreferenceClickListener( preference -> { if (Build.VERSION_CODES.KITKAT <= Build.VERSION.SDK_INT && @@ -228,130 +307,70 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc return true; } ); - ui.findPreference(UserPreferences.PREF_THEME) - .setOnPreferenceChangeListener( - (preference, newValue) -> { - Intent i = new Intent(activity, MainActivity.class); - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { - i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK - | Intent.FLAG_ACTIVITY_NEW_TASK); - } else { - i.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - } - activity.finish(); - activity.startActivity(i); - 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(); } - ); - ui.findPreference(UserPreferences.PREF_HIDDEN_DRAWER_ITEMS) - .setOnPreferenceClickListener(preference -> { - showDrawerPreferencesDialog(); - return true; - }); + return true; + } + return false; + } + ); + } - ui.findPreference(UserPreferences.PREF_COMPACT_NOTIFICATION_BUTTONS) - .setOnPreferenceClickListener(preference -> { - showNotificationButtonsDialog(); + private void setupIntegrationsScreen() { + final AppCompatActivity activity = ui.getActivity(); + + ui.findPreference(PREF_SCREEN_FLATTR).setOnPreferenceClickListener(preference -> { + openScreen(R.xml.preferences_flattr, activity); + return true; + }); + ui.findPreference(PREF_SCREEN_GPODDER).setOnPreferenceClickListener(preference -> { + openScreen(R.xml.preferences_gpodder, activity); + return true; + }); + } + + private void setupFlattrScreen() { + final AppCompatActivity activity = ui.getActivity(); + + ui.findPreference(PreferenceController.PREF_FLATTR_REVOKE).setOnPreferenceClickListener( + preference -> { + FlattrUtils.revokeAccessToken(activity); + checkFlattrItemVisibility(); return true; - }); + } + ); - ui.findPreference(UserPreferences.PREF_UPDATE_INTERVAL) + ui.findPreference(PreferenceController.PREF_AUTO_FLATTR_PREFS) .setOnPreferenceClickListener(preference -> { - showUpdateIntervalTimePreferencesDialog(); - return true; - }); + AutoFlattrPreferenceDialog.newAutoFlattrPreferenceDialog(activity, + new AutoFlattrPreferenceDialog.AutoFlattrPreferenceDialogInterface() { + @Override + public void onCancelled() { - 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_PLAYBACK_REWIND_DELTA_LAUNCHER) - .setOnPreferenceClickListener(preference -> { - MediaplayerActivity.showSkipPreference(activity, MediaplayerActivity.SkipDirection.SKIP_REWIND); - return true; - }); - ui.findPreference(PreferenceController.PREF_PLAYBACK_FAST_FORWARD_DELTA_LAUNCHER) - .setOnPreferenceClickListener(preference -> { - MediaplayerActivity.showSkipPreference(activity, MediaplayerActivity.SkipDirection.SKIP_FORWARD); + @Override + public void onConfirmed(boolean autoFlattrEnabled, float autoFlattrValue) { + UserPreferences.setAutoFlattrSettings(autoFlattrEnabled, autoFlattrValue); + checkFlattrItemVisibility(); + } + }); return true; }); + } + + private void setupGpodderScreen() { + final AppCompatActivity activity = ui.getActivity(); + ui.findPreference(PreferenceController.PREF_GPODNET_SETLOGIN_INFORMATION) .setOnPreferenceClickListener(preference -> { AuthenticationDialog dialog = new AuthenticationDialog(activity, @@ -399,45 +418,127 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc GpodnetSetHostnameDialog.createDialog(activity).setOnDismissListener(dialog -> updateGpodnetPreferenceScreen()); return true; }); + } - ui.findPreference(PreferenceController.PREF_AUTO_FLATTR_PREFS) + private void setupPlaybackScreen() { + final Activity activity = ui.getActivity(); + + ui.findPreference(PreferenceController.PREF_PLAYBACK_SPEED_LAUNCHER) .setOnPreferenceClickListener(preference -> { - AutoFlattrPreferenceDialog.newAutoFlattrPreferenceDialog(activity, - new AutoFlattrPreferenceDialog.AutoFlattrPreferenceDialogInterface() { - @Override - public void onCancelled() { + VariableSpeedDialog.showDialog(activity); + return true; + }); + ui.findPreference(PreferenceController.PREF_PLAYBACK_REWIND_DELTA_LAUNCHER) + .setOnPreferenceClickListener(preference -> { + MediaplayerActivity.showSkipPreference(activity, MediaplayerActivity.SkipDirection.SKIP_REWIND); + return true; + }); + ui.findPreference(PreferenceController.PREF_PLAYBACK_FAST_FORWARD_DELTA_LAUNCHER) + .setOnPreferenceClickListener(preference -> { + MediaplayerActivity.showSkipPreference(activity, MediaplayerActivity.SkipDirection.SKIP_FORWARD); + return true; + }); + if (!PictureInPictureUtil.supportsPictureInPicture(activity)) { + ListPreference behaviour = (ListPreference) ui.findPreference(UserPreferences.PREF_VIDEO_BEHAVIOR); + behaviour.setEntries(R.array.video_background_behavior_options_without_pip); + behaviour.setEntryValues(R.array.video_background_behavior_values_without_pip); + } + } - } + private void setupAutoDownloadScreen() { + ui.findPreference(UserPreferences.PREF_ENABLE_AUTODL).setOnPreferenceChangeListener( + (preference, newValue) -> { + if (newValue instanceof Boolean) { + checkAutodownloadItemVisibility((Boolean) newValue); + } + 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_EPISODE_CACHE_SIZE) + .setOnPreferenceChangeListener( + (preference, o) -> { + if (o instanceof String) { + setEpisodeCacheSizeText(UserPreferences.readEpisodeCacheSize((String) o)); + } + return true; + } + ); + } - @Override - public void onConfirmed(boolean autoFlattrEnabled, float autoFlattrValue) { - UserPreferences.setAutoFlattrSettings(autoFlattrEnabled, autoFlattrValue); - checkItemVisibility(); - } - }); + private void setupNetworkScreen() { + final AppCompatActivity activity = ui.getActivity(); + ui.findPreference(PREF_SCREEN_AUTODL).setOnPreferenceClickListener(preference -> { + openScreen(R.xml.preferences_autodownload, activity); + return true; + }); + ui.findPreference(UserPreferences.PREF_UPDATE_INTERVAL) + .setOnPreferenceClickListener(preference -> { + showUpdateIntervalTimePreferencesDialog(); 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(); + ui.findPreference(UserPreferences.PREF_PARALLEL_DOWNLOADS) + .setOnPreferenceChangeListener( + (preference, o) -> { + if (o instanceof Integer) { + setParallelDownloadsText((Integer) o); + } + return true; } - return true; - } - return false; - } - ); + ); + // validate and set correct value: number of downloads between 1 and 50 (inclusive) ui.findPreference(PREF_PROXY).setOnPreferenceClickListener(preference -> { ProxyDialog dialog = new ProxyDialog(ui.getActivity()); dialog.createDialog().show(); return true; }); + } + + private void setupMainScreen() { + final AppCompatActivity activity = ui.getActivity(); + setupSearch(); + ui.findPreference(PREF_SCREEN_USER_INTERFACE).setOnPreferenceClickListener(preference -> { + openScreen(R.xml.preferences_user_interface, activity); + return true; + }); + ui.findPreference(PREF_SCREEN_PLAYBACK).setOnPreferenceClickListener(preference -> { + openScreen(R.xml.preferences_playback, activity); + return true; + }); + ui.findPreference(PREF_SCREEN_NETWORK).setOnPreferenceClickListener(preference -> { + openScreen(R.xml.preferences_network, activity); + return true; + }); + ui.findPreference(PREF_SCREEN_INTEGRATIONS).setOnPreferenceClickListener(preference -> { + openScreen(R.xml.preferences_integrations, activity); + return true; + }); + ui.findPreference(PREF_SCREEN_STORAGE).setOnPreferenceClickListener(preference -> { + openScreen(R.xml.preferences_storage, activity); + 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(PREF_KNOWN_ISSUES).setOnPreferenceClickListener(preference -> { openInBrowser("https://github.com/AntennaPod/AntennaPod/labels/bug"); return true; @@ -469,11 +570,76 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc ui.getActivity().startActivity(Intent.createChooser(emailIntent, intentTitle)); return true; }); - PreferenceControllerFlavorHelper.setupFlavoredUI(ui); - buildEpisodeCleanupPreference(); - buildSmartMarkAsPlayedPreference(); - buildAutodownloadSelectedNetworsPreference(); - setSelectedNetworksEnabled(UserPreferences.isEnableAutodownloadWifiFilter()); + } + + private void setupSearch() { + final AppCompatActivity activity = ui.getActivity(); + + SearchPreference searchPreference = (SearchPreference) ui.findPreference("searchPreference"); + SearchConfiguration config = searchPreference.getSearchConfiguration(); + config.setActivity(activity); + config.setFragmentContainerViewId(R.id.content); + config.setBreadcrumbsEnabled(true); + + config.index() + .addBreadcrumb(getTitleOfPage(R.xml.preferences_user_interface)) + .addFile(R.xml.preferences_user_interface); + config.index() + .addBreadcrumb(getTitleOfPage(R.xml.preferences_playback)) + .addFile(R.xml.preferences_playback); + config.index() + .addBreadcrumb(getTitleOfPage(R.xml.preferences_network)) + .addFile(R.xml.preferences_network); + config.index() + .addBreadcrumb(getTitleOfPage(R.xml.preferences_storage)) + .addFile(R.xml.preferences_storage); + config.index() + .addBreadcrumb(getTitleOfPage(R.xml.preferences_network)) + .addBreadcrumb(R.string.automation) + .addBreadcrumb(getTitleOfPage(R.xml.preferences_autodownload)) + .addFile(R.xml.preferences_autodownload); + config.index() + .addBreadcrumb(getTitleOfPage(R.xml.preferences_integrations)) + .addBreadcrumb(getTitleOfPage(R.xml.preferences_gpodder)) + .addFile(R.xml.preferences_gpodder); + config.index() + .addBreadcrumb(getTitleOfPage(R.xml.preferences_integrations)) + .addBreadcrumb(getTitleOfPage(R.xml.preferences_flattr)) + .addFile(R.xml.preferences_flattr); + } + + public PreferenceFragmentCompat openScreen(int preferences, AppCompatActivity activity) { + PreferenceFragmentCompat prefFragment = new PreferenceActivity.MainFragment(); + Bundle args = new Bundle(); + args.putInt(PARAM_RESOURCE, preferences); + prefFragment.setArguments(args); + activity.getSupportFragmentManager().beginTransaction() + .replace(R.id.content, prefFragment) + .addToBackStack(TAG).commit(); + return prefFragment; + } + + public static int getTitleOfPage(int preferences) { + switch (preferences) { + case R.xml.preferences_network: + return R.string.network_pref; + case R.xml.preferences_autodownload: + return R.string.pref_automatic_download_title; + case R.xml.preferences_playback: + return R.string.playback_pref; + case R.xml.preferences_storage: + return R.string.storage_pref; + case R.xml.preferences_user_interface: + return R.string.user_interface_label; + case R.xml.preferences_integrations: + return R.string.integrations_label; + case R.xml.preferences_flattr: + return R.string.flattr_label; + case R.xml.preferences_gpodder: + return R.string.gpodnet_main_label; + default: + return R.string.settings_label; + } } private boolean export(ExportWriter exportWriter) { @@ -485,11 +651,11 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc final AlertDialog.Builder alert = new AlertDialog.Builder(context) .setNeutralButton(android.R.string.ok, (dialog, which) -> dialog.dismiss()); Observable<File> observable = new ExportWorker(exportWriter).exportObservable(); - subscription = observable.subscribeOn(Schedulers.newThread()) + disposable = observable.subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(output -> { - alert.setTitle(R.string.opml_export_success_title); - String message = context.getString(R.string.opml_export_success_sum) + output.toString(); + 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(), @@ -515,7 +681,7 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc alert.setTitle(R.string.export_error_label); alert.setMessage(error.getMessage()); alert.show(); - }, () -> progressDialog.dismiss()); + }, progressDialog::dismiss); return true; } @@ -529,23 +695,42 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc } } - public void onResume() { - checkItemVisibility(); - setUpdateIntervalText(); - setParallelDownloadsText(UserPreferences.getParallelDownloads()); - setEpisodeCacheSizeText(UserPreferences.getEpisodeCacheSize()); - setDataFolderText(); - GpodnetPreferences.registerOnSharedPreferenceChangeListener(gpoddernetListener); - updateGpodnetPreferenceScreen(); + public void onResume(int screen) { + switch (screen) { + case R.xml.preferences_network: + setUpdateIntervalText(); + setParallelDownloadsText(UserPreferences.getParallelDownloads()); + break; + case R.xml.preferences_autodownload: + setEpisodeCacheSizeText(UserPreferences.getEpisodeCacheSize()); + checkAutodownloadItemVisibility(UserPreferences.isEnableAutodownload()); + break; + case R.xml.preferences_storage: + setDataFolderText(); + break; + case R.xml.preferences_integrations: + setIntegrationsItemVisibility(); + return; + case R.xml.preferences_flattr: + checkFlattrItemVisibility(); + break; + case R.xml.preferences_gpodder: + GpodnetPreferences.registerOnSharedPreferenceChangeListener(gpoddernetListener); + updateGpodnetPreferenceScreen(); + break; + case R.xml.preferences_playback: + checkSonicItemVisibility(); + break; + } } - public void onPause() { + public void unregisterGpodnet() { GpodnetPreferences.unregisterOnSharedPreferenceChangeListener(gpoddernetListener); } - public void onStop() { - if(subscription != null) { - subscription.unsubscribe(); + public void unsubscribeExportSubscription() { + if (disposable != null) { + disposable.dispose(); } } @@ -698,27 +883,32 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc } } + private void setIntegrationsItemVisibility() { + ui.findPreference(PreferenceController.PREF_SCREEN_FLATTR).setEnabled(FlattrUtils.hasAPICredentials()); + } + @SuppressWarnings("deprecation") - private void checkItemVisibility() { + private void checkFlattrItemVisibility() { 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(); + private void checkAutodownloadItemVisibility(boolean autoDownload) { 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); + ui.findPreference(UserPreferences.PREF_EPISODE_CLEANUP).setEnabled(autoDownload); + ui.findPreference(UserPreferences.PREF_ENABLE_AUTODL_ON_MOBILE).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 checkSonicItemVisibility() { + if (Build.VERSION.SDK_INT < 16) { + ListPreference p = (ListPreference) ui.findPreference(UserPreferences.PREF_MEDIA_PLAYER); + p.setEntries(R.array.media_player_options_no_sonic); + p.setEntryValues(R.array.media_player_values_no_sonic); } } @@ -778,7 +968,11 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc } } - private void buildAutodownloadSelectedNetworsPreference() { + private static String blankIfNull(String val) { + return val == null ? "" : val; + } + + private void buildAutodownloadSelectedNetworksPreference() { final Activity activity = ui.getActivity(); if (selectedNetworks != null) { @@ -788,67 +982,63 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc WifiManager wifiservice = (WifiManager) activity.getApplicationContext().getSystemService(Context.WIFI_SERVICE); List<WifiConfiguration> networks = wifiservice.getConfiguredNetworks(); - if (networks != null) { - Collections.sort(networks, new Comparator<WifiConfiguration>() { - @Override - public int compare(WifiConfiguration x, WifiConfiguration y) { - return x.SSID.compareTo(y.SSID); + if (networks == null) { + Log.e(TAG, "Couldn't get list of configure Wi-Fi networks"); + return; + } + Collections.sort(networks, (x, y) -> + blankIfNull(x.SSID).compareTo(blankIfNull(y.SSID))); + selectedNetworks = new CheckBoxPreference[networks.size()]; + List<String> prefValues = Arrays.asList(UserPreferences + .getAutodownloadSelectedNetworks()); + PreferenceScreen prefScreen = ui.getPreferenceScreen(); + 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); } - }); - 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); + UserPreferences.setAutodownloadSelectedNetworks( + prefValuesList.toArray(new String[prefValuesList.size()]) + ); + return true; + } else { + return false; } - } else { - Log.e(TAG, "Couldn't get list of configure Wi-Fi networks"); + }; + // 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); } } private void clearAutodownloadSelectedNetworsPreference() { if (selectedNetworks != null) { - PreferenceScreen prefScreen = (PreferenceScreen) ui.findPreference(PreferenceController.AUTO_DL_PREF_SCREEN); + PreferenceScreen prefScreen = ui.getPreferenceScreen(); for (CheckBoxPreference network : selectedNetworks) { if (network != null) { @@ -1013,11 +1203,16 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc public interface PreferenceUI { + void setFragment(PreferenceFragmentCompat fragment); + PreferenceFragmentCompat getFragment(); + /** * Finds a preference based on its key. */ Preference findPreference(CharSequence key); - Activity getActivity(); + PreferenceScreen getPreferenceScreen(); + + AppCompatActivity getActivity(); } } diff --git a/app/src/main/java/de/danoeh/antennapod/preferences/SwitchCompatPreference.java b/app/src/main/java/de/danoeh/antennapod/preferences/SwitchCompatPreference.java deleted file mode 100644 index 10c11b88e..000000000 --- a/app/src/main/java/de/danoeh/antennapod/preferences/SwitchCompatPreference.java +++ /dev/null @@ -1,37 +0,0 @@ -package de.danoeh.antennapod.preferences; - -import android.annotation.TargetApi; -import android.content.Context; -import android.os.Build; -import android.preference.CheckBoxPreference; -import android.util.AttributeSet; - -import de.danoeh.antennapod.R; - -public class SwitchCompatPreference extends CheckBoxPreference { - - public SwitchCompatPreference(Context context, AttributeSet attrs, int defStyleAttr) { - super(context, attrs, defStyleAttr); - init(); - } - - @TargetApi(Build.VERSION_CODES.LOLLIPOP) - public SwitchCompatPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { - super(context, attrs, defStyleAttr, defStyleRes); - init(); - } - - public SwitchCompatPreference(Context context, AttributeSet attrs) { - super(context, attrs); - init(); - } - - public SwitchCompatPreference(Context context) { - super(context); - init(); - } - - private void init() { - setWidgetLayoutResource(R.layout.preference_switch_layout); - } -}
\ No newline at end of file diff --git a/app/src/main/java/de/danoeh/antennapod/receiver/PlayerWidget.java b/app/src/main/java/de/danoeh/antennapod/receiver/PlayerWidget.java deleted file mode 100644 index f0d4014ed..000000000 --- a/app/src/main/java/de/danoeh/antennapod/receiver/PlayerWidget.java +++ /dev/null @@ -1,80 +0,0 @@ -package de.danoeh.antennapod.receiver; - -import java.util.Arrays; - -import android.appwidget.AppWidgetManager; -import android.appwidget.AppWidgetProvider; -import android.content.Context; -import android.content.Intent; -import android.content.SharedPreferences; -import android.text.TextUtils; -import android.util.Log; - -import de.danoeh.antennapod.core.service.playback.PlaybackService; -import de.danoeh.antennapod.service.PlayerWidgetService; - -public class PlayerWidget extends AppWidgetProvider { - private static final String TAG = "PlayerWidget"; - private static final String PREFS_NAME = "PlayerWidgetPrefs"; - private static final String KEY_ENABLED = "WidgetEnabled"; - - @Override - public void onReceive(Context context, Intent intent) { - Log.d(TAG, "onReceive"); - super.onReceive(context, intent); - // don't do anything if we're not enabled - if (!isEnabled(context)) { - return; - } - - // these come from the PlaybackService when things should get updated - if (TextUtils.equals(intent.getAction(), PlaybackService.FORCE_WIDGET_UPDATE)) { - startUpdate(context); - } else if (TextUtils.equals(intent.getAction(), PlaybackService.STOP_WIDGET_UPDATE)) { - stopUpdate(context); - } - } - - @Override - public void onEnabled(Context context) { - super.onEnabled(context); - Log.d(TAG, "Widget enabled"); - setEnabled(context, true); - startUpdate(context); - } - - @Override - public void onUpdate(Context context, AppWidgetManager appWidgetManager, - int[] appWidgetIds) { - Log.d(TAG, "onUpdate() called with: " + "context = [" + context + "], appWidgetManager = [" + appWidgetManager + "], appWidgetIds = [" + Arrays.toString(appWidgetIds) + "]"); - startUpdate(context); - } - - @Override - public void onDisabled(Context context) { - super.onDisabled(context); - Log.d(TAG, "Widget disabled"); - setEnabled(context, false); - stopUpdate(context); - } - - private void startUpdate(Context context) { - Log.d(TAG, "startUpdate() called with: " + "context = [" + context + "]"); - context.startService(new Intent(context, PlayerWidgetService.class)); - } - - private void stopUpdate(Context context) { - Log.d(TAG, "stopUpdate() called with: " + "context = [" + context + "]"); - context.stopService(new Intent(context, PlayerWidgetService.class)); - } - - private boolean isEnabled(Context context) { - SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE); - return prefs.getBoolean(KEY_ENABLED, false); - } - - private void setEnabled(Context context, boolean enabled) { - SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE); - prefs.edit().putBoolean(KEY_ENABLED, enabled).apply(); - } -} diff --git a/app/src/main/java/de/danoeh/antennapod/receiver/SPAReceiver.java b/app/src/main/java/de/danoeh/antennapod/receiver/SPAReceiver.java index a373c5353..c9bd973cb 100644 --- a/app/src/main/java/de/danoeh/antennapod/receiver/SPAReceiver.java +++ b/app/src/main/java/de/danoeh/antennapod/receiver/SPAReceiver.java @@ -22,8 +22,8 @@ public class SPAReceiver extends BroadcastReceiver{ private static final String TAG = "SPAReceiver"; public static final String ACTION_SP_APPS_QUERY_FEEDS = "de.danoeh.antennapdsp.intent.SP_APPS_QUERY_FEEDS"; - public static final String ACTION_SP_APPS_QUERY_FEEDS_REPSONSE = "de.danoeh.antennapdsp.intent.SP_APPS_QUERY_FEEDS_RESPONSE"; - public static final String ACTION_SP_APPS_QUERY_FEEDS_REPSONSE_FEEDS_EXTRA = "feeds"; + private static final String ACTION_SP_APPS_QUERY_FEEDS_REPSONSE = "de.danoeh.antennapdsp.intent.SP_APPS_QUERY_FEEDS_RESPONSE"; + private static final String ACTION_SP_APPS_QUERY_FEEDS_REPSONSE_FEEDS_EXTRA = "feeds"; @Override public void onReceive(Context context, Intent intent) { diff --git a/app/src/main/java/de/danoeh/antennapod/service/PlayerWidgetService.java b/app/src/main/java/de/danoeh/antennapod/service/PlayerWidgetService.java deleted file mode 100644 index b5bfb1ae4..000000000 --- a/app/src/main/java/de/danoeh/antennapod/service/PlayerWidgetService.java +++ /dev/null @@ -1,245 +0,0 @@ -package de.danoeh.antennapod.service; - -import android.app.PendingIntent; -import android.app.Service; -import android.appwidget.AppWidgetManager; -import android.content.ComponentName; -import android.content.Intent; -import android.content.ServiceConnection; -import android.os.Build; -import android.os.IBinder; -import android.util.Log; -import android.view.KeyEvent; -import android.view.View; -import android.widget.RemoteViews; - -import de.danoeh.antennapod.R; -import de.danoeh.antennapod.activity.MainActivity; -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.receiver.MediaButtonReceiver; -import de.danoeh.antennapod.core.service.playback.PlaybackService; -import de.danoeh.antennapod.core.service.playback.PlayerStatus; -import de.danoeh.antennapod.core.storage.DBWriter; -import de.danoeh.antennapod.core.util.Converter; -import de.danoeh.antennapod.core.util.playback.Playable; -import de.danoeh.antennapod.fragment.QueueFragment; -import de.danoeh.antennapod.receiver.PlayerWidget; - -/** - * Updates the state of the player widget - */ -public class PlayerWidgetService extends Service { - private static final String TAG = "PlayerWidgetService"; - - private PlaybackService playbackService; - - /** - * Controls write access to playbackservice reference - */ - private Object psLock; - - /** - * True while service is updating the widget - */ - private volatile boolean isUpdating; - - public PlayerWidgetService() { - } - - @Override - public void onCreate() { - super.onCreate(); - Log.d(TAG, "Service created"); - isUpdating = false; - psLock = new Object(); - } - - @Override - public void onDestroy() { - super.onDestroy(); - Log.d(TAG, "Service is about to be destroyed"); - if (playbackService != null) { - Playable playable = playbackService.getPlayable(); - if (playable != null && playable instanceof FeedMedia) { - FeedMedia media = (FeedMedia) playable; - if (media.hasAlmostEnded()) { - Log.d(TAG, "smart mark as read"); - FeedItem item = media.getItem(); - DBWriter.markItemPlayed(item, FeedItem.PLAYED, false); - DBWriter.removeQueueItem(this, item, false); - DBWriter.addItemToPlaybackHistory(media); - if (item.getFeed().getPreferences().getCurrentAutoDelete() && - (!item.isTagged(FeedItem.TAG_FAVORITE) || !UserPreferences.shouldFavoriteKeepEpisode())) { - Log.d(TAG, "Delete " + media.toString()); - DBWriter.deleteFeedMediaOfItem(this, media.getId()); - } - } - } - } - - try { - unbindService(mConnection); - } catch (IllegalArgumentException e) { - Log.w(TAG, "IllegalArgumentException when trying to unbind service"); - } - } - - @Override - public IBinder onBind(Intent intent) { - return null; - } - - @Override - public int onStartCommand(Intent intent, int flags, int startId) { - if (!isUpdating) { - if (playbackService == null && PlaybackService.isRunning) { - bindService(new Intent(this, PlaybackService.class), - mConnection, 0); - } else { - startViewUpdaterIfNotRunning(); - } - } else { - Log.d(TAG, "Service was called while updating. Ignoring update request"); - } - return Service.START_NOT_STICKY; - } - - private void updateViews() { - isUpdating = true; - - ComponentName playerWidget = new ComponentName(this, PlayerWidget.class); - AppWidgetManager manager = AppWidgetManager.getInstance(this); - RemoteViews views = new RemoteViews(getPackageName(), - R.layout.player_widget); - PendingIntent startMediaplayer = PendingIntent.getActivity(this, 0, - PlaybackService.getPlayerActivityIntent(this), 0); - - Intent startApp = new Intent(getBaseContext(), MainActivity.class); - startApp.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - startApp.putExtra(MainActivity.EXTRA_FRAGMENT_TAG, QueueFragment.TAG); - PendingIntent startAppPending = PendingIntent.getActivity(getBaseContext(), 0, startApp, PendingIntent.FLAG_UPDATE_CURRENT); - - boolean nothingPlaying = false; - if (playbackService != null) { - final Playable media = playbackService.getPlayable(); - if (media != null) { - PlayerStatus status = playbackService.getStatus(); - views.setOnClickPendingIntent(R.id.layout_left, startMediaplayer); - - views.setTextViewText(R.id.txtvTitle, media.getEpisodeTitle()); - - String progressString = getProgressString(); - if (progressString != null) { - views.setViewVisibility(R.id.txtvProgress, View.VISIBLE); - views.setTextViewText(R.id.txtvProgress, progressString); - } - - if (status == PlayerStatus.PLAYING) { - views.setImageViewResource(R.id.butPlay, R.drawable.ic_pause_white_24dp); - if (Build.VERSION.SDK_INT >= 15) { - views.setContentDescription(R.id.butPlay, getString(R.string.pause_label)); - } - } else { - views.setImageViewResource(R.id.butPlay, R.drawable.ic_play_arrow_white_24dp); - if (Build.VERSION.SDK_INT >= 15) { - views.setContentDescription(R.id.butPlay, getString(R.string.play_label)); - } - } - views.setOnClickPendingIntent(R.id.butPlay, - createMediaButtonIntent()); - } else { - nothingPlaying = true; - } - } else { - nothingPlaying = true; - } - - if (nothingPlaying) { - // start the app if they click anything - views.setOnClickPendingIntent(R.id.layout_left, startAppPending); - views.setOnClickPendingIntent(R.id.butPlay, startAppPending); - views.setViewVisibility(R.id.txtvProgress, View.INVISIBLE); - views.setTextViewText(R.id.txtvTitle, - this.getString(R.string.no_media_playing_label)); - views.setImageViewResource(R.id.butPlay, R.drawable.ic_play_arrow_white_24dp); - } - - manager.updateAppWidget(playerWidget, views); - isUpdating = false; - } - - /** - * Creates an intent which fakes a mediabutton press - */ - private PendingIntent createMediaButtonIntent() { - KeyEvent event = new KeyEvent(KeyEvent.ACTION_DOWN, - KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE); - Intent startingIntent = new Intent( - MediaButtonReceiver.NOTIFY_BUTTON_RECEIVER); - startingIntent.putExtra(Intent.EXTRA_KEY_EVENT, event); - - return PendingIntent.getBroadcast(this, 0, startingIntent, 0); - } - - private String getProgressString() { - int position = playbackService.getCurrentPosition(); - int duration = playbackService.getDuration(); - if (position > 0 && duration > 0) { - return Converter.getDurationStringLong(position) + " / " - + Converter.getDurationStringLong(duration); - } else { - return null; - } - } - - private ServiceConnection mConnection = new ServiceConnection() { - public void onServiceConnected(ComponentName className, IBinder service) { - Log.d(TAG, "Connection to service established"); - synchronized (psLock) { - if(service instanceof PlaybackService.LocalBinder) { - playbackService = ((PlaybackService.LocalBinder) service).getService(); - startViewUpdaterIfNotRunning(); - } - } - } - - @Override - public void onServiceDisconnected(ComponentName name) { - synchronized (psLock) { - playbackService = null; - Log.d(TAG, "Disconnected from service"); - } - } - - }; - - private void startViewUpdaterIfNotRunning() { - if (!isUpdating) { - ViewUpdater updateThread = new ViewUpdater(this); - updateThread.start(); - } - } - - class ViewUpdater extends Thread { - private static final String THREAD_NAME = "ViewUpdater"; - private PlayerWidgetService service; - - public ViewUpdater(PlayerWidgetService service) { - super(); - setName(THREAD_NAME); - this.service = service; - - } - - @Override - public void run() { - synchronized (psLock) { - service.updateViews(); - } - } - - } - -} diff --git a/app/src/main/java/de/danoeh/antennapod/spa/SPAUtil.java b/app/src/main/java/de/danoeh/antennapod/spa/SPAUtil.java index 75cbd8b5a..03958508d 100644 --- a/app/src/main/java/de/danoeh/antennapod/spa/SPAUtil.java +++ b/app/src/main/java/de/danoeh/antennapod/spa/SPAUtil.java @@ -33,7 +33,7 @@ public class SPAUtil { * sent before. */ public static synchronized boolean sendSPAppsQueryFeedsIntent(Context context) { - if (context == null) throw new IllegalArgumentException("context = null"); + assert context != null : "context = null"; final Context appContext = context.getApplicationContext(); if (appContext == null) { Log.wtf(TAG, "Unable to get application context"); diff --git a/app/src/main/java/de/danoeh/antennapod/view/AspectRatioVideoView.java b/app/src/main/java/de/danoeh/antennapod/view/AspectRatioVideoView.java index f930c912a..e79389fb3 100644 --- a/app/src/main/java/de/danoeh/antennapod/view/AspectRatioVideoView.java +++ b/app/src/main/java/de/danoeh/antennapod/view/AspectRatioVideoView.java @@ -25,6 +25,8 @@ public class AspectRatioVideoView extends VideoView { private int mVideoWidth; private int mVideoHeight; + private float mAvailableWidth = -1; + private float mAvailableHeight = -1; public AspectRatioVideoView(Context context) { this(context, null); @@ -48,8 +50,13 @@ public class AspectRatioVideoView extends VideoView { return; } - float heightRatio = (float) mVideoHeight / (float) getHeight(); - float widthRatio = (float) mVideoWidth / (float) getWidth(); + if (mAvailableWidth < 0 || mAvailableHeight < 0) { + mAvailableWidth = getWidth(); + mAvailableHeight = getHeight(); + } + + float heightRatio = (float) mVideoHeight / mAvailableHeight; + float widthRatio = (float) mVideoWidth / mAvailableWidth; int scaledHeight; int scaledWidth; @@ -94,4 +101,15 @@ public class AspectRatioVideoView extends VideoView { invalidate(); } + /** + * Sets the maximum size that the view might expand to + * @param width + * @param height + */ + public void setAvailableSize(float width, float height) { + mAvailableWidth = width; + mAvailableHeight = height; + requestLayout(); + } + } diff --git a/app/src/main/java/de/danoeh/antennapod/view/SquareImageView.java b/app/src/main/java/de/danoeh/antennapod/view/SquareImageView.java index 27b6ee2bc..7ce33e11f 100644 --- a/app/src/main/java/de/danoeh/antennapod/view/SquareImageView.java +++ b/app/src/main/java/de/danoeh/antennapod/view/SquareImageView.java @@ -1,13 +1,13 @@ package de.danoeh.antennapod.view; import android.content.Context; +import android.support.v7.widget.AppCompatImageView; import android.util.AttributeSet; -import android.widget.ImageView; /** * From http://stackoverflow.com/a/19449488/6839 */ -public class SquareImageView extends ImageView { +public class SquareImageView extends AppCompatImageView { public SquareImageView(Context context) { super(context); @@ -26,6 +26,7 @@ public class SquareImageView extends ImageView { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int width = getMeasuredWidth(); + //noinspection SuspiciousNameCombination setMeasuredDimension(width, width); } |