diff options
Diffstat (limited to 'app/src/main/java/de/danoeh/antennapod')
25 files changed, 1236 insertions, 924 deletions
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 123f66661..207aec20f 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java @@ -13,6 +13,7 @@ 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.core.util.playback.PlaybackServiceStarter; import de.danoeh.antennapod.dialog.VariableSpeedDialog; /** @@ -34,14 +35,13 @@ public class AudioplayerActivity extends MediaplayerInfoActivity { 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); + + new PlaybackServiceStarter(this, media) + .startWhenPrepared(true) + .shouldStream(false) + .prepareImmediately(true) + .start(); + } else if (PlaybackService.isCasting()) { Intent intent = PlaybackService.getPlayerActivityIntent(this); if (intent.getComponent() != null && 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 8dea41b7c..6b1272b01 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java @@ -2,51 +2,37 @@ 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.joanzapata.iconify.Iconify; - -import org.apache.commons.lang3.StringUtils; -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 org.apache.commons.lang3.StringUtils; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; import rx.Observable; import rx.Subscription; import rx.android.schedulers.AndroidSchedulers; @@ -59,7 +45,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,15 +55,6 @@ 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; @@ -98,40 +74,6 @@ public class FeedInfoActivity extends AppCompatActivity { } }; - 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()); @@ -142,28 +84,20 @@ public class FeedInfoActivity extends AppCompatActivity { imgvCover = (ImageView) findViewById(R.id.imgvCover); txtvTitle = (TextView) findViewById(R.id.txtvTitle); + TextView txtvAuthorHeader = (TextView) findViewById(R.id.txtvAuthor); + ImageView imgvBackground = (ImageView) 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 = (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); + txtvAuthor = (TextView) findViewById(R.id.txtvDetailsAuthor); 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; - }); txtvUrl.setOnClickListener(copyUrlToClipboard); @@ -179,7 +113,6 @@ public class FeedInfoActivity extends AppCompatActivity { 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) @@ -188,6 +121,14 @@ public class FeedInfoActivity extends AppCompatActivity { .fitCenter() .dontAnimate() .into(imgvCover); + Glide.with(FeedInfoActivity.this) + .load(feed.getImageLocation()) + .placeholder(R.color.image_readability_tint) + .error(R.color.image_readability_tint) + .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) + .transform(new FastBlurTransformation(FeedInfoActivity.this)) + .dontAnimate() + .into(imgvBackground); txtvTitle.setText(feed.getTitle()); @@ -205,6 +146,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); @@ -218,76 +160,7 @@ 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(); @@ -295,37 +168,6 @@ public class FeedInfoActivity extends AppCompatActivity { } @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) { @@ -369,34 +211,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 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/FeedSettingsActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/FeedSettingsActivity.java new file mode 100644 index 000000000..5e15585a5 --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/activity/FeedSettingsActivity.java @@ -0,0 +1,370 @@ +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 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 rx.Observable; +import rx.Subscription; +import rx.android.schedulers.AndroidSchedulers; +import rx.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 Subscription subscription; + + + private final View.OnClickListener copyUrlToClipboard = new View.OnClickListener() { + @Override + public void onClick(View v) { + if(feed != null && feed.getDownload_url() != null) { + String url = feed.getDownload_url(); + ClipData clipData = ClipData.newPlainText(url, url); + android.content.ClipboardManager cm = (android.content.ClipboardManager) FeedSettingsActivity.this + .getSystemService(Context.CLIPBOARD_SERVICE); + cm.setPrimaryClip(clipData); + Toast t = Toast.makeText(FeedSettingsActivity.this, R.string.copied_url_msg, Toast.LENGTH_SHORT); + t.show(); + } + } + }; + + 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 = (ImageView) findViewById(R.id.imgvCover); + txtvTitle = (TextView) findViewById(R.id.txtvTitle); + TextView txtvAuthorHeader = (TextView) findViewById(R.id.txtvAuthor); + ImageView imgvBackground = (ImageView) 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 = (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; + }); + + subscription = Observable.fromCallable(()-> DBReader.getFeed(feedId)) + .subscribeOn(Schedulers.newThread()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(result -> { + if (result == null) { + Log.e(TAG, "Activity was started with invalid arguments"); + finish(); + } + feed = result; + FeedPreferences prefs = feed.getPreferences(); + Glide.with(FeedSettingsActivity.this) + .load(feed.getImageLocation()) + .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()) + .placeholder(R.color.image_readability_tint) + .error(R.color.image_readability_tint) + .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) + .transform(new FastBlurTransformation(FeedSettingsActivity.this)) + .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(); + }); + } + + @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(); + } + } + + @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/ImportExportActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/ImportExportActivity.java index 6a97adcc3..91462bce9 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/ImportExportActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/ImportExportActivity.java @@ -8,7 +8,7 @@ import android.os.Bundle; import android.os.Environment; import android.os.ParcelFileDescriptor; import android.support.design.widget.Snackbar; -import android.support.v4.content.IntentCompat; +import android.support.v7.app.ActionBar; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.util.Log; @@ -39,7 +39,10 @@ public class ImportExportActivity extends AppCompatActivity { protected void onCreate(Bundle savedInstanceState) { setTheme(UserPreferences.getTheme()); super.onCreate(savedInstanceState); - getSupportActionBar().setDisplayShowHomeEnabled(true); + ActionBar actionBar = getSupportActionBar(); + if (actionBar != null) { + actionBar.setDisplayShowHomeEnabled(true); + } setContentView(R.layout.import_export_activity); findViewById(R.id.button_export).setOnClickListener(view -> backup()); @@ -125,7 +128,7 @@ public class ImportExportActivity extends AppCompatActivity { d.setPositiveButton(android.R.string.ok, (dialogInterface, i) -> { Intent intent = new Intent(getApplicationContext(), SplashActivity.class); ComponentName cn = intent.getComponent(); - Intent mainIntent = IntentCompat.makeRestartActivityTask(cn); + Intent mainIntent = Intent.makeRestartActivityTask(cn); startActivity(mainIntent); }); d.show(); 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 f56dca173..294ab5af8 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java @@ -30,6 +30,8 @@ import android.widget.ListView; import com.bumptech.glide.Glide; +import de.danoeh.antennapod.core.event.ServiceEvent; +import de.danoeh.antennapod.core.util.gui.NotificationUtils; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.Validate; @@ -200,6 +202,8 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi transaction.commit(); checkFirstLaunch(); + NotificationUtils.createChannels(this); + UserPreferences.restartUpdateAlarm(false); } private void saveLastNavFragment(String tag) { @@ -572,10 +576,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(); @@ -720,6 +743,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) { 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 232ff4311..091f8daab 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java @@ -34,6 +34,7 @@ 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.preferences.UserPreferences; @@ -42,10 +43,12 @@ import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBTasks; import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.util.Converter; +import de.danoeh.antennapod.core.util.FeedItemUtil; import de.danoeh.antennapod.core.util.Flavors; import de.danoeh.antennapod.core.util.ShareUtils; import de.danoeh.antennapod.core.util.StorageUtils; import de.danoeh.antennapod.core.util.Supplier; +import de.danoeh.antennapod.core.util.gui.PictureInPictureUtil; import de.danoeh.antennapod.core.util.playback.MediaPlayerError; import de.danoeh.antennapod.core.util.playback.Playable; import de.danoeh.antennapod.core.util.playback.PlaybackController; @@ -225,9 +228,11 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements @Override protected void onPause() { - if(controller != null) { - controller.reinitServiceIfPaused(); - controller.pause(); + if (!PictureInPictureUtil.isInPictureInPictureMode(this)) { + if (controller != null) { + controller.reinitServiceIfPaused(); + controller.pause(); + } } super.onPause(); } @@ -267,6 +272,9 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements controller.release(); } controller = newPlaybackController(); + setupGUI(); + loadMediaInfo(); + onPositionObserverUpdate(); } @Override @@ -317,11 +325,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); @@ -379,6 +387,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements } else { startActivity(intent); } + finish(); return true; } else { if (media != null) { @@ -556,7 +565,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: @@ -599,16 +608,37 @@ 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. @@ -849,6 +879,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements if(controller == null) { return; } + controller.init(); controller.playPause(); } 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 12d918a76..3f005fe36 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java @@ -1,12 +1,12 @@ package de.danoeh.antennapod.activity; -import android.app.Activity; import android.content.Intent; 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; @@ -14,6 +14,9 @@ import android.widget.FrameLayout; import java.lang.ref.WeakReference; +import com.bytehamster.lib.preferencesearch.SearchPreference; +import com.bytehamster.lib.preferencesearch.SearchPreferenceResult; +import com.bytehamster.lib.preferencesearch.SearchPreferenceResultListener; import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.preferences.PreferenceController; @@ -22,19 +25,36 @@ 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() { + 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 Activity getActivity() { + public PreferenceScreen getPreferenceScreen() { + return fragment.getPreferenceScreen(); + } + + @Override + public AppCompatActivity getActivity() { return PreferenceActivity.this; } }; @@ -64,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 @@ -84,23 +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; } } - 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); } } @@ -109,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(); } @@ -125,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/VideoplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java index 54758acf4..c8fb12abc 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java @@ -10,27 +10,32 @@ import android.support.v4.view.WindowCompat; import android.support.v7.app.ActionBar; 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; - -import java.lang.ref.WeakReference; -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.gui.PictureInPictureUtil; import de.danoeh.antennapod.core.util.playback.ExternalMedia; import de.danoeh.antennapod.core.util.playback.Playable; +import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter; import de.danoeh.antennapod.view.AspectRatioVideoView; +import java.lang.ref.WeakReference; +import java.util.concurrent.atomic.AtomicBoolean; + /** * Activity for playing video files. */ @@ -52,6 +57,7 @@ public class VideoplayerActivity extends MediaplayerActivity { private LinearLayout videoOverlay; private AspectRatioVideoView videoview; private ProgressBar progressIndicator; + private FrameLayout videoframe; @Override protected void chooseTheme() { @@ -77,14 +83,12 @@ public class VideoplayerActivity extends MediaplayerActivity { 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); + + new PlaybackServiceStarter(this, media) + .startWhenPrepared(true) + .shouldStream(false) + .prepareImmediately(true) + .start(); } else if (PlaybackService.isCasting()) { Intent intent = PlaybackService.getPlayerActivityIntent(this); if (!intent.getComponent().getClassName().equals(VideoplayerActivity.class.getName())) { @@ -96,10 +100,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(); } @@ -127,7 +148,7 @@ public class VideoplayerActivity extends MediaplayerActivity { @Override protected void setupGUI() { - if(isSetup.getAndSet(true)) { + if (isSetup.getAndSet(true)) { return; } super.setupGUI(); @@ -135,20 +156,23 @@ public class VideoplayerActivity extends MediaplayerActivity { controls = (LinearLayout) findViewById(R.id.controls); videoOverlay = (LinearLayout) findViewById(R.id.overlay); videoview = (AspectRatioVideoView) findViewById(R.id.videoview); + videoframe = (FrameLayout) findViewById(R.id.videoframe); progressIndicator = (ProgressBar) 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 @@ -176,6 +200,9 @@ public class VideoplayerActivity extends MediaplayerActivity { private final View.OnTouchListener onVideoviewTouched = (v, event) -> { if (event.getAction() == MotionEvent.ACTION_DOWN) { + if (PictureInPictureUtil.isInPictureInPictureMode(this)) { + return true; + } videoControlsHider.stop(); toggleVideoControlsVisibility(); if (videoControlsShowing) { @@ -260,7 +287,9 @@ public class VideoplayerActivity extends MediaplayerActivity { 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(); } } @@ -269,6 +298,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; @@ -313,28 +349,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; @@ -350,6 +389,32 @@ 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; @@ -362,7 +427,7 @@ public class VideoplayerActivity extends MediaplayerActivity { private final Runnable hideVideoControls = () -> { VideoplayerActivity vpa = activity != null ? activity.get() : null; - if(vpa == null) { + if (vpa == null) { return; } if (vpa.videoControlsShowing) { 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..d8f324e8a 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/DefaultActionButtonCallback.java @@ -6,6 +6,7 @@ import android.widget.Toast; import com.afollestad.materialdialogs.MaterialDialog; +import de.danoeh.antennapod.core.util.playback.PlaybackServiceStarter; import org.apache.commons.lang3.Validate; import de.danoeh.antennapod.R; @@ -80,13 +81,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()) { + if (media.isCurrentlyPlaying()) { + new PlaybackServiceStarter(context, media) + .startWhenPrepared(true) + .shouldStream(false) + .start(); context.sendBroadcast(new Intent(PlaybackService.ACTION_PAUSE_PLAY_CURRENT_EPISODE)); - } - else if (item.hasMedia() && item.getMedia().isCurrentlyPaused()) { + } else if (media.isCurrentlyPaused()) { + new PlaybackServiceStarter(context, media) + .startWhenPrepared(true) + .shouldStream(false) + .start(); context.sendBroadcast(new Intent(PlaybackService.ACTION_RESUME_PLAY_CURRENT_EPISODE)); - } - else { + } else { DBTasks.playMedia(context, media, false, true, false); } } 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 c62c30c90..c4f476634 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadedEpisodesListAdapter.java @@ -100,8 +100,10 @@ public class DownloadedEpisodesListAdapter extends BaseAdapter { FeedItem.State state = item.getState(); if (state == FeedItem.State.PLAYING) { 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); 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..83dd3fe9c 100644 --- a/app/src/main/java/de/danoeh/antennapod/config/PlaybackServiceCallbacksImpl.java +++ b/app/src/main/java/de/danoeh/antennapod/config/PlaybackServiceCallbacksImpl.java @@ -3,6 +3,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 +19,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/fragment/CompletedDownloadsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java index f37ecd5e7..f59bc88bf 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java @@ -140,18 +140,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); } } 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 8928d2bf3..417ecff89 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java @@ -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 4ef26ad6c..b072aeaf2 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java @@ -37,7 +37,6 @@ public class ExternalPlayerFragment extends Fragment { private ImageButton butPlay; private TextView mFeedName; private ProgressBar mProgressBar; - private PlaybackController controller; public ExternalPlayerFragment() { @@ -83,6 +82,11 @@ public class ExternalPlayerFragment extends Fragment { controller.playPause(); } }); + loadMediaInfo(); + } + + public void connectToPlaybackService() { + controller.init(); } private PlaybackController setupPlaybackController() { @@ -124,8 +128,7 @@ public class ExternalPlayerFragment extends Fragment { public void onResume() { super.onResume(); controller.init(); - mProgressBar.setProgress((int) - ((double) controller.getPosition() / controller.getDuration() * 100)); + onPositionObserverUpdate(); } @Override @@ -165,36 +168,35 @@ 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()) - .placeholder(R.color.light_gray) - .error(R.color.light_gray) - .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) - .fitCenter() - .dontAnimate() - .into(imgvCover); - - fragmentLayout.setVisibility(View.VISIBLE); - if (controller.isPlayingVideoLocally()) { - butPlay.setVisibility(View.GONE); - } else { - butPlay.setVisibility(View.VISIBLE); - } - return true; + if (controller == null) { + Log.w(TAG, "loadMediaInfo was called while PlaybackController was null!"); + return false; + } + + Playable media = controller.getMedia(); + if (media != null) { + txtvTitle.setText(media.getEpisodeTitle()); + mFeedName.setText(media.getFeedTitle()); + onPositionObserverUpdate(); + + Glide.with(getActivity()) + .load(media.getImageLocation()) + .placeholder(R.color.light_gray) + .error(R.color.light_gray) + .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) + .fitCenter() + .dontAnimate() + .into(imgvCover); + + 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); } + return true; } else { - Log.w(TAG, "loadMediaInfo was called while playbackService was null!"); + Log.w(TAG, "loadMediaInfo was called while the media object of playbackService was null!"); return false; } } 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 2d11e9f71..6b589493b 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java @@ -34,6 +34,7 @@ import com.bumptech.glide.Glide; import com.joanzapata.iconify.Iconify; import com.joanzapata.iconify.widget.IconButton; +import de.danoeh.antennapod.core.service.playback.PlaybackService; import de.danoeh.antennapod.core.util.NetworkUtils; import org.apache.commons.lang3.ArrayUtils; @@ -432,6 +433,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) { + 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); 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 890f31f57..83d6f9615 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemlistFragment.java @@ -31,6 +31,7 @@ import com.joanzapata.iconify.Iconify; import com.joanzapata.iconify.fonts.FontAwesomeIcons; import com.joanzapata.iconify.widget.IconTextView; +import de.danoeh.antennapod.activity.FeedSettingsActivity; import org.apache.commons.lang3.Validate; import java.util.List; @@ -221,13 +222,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); } @@ -497,6 +491,7 @@ public class ItemlistFragment extends ListFragment { imgvBackground = (ImageView) header.findViewById(R.id.imgvBackground); imgvCover = (ImageView) header.findViewById(R.id.imgvCover); ImageButton butShowInfo = (ImageButton) header.findViewById(R.id.butShowInfo); + ImageButton butShowSettings = (ImageButton) header.findViewById(R.id.butShowSettings); txtvInformation = (TextView) header.findViewById(R.id.txtvInformation); txtvFailure = (IconTextView) header.findViewById(R.id.txtvFailure); @@ -509,10 +504,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); } @@ -520,6 +517,15 @@ 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()) 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 fba445d3a..bae77d58b 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java @@ -327,6 +327,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; } @@ -525,14 +534,15 @@ 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; for(FeedItem item : queue) { if(item.getMedia() != null) { - duration += item.getMedia().getDuration(); + timeLeft += item.getMedia().getDuration() - item.getMedia().getPosition(); } } info += " \u2022 "; - info += Converter.getDurationStringLocalized(getActivity(), duration); + info += getString(R.string.time_left_label); + info += Converter.getDurationStringLocalized(getActivity(), timeLeft); } infoBar.setText(info); } 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 c834b7ea7..1247aacbb 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java @@ -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.newThread()) + .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.newThread()) + .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(); 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 f6f73e017..de47ee5e4 100644 --- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java +++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java @@ -17,6 +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.util.FeedItemUtil; import de.danoeh.antennapod.core.util.IntentUtils; import de.danoeh.antennapod.core.util.LongList; import de.danoeh.antennapod.core.util.ShareUtils; @@ -86,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); @@ -216,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/preferences/MasterSwitchPreference.java b/app/src/main/java/de/danoeh/antennapod/preferences/MasterSwitchPreference.java new file mode 100644 index 000000000..e500267fe --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/preferences/MasterSwitchPreference.java @@ -0,0 +1,47 @@ +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/PreferenceController.java b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java index 2c7d738dd..9bb0edeb2 100644 --- a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java +++ b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceController.java @@ -16,30 +16,44 @@ 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.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 de.danoeh.antennapod.activity.AboutActivity; import de.danoeh.antennapod.activity.ImportExportActivity; +import de.danoeh.antennapod.activity.MediaplayerActivity; import de.danoeh.antennapod.activity.OpmlImportFromPathActivity; +import de.danoeh.antennapod.activity.PreferenceActivity; +import de.danoeh.antennapod.activity.StatisticsActivity; +import de.danoeh.antennapod.core.export.html.HtmlWriter; +import de.danoeh.antennapod.core.export.opml.OpmlWriter; +import de.danoeh.antennapod.core.service.GpodnetSyncService; +import de.danoeh.antennapod.dialog.AuthenticationDialog; +import de.danoeh.antennapod.dialog.AutoFlattrPreferenceDialog; +import de.danoeh.antennapod.dialog.GpodnetSetHostnameDialog; +import de.danoeh.antennapod.dialog.ProxyDialog; +import de.danoeh.antennapod.dialog.VariableSpeedDialog; +import de.danoeh.antennapod.core.util.gui.PictureInPictureUtil; import org.apache.commons.lang3.ArrayUtils; import java.io.File; @@ -53,30 +67,21 @@ import java.util.concurrent.TimeUnit; import de.danoeh.antennapod.CrashReportWriter; import de.danoeh.antennapod.R; -import de.danoeh.antennapod.activity.AboutActivity; import de.danoeh.antennapod.activity.DirectoryChooserActivity; import de.danoeh.antennapod.activity.MainActivity; -import de.danoeh.antennapod.activity.MediaplayerActivity; -import de.danoeh.antennapod.activity.StatisticsActivity; import de.danoeh.antennapod.asynctask.ExportWorker; import de.danoeh.antennapod.core.export.ExportWriter; -import de.danoeh.antennapod.core.export.html.HtmlWriter; -import de.danoeh.antennapod.core.export.opml.OpmlWriter; 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.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 static de.danoeh.antennapod.activity.PreferenceActivity.PARAM_RESOURCE; + /** * Sets up a preference UI that lets the user change user preferences. */ @@ -85,7 +90,15 @@ 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"; @@ -96,7 +109,6 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc 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"; private static final String PREF_PLAYBACK_REWIND_DELTA_LAUNCHER = "prefPlaybackRewindDeltaLauncher"; private static final String PREF_PLAYBACK_FAST_FORWARD_DELTA_LAUNCHER = "prefPlaybackFastForwardDeltaLauncher"; @@ -143,7 +155,46 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc } } - public void onCreate() { + + + 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(); + buildAutodownloadSelectedNetworsPreference(); + 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; + } + } + + private void setupInterfaceScreen() { final Activity activity = ui.getActivity(); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { @@ -158,25 +209,34 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc } ); } - ui.findPreference(PreferenceController.PREF_FLATTR_REVOKE).setOnPreferenceClickListener( - preference -> { - FlattrUtils.revokeAccessToken(activity); - checkItemVisibility(); - return true; - } - ); - ui.findPreference(PreferenceController.PREF_ABOUT).setOnPreferenceClickListener( - preference -> { - activity.startActivity(new Intent(activity, AboutActivity.class)); + 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.STATISTICS).setOnPreferenceClickListener( - preference -> { - activity.startActivity(new Intent(activity, StatisticsActivity.class)); + }); + + ui.findPreference(UserPreferences.PREF_COMPACT_NOTIFICATION_BUTTONS) + .setOnPreferenceClickListener(preference -> { + showNotificationButtonsDialog(); return true; - } - ); + }); + + } + + private void setupStorageScreen() { + final Activity activity = ui.getActivity(); + ui.findPreference(PreferenceController.IMPORT_EXPORT).setOnPreferenceClickListener( preference -> { activity.startActivity(new Intent(activity, ImportExportActivity.class)); @@ -225,126 +285,70 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc return true; } ); - ui.findPreference(UserPreferences.PREF_THEME) - .setOnPreferenceChangeListener( - (preference, newValue) -> { - Intent i = new Intent(activity, MainActivity.class); - i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK - | Intent.FLAG_ACTIVITY_NEW_TASK); - activity.finish(); - activity.startActivity(i); - return true; + ui.findPreference(UserPreferences.PREF_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, @@ -392,45 +396,135 @@ 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 String) { + try { + int value = Integer.parseInt((String) o); + if (1 <= value && value <= 50) { + setParallelDownloadsText(value); + return true; + } + } catch (NumberFormatException e) { + return false; + } + } + return false; } - 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; @@ -462,11 +556,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) { @@ -522,22 +681,41 @@ 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) { + public void unsubscribeExportSubscription() { + if (subscription != null) { subscription.unsubscribe(); } } @@ -691,22 +869,28 @@ 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()); - + private void checkSonicItemVisibility() { if (Build.VERSION.SDK_INT >= 16) { ui.findPreference(UserPreferences.PREF_SONIC).setEnabled(true); } else { @@ -794,7 +978,7 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc selectedNetworks = new CheckBoxPreference[networks.size()]; List<String> prefValues = Arrays.asList(UserPreferences .getAutodownloadSelectedNetworks()); - PreferenceScreen prefScreen = (PreferenceScreen) ui.findPreference(PreferenceController.AUTO_DL_PREF_SCREEN); + PreferenceScreen prefScreen = ui.getPreferenceScreen(); Preference.OnPreferenceClickListener clickListener = preference -> { if (preference instanceof CheckBoxPreference) { String key = preference.getKey(); @@ -841,7 +1025,7 @@ public class PreferenceController implements SharedPreferences.OnSharedPreferenc 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) { @@ -1006,11 +1190,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 a90f0f706..000000000 --- a/app/src/main/java/de/danoeh/antennapod/receiver/PlayerWidget.java +++ /dev/null @@ -1,80 +0,0 @@ -package de.danoeh.antennapod.receiver; - -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 java.util.Arrays; - -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/service/PlayerWidgetService.java b/app/src/main/java/de/danoeh/antennapod/service/PlayerWidgetService.java deleted file mode 100644 index d5141bd37..000000000 --- a/app/src/main/java/de/danoeh/antennapod/service/PlayerWidgetService.java +++ /dev/null @@ -1,244 +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 final Object psLock = new Object(); - - /** - * 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; - } - - @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 final 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 final 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/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(); + } + } |