diff options
Diffstat (limited to 'app/src/main')
147 files changed, 1995 insertions, 1519 deletions
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 73af654e9..488d4e473 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -36,7 +36,7 @@ android:usesCleartextTraffic="true" android:logo="@mipmap/ic_launcher"> <meta-data android:name="com.google.android.gms.car.notification.SmallIcon" - android:resource="@drawable/ic_notification" /> + android:resource="@drawable/ic_antenna" /> <meta-data android:name="com.google.android.backup.api_key" android:value="AEdPqrEAAAAI3a05VToCTlqBymJrbFGaKQMvF-bBAuLsOdavBA"/> @@ -105,17 +105,6 @@ android:value="de.danoeh.antennapod.activity.MainActivity"/> </activity> - <activity - android:name=".activity.FeedInfoActivity" - android:label="@string/feed_info_label"> - </activity> - - <activity - android:name=".activity.FeedSettingsActivity" - android:windowSoftInputMode="stateHidden" - android:label="@string/feed_settings_label"> - </activity> - <service android:name=".core.service.PlayerWidgetJobService" android:permission="android.permission.BIND_JOB_SERVICE" @@ -151,13 +140,6 @@ android:value="de.danoeh.antennapod.activity.PreferenceActivity"/> </activity> <activity - android:name=".activity.StatisticsActivity" - android:label="@string/statistics_label"> - <meta-data - android:name="android.support.PARENT_ACTIVITY" - android:value="de.danoeh.antennapod.activity.PreferenceActivity"/> - </activity> - <activity android:name=".activity.ImportExportActivity" android:label="@string/import_export"> <meta-data @@ -209,6 +191,13 @@ android:name=".activity.OpmlFeedChooserActivity" android:label="@string/opml_import_label"> </activity> + <activity + android:name=".activity.BugReportActivity" + android:label="@string/bug_report_title"> + <meta-data + android:name="android.support.PARENT_ACTIVITY" + android:value="de.danoeh.antennapod.activity.PreferenceActivity"/> + </activity> <activity android:name=".activity.VideoplayerActivity" @@ -350,7 +339,7 @@ <provider android:authorities="@string/provider_authority" - android:name="android.support.v4.content.FileProvider" + android:name="androidx.core.content.FileProvider" android:exported="false" android:grantUriPermissions="true"> <meta-data diff --git a/app/src/main/java/de/danoeh/antennapod/CrashReportWriter.java b/app/src/main/java/de/danoeh/antennapod/CrashReportWriter.java index ea2166674..061ea9ae2 100644 --- a/app/src/main/java/de/danoeh/antennapod/CrashReportWriter.java +++ b/app/src/main/java/de/danoeh/antennapod/CrashReportWriter.java @@ -9,6 +9,9 @@ import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.io.PrintWriter; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.Locale; import de.danoeh.antennapod.core.preferences.UserPreferences; @@ -32,13 +35,7 @@ public class CrashReportWriter implements Thread.UncaughtExceptionHandler { PrintWriter out = null; try { out = new PrintWriter(new FileWriter(path)); - out.println("[ Environment ]"); - out.println("Android version: " + Build.VERSION.RELEASE); - out.println("OS version: " + System.getProperty("os.version")); - out.println("AntennaPod version: " + BuildConfig.VERSION_NAME); - out.println("Model: " + Build.MODEL); - out.println("Device: " + Build.DEVICE); - out.println("Product: " + Build.PRODUCT); + out.println(getSystemInfo()); out.println(); out.println("[ StackTrace ]"); ex.printStackTrace(out); @@ -49,4 +46,15 @@ public class CrashReportWriter implements Thread.UncaughtExceptionHandler { } defaultHandler.uncaughtException(thread, ex); } + + public static String getSystemInfo() { + return "[ Environment ]" + + "\nAndroid version: " + Build.VERSION.RELEASE + + "\nOS version: " + System.getProperty("os.version") + + "\nAntennaPod version: " + BuildConfig.VERSION_NAME + + "\nModel: " + Build.MODEL + + "\nDevice: " + Build.DEVICE + + "\nProduct: " + Build.PRODUCT + + "\nTime: " + new SimpleDateFormat("yyyy-MM-dd HH:mm:ss", Locale.getDefault()).format(new Date()) + "\n"; + } } diff --git a/app/src/main/java/de/danoeh/antennapod/PodcastApp.java b/app/src/main/java/de/danoeh/antennapod/PodcastApp.java index cb2f597d6..94d281a45 100644 --- a/app/src/main/java/de/danoeh/antennapod/PodcastApp.java +++ b/app/src/main/java/de/danoeh/antennapod/PodcastApp.java @@ -63,6 +63,8 @@ public class PodcastApp extends Application { EventBus.builder() .addIndex(new ApEventBusIndex()) .addIndex(new ApCoreEventBusIndex()) + .logNoSubscriberMessages(false) + .sendNoSubscriberEvent(false) .installDefaultEventBus(); } diff --git a/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java index 1bcdada44..ef7ea2b16 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/AboutActivity.java @@ -1,12 +1,10 @@ package de.danoeh.antennapod.activity; -import android.content.Intent; import android.content.res.TypedArray; import android.graphics.Color; -import android.net.Uri; import android.os.Build; import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; +import androidx.appcompat.app.AppCompatActivity; import android.util.Log; import android.view.MenuItem; import android.view.View; @@ -15,6 +13,7 @@ import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.LinearLayout; +import de.danoeh.antennapod.core.util.IntentUtils; import org.apache.commons.io.IOUtils; import java.io.IOException; @@ -57,8 +56,7 @@ public class AboutActivity extends AppCompatActivity { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { if (url.startsWith("http")) { - Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); - startActivity(browserIntent); + IntentUtils.openInBrowser(AboutActivity.this, url); return true; } else { url = url.replace("file:///android_asset/", ""); 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 2321a3602..795891273 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/AudioplayerActivity.java @@ -1,7 +1,7 @@ package de.danoeh.antennapod.activity; import android.content.Intent; -import android.support.v4.view.ViewCompat; +import androidx.core.view.ViewCompat; import android.text.TextUtils; import android.util.Log; import android.view.View; @@ -60,11 +60,13 @@ public class AudioplayerActivity extends MediaplayerInfoActivity { } if (controller == null) { butPlaybackSpeed.setVisibility(View.GONE); + txtvPlaybackSpeed.setVisibility(View.GONE); return; } updatePlaybackSpeedButtonText(); ViewCompat.setAlpha(butPlaybackSpeed, controller.canSetPlaybackSpeed() ? 1.0f : 0.5f); butPlaybackSpeed.setVisibility(View.VISIBLE); + txtvPlaybackSpeed.setVisibility(View.VISIBLE); } @Override @@ -74,14 +76,15 @@ public class AudioplayerActivity extends MediaplayerInfoActivity { } if (controller == null) { butPlaybackSpeed.setVisibility(View.GONE); + txtvPlaybackSpeed.setVisibility(View.GONE); return; } float speed = 1.0f; if(controller.canSetPlaybackSpeed()) { speed = UserPreferences.getPlaybackSpeed(); } - String speedStr = new DecimalFormat("0.00x").format(speed); - butPlaybackSpeed.setText(speedStr); + String speedStr = new DecimalFormat("0.00").format(speed); + txtvPlaybackSpeed.setText(speedStr); } @Override @@ -136,6 +139,7 @@ public class AudioplayerActivity extends MediaplayerInfoActivity { return true; }); butPlaybackSpeed.setVisibility(View.VISIBLE); + txtvPlaybackSpeed.setVisibility(View.VISIBLE); } } } diff --git a/app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java new file mode 100644 index 000000000..666eacfa8 --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/activity/BugReportActivity.java @@ -0,0 +1,55 @@ +package de.danoeh.antennapod.activity; + +import android.content.ClipData; +import android.content.ClipboardManager; +import android.content.Context; +import android.os.Bundle; +import com.google.android.material.snackbar.Snackbar; +import androidx.appcompat.app.AppCompatActivity; +import android.widget.TextView; +import de.danoeh.antennapod.CrashReportWriter; +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.core.preferences.UserPreferences; +import de.danoeh.antennapod.core.util.IntentUtils; +import org.apache.commons.io.IOUtils; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.nio.charset.Charset; + +/** + * Displays the 'crash report' screen + */ +public class BugReportActivity extends AppCompatActivity { + + @Override + protected void onCreate(Bundle savedInstanceState) { + setTheme(UserPreferences.getTheme()); + super.onCreate(savedInstanceState); + getSupportActionBar().setDisplayShowHomeEnabled(true); + setContentView(R.layout.bug_report); + + TextView crashDetailsText = findViewById(R.id.crash_report_logs); + + try { + File crashFile = CrashReportWriter.getFile(); + String crashReportContent = IOUtils.toString(new FileInputStream(crashFile), Charset.forName("UTF-8")); + crashDetailsText.setText(crashReportContent); + } catch (IOException e) { + e.printStackTrace(); + crashDetailsText.setText("No crash report recorded\n" + CrashReportWriter.getSystemInfo()); + } + + findViewById(R.id.btn_open_bug_tracker).setOnClickListener(v -> { + IntentUtils.openInBrowser(BugReportActivity.this, "https://github.com/AntennaPod/AntennaPod/issues"); + }); + + findViewById(R.id.btn_copy_log).setOnClickListener(v -> { + ClipboardManager clipboard = (ClipboardManager) getSystemService(Context.CLIPBOARD_SERVICE); + ClipData clip = ClipData.newPlainText(getString(R.string.bug_report_title), crashDetailsText.getText()); + clipboard.setPrimaryClip(clip); + Snackbar.make(findViewById(android.R.id.content), R.string.copied_to_clipboard, Snackbar.LENGTH_SHORT).show(); + }); + } +} diff --git a/app/src/main/java/de/danoeh/antennapod/activity/CastplayerActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/CastplayerActivity.java index 871e9c279..c60c7b769 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/CastplayerActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/CastplayerActivity.java @@ -49,6 +49,7 @@ public class CastplayerActivity extends MediaplayerInfoActivity { super.setupGUI(); if (butPlaybackSpeed != null) { butPlaybackSpeed.setVisibility(View.GONE); + txtvPlaybackSpeed.setVisibility(View.GONE); } // if (butCastDisconnect != null) { // butCastDisconnect.setOnClickListener(v -> castManager.disconnect()); diff --git a/app/src/main/java/de/danoeh/antennapod/activity/DirectoryChooserActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/DirectoryChooserActivity.java index 33def125e..49ce954bc 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/DirectoryChooserActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/DirectoryChooserActivity.java @@ -5,9 +5,9 @@ import android.content.Intent; import android.os.Bundle; import android.os.Environment; import android.os.FileObserver; -import android.support.v4.app.NavUtils; -import android.support.v7.app.AlertDialog; -import android.support.v7.app.AppCompatActivity; +import androidx.core.app.NavUtils; +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.app.AppCompatActivity; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; diff --git a/app/src/main/java/de/danoeh/antennapod/activity/DownloadAuthenticationActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/DownloadAuthenticationActivity.java index 5e04d743d..08ebc6421 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/DownloadAuthenticationActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/DownloadAuthenticationActivity.java @@ -3,8 +3,8 @@ package de.danoeh.antennapod.activity; import android.app.Activity; import android.content.Intent; import android.os.Bundle; -import android.support.v7.app.ActionBar; -import android.support.v7.app.AppCompatActivity; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AppCompatActivity; import android.widget.Button; import android.widget.EditText; import android.widget.TextView; diff --git a/app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java deleted file mode 100644 index 26e360bd3..000000000 --- a/app/src/main/java/de/danoeh/antennapod/activity/FeedInfoActivity.java +++ /dev/null @@ -1,225 +0,0 @@ -package de.danoeh.antennapod.activity; - -import android.content.ClipData; -import android.content.Context; -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.TextUtils; -import android.util.Log; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; -import android.widget.ImageView; -import android.widget.TextView; -import android.widget.Toast; - -import com.bumptech.glide.Glide; -import com.bumptech.glide.request.RequestOptions; -import com.joanzapata.iconify.Iconify; - -import org.apache.commons.lang3.StringUtils; -import org.jsoup.Jsoup; -import org.jsoup.nodes.Document; - -import de.danoeh.antennapod.R; -import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator; -import de.danoeh.antennapod.core.feed.Feed; -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.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 io.reactivex.Maybe; -import io.reactivex.MaybeOnSubscribe; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.disposables.Disposable; -import io.reactivex.schedulers.Schedulers; - -/** - * Displays information about a feed. - */ -public class FeedInfoActivity extends AppCompatActivity { - - public static final String EXTRA_FEED_ID = "de.danoeh.antennapod.extra.feedId"; - private static final String TAG = "FeedInfoActivity"; - private Feed feed; - - private ImageView imgvCover; - private TextView txtvTitle; - private TextView txtvDescription; - private TextView lblLanguage; - private TextView txtvLanguage; - private TextView lblAuthor; - private TextView txtvAuthor; - private TextView txtvUrl; - - private Disposable disposable; - - - 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) FeedInfoActivity.this - .getSystemService(Context.CLIPBOARD_SERVICE); - cm.setPrimaryClip(clipData); - Toast t = Toast.makeText(FeedInfoActivity.this, R.string.copied_url_msg, Toast.LENGTH_SHORT); - t.show(); - } - } - }; - - @Override - protected void onCreate(Bundle savedInstanceState) { - setTheme(UserPreferences.getTheme()); - super.onCreate(savedInstanceState); - setContentView(R.layout.feedinfo); - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - long feedId = getIntent().getLongExtra(EXTRA_FEED_ID, -1); - - imgvCover = findViewById(R.id.imgvCover); - txtvTitle = findViewById(R.id.txtvTitle); - TextView txtvAuthorHeader = findViewById(R.id.txtvAuthor); - ImageView imgvBackground = findViewById(R.id.imgvBackground); - findViewById(R.id.butShowInfo).setVisibility(View.INVISIBLE); - findViewById(R.id.butShowSettings).setVisibility(View.INVISIBLE); - // https://github.com/bumptech/glide/issues/529 - imgvBackground.setColorFilter(new LightingColorFilter(0xff828282, 0x000000)); - - - txtvDescription = findViewById(R.id.txtvDescription); - lblLanguage = findViewById(R.id.lblLanguage); - txtvLanguage = findViewById(R.id.txtvLanguage); - lblAuthor = findViewById(R.id.lblAuthor); - txtvAuthor = findViewById(R.id.txtvDetailsAuthor); - txtvUrl = findViewById(R.id.txtvUrl); - - txtvUrl.setOnClickListener(copyUrlToClipboard); - - disposable = Maybe.create((MaybeOnSubscribe<Feed>) emitter -> { - Feed feed = DBReader.getFeed(feedId); - if (feed != null) { - emitter.onSuccess(feed); - } else { - emitter.onComplete(); - } - }) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(result -> { - feed = result; - Log.d(TAG, "Language is " + feed.getLanguage()); - Log.d(TAG, "Author is " + feed.getAuthor()); - Log.d(TAG, "URL is " + feed.getDownload_url()); - Glide.with(FeedInfoActivity.this) - .load(feed.getImageLocation()) - .apply(new RequestOptions() - .placeholder(R.color.light_gray) - .error(R.color.light_gray) - .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) - .fitCenter() - .dontAnimate()) - .into(imgvCover); - Glide.with(FeedInfoActivity.this) - .load(feed.getImageLocation()) - .apply(new RequestOptions() - .placeholder(R.color.image_readability_tint) - .error(R.color.image_readability_tint) - .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) - .transform(new FastBlurTransformation()) - .dontAnimate()) - .into(imgvBackground); - - txtvTitle.setText(feed.getTitle()); - - String description = feed.getDescription(); - if(description != null) { - if(Feed.TYPE_ATOM1.equals(feed.getType())) { - HtmlToPlainText formatter = new HtmlToPlainText(); - Document feedDescription = Jsoup.parse(feed.getDescription()); - description = StringUtils.trim(formatter.getPlainText(feedDescription)); - } - } else { - description = ""; - } - txtvDescription.setText(description); - - if (!TextUtils.isEmpty(feed.getAuthor())) { - txtvAuthor.setText(feed.getAuthor()); - txtvAuthorHeader.setText(feed.getAuthor()); - } else { - lblAuthor.setVisibility(View.GONE); - txtvAuthor.setVisibility(View.GONE); - } - if (!TextUtils.isEmpty(feed.getLanguage())) { - txtvLanguage.setText(LangUtils.getLanguageString(feed.getLanguage())); - } else { - lblLanguage.setVisibility(View.GONE); - txtvLanguage.setVisibility(View.GONE); - } - txtvUrl.setText(feed.getDownload_url() + " {fa-paperclip}"); - Iconify.addIcons(txtvUrl); - - supportInvalidateOptionsMenu(); - }, error -> { - Log.d(TAG, Log.getStackTraceString(error)); - finish(); - }, () -> { - Log.e(TAG, "Activity was started with invalid arguments"); - finish(); - }); - } - - @Override - public void onDestroy() { - super.onDestroy(); - if (disposable != null) { - disposable.dispose(); - } - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - super.onCreateOptionsMenu(menu); - MenuInflater inflater = getMenuInflater(); - inflater.inflate(R.menu.feedinfo, menu); - return true; - } - - @Override - public boolean onPrepareOptionsMenu(Menu menu) { - super.onPrepareOptionsMenu(menu); - menu.findItem(R.id.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); - } - } -} diff --git a/app/src/main/java/de/danoeh/antennapod/activity/FeedSettingsActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/FeedSettingsActivity.java deleted file mode 100644 index fbd19f88a..000000000 --- a/app/src/main/java/de/danoeh/antennapod/activity/FeedSettingsActivity.java +++ /dev/null @@ -1,130 +0,0 @@ -package de.danoeh.antennapod.activity; - -import android.arch.lifecycle.ViewModelProviders; -import android.graphics.LightingColorFilter; -import android.os.Bundle; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentTransaction; -import android.support.v7.app.AppCompatActivity; -import android.text.TextUtils; -import android.util.Log; -import android.view.MenuItem; -import android.view.View; -import android.widget.ImageView; -import android.widget.TextView; -import com.bumptech.glide.Glide; -import com.bumptech.glide.request.RequestOptions; -import de.danoeh.antennapod.R; -import de.danoeh.antennapod.core.feed.Feed; -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.fragment.FeedSettingsFragment; -import de.danoeh.antennapod.viewmodel.FeedSettingsViewModel; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.disposables.Disposable; -import io.reactivex.schedulers.Schedulers; - -/** - * Displays information about a feed. - */ -public class FeedSettingsActivity extends AppCompatActivity { - public static final String EXTRA_FEED_ID = "de.danoeh.antennapod.extra.feedId"; - private static final String TAG = "FeedSettingsActivity"; - private Feed feed; - private Disposable disposable; - private ImageView imgvCover; - private TextView txtvTitle; - private ImageView imgvBackground; - private TextView txtvAuthorHeader; - - @Override - protected void onCreate(Bundle savedInstanceState) { - setTheme(UserPreferences.getTheme()); - super.onCreate(savedInstanceState); - setContentView(R.layout.feedsettings); - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - - imgvCover = findViewById(R.id.imgvCover); - txtvTitle = findViewById(R.id.txtvTitle); - txtvAuthorHeader = findViewById(R.id.txtvAuthor); - imgvBackground = findViewById(R.id.imgvBackground); - findViewById(R.id.butShowInfo).setVisibility(View.INVISIBLE); - findViewById(R.id.butShowSettings).setVisibility(View.INVISIBLE); - // https://github.com/bumptech/glide/issues/529 - imgvBackground.setColorFilter(new LightingColorFilter(0xff828282, 0x000000)); - - long feedId = getIntent().getLongExtra(EXTRA_FEED_ID, -1); - disposable = ViewModelProviders.of(this).get(FeedSettingsViewModel.class).getFeed(feedId) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(result -> { - feed = result; - showFragment(); - showHeader(); - }, error -> { - Log.d(TAG, Log.getStackTraceString(error)); - finish(); - }, () -> { - Log.e(TAG, "Activity was started with invalid arguments"); - finish(); - }); - } - - private void showFragment() { - FeedSettingsFragment fragment = new FeedSettingsFragment(); - fragment.setArguments(getIntent().getExtras()); - - FragmentManager fragmentManager = getSupportFragmentManager(); - FragmentTransaction fragmentTransaction = - fragmentManager.beginTransaction(); - fragmentTransaction.replace(R.id.settings_fragment_container, fragment); - fragmentTransaction.commit(); - } - - private void showHeader() { - txtvTitle.setText(feed.getTitle()); - - if (!TextUtils.isEmpty(feed.getAuthor())) { - txtvAuthorHeader.setText(feed.getAuthor()); - } - - Glide.with(FeedSettingsActivity.this) - .load(feed.getImageLocation()) - .apply(new RequestOptions() - .placeholder(R.color.light_gray) - .error(R.color.light_gray) - .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) - .fitCenter() - .dontAnimate()) - .into(imgvCover); - Glide.with(FeedSettingsActivity.this) - .load(feed.getImageLocation()) - .apply(new RequestOptions() - .placeholder(R.color.image_readability_tint) - .error(R.color.image_readability_tint) - .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) - .transform(new FastBlurTransformation()) - .dontAnimate()) - .into(imgvBackground); - } - - @Override - public void onDestroy() { - super.onDestroy(); - if (disposable != null) { - disposable.dispose(); - } - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - switch (item.getItemId()) { - case android.R.id.home: - finish(); - return true; - default: - return super.onOptionsItemSelected(item); - } - } -} 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 9795c1240..f85a1cd77 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/ImportExportActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/ImportExportActivity.java @@ -7,10 +7,10 @@ import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.os.ParcelFileDescriptor; -import android.support.design.widget.Snackbar; -import android.support.v7.app.ActionBar; -import android.support.v7.app.AlertDialog; -import android.support.v7.app.AppCompatActivity; +import com.google.android.material.snackbar.Snackbar; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.app.AppCompatActivity; import android.util.Log; import android.view.MenuItem; 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 339ce01c2..6a38f8f0a 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java @@ -2,6 +2,7 @@ package de.danoeh.antennapod.activity; import android.annotation.TargetApi; import android.app.ProgressDialog; +import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; @@ -10,15 +11,16 @@ import android.database.DataSetObserver; import android.os.Build; import android.os.Bundle; import android.os.Handler; -import android.support.annotation.VisibleForTesting; -import android.support.design.widget.Snackbar; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentTransaction; -import android.support.v4.widget.DrawerLayout; -import android.support.v7.app.ActionBarDrawerToggle; -import android.support.v7.app.AlertDialog; -import android.support.v7.widget.Toolbar; +import androidx.annotation.NonNull; +import androidx.annotation.VisibleForTesting; +import com.google.android.material.snackbar.Snackbar; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentTransaction; +import androidx.drawerlayout.widget.DrawerLayout; +import androidx.appcompat.app.ActionBarDrawerToggle; +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.widget.Toolbar; import android.util.Log; import android.util.TypedValue; import android.view.ContextMenu; @@ -32,9 +34,11 @@ import android.widget.Toast; import com.bumptech.glide.Glide; -import de.danoeh.antennapod.preferences.PreferenceUpgrader; import org.apache.commons.lang3.ArrayUtils; import org.apache.commons.lang3.Validate; +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; import java.util.List; @@ -43,7 +47,6 @@ import de.danoeh.antennapod.adapter.NavListAdapter; import de.danoeh.antennapod.core.asynctask.FeedRemover; import de.danoeh.antennapod.core.dialog.ConfirmationDialog; import de.danoeh.antennapod.core.event.MessageEvent; -import de.danoeh.antennapod.core.event.ProgressEvent; import de.danoeh.antennapod.core.event.QueueEvent; import de.danoeh.antennapod.core.feed.EventDistributor; import de.danoeh.antennapod.core.feed.Feed; @@ -66,14 +69,13 @@ import de.danoeh.antennapod.fragment.FeedItemlistFragment; import de.danoeh.antennapod.fragment.PlaybackHistoryFragment; import de.danoeh.antennapod.fragment.QueueFragment; import de.danoeh.antennapod.fragment.SubscriptionFragment; +import de.danoeh.antennapod.fragment.TransitionEffect; import de.danoeh.antennapod.menuhandler.NavDrawerActivity; +import de.danoeh.antennapod.preferences.PreferenceUpgrader; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; -import org.greenrobot.eventbus.EventBus; -import org.greenrobot.eventbus.Subscribe; -import org.greenrobot.eventbus.ThreadMode; /** * The activity that is shown when the user launches the app. @@ -93,7 +95,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi public static final String EXTRA_NAV_INDEX = "nav_index"; public static final String EXTRA_FRAGMENT_TAG = "fragment_tag"; public static final String EXTRA_FRAGMENT_ARGS = "fragment_args"; - public static final String EXTRA_FEED_ID = "fragment_feed_id"; + private static final String EXTRA_FEED_ID = "fragment_feed_id"; private static final String SAVE_BACKSTACK_COUNT = "backstackCount"; private static final String SAVE_TITLE = "title"; @@ -127,6 +129,14 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi private long lastBackButtonPressTime = 0; + @NonNull + public static Intent getIntentToOpenFeed(@NonNull Context context, long feedId) { + Intent intent = new Intent(context.getApplicationContext(), MainActivity.class); + intent.putExtra(MainActivity.EXTRA_FEED_ID, feedId); + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + return intent; + } + @Override public void onCreate(Bundle savedInstanceState) { setTheme(UserPreferences.getNoTitleTheme()); @@ -240,7 +250,7 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi SharedPreferences.Editor edit = prefs.edit(); edit.putBoolean(PREF_IS_FIRST_LAUNCH, false); - edit.commit(); + edit.apply(); } } @@ -368,15 +378,34 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi } } - public void loadChildFragment(Fragment fragment) { + public void loadChildFragment(Fragment fragment, TransitionEffect transition) { Validate.notNull(fragment); - FragmentManager fm = getSupportFragmentManager(); - fm.beginTransaction() - .replace(R.id.main_view, fragment, "main") + FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); + + switch (transition) { + case FADE: + transaction.setCustomAnimations(R.anim.fade_in, R.anim.fade_out); + break; + case FLIP: + transaction.setCustomAnimations( + R.anim.card_flip_left_in, + R.anim.card_flip_left_out, + R.anim.card_flip_right_in, + R.anim.card_flip_right_out); + break; + } + + transaction + .hide(getSupportFragmentManager().findFragmentByTag("main")) + .add(R.id.main_view, fragment, "main") .addToBackStack(null) .commit(); } + public void loadChildFragment(Fragment fragment) { + loadChildFragment(fragment, TransitionEffect.NONE); + } + public void dismissChildFragment() { getSupportFragmentManager().popBackStack(); } @@ -778,25 +807,6 @@ public class MainActivity extends CastEnabledActivity implements NavDrawerActivi } @Subscribe(threadMode = ThreadMode.MAIN) - public void onEventMainThread(ProgressEvent event) { - Log.d(TAG, "onEvent(" + event + ")"); - switch(event.action) { - case START: - pd = new ProgressDialog(this); - pd.setMessage(event.message); - pd.setIndeterminate(true); - pd.setCancelable(false); - pd.show(); - break; - case END: - if(pd != null) { - pd.dismiss(); - } - break; - } - } - - @Subscribe(threadMode = ThreadMode.MAIN) public void onEventMainThread(MessageEvent event) { Log.d(TAG, "onEvent(" + event + ")"); View parentLayout = findViewById(R.id.drawer_layout); 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 52497a27f..566eea3f5 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerActivity.java @@ -9,14 +9,13 @@ import android.content.pm.PackageManager; import android.content.res.TypedArray; import android.graphics.Color; import android.graphics.PixelFormat; -import android.net.Uri; import android.os.Build; import android.os.Bundle; -import android.support.annotation.Nullable; -import android.support.v4.app.ActivityCompat; -import android.support.v4.app.ActivityOptionsCompat; -import android.support.v4.content.ContextCompat; -import android.support.v7.app.AlertDialog; +import androidx.annotation.Nullable; +import androidx.core.app.ActivityCompat; +import androidx.core.app.ActivityOptionsCompat; +import androidx.core.content.ContextCompat; +import androidx.appcompat.app.AlertDialog; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; @@ -34,6 +33,7 @@ import com.joanzapata.iconify.IconDrawable; import com.joanzapata.iconify.fonts.FontAwesomeIcons; import de.danoeh.antennapod.R; +import de.danoeh.antennapod.core.event.PlaybackPositionEvent; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.feed.MediaType; @@ -63,6 +63,9 @@ import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; /** @@ -194,6 +197,11 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements }; } + @Subscribe(threadMode = ThreadMode.MAIN) + public void onEventMainThread(PlaybackPositionEvent event) { + onPositionObserverUpdate(); + } + private static TextView getTxtvFFFromActivity(MediaplayerActivity activity) { return activity.txtvFF; } @@ -274,6 +282,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements controller.init(); loadMediaInfo(); onPositionObserverUpdate(); + EventBus.getDefault().register(this); } @Override @@ -286,6 +295,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements if (disposable != null) { disposable.dispose(); } + EventBus.getDefault().unregister(this); super.onStop(); } @@ -322,6 +332,8 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements Playable media = controller.getMedia(); boolean isFeedMedia = media != null && (media instanceof FeedMedia); + menu.findItem(R.id.open_feed_item).setVisible(isFeedMedia); // FeedMedia implies it belongs to a Feed + boolean hasWebsiteLink = ( getWebsiteLinkWithFallback(media) != null ); menu.findItem(R.id.visit_website_item).setVisible(hasWebsiteLink); @@ -389,29 +401,24 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements return true; } else { if (media != null) { + final @Nullable FeedItem feedItem = getFeedItem(media); // some options option requires FeedItem switch (item.getItemId()) { case R.id.add_to_favorites_item: - if(media instanceof FeedMedia) { - FeedItem feedItem = ((FeedMedia)media).getItem(); - if(feedItem != null) { - DBWriter.addFavoriteItem(feedItem); - isFavorite = true; - invalidateOptionsMenu(); - Toast.makeText(this, R.string.added_to_favorites, Toast.LENGTH_SHORT) - .show(); - } + if (feedItem != null) { + DBWriter.addFavoriteItem(feedItem); + isFavorite = true; + invalidateOptionsMenu(); + Toast.makeText(this, R.string.added_to_favorites, Toast.LENGTH_SHORT) + .show(); } break; case R.id.remove_from_favorites_item: - if(media instanceof FeedMedia) { - FeedItem feedItem = ((FeedMedia)media).getItem(); - if(feedItem != null) { - DBWriter.removeFavoriteItem(feedItem); - isFavorite = false; - invalidateOptionsMenu(); - Toast.makeText(this, R.string.removed_from_favorites, Toast.LENGTH_SHORT) - .show(); - } + if (feedItem != null) { + DBWriter.removeFavoriteItem(feedItem); + isFavorite = false; + invalidateOptionsMenu(); + Toast.makeText(this, R.string.removed_from_favorites, Toast.LENGTH_SHORT) + .show(); } break; case R.id.disable_sleeptimer_item: @@ -448,28 +455,33 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements PlaybackControlsDialog dialog = PlaybackControlsDialog.newInstance(isPlayingVideo); dialog.show(getSupportFragmentManager(), "playback_controls"); break; + case R.id.open_feed_item: + if (feedItem != null) { + Intent intent = MainActivity.getIntentToOpenFeed(this, feedItem.getFeedId()); + startActivity(intent); + } + break; case R.id.visit_website_item: - Uri uri = Uri.parse(getWebsiteLinkWithFallback(media)); - startActivity(new Intent(Intent.ACTION_VIEW, uri)); + IntentUtils.openInBrowser(MediaplayerActivity.this, getWebsiteLinkWithFallback(media)); break; case R.id.share_link_item: - if (media instanceof FeedMedia) { - ShareUtils.shareFeedItemLink(this, ((FeedMedia) media).getItem()); + if (feedItem != null) { + ShareUtils.shareFeedItemLink(this, feedItem); } break; case R.id.share_download_url_item: - if (media instanceof FeedMedia) { - ShareUtils.shareFeedItemDownloadLink(this, ((FeedMedia) media).getItem()); + if (feedItem != null) { + ShareUtils.shareFeedItemDownloadLink(this, feedItem); } break; case R.id.share_link_with_position_item: - if (media instanceof FeedMedia) { - ShareUtils.shareFeedItemLink(this, ((FeedMedia) media).getItem(), true); + if (feedItem != null) { + ShareUtils.shareFeedItemLink(this, feedItem, true); } break; case R.id.share_download_url_with_position_item: - if (media instanceof FeedMedia) { - ShareUtils.shareFeedItemDownloadLink(this, ((FeedMedia) media).getItem(), true); + if (feedItem != null) { + ShareUtils.shareFeedItemDownloadLink(this, feedItem, true); } break; case R.id.share_file: @@ -635,7 +647,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements } } - static public void showSkipPreference(Activity activity, SkipDirection direction) { + public static void showSkipPreference(Activity activity, SkipDirection direction) { int checked = 0; int skipSecs = direction.getPrefSkipSeconds(); final int[] values = activity.getResources().getIntArray(R.array.seek_delta_values); @@ -813,11 +825,7 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements } private void checkFavorite() { - Playable playable = controller.getMedia(); - if (!(playable instanceof FeedMedia)) { - return; - } - FeedItem feedItem = ((FeedMedia) playable).getItem(); + FeedItem feedItem = getFeedItem(controller.getMedia()); if (feedItem == null) { return; } @@ -872,4 +880,13 @@ public abstract class MediaplayerActivity extends CastEnabledActivity implements } } } + + @Nullable + private static FeedItem getFeedItem(@Nullable Playable playable) { + if ((playable != null) && (playable instanceof FeedMedia)) { + return ((FeedMedia)playable).getItem(); + } else { + return null; + } + } } diff --git a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java index 4fec1cfc5..016168b45 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/MediaplayerInfoActivity.java @@ -7,15 +7,15 @@ import android.content.SharedPreferences; import android.content.res.Configuration; import android.os.Build; import android.os.Bundle; -import android.support.design.widget.AppBarLayout; -import android.support.design.widget.Snackbar; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentStatePagerAdapter; -import android.support.v4.view.ViewPager; -import android.support.v4.widget.DrawerLayout; -import android.support.v7.app.ActionBarDrawerToggle; -import android.support.v7.widget.Toolbar; +import com.google.android.material.appbar.AppBarLayout; +import com.google.android.material.snackbar.Snackbar; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentStatePagerAdapter; +import androidx.viewpager.widget.ViewPager; +import androidx.drawerlayout.widget.DrawerLayout; +import androidx.appcompat.app.ActionBarDrawerToggle; +import androidx.appcompat.widget.Toolbar; import android.util.Log; import android.util.TypedValue; import android.view.ContextMenu; @@ -23,9 +23,9 @@ import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.widget.AdapterView; -import android.widget.Button; import android.widget.ImageButton; import android.widget.ListView; +import android.widget.TextView; import android.widget.Toast; import com.viewpagerindicator.CirclePageIndicator; @@ -63,7 +63,6 @@ import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; -import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; @@ -92,7 +91,8 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem NavListAdapter.SUBSCRIPTION_LIST_TAG }; - Button butPlaybackSpeed; + ImageButton butPlaybackSpeed; + TextView txtvPlaybackSpeed; ImageButton butCastDisconnect; private DrawerLayout drawerLayout; private NavListAdapter navAdapter; @@ -120,7 +120,6 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem disposable.dispose(); } EventDistributor.getInstance().unregister(contentUpdate); - EventBus.getDefault().unregister(this); saveCurrentFragment(); } @@ -173,7 +172,6 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem protected void onStart() { super.onStart(); EventDistributor.getInstance().register(contentUpdate); - EventBus.getDefault().register(this); loadData(); } @@ -258,6 +256,7 @@ public abstract class MediaplayerInfoActivity extends MediaplayerActivity implem }); butPlaybackSpeed = findViewById(R.id.butPlaybackSpeed); + txtvPlaybackSpeed = findViewById(R.id.txtvPlaybackSpeed); butCastDisconnect = findViewById(R.id.butCastDisconnect); pager = findViewById(R.id.pager); diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java index ea7687bc9..39715495a 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java @@ -7,12 +7,12 @@ import android.content.Intent; import android.graphics.LightingColorFilter; import android.os.Build; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.UiThread; -import android.support.v4.app.NavUtils; -import android.support.v7.app.ActionBar; -import android.support.v7.app.AlertDialog; -import android.support.v7.app.AppCompatActivity; +import androidx.annotation.NonNull; +import androidx.annotation.UiThread; +import androidx.core.app.NavUtils; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.app.AppCompatActivity; import android.text.TextUtils; import android.util.Log; import android.view.LayoutInflater; @@ -32,7 +32,6 @@ import android.widget.TextView; import com.bumptech.glide.Glide; import com.bumptech.glide.request.RequestOptions; -import de.danoeh.antennapod.core.glide.FastBlurTransformation; import org.apache.commons.lang3.StringUtils; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; @@ -55,6 +54,7 @@ import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.feed.FeedItem; 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.service.download.DownloadRequest; import de.danoeh.antennapod.core.service.download.DownloadStatus; @@ -442,11 +442,9 @@ public class OnlineFeedViewActivity extends AppCompatActivity { subscribeButton.setOnClickListener(v -> { if(feedInFeedlist(feed)) { - Intent intent = new Intent(OnlineFeedViewActivity.this, MainActivity.class); // feed.getId() is always 0, we have to retrieve the id from the feed list from // the database - intent.putExtra(MainActivity.EXTRA_FEED_ID, getFeedId(feed)); - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); + Intent intent = MainActivity.getIntentToOpenFeed(this, getFeedId(feed)); startActivity(intent); } else { Feed f = new Feed(selectedDownloadUrl, null, feed.getTitle()); diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OpmlFeedChooserActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OpmlFeedChooserActivity.java index 72759c59c..376074525 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/OpmlFeedChooserActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/OpmlFeedChooserActivity.java @@ -2,7 +2,7 @@ package de.danoeh.antennapod.activity; import android.content.Intent; import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; +import androidx.appcompat.app.AppCompatActivity; import android.util.SparseBooleanArray; import android.view.Menu; import android.view.MenuInflater; diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportBaseActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportBaseActivity.java index c04ae051e..9caff0fc0 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportBaseActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportBaseActivity.java @@ -5,9 +5,9 @@ import android.content.pm.PackageManager; import android.net.Uri; import android.os.Build; import android.os.Environment; -import android.support.annotation.Nullable; -import android.support.v4.app.ActivityCompat; -import android.support.v7.app.AppCompatActivity; +import androidx.annotation.Nullable; +import androidx.core.app.ActivityCompat; +import androidx.appcompat.app.AppCompatActivity; import android.util.Log; import com.afollestad.materialdialogs.MaterialDialog; 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 7e0ae173f..a0f9bf6d8 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/PreferenceActivity.java @@ -1,9 +1,9 @@ package de.danoeh.antennapod.activity; import android.os.Bundle; -import android.support.v7.app.ActionBar; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.preference.PreferenceFragmentCompat; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AppCompatActivity; +import androidx.preference.PreferenceFragmentCompat; import android.view.Menu; import android.view.MenuItem; import android.view.ViewGroup; diff --git a/app/src/main/java/de/danoeh/antennapod/activity/SplashActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/SplashActivity.java index 52102eee1..bd1ccaea4 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/SplashActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/SplashActivity.java @@ -5,9 +5,9 @@ import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Bundle; -import android.support.annotation.Nullable; -import android.support.v4.graphics.drawable.DrawableCompat; -import android.support.v7.app.AppCompatActivity; +import androidx.annotation.Nullable; +import androidx.core.graphics.drawable.DrawableCompat; +import androidx.appcompat.app.AppCompatActivity; import android.widget.ProgressBar; import de.danoeh.antennapod.R; diff --git a/app/src/main/java/de/danoeh/antennapod/activity/StorageErrorActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/StorageErrorActivity.java index 20e34cc52..c9c9a0e2c 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/StorageErrorActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/StorageErrorActivity.java @@ -9,9 +9,9 @@ import android.content.IntentFilter; import android.content.pm.PackageManager; import android.os.Build; import android.os.Bundle; -import android.support.v4.app.ActivityCompat; -import android.support.v7.app.AlertDialog; -import android.support.v7.app.AppCompatActivity; +import androidx.core.app.ActivityCompat; +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.app.AppCompatActivity; import android.text.TextUtils; import android.util.Log; import android.widget.Button; 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 78cc15b2c..2d28ea561 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/VideoplayerActivity.java @@ -6,8 +6,8 @@ import android.graphics.drawable.ColorDrawable; import android.os.Build; import android.os.Bundle; import android.os.Handler; -import android.support.v4.view.WindowCompat; -import android.support.v7.app.ActionBar; +import androidx.core.view.WindowCompat; +import androidx.appcompat.app.ActionBar; import android.text.TextUtils; import android.util.Log; import android.util.Pair; diff --git a/app/src/main/java/de/danoeh/antennapod/activity/gpoddernet/GpodnetAuthenticationActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/gpoddernet/GpodnetAuthenticationActivity.java index 2d7898d5b..c79c611ce 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/gpoddernet/GpodnetAuthenticationActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/gpoddernet/GpodnetAuthenticationActivity.java @@ -2,11 +2,10 @@ package de.danoeh.antennapod.activity.gpoddernet; import android.content.Context; import android.content.Intent; -import android.content.res.Configuration; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; +import androidx.appcompat.app.AppCompatActivity; import android.util.Log; import android.view.LayoutInflater; import android.view.MenuItem; diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java index 2eff33339..5b735cd1f 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/AllEpisodesRecycleAdapter.java @@ -1,10 +1,11 @@ package de.danoeh.antennapod.adapter; import android.os.Build; -import android.support.annotation.Nullable; -import android.support.v4.content.ContextCompat; -import android.support.v7.widget.RecyclerView; -import android.support.v7.widget.helper.ItemTouchHelper; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.content.ContextCompat; +import androidx.recyclerview.widget.RecyclerView; +import androidx.recyclerview.widget.ItemTouchHelper; import android.text.Layout; import android.util.Log; import android.view.ContextMenu; @@ -23,10 +24,12 @@ import android.widget.TextView; import com.joanzapata.iconify.Iconify; import java.lang.ref.WeakReference; +import java.util.List; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.adapter.actionbutton.ItemActionButton; +import de.danoeh.antennapod.core.event.PlaybackPositionEvent; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.storage.DownloadRequester; @@ -50,6 +53,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR private final boolean showOnlyNewEpisodes; private FeedItem selectedItem; + private Holder currentlyPlayingItem = null; private final int playingBackGroundColor; private final int normalBackGroundColor; @@ -165,8 +169,9 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR holder.progress.setVisibility(View.INVISIBLE); } - if(media.isCurrentlyPlaying()) { + if (media.isCurrentlyPlaying()) { holder.container.setBackgroundColor(playingBackGroundColor); + currentlyPlayingItem = holder; } else { holder.container.setBackgroundColor(normalBackGroundColor); } @@ -196,6 +201,22 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR .load(); } + @Override + public void onBindViewHolder(@NonNull Holder holder, int pos, List<Object> payload) { + onBindViewHolder(holder, pos); + + if (holder == currentlyPlayingItem && payload.size() == 1 && payload.get(0) instanceof PlaybackPositionEvent) { + PlaybackPositionEvent event = (PlaybackPositionEvent) payload.get(0); + holder.progress.setProgress((int) (100.0 * event.getPosition() / event.getDuration())); + } + } + + public void notifyCurrentlyPlayingItemChanged(PlaybackPositionEvent event) { + if (currentlyPlayingItem != null && currentlyPlayingItem.getAdapterPosition() != RecyclerView.NO_POSITION) { + notifyItemChanged(currentlyPlayingItem.getAdapterPosition(), event); + } + } + @Nullable public FeedItem getSelectedItem() { return selectedItem; @@ -262,7 +283,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR FeedItem item = itemAccess.getItem(getAdapterPosition()); MenuInflater inflater = mainActivityRef.get().getMenuInflater(); - inflater.inflate(R.menu.allepisodes_context, menu); + inflater.inflate(R.menu.feeditemlist_context, menu); if (item != null) { menu.setHeaderTitle(item.getTitle()); @@ -277,9 +298,7 @@ public class AllEpisodesRecycleAdapter extends RecyclerView.Adapter<AllEpisodesR item1.setVisible(visible); } }; - FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item, true, null); - - contextMenuInterface.setItemVisibility(R.id.remove_new_flag_item, item.isNew()); + FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item); } } diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java index c3fac7e18..f6e6da8b4 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/ChaptersListAdapter.java @@ -1,8 +1,8 @@ package de.danoeh.antennapod.adapter; import android.content.Context; -import android.support.annotation.NonNull; -import android.support.v4.content.ContextCompat; +import androidx.annotation.NonNull; +import androidx.core.content.ContextCompat; import android.text.Layout; import android.text.Selection; import android.text.Spannable; diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/CoverLoader.java b/app/src/main/java/de/danoeh/antennapod/adapter/CoverLoader.java index 33f925e3f..098e9a616 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/CoverLoader.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/CoverLoader.java @@ -1,9 +1,8 @@ package de.danoeh.antennapod.adapter; import android.graphics.drawable.Drawable; -import android.os.Handler; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import android.view.View; import android.widget.ImageView; import android.widget.TextView; diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DataFolderAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/DataFolderAdapter.java index 74bc84878..9014de525 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/DataFolderAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/DataFolderAdapter.java @@ -2,9 +2,9 @@ package de.danoeh.antennapod.adapter; import android.app.Dialog; import android.content.Context; -import android.support.annotation.NonNull; -import android.support.v4.content.ContextCompat; -import android.support.v7.widget.RecyclerView; +import androidx.annotation.NonNull; +import androidx.core.content.ContextCompat; +import androidx.recyclerview.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java index 789c01a26..b8764c2ae 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java @@ -2,7 +2,7 @@ package de.danoeh.antennapod.adapter; import android.content.Context; import android.os.Build; -import android.support.v4.content.ContextCompat; +import androidx.core.content.ContextCompat; import android.text.Layout; import android.text.format.DateUtils; import android.util.Log; diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java index d090bc4b1..aec0f0c91 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistAdapter.java @@ -3,7 +3,7 @@ package de.danoeh.antennapod.adapter; import android.content.Context; import android.content.res.TypedArray; import android.os.Build; -import android.support.v4.content.ContextCompat; +import androidx.core.content.ContextCompat; import android.text.Layout; import android.view.LayoutInflater; import android.view.View; @@ -13,15 +13,18 @@ import android.widget.BaseAdapter; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; +import android.widget.ListView; import android.widget.ProgressBar; import android.widget.TextView; import de.danoeh.antennapod.R; import de.danoeh.antennapod.adapter.actionbutton.ItemActionButton; +import de.danoeh.antennapod.core.event.PlaybackPositionEvent; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.feed.MediaType; import de.danoeh.antennapod.core.storage.DownloadRequester; +import de.danoeh.antennapod.core.util.Converter; import de.danoeh.antennapod.core.util.DateUtils; import de.danoeh.antennapod.core.util.LongList; import de.danoeh.antennapod.core.util.ThemeUtils; @@ -39,6 +42,8 @@ public class FeedItemlistAdapter extends BaseAdapter { private final int playingBackGroundColor; private final int normalBackGroundColor; + private int currentlyPlayingItem = -1; + public FeedItemlistAdapter(Context context, ItemAccess itemAccess, boolean showFeedtitle, @@ -176,8 +181,9 @@ public class FeedItemlistAdapter extends BaseAdapter { } typeDrawables.recycle(); - if(media.isCurrentlyPlaying()) { + if (media.isCurrentlyPlaying()) { holder.container.setBackgroundColor(playingBackGroundColor); + currentlyPlayingItem = position; } else { holder.container.setBackgroundColor(normalBackGroundColor); } @@ -195,6 +201,20 @@ public class FeedItemlistAdapter extends BaseAdapter { return convertView; } + public void notifyCurrentlyPlayingItemChanged(PlaybackPositionEvent event, ListView listView) { + if (currentlyPlayingItem != -1 && currentlyPlayingItem < getCount()) { + View view = listView.getChildAt(currentlyPlayingItem + - listView.getFirstVisiblePosition() + listView.getHeaderViewsCount()); + if (view == null) { + return; + } + Holder holder = (Holder) view.getTag(); + holder.episodeProgress.setVisibility(View.VISIBLE); + holder.episodeProgress.setProgress((int) (100.0 * event.getPosition() / event.getDuration())); + holder.lenSize.setText(Converter.getDurationStringLong(event.getDuration() - event.getPosition())); + } + } + static class Holder { LinearLayout container; TextView title; diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java index c10bb7638..8d469c7a6 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/FeedItemlistDescriptionAdapter.java @@ -1,6 +1,7 @@ package de.danoeh.antennapod.adapter; import android.content.Context; +import android.os.Build; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -51,6 +52,17 @@ public class FeedItemlistDescriptionAdapter extends ArrayAdapter<FeedItem> { .replaceAll("\\s+", " ") .trim(); holder.description.setText(description); + + final int MAX_LINES_COLLAPSED = 3; + holder.description.setMaxLines(MAX_LINES_COLLAPSED); + holder.description.setOnClickListener(v -> { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN + && holder.description.getMaxLines() > MAX_LINES_COLLAPSED) { + holder.description.setMaxLines(MAX_LINES_COLLAPSED); + } else { + holder.description.setMaxLines(2000); + } + }); } return convertView; } diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java index be8e52cfc..50b11a15b 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/NavListAdapter.java @@ -7,7 +7,7 @@ import android.content.res.TypedArray; import android.graphics.Typeface; import android.graphics.drawable.Drawable; import android.preference.PreferenceManager; -import android.support.v7.app.AlertDialog; +import androidx.appcompat.app.AlertDialog; import android.util.TypedValue; import android.view.LayoutInflater; import android.view.View; diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java index 382abfb32..fcdcb4ba6 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/QueueRecyclerAdapter.java @@ -1,12 +1,12 @@ package de.danoeh.antennapod.adapter; -import android.content.Context; import android.os.Build; -import android.support.annotation.Nullable; -import android.support.v4.content.ContextCompat; -import android.support.v4.view.MotionEventCompat; -import android.support.v7.widget.RecyclerView; -import android.support.v7.widget.helper.ItemTouchHelper; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.content.ContextCompat; +import androidx.core.view.MotionEventCompat; +import androidx.recyclerview.widget.RecyclerView; +import androidx.recyclerview.widget.ItemTouchHelper; import android.text.Layout; import android.text.TextUtils; import android.util.Log; @@ -25,9 +25,11 @@ import android.widget.TextView; import com.joanzapata.iconify.Iconify; +import de.danoeh.antennapod.core.event.PlaybackPositionEvent; import org.apache.commons.lang3.ArrayUtils; import java.lang.ref.WeakReference; +import java.util.List; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; @@ -58,6 +60,7 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap private boolean locked; private FeedItem selectedItem; + private ViewHolder currentlyPlayingItem = null; private final int playingBackGroundColor; private final int normalBackGroundColor; @@ -94,6 +97,18 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap }); } + @Override + public void onBindViewHolder(@NonNull ViewHolder holder, int pos, List<Object> payload) { + onBindViewHolder(holder, pos); + + if (holder == currentlyPlayingItem && payload.size() == 1 && payload.get(0) instanceof PlaybackPositionEvent) { + PlaybackPositionEvent event = (PlaybackPositionEvent) payload.get(0); + holder.progressBar.setProgress((int) (100.0 * event.getPosition() / event.getDuration())); + holder.progressLeft.setText(Converter.getDurationStringLong(event.getPosition())); + holder.progressRight.setText(Converter.getDurationStringLong(event.getDuration())); + } + } + @Nullable public FeedItem getSelectedItem() { return selectedItem; @@ -109,6 +124,12 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap return itemAccess.getCount(); } + public void notifyCurrentlyPlayingItemChanged(PlaybackPositionEvent event) { + if (currentlyPlayingItem != null && currentlyPlayingItem.getAdapterPosition() != RecyclerView.NO_POSITION) { + notifyItemChanged(currentlyPlayingItem.getAdapterPosition(), event); + } + } + public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnCreateContextMenuListener, @@ -169,7 +190,8 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap FeedItem item = itemAccess.getItem(getAdapterPosition()); MenuInflater inflater = mainActivity.get().getMenuInflater(); - inflater.inflate(R.menu.queue_context, menu); + inflater.inflate(R.menu.queue_context, menu); // queue-specific menu items + inflater.inflate(R.menu.feeditemlist_context, menu); // generic menu items for item feeds if (item != null) { menu.setHeaderTitle(item.getTitle()); @@ -184,7 +206,18 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap item1.setVisible(visible); } }; - FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item, true, itemAccess.getQueueIds()); + + FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item, + R.id.skip_episode_item); // Skip Episode is not useful in Queue, so hide it. + // Queue-specific menu preparation + final boolean keepSorted = UserPreferences.isQueueKeepSorted(); + final LongList queueAccess = itemAccess.getQueueIds(); + if (queueAccess.size() == 0 || queueAccess.get(0) == item.getId() || keepSorted) { + contextMenuInterface.setItemVisibility(R.id.move_to_top_item, false); + } + if (queueAccess.size() == 0 || queueAccess.get(queueAccess.size()-1) == item.getId() || keepSorted) { + contextMenuInterface.setItemVisibility(R.id.move_to_bottom_item, false); + } } @Override @@ -276,6 +309,7 @@ public class QueueRecyclerAdapter extends RecyclerView.Adapter<QueueRecyclerAdap if(media.isCurrentlyPlaying()) { container.setBackgroundColor(playingBackGroundColor); + currentlyPlayingItem = this; } else { container.setBackgroundColor(normalBackGroundColor); } diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/StatisticsListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/StatisticsListAdapter.java index 31e82dbe0..f013f2a49 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/StatisticsListAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/StatisticsListAdapter.java @@ -1,31 +1,30 @@ package de.danoeh.antennapod.adapter; import android.content.Context; +import androidx.annotation.NonNull; +import androidx.appcompat.app.AlertDialog; +import androidx.recyclerview.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.widget.BaseAdapter; import android.widget.ImageView; import android.widget.TextView; - import com.bumptech.glide.Glide; - -import java.util.ArrayList; -import java.util.List; - import com.bumptech.glide.request.RequestOptions; import de.danoeh.antennapod.R; -import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.glide.ApGlideSettings; import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.util.Converter; +import de.danoeh.antennapod.view.PieChartView; /** * Adapter for the statistics list */ -public class StatisticsListAdapter extends BaseAdapter { +public class StatisticsListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> { + private static final int TYPE_HEADER = 0; + private static final int TYPE_FEED = 1; private final Context context; - private List<DBReader.StatisticsItem> feedTime = new ArrayList<>(); + private DBReader.StatisticsData statisticsData; private boolean countAll = true; public StatisticsListAdapter(Context context) { @@ -37,66 +36,102 @@ public class StatisticsListAdapter extends BaseAdapter { } @Override - public int getCount() { - return feedTime.size(); + public int getItemCount() { + return statisticsData.feedTime.size() + 1; } - @Override public DBReader.StatisticsItem getItem(int position) { - return feedTime.get(position); + if (position == 0) { + return null; + } + return statisticsData.feedTime.get(position - 1); } @Override - public long getItemId(int position) { - return feedTime.get(position).feed.getId(); + public int getItemViewType(int position) { + return position == 0 ? TYPE_HEADER : TYPE_FEED; } + @NonNull @Override - public View getView(int position, View convertView, ViewGroup parent) { - StatisticsHolder holder; - Feed feed = feedTime.get(position).feed; - - if (convertView == null) { - holder = new StatisticsHolder(); - LayoutInflater inflater = (LayoutInflater) context - .getSystemService(Context.LAYOUT_INFLATER_SERVICE); - - convertView = inflater.inflate(R.layout.statistics_listitem, parent, false); + public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + LayoutInflater inflater = LayoutInflater.from(context); + if (viewType == TYPE_HEADER) { + return new HeaderHolder(inflater.inflate(R.layout.statistics_listitem_total_time, parent, false)); + } + return new StatisticsHolder(inflater.inflate(R.layout.statistics_listitem, parent, false)); + } - holder.image = convertView.findViewById(R.id.imgvCover); - holder.title = convertView.findViewById(R.id.txtvTitle); - holder.time = convertView.findViewById(R.id.txtvTime); - convertView.setTag(holder); + @Override + public void onBindViewHolder(@NonNull RecyclerView.ViewHolder h, int position) { + if (getItemViewType(position) == TYPE_HEADER) { + HeaderHolder holder = (HeaderHolder) h; + long time = countAll ? statisticsData.totalTimeCountAll : statisticsData.totalTime; + holder.totalTime.setText(Converter.shortLocalizedDuration(context, time)); + float[] dataValues = new float[statisticsData.feedTime.size()]; + for (int i = 0; i < statisticsData.feedTime.size(); i++) { + DBReader.StatisticsItem item = statisticsData.feedTime.get(i); + dataValues[i] = countAll ? item.timePlayedCountAll : item.timePlayed; + } + holder.pieChart.setData(dataValues); } else { - holder = (StatisticsHolder) convertView.getTag(); + StatisticsHolder holder = (StatisticsHolder) h; + DBReader.StatisticsItem statsItem = statisticsData.feedTime.get(position - 1); + Glide.with(context) + .load(statsItem.feed.getImageLocation()) + .apply(new RequestOptions() + .placeholder(R.color.light_gray) + .error(R.color.light_gray) + .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) + .fitCenter() + .dontAnimate()) + .into(holder.image); + + holder.title.setText(statsItem.feed.getTitle()); + long time = countAll ? statsItem.timePlayedCountAll : statsItem.timePlayed; + holder.time.setText(Converter.shortLocalizedDuration(context, time)); + + holder.itemView.setOnClickListener(v -> { + AlertDialog.Builder dialog = new AlertDialog.Builder(context); + dialog.setTitle(statsItem.feed.getTitle()); + dialog.setMessage(context.getString(R.string.statistics_details_dialog, + countAll ? statsItem.episodesStartedIncludingMarked : statsItem.episodesStarted, + statsItem.episodes, Converter.shortLocalizedDuration(context, + countAll ? statsItem.timePlayedCountAll : statsItem.timePlayed), + Converter.shortLocalizedDuration(context, statsItem.time))); + dialog.setPositiveButton(android.R.string.ok, null); + dialog.show(); + }); } - - Glide.with(context) - .load(feed.getImageLocation()) - .apply(new RequestOptions() - .placeholder(R.color.light_gray) - .error(R.color.light_gray) - .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) - .fitCenter() - .dontAnimate()) - .into(holder.image); - - holder.title.setText(feed.getTitle()); - holder.time.setText(Converter.shortLocalizedDuration(context, - countAll ? feedTime.get(position).timePlayedCountAll - : feedTime.get(position).timePlayed)); - return convertView; } - public void update(List<DBReader.StatisticsItem> feedTime) { - this.feedTime = feedTime; + public void update(DBReader.StatisticsData statistics) { + this.statisticsData = statistics; notifyDataSetChanged(); } - static class StatisticsHolder { + static class HeaderHolder extends RecyclerView.ViewHolder { + TextView totalTime; + PieChartView pieChart; + + HeaderHolder(View itemView) { + super(itemView); + totalTime = itemView.findViewById(R.id.total_time); + pieChart = itemView.findViewById(R.id.pie_chart); + } + } + + static class StatisticsHolder extends RecyclerView.ViewHolder { ImageView image; TextView title; TextView time; + + StatisticsHolder(View itemView) { + super(itemView); + image = itemView.findViewById(R.id.imgvCover); + title = itemView.findViewById(R.id.txtvTitle); + time = itemView.findViewById(R.id.txtvTime); + } } } diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java index e0fb65c61..230b7ee31 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsAdapter.java @@ -1,7 +1,7 @@ package de.danoeh.antennapod.adapter; import android.content.Context; -import android.support.v4.app.Fragment; +import androidx.fragment.app.Fragment; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/AddToQueueActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/AddToQueueActionButton.java index 3299db3ab..a8001eeb1 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/AddToQueueActionButton.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/AddToQueueActionButton.java @@ -1,8 +1,8 @@ package de.danoeh.antennapod.adapter.actionbutton; import android.content.Context; -import android.support.annotation.AttrRes; -import android.support.annotation.StringRes; +import androidx.annotation.AttrRes; +import androidx.annotation.StringRes; import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.feed.FeedItem; diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/CancelDownloadActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/CancelDownloadActionButton.java index 1275a799b..10458ed46 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/CancelDownloadActionButton.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/CancelDownloadActionButton.java @@ -1,8 +1,8 @@ package de.danoeh.antennapod.adapter.actionbutton; import android.content.Context; -import android.support.annotation.AttrRes; -import android.support.annotation.StringRes; +import androidx.annotation.AttrRes; +import androidx.annotation.StringRes; import android.widget.Toast; import de.danoeh.antennapod.R; diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DownloadActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DownloadActionButton.java index c1559528e..55ca5471b 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DownloadActionButton.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/DownloadActionButton.java @@ -1,9 +1,9 @@ package de.danoeh.antennapod.adapter.actionbutton; import android.content.Context; -import android.support.annotation.AttrRes; -import android.support.annotation.NonNull; -import android.support.annotation.StringRes; +import androidx.annotation.AttrRes; +import androidx.annotation.NonNull; +import androidx.annotation.StringRes; import android.widget.Toast; import de.danoeh.antennapod.R; diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java index da5ebf6e1..31e9fccb5 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/ItemActionButton.java @@ -2,9 +2,9 @@ package de.danoeh.antennapod.adapter.actionbutton; import android.content.Context; import android.content.res.TypedArray; -import android.support.annotation.AttrRes; -import android.support.annotation.NonNull; -import android.support.annotation.StringRes; +import androidx.annotation.AttrRes; +import androidx.annotation.NonNull; +import androidx.annotation.StringRes; import android.view.View; import android.widget.ImageButton; @@ -20,12 +20,12 @@ public abstract class ItemActionButton { } @StringRes - abstract public int getLabel(); + public abstract int getLabel(); @AttrRes - abstract public int getDrawable(); + public abstract int getDrawable(); - abstract public void onClick(Context context); + public abstract void onClick(Context context); public int getVisibility() { return View.VISIBLE; diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MarkAsPlayedActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MarkAsPlayedActionButton.java index 4d906cee5..354ded73d 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MarkAsPlayedActionButton.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/MarkAsPlayedActionButton.java @@ -1,8 +1,8 @@ package de.danoeh.antennapod.adapter.actionbutton; import android.content.Context; -import android.support.annotation.AttrRes; -import android.support.annotation.StringRes; +import androidx.annotation.AttrRes; +import androidx.annotation.StringRes; import android.view.View; import de.danoeh.antennapod.R; diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/PlayActionButton.java b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/PlayActionButton.java index 3992c7240..23a7e03ad 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/PlayActionButton.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/actionbutton/PlayActionButton.java @@ -1,8 +1,8 @@ package de.danoeh.antennapod.adapter.actionbutton; import android.content.Context; -import android.support.annotation.AttrRes; -import android.support.annotation.StringRes; +import androidx.annotation.AttrRes; +import androidx.annotation.StringRes; import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.feed.FeedItem; diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java index f5213e4ab..cc3b6fba0 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/itunes/ItunesAdapter.java @@ -1,7 +1,7 @@ package de.danoeh.antennapod.adapter.itunes; import android.content.Context; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import android.view.View; import android.view.ViewGroup; import android.widget.ArrayAdapter; diff --git a/app/src/main/java/de/danoeh/antennapod/asynctask/DocumentFileExportWorker.java b/app/src/main/java/de/danoeh/antennapod/asynctask/DocumentFileExportWorker.java new file mode 100644 index 000000000..339a98dfa --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/asynctask/DocumentFileExportWorker.java @@ -0,0 +1,72 @@ +package de.danoeh.antennapod.asynctask; + +import android.content.Context; +import android.net.Uri; +import androidx.annotation.NonNull; +import androidx.documentfile.provider.DocumentFile; + +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.OutputStream; +import java.io.OutputStreamWriter; + +import de.danoeh.antennapod.core.export.ExportWriter; +import de.danoeh.antennapod.core.storage.DBReader; +import de.danoeh.antennapod.core.util.LangUtils; +import io.reactivex.Observable; + +/** + * Writes an OPML file into the user selected export directory in the background. + */ +public class DocumentFileExportWorker { + + private final @NonNull ExportWriter exportWriter; + private @NonNull Context context; + private @NonNull Uri outputFileUri; + + public DocumentFileExportWorker(@NonNull ExportWriter exportWriter, @NonNull Context context, @NonNull Uri outputFileUri) { + this.exportWriter = exportWriter; + this.context = context; + this.outputFileUri = outputFileUri; + } + + public Observable<DocumentFile> exportObservable() { + DocumentFile output = DocumentFile.fromSingleUri(context, outputFileUri); + return Observable.create(subscriber -> { + OutputStream outputStream = null; + OutputStreamWriter writer = null; + try { + Uri uri = output.getUri(); + if (uri == null) { + throw new FileNotFoundException("Export file not found."); + } + outputStream = context.getContentResolver().openOutputStream(uri); + if (outputStream == null) { + throw new IOException(); + } + writer = new OutputStreamWriter(outputStream, LangUtils.UTF_8); + exportWriter.writeDocument(DBReader.getFeedList(), writer); + subscriber.onNext(output); + } catch (IOException e) { + subscriber.onError(e); + } finally { + if (writer != null) { + try { + writer.close(); + } catch (IOException e) { + subscriber.onError(e); + } + } + if (outputStream != null) { + try { + outputStream.close(); + } catch (IOException e) { + subscriber.onError(e); + } + } + subscriber.onComplete(); + } + }); + } + +} diff --git a/app/src/main/java/de/danoeh/antennapod/asynctask/ExportWorker.java b/app/src/main/java/de/danoeh/antennapod/asynctask/ExportWorker.java index 219725b01..40b101ddf 100644 --- a/app/src/main/java/de/danoeh/antennapod/asynctask/ExportWorker.java +++ b/app/src/main/java/de/danoeh/antennapod/asynctask/ExportWorker.java @@ -1,6 +1,6 @@ package de.danoeh.antennapod.asynctask; -import android.support.annotation.NonNull; +import androidx.annotation.NonNull; import android.util.Log; import java.io.File; diff --git a/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlImportWorker.java b/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlImportWorker.java index 13b95907f..b88b58537 100644 --- a/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlImportWorker.java +++ b/app/src/main/java/de/danoeh/antennapod/asynctask/OpmlImportWorker.java @@ -3,7 +3,7 @@ package de.danoeh.antennapod.asynctask; import android.app.ProgressDialog; import android.content.Context; import android.os.AsyncTask; -import android.support.v7.app.AlertDialog; +import androidx.appcompat.app.AlertDialog; import android.util.Log; import org.xmlpull.v1.XmlPullParserException; 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 eb70d8e0b..c3f5d898c 100644 --- a/app/src/main/java/de/danoeh/antennapod/config/PlaybackServiceCallbacksImpl.java +++ b/app/src/main/java/de/danoeh/antennapod/config/PlaybackServiceCallbacksImpl.java @@ -35,6 +35,6 @@ public class PlaybackServiceCallbacksImpl implements PlaybackServiceCallbacks { @Override public int getNotificationIconResource(Context context) { - return R.drawable.ic_stat_antenna_default; + return R.drawable.ic_antenna; } } diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/ChooseDataFolderDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/ChooseDataFolderDialog.java index c185a5557..4cfa7e870 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/ChooseDataFolderDialog.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/ChooseDataFolderDialog.java @@ -9,7 +9,7 @@ import de.danoeh.antennapod.adapter.DataFolderAdapter; public class ChooseDataFolderDialog { - public static abstract class RunnableWithString implements Runnable { + public abstract static class RunnableWithString implements Runnable { public RunnableWithString() { super(); } diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java index ed35495fa..f6d08b7bf 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java @@ -4,16 +4,16 @@ import android.app.AlertDialog; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.os.Bundle; -import android.support.annotation.IdRes; -import android.support.annotation.NonNull; -import android.support.annotation.PluralsRes; -import android.support.annotation.StringRes; -import android.support.design.widget.Snackbar; -import android.support.v4.app.ActivityCompat; -import android.support.v4.app.Fragment; -import android.support.v4.util.ArrayMap; -import android.support.v7.app.ActionBar; -import android.support.v7.app.AppCompatActivity; +import androidx.annotation.IdRes; +import androidx.annotation.NonNull; +import androidx.annotation.PluralsRes; +import androidx.annotation.StringRes; +import com.google.android.material.snackbar.Snackbar; +import androidx.core.app.ActivityCompat; +import androidx.fragment.app.Fragment; +import androidx.collection.ArrayMap; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AppCompatActivity; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/FilterDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/FilterDialog.java index 607084c42..d2912f90f 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/FilterDialog.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/FilterDialog.java @@ -1,7 +1,7 @@ package de.danoeh.antennapod.dialog; import android.content.Context; -import android.support.v7.app.AlertDialog; +import androidx.appcompat.app.AlertDialog; import android.text.TextUtils; import java.util.Arrays; diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/GpodnetSetHostnameDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/GpodnetSetHostnameDialog.java index 933ced0f9..17668586b 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/GpodnetSetHostnameDialog.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/GpodnetSetHostnameDialog.java @@ -1,7 +1,7 @@ package de.danoeh.antennapod.dialog; import android.content.Context; -import android.support.v7.app.AlertDialog; +import androidx.appcompat.app.AlertDialog; import android.text.Editable; import android.text.InputType; import android.view.View; diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java index e8c7520b7..d742a1fd4 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java @@ -2,8 +2,8 @@ package de.danoeh.antennapod.dialog; import android.app.Dialog; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v4.app.DialogFragment; +import androidx.annotation.NonNull; +import androidx.fragment.app.DialogFragment; import android.widget.Button; import android.widget.CheckBox; import android.widget.SeekBar; diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java index c1008a380..0499d02f1 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/ProxyDialog.java @@ -4,7 +4,7 @@ import android.app.Dialog; import android.content.Context; import android.content.res.TypedArray; import android.os.Build; -import android.support.v4.content.ContextCompat; +import androidx.core.content.ContextCompat; import android.text.Editable; import android.text.TextUtils; import android.text.TextWatcher; diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java index 24656ed29..c49e9153e 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/RatingDialog.java @@ -2,11 +2,9 @@ package de.danoeh.antennapod.dialog; import android.app.Dialog; import android.content.Context; -import android.content.Intent; import android.content.SharedPreferences; -import android.net.Uri; -import android.support.annotation.Nullable; -import android.support.annotation.VisibleForTesting; +import androidx.annotation.Nullable; +import androidx.annotation.VisibleForTesting; import android.util.Log; import com.afollestad.materialdialogs.MaterialDialog; @@ -15,6 +13,7 @@ import java.lang.ref.WeakReference; import java.util.concurrent.TimeUnit; import de.danoeh.antennapod.R; +import de.danoeh.antennapod.core.util.IntentUtils; public class RatingDialog { @@ -59,14 +58,10 @@ public class RatingDialog { private static void rateNow() { Context context = mContext.get(); - if(context == null) { + if (context == null) { return; } - final String appPackage = "de.danoeh.antennapod"; - final Uri uri = Uri.parse("https://play.google.com/store/apps/details?id=" + appPackage); - Intent intent = new Intent(Intent.ACTION_VIEW, uri); - intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); - context.startActivity(intent); + IntentUtils.openInBrowser(context, "https://play.google.com/store/apps/details?id=de.danoeh.antennapod"); saveRated(); } diff --git a/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java b/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java index bf3faf89a..1cf34b2b3 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/VariableSpeedDialog.java @@ -5,7 +5,7 @@ import android.content.Context; import android.content.Intent; import android.net.Uri; import android.os.Build; -import android.support.v7.app.AlertDialog; +import androidx.appcompat.app.AlertDialog; import android.util.Log; import android.view.View; diff --git a/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearchResult.java b/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearchResult.java index ca9ed83d7..6535df5ef 100644 --- a/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearchResult.java +++ b/app/src/main/java/de/danoeh/antennapod/discovery/PodcastSearchResult.java @@ -1,6 +1,6 @@ package de.danoeh.antennapod.discovery; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import de.danoeh.antennapod.core.gpoddernet.model.GpodnetPodcast; import de.mfietz.fyydlin.SearchHit; import org.json.JSONArray; diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java index 3ef010f88..b7bfe3438 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java @@ -2,20 +2,15 @@ package de.danoeh.antennapod.fragment; import android.content.Intent; import android.os.Bundle; -import android.provider.MediaStore; -import android.support.v4.app.Fragment; +import androidx.fragment.app.Fragment; import android.view.ContextMenu; -import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; -import android.view.inputmethod.EditorInfo; import android.widget.Button; import android.widget.EditText; -import android.widget.ImageView; -import android.widget.TextView; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.activity.OnlineFeedViewActivity; diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java index bb52b26b7..3949a03a9 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/AllEpisodesFragment.java @@ -1,9 +1,9 @@ package de.danoeh.antennapod.fragment; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java index bb8f4df9a..2df28b262 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ChaptersFragment.java @@ -1,7 +1,7 @@ package de.danoeh.antennapod.fragment; import android.os.Bundle; -import android.support.v4.app.ListFragment; +import androidx.fragment.app.ListFragment; import android.util.Log; import android.view.View; import android.widget.ListView; diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/CombinedSearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/CombinedSearchFragment.java index 1d9020f0d..5ab781fe3 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/CombinedSearchFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/CombinedSearchFragment.java @@ -2,9 +2,9 @@ package de.danoeh.antennapod.fragment; import android.content.Intent; import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.support.v4.view.MenuItemCompat; -import android.support.v7.widget.SearchView; +import androidx.fragment.app.Fragment; +import androidx.core.view.MenuItemCompat; +import androidx.appcompat.widget.SearchView; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; 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 705151062..1917e4c75 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java @@ -1,8 +1,8 @@ package de.danoeh.antennapod.fragment; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v4.app.ListFragment; +import androidx.annotation.NonNull; +import androidx.fragment.app.ListFragment; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java index db9dd9530..cf9ee6c41 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/CoverFragment.java @@ -1,8 +1,8 @@ package de.danoeh.antennapod.fragment; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v4.app.Fragment; +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; import android.util.Log; import android.view.LayoutInflater; import android.view.View; diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java index 26b115b4b..528c50747 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadLogFragment.java @@ -4,8 +4,8 @@ import android.app.AlertDialog; import android.app.Dialog; import android.content.res.TypedArray; import android.os.Bundle; -import android.support.v4.app.ListFragment; -import android.support.v4.view.MenuItemCompat; +import androidx.fragment.app.ListFragment; +import androidx.core.view.MenuItemCompat; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java index aa6029c84..b1bcdf404 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/DownloadsFragment.java @@ -4,11 +4,11 @@ import android.content.Context; import android.content.SharedPreferences; import android.content.res.Resources; import android.os.Bundle; -import android.support.design.widget.TabLayout; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentPagerAdapter; -import android.support.v4.view.ViewPager; +import com.google.android.material.tabs.TabLayout; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentPagerAdapter; +import androidx.viewpager.widget.ViewPager; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; 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 ca21df661..8cdec9f38 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesFragment.java @@ -4,11 +4,11 @@ import android.content.Context; import android.content.SharedPreferences; import android.content.res.Resources; import android.os.Bundle; -import android.support.design.widget.TabLayout; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentPagerAdapter; -import android.support.v4.view.ViewPager; +import com.google.android.material.tabs.TabLayout; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentPagerAdapter; +import androidx.viewpager.widget.ViewPager; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java index 1cedb5a91..3fc67f795 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/EpisodesListFragment.java @@ -4,15 +4,13 @@ import android.content.Context; import android.content.DialogInterface; import android.content.SharedPreferences; import android.os.Bundle; -import android.os.Handler; -import android.support.annotation.NonNull; -import android.support.design.widget.Snackbar; -import android.support.v4.app.Fragment; -import android.support.v4.view.MenuItemCompat; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; -import android.support.v7.widget.SearchView; -import android.support.v7.widget.SimpleItemAnimator; +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; +import androidx.core.view.MenuItemCompat; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import androidx.appcompat.widget.SearchView; +import androidx.recyclerview.widget.SimpleItemAnimator; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; @@ -23,8 +21,17 @@ import android.view.ViewGroup; import android.widget.ProgressBar; import android.widget.TextView; import android.widget.Toast; -import com.joanzapata.iconify.Iconify; + import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration; + +import de.danoeh.antennapod.core.event.PlaybackPositionEvent; +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; + +import java.util.ArrayList; +import java.util.List; + import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.adapter.AllEpisodesRecycleAdapter; @@ -33,21 +40,16 @@ import de.danoeh.antennapod.core.event.DownloadEvent; import de.danoeh.antennapod.core.event.DownloaderUpdate; import de.danoeh.antennapod.core.event.FeedItemEvent; import de.danoeh.antennapod.core.feed.EventDistributor; -import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.feed.FeedItem; -import de.danoeh.antennapod.core.feed.FeedItemFilter; import de.danoeh.antennapod.core.feed.FeedMedia; -import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.service.download.DownloadRequest; import de.danoeh.antennapod.core.service.download.DownloadService; import de.danoeh.antennapod.core.service.download.Downloader; -import de.danoeh.antennapod.core.storage.DBReader; -import de.danoeh.antennapod.core.storage.DBTasks; import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.storage.DownloadRequester; import de.danoeh.antennapod.core.util.FeedItemUtil; import de.danoeh.antennapod.core.util.LongList; -import de.danoeh.antennapod.dialog.FilterDialog; +import de.danoeh.antennapod.core.util.download.AutoUpdateManager; import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler; import de.danoeh.antennapod.menuhandler.MenuItemUtils; import de.danoeh.antennapod.view.EmptyViewHandler; @@ -55,13 +57,6 @@ import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; -import org.greenrobot.eventbus.EventBus; -import org.greenrobot.eventbus.Subscribe; -import org.greenrobot.eventbus.ThreadMode; - -import java.util.ArrayList; -import java.util.List; -import java.util.Set; /** * Shows unread or recently published episodes @@ -149,7 +144,7 @@ public abstract class EpisodesListFragment extends Fragment { SharedPreferences.Editor editor = prefs.edit(); editor.putInt(PREF_SCROLL_POSITION, firstItem); editor.putFloat(PREF_SCROLL_OFFSET, topOffset); - editor.commit(); + editor.apply(); } private void restoreScrollPosition() { @@ -162,7 +157,7 @@ public abstract class EpisodesListFragment extends Fragment { SharedPreferences.Editor editor = prefs.edit(); editor.putInt(PREF_SCROLL_POSITION, 0); editor.putFloat(PREF_SCROLL_OFFSET, 0.0f); - editor.commit(); + editor.apply(); } } @@ -202,10 +197,7 @@ public abstract class EpisodesListFragment extends Fragment { if (!super.onOptionsItemSelected(item)) { switch (item.getItemId()) { case R.id.refresh_item: - List<Feed> feeds = ((MainActivity) getActivity()).getFeeds(); - if (feeds != null) { - DBTasks.refreshAllFeeds(getActivity(), feeds); - } + AutoUpdateManager.runImmediate(requireContext()); return true; case R.id.mark_all_read_item: ConfirmationDialog markAllReadConfirmationDialog = new ConfirmationDialog(getActivity(), @@ -262,17 +254,7 @@ public abstract class EpisodesListFragment extends Fragment { } FeedItem selectedItem = listAdapter.getSelectedItem(); - // Remove new flag contains UI logic specific to All/New/FavoriteSegments, - // e.g., Undo with Snackbar, - // and is handled by this class rather than the generic FeedItemMenuHandler - // Undo is useful for Remove new flag, given there is no UI to undo it otherwise, - // i.e., there is context menu item for Mark as new - if (R.id.remove_new_flag_item == item.getItemId()) { - removeNewFlagWithUndo(selectedItem); - return true; - } - - return FeedItemMenuHandler.onMenuItemClicked(getActivity(), item.getItemId(), selectedItem); + return FeedItemMenuHandler.onMenuItemClicked(this, item.getItemId(), selectedItem); } @NonNull @@ -403,6 +385,13 @@ public abstract class EpisodesListFragment extends Fragment { } } + @Subscribe(threadMode = ThreadMode.MAIN) + public void onEventMainThread(PlaybackPositionEvent event) { + if (listAdapter != null) { + listAdapter.notifyCurrentlyPlayingItemChanged(event); + } + } + protected boolean shouldUpdatedItemRemainInList(FeedItem item) { return true; } @@ -412,7 +401,7 @@ public abstract class EpisodesListFragment extends Fragment { Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]"); DownloaderUpdate update = event.update; downloaderList = update.downloaders; - if (isMenuInvalidationAllowed && isUpdatingFeeds != update.feedIds.length > 0) { + if (isMenuInvalidationAllowed && event.hasChangedFeedUpdateStatus(isUpdatingFeeds)) { requireActivity().invalidateOptionsMenu(); } if (update.mediaIds.length > 0) { @@ -453,36 +442,4 @@ public abstract class EpisodesListFragment extends Fragment { @NonNull protected abstract List<FeedItem> loadData(); - - void removeNewFlagWithUndo(FeedItem item) { - if (item == null) { - return; - } - - Log.d(TAG, "removeNewFlagWithUndo(" + item.getId() + ")"); - if (disposable != null) { - disposable.dispose(); - } - // we're marking it as unplayed since the user didn't actually play it - // but they don't want it considered 'NEW' anymore - DBWriter.markItemPlayed(FeedItem.UNPLAYED, item.getId()); - - final Handler h = new Handler(getActivity().getMainLooper()); - final Runnable r = () -> { - FeedMedia media = item.getMedia(); - if (media != null && media.hasAlmostEnded() && UserPreferences.isAutoDelete()) { - DBWriter.deleteFeedMediaOfItem(getActivity(), media.getId()); - } - }; - - Snackbar snackbar = Snackbar.make(getView(), getString(R.string.removed_new_flag_label), - Snackbar.LENGTH_LONG); - snackbar.setAction(getString(R.string.undo), v -> { - DBWriter.markItemPlayed(FeedItem.NEW, item.getId()); - // don't forget to cancel the thing that's going to remove the media - h.removeCallbacks(r); - }); - snackbar.show(); - h.postDelayed(r, (int) Math.ceil(snackbar.getDuration() * 1.05f)); - } } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java index 348c73b92..bbc33c6ca 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ExternalPlayerFragment.java @@ -3,8 +3,8 @@ package de.danoeh.antennapod.fragment; import android.content.Intent; import android.os.Build; import android.os.Bundle; -import android.support.v4.app.ActivityOptionsCompat; -import android.support.v4.app.Fragment; +import androidx.core.app.ActivityOptionsCompat; +import androidx.fragment.app.Fragment; import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -18,7 +18,7 @@ import com.bumptech.glide.Glide; import com.bumptech.glide.request.RequestOptions; import de.danoeh.antennapod.R; -import de.danoeh.antennapod.core.event.ServiceEvent; +import de.danoeh.antennapod.core.event.PlaybackPositionEvent; import de.danoeh.antennapod.core.feed.MediaType; import de.danoeh.antennapod.core.glide.ApGlideSettings; import de.danoeh.antennapod.core.service.playback.PlaybackService; @@ -28,6 +28,9 @@ import io.reactivex.Maybe; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; /** * Fragment which is supposed to be displayed outside of the MediaplayerActivity @@ -138,6 +141,7 @@ public class ExternalPlayerFragment extends Fragment { controller = setupPlaybackController(); controller.init(); loadMediaInfo(); + EventBus.getDefault().register(this); } @Override @@ -147,6 +151,12 @@ public class ExternalPlayerFragment extends Fragment { controller.release(); controller = null; } + EventBus.getDefault().unregister(this); + } + + @Subscribe(threadMode = ThreadMode.MAIN) + public void onEventMainThread(PlaybackPositionEvent event) { + onPositionObserverUpdate(); } @Override diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java index 536ebd468..5282a6bb2 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/FavoriteEpisodesFragment.java @@ -1,13 +1,12 @@ package de.danoeh.antennapod.fragment; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.design.widget.Snackbar; -import android.support.v7.widget.RecyclerView; -import android.support.v7.widget.helper.ItemTouchHelper; +import androidx.annotation.NonNull; +import com.google.android.material.snackbar.Snackbar; +import androidx.recyclerview.widget.RecyclerView; +import androidx.recyclerview.widget.ItemTouchHelper; import android.util.Log; import android.view.LayoutInflater; -import android.view.Menu; import android.view.View; import android.view.ViewGroup; diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FeedInfoFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FeedInfoFragment.java new file mode 100644 index 000000000..6b270e220 --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedInfoFragment.java @@ -0,0 +1,234 @@ +package de.danoeh.antennapod.fragment; + +import android.content.ClipData; +import android.content.Context; +import android.content.Intent; +import android.graphics.LightingColorFilter; +import android.net.Uri; +import android.os.Bundle; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import android.text.TextUtils; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageView; +import android.widget.TextView; +import android.widget.Toast; +import com.bumptech.glide.Glide; +import com.bumptech.glide.request.RequestOptions; +import com.joanzapata.iconify.Iconify; +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.activity.MainActivity; +import de.danoeh.antennapod.core.dialog.DownloadRequestErrorDialogCreator; +import de.danoeh.antennapod.core.feed.Feed; +import de.danoeh.antennapod.core.glide.ApGlideSettings; +import de.danoeh.antennapod.core.glide.FastBlurTransformation; +import de.danoeh.antennapod.core.storage.DBReader; +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 io.reactivex.Maybe; +import io.reactivex.MaybeOnSubscribe; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; +import org.apache.commons.lang3.StringUtils; +import org.jsoup.Jsoup; +import org.jsoup.nodes.Document; + +/** + * Displays information about a feed. + */ +public class FeedInfoFragment extends Fragment { + + private static final String EXTRA_FEED_ID = "de.danoeh.antennapod.extra.feedId"; + private static final String TAG = "FeedInfoActivity"; + + private Feed feed; + private Disposable disposable; + private ImageView imgvCover; + private TextView txtvTitle; + private TextView txtvDescription; + private TextView lblLanguage; + private TextView txtvLanguage; + private TextView lblAuthor; + private TextView txtvAuthor; + private TextView txtvUrl; + private TextView txtvAuthorHeader; + private ImageView imgvBackground; + + public static FeedInfoFragment newInstance(Feed feed) { + FeedInfoFragment fragment = new FeedInfoFragment(); + Bundle arguments = new Bundle(); + arguments.putLong(EXTRA_FEED_ID, feed.getId()); + fragment.setArguments(arguments); + return fragment; + } + + 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) getContext() + .getSystemService(Context.CLIPBOARD_SERVICE); + cm.setPrimaryClip(clipData); + Toast t = Toast.makeText(getContext(), R.string.copied_url_msg, Toast.LENGTH_SHORT); + t.show(); + } + } + }; + + @Override + public void onResume() { + super.onResume(); + ((MainActivity)getActivity()).getSupportActionBar().setTitle(R.string.feed_info_label); + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View root = inflater.inflate(R.layout.feedinfo, null); + setHasOptionsMenu(true); + + imgvCover = root.findViewById(R.id.imgvCover); + txtvTitle = root.findViewById(R.id.txtvTitle); + txtvAuthorHeader = root.findViewById(R.id.txtvAuthor); + imgvBackground = root.findViewById(R.id.imgvBackground); + root.findViewById(R.id.butShowInfo).setVisibility(View.INVISIBLE); + root.findViewById(R.id.butShowSettings).setVisibility(View.INVISIBLE); + // https://github.com/bumptech/glide/issues/529 + imgvBackground.setColorFilter(new LightingColorFilter(0xff828282, 0x000000)); + + + txtvDescription = root.findViewById(R.id.txtvDescription); + lblLanguage = root.findViewById(R.id.lblLanguage); + txtvLanguage = root.findViewById(R.id.txtvLanguage); + lblAuthor = root.findViewById(R.id.lblAuthor); + txtvAuthor = root.findViewById(R.id.txtvDetailsAuthor); + txtvUrl = root.findViewById(R.id.txtvUrl); + + txtvUrl.setOnClickListener(copyUrlToClipboard); + postponeEnterTransition(); + return root; + } + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + long feedId = getArguments().getLong(EXTRA_FEED_ID); + disposable = Maybe.create((MaybeOnSubscribe<Feed>) emitter -> { + Feed feed = DBReader.getFeed(feedId); + if (feed != null) { + emitter.onSuccess(feed); + } else { + emitter.onComplete(); + } + }) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(result -> { + feed = result; + showFeed(); + }, error -> Log.d(TAG, Log.getStackTraceString(error)), + this::startPostponedEnterTransition); + } + + private void showFeed() { + Log.d(TAG, "Language is " + feed.getLanguage()); + Log.d(TAG, "Author is " + feed.getAuthor()); + Log.d(TAG, "URL is " + feed.getDownload_url()); + Glide.with(getContext()) + .load(feed.getImageLocation()) + .apply(new RequestOptions() + .placeholder(R.color.light_gray) + .error(R.color.light_gray) + .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) + .fitCenter() + .dontAnimate()) + .into(imgvCover); + Glide.with(getContext()) + .load(feed.getImageLocation()) + .apply(new RequestOptions() + .placeholder(R.color.image_readability_tint) + .error(R.color.image_readability_tint) + .diskCacheStrategy(ApGlideSettings.AP_DISK_CACHE_STRATEGY) + .transform(new FastBlurTransformation()) + .dontAnimate()) + .into(imgvBackground); + + txtvTitle.setText(feed.getTitle()); + + String description = feed.getDescription(); + if(description != null) { + if(Feed.TYPE_ATOM1.equals(feed.getType())) { + HtmlToPlainText formatter = new HtmlToPlainText(); + Document feedDescription = Jsoup.parse(feed.getDescription()); + description = StringUtils.trim(formatter.getPlainText(feedDescription)); + } + } else { + description = ""; + } + txtvDescription.setText(description); + + if (!TextUtils.isEmpty(feed.getAuthor())) { + txtvAuthor.setText(feed.getAuthor()); + txtvAuthorHeader.setText(feed.getAuthor()); + } else { + lblAuthor.setVisibility(View.GONE); + txtvAuthor.setVisibility(View.GONE); + } + if (!TextUtils.isEmpty(feed.getLanguage())) { + txtvLanguage.setText(LangUtils.getLanguageString(feed.getLanguage())); + } else { + lblLanguage.setVisibility(View.GONE); + txtvLanguage.setVisibility(View.GONE); + } + txtvUrl.setText(feed.getDownload_url() + " {fa-paperclip}"); + Iconify.addIcons(txtvUrl); + + getActivity().invalidateOptionsMenu(); + } + + @Override + public void onDestroy() { + super.onDestroy(); + if (disposable != null) { + disposable.dispose(); + } + } + + @Override + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { + super.onCreateOptionsMenu(menu, inflater); + inflater.inflate(R.menu.feedinfo, menu); + } + + @Override + public void onPrepareOptionsMenu(Menu menu) { + super.onPrepareOptionsMenu(menu); + 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(getContext(), new Intent(Intent.ACTION_VIEW, Uri.parse(feed.getLink())))); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + boolean handled = false; + try { + handled = FeedMenuHandler.onOptionsItemClicked(getContext(), item, feed); + } catch (DownloadRequestException e) { + e.printStackTrace(); + DownloadRequestErrorDialogCreator.newRequestErrorDialog(getContext(), e.getMessage()); + } + return handled || super.onOptionsItemSelected(item); + } +} diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java index 4e4b40096..0c33dce5a 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java @@ -3,13 +3,12 @@ package de.danoeh.antennapod.fragment; import android.annotation.SuppressLint; import android.content.Context; import android.content.DialogInterface; -import android.content.Intent; import android.graphics.LightingColorFilter; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v4.app.ListFragment; -import android.support.v4.view.MenuItemCompat; -import android.support.v7.widget.SearchView; +import androidx.annotation.NonNull; +import androidx.fragment.app.ListFragment; +import androidx.core.view.MenuItemCompat; +import androidx.appcompat.widget.SearchView; import android.util.Log; import android.view.ContextMenu; import android.view.LayoutInflater; @@ -29,6 +28,7 @@ import com.bumptech.glide.request.RequestOptions; import com.joanzapata.iconify.Iconify; import com.joanzapata.iconify.widget.IconTextView; +import de.danoeh.antennapod.core.event.PlaybackPositionEvent; import org.apache.commons.lang3.Validate; import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; @@ -37,8 +37,6 @@ import org.greenrobot.eventbus.ThreadMode; import java.util.List; import de.danoeh.antennapod.R; -import de.danoeh.antennapod.activity.FeedInfoActivity; -import de.danoeh.antennapod.activity.FeedSettingsActivity; import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.adapter.FeedItemlistAdapter; import de.danoeh.antennapod.core.asynctask.FeedRemover; @@ -140,37 +138,33 @@ public class FeedItemlistFragment extends ListFragment { } @Override - public void onStart() { - super.onStart(); + public void onHiddenChanged(boolean hidden) { + super.onHiddenChanged(hidden); + if (!hidden && getActivity() != null) { + ((MainActivity) getActivity()).getSupportActionBar().setTitle(""); + } + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + registerForContextMenu(getListView()); + EventDistributor.getInstance().register(contentUpdate); EventBus.getDefault().register(this); loadItems(); } @Override - public void onResume() { - super.onResume(); - ((MainActivity)getActivity()).getSupportActionBar().setTitle(""); - updateProgressBarVisibility(); - } + public void onDestroyView() { + super.onDestroyView(); - @Override - public void onStop() { - super.onStop(); EventDistributor.getInstance().unregister(contentUpdate); EventBus.getDefault().unregister(this); - if(disposable != null) { + if (disposable != null) { disposable.dispose(); } - } - - @Override - public void onDestroyView() { - super.onDestroyView(); - resetViewState(); - } - - private void resetViewState() { adapter = null; listFooter = null; } @@ -323,7 +317,7 @@ public class FeedItemlistFragment extends ListFragment { contextMenu = menu; lastMenuInfo = (AdapterView.AdapterContextMenuInfo) menuInfo; - FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item, true, null); + FeedItemMenuHandler.onPrepareMenu(contextMenuInterface, item); } @Override @@ -340,14 +334,7 @@ public class FeedItemlistFragment extends ListFragment { return super.onContextItemSelected(item); } - return FeedItemMenuHandler.onMenuItemClicked(getActivity(), item.getItemId(), selectedItem); - } - - @Override - public void onViewCreated(View view, Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - - registerForContextMenu(getListView()); + return FeedItemMenuHandler.onMenuItemClicked(this, item.getItemId(), selectedItem); } @Override @@ -390,14 +377,21 @@ public class FeedItemlistFragment extends ListFragment { Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]"); DownloaderUpdate update = event.update; downloaderList = update.downloaders; - if (isUpdatingFeed != event.update.feedIds.length > 0) { + if (event.hasChangedFeedUpdateStatus(isUpdatingFeed)) { updateProgressBarVisibility(); } - if(adapter != null && update.mediaIds.length > 0) { + if (adapter != null && update.mediaIds.length > 0) { adapter.notifyDataSetChanged(); } } + @Subscribe(threadMode = ThreadMode.MAIN) + public void onEventMainThread(PlaybackPositionEvent event) { + if (adapter != null) { + adapter.notifyCurrentlyPlayingItemChanged(event, getListView()); + } + } + private final EventDistributor.EventListener contentUpdate = new EventDistributor.EventListener() { @Override @@ -421,8 +415,9 @@ public class FeedItemlistFragment extends ListFragment { } - private void onFragmentLoaded() { - if(!isVisible()) { + private void displayList() { + if (getView() == null) { + Log.e(TAG, "Required root view is not yet created. Stop binding data to UI."); return; } if (adapter == null) { @@ -506,10 +501,8 @@ public class FeedItemlistFragment extends ListFragment { imgvCover.setOnClickListener(v -> showFeedInfo()); butShowSettings.setOnClickListener(v -> { if (feed != null) { - Intent startIntent = new Intent(getActivity(), FeedSettingsActivity.class); - startIntent.putExtra(FeedSettingsActivity.EXTRA_FEED_ID, - feed.getId()); - startActivity(startIntent); + FeedSettingsFragment fragment = FeedSettingsFragment.newInstance(feed); + ((MainActivity) getActivity()).loadChildFragment(fragment, TransitionEffect.FLIP); } }); headerCreated = true; @@ -517,10 +510,8 @@ public class FeedItemlistFragment extends ListFragment { private void showFeedInfo() { if (feed != null) { - Intent startIntent = new Intent(getActivity(), FeedInfoActivity.class); - startIntent.putExtra(FeedInfoActivity.EXTRA_FEED_ID, - feed.getId()); - startActivity(startIntent); + FeedInfoFragment fragment = FeedInfoFragment.newInstance(feed); + ((MainActivity) getActivity()).loadChildFragment(fragment, TransitionEffect.FLIP); } } @@ -626,7 +617,7 @@ public class FeedItemlistFragment extends ListFragment { .observeOn(AndroidSchedulers.mainThread()) .subscribe(result -> { feed = result.orElse(null); - onFragmentLoaded(); + displayList(); }, error -> Log.e(TAG, Log.getStackTraceString(error))); } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java index 4fb3d90f5..b13521fa2 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedSettingsFragment.java @@ -1,39 +1,66 @@ package de.danoeh.antennapod.fragment; -import android.arch.lifecycle.ViewModelProviders; import android.content.Context; import android.content.DialogInterface; import android.os.Bundle; -import android.support.v14.preference.SwitchPreference; -import android.support.v7.preference.ListPreference; -import android.support.v7.preference.PreferenceFragmentCompat; +import androidx.preference.SwitchPreference; +import androidx.preference.ListPreference; +import androidx.preference.PreferenceFragmentCompat; +import android.util.Log; import de.danoeh.antennapod.R; +import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.core.dialog.ConfirmationDialog; 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.preferences.UserPreferences; +import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.dialog.AuthenticationDialog; import de.danoeh.antennapod.dialog.EpisodeFilterDialog; -import de.danoeh.antennapod.viewmodel.FeedSettingsViewModel; - -import static de.danoeh.antennapod.activity.FeedSettingsActivity.EXTRA_FEED_ID; +import io.reactivex.Maybe; +import io.reactivex.MaybeOnSubscribe; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.Disposable; +import io.reactivex.schedulers.Schedulers; public class FeedSettingsFragment extends PreferenceFragmentCompat { private static final CharSequence PREF_EPISODE_FILTER = "episodeFilter"; + private static final String EXTRA_FEED_ID = "de.danoeh.antennapod.extra.feedId"; + private static final String TAG = "FeedSettingsFragment"; + private Feed feed; + private Disposable disposable; private FeedPreferences feedPreferences; + public static FeedSettingsFragment newInstance(Feed feed) { + FeedSettingsFragment fragment = new FeedSettingsFragment(); + Bundle arguments = new Bundle(); + arguments.putLong(EXTRA_FEED_ID, feed.getId()); + fragment.setArguments(arguments); + return fragment; + } + @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { addPreferencesFromResource(R.xml.feed_settings); + postponeEnterTransition(); long feedId = getArguments().getLong(EXTRA_FEED_ID); - ViewModelProviders.of(getActivity()).get(FeedSettingsViewModel.class).getFeed(feedId) + disposable = Maybe.create((MaybeOnSubscribe<Feed>) emitter -> { + Feed feed = DBReader.getFeed(feedId); + if (feed != null) { + emitter.onSuccess(feed); + } else { + emitter.onComplete(); + } + }) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) .subscribe(result -> { feed = result; feedPreferences = feed.getPreferences(); + ((MainActivity) getActivity()).getSupportActionBar().setSubtitle(feed.getTitle()); setupAutoDownloadPreference(); setupKeepUpdatedPreference(); @@ -43,7 +70,31 @@ public class FeedSettingsFragment extends PreferenceFragmentCompat { updateAutoDeleteSummary(); updateAutoDownloadEnabled(); - }).dispose(); + }, error -> Log.d(TAG, Log.getStackTraceString(error)), + this::startPostponedEnterTransition); + } + + @Override + public void onResume() { + super.onResume(); + ((MainActivity) getActivity()).getSupportActionBar().setTitle(R.string.feed_settings_label); + if (feed != null) { + ((MainActivity) getActivity()).getSupportActionBar().setSubtitle(feed.getTitle()); + } + } + + @Override + public void onStop() { + super.onStop(); + ((MainActivity) getActivity()).getSupportActionBar().setSubtitle(null); + } + + @Override + public void onDestroy() { + super.onDestroy(); + if (disposable != null) { + disposable.dispose(); + } } private void setupEpisodeFilterPreference() { diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/FyydSearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/FyydSearchFragment.java index 9c16cfe56..a3c3df340 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/FyydSearchFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/FyydSearchFragment.java @@ -2,9 +2,9 @@ package de.danoeh.antennapod.fragment; import android.content.Intent; import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.support.v4.view.MenuItemCompat; -import android.support.v7.widget.SearchView; +import androidx.fragment.app.Fragment; +import androidx.core.view.MenuItemCompat; +import androidx.appcompat.widget.SearchView; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java index 5cf2c5eeb..a97d60099 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java @@ -2,7 +2,6 @@ package de.danoeh.antennapod.fragment; import android.annotation.SuppressLint; import android.app.Activity; -import android.content.ActivityNotFoundException; import android.content.ClipData; import android.content.Context; import android.content.Intent; @@ -12,8 +11,8 @@ import android.graphics.Color; import android.net.Uri; import android.os.Build; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v4.app.Fragment; +import androidx.annotation.NonNull; +import androidx.fragment.app.Fragment; import android.util.Log; import android.view.ContextMenu; import android.view.ContextMenu.ContextMenuInfo; @@ -96,13 +95,7 @@ public class ItemDescriptionFragment extends Fragment { if (Timeline.isTimecodeLink(url)) { onTimecodeLinkSelected(url); } else { - Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); - try { - startActivity(intent); - } catch (ActivityNotFoundException e) { - e.printStackTrace(); - return true; - } + IntentUtils.openInBrowser(getContext(), url); } return true; } @@ -159,11 +152,7 @@ public class ItemDescriptionFragment extends Fragment { if (selectedURL != null) { switch (item.getItemId()) { case R.id.open_in_browser_item: - Uri uri = Uri.parse(selectedURL); - final Intent intent = new Intent(Intent.ACTION_VIEW, uri); - if(IntentUtils.isCallable(getActivity(), intent)) { - getActivity().startActivity(intent); - } + IntentUtils.openInBrowser(getContext(), selectedURL); break; case R.id.share_url_item: ShareUtils.shareLink(getActivity(), selectedURL); 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 3a48c5431..f17f8c645 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java @@ -6,10 +6,10 @@ import android.content.Intent; import android.net.Uri; import android.os.Build; import android.os.Bundle; -import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; -import android.support.v4.content.ContextCompat; -import android.support.v4.view.GestureDetectorCompat; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.core.content.ContextCompat; +import androidx.core.view.GestureDetectorCompat; import android.text.Layout; import android.text.TextUtils; import android.util.Log; @@ -55,7 +55,6 @@ import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.glide.ApGlideSettings; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.service.download.Downloader; -import de.danoeh.antennapod.core.service.playback.PlaybackService; import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBTasks; import de.danoeh.antennapod.core.storage.DBWriter; @@ -211,10 +210,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture { webvDescription.setWebViewClient(new WebViewClient() { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { - Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); - if(IntentUtils.isCallable(getActivity(), intent)) { - startActivity(intent); - } + IntentUtils.openInBrowser(getContext(), url); return true; } }); @@ -336,10 +332,10 @@ public class ItemFragment extends Fragment implements OnSwipeGesture { inflater.inflate(R.menu.feeditem_options, menu); popupMenu = menu; if (item.hasMedia()) { - FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item, true, null); + FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item); } else { // these are already available via button1 and button2 - FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item, true, null, + FeedItemMenuHandler.onPrepareMenu(popupMenuInterface, item, R.id.mark_read_item, R.id.visit_website_item); } } @@ -351,7 +347,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture { openPodcast(); return true; default: - return FeedItemMenuHandler.onMenuItemClicked(getActivity(), menuItem.getItemId(), item); + return FeedItemMenuHandler.onMenuItemClicked(this, menuItem.getItemId(), item); } } @@ -485,11 +481,7 @@ public class ItemFragment extends Fragment implements OnSwipeGesture { if (selectedURL != null) { switch (item.getItemId()) { case R.id.open_in_browser_item: - Uri uri = Uri.parse(selectedURL); - final Intent intent = new Intent(Intent.ACTION_VIEW, uri); - if(IntentUtils.isCallable(getActivity(), intent)) { - getActivity().startActivity(intent); - } + IntentUtils.openInBrowser(getContext(), selectedURL); break; case R.id.share_url_item: ShareUtils.shareLink(getActivity(), selectedURL); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java index 80767bef2..673b58901 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItunesSearchFragment.java @@ -2,10 +2,10 @@ package de.danoeh.antennapod.fragment; import android.content.Intent; import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.support.v4.view.MenuItemCompat; -import android.support.v7.widget.SearchView; -import android.support.annotation.NonNull; +import androidx.fragment.app.Fragment; +import androidx.core.view.MenuItemCompat; +import androidx.appcompat.widget.SearchView; +import androidx.annotation.NonNull; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; @@ -23,29 +23,15 @@ import com.afollestad.materialdialogs.MaterialDialog; import de.danoeh.antennapod.discovery.ItunesPodcastSearcher; import de.danoeh.antennapod.discovery.ItunesTopListLoader; import de.danoeh.antennapod.discovery.PodcastSearchResult; -import org.json.JSONArray; -import org.json.JSONException; -import org.json.JSONObject; -import java.io.IOException; import java.util.ArrayList; import java.util.List; -import java.util.Locale; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.OnlineFeedViewActivity; import de.danoeh.antennapod.adapter.itunes.ItunesAdapter; -import de.danoeh.antennapod.core.ClientConfig; -import de.danoeh.antennapod.core.service.download.AntennapodHttpClient; import de.danoeh.antennapod.menuhandler.MenuItemUtils; -import io.reactivex.Single; -import io.reactivex.SingleOnSubscribe; -import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; -import io.reactivex.schedulers.Schedulers; -import okhttp3.OkHttpClient; -import okhttp3.Request; -import okhttp3.Response; //Searches iTunes store for given string and displays results in a list public class ItunesSearchFragment extends Fragment { @@ -167,7 +153,7 @@ public class ItunesSearchFragment extends Fragment { final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem); MenuItemUtils.adjustTextColor(getActivity(), sv); sv.setQueryHint(getString(R.string.search_itunes_label)); - sv.setOnQueryTextListener(new android.support.v7.widget.SearchView.OnQueryTextListener() { + sv.setOnQueryTextListener(new androidx.appcompat.widget.SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String s) { sv.clearFocus(); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java index adae4f2a5..5dbb84bc7 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/NewEpisodesFragment.java @@ -1,12 +1,11 @@ package de.danoeh.antennapod.fragment; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v7.widget.RecyclerView; -import android.support.v7.widget.helper.ItemTouchHelper; +import androidx.annotation.NonNull; +import androidx.recyclerview.widget.RecyclerView; +import androidx.recyclerview.widget.ItemTouchHelper; import android.view.LayoutInflater; import android.view.Menu; -import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; @@ -16,6 +15,7 @@ import de.danoeh.antennapod.R; import de.danoeh.antennapod.adapter.AllEpisodesRecycleAdapter; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.storage.DBReader; +import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler; /** * Like 'EpisodesFragment' except that it only shows new episodes and @@ -63,7 +63,7 @@ public class NewEpisodesFragment extends EpisodesListFragment { @Override public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) { AllEpisodesRecycleAdapter.Holder holder = (AllEpisodesRecycleAdapter.Holder) viewHolder; - removeNewFlagWithUndo(holder.getFeedItem()); + FeedItemMenuHandler.removeNewFlagWithUndo(NewEpisodesFragment.this, holder.getFeedItem()); } @Override diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java index e2060481f..f9fca87fc 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/PlaybackHistoryFragment.java @@ -2,9 +2,9 @@ package de.danoeh.antennapod.fragment; import android.content.res.TypedArray; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v4.app.ListFragment; -import android.support.v4.view.MenuItemCompat; +import androidx.annotation.NonNull; +import androidx.fragment.app.ListFragment; +import androidx.core.view.MenuItemCompat; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; 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 9763f8f2d..5634243da 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java @@ -4,14 +4,15 @@ import android.content.Context; import android.content.DialogInterface; import android.content.SharedPreferences; import android.os.Bundle; -import android.support.design.widget.Snackbar; -import android.support.v4.app.Fragment; -import android.support.v4.view.MenuItemCompat; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; -import android.support.v7.widget.SearchView; -import android.support.v7.widget.SimpleItemAnimator; -import android.support.v7.widget.helper.ItemTouchHelper; +import com.google.android.material.snackbar.Snackbar; +import androidx.fragment.app.Fragment; +import androidx.core.view.MenuItemCompat; +import androidx.appcompat.app.AlertDialog; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import androidx.appcompat.widget.SearchView; +import androidx.recyclerview.widget.SimpleItemAnimator; +import androidx.recyclerview.widget.ItemTouchHelper; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; @@ -19,11 +20,17 @@ import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; +import android.widget.CheckBox; import android.widget.ProgressBar; import android.widget.TextView; import com.yqritc.recyclerviewflexibledivider.HorizontalDividerItemDecoration; +import de.danoeh.antennapod.core.event.PlaybackPositionEvent; +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; + import java.util.List; import de.danoeh.antennapod.R; @@ -35,14 +42,12 @@ import de.danoeh.antennapod.core.event.DownloaderUpdate; import de.danoeh.antennapod.core.event.FeedItemEvent; import de.danoeh.antennapod.core.event.QueueEvent; import de.danoeh.antennapod.core.feed.EventDistributor; -import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.feed.FeedItem; import de.danoeh.antennapod.core.feed.FeedMedia; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.service.download.DownloadService; import de.danoeh.antennapod.core.service.download.Downloader; import de.danoeh.antennapod.core.storage.DBReader; -import de.danoeh.antennapod.core.storage.DBTasks; import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.storage.DownloadRequester; import de.danoeh.antennapod.core.util.Converter; @@ -50,18 +55,15 @@ import de.danoeh.antennapod.core.util.FeedItemUtil; import de.danoeh.antennapod.core.util.LongList; import de.danoeh.antennapod.core.util.QueueSorter; import de.danoeh.antennapod.core.util.SortOrder; +import de.danoeh.antennapod.core.util.download.AutoUpdateManager; import de.danoeh.antennapod.dialog.EpisodesApplyActionFragment; import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler; import de.danoeh.antennapod.menuhandler.MenuItemUtils; - import de.danoeh.antennapod.view.EmptyViewHandler; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; -import org.greenrobot.eventbus.EventBus; -import org.greenrobot.eventbus.Subscribe; -import org.greenrobot.eventbus.ThreadMode; import static de.danoeh.antennapod.dialog.EpisodesApplyActionFragment.ACTION_DELETE; import static de.danoeh.antennapod.dialog.EpisodesApplyActionFragment.ACTION_REMOVE_FROM_QUEUE; @@ -91,10 +93,12 @@ public class QueueFragment extends Fragment { private static final String PREFS = "QueueFragment"; private static final String PREF_SCROLL_POSITION = "scroll_position"; private static final String PREF_SCROLL_OFFSET = "scroll_offset"; + private static final String PREF_SHOW_LOCK_WARNING = "show_lock_warning"; private Disposable disposable; private LinearLayoutManager layoutManager; private ItemTouchHelper itemTouchHelper; + private SharedPreferences prefs; @Override @@ -102,6 +106,7 @@ public class QueueFragment extends Fragment { super.onCreate(savedInstanceState); setRetainInstance(true); setHasOptionsMenu(true); + prefs = getActivity().getSharedPreferences(PREFS, Context.MODE_PRIVATE); } @Override @@ -196,8 +201,8 @@ public class QueueFragment extends Fragment { Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]"); DownloaderUpdate update = event.update; downloaderList = update.downloaders; - if (isUpdatingFeeds != update.feedIds.length > 0) { - getActivity().supportInvalidateOptionsMenu(); + if (event.hasChangedFeedUpdateStatus(isUpdatingFeeds)) { + getActivity().invalidateOptionsMenu(); } if (recyclerAdapter != null && update.mediaIds.length > 0) { for (long mediaId : update.mediaIds) { @@ -209,6 +214,13 @@ public class QueueFragment extends Fragment { } } + @Subscribe(threadMode = ThreadMode.MAIN) + public void onEventMainThread(PlaybackPositionEvent event) { + if (recyclerAdapter != null) { + recyclerAdapter.notifyCurrentlyPlayingItemChanged(event); + } + } + private void saveScrollPosition() { int firstItem = layoutManager.findFirstVisibleItemPosition(); View firstItemView = layoutManager.findViewByPosition(firstItem); @@ -219,15 +231,13 @@ public class QueueFragment extends Fragment { topOffset = firstItemView.getTop(); } - SharedPreferences prefs = getActivity().getSharedPreferences(PREFS, Context.MODE_PRIVATE); - SharedPreferences.Editor editor = prefs.edit(); - editor.putInt(PREF_SCROLL_POSITION, firstItem); - editor.putFloat(PREF_SCROLL_OFFSET, topOffset); - editor.commit(); + prefs.edit() + .putInt(PREF_SCROLL_POSITION, firstItem) + .putFloat(PREF_SCROLL_OFFSET, topOffset) + .apply(); } private void restoreScrollPosition() { - SharedPreferences prefs = getActivity().getSharedPreferences(PREFS, Context.MODE_PRIVATE); int position = prefs.getInt(PREF_SCROLL_POSITION, 0); float offset = prefs.getFloat(PREF_SCROLL_OFFSET, 0.0f); if (position > 0 || offset > 0) { @@ -299,25 +309,10 @@ public class QueueFragment extends Fragment { if (!super.onOptionsItemSelected(item)) { switch (item.getItemId()) { case R.id.queue_lock: - boolean newLockState = !UserPreferences.isQueueLocked(); - UserPreferences.setQueueLocked(newLockState); - getActivity().supportInvalidateOptionsMenu(); - if (recyclerAdapter != null) { - recyclerAdapter.setLocked(newLockState); - } - if (newLockState) { - Snackbar.make(getActivity().findViewById(R.id.content), R.string - .queue_locked, Snackbar.LENGTH_SHORT).show(); - } else { - Snackbar.make(getActivity().findViewById(R.id.content), R.string - .queue_unlocked, Snackbar.LENGTH_SHORT).show(); - } + toggleQueueLock(); return true; case R.id.refresh_item: - List<Feed> feeds = ((MainActivity) getActivity()).getFeeds(); - if (feeds != null) { - DBTasks.refreshAllFeeds(getActivity(), feeds); - } + AutoUpdateManager.runImmediate(requireContext()); return true; case R.id.clear_queue: // make sure the user really wants to clear the queue @@ -394,6 +389,48 @@ public class QueueFragment extends Fragment { } } + private void toggleQueueLock() { + boolean isLocked = UserPreferences.isQueueLocked(); + if (isLocked) { + setQueueLocked(false); + } else { + boolean shouldShowLockWarning = prefs.getBoolean(PREF_SHOW_LOCK_WARNING, true); + if (!shouldShowLockWarning) { + setQueueLocked(true); + } else { + AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); + builder.setTitle(R.string.lock_queue); + builder.setMessage(R.string.queue_lock_warning); + + View view = View.inflate(getContext(), R.layout.checkbox_do_not_show_again, null); + CheckBox checkDoNotShowAgain = view.findViewById(R.id.checkbox_do_not_show_again); + builder.setView(view); + + builder.setPositiveButton(R.string.lock_queue, (dialog, which) -> { + prefs.edit().putBoolean(PREF_SHOW_LOCK_WARNING, !checkDoNotShowAgain.isChecked()).apply(); + setQueueLocked(true); + }); + builder.setNegativeButton(R.string.cancel_label, null); + builder.show(); + } + } + } + + private void setQueueLocked(boolean locked) { + UserPreferences.setQueueLocked(locked); + getActivity().supportInvalidateOptionsMenu(); + if (recyclerAdapter != null) { + recyclerAdapter.setLocked(locked); + } + if (locked) { + Snackbar.make(getActivity().findViewById(R.id.content), R.string + .queue_locked, Snackbar.LENGTH_SHORT).show(); + } else { + Snackbar.make(getActivity().findViewById(R.id.content), R.string + .queue_unlocked, Snackbar.LENGTH_SHORT).show(); + } + } + /** * This method is called if the user clicks on a sort order menu item. * @@ -430,7 +467,7 @@ public class QueueFragment extends Fragment { DBWriter.moveQueueItemToBottom(selectedItem.getId(), true); return true; default: - return FeedItemMenuHandler.onMenuItemClicked(getActivity(), item.getItemId(), selectedItem); + return FeedItemMenuHandler.onMenuItemClicked(this, item.getItemId(), selectedItem); } } @@ -606,7 +643,7 @@ public class QueueFragment extends Fragment { / playbackSpeed); } } - info += " \u2022 "; + info += " • "; info += getString(R.string.time_left_label); info += Converter.getDurationStringLocalized(getActivity(), timeLeft); } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java index e4213cc6b..226209740 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java @@ -2,7 +2,7 @@ package de.danoeh.antennapod.fragment; import android.content.Intent; import android.os.Bundle; -import android.support.v4.app.Fragment; +import androidx.fragment.app.Fragment; import android.util.Log; import android.view.LayoutInflater; import android.view.View; diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java index 2a7f7d12b..528fa7c32 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/RunningDownloadsFragment.java @@ -1,7 +1,7 @@ package de.danoeh.antennapod.fragment; import android.os.Bundle; -import android.support.v4.app.ListFragment; +import androidx.fragment.app.ListFragment; import android.util.Log; import android.view.View; import android.widget.ListView; diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java index 0892bce0a..7f3c60e5d 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java @@ -2,11 +2,11 @@ package de.danoeh.antennapod.fragment; import android.content.Context; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v4.app.ListFragment; -import android.support.v4.view.MenuItemCompat; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.SearchView; +import androidx.annotation.NonNull; +import androidx.fragment.app.ListFragment; +import androidx.core.view.MenuItemCompat; +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.SearchView; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; 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 15c6052a9..21c744727 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java @@ -5,8 +5,8 @@ import android.content.Context; import android.content.DialogInterface; import android.content.SharedPreferences; import android.os.Bundle; -import android.support.annotation.StringRes; -import android.support.v4.app.Fragment; +import androidx.annotation.StringRes; +import androidx.fragment.app.Fragment; import android.util.Log; import android.view.ContextMenu; import android.view.LayoutInflater; @@ -25,19 +25,27 @@ import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.adapter.SubscriptionsAdapter; import de.danoeh.antennapod.core.asynctask.FeedRemover; import de.danoeh.antennapod.core.dialog.ConfirmationDialog; +import de.danoeh.antennapod.core.event.DownloadEvent; import de.danoeh.antennapod.core.feed.EventDistributor; import de.danoeh.antennapod.core.feed.Feed; import de.danoeh.antennapod.core.preferences.PlaybackPreferences; +import de.danoeh.antennapod.core.service.download.DownloadService; import de.danoeh.antennapod.core.service.playback.PlaybackService; import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBWriter; +import de.danoeh.antennapod.core.storage.DownloadRequester; import de.danoeh.antennapod.core.util.FeedItemUtil; import de.danoeh.antennapod.core.util.IntentUtils; +import de.danoeh.antennapod.core.util.download.AutoUpdateManager; import de.danoeh.antennapod.dialog.RenameFeedDialog; +import de.danoeh.antennapod.menuhandler.MenuItemUtils; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; import io.reactivex.schedulers.Schedulers; +import org.greenrobot.eventbus.EventBus; +import org.greenrobot.eventbus.Subscribe; +import org.greenrobot.eventbus.ThreadMode; /** * Fragment for displaying feed subscriptions @@ -56,6 +64,7 @@ public class SubscriptionFragment extends Fragment { private SubscriptionsAdapter subscriptionAdapter; private int mPosition = -1; + private boolean isUpdatingFeeds = false; private Disposable disposable; private SharedPreferences prefs; @@ -89,6 +98,8 @@ public class SubscriptionFragment extends Fragment { menu.findItem(R.id.subscription_num_columns_3).setChecked(columns == 3); menu.findItem(R.id.subscription_num_columns_4).setChecked(columns == 4); menu.findItem(R.id.subscription_num_columns_5).setChecked(columns == 5); + + isUpdatingFeeds = MenuItemUtils.updateRefreshMenuItem(menu, R.id.refresh_item, updateRefreshMenuItemChecker); } @Override @@ -97,6 +108,9 @@ public class SubscriptionFragment extends Fragment { return true; } switch (item.getItemId()) { + case R.id.refresh_item: + AutoUpdateManager.runImmediate(requireContext()); + return true; case R.id.subscription_num_columns_2: setColumnNumber(2); return true; @@ -136,6 +150,7 @@ public class SubscriptionFragment extends Fragment { public void onStart() { super.onStart(); EventDistributor.getInstance().register(contentUpdate); + EventBus.getDefault().register(this); loadSubscriptions(); } @@ -143,6 +158,7 @@ public class SubscriptionFragment extends Fragment { public void onStop() { super.onStop(); EventDistributor.getInstance().unregister(contentUpdate); + EventBus.getDefault().unregister(this); if(disposable != null) { disposable.dispose(); } @@ -278,6 +294,17 @@ public class SubscriptionFragment extends Fragment { } }; + @Subscribe(sticky = true, threadMode = ThreadMode.MAIN) + public void onEventMainThread(DownloadEvent event) { + Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]"); + if (event.hasChangedFeedUpdateStatus(isUpdatingFeeds)) { + getActivity().invalidateOptionsMenu(); + } + } + + private final MenuItemUtils.UpdateRefreshMenuItemChecker updateRefreshMenuItemChecker = + () -> DownloadService.isRunning && DownloadRequester.getInstance().isDownloadingFeeds(); + private final SubscriptionsAdapter.ItemAccess itemAccess = new SubscriptionsAdapter.ItemAccess() { @Override public int getCount() { diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/TransitionEffect.java b/app/src/main/java/de/danoeh/antennapod/fragment/TransitionEffect.java new file mode 100644 index 000000000..461fa9da3 --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/fragment/TransitionEffect.java @@ -0,0 +1,5 @@ +package de.danoeh.antennapod.fragment; + +public enum TransitionEffect { + NONE, FLIP, FADE +} diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/GpodnetMainFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/GpodnetMainFragment.java index 4dc114f9b..380f6741a 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/GpodnetMainFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/GpodnetMainFragment.java @@ -4,11 +4,11 @@ import android.content.Context; import android.content.SharedPreferences; import android.content.res.Resources; import android.os.Bundle; -import android.support.design.widget.TabLayout; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentPagerAdapter; -import android.support.v4.view.ViewPager; +import com.google.android.material.tabs.TabLayout; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentPagerAdapter; +import androidx.viewpager.widget.ViewPager; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java index 49851ebb4..1d6debbfe 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/PodcastListFragment.java @@ -4,9 +4,9 @@ import android.content.Context; import android.content.Intent; import android.os.AsyncTask; import android.os.Bundle; -import android.support.v4.app.Fragment; -import android.support.v4.view.MenuItemCompat; -import android.support.v7.widget.SearchView; +import androidx.fragment.app.Fragment; +import androidx.core.view.MenuItemCompat; +import androidx.appcompat.widget.SearchView; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; @@ -56,7 +56,7 @@ public abstract class PodcastListFragment extends Fragment { final SearchView sv = (SearchView) MenuItemCompat.getActionView(searchItem); MenuItemUtils.adjustTextColor(getActivity(), sv); sv.setQueryHint(getString(R.string.gpodnet_search_hint)); - sv.setOnQueryTextListener(new android.support.v7.widget.SearchView.OnQueryTextListener() { + sv.setOnQueryTextListener(new androidx.appcompat.widget.SearchView.OnQueryTextListener() { @Override public boolean onQueryTextSubmit(String s) { sv.clearFocus(); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/SearchListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/SearchListFragment.java index 10bd636dd..60fc1f446 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/SearchListFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/SearchListFragment.java @@ -1,8 +1,8 @@ package de.danoeh.antennapod.fragment.gpodnet; import android.os.Bundle; -import android.support.v4.view.MenuItemCompat; -import android.support.v7.widget.SearchView; +import androidx.core.view.MenuItemCompat; +import androidx.appcompat.widget.SearchView; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagFragment.java index d39829260..92cd4ca84 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagFragment.java @@ -1,7 +1,7 @@ package de.danoeh.antennapod.fragment.gpodnet; import android.os.Bundle; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import org.apache.commons.lang3.Validate; diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java index 1e46b1ac5..b5a95bc33 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/gpodnet/TagListFragment.java @@ -4,9 +4,9 @@ import android.app.Activity; import android.content.Context; import android.os.AsyncTask; import android.os.Bundle; -import android.support.v4.app.ListFragment; -import android.support.v4.view.MenuItemCompat; -import android.support.v7.widget.SearchView; +import androidx.fragment.app.ListFragment; +import androidx.core.view.MenuItemCompat; +import androidx.appcompat.widget.SearchView; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java index a04615a00..121b7fef8 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/AutoDownloadPreferencesFragment.java @@ -1,29 +1,42 @@ package de.danoeh.antennapod.fragment.preferences; +import android.Manifest; import android.app.Activity; import android.content.Context; +import android.content.pm.PackageManager; import android.content.res.Resources; import android.net.wifi.WifiConfiguration; import android.net.wifi.WifiManager; +import android.os.Build; import android.os.Bundle; -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.PreferenceScreen; +import androidx.annotation.NonNull; +import androidx.core.content.ContextCompat; +import androidx.preference.CheckBoxPreference; +import androidx.preference.ListPreference; +import androidx.preference.Preference; +import androidx.preference.PreferenceFragmentCompat; +import androidx.preference.PreferenceScreen; import android.util.Log; -import de.danoeh.antennapod.R; -import de.danoeh.antennapod.core.preferences.UserPreferences; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.activity.PreferenceActivity; +import de.danoeh.antennapod.core.preferences.UserPreferences; + public class AutoDownloadPreferencesFragment extends PreferenceFragmentCompat { private static final String TAG = "AutoDnldPrefFragment"; + + private static final int LOCATION_PERMISSION_REQUEST_CODE = 1; + private static final String PREF_KEY_LOCATION_PERMISSION_REQUEST_PROMPT = "prefAutoDownloadWifiFilterAndroid10PermissionPrompt"; + private CheckBoxPreference[] selectedNetworks; + private Preference prefPermissionRequestPromptOnAndroid10 = null; + @Override public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { addPreferencesFromResource(R.xml.preferences_autodownload); @@ -35,6 +48,12 @@ public class AutoDownloadPreferencesFragment extends PreferenceFragmentCompat { } @Override + public void onStart() { + super.onStart(); + ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.auto_download_label); + } + + @Override public void onResume() { super.onResume(); checkAutodownloadItemVisibility(UserPreferences.isEnableAutodownload()); @@ -175,10 +194,65 @@ public class AutoDownloadPreferencesFragment extends PreferenceFragmentCompat { } private void setSelectedNetworksEnabled(boolean b) { + if (showPermissionRequestPromptOnAndroid10IfNeeded(b)) { + return; + } + if (selectedNetworks != null) { for (Preference p : selectedNetworks) { p.setEnabled(b); } } } + + @Override + public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { + if (requestCode != LOCATION_PERMISSION_REQUEST_CODE) { + return; + } + if (permissions.length > 0 && permissions[0].equals(Manifest.permission.ACCESS_FINE_LOCATION) && + grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { + buildAutodownloadSelectedNetworksPreference(); + } + } + + private boolean showPermissionRequestPromptOnAndroid10IfNeeded(boolean wifiFilterEnabled) { + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.P) { + return false; + } + + // Cases Android 10(Q) or later + if (prefPermissionRequestPromptOnAndroid10 != null) { + getPreferenceScreen().removePreference(prefPermissionRequestPromptOnAndroid10); + prefPermissionRequestPromptOnAndroid10 = null; + } + + if (ContextCompat.checkSelfPermission(requireContext(), Manifest.permission.ACCESS_FINE_LOCATION) + == PackageManager.PERMISSION_GRANTED) { + return false; + } + + // Case location permission not yet granted, permission-specific UI is needed + if (!wifiFilterEnabled) { + // Don't show the UI when WiFi filter disabled. + // it still return true, so that the caller knows + // it does not have required permission, and will not invoke codes that require so. + return true; + } + + Preference pref = new Preference(requireActivity()); + pref.setKey(PREF_KEY_LOCATION_PERMISSION_REQUEST_PROMPT); + pref.setTitle(R.string.autodl_wifi_filter_permission_title); + pref.setSummary(R.string.autodl_wifi_filter_permission_message); + pref.setIcon(R.drawable.ic_warning_red); + pref.setOnPreferenceClickListener(preference -> { + requestPermissions(new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, LOCATION_PERMISSION_REQUEST_CODE); + return true; + }); + pref.setPersistent(false); + getPreferenceScreen().addPreference(pref); + prefPermissionRequestPromptOnAndroid10 = pref; + return true; + } + } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderPreferencesFragment.java index 491922056..c6ae8e20c 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderPreferencesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/GpodderPreferencesFragment.java @@ -3,12 +3,13 @@ package de.danoeh.antennapod.fragment.preferences; import android.app.Activity; import android.content.SharedPreferences; import android.os.Bundle; -import android.support.v7.preference.Preference; -import android.support.v7.preference.PreferenceFragmentCompat; +import androidx.preference.Preference; +import androidx.preference.PreferenceFragmentCompat; import android.text.Html; import android.text.format.DateUtils; import android.widget.Toast; import de.danoeh.antennapod.R; +import de.danoeh.antennapod.activity.PreferenceActivity; import de.danoeh.antennapod.core.preferences.GpodnetPreferences; import de.danoeh.antennapod.core.service.GpodnetSyncService; import de.danoeh.antennapod.dialog.AuthenticationDialog; @@ -30,6 +31,12 @@ public class GpodderPreferencesFragment extends PreferenceFragmentCompat { } @Override + public void onStart() { + super.onStart(); + ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.gpodnet_main_label); + } + + @Override public void onResume() { super.onResume(); GpodnetPreferences.registerOnSharedPreferenceChangeListener(gpoddernetListener); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/IntegrationsPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/IntegrationsPreferencesFragment.java index 229274b76..51f31eb92 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/IntegrationsPreferencesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/IntegrationsPreferencesFragment.java @@ -1,7 +1,7 @@ package de.danoeh.antennapod.fragment.preferences; import android.os.Bundle; -import android.support.v7.preference.PreferenceFragmentCompat; +import androidx.preference.PreferenceFragmentCompat; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.PreferenceActivity; @@ -14,6 +14,12 @@ public class IntegrationsPreferencesFragment extends PreferenceFragmentCompat { setupIntegrationsScreen(); } + @Override + public void onStart() { + super.onStart(); + ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.integrations_label); + } + private void setupIntegrationsScreen() { findPreference(PREF_SCREEN_GPODDER).setOnPreferenceClickListener(preference -> { ((PreferenceActivity) getActivity()).openScreen(R.xml.preferences_gpodder); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java index dcba5fe89..00e69f1db 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/MainPreferencesFragment.java @@ -1,27 +1,16 @@ package de.danoeh.antennapod.fragment.preferences; -import android.content.ActivityNotFoundException; -import android.content.Context; import android.content.Intent; -import android.content.pm.PackageManager; -import android.content.pm.ResolveInfo; -import android.net.Uri; -import android.os.Build; import android.os.Bundle; -import android.support.v4.content.FileProvider; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.preference.PreferenceFragmentCompat; -import android.util.Log; -import android.widget.Toast; +import androidx.appcompat.app.AppCompatActivity; +import androidx.preference.PreferenceFragmentCompat; import com.bytehamster.lib.preferencesearch.SearchConfiguration; import com.bytehamster.lib.preferencesearch.SearchPreference; -import de.danoeh.antennapod.CrashReportWriter; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.AboutActivity; +import de.danoeh.antennapod.activity.BugReportActivity; import de.danoeh.antennapod.activity.PreferenceActivity; -import de.danoeh.antennapod.activity.StatisticsActivity; - -import java.util.List; +import de.danoeh.antennapod.core.util.IntentUtils; public class MainPreferencesFragment extends PreferenceFragmentCompat { private static final String TAG = "MainPreferencesFragment"; @@ -31,9 +20,9 @@ public class MainPreferencesFragment extends PreferenceFragmentCompat { 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_KNOWN_ISSUES = "prefKnownIssues"; private static final String PREF_FAQ = "prefFaq"; - private static final String PREF_SEND_CRASH_REPORT = "prefSendCrashReport"; + private static final String PREF_VIEW_MAILING_LIST = "prefViewMailingList"; + private static final String PREF_SEND_BUG_REPORT = "prefSendBugReport"; private static final String STATISTICS = "statistics"; private static final String PREF_ABOUT = "prefAbout"; @@ -44,6 +33,12 @@ public class MainPreferencesFragment extends PreferenceFragmentCompat { setupSearch(); } + @Override + public void onStart() { + super.onStart(); + ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.settings_label); + } + private void setupMainScreen() { findPreference(PREF_SCREEN_USER_INTERFACE).setOnPreferenceClickListener(preference -> { ((PreferenceActivity) getActivity()).openScreen(R.xml.preferences_user_interface); @@ -74,53 +69,25 @@ public class MainPreferencesFragment extends PreferenceFragmentCompat { ); findPreference(STATISTICS).setOnPreferenceClickListener( preference -> { - startActivity(new Intent(getActivity(), StatisticsActivity.class)); + getFragmentManager().beginTransaction().replace(R.id.content, new StatisticsFragment()) + .addToBackStack(getString(R.string.statistics_label)).commit(); return true; } ); - findPreference(PREF_KNOWN_ISSUES).setOnPreferenceClickListener(preference -> { - openInBrowser("https://github.com/AntennaPod/AntennaPod/labels/bug"); + findPreference(PREF_FAQ).setOnPreferenceClickListener(preference -> { + IntentUtils.openInBrowser(getContext(), "https://antennapod.org/faq.html"); return true; }); - findPreference(PREF_FAQ).setOnPreferenceClickListener(preference -> { - openInBrowser("https://antennapod.org/faq.html"); + findPreference(PREF_VIEW_MAILING_LIST).setOnPreferenceClickListener(preference -> { + IntentUtils.openInBrowser(getContext(), "https://groups.google.com/forum/#!forum/antennapod"); return true; }); - findPreference(PREF_SEND_CRASH_REPORT).setOnPreferenceClickListener(preference -> { - Context context = getActivity().getApplicationContext(); - Intent emailIntent = new Intent(Intent.ACTION_SEND); - emailIntent.setType("text/plain"); - emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[]{"Martin.Fietz@gmail.com"}); - emailIntent.putExtra(Intent.EXTRA_SUBJECT, "AntennaPod Crash Report"); - emailIntent.putExtra(Intent.EXTRA_TEXT, "Please describe what you were doing when the app crashed"); - // the attachment - Uri fileUri = FileProvider.getUriForFile(context, context.getString(R.string.provider_authority), - CrashReportWriter.getFile()); - emailIntent.putExtra(Intent.EXTRA_STREAM, fileUri); - emailIntent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - String intentTitle = getActivity().getString(R.string.send_email); - if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { - List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(emailIntent, PackageManager.MATCH_DEFAULT_ONLY); - for (ResolveInfo resolveInfo : resInfoList) { - String packageName = resolveInfo.activityInfo.packageName; - context.grantUriPermission(packageName, fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION); - } - } - getActivity().startActivity(Intent.createChooser(emailIntent, intentTitle)); + findPreference(PREF_SEND_BUG_REPORT).setOnPreferenceClickListener(preference -> { + startActivity(new Intent(getActivity(), BugReportActivity.class)); return true; }); } - private void openInBrowser(String url) { - try { - Intent myIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url)); - startActivity(myIntent); - } catch (ActivityNotFoundException e) { - Toast.makeText(getActivity(), R.string.pref_no_browser_found, Toast.LENGTH_LONG).show(); - Log.e(TAG, Log.getStackTraceString(e)); - } - } - private void setupSearch() { SearchPreference searchPreference = (SearchPreference) findPreference("searchPreference"); SearchConfiguration config = searchPreference.getSearchConfiguration(); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NetworkPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NetworkPreferencesFragment.java index ac2436e25..440660942 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NetworkPreferencesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/NetworkPreferencesFragment.java @@ -4,10 +4,10 @@ import android.app.TimePickerDialog; import android.content.Context; import android.content.res.Resources; import android.os.Bundle; -import android.support.v7.app.AlertDialog; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.preference.Preference; -import android.support.v7.preference.PreferenceFragmentCompat; +import androidx.appcompat.app.AlertDialog; +import androidx.appcompat.app.AppCompatActivity; +import androidx.preference.Preference; +import androidx.preference.PreferenceFragmentCompat; import android.text.format.DateFormat; import com.afollestad.materialdialogs.MaterialDialog; import de.danoeh.antennapod.R; @@ -31,6 +31,12 @@ public class NetworkPreferencesFragment extends PreferenceFragmentCompat { } @Override + public void onStart() { + super.onStart(); + ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.network_pref); + } + + @Override public void onResume() { super.onResume(); setUpdateIntervalText(); diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java index e1714d4bd..1795dfc29 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/PlaybackPreferencesFragment.java @@ -4,10 +4,11 @@ import android.app.Activity; import android.content.res.Resources; import android.os.Build; import android.os.Bundle; -import android.support.v7.preference.ListPreference; -import android.support.v7.preference.PreferenceFragmentCompat; +import androidx.preference.ListPreference; +import androidx.preference.PreferenceFragmentCompat; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MediaplayerActivity; +import de.danoeh.antennapod.activity.PreferenceActivity; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.util.gui.PictureInPictureUtil; import de.danoeh.antennapod.dialog.VariableSpeedDialog; @@ -28,6 +29,12 @@ public class PlaybackPreferencesFragment extends PreferenceFragmentCompat { } @Override + public void onStart() { + super.onStart(); + ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.playback_pref); + } + + @Override public void onResume() { super.onResume(); checkSonicItemVisibility(); diff --git a/app/src/main/java/de/danoeh/antennapod/activity/StatisticsActivity.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StatisticsFragment.java index 37199ccf7..2b5310837 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/StatisticsActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StatisticsFragment.java @@ -1,25 +1,27 @@ -package de.danoeh.antennapod.activity; +package de.danoeh.antennapod.fragment.preferences; +import android.content.Context; import android.content.SharedPreferences; import android.os.Bundle; -import android.support.v7.app.AlertDialog; -import android.support.v7.app.AppCompatActivity; +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.appcompat.app.AlertDialog; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; import android.util.Log; +import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; -import android.widget.AdapterView; -import android.widget.ListView; +import android.view.ViewGroup; import android.widget.ProgressBar; import android.widget.RadioButton; -import android.widget.TextView; - import de.danoeh.antennapod.R; +import de.danoeh.antennapod.activity.PreferenceActivity; import de.danoeh.antennapod.adapter.StatisticsListAdapter; -import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.storage.DBReader; -import de.danoeh.antennapod.core.util.Converter; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; @@ -28,70 +30,63 @@ import io.reactivex.schedulers.Schedulers; /** * Displays the 'statistics' screen */ -public class StatisticsActivity extends AppCompatActivity - implements AdapterView.OnItemClickListener { - - private static final String TAG = StatisticsActivity.class.getSimpleName(); +public class StatisticsFragment extends Fragment { + private static final String TAG = StatisticsFragment.class.getSimpleName(); private static final String PREF_NAME = "StatisticsActivityPrefs"; private static final String PREF_COUNT_ALL = "countAll"; private Disposable disposable; - private TextView totalTimeTextView; - private ListView feedStatisticsList; + private RecyclerView feedStatisticsList; private ProgressBar progressBar; private StatisticsListAdapter listAdapter; private boolean countAll = false; private SharedPreferences prefs; @Override - protected void onCreate(Bundle savedInstanceState) { - setTheme(UserPreferences.getTheme()); + public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); - getSupportActionBar().setDisplayShowHomeEnabled(true); - setContentView(R.layout.statistics_activity); - - prefs = getSharedPreferences(PREF_NAME, MODE_PRIVATE); + prefs = getContext().getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE); countAll = prefs.getBoolean(PREF_COUNT_ALL, false); + setHasOptionsMenu(true); + } - totalTimeTextView = findViewById(R.id.total_time); - feedStatisticsList = findViewById(R.id.statistics_list); - progressBar = findViewById(R.id.progressBar); - listAdapter = new StatisticsListAdapter(this); + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + View root = inflater.inflate(R.layout.statistics_activity, container, false); + feedStatisticsList = root.findViewById(R.id.statistics_list); + progressBar = root.findViewById(R.id.progressBar); + listAdapter = new StatisticsListAdapter(getContext()); listAdapter.setCountAll(countAll); + feedStatisticsList.setLayoutManager(new LinearLayoutManager(getContext())); feedStatisticsList.setAdapter(listAdapter); - feedStatisticsList.setOnItemClickListener(this); + return root; } @Override - public void onResume() { - super.onResume(); + public void onStart() { + super.onStart(); + ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.statistics_label); refreshStatistics(); } @Override - public boolean onCreateOptionsMenu(Menu menu) { - super.onCreateOptionsMenu(menu); - MenuInflater inflater = getMenuInflater(); + public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { inflater.inflate(R.menu.statistics, menu); - return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { - if (item.getItemId() == android.R.id.home) { - finish(); - return true; - } else if (item.getItemId() == R.id.statistics_mode) { + if (item.getItemId() == R.id.statistics_mode) { selectStatisticsMode(); return true; - } else { - return super.onOptionsItemSelected(item); } + return super.onOptionsItemSelected(item); } private void selectStatisticsMode() { - View contentView = View.inflate(this, R.layout.statistics_mode_select_dialog, null); - AlertDialog.Builder builder = new AlertDialog.Builder(this); + View contentView = View.inflate(getContext(), R.layout.statistics_mode_select_dialog, null); + AlertDialog.Builder builder = new AlertDialog.Builder(getContext()); builder.setView(contentView); builder.setTitle(R.string.statistics_mode); @@ -113,7 +108,6 @@ public class StatisticsActivity extends AppCompatActivity private void refreshStatistics() { progressBar.setVisibility(View.VISIBLE); - totalTimeTextView.setVisibility(View.GONE); feedStatisticsList.setVisibility(View.GONE); loadStatistics(); } @@ -126,28 +120,9 @@ public class StatisticsActivity extends AppCompatActivity .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(result -> { - totalTimeTextView.setText(Converter - .shortLocalizedDuration(this, countAll ? result.totalTimeCountAll : result.totalTime)); - listAdapter.update(result.feedTime); + listAdapter.update(result); progressBar.setVisibility(View.GONE); - totalTimeTextView.setVisibility(View.VISIBLE); feedStatisticsList.setVisibility(View.VISIBLE); }, error -> Log.e(TAG, Log.getStackTraceString(error))); } - - @Override - public void onItemClick(AdapterView<?> parent, View view, int position, long id) { - DBReader.StatisticsItem stats = listAdapter.getItem(position); - - AlertDialog.Builder dialog = new AlertDialog.Builder(this); - dialog.setTitle(stats.feed.getTitle()); - dialog.setMessage(getString(R.string.statistics_details_dialog, - countAll ? stats.episodesStartedIncludingMarked : stats.episodesStarted, - stats.episodes, - Converter.shortLocalizedDuration(this, countAll ? - stats.timePlayedCountAll : stats.timePlayed), - Converter.shortLocalizedDuration(this, stats.time))); - dialog.setPositiveButton(android.R.string.ok, null); - dialog.show(); - } } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StoragePreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StoragePreferencesFragment.java index b4226b546..2c1590c47 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StoragePreferencesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/StoragePreferencesFragment.java @@ -4,6 +4,7 @@ import android.Manifest; import android.annotation.SuppressLint; import android.app.Activity; import android.app.ProgressDialog; +import android.content.ActivityNotFoundException; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; @@ -11,15 +12,19 @@ import android.content.pm.ResolveInfo; import android.net.Uri; import android.os.Build; import android.os.Bundle; -import android.support.v4.app.ActivityCompat; -import android.support.v4.content.FileProvider; -import android.support.v7.app.AlertDialog; -import android.support.v7.preference.PreferenceFragmentCompat; +import androidx.core.app.ActivityCompat; +import androidx.core.content.FileProvider; +import androidx.documentfile.provider.DocumentFile; +import androidx.appcompat.app.AlertDialog; +import androidx.preference.PreferenceFragmentCompat; import android.util.Log; + import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.DirectoryChooserActivity; import de.danoeh.antennapod.activity.ImportExportActivity; import de.danoeh.antennapod.activity.OpmlImportFromPathActivity; +import de.danoeh.antennapod.activity.PreferenceActivity; +import de.danoeh.antennapod.asynctask.DocumentFileExportWorker; import de.danoeh.antennapod.asynctask.ExportWorker; import de.danoeh.antennapod.core.export.ExportWriter; import de.danoeh.antennapod.core.export.html.HtmlWriter; @@ -45,6 +50,12 @@ public class StoragePreferencesFragment extends PreferenceFragmentCompat { Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE }; private static final int PERMISSION_REQUEST_EXTERNAL_STORAGE = 41; + private static final int CHOOSE_OPML_EXPORT_PATH = 1; + private static final String DEFAULT_OPML_OUTPUT_NAME = "antennapod-feeds.opml"; + private static final String CONTENT_TYPE_OPML = "text/x-opml"; + private static final int CHOOSE_HTML_EXPORT_PATH = 2; + private static final String DEFAULT_HTML_OUTPUT_NAME = "antennapod-feeds.html"; + private static final String CONTENT_TYPE_HTML = "text/html"; private Disposable disposable; @Override @@ -54,11 +65,25 @@ public class StoragePreferencesFragment extends PreferenceFragmentCompat { } @Override + public void onStart() { + super.onStart(); + ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.storage_pref); + } + + @Override public void onResume() { super.onResume(); setDataFolderText(); } + @Override + public void onStop() { + super.onStop(); + if (disposable != null) { + disposable.dispose(); + } + } + private void setupStorageScreen() { final Activity activity = getActivity(); @@ -69,9 +94,16 @@ public class StoragePreferencesFragment extends PreferenceFragmentCompat { } ); findPreference(PREF_OPML_EXPORT).setOnPreferenceClickListener( - preference -> export(new OpmlWriter())); + preference -> { + openOpmlExportPathPicker(); + return true; + } + ); findPreference(PREF_HTML_EXPORT).setOnPreferenceClickListener( - preference -> export(new HtmlWriter())); + preference -> { + openHtmlExportPathPicker(); + return true; + }); findPreference(PREF_OPML_IMPORT).setOnPreferenceClickListener( preference -> { activity.startActivity(new Intent(activity, OpmlImportFromPathActivity.class)); @@ -129,52 +161,65 @@ public class StoragePreferencesFragment extends PreferenceFragmentCompat { } private boolean export(ExportWriter exportWriter) { + return export(exportWriter, null); + } + + private boolean export(ExportWriter exportWriter, final Uri uri) { Context context = getActivity(); final ProgressDialog progressDialog = new ProgressDialog(context); progressDialog.setMessage(context.getString(R.string.exporting_label)); progressDialog.setIndeterminate(true); progressDialog.show(); - final AlertDialog.Builder alert = new AlertDialog.Builder(context) - .setNeutralButton(android.R.string.ok, (dialog, which) -> dialog.dismiss()); - Observable<File> observable = new ExportWorker(exportWriter).exportObservable(); - disposable = observable.subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(output -> { - alert.setTitle(R.string.export_success_title); - String message = context.getString(R.string.export_success_sum, output.toString()); - alert.setMessage(message); - alert.setPositiveButton(R.string.send_label, (dialog, which) -> { + if (uri == null) { + Observable<File> observable = new ExportWorker(exportWriter).exportObservable(); + disposable = observable.subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(output -> { Uri fileUri = FileProvider.getUriForFile(context.getApplicationContext(), context.getString(R.string.provider_authority), output); - Intent sendIntent = new Intent(Intent.ACTION_SEND); - sendIntent.putExtra(Intent.EXTRA_SUBJECT, - context.getResources().getText(R.string.opml_export_label)); - sendIntent.putExtra(Intent.EXTRA_STREAM, fileUri); - sendIntent.setType("text/plain"); - sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); - if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { - List<ResolveInfo> resInfoList = context.getPackageManager().queryIntentActivities(sendIntent, PackageManager.MATCH_DEFAULT_ONLY); - for (ResolveInfo resolveInfo : resInfoList) { - String packageName = resolveInfo.activityInfo.packageName; - context.grantUriPermission(packageName, fileUri, Intent.FLAG_GRANT_READ_URI_PERMISSION); - } - } - context.startActivity(Intent.createChooser(sendIntent, - context.getResources().getText(R.string.send_label))); - }); - alert.create().show(); - }, error -> { - alert.setTitle(R.string.export_error_label); - alert.setMessage(error.getMessage()); - alert.show(); - }, progressDialog::dismiss); + showExportSuccessDialog(context.getString(R.string.export_success_sum, output.toString()), fileUri); + }, this::showExportErrorDialog, progressDialog::dismiss); + } else { + Observable<DocumentFile> observable = new DocumentFileExportWorker(exportWriter, context, uri).exportObservable(); + disposable = observable.subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(output -> { + showExportSuccessDialog(context.getString(R.string.export_success_sum, output.getUri()), output.getUri()); + }, this::showExportErrorDialog, progressDialog::dismiss); + } return true; } - public void unsubscribeExportSubscription() { - if (disposable != null) { - disposable.dispose(); - } + private void showExportSuccessDialog(final String message, final Uri streamUri) { + final AlertDialog.Builder alert = new AlertDialog.Builder(getContext()) + .setNeutralButton(android.R.string.ok, (dialog, which) -> dialog.dismiss()); + alert.setTitle(R.string.export_success_title); + alert.setMessage(message); + alert.setPositiveButton(R.string.send_label, (dialog, which) -> { + Intent sendIntent = new Intent(Intent.ACTION_SEND); + sendIntent.putExtra(Intent.EXTRA_SUBJECT, getString(R.string.opml_export_label)); + sendIntent.putExtra(Intent.EXTRA_STREAM, streamUri); + sendIntent.setType("text/plain"); + sendIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) { + List<ResolveInfo> resInfoList = getContext().getPackageManager() + .queryIntentActivities(sendIntent, PackageManager.MATCH_DEFAULT_ONLY); + for (ResolveInfo resolveInfo : resInfoList) { + String packageName = resolveInfo.activityInfo.packageName; + getContext().grantUriPermission(packageName, streamUri, Intent.FLAG_GRANT_READ_URI_PERMISSION); + } + } + getContext().startActivity(Intent.createChooser(sendIntent, getString(R.string.send_label))); + }); + alert.create().show(); + } + + private void showExportErrorDialog(final Throwable error) { + final AlertDialog.Builder alert = new AlertDialog.Builder(getContext()) + .setNeutralButton(android.R.string.ok, (dialog, which) -> dialog.dismiss()); + alert.setTitle(R.string.export_error_label); + alert.setMessage(error.getMessage()); + alert.show(); } @SuppressLint("NewApi") @@ -184,22 +229,22 @@ public class StoragePreferencesFragment extends PreferenceFragmentCompat { String dir = data.getStringExtra(DirectoryChooserActivity.RESULT_SELECTED_DIR); File path; - if(dir != null) { + if (dir != null) { path = new File(dir); } else { path = getActivity().getExternalFilesDir(null); } String message = null; - final Context context= getActivity().getApplicationContext(); - if(!path.exists()) { + final Context context = getActivity().getApplicationContext(); + if (!path.exists()) { message = String.format(context.getString(R.string.folder_does_not_exist_error), dir); - } else if(!path.canRead()) { + } else if (!path.canRead()) { message = String.format(context.getString(R.string.folder_not_readable_error), dir); - } else if(!path.canWrite()) { + } else if (!path.canWrite()) { message = String.format(context.getString(R.string.folder_not_writable_error), dir); } - if(message == null) { + if (message == null) { Log.d(TAG, "Setting data folder: " + dir); UserPreferences.setDataFolder(dir); setDataFolderText(); @@ -210,6 +255,16 @@ public class StoragePreferencesFragment extends PreferenceFragmentCompat { ab.show(); } } + + if (resultCode == Activity.RESULT_OK && requestCode == CHOOSE_OPML_EXPORT_PATH) { + Uri uri = data.getData(); + export(new OpmlWriter(), uri); + } + + if (resultCode == Activity.RESULT_OK && requestCode == CHOOSE_HTML_EXPORT_PATH) { + Uri uri = data.getData(); + export(new HtmlWriter(), uri); + } } private void setDataFolderText() { @@ -231,6 +286,50 @@ public class StoragePreferencesFragment extends PreferenceFragmentCompat { activity.startActivityForResult(intent, DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED); } + private void openOpmlExportPathPicker() { + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2) { + Intent intentPickAction = new Intent(Intent.ACTION_CREATE_DOCUMENT) + .addCategory(Intent.CATEGORY_OPENABLE) + .setType(CONTENT_TYPE_OPML) + .putExtra(Intent.EXTRA_TITLE, DEFAULT_OPML_OUTPUT_NAME); + + // Creates an implicit intent to launch a file manager which lets + // the user choose a specific directory to export to. + try { + startActivityForResult(intentPickAction, CHOOSE_OPML_EXPORT_PATH); + return; + } catch (ActivityNotFoundException e) { + Log.e(TAG, "No activity found. Should never happen..."); + } + } + + // If we are using a SDK lower than API 21 or the implicit intent failed + // fallback to the legacy export process + export(new OpmlWriter()); + } + + private void openHtmlExportPathPicker() { + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2) { + Intent intentPickAction = new Intent(Intent.ACTION_CREATE_DOCUMENT) + .addCategory(Intent.CATEGORY_OPENABLE) + .setType(CONTENT_TYPE_HTML) + .putExtra(Intent.EXTRA_TITLE, DEFAULT_HTML_OUTPUT_NAME); + + // Creates an implicit intent to launch a file manager which lets + // the user choose a specific directory to export to. + try { + startActivityForResult(intentPickAction, CHOOSE_HTML_EXPORT_PATH); + return; + } catch (ActivityNotFoundException e) { + Log.e(TAG, "No activity found. Should never happen..."); + } + } + + // If we are using a SDK lower than API 21 or the implicit intent failed + // fallback to the legacy export process + export(new HtmlWriter()); + } + private void showChooseDataFolderDialog() { ChooseDataFolderDialog.showDialog( getActivity(), new ChooseDataFolderDialog.RunnableWithString() { diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/UserInterfacePreferencesFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/UserInterfacePreferencesFragment.java index e1d44f7d3..191999cf7 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/preferences/UserInterfacePreferencesFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/preferences/UserInterfacePreferencesFragment.java @@ -4,13 +4,14 @@ import android.content.Context; import android.content.Intent; import android.os.Build; import android.os.Bundle; -import android.support.design.widget.Snackbar; -import android.support.v7.app.AlertDialog; -import android.support.v7.preference.PreferenceFragmentCompat; +import com.google.android.material.snackbar.Snackbar; +import androidx.appcompat.app.AlertDialog; +import androidx.preference.PreferenceFragmentCompat; import android.widget.ListView; import android.widget.Toast; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; +import de.danoeh.antennapod.activity.PreferenceActivity; import de.danoeh.antennapod.core.preferences.UserPreferences; import org.apache.commons.lang3.ArrayUtils; @@ -25,6 +26,12 @@ public class UserInterfacePreferencesFragment extends PreferenceFragmentCompat { setupInterfaceScreen(); } + @Override + public void onStart() { + super.onStart(); + ((PreferenceActivity) getActivity()).getSupportActionBar().setTitle(R.string.user_interface_label); + } + private void setupInterfaceScreen() { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { 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 156657a00..fb2675db9 100644 --- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java +++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedItemMenuHandler.java @@ -1,11 +1,11 @@ package de.danoeh.antennapod.menuhandler; import android.content.Context; -import android.content.Intent; -import android.net.Uri; -import android.support.annotation.Nullable; +import android.os.Handler; +import androidx.annotation.NonNull; +import com.google.android.material.snackbar.Snackbar; +import androidx.fragment.app.Fragment; import android.util.Log; -import android.widget.Toast; import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.feed.FeedItem; @@ -18,7 +18,6 @@ import de.danoeh.antennapod.core.service.playback.PlaybackService; 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; /** @@ -51,35 +50,21 @@ public class FeedItemMenuHandler { * @param mi An instance of MenuInterface that the method uses to change a * MenuItem's visibility * @param selectedItem The FeedItem for which the menu is supposed to be prepared - * @param showExtendedMenu True if MenuItems that let the user share information about - * the FeedItem and visit its website should be set visible. This - * parameter should be set to false if the menu space is limited. - * @param queueAccess Used for testing if the queue contains the selected item; only used for - * move to top/bottom in the queue * @return Returns true if selectedItem is not null. */ public static boolean onPrepareMenu(MenuInterface mi, - FeedItem selectedItem, - boolean showExtendedMenu, - @Nullable LongList queueAccess) { + FeedItem selectedItem) { if (selectedItem == null) { return false; } boolean hasMedia = selectedItem.getMedia() != null; boolean isPlaying = hasMedia && selectedItem.getState() == FeedItem.State.PLAYING; - boolean keepSorted = UserPreferences.isQueueKeepSorted(); if (!isPlaying) { mi.setItemVisibility(R.id.skip_episode_item, false); } boolean isInQueue = selectedItem.isTagged(FeedItem.TAG_QUEUE); - if (queueAccess == null || queueAccess.size() == 0 || queueAccess.get(0) == selectedItem.getId() || keepSorted) { - mi.setItemVisibility(R.id.move_to_top_item, false); - } - if (queueAccess == null || queueAccess.size() == 0 || queueAccess.get(queueAccess.size()-1) == selectedItem.getId() || keepSorted) { - mi.setItemVisibility(R.id.move_to_bottom_item, false); - } if (!isInQueue) { mi.setItemVisibility(R.id.remove_from_queue_item, false); } @@ -87,12 +72,12 @@ public class FeedItemMenuHandler { mi.setItemVisibility(R.id.add_to_queue_item, false); } - if (!showExtendedMenu || !ShareUtils.hasLinkToShare(selectedItem)) { + if (!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); } - if (!showExtendedMenu || !hasMedia || selectedItem.getMedia().getDownload_url() == null) { + if (!hasMedia || selectedItem.getMedia().getDownload_url() == null) { mi.setItemVisibility(R.id.share_download_url_item, false); mi.setItemVisibility(R.id.share_download_url_with_position_item, false); } @@ -104,6 +89,7 @@ public class FeedItemMenuHandler { boolean fileDownloaded = hasMedia && selectedItem.getMedia().fileExists(); mi.setItemVisibility(R.id.share_file, fileDownloaded); + mi.setItemVisibility(R.id.remove_new_flag_item, selectedItem.isNew()); if (selectedItem.isPlayed()) { mi.setItemVisibility(R.id.mark_read_item, false); } else { @@ -114,7 +100,7 @@ public class FeedItemMenuHandler { mi.setItemVisibility(R.id.reset_position, false); } - if(!UserPreferences.isEnableAutodownload()) { + if(!UserPreferences.isEnableAutodownload() || fileDownloaded) { mi.setItemVisibility(R.id.activate_auto_download, false); mi.setItemVisibility(R.id.deactivate_auto_download, false); } else if(selectedItem.getAutoDownload()) { @@ -141,10 +127,8 @@ public class FeedItemMenuHandler { */ public static boolean onPrepareMenu(MenuInterface mi, FeedItem selectedItem, - boolean showExtendedMenu, - LongList queueAccess, int... excludeIds) { - boolean rc = onPrepareMenu(mi, selectedItem, showExtendedMenu, queueAccess); + boolean rc = onPrepareMenu(mi, selectedItem); if (rc && excludeIds != null) { for (int id : excludeIds) { mi.setItemVisibility(id, false); @@ -153,8 +137,16 @@ public class FeedItemMenuHandler { return rc; } - public static boolean onMenuItemClicked(Context context, int menuItemId, - FeedItem selectedItem) { + /** + * Default menu handling for the given FeedItem. + * + * A Fragment instance, (rather than the more generic Context), is needed as a parameter + * to support some UI operations, e.g., creating a Snackbar. + */ + public static boolean onMenuItemClicked(@NonNull Fragment fragment, int menuItemId, + @NonNull FeedItem selectedItem) { + + @NonNull Context context = fragment.requireContext(); switch (menuItemId) { case R.id.skip_episode_item: IntentUtils.sendLocalBroadcast(context, PlaybackService.ACTION_SKIP_CURRENT_EPISODE); @@ -162,6 +154,9 @@ public class FeedItemMenuHandler { case R.id.remove_item: DBWriter.deleteFeedMediaOfItem(context, selectedItem.getMedia().getId()); break; + case R.id.remove_new_flag_item: + removeNewFlagWithUndo(fragment, selectedItem); + break; case R.id.mark_read_item: selectedItem.setPlayed(true); DBWriter.markItemPlayed(selectedItem, FeedItem.PLAYED, false); @@ -216,14 +211,7 @@ public class FeedItemMenuHandler { DBWriter.setFeedItemAutoDownload(selectedItem, false); break; case R.id.visit_website_item: - Uri uri = Uri.parse(FeedItemUtil.getLinkWithFallback(selectedItem)); - Intent intent = new Intent(Intent.ACTION_VIEW, uri); - if(IntentUtils.isCallable(context, intent)) { - context.startActivity(intent); - } else { - Toast.makeText(context, context.getString(R.string.download_error_malformed_url), - Toast.LENGTH_SHORT).show(); - } + IntentUtils.openInBrowser(context, FeedItemUtil.getLinkWithFallback(selectedItem)); break; case R.id.share_link_item: ShareUtils.shareFeedItemLink(context, selectedItem); @@ -249,4 +237,39 @@ public class FeedItemMenuHandler { return true; } + /** + * Remove new flag with additional UI logic to allow undo with Snackbar. + * + * Undo is useful for Remove new flag, given there is no UI to undo it otherwise + * ,i.e., there is (context) menu item for add new flag + */ + public static void removeNewFlagWithUndo(@NonNull Fragment fragment, FeedItem item) { + if (item == null) { + return; + } + + Log.d(TAG, "removeNewFlagWithUndo(" + item.getId() + ")"); + // we're marking it as unplayed since the user didn't actually play it + // but they don't want it considered 'NEW' anymore + DBWriter.markItemPlayed(FeedItem.UNPLAYED, item.getId()); + + final Handler h = new Handler(fragment.requireContext().getMainLooper()); + final Runnable r = () -> { + FeedMedia media = item.getMedia(); + if (media != null && media.hasAlmostEnded() && UserPreferences.isAutoDelete()) { + DBWriter.deleteFeedMediaOfItem(fragment.requireContext(), media.getId()); + } + }; + + Snackbar snackbar = Snackbar.make(fragment.getView(), fragment.getString(R.string.removed_new_flag_label), + Snackbar.LENGTH_LONG); + snackbar.setAction(fragment.getString(R.string.undo), v -> { + DBWriter.markItemPlayed(FeedItem.NEW, item.getId()); + // don't forget to cancel the thing that's going to remove the media + h.removeCallbacks(r); + }); + snackbar.show(); + h.postDelayed(r, (int) Math.ceil(snackbar.getDuration() * 1.05f)); + } + } diff --git a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java index 3949119fc..5d012168e 100644 --- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java +++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java @@ -2,25 +2,17 @@ package de.danoeh.antennapod.menuhandler; import android.content.Context; import android.content.DialogInterface; -import android.content.Intent; -import android.net.Uri; -import android.support.v7.app.AlertDialog; -import android.text.TextUtils; +import androidx.appcompat.app.AlertDialog; import android.util.Log; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; -import android.widget.Toast; -import java.util.Arrays; -import java.util.HashSet; -import java.util.Iterator; import java.util.Set; import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.dialog.ConfirmationDialog; import de.danoeh.antennapod.core.feed.Feed; -import de.danoeh.antennapod.core.feed.FeedItemFilter; import de.danoeh.antennapod.core.storage.DBTasks; import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.storage.DownloadRequestException; @@ -86,14 +78,7 @@ public class FeedMenuHandler { conDialog.createNewDialog().show(); break; case R.id.visit_website_item: - Uri uri = Uri.parse(selectedFeed.getLink()); - Intent intent = new Intent(Intent.ACTION_VIEW, uri); - if(IntentUtils.isCallable(context, intent)) { - context.startActivity(intent); - } else { - Toast.makeText(context, context.getString(R.string.download_error_malformed_url), - Toast.LENGTH_SHORT).show(); - } + IntentUtils.openInBrowser(context, selectedFeed.getLink()); break; case R.id.share_link_item: ShareUtils.shareFeedlink(context, selectedFeed); diff --git a/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java b/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java index 7b9fcad9b..4a57726b1 100644 --- a/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java +++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/MenuItemUtils.java @@ -4,7 +4,7 @@ import android.content.Context; import android.content.res.TypedArray; import android.graphics.Color; import android.os.Build; -import android.support.v7.widget.SearchView; +import androidx.appcompat.widget.SearchView; import android.view.Menu; import android.view.MenuItem; import android.widget.EditText; diff --git a/app/src/main/java/de/danoeh/antennapod/preferences/MasterSwitchPreference.java b/app/src/main/java/de/danoeh/antennapod/preferences/MasterSwitchPreference.java index b810cbfa6..007457c24 100644 --- a/app/src/main/java/de/danoeh/antennapod/preferences/MasterSwitchPreference.java +++ b/app/src/main/java/de/danoeh/antennapod/preferences/MasterSwitchPreference.java @@ -4,8 +4,8 @@ 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 androidx.preference.SwitchPreference; +import androidx.preference.PreferenceViewHolder; import android.util.AttributeSet; import android.util.TypedValue; import android.widget.TextView; diff --git a/app/src/main/java/de/danoeh/antennapod/preferences/NumberPickerPreference.java b/app/src/main/java/de/danoeh/antennapod/preferences/NumberPickerPreference.java index 50e76838c..a58986241 100644 --- a/app/src/main/java/de/danoeh/antennapod/preferences/NumberPickerPreference.java +++ b/app/src/main/java/de/danoeh/antennapod/preferences/NumberPickerPreference.java @@ -2,7 +2,7 @@ package de.danoeh.antennapod.preferences; import android.app.AlertDialog; import android.content.Context; -import android.support.v7.preference.Preference; +import androidx.preference.Preference; import android.text.InputFilter; import android.util.AttributeSet; import android.view.View; diff --git a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java index 1879dcbf8..6392d0535 100644 --- a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java +++ b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java @@ -3,8 +3,11 @@ package de.danoeh.antennapod.preferences; import android.content.Context; import android.content.SharedPreferences; import android.preference.PreferenceManager; + import de.danoeh.antennapod.BuildConfig; +import de.danoeh.antennapod.R; import de.danoeh.antennapod.core.preferences.UserPreferences; +import de.danoeh.antennapod.core.util.download.AutoUpdateManager; import de.danoeh.antennapod.core.util.gui.NotificationUtils; public class PreferenceUpgrader { @@ -21,7 +24,7 @@ public class PreferenceUpgrader { if (oldVersion != newVersion) { NotificationUtils.createChannels(context); - UserPreferences.restartUpdateAlarm(); + AutoUpdateManager.restartUpdateAlarm(); upgrade(oldVersion); upgraderPrefs.edit().putInt(PREF_CONFIGURED_VERSION, newVersion).apply(); @@ -62,5 +65,13 @@ public class PreferenceUpgrader { break; } } + if (oldVersion < 1070400) { + int theme = UserPreferences.getTheme(); + if (theme == R.style.Theme_AntennaPod_Light) { + prefs.edit().putString(UserPreferences.PREF_THEME, "system").apply(); + } + + UserPreferences.setQueueLocked(false); + } } } diff --git a/app/src/main/java/de/danoeh/antennapod/spa/SPAUtil.java b/app/src/main/java/de/danoeh/antennapod/spa/SPAUtil.java index 03958508d..5fa6588d9 100644 --- a/app/src/main/java/de/danoeh/antennapod/spa/SPAUtil.java +++ b/app/src/main/java/de/danoeh/antennapod/spa/SPAUtil.java @@ -46,7 +46,7 @@ public class SPAUtil { SharedPreferences.Editor editor = prefs.edit(); editor.putBoolean(PREF_HAS_QUERIED_SP_APPS, true); - editor.commit(); + editor.apply(); return true; } else { @@ -63,7 +63,7 @@ public class SPAUtil { SharedPreferences.Editor editor = PreferenceManager .getDefaultSharedPreferences(c.getApplicationContext()).edit(); editor.putBoolean(PREF_HAS_QUERIED_SP_APPS, false); - editor.commit(); + editor.apply(); } } } diff --git a/app/src/main/java/de/danoeh/antennapod/view/EmptyViewHandler.java b/app/src/main/java/de/danoeh/antennapod/view/EmptyViewHandler.java index 8b886e699..1516c4eb6 100644 --- a/app/src/main/java/de/danoeh/antennapod/view/EmptyViewHandler.java +++ b/app/src/main/java/de/danoeh/antennapod/view/EmptyViewHandler.java @@ -2,9 +2,9 @@ package de.danoeh.antennapod.view; import android.content.Context;
import android.graphics.drawable.Drawable;
-import android.support.annotation.AttrRes;
-import android.support.v4.content.ContextCompat;
-import android.support.v7.widget.RecyclerView;
+import androidx.annotation.AttrRes;
+import androidx.core.content.ContextCompat;
+import androidx.recyclerview.widget.RecyclerView;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
diff --git a/app/src/main/java/de/danoeh/antennapod/view/PieChartView.java b/app/src/main/java/de/danoeh/antennapod/view/PieChartView.java new file mode 100644 index 000000000..ada5401d8 --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/view/PieChartView.java @@ -0,0 +1,121 @@ +package de.danoeh.antennapod.view; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorFilter; +import android.graphics.Paint; +import android.graphics.PixelFormat; +import android.graphics.RectF; +import android.graphics.drawable.Drawable; +import android.util.AttributeSet; +import androidx.annotation.NonNull; +import androidx.appcompat.widget.AppCompatImageView; +import io.reactivex.annotations.Nullable; + +public class PieChartView extends AppCompatImageView { + private PieChartDrawable drawable; + + public PieChartView(Context context) { + super(context); + setup(); + } + + public PieChartView(Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + setup(); + } + + public PieChartView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + setup(); + } + + @SuppressLint("ClickableViewAccessibility") + private void setup() { + drawable = new PieChartDrawable(); + setImageDrawable(drawable); + } + + /** + * Set array od names, array of values and array of colors. + */ + public void setData(float[] dataValues) { + drawable.dataValues = dataValues; + drawable.valueSum = 0; + for (float datum : dataValues) { + drawable.valueSum += datum; + } + } + + @Override + protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + int width = getMeasuredWidth(); + setMeasuredDimension(width, width / 2); + } + + private static class PieChartDrawable extends Drawable { + private static final float MIN_DEGREES = 10f; + private static final float PADDING_DEGREES = 3f; + private static final float STROKE_SIZE = 15f; + private static final int[] COLOR_VALUES = new int[]{0xFF3775E6, 0xffe51c23, 0xffff9800, 0xff259b24, 0xff9c27b0, + 0xff0099c6, 0xffdd4477, 0xff66aa00, 0xffb82e2e, 0xff316395, + 0xff994499, 0xff22aa99, 0xffaaaa11, 0xff6633cc, 0xff0073e6}; + private float[] dataValues; + private float valueSum; + private final Paint paint; + + private PieChartDrawable() { + paint = new Paint(); + paint.setFlags(Paint.ANTI_ALIAS_FLAG); + paint.setStyle(Paint.Style.STROKE); + paint.setStrokeJoin(Paint.Join.ROUND); + paint.setStrokeCap(Paint.Cap.ROUND); + paint.setStrokeWidth(STROKE_SIZE); + } + + @Override + public void draw(@NonNull Canvas canvas) { + if (valueSum == 0) { + return; + } + float radius = getBounds().height() - STROKE_SIZE; + float center = getBounds().width() / 2.f; + RectF arcBounds = new RectF(center - radius, STROKE_SIZE, center + radius, STROKE_SIZE + radius * 2); + + float startAngle = 180; + for (int i = 0; i < dataValues.length; i++) { + float datum = dataValues[i]; + float sweepAngle = 180 * datum / valueSum; + if (sweepAngle < MIN_DEGREES) { + break; + } + paint.setColor(COLOR_VALUES[i % COLOR_VALUES.length]); + float padding = i == 0 ? PADDING_DEGREES / 2 : PADDING_DEGREES; + canvas.drawArc(arcBounds, startAngle + padding, sweepAngle - padding, false, paint); + startAngle = startAngle + sweepAngle; + } + + paint.setColor(Color.GRAY); + float sweepAngle = 360 - startAngle - PADDING_DEGREES / 2; + if (sweepAngle > PADDING_DEGREES) { + canvas.drawArc(arcBounds, startAngle + PADDING_DEGREES, sweepAngle - PADDING_DEGREES, false, paint); + } + } + + @Override + public int getOpacity() { + return PixelFormat.TRANSLUCENT; + } + + @Override + public void setAlpha(int alpha) { + } + + @Override + public void setColorFilter(ColorFilter cf) { + } + } +} diff --git a/app/src/main/java/de/danoeh/antennapod/view/SimpleAdapterDataObserver.java b/app/src/main/java/de/danoeh/antennapod/view/SimpleAdapterDataObserver.java index 45c3a35bd..5bd335532 100644 --- a/app/src/main/java/de/danoeh/antennapod/view/SimpleAdapterDataObserver.java +++ b/app/src/main/java/de/danoeh/antennapod/view/SimpleAdapterDataObserver.java @@ -1,7 +1,7 @@ package de.danoeh.antennapod.view; -import android.support.annotation.Nullable; -import android.support.v7.widget.RecyclerView; +import androidx.annotation.Nullable; +import androidx.recyclerview.widget.RecyclerView; /** * AdapterDataObserver that relays all events to the method anythingChanged(). diff --git a/app/src/main/java/de/danoeh/antennapod/view/SquareImageView.java b/app/src/main/java/de/danoeh/antennapod/view/SquareImageView.java index 7ce33e11f..f82309c4a 100644 --- a/app/src/main/java/de/danoeh/antennapod/view/SquareImageView.java +++ b/app/src/main/java/de/danoeh/antennapod/view/SquareImageView.java @@ -1,7 +1,7 @@ package de.danoeh.antennapod.view; import android.content.Context; -import android.support.v7.widget.AppCompatImageView; +import androidx.appcompat.widget.AppCompatImageView; import android.util.AttributeSet; /** diff --git a/app/src/main/java/de/danoeh/antennapod/viewmodel/FeedSettingsViewModel.java b/app/src/main/java/de/danoeh/antennapod/viewmodel/FeedSettingsViewModel.java deleted file mode 100644 index fe11a645c..000000000 --- a/app/src/main/java/de/danoeh/antennapod/viewmodel/FeedSettingsViewModel.java +++ /dev/null @@ -1,30 +0,0 @@ -package de.danoeh.antennapod.viewmodel; - -import android.arch.lifecycle.ViewModel; -import de.danoeh.antennapod.core.feed.Feed; -import de.danoeh.antennapod.core.storage.DBReader; -import io.reactivex.Maybe; - -public class FeedSettingsViewModel extends ViewModel { - private Feed feed; - - public Maybe<Feed> getFeed(long feedId) { - if (feed == null) { - return loadFeed(feedId); - } else { - return Maybe.just(feed); - } - } - - private Maybe<Feed> loadFeed(long feedId) { - return Maybe.create(emitter -> { - Feed feed = DBReader.getFeed(feedId); - if (feed != null) { - this.feed = feed; - emitter.onSuccess(feed); - } else { - emitter.onComplete(); - } - }); - } -} diff --git a/app/src/main/res/anim/card_flip_left_in.xml b/app/src/main/res/anim/card_flip_left_in.xml new file mode 100644 index 000000000..0ffc85aec --- /dev/null +++ b/app/src/main/res/anim/card_flip_left_in.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<set xmlns:android="http://schemas.android.com/apk/res/android"> + <!-- Before rotating, immediately set the alpha to 0. --> + <objectAnimator + android:valueFrom="1.0" + android:valueTo="0.0" + android:propertyName="alpha" + android:duration="0" /> + + <!-- Rotate. --> + <objectAnimator + android:valueFrom="-180" + android:valueTo="0" + android:propertyName="rotationY" + android:interpolator="@android:interpolator/accelerate_decelerate" + android:duration="@integer/card_flip_time_full" /> + + <!-- Half-way through the rotation (see startOffset), set the alpha to 1. --> + <objectAnimator + android:valueFrom="0.0" + android:valueTo="1.0" + android:propertyName="alpha" + android:startOffset="@integer/card_flip_time_half" + android:duration="1" /> +</set>
\ No newline at end of file diff --git a/app/src/main/res/anim/card_flip_left_out.xml b/app/src/main/res/anim/card_flip_left_out.xml new file mode 100644 index 000000000..817f0d3fc --- /dev/null +++ b/app/src/main/res/anim/card_flip_left_out.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<set xmlns:android="http://schemas.android.com/apk/res/android"> + <!-- Rotate. --> + <objectAnimator + android:valueFrom="0" + android:valueTo="180" + android:propertyName="rotationY" + android:interpolator="@android:interpolator/accelerate_decelerate" + android:duration="@integer/card_flip_time_full" /> + + <!-- Half-way through the rotation (see startOffset), set the alpha to 0. --> + <objectAnimator + android:valueFrom="1.0" + android:valueTo="0.0" + android:propertyName="alpha" + android:startOffset="@integer/card_flip_time_half" + android:duration="1" /> +</set>
\ No newline at end of file diff --git a/app/src/main/res/anim/card_flip_right_in.xml b/app/src/main/res/anim/card_flip_right_in.xml new file mode 100644 index 000000000..31ff1dde5 --- /dev/null +++ b/app/src/main/res/anim/card_flip_right_in.xml @@ -0,0 +1,25 @@ +<?xml version="1.0" encoding="utf-8"?> +<set xmlns:android="http://schemas.android.com/apk/res/android"> + <!-- Before rotating, immediately set the alpha to 0. --> + <objectAnimator + android:valueFrom="1.0" + android:valueTo="0.0" + android:propertyName="alpha" + android:duration="0" /> + + <!-- Rotate. --> + <objectAnimator + android:valueFrom="180" + android:valueTo="0" + android:propertyName="rotationY" + android:interpolator="@android:interpolator/accelerate_decelerate" + android:duration="@integer/card_flip_time_full" /> + + <!-- Half-way through the rotation (see startOffset), set the alpha to 1. --> + <objectAnimator + android:valueFrom="0.0" + android:valueTo="1.0" + android:propertyName="alpha" + android:startOffset="@integer/card_flip_time_half" + android:duration="1" /> +</set>
\ No newline at end of file diff --git a/app/src/main/res/anim/card_flip_right_out.xml b/app/src/main/res/anim/card_flip_right_out.xml new file mode 100644 index 000000000..b57113fea --- /dev/null +++ b/app/src/main/res/anim/card_flip_right_out.xml @@ -0,0 +1,18 @@ +<?xml version="1.0" encoding="utf-8"?> +<set xmlns:android="http://schemas.android.com/apk/res/android"> + <!-- Rotate. --> + <objectAnimator + android:valueFrom="0" + android:valueTo="-180" + android:propertyName="rotationY" + android:interpolator="@android:interpolator/accelerate_decelerate" + android:duration="@integer/card_flip_time_full" /> + + <!-- Half-way through the rotation (see startOffset), set the alpha to 0. --> + <objectAnimator + android:valueFrom="1.0" + android:valueTo="0.0" + android:propertyName="alpha" + android:startOffset="@integer/card_flip_time_half" + android:duration="1" /> +</set>
\ No newline at end of file diff --git a/app/src/main/res/layout/addfeed.xml b/app/src/main/res/layout/addfeed.xml index a7f7d9f12..99fa1c3f9 100644 --- a/app/src/main/res/layout/addfeed.xml +++ b/app/src/main/res/layout/addfeed.xml @@ -13,7 +13,7 @@ android:focusableInTouchMode="true" android:padding="8dp"> - <android.support.v7.widget.CardView + <androidx.cardview.widget.CardView android:layout_width="match_parent" android:layout_height="wrap_content" app:cardCornerRadius="4dp" @@ -53,7 +53,7 @@ </LinearLayout> - </android.support.v7.widget.CardView> + </androidx.cardview.widget.CardView> <fragment android:id="@+id/quickFeedDiscovery" @@ -62,7 +62,7 @@ android:layout_height="wrap_content" android:layout_margin="8dp"/> - <android.support.v7.widget.CardView + <androidx.cardview.widget.CardView android:layout_width="match_parent" android:layout_height="wrap_content" app:cardCornerRadius="4dp" @@ -102,9 +102,9 @@ </LinearLayout> - </android.support.v7.widget.CardView> + </androidx.cardview.widget.CardView> - <android.support.v7.widget.CardView + <androidx.cardview.widget.CardView android:layout_width="match_parent" android:layout_height="wrap_content" app:cardCornerRadius="4dp" @@ -170,7 +170,7 @@ </LinearLayout> - </android.support.v7.widget.CardView> + </androidx.cardview.widget.CardView> </LinearLayout> diff --git a/app/src/main/res/layout/all_episodes_fragment.xml b/app/src/main/res/layout/all_episodes_fragment.xml index 53636c2b6..9160998ac 100644 --- a/app/src/main/res/layout/all_episodes_fragment.xml +++ b/app/src/main/res/layout/all_episodes_fragment.xml @@ -17,7 +17,7 @@ android:visibility="gone" tools:text="(i) Information" /> - <android.support.v7.widget.RecyclerView + <androidx.recyclerview.widget.RecyclerView android:id="@android:id/list" android:layout_width="match_parent" android:layout_height="match_parent" diff --git a/app/src/main/res/layout/bug_report.xml b/app/src/main/res/layout/bug_report.xml new file mode 100644 index 000000000..e97e85265 --- /dev/null +++ b/app/src/main/res/layout/bug_report.xml @@ -0,0 +1,28 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical" + android:padding="16dp"> + <Button + android:id="@+id/btn_open_bug_tracker" + android:text="@string/open_bug_tracker" + android:layout_width="match_parent" + android:layout_height="wrap_content"/> + + <Button + android:id="@+id/btn_copy_log" + android:text="@string/copy_to_clipboard" + android:layout_width="match_parent" + android:layout_height="wrap_content"/> + + <TextView + android:layout_marginTop="8dp" + android:id="@+id/crash_report_logs" + android:textIsSelectable="true" + android:textSize="12sp" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_weight="1"/> + +</LinearLayout> diff --git a/app/src/main/res/layout/checkbox_do_not_show_again.xml b/app/src/main/res/layout/checkbox_do_not_show_again.xml new file mode 100644 index 000000000..15f26e8b4 --- /dev/null +++ b/app/src/main/res/layout/checkbox_do_not_show_again.xml @@ -0,0 +1,17 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:paddingLeft="16dp" + android:paddingRight="16dp" + android:paddingTop="8dp" + android:paddingBottom="8dp"> + + <CheckBox + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:id="@+id/checkbox_do_not_show_again" + android:text="@string/checkbox_do_not_show_again"/> + +</LinearLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/downloaded_episodeslist_item.xml b/app/src/main/res/layout/downloaded_episodeslist_item.xml index 65a08251f..3f8065466 100644 --- a/app/src/main/res/layout/downloaded_episodeslist_item.xml +++ b/app/src/main/res/layout/downloaded_episodeslist_item.xml @@ -17,7 +17,7 @@ android:layout_marginTop="@dimen/listitem_threeline_verticalpadding" android:contentDescription="@string/cover_label" android:scaleType="centerCrop" - tools:src="@drawable/ic_stat_antenna_default" + tools:src="@drawable/ic_antenna" tools:background="@android:color/holo_green_dark"/> diff --git a/app/src/main/res/layout/feedinfo.xml b/app/src/main/res/layout/feedinfo.xml index 50061c4d8..416fc3aec 100644 --- a/app/src/main/res/layout/feedinfo.xml +++ b/app/src/main/res/layout/feedinfo.xml @@ -24,7 +24,7 @@ android:layout_height="wrap_content" android:orientation="vertical"> - <android.support.v7.widget.GridLayout + <androidx.gridlayout.widget.GridLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginTop="8dp" @@ -107,7 +107,7 @@ tools:text="http://www.example.com/feed" tools:background="@android:color/holo_green_dark"/> - </android.support.v7.widget.GridLayout> + </androidx.gridlayout.widget.GridLayout> <TextView style="@style/AntennaPod.TextView.Heading" diff --git a/app/src/main/res/layout/feeditem_fragment.xml b/app/src/main/res/layout/feeditem_fragment.xml index 000539e60..b047b3da0 100644 --- a/app/src/main/res/layout/feeditem_fragment.xml +++ b/app/src/main/res/layout/feeditem_fragment.xml @@ -37,7 +37,7 @@ android:contentDescription="@string/cover_label" android:gravity="center_vertical" android:foreground="?attr/selectableItemBackground" - tools:src="@drawable/ic_stat_antenna_default" + tools:src="@drawable/ic_antenna" tools:background="@android:color/holo_green_dark" /> <TextView diff --git a/app/src/main/res/layout/feeditemlist_header.xml b/app/src/main/res/layout/feeditemlist_header.xml index e1f451e9e..596135d88 100644 --- a/app/src/main/res/layout/feeditemlist_header.xml +++ b/app/src/main/res/layout/feeditemlist_header.xml @@ -27,7 +27,7 @@ android:layout_marginStart="16dp" android:layout_marginTop="16dp" android:contentDescription="@string/cover_label" - tools:src="@drawable/ic_stat_antenna_default" + tools:src="@drawable/ic_antenna" tools:background="@android:color/holo_green_dark"/> <ImageButton diff --git a/app/src/main/res/layout/gpodnet_podcast_listitem.xml b/app/src/main/res/layout/gpodnet_podcast_listitem.xml index 27a8bbdca..6e02fa090 100644 --- a/app/src/main/res/layout/gpodnet_podcast_listitem.xml +++ b/app/src/main/res/layout/gpodnet_podcast_listitem.xml @@ -23,7 +23,7 @@ android:contentDescription="@string/cover_label" android:cropToPadding="true" android:scaleType="fitXY" - tools:src="@drawable/ic_stat_antenna_default" + tools:src="@drawable/ic_antenna" tools:background="@android:color/holo_green_dark" /> <LinearLayout diff --git a/app/src/main/res/layout/itunes_podcast_listitem.xml b/app/src/main/res/layout/itunes_podcast_listitem.xml index 4848563b1..b2411c5df 100644 --- a/app/src/main/res/layout/itunes_podcast_listitem.xml +++ b/app/src/main/res/layout/itunes_podcast_listitem.xml @@ -25,7 +25,7 @@ android:cropToPadding="true" android:scaleType="fitXY" tools:background="@android:color/holo_green_dark" - tools:src="@drawable/ic_stat_antenna_default" /> + tools:src="@drawable/ic_antenna" /> <LinearLayout android:layout_width="match_parent" diff --git a/app/src/main/res/layout/main.xml b/app/src/main/res/layout/main.xml index 6cabcdff2..a226e482f 100644 --- a/app/src/main/res/layout/main.xml +++ b/app/src/main/res/layout/main.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<android.support.v4.widget.DrawerLayout +<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/drawer_layout" @@ -19,7 +19,7 @@ tools:layout_height="64dp" tools:background="@android:color/holo_green_light" /> - <android.support.v7.widget.Toolbar + <androidx.appcompat.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="wrap_content" @@ -48,4 +48,4 @@ <include layout="@layout/nav_list" /> -</android.support.v4.widget.DrawerLayout>
\ No newline at end of file +</androidx.drawerlayout.widget.DrawerLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/mediaplayerinfo_activity.xml b/app/src/main/res/layout/mediaplayerinfo_activity.xml index a6427e985..b759b0092 100644 --- a/app/src/main/res/layout/mediaplayerinfo_activity.xml +++ b/app/src/main/res/layout/mediaplayerinfo_activity.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<android.support.v4.widget.DrawerLayout +<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:app="http://schemas.android.com/apk/res-auto" @@ -12,12 +12,12 @@ android:layout_width="match_parent" android:layout_height="match_parent"> - <android.support.design.widget.AppBarLayout + <com.google.android.material.appbar.AppBarLayout android:id="@+id/appBar" android:layout_width="match_parent" android:layout_height="wrap_content"> - <android.support.v7.widget.Toolbar + <androidx.appcompat.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="wrap_content" @@ -37,7 +37,7 @@ app:strokeColor="?android:attr/textColorSecondary" app:radius="4dp" /> - </android.support.design.widget.AppBarLayout> + </com.google.android.material.appbar.AppBarLayout> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" @@ -155,11 +155,11 @@ android:layout_marginTop="-8dp" android:gravity="center" android:text="30" - android:textSize="10sp" + android:textSize="12sp" android:textColor="?android:attr/textColorSecondary" android:clickable="false"/> - <Button + <ImageButton android:id="@+id/butPlaybackSpeed" android:layout_width="@dimen/audioplayer_playercontrols_length" android:layout_height="@dimen/audioplayer_playercontrols_length" @@ -167,13 +167,28 @@ android:layout_toStartOf="@id/butRev" android:background="?attr/selectableItemBackground" android:contentDescription="@string/set_playback_speed_label" - android:src="?attr/av_fast_forward" - android:textSize="@dimen/text_size_medium" - android:textAllCaps="false" - android:maxLines="1" + android:src="?attr/av_speed" + android:scaleType="fitCenter" + tools:src="@drawable/ic_playback_speed_white" tools:visibility="gone" tools:background="@android:color/holo_green_dark" /> + <TextView + android:id="@+id/txtvPlaybackSpeed" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_below="@id/butPlaybackSpeed" + android:layout_alignLeft="@id/butPlaybackSpeed" + android:layout_alignStart="@id/butPlaybackSpeed" + android:layout_alignRight="@id/butPlaybackSpeed" + android:layout_alignEnd="@id/butPlaybackSpeed" + android:layout_marginTop="-8dp" + android:gravity="center" + android:text="1.00" + android:textSize="12sp" + android:textColor="?android:attr/textColorSecondary" + android:clickable="false"/> + <ImageButton android:id="@+id/butCastDisconnect" android:layout_width="@dimen/audioplayer_playercontrols_length" @@ -216,7 +231,7 @@ android:layout_marginTop="-8dp" android:gravity="center" android:text="30" - android:textSize="10sp" + android:textSize="12sp" android:textColor="?android:attr/textColorSecondary" android:clickable="false"/> @@ -236,7 +251,7 @@ </LinearLayout> - <android.support.v4.view.ViewPager + <androidx.viewpager.widget.ViewPager xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/pager" android:layout_width="match_parent" @@ -257,4 +272,4 @@ <include layout="@layout/nav_list" /> -</android.support.v4.widget.DrawerLayout>
\ No newline at end of file +</androidx.drawerlayout.widget.DrawerLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/nav_feedlistitem.xml b/app/src/main/res/layout/nav_feedlistitem.xml index 816870d1c..52833b3cd 100644 --- a/app/src/main/res/layout/nav_feedlistitem.xml +++ b/app/src/main/res/layout/nav_feedlistitem.xml @@ -25,7 +25,7 @@ android:scaleType="centerCrop" android:layout_marginTop="4dp" android:layout_marginBottom="4dp" - tools:src="@drawable/ic_stat_antenna_default" + tools:src="@drawable/ic_antenna" tools:background="@android:color/holo_green_dark"/> <TextView diff --git a/app/src/main/res/layout/pager_fragment.xml b/app/src/main/res/layout/pager_fragment.xml index 54b711b1c..492743239 100644 --- a/app/src/main/res/layout/pager_fragment.xml +++ b/app/src/main/res/layout/pager_fragment.xml @@ -7,14 +7,14 @@ android:orientation="vertical"> - <android.support.design.widget.TabLayout + <com.google.android.material.tabs.TabLayout android:id="@+id/sliding_tabs" android:layout_width="match_parent" android:layout_height="wrap_content" app:tabGravity="fill" app:tabMode="fixed" /> - <android.support.v4.view.ViewPager + <androidx.viewpager.widget.ViewPager android:id="@+id/viewpager" android:layout_width="match_parent" android:layout_height="0px" diff --git a/app/src/main/res/layout/queue_fragment.xml b/app/src/main/res/layout/queue_fragment.xml index 85b0df58c..63da6a315 100644 --- a/app/src/main/res/layout/queue_fragment.xml +++ b/app/src/main/res/layout/queue_fragment.xml @@ -20,7 +20,7 @@ android:layout_below="@id/info_bar" android:background="?android:attr/listDivider"/> - <android.support.v7.widget.RecyclerView + <androidx.recyclerview.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="match_parent" android:layout_height="match_parent" diff --git a/app/src/main/res/layout/queue_listitem.xml b/app/src/main/res/layout/queue_listitem.xml index 6b41b68d5..1dcc34bce 100644 --- a/app/src/main/res/layout/queue_listitem.xml +++ b/app/src/main/res/layout/queue_listitem.xml @@ -51,7 +51,7 @@ android:layout_height="@dimen/thumbnail_length_queue_item" android:layout_centerVertical="true" android:contentDescription="@string/cover_label" - tools:src="@drawable/ic_stat_antenna_default" + tools:src="@drawable/ic_antenna" tools:background="@android:color/holo_green_dark"/> </RelativeLayout> diff --git a/app/src/main/res/layout/quick_feed_discovery.xml b/app/src/main/res/layout/quick_feed_discovery.xml index ce5cfa65b..e791d19d7 100644 --- a/app/src/main/res/layout/quick_feed_discovery.xml +++ b/app/src/main/res/layout/quick_feed_discovery.xml @@ -1,5 +1,5 @@ <?xml version="1.0" encoding="utf-8"?> -<android.support.v7.widget.CardView +<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" @@ -79,5 +79,5 @@ </LinearLayout> -</android.support.v7.widget.CardView> +</androidx.cardview.widget.CardView> diff --git a/app/src/main/res/layout/searchlist_item.xml b/app/src/main/res/layout/searchlist_item.xml index 50374c737..4a055fea9 100644 --- a/app/src/main/res/layout/searchlist_item.xml +++ b/app/src/main/res/layout/searchlist_item.xml @@ -18,7 +18,7 @@ android:layout_marginStart="@dimen/listitem_threeline_horizontalpadding" android:contentDescription="@string/cover_label" android:scaleType="centerCrop" - tools:src="@drawable/ic_stat_antenna_default" + tools:src="@drawable/ic_antenna" tools:background="@android:color/holo_green_dark"/> <LinearLayout diff --git a/app/src/main/res/layout/statistics_activity.xml b/app/src/main/res/layout/statistics_activity.xml index 4a72dc7de..9d9cad438 100644 --- a/app/src/main/res/layout/statistics_activity.xml +++ b/app/src/main/res/layout/statistics_activity.xml @@ -1,41 +1,24 @@ <?xml version="1.0" encoding="utf-8"?> -<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:orientation="vertical" - android:paddingTop="8dp" - android:paddingBottom="8dp"> - - <TextView - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:text="@string/total_time_listened_to_podcasts" - android:gravity="center_horizontal"/> +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:orientation="vertical"> <ProgressBar - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:id="@+id/progressBar" - android:layout_gravity="center_horizontal" - style="?android:attr/progressBarStyleLarge"/> - - <TextView - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:id="@+id/total_time" - android:gravity="center_horizontal" - android:textSize="45sp"/> + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:id="@+id/progressBar" + android:layout_gravity="center"/> - <ListView - android:id="@+id/statistics_list" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:choiceMode="singleChoice" - android:clipToPadding="false" - android:divider="@android:color/transparent" - android:dividerHeight="0dp" - android:paddingBottom="@dimen/list_vertical_padding" - android:paddingTop="@dimen/list_vertical_padding" - android:scrollbarStyle="outsideOverlay" /> + <androidx.recyclerview.widget.RecyclerView + android:id="@+id/statistics_list" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:clipToPadding="false" + android:paddingBottom="@dimen/list_vertical_padding" + android:paddingTop="@dimen/list_vertical_padding" + android:scrollbarStyle="outsideOverlay" + tools:listitem="@layout/statistics_listitem"/> -</LinearLayout> +</FrameLayout> diff --git a/app/src/main/res/layout/statistics_listitem.xml b/app/src/main/res/layout/statistics_listitem.xml index b186add9e..6bd2a907e 100644 --- a/app/src/main/res/layout/statistics_listitem.xml +++ b/app/src/main/res/layout/statistics_listitem.xml @@ -3,63 +3,55 @@ xmlns:tools="http://schemas.android.com/tools" android:orientation="vertical" android:layout_width="match_parent" - android:layout_height="@dimen/listitem_iconwithtext_height" - android:paddingLeft="@dimen/listitem_threeline_verticalpadding" - android:paddingStart="@dimen/listitem_threeline_verticalpadding" - android:paddingRight="@dimen/listitem_threeline_verticalpadding" - android:paddingEnd="@dimen/listitem_threeline_verticalpadding" - tools:background="@android:color/darker_gray"> + android:layout_height="wrap_content" + android:paddingLeft="16dp" + android:paddingRight="16dp" + android:paddingTop="8dp" + android:paddingBottom="8dp" + android:background="?android:attr/selectableItemBackground"> <ImageView - android:id="@+id/imgvCover" - android:contentDescription="@string/cover_label" - android:layout_width="@dimen/thumbnail_length_navlist" - android:layout_height="@dimen/thumbnail_length_navlist" - android:layout_alignParentLeft="true" - android:layout_alignParentStart="true" - android:layout_centerVertical="true" - android:adjustViewBounds="true" - android:cropToPadding="true" - android:scaleType="centerCrop" - android:layout_marginTop="4dp" - android:layout_marginBottom="4dp" - tools:src="@drawable/ic_stat_antenna_default" - tools:background="@android:color/holo_green_dark"/> + android:id="@+id/imgvCover" + android:contentDescription="@string/cover_label" + android:layout_width="40dp" + android:layout_height="40dp" + android:layout_alignParentLeft="true" + android:layout_alignParentStart="true" + android:layout_centerVertical="true" + android:adjustViewBounds="true" + android:cropToPadding="true" + android:scaleType="centerCrop" + tools:src="@drawable/ic_antenna" + tools:background="@android:color/holo_green_dark"/> <TextView - android:id="@+id/txtvTime" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginLeft="@dimen/list_vertical_padding" - android:layout_marginStart="@dimen/list_vertical_padding" - android:lines="1" - android:textColor="?android:attr/textColorTertiary" - android:textSize="@dimen/text_size_navdrawer" - android:layout_alignParentRight="true" - android:layout_alignParentEnd="true" - android:layout_centerVertical="true" - tools:text="23" - tools:background="@android:color/holo_green_dark"/> + android:id="@+id/txtvTitle" + android:lines="1" + android:ellipsize="end" + android:singleLine="true" + android:textColor="?android:attr/textColorPrimary" + android:textSize="16sp" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginLeft="16dp" + android:layout_marginStart="16dp" + android:layout_toRightOf="@id/imgvCover" + android:layout_alignTop="@id/imgvCover" + android:layout_alignWithParentIfMissing="true" + tools:text="Feed title"/> <TextView - android:id="@+id/txtvTitle" - android:lines="1" - android:ellipsize="end" - android:singleLine="true" - android:layout_centerVertical="true" - android:textColor="?android:attr/textColorPrimary" - android:textSize="@dimen/text_size_navdrawer" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_marginLeft="@dimen/listitem_iconwithtext_textleftpadding" - android:layout_marginStart="@dimen/listitem_iconwithtext_textleftpadding" - android:layout_toRightOf="@id/imgvCover" - android:layout_toEndOf="@id/imgvCover" - android:layout_toLeftOf="@id/txtvTime" - android:layout_toStartOf="@id/txtvTime" - android:layout_alignWithParentIfMissing="true" - tools:text="Navigation feed item title" - tools:background="@android:color/holo_green_dark"/> - + android:id="@+id/txtvTime" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:lines="1" + android:textColor="?android:attr/textColorTertiary" + android:textSize="14sp" + android:layout_toEndOf="@+id/imgvCover" + android:layout_toRightOf="@+id/imgvCover" + android:layout_marginLeft="16dp" + android:layout_marginStart="16dp" + android:layout_below="@+id/txtvTitle" + tools:text="23 hours"/> </RelativeLayout> diff --git a/app/src/main/res/layout/statistics_listitem_total_time.xml b/app/src/main/res/layout/statistics_listitem_total_time.xml new file mode 100644 index 000000000..2e0ae54d6 --- /dev/null +++ b/app/src/main/res/layout/statistics_listitem_total_time.xml @@ -0,0 +1,42 @@ +<?xml version="1.0" encoding="utf-8"?> +<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:tools="http://schemas.android.com/tools" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:padding="16dp"> + + <de.danoeh.antennapod.view.PieChartView + android:id="@+id/pie_chart" + android:layout_marginLeft="8dp" + android:layout_marginRight="8dp" + android:layout_width="match_parent" + android:layout_height="wrap_content"/> + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:id="@+id/total_time_description" + android:textSize="14sp" + android:text="@string/total_time_listened_to_podcasts" + android:gravity="center_horizontal" + android:layout_above="@+id/total_time"/> + + <TextView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:id="@+id/total_time" + android:textColor="?android:attr/textColorPrimary" + android:gravity="center_horizontal" + android:textSize="28sp" + android:layout_marginBottom="16dp" + android:layout_alignBottom="@id/pie_chart" + tools:text="10.0 hours"/> + + <View + android:layout_width="match_parent" + android:layout_height="1dp" + android:layout_marginTop="16dp" + android:background="?android:attr/dividerVertical" + android:layout_below="@+id/pie_chart"/> + +</RelativeLayout>
\ No newline at end of file diff --git a/app/src/main/res/menu/allepisodes_context.xml b/app/src/main/res/menu/allepisodes_context.xml deleted file mode 100644 index 907bc9334..000000000 --- a/app/src/main/res/menu/allepisodes_context.xml +++ /dev/null @@ -1,86 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> - -<menu xmlns:android="http://schemas.android.com/apk/res/android"> - - <item - android:id="@id/skip_episode_item" - android:menuCategory="container" - android:title="@string/skip_episode_label" /> - - - <item - android:id="@+id/remove_new_flag_item" - android:menuCategory="container" - android:title="@string/remove_new_flag_label" /> - - <item - android:id="@+id/mark_read_item" - android:menuCategory="container" - android:title="@string/mark_read_label" /> - <item - android:id="@+id/mark_unread_item" - android:menuCategory="container" - android:title="@string/mark_unread_label" /> - - <item - android:id="@+id/add_to_queue_item" - android:menuCategory="container" - android:title="@string/add_to_queue_label" /> - <item - android:id="@+id/remove_from_queue_item" - android:menuCategory="container" - android:title="@string/remove_from_queue_label" /> - <item - android:id="@+id/add_to_favorites_item" - android:menuCategory="container" - android:title="@string/add_to_favorite_label" /> - <item - android:id="@+id/remove_from_favorites_item" - android:menuCategory="container" - android:title="@string/remove_from_favorite_label" /> - <item - android:id="@+id/reset_position" - android:menuCategory="container" - android:title="@string/reset_position" /> - - <item - android:id="@+id/activate_auto_download" - android:menuCategory="container" - android:title="@string/activate_auto_download" /> - <item - android:id="@+id/deactivate_auto_download" - android:menuCategory="container" - android:title="@string/deactivate_auto_download" /> - - <item - android:id="@+id/visit_website_item" - android:menuCategory="container" - android:title="@string/visit_website_label" /> - <item - android:id="@+id/share_item" - android:menuCategory="container" - android:title="@string/share_label"> - <menu> - <item - android:id="@+id/share_link_item" - android:menuCategory="container" - android:title="@string/share_link_label" /> - <item - android:id="@+id/share_link_with_position_item" - android:menuCategory="container" - android:title="@string/share_link_with_position_label" /> - <item - android:id="@+id/share_download_url_item" - android:menuCategory="container" - android:title="@string/share_item_url_label" /> - <item - android:id="@+id/share_download_url_with_position_item" - android:menuCategory="container" - android:title="@string/share_item_url_with_position_label" /> - <item - android:id="@+id/share_file" - android:menuCategory="container" - android:title="@string/share_file_label" /> - </menu> - </item> -</menu>
\ No newline at end of file diff --git a/app/src/main/res/menu/episodes.xml b/app/src/main/res/menu/episodes.xml index 1e1aa8f56..7398754e5 100644 --- a/app/src/main/res/menu/episodes.xml +++ b/app/src/main/res/menu/episodes.xml @@ -7,7 +7,7 @@ android:id="@+id/action_search" android:icon="?attr/action_search" custom:showAsAction="collapseActionView|ifRoom" - custom:actionViewClass="android.support.v7.widget.SearchView" + custom:actionViewClass="androidx.appcompat.widget.SearchView" android:title="@string/search_label"/> <item diff --git a/app/src/main/res/menu/feedinfo.xml b/app/src/main/res/menu/feedinfo.xml index 300068007..f20d679a5 100644 --- a/app/src/main/res/menu/feedinfo.xml +++ b/app/src/main/res/menu/feedinfo.xml @@ -11,7 +11,7 @@ <item android:id="@+id/share_link_item" custom:showAsAction="collapseActionView" - android:title="@string/share_link_label"> + android:title="@string/share_website_url_label"> </item> <item android:id="@+id/share_download_url_item" diff --git a/app/src/main/res/menu/feeditem_options.xml b/app/src/main/res/menu/feeditem_options.xml index 0801b79a1..e415ff85a 100644 --- a/app/src/main/res/menu/feeditem_options.xml +++ b/app/src/main/res/menu/feeditem_options.xml @@ -9,6 +9,11 @@ </item> <item + android:id="@+id/remove_new_flag_item" + custom:showAsAction="collapseActionView" + android:title="@string/remove_new_flag_label" /> + + <item android:id="@+id/mark_read_item" custom:showAsAction="collapseActionView" android:title="@string/mark_read_label"> diff --git a/app/src/main/res/menu/feeditemlist_context.xml b/app/src/main/res/menu/feeditemlist_context.xml index df13cb027..6e4966206 100644 --- a/app/src/main/res/menu/feeditemlist_context.xml +++ b/app/src/main/res/menu/feeditemlist_context.xml @@ -8,6 +8,11 @@ android:title="@string/skip_episode_label" /> <item + android:id="@+id/remove_new_flag_item" + android:menuCategory="container" + android:title="@string/remove_new_flag_label" /> + + <item android:id="@+id/mark_read_item" android:menuCategory="container" android:title="@string/mark_read_label" /> diff --git a/app/src/main/res/menu/feedlist.xml b/app/src/main/res/menu/feedlist.xml index 0cc8addfe..fdd0e01bc 100644 --- a/app/src/main/res/menu/feedlist.xml +++ b/app/src/main/res/menu/feedlist.xml @@ -34,7 +34,7 @@ android:id="@+id/action_search" android:icon="?attr/action_search" custom:showAsAction="always|collapseActionView" - custom:actionViewClass="android.support.v7.widget.SearchView" + custom:actionViewClass="androidx.appcompat.widget.SearchView" android:title="@string/search_label"/> <item @@ -49,7 +49,7 @@ android:id="@+id/share_link_item" android:menuCategory="container" custom:showAsAction="collapseActionView" - android:title="@string/share_link_label"> + android:title="@string/share_website_url_label"> </item> <item android:id="@+id/share_download_url_item" diff --git a/app/src/main/res/menu/gpodder_podcasts.xml b/app/src/main/res/menu/gpodder_podcasts.xml index 88fa36a4a..93d93157a 100644 --- a/app/src/main/res/menu/gpodder_podcasts.xml +++ b/app/src/main/res/menu/gpodder_podcasts.xml @@ -7,7 +7,7 @@ android:id="@+id/action_search" android:icon="?attr/action_search" custom:showAsAction="collapseActionView|ifRoom" - custom:actionViewClass="android.support.v7.widget.SearchView" + custom:actionViewClass="androidx.appcompat.widget.SearchView" android:title="@string/search_label"/> </menu> diff --git a/app/src/main/res/menu/itunes_search.xml b/app/src/main/res/menu/itunes_search.xml index 88fa36a4a..93d93157a 100644 --- a/app/src/main/res/menu/itunes_search.xml +++ b/app/src/main/res/menu/itunes_search.xml @@ -7,7 +7,7 @@ android:id="@+id/action_search" android:icon="?attr/action_search" custom:showAsAction="collapseActionView|ifRoom" - custom:actionViewClass="android.support.v7.widget.SearchView" + custom:actionViewClass="androidx.appcompat.widget.SearchView" android:title="@string/search_label"/> </menu> diff --git a/app/src/main/res/menu/mediaplayer.xml b/app/src/main/res/menu/mediaplayer.xml index 44d511ee4..055951760 100644 --- a/app/src/main/res/menu/mediaplayer.xml +++ b/app/src/main/res/menu/mediaplayer.xml @@ -35,6 +35,14 @@ </item> <item + android:id="@+id/open_feed_item" + android:icon="?attr/feed" + custom:showAsAction="collapseActionView" + android:title="@string/open_podcast" + android:visible="false"> + </item> + + <item android:id="@+id/visit_website_item" android:icon="?attr/location_web_site" custom:showAsAction="collapseActionView" diff --git a/app/src/main/res/menu/queue.xml b/app/src/main/res/menu/queue.xml index a4e511eb8..b86ad5d63 100644 --- a/app/src/main/res/menu/queue.xml +++ b/app/src/main/res/menu/queue.xml @@ -20,7 +20,7 @@ android:id="@+id/action_search" android:icon="?attr/action_search" custom:showAsAction="collapseActionView|ifRoom" - custom:actionViewClass="android.support.v7.widget.SearchView" + custom:actionViewClass="androidx.appcompat.widget.SearchView" android:title="@string/search_label"/> <item diff --git a/app/src/main/res/menu/queue_context.xml b/app/src/main/res/menu/queue_context.xml index e1c3e6216..522e712e2 100644 --- a/app/src/main/res/menu/queue_context.xml +++ b/app/src/main/res/menu/queue_context.xml @@ -11,77 +11,6 @@ android:id="@+id/move_to_bottom_item" android:menuCategory="container" android:title="@string/move_to_bottom_label" /> + <!-- rest of the menu items can be found in the generic feeditemlist_context.xml --> - <item - android:id="@+id/mark_read_item" - android:menuCategory="container" - android:title="@string/mark_read_label" /> - - <item - android:id="@+id/mark_unread_item" - android:menuCategory="container" - android:title="@string/mark_unread_label" /> - - <item - android:id="@+id/remove_from_queue_item" - android:menuCategory="container" - android:title="@string/remove_from_queue_label" /> - <item - android:id="@+id/remove_item" - android:menuCategory="container" - android:title="@string/delete_label" /> - - <item - android:id="@+id/add_to_favorites_item" - android:menuCategory="container" - android:title="@string/add_to_favorite_label" /> - <item - android:id="@+id/remove_from_favorites_item" - android:menuCategory="container" - android:title="@string/remove_from_favorite_label" /> - <item - android:id="@+id/reset_position" - android:menuCategory="container" - android:title="@string/reset_position" /> - - <item - android:id="@+id/activate_auto_download" - android:menuCategory="container" - android:title="@string/activate_auto_download" /> - <item - android:id="@+id/deactivate_auto_download" - android:menuCategory="container" - android:title="@string/deactivate_auto_download" /> - - <item - android:id="@+id/visit_website_item" - android:menuCategory="container" - android:title="@string/visit_website_label" /> - <item - android:id="@+id/share_item" - android:menuCategory="container" - android:title="@string/share_label"> - <menu> - <item - android:id="@+id/share_link_item" - android:menuCategory="container" - android:title="@string/share_link_label" /> - <item - android:id="@+id/share_link_with_position_item" - android:menuCategory="container" - android:title="@string/share_link_with_position_label" /> - <item - android:id="@+id/share_download_url_item" - android:menuCategory="container" - android:title="@string/share_item_url_label" /> - <item - android:id="@+id/share_download_url_with_position_item" - android:menuCategory="container" - android:title="@string/share_item_url_with_position_label" /> - <item - android:id="@+id/share_file" - android:menuCategory="container" - android:title="@string/share_file_label" /> - </menu> - </item> </menu>
\ No newline at end of file diff --git a/app/src/main/res/menu/subscriptions.xml b/app/src/main/res/menu/subscriptions.xml index f39e0ac97..1780592d5 100644 --- a/app/src/main/res/menu/subscriptions.xml +++ b/app/src/main/res/menu/subscriptions.xml @@ -3,6 +3,13 @@ xmlns:custom="http://schemas.android.com/apk/res-auto"> <item + android:id="@+id/refresh_item" + android:title="@string/refresh_label" + android:menuCategory="container" + custom:showAsAction="always" + android:icon="?attr/navigation_refresh"/> + + <item android:id="@+id/subscription_num_columns" android:title="@string/subscription_num_columns" custom:showAsAction="never"> diff --git a/app/src/main/res/values/integers.xml b/app/src/main/res/values/integers.xml new file mode 100644 index 000000000..8c444ee8b --- /dev/null +++ b/app/src/main/res/values/integers.xml @@ -0,0 +1,5 @@ +<?xml version="1.0" encoding="utf-8"?> +<resources> + <integer name="card_flip_time_full">400</integer> + <integer name="card_flip_time_half">200</integer> +</resources>
\ No newline at end of file diff --git a/app/src/main/res/xml/preferences.xml b/app/src/main/res/xml/preferences.xml index 6c1e470c0..37707ead6 100644 --- a/app/src/main/res/xml/preferences.xml +++ b/app/src/main/res/xml/preferences.xml @@ -43,16 +43,14 @@ <Preference android:key="prefFaq" android:title="@string/pref_faq" - android:icon="?attr/ic_question_answer" /> - + android:icon="?attr/ic_questionmark" /> <Preference - android:key="prefKnownIssues" - android:title="@string/pref_known_issues" - android:icon="?attr/ic_known_issues" /> + android:key="prefViewMailingList" + android:title="@string/view_mailing_list" + android:icon="?attr/ic_chat" /> <Preference - android:key="prefSendCrashReport" - android:title="@string/crash_report_title" - android:summary="@string/crash_report_sum" + android:key="prefSendBugReport" + android:title="@string/bug_report_title" android:icon="?attr/ic_bug" /> <Preference android:key="prefAbout" diff --git a/app/src/main/res/xml/preferences_user_interface.xml b/app/src/main/res/xml/preferences_user_interface.xml index 1d970a5f7..c48e9adc8 100644 --- a/app/src/main/res/xml/preferences_user_interface.xml +++ b/app/src/main/res/xml/preferences_user_interface.xml @@ -10,7 +10,7 @@ android:title="@string/pref_set_theme_title" android:key="prefTheme" android:summary="@string/pref_set_theme_sum" - android:defaultValue="0" + android:defaultValue="system" app:useStockLayout="true"/> <Preference android:key="prefHiddenDrawerItems" |