diff options
Diffstat (limited to 'app')
34 files changed, 790 insertions, 530 deletions
diff --git a/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java b/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java index 909b7a5a2..b790bc005 100644 --- a/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java +++ b/app/src/androidTest/java/de/test/antennapod/ui/PreferencesTest.java @@ -3,32 +3,29 @@ package de.test.antennapod.ui; import android.content.Intent; import android.content.SharedPreferences; import android.content.res.Resources; - import androidx.annotation.StringRes; import androidx.preference.PreferenceManager; import androidx.test.espresso.matcher.RootMatchers; import androidx.test.filters.LargeTest; import androidx.test.rule.ActivityTestRule; - -import de.danoeh.antennapod.core.storage.EpisodeCleanupAlgorithmFactory; -import org.awaitility.Awaitility; -import org.junit.Before; -import org.junit.Rule; -import org.junit.Test; - -import java.util.Arrays; -import java.util.concurrent.TimeUnit; - import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.PreferenceActivity; -import de.danoeh.antennapod.storage.preferences.UserPreferences; -import de.danoeh.antennapod.storage.preferences.UserPreferences.EnqueueLocation; import de.danoeh.antennapod.core.storage.APCleanupAlgorithm; import de.danoeh.antennapod.core.storage.APNullCleanupAlgorithm; import de.danoeh.antennapod.core.storage.APQueueCleanupAlgorithm; import de.danoeh.antennapod.core.storage.EpisodeCleanupAlgorithm; +import de.danoeh.antennapod.core.storage.EpisodeCleanupAlgorithmFactory; import de.danoeh.antennapod.core.storage.ExceptFavoriteCleanupAlgorithm; +import de.danoeh.antennapod.storage.preferences.UserPreferences; +import de.danoeh.antennapod.storage.preferences.UserPreferences.EnqueueLocation; import de.test.antennapod.EspressoTestUtils; +import org.awaitility.Awaitility; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; + +import java.util.Arrays; +import java.util.concurrent.TimeUnit; import static androidx.test.espresso.Espresso.onData; import static androidx.test.espresso.Espresso.onView; @@ -78,22 +75,6 @@ public class PreferencesTest { } @Test - public void testSwitchTheme() { - final UserPreferences.ThemePreference theme = UserPreferences.getTheme(); - int otherThemeText; - if (theme == UserPreferences.ThemePreference.DARK) { - otherThemeText = R.string.pref_theme_title_light; - } else { - otherThemeText = R.string.pref_theme_title_dark; - } - clickPreference(R.string.user_interface_label); - clickPreference(R.string.pref_set_theme_title); - onView(withText(otherThemeText)).perform(click()); - Awaitility.await().atMost(1000, MILLISECONDS) - .until(() -> UserPreferences.getTheme() != theme); - } - - @Test public void testEnablePersistentPlaybackControls() { final boolean persistNotify = UserPreferences.isPersistNotify(); clickPreference(R.string.user_interface_label); diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9cce60469..d784e8709 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -126,7 +126,13 @@ <activity android:name=".activity.DownloadAuthenticationActivity" android:theme="@style/Theme.AntennaPod.Dark.Translucent" - android:launchMode="singleInstance"/> + android:exported="false" + android:launchMode="singleInstance"> + <intent-filter> + <action android:name="de.danoeh.antennapod.intents.DOWNLOAD_AUTH_ACTIVITY" /> + <category android:name="android.intent.category.DEFAULT" /> + </intent-filter> + </activity> <activity android:name=".activity.PreferenceActivity" diff --git a/app/src/main/java/de/danoeh/antennapod/PodcastApp.java b/app/src/main/java/de/danoeh/antennapod/PodcastApp.java index 7cbe32e11..942e2cf24 100644 --- a/app/src/main/java/de/danoeh/antennapod/PodcastApp.java +++ b/app/src/main/java/de/danoeh/antennapod/PodcastApp.java @@ -12,7 +12,6 @@ import com.joanzapata.iconify.fonts.MaterialModule; import de.danoeh.antennapod.activity.SplashActivity; import de.danoeh.antennapod.config.ApplicationCallbacksImpl; -import de.danoeh.antennapod.config.DownloadServiceCallbacksImpl; import de.danoeh.antennapod.core.ApCoreEventBusIndex; import de.danoeh.antennapod.core.ClientConfig; import de.danoeh.antennapod.core.ClientConfigurator; @@ -35,7 +34,6 @@ public class PodcastApp extends MultiDexApplication { super.onCreate(); ClientConfig.USER_AGENT = "AntennaPod/" + BuildConfig.VERSION_NAME; ClientConfig.applicationCallbacks = new ApplicationCallbacksImpl(); - ClientConfig.downloadServiceCallbacks = new DownloadServiceCallbacksImpl(); Thread.setDefaultUncaughtExceptionHandler(new CrashReportWriter()); RxJavaErrorHandlerSetup.setupRxJavaErrorHandler(); 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 82b9d3d25..176c3c990 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/DownloadAuthenticationActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/DownloadAuthenticationActivity.java @@ -12,10 +12,10 @@ import de.danoeh.antennapod.net.download.serviceinterface.DownloadRequest; import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.dialog.AuthenticationDialog; +import de.danoeh.antennapod.ui.appstartintent.DownloadAuthenticationActivityStarter; import io.reactivex.Completable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.schedulers.Schedulers; -import org.apache.commons.lang3.Validate; /** @@ -23,19 +23,13 @@ import org.apache.commons.lang3.Validate; * The activity MUST be started with the ARG_DOWNlOAD_REQUEST argument set to a non-null value. */ public class DownloadAuthenticationActivity extends AppCompatActivity { - - /** - * The download request object that contains information about the resource that requires a username and a password. - */ - public static final String ARG_DOWNLOAD_REQUEST = "request"; - @Override protected void onCreate(Bundle savedInstanceState) { setTheme(ThemeSwitcher.getTranslucentTheme(this)); super.onCreate(savedInstanceState); - Validate.isTrue(getIntent().hasExtra(ARG_DOWNLOAD_REQUEST), "Download request missing"); - DownloadRequest request = getIntent().getParcelableExtra(ARG_DOWNLOAD_REQUEST); + DownloadRequest request = getIntent().getParcelableExtra( + DownloadAuthenticationActivityStarter.EXTRA_DOWNLOAD_REQUEST); new AuthenticationDialog(this, R.string.authentication_label, true, "", "") { @Override 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 6b2267926..fad586328 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/MainActivity.java @@ -76,8 +76,6 @@ public class MainActivity extends CastEnabledActivity { public static final String PREF_NAME = "MainActivityPrefs"; public static final String PREF_IS_FIRST_LAUNCH = "prefMainActivityIsFirstLaunch"; - public static final String EXTRA_FRAGMENT_TAG = "fragment_tag"; - public static final String EXTRA_FRAGMENT_ARGS = "fragment_args"; public static final String EXTRA_FEED_ID = "fragment_feed_id"; public static final String EXTRA_REFRESH_ON_START = "refresh_on_start"; public static final String EXTRA_STARTED_FROM_SEARCH = "started_from_search"; @@ -441,6 +439,9 @@ public class MainActivity extends CastEnabledActivity { finish(); startActivity(new Intent(this, MainActivity.class)); } + if (UserPreferences.getHiddenDrawerItems().contains(NavDrawerFragment.getLastNavFragment(this))) { + loadFragment(UserPreferences.getDefaultPage(), null); + } } @Override @@ -511,20 +512,12 @@ public class MainActivity extends CastEnabledActivity { } private void handleNavIntent() { + Log.d(TAG, "handleNavIntent()"); Intent intent = getIntent(); - if (intent.hasExtra(EXTRA_FEED_ID) || intent.hasExtra(EXTRA_FRAGMENT_TAG) || intent.hasExtra(EXTRA_REFRESH_ON_START)) { - Log.d(TAG, "handleNavIntent()"); - String tag = intent.getStringExtra(EXTRA_FRAGMENT_TAG); - Bundle args = intent.getBundleExtra(EXTRA_FRAGMENT_ARGS); - boolean refreshOnStart = intent.getBooleanExtra(EXTRA_REFRESH_ON_START, false); - if (refreshOnStart) { - AutoUpdateManager.runImmediate(this); - } - + if (intent.hasExtra(EXTRA_FEED_ID)) { long feedId = intent.getLongExtra(EXTRA_FEED_ID, 0); - if (tag != null) { - loadFragment(tag, args); - } else if (feedId > 0) { + Bundle args = intent.getBundleExtra(MainActivityStarter.EXTRA_FRAGMENT_ARGS); + if (feedId > 0) { boolean startedFromSearch = intent.getBooleanExtra(EXTRA_STARTED_FROM_SEARCH, false); boolean addToBackStack = intent.getBooleanExtra(EXTRA_ADD_TO_BACK_STACK, false); if (startedFromSearch || addToBackStack) { @@ -534,12 +527,26 @@ public class MainActivity extends CastEnabledActivity { } } sheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED); + } else if (intent.hasExtra(MainActivityStarter.EXTRA_FRAGMENT_TAG)) { + String tag = intent.getStringExtra(MainActivityStarter.EXTRA_FRAGMENT_TAG); + Bundle args = intent.getBundleExtra(MainActivityStarter.EXTRA_FRAGMENT_ARGS); + if (tag != null) { + loadFragment(tag, args); + } + sheetBehavior.setState(BottomSheetBehavior.STATE_COLLAPSED); } else if (intent.getBooleanExtra(MainActivityStarter.EXTRA_OPEN_PLAYER, false)) { sheetBehavior.setState(BottomSheetBehavior.STATE_EXPANDED); bottomSheetCallback.onSlide(null, 1.0f); } else if (Intent.ACTION_VIEW.equals(intent.getAction())) { handleDeeplink(intent.getData()); } + + if (intent.getBooleanExtra(MainActivityStarter.EXTRA_OPEN_DRAWER, false) && drawerLayout != null) { + drawerLayout.open(); + } + if (intent.getBooleanExtra(EXTRA_REFRESH_ON_START, false)) { + AutoUpdateManager.runImmediate(this); + } // to avoid handling the intent twice when the configuration changes setIntent(new Intent(MainActivity.this, MainActivity.class)); } 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 dc82e3adc..1d06bf07c 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/CoverLoader.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/CoverLoader.java @@ -1,40 +1,28 @@ package de.danoeh.antennapod.adapter; -import android.content.Context; import android.graphics.drawable.Drawable; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.core.content.ContextCompat; -import androidx.core.graphics.ColorUtils; -import androidx.palette.graphics.Palette; - import android.view.View; import android.widget.ImageView; import android.widget.TextView; - +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import com.bumptech.glide.Glide; import com.bumptech.glide.RequestBuilder; import com.bumptech.glide.request.RequestOptions; import com.bumptech.glide.request.target.CustomViewTarget; - -import java.lang.ref.WeakReference; - import com.bumptech.glide.request.transition.Transition; - -import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; -import de.danoeh.antennapod.storage.preferences.UserPreferences; -import de.danoeh.antennapod.ui.common.ThemeUtils; -import de.danoeh.antennapod.ui.glide.PaletteBitmap; + +import java.lang.ref.WeakReference; public class CoverLoader { private int resource = 0; private String uri; private String fallbackUri; - private TextView txtvPlaceholder; private ImageView imgvCover; private boolean textAndImageCombined; private MainActivity activity; + private TextView fallbackTitle; public CoverLoader(MainActivity activity) { this.activity = activity; @@ -60,31 +48,30 @@ public class CoverLoader { return this; } - public CoverLoader withPlaceholderView(TextView placeholderView) { - txtvPlaceholder = placeholderView; + public CoverLoader withPlaceholderView(TextView title) { + this.fallbackTitle = title; return this; } /** * Set cover text and if it should be shown even if there is a cover image. - * - * @param placeholderView Cover text. + * @param fallbackTitle Fallback title text * @param textAndImageCombined Show cover text even if there is a cover image? */ @NonNull - public CoverLoader withPlaceholderView(@NonNull TextView placeholderView, boolean textAndImageCombined) { - this.txtvPlaceholder = placeholderView; + public CoverLoader withPlaceholderView(TextView fallbackTitle, boolean textAndImageCombined) { + this.fallbackTitle = fallbackTitle; this.textAndImageCombined = textAndImageCombined; return this; } public void load() { - CoverTarget coverTarget = new CoverTarget(txtvPlaceholder, imgvCover, textAndImageCombined); + CoverTarget coverTarget = new CoverTarget(fallbackTitle, imgvCover, textAndImageCombined); if (resource != 0) { Glide.with(activity).clear(coverTarget); imgvCover.setImageResource(resource); - CoverTarget.setPlaceholderVisibility(txtvPlaceholder, textAndImageCombined, null); + CoverTarget.setTitleVisibility(fallbackTitle, textAndImageCombined); return; } @@ -92,14 +79,14 @@ public class CoverLoader { .fitCenter() .dontAnimate(); - RequestBuilder<PaletteBitmap> builder = Glide.with(activity) - .as(PaletteBitmap.class) + RequestBuilder<Drawable> builder = Glide.with(activity) + .as(Drawable.class) .load(uri) .apply(options); - if (fallbackUri != null && txtvPlaceholder != null && imgvCover != null) { + if (fallbackUri != null) { builder = builder.error(Glide.with(activity) - .as(PaletteBitmap.class) + .as(Drawable.class) .load(fallbackUri) .apply(options)); } @@ -107,64 +94,41 @@ public class CoverLoader { builder.into(coverTarget); } - static class CoverTarget extends CustomViewTarget<ImageView, PaletteBitmap> { - private final WeakReference<TextView> placeholder; + static class CoverTarget extends CustomViewTarget<ImageView, Drawable> { + private final WeakReference<TextView> fallbackTitle; private final WeakReference<ImageView> cover; - private boolean textAndImageCombined; + private final boolean textAndImageCombined; - public CoverTarget(TextView txtvPlaceholder, ImageView imgvCover, boolean textAndImageCombined) { - super(imgvCover); - if (txtvPlaceholder != null) { - txtvPlaceholder.setVisibility(View.VISIBLE); - } - placeholder = new WeakReference<>(txtvPlaceholder); - cover = new WeakReference<>(imgvCover); + public CoverTarget(TextView fallbackTitle, ImageView coverImage, boolean textAndImageCombined) { + super(coverImage); + this.fallbackTitle = new WeakReference<>(fallbackTitle); + this.cover = new WeakReference<>(coverImage); this.textAndImageCombined = textAndImageCombined; } @Override public void onLoadFailed(Drawable errorDrawable) { - setPlaceholderVisibility(this.placeholder.get(), true, null); + setTitleVisibility(fallbackTitle.get(), true); } @Override - public void onResourceReady(@NonNull PaletteBitmap resource, - @Nullable Transition<? super PaletteBitmap> transition) { + public void onResourceReady(@NonNull Drawable resource, + @Nullable Transition<? super Drawable> transition) { ImageView ivCover = cover.get(); - ivCover.setImageBitmap(resource.bitmap); - setPlaceholderVisibility(placeholder.get(), textAndImageCombined, resource.palette); + ivCover.setImageDrawable(resource); + setTitleVisibility(fallbackTitle.get(), textAndImageCombined); } @Override protected void onResourceCleared(@Nullable Drawable placeholder) { ImageView ivCover = cover.get(); ivCover.setImageDrawable(placeholder); - setPlaceholderVisibility(this.placeholder.get(), textAndImageCombined, null); + setTitleVisibility(fallbackTitle.get(), textAndImageCombined); } - static void setPlaceholderVisibility(TextView placeholder, boolean textAndImageCombined, Palette palette) { - boolean showTitle = UserPreferences.shouldShowSubscriptionTitle(); - if (placeholder != null) { - if (textAndImageCombined || showTitle) { - final Context context = placeholder.getContext(); - placeholder.setVisibility(View.VISIBLE); - int bgColor = ContextCompat.getColor(context, R.color.feed_text_bg); - if (palette == null || !showTitle) { - placeholder.setBackgroundColor(bgColor); - placeholder.setTextColor(ThemeUtils.getColorFromAttr(placeholder.getContext(), - android.R.attr.textColorPrimary)); - return; - } - int dominantColor = palette.getDominantColor(bgColor); - int textColor = ContextCompat.getColor(context, R.color.white); - if (ColorUtils.calculateLuminance(dominantColor) > 0.5) { - textColor = ContextCompat.getColor(context, R.color.black); - } - placeholder.setTextColor(textColor); - placeholder.setBackgroundColor(dominantColor); - } else { - placeholder.setVisibility(View.INVISIBLE); - } + static void setTitleVisibility(TextView fallbackTitle, boolean textAndImageCombined) { + if (fallbackTitle != null) { + fallbackTitle.setVisibility(textAndImageCombined ? View.VISIBLE : View.GONE); } } } diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/HorizontalFeedListAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/HorizontalFeedListAdapter.java index 9d2713f48..9a578da6a 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/HorizontalFeedListAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/HorizontalFeedListAdapter.java @@ -16,10 +16,17 @@ import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.List; -public class HorizontalFeedListAdapter extends RecyclerView.Adapter<HorizontalFeedListAdapter.Holder> { +import android.view.ContextMenu; +import android.view.MenuInflater; +import androidx.annotation.Nullable; + +public class HorizontalFeedListAdapter extends RecyclerView.Adapter<HorizontalFeedListAdapter.Holder> + implements View.OnCreateContextMenuListener { private final WeakReference<MainActivity> mainActivityRef; private final List<Feed> data = new ArrayList<>(); private int dummyViews = 0; + private Feed longPressedItem; + public HorizontalFeedListAdapter(MainActivity mainActivity) { this.mainActivityRef = new WeakReference<>(mainActivity); @@ -57,6 +64,13 @@ public class HorizontalFeedListAdapter extends RecyclerView.Adapter<HorizontalFe holder.imageView.setOnClickListener(v -> mainActivityRef.get().loadChildFragment(FeedItemlistFragment.newInstance(podcast.getId()))); + holder.imageView.setOnCreateContextMenuListener(this); + holder.imageView.setOnLongClickListener(v -> { + int currentItemPosition = holder.getBindingAdapterPosition(); + longPressedItem = data.get(currentItemPosition); + return false; + }); + Glide.with(mainActivityRef.get()) .load(podcast.getImageUrl()) .apply(new RequestOptions() @@ -66,6 +80,11 @@ public class HorizontalFeedListAdapter extends RecyclerView.Adapter<HorizontalFe .into(holder.imageView); } + @Nullable + public Feed getLongPressedItem() { + return longPressedItem; + } + @Override public long getItemId(int position) { if (position >= data.size()) { @@ -79,6 +98,16 @@ public class HorizontalFeedListAdapter extends RecyclerView.Adapter<HorizontalFe return dummyViews + data.size(); } + @Override + public void onCreateContextMenu(ContextMenu contextMenu, View view, ContextMenu.ContextMenuInfo contextMenuInfo) { + MenuInflater inflater = mainActivityRef.get().getMenuInflater(); + if (longPressedItem == null) { + return; + } + inflater.inflate(R.menu.nav_feed_context, contextMenu); + contextMenu.setHeaderTitle(longPressedItem.getTitle()); + } + static class Holder extends RecyclerView.ViewHolder { SquareImageView imageView; diff --git a/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsRecyclerAdapter.java b/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsRecyclerAdapter.java index 7be417984..bfa37b600 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsRecyclerAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/SubscriptionsRecyclerAdapter.java @@ -5,7 +5,6 @@ import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.os.Build; -import android.text.TextUtils; import android.view.ContextMenu; import android.view.InputDevice; import android.view.LayoutInflater; @@ -17,20 +16,19 @@ import android.view.ViewGroup; import android.widget.CheckBox; import android.widget.FrameLayout; import android.widget.ImageView; -import android.widget.RelativeLayout; import android.widget.TextView; import androidx.annotation.NonNull; import androidx.appcompat.content.res.AppCompatResources; -import androidx.core.content.ContextCompat; +import androidx.cardview.widget.CardView; import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.RecyclerView; +import com.google.android.material.elevation.SurfaceColors; import java.lang.ref.WeakReference; import java.text.NumberFormat; import java.util.ArrayList; import java.util.List; -import java.util.Locale; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; @@ -39,7 +37,6 @@ import de.danoeh.antennapod.core.storage.NavDrawerData; import de.danoeh.antennapod.fragment.FeedItemlistFragment; import de.danoeh.antennapod.fragment.SubscriptionFragment; import de.danoeh.antennapod.model.feed.Feed; -import de.danoeh.antennapod.ui.common.TriangleLabelView; /** * Adapter for subscriptions @@ -52,6 +49,7 @@ public class SubscriptionsRecyclerAdapter extends SelectableAdapter<Subscription private List<NavDrawerData.DrawerItem> listItems; private NavDrawerData.DrawerItem selectedItem = null; int longPressedPosition = 0; // used to init actionMode + private int columnCount = 3; public SubscriptionsRecyclerAdapter(MainActivity mainActivity) { super(mainActivity); @@ -60,6 +58,10 @@ public class SubscriptionsRecyclerAdapter extends SelectableAdapter<Subscription setHasStableIds(true); } + public void setColumnCount(int columnCount) { + this.columnCount = columnCount; + } + public Object getItem(int position) { return listItems.get(position); } @@ -72,24 +74,7 @@ public class SubscriptionsRecyclerAdapter extends SelectableAdapter<Subscription @Override public SubscriptionViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { View itemView = LayoutInflater.from(mainActivityRef.get()).inflate(R.layout.subscription_item, parent, false); - TextView feedTitle = itemView.findViewById(R.id.txtvTitle); - RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) feedTitle.getLayoutParams(); - int topAndBottomItemId = R.id.imgvCover; - int belowItemId = 0; - - if (viewType == COVER_WITH_TITLE) { - topAndBottomItemId = 0; - belowItemId = R.id.imgvCover; - feedTitle.setBackgroundColor( - ContextCompat.getColor(feedTitle.getContext(), R.color.feed_text_bg)); - int padding = (int) convertDpToPixel(feedTitle.getContext(), 6); - feedTitle.setPadding(padding, padding, padding, padding); - } - params.addRule(RelativeLayout.BELOW, belowItemId); - params.addRule(RelativeLayout.ALIGN_TOP, topAndBottomItemId); - params.addRule(RelativeLayout.ALIGN_BOTTOM, topAndBottomItemId); - feedTitle.setLayoutParams(params); - feedTitle.setSingleLine(viewType == COVER_WITH_TITLE); + itemView.findViewById(R.id.titleLabel).setVisibility(viewType == COVER_WITH_TITLE ? View.VISIBLE : View.GONE); return new SubscriptionViewHolder(itemView); } @@ -107,11 +92,11 @@ public class SubscriptionsRecyclerAdapter extends SelectableAdapter<Subscription holder.selectCheckbox.setChecked((isSelected(position))); holder.selectCheckbox.setOnCheckedChangeListener((buttonView, isChecked) -> setSelected(holder.getBindingAdapterPosition(), isChecked)); - holder.imageView.setAlpha(0.6f); + holder.coverImage.setAlpha(0.6f); holder.count.setVisibility(View.GONE); } else { holder.selectView.setVisibility(View.GONE); - holder.imageView.setAlpha(1.0f); + holder.coverImage.setAlpha(1.0f); } holder.itemView.setOnLongClickListener(v -> { @@ -224,55 +209,74 @@ public class SubscriptionsRecyclerAdapter extends SelectableAdapter<Subscription } public class SubscriptionViewHolder extends RecyclerView.ViewHolder { - private final TextView feedTitle; - private final ImageView imageView; - private final TriangleLabelView count; + private final TextView title; + private final ImageView coverImage; + private final TextView count; + private final TextView fallbackTitle; private final FrameLayout selectView; private final CheckBox selectCheckbox; + private final CardView card; public SubscriptionViewHolder(@NonNull View itemView) { super(itemView); - feedTitle = itemView.findViewById(R.id.txtvTitle); - imageView = itemView.findViewById(R.id.imgvCover); - count = itemView.findViewById(R.id.triangleCountView); - selectView = itemView.findViewById(R.id.selectView); + title = itemView.findViewById(R.id.titleLabel); + coverImage = itemView.findViewById(R.id.coverImage); + count = itemView.findViewById(R.id.countViewPill); + fallbackTitle = itemView.findViewById(R.id.fallbackTitleLabel); + selectView = itemView.findViewById(R.id.selectContainer); selectCheckbox = itemView.findViewById(R.id.selectCheckBox); + card = itemView.findViewById(R.id.outerContainer); } public void bind(NavDrawerData.DrawerItem drawerItem) { Drawable drawable = AppCompatResources.getDrawable(selectView.getContext(), R.drawable.ic_checkbox_background); selectView.setBackground(drawable); // Setting this in XML crashes API <= 21 - feedTitle.setText(drawerItem.getTitle()); - imageView.setContentDescription(drawerItem.getTitle()); - feedTitle.setVisibility(View.VISIBLE); - if (TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()) == View.LAYOUT_DIRECTION_RTL) { - count.setCorner(TriangleLabelView.Corner.TOP_LEFT); - } - + title.setText(drawerItem.getTitle()); + fallbackTitle.setText(drawerItem.getTitle()); + coverImage.setContentDescription(drawerItem.getTitle()); if (drawerItem.getCounter() > 0) { - count.setPrimaryText(NumberFormat.getInstance().format(drawerItem.getCounter())); + count.setText(NumberFormat.getInstance().format(drawerItem.getCounter())); count.setVisibility(View.VISIBLE); } else { count.setVisibility(View.GONE); } + CoverLoader coverLoader = new CoverLoader(mainActivityRef.get()); + boolean textAndImageCombined; if (drawerItem.type == NavDrawerData.DrawerItem.Type.FEED) { Feed feed = ((NavDrawerData.FeedDrawerItem) drawerItem).feed; - boolean textAndImageCombind = feed.isLocalFeed() - && feed.getImageUrl() != null && feed.getImageUrl().startsWith(Feed.PREFIX_GENERATIVE_COVER); - new CoverLoader(mainActivityRef.get()) - .withUri(feed.getImageUrl()) - .withPlaceholderView(feedTitle, textAndImageCombind) - .withCoverView(imageView) - .load(); + textAndImageCombined = feed.isLocalFeed() && feed.getImageUrl() != null + && feed.getImageUrl().startsWith(Feed.PREFIX_GENERATIVE_COVER); + coverLoader.withUri(feed.getImageUrl()); } else { - new CoverLoader(mainActivityRef.get()) - .withResource(R.drawable.ic_tag) - .withPlaceholderView(feedTitle, true) - .withCoverView(imageView) - .load(); + textAndImageCombined = true; + coverLoader.withResource(R.drawable.ic_tag); + } + if (UserPreferences.shouldShowSubscriptionTitle()) { + // No need for fallback title when already showing title + fallbackTitle.setVisibility(View.GONE); + } else { + coverLoader.withPlaceholderView(fallbackTitle, textAndImageCombined); + } + coverLoader.withCoverView(coverImage); + coverLoader.load(); + + float density = mainActivityRef.get().getResources().getDisplayMetrics().density; + card.setCardBackgroundColor(SurfaceColors.getColorForElevation(mainActivityRef.get(), 1 * density)); + + int textPadding = columnCount <= 3 ? 16 : 8; + title.setPadding(textPadding, textPadding, textPadding, textPadding); + fallbackTitle.setPadding(textPadding, textPadding, textPadding, textPadding); + + int textSize = 14; + if (columnCount == 3) { + textSize = 15; + } else if (columnCount == 2) { + textSize = 16; } + title.setTextSize(textSize); + fallbackTitle.setTextSize(textSize); } } diff --git a/app/src/main/java/de/danoeh/antennapod/config/DownloadServiceCallbacksImpl.java b/app/src/main/java/de/danoeh/antennapod/config/DownloadServiceCallbacksImpl.java deleted file mode 100644 index 69f112c3b..000000000 --- a/app/src/main/java/de/danoeh/antennapod/config/DownloadServiceCallbacksImpl.java +++ /dev/null @@ -1,57 +0,0 @@ -package de.danoeh.antennapod.config; - -import android.app.PendingIntent; -import android.content.Context; -import android.content.Intent; -import android.os.Build; - -import android.os.Bundle; -import de.danoeh.antennapod.R; -import de.danoeh.antennapod.activity.DownloadAuthenticationActivity; -import de.danoeh.antennapod.activity.MainActivity; -import de.danoeh.antennapod.core.DownloadServiceCallbacks; -import de.danoeh.antennapod.net.download.serviceinterface.DownloadRequest; -import de.danoeh.antennapod.fragment.CompletedDownloadsFragment; -import de.danoeh.antennapod.fragment.QueueFragment; - - -public class DownloadServiceCallbacksImpl implements DownloadServiceCallbacks { - - @Override - public PendingIntent getNotificationContentIntent(Context context) { - Intent intent = new Intent(context, MainActivity.class); - intent.putExtra(MainActivity.EXTRA_FRAGMENT_TAG, CompletedDownloadsFragment.TAG); - return PendingIntent.getActivity(context, - R.id.pending_intent_download_service_notification, intent, - PendingIntent.FLAG_UPDATE_CURRENT | (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0)); - } - - @Override - public PendingIntent getAuthentificationNotificationContentIntent(Context context, DownloadRequest request) { - final Intent activityIntent = new Intent(context.getApplicationContext(), DownloadAuthenticationActivity.class); - activityIntent.setAction("request" + request.getFeedfileId()); - activityIntent.putExtra(DownloadAuthenticationActivity.ARG_DOWNLOAD_REQUEST, request); - return PendingIntent.getActivity(context.getApplicationContext(), - request.getSource().hashCode(), activityIntent, - PendingIntent.FLAG_ONE_SHOT | (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0)); - } - - @Override - public PendingIntent getReportNotificationContentIntent(Context context) { - Intent intent = new Intent(context, MainActivity.class); - intent.putExtra(MainActivity.EXTRA_FRAGMENT_TAG, CompletedDownloadsFragment.TAG); - Bundle args = new Bundle(); - args.putBoolean(CompletedDownloadsFragment.ARG_SHOW_LOGS, true); - intent.putExtra(MainActivity.EXTRA_FRAGMENT_ARGS, args); - return PendingIntent.getActivity(context, R.id.pending_intent_download_service_report, intent, - PendingIntent.FLAG_UPDATE_CURRENT | (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0)); - } - - @Override - public PendingIntent getAutoDownloadReportNotificationContentIntent(Context context) { - Intent intent = new Intent(context, MainActivity.class); - intent.putExtra(MainActivity.EXTRA_FRAGMENT_TAG, QueueFragment.TAG); - return PendingIntent.getActivity(context, R.id.pending_intent_download_service_autodownload_report, intent, - PendingIntent.FLAG_UPDATE_CURRENT | (Build.VERSION.SDK_INT >= 23 ? PendingIntent.FLAG_IMMUTABLE : 0)); - } -} 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 b39b41897..6d91765da 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/CompletedDownloadsFragment.java @@ -4,6 +4,8 @@ import android.os.Bundle; import android.util.Log; import android.view.ContextMenu; import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; @@ -33,6 +35,8 @@ import de.danoeh.antennapod.fragment.swipeactions.SwipeActions; import de.danoeh.antennapod.menuhandler.FeedItemMenuHandler; import de.danoeh.antennapod.model.feed.FeedItem; import de.danoeh.antennapod.model.feed.FeedItemFilter; +import de.danoeh.antennapod.model.feed.SortOrder; +import de.danoeh.antennapod.storage.preferences.UserPreferences; import de.danoeh.antennapod.view.EmptyViewHandler; import de.danoeh.antennapod.view.EpisodeItemListRecyclerView; import de.danoeh.antennapod.view.LiftOnScrollListener; @@ -77,6 +81,8 @@ public class CompletedDownloadsFragment extends Fragment MaterialToolbar toolbar = root.findViewById(R.id.toolbar); toolbar.setTitle(R.string.downloads_label); toolbar.inflateMenu(R.menu.downloads_completed); + inflateSortMenu(toolbar); + toolbar.setOnMenuItemClickListener(this); toolbar.setOnLongClickListener(v -> { recyclerView.scrollToPosition(5); @@ -139,6 +145,19 @@ public class CompletedDownloadsFragment extends Fragment return root; } + private void inflateSortMenu(MaterialToolbar toolbar) { + Menu menu = toolbar.getMenu(); + MenuItem downloadsItem = menu.findItem(R.id.downloads_sort); + MenuInflater menuInflater = getActivity().getMenuInflater(); + menuInflater.inflate(R.menu.sort_menu, downloadsItem.getSubMenu()); + + // Remove the sorting options that are not needed in this fragment + menu.findItem(R.id.sort_feed_title).setVisible(false); + menu.findItem(R.id.sort_random).setVisible(false); + menu.findItem(R.id.sort_smart_shuffle).setVisible(false); + menu.findItem(R.id.keep_sorted).setVisible(false); + } + @Override public void onSaveInstanceState(@NonNull Bundle outState) { outState.putBoolean(KEY_UP_ARROW, displayUpArrow); @@ -177,10 +196,21 @@ public class CompletedDownloadsFragment extends Fragment } else if (item.getItemId() == R.id.action_search) { ((MainActivity) getActivity()).loadChildFragment(SearchFragment.newInstance()); return true; + } else { + SortOrder sortOrder = MenuItemToSortOrderConverter.convert(item); + if (sortOrder != null) { + setSortOrder(sortOrder); + return true; + } } return false; } + private void setSortOrder(SortOrder sortOrder) { + UserPreferences.setDownloadsSortedOrder(sortOrder); + loadItems(); + } + @Subscribe(sticky = true, threadMode = ThreadMode.MAIN) public void onEventMainThread(DownloadEvent event) { Log.d(TAG, "onEventMainThread() called with: " + "event = [" + event + "]"); @@ -279,7 +309,9 @@ public class CompletedDownloadsFragment extends Fragment } emptyView.hide(); disposable = Observable.fromCallable(() -> { - List<FeedItem> downloadedItems = DBReader.getDownloadedItems(); + SortOrder sortOrder = UserPreferences.getDownloadsSortedOrder(); + List<FeedItem> downloadedItems = DBReader.getDownloadedItems(sortOrder); + List<Long> mediaIds = new ArrayList<>(); if (runningDownloads == null) { return downloadedItems; diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/DiscoveryFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/DiscoveryFragment.java index 88b9ac8f1..16ccb2af4 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/DiscoveryFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/DiscoveryFragment.java @@ -14,17 +14,25 @@ import android.widget.Button; import android.widget.GridView; import android.widget.ProgressBar; import android.widget.TextView; - import androidx.annotation.NonNull; import androidx.appcompat.widget.Toolbar; import androidx.fragment.app.Fragment; - import com.google.android.material.appbar.MaterialToolbar; import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.textfield.MaterialAutoCompleteTextView; import com.google.android.material.textfield.TextInputLayout; - +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.activity.OnlineFeedViewActivity; +import de.danoeh.antennapod.adapter.itunes.ItunesAdapter; import de.danoeh.antennapod.core.BuildConfig; +import de.danoeh.antennapod.core.storage.DBReader; +import de.danoeh.antennapod.event.DiscoveryDefaultUpdateEvent; +import de.danoeh.antennapod.net.discovery.ItunesTopListLoader; +import de.danoeh.antennapod.net.discovery.PodcastSearchResult; +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 java.util.ArrayList; @@ -35,20 +43,13 @@ import java.util.List; import java.util.Locale; import java.util.Map; -import de.danoeh.antennapod.R; -import de.danoeh.antennapod.activity.OnlineFeedViewActivity; -import de.danoeh.antennapod.adapter.itunes.ItunesAdapter; -import de.danoeh.antennapod.event.DiscoveryDefaultUpdateEvent; -import de.danoeh.antennapod.net.discovery.ItunesTopListLoader; -import de.danoeh.antennapod.net.discovery.PodcastSearchResult; -import io.reactivex.disposables.Disposable; - /** * Searches iTunes store for top podcasts and displays results in a list. */ public class DiscoveryFragment extends Fragment implements Toolbar.OnMenuItemClickListener { private static final String TAG = "ItunesSearchFragment"; + private static final int NUM_OF_TOP_PODCASTS = 25; private SharedPreferences prefs; /** @@ -188,19 +189,23 @@ public class DiscoveryFragment extends Fragment implements Toolbar.OnMenuItemCli } ItunesTopListLoader loader = new ItunesTopListLoader(getContext()); - disposable = loader.loadToplist(country, 25).subscribe( - podcasts -> { - progressBar.setVisibility(View.GONE); - topList = podcasts; - updateData(topList); - }, error -> { - Log.e(TAG, Log.getStackTraceString(error)); - progressBar.setVisibility(View.GONE); - txtvError.setText(error.getMessage()); - txtvError.setVisibility(View.VISIBLE); - butRetry.setOnClickListener(v -> loadToplist(country)); - butRetry.setVisibility(View.VISIBLE); - }); + disposable = Observable.fromCallable(() -> + loader.loadToplist(country, NUM_OF_TOP_PODCASTS, DBReader.getFeedList())) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + podcasts -> { + progressBar.setVisibility(View.GONE); + topList = podcasts; + updateData(topList); + }, error -> { + Log.e(TAG, Log.getStackTraceString(error)); + progressBar.setVisibility(View.GONE); + txtvError.setText(error.getMessage()); + txtvError.setVisibility(View.VISIBLE); + butRetry.setOnClickListener(v -> loadToplist(country)); + butRetry.setVisibility(View.VISIBLE); + }); } @Override diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/MenuItemToSortOrderConverter.java b/app/src/main/java/de/danoeh/antennapod/fragment/MenuItemToSortOrderConverter.java new file mode 100644 index 000000000..51911b122 --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/fragment/MenuItemToSortOrderConverter.java @@ -0,0 +1,40 @@ +package de.danoeh.antennapod.fragment; + +import android.view.MenuItem; + +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.model.feed.SortOrder; + +public class MenuItemToSortOrderConverter { + + public static SortOrder convert(MenuItem item) { + final int itemId = item.getItemId(); + + if (itemId == R.id.sort_episode_title_asc) { + return SortOrder.EPISODE_TITLE_A_Z; + } else if (itemId == R.id.sort_episode_title_desc) { + return SortOrder.EPISODE_TITLE_Z_A; + } else if (itemId == R.id.sort_date_asc) { + return SortOrder.DATE_OLD_NEW; + } else if (itemId == R.id.sort_date_desc) { + return SortOrder.DATE_NEW_OLD; + } else if (itemId == R.id.sort_duration_asc) { + return SortOrder.DURATION_SHORT_LONG; + } else if (itemId == R.id.sort_duration_desc) { + return SortOrder.DURATION_LONG_SHORT; + } else if (itemId == R.id.sort_feed_title_asc) { + return SortOrder.FEED_TITLE_A_Z; + } else if (itemId == R.id.sort_feed_title_desc) { + return SortOrder.FEED_TITLE_Z_A; + } else if (itemId == R.id.sort_random) { + return SortOrder.RANDOM; + } else if (itemId == R.id.sort_smart_shuffle_asc) { + return SortOrder.SMART_SHUFFLE_OLD_NEW; + } else if (itemId == R.id.sort_smart_shuffle_desc) { + return SortOrder.SMART_SHUFFLE_NEW_OLD; + } + + return null; + } + +} diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/NavDrawerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/NavDrawerFragment.java index 7c3ed4f2f..e155e467d 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/NavDrawerFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/NavDrawerFragment.java @@ -40,6 +40,7 @@ import de.danoeh.antennapod.event.FeedListUpdateEvent; import de.danoeh.antennapod.event.QueueEvent; import de.danoeh.antennapod.event.UnreadItemsUpdateEvent; import de.danoeh.antennapod.model.feed.Feed; +import de.danoeh.antennapod.ui.appstartintent.MainActivityStarter; import de.danoeh.antennapod.ui.home.HomeFragment; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; @@ -338,7 +339,15 @@ public class NavDrawerFragment extends Fragment implements SharedPreferences.OnS @Override public boolean onItemLongClick(int position) { if (position < navAdapter.getFragmentTags().size()) { - DrawerPreferencesDialog.show(getContext(), () -> navAdapter.notifyDataSetChanged()); + DrawerPreferencesDialog.show(getContext(), () -> { + navAdapter.notifyDataSetChanged(); + if (UserPreferences.getHiddenDrawerItems().contains(getLastNavFragment(getContext()))) { + new MainActivityStarter(getContext()) + .withFragmentLoaded(UserPreferences.getDefaultPage()) + .withDrawerOpen() + .start(); + } + }); return true; } else { contextPressedItem = flatItemList.get(position - navAdapter.getSubscriptionOffset()); 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 12fa20d05..4e8999d0f 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/QueueFragment.java @@ -10,6 +10,7 @@ import android.util.Log; import android.view.ContextMenu; import android.view.KeyEvent; import android.view.LayoutInflater; +import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; @@ -256,8 +257,8 @@ public class QueueFragment extends Fragment implements MaterialToolbar.OnMenuIte boolean keepSorted = UserPreferences.isQueueKeepSorted(); toolbar.getMenu().findItem(R.id.queue_lock).setChecked(UserPreferences.isQueueLocked()); toolbar.getMenu().findItem(R.id.queue_lock).setVisible(!keepSorted); - toolbar.getMenu().findItem(R.id.queue_sort_random).setVisible(!keepSorted); - toolbar.getMenu().findItem(R.id.queue_keep_sorted).setChecked(keepSorted); + toolbar.getMenu().findItem(R.id.sort_random).setVisible(!keepSorted); + toolbar.getMenu().findItem(R.id.keep_sorted).setChecked(keepSorted); MenuItemUtils.updateRefreshMenuItem(toolbar.getMenu(), R.id.refresh_item, DownloadService.isRunning && DownloadService.isDownloadingFeeds()); } @@ -286,40 +287,7 @@ public class QueueFragment extends Fragment implements MaterialToolbar.OnMenuIte }; conDialog.createNewDialog().show(); return true; - } else if (itemId == R.id.queue_sort_episode_title_asc) { - setSortOrder(SortOrder.EPISODE_TITLE_A_Z); - return true; - } else if (itemId == R.id.queue_sort_episode_title_desc) { - setSortOrder(SortOrder.EPISODE_TITLE_Z_A); - return true; - } else if (itemId == R.id.queue_sort_date_asc) { - setSortOrder(SortOrder.DATE_OLD_NEW); - return true; - } else if (itemId == R.id.queue_sort_date_desc) { - setSortOrder(SortOrder.DATE_NEW_OLD); - return true; - } else if (itemId == R.id.queue_sort_duration_asc) { - setSortOrder(SortOrder.DURATION_SHORT_LONG); - return true; - } else if (itemId == R.id.queue_sort_duration_desc) { - setSortOrder(SortOrder.DURATION_LONG_SHORT); - return true; - } else if (itemId == R.id.queue_sort_feed_title_asc) { - setSortOrder(SortOrder.FEED_TITLE_A_Z); - return true; - } else if (itemId == R.id.queue_sort_feed_title_desc) { - setSortOrder(SortOrder.FEED_TITLE_Z_A); - return true; - } else if (itemId == R.id.queue_sort_random) { - setSortOrder(SortOrder.RANDOM); - return true; - } else if (itemId == R.id.queue_sort_smart_shuffle_asc) { - setSortOrder(SortOrder.SMART_SHUFFLE_OLD_NEW); - return true; - } else if (itemId == R.id.queue_sort_smart_shuffle_desc) { - setSortOrder(SortOrder.SMART_SHUFFLE_NEW_OLD); - return true; - } else if (itemId == R.id.queue_keep_sorted) { + } else if (itemId == R.id.keep_sorted) { boolean keepSortedOld = UserPreferences.isQueueKeepSorted(); boolean keepSortedNew = !keepSortedOld; UserPreferences.setQueueKeepSorted(keepSortedNew); @@ -335,6 +303,12 @@ public class QueueFragment extends Fragment implements MaterialToolbar.OnMenuIte } else if (itemId == R.id.action_search) { ((MainActivity) getActivity()).loadChildFragment(SearchFragment.newInstance()); return true; + } else { + SortOrder sortOrder = MenuItemToSortOrderConverter.convert(item); + if (sortOrder != null) { + setSortOrder(sortOrder); + return true; + } } return false; } @@ -444,6 +418,10 @@ public class QueueFragment extends Fragment implements MaterialToolbar.OnMenuIte } ((MainActivity) getActivity()).setupToolbarToggle(toolbar, displayUpArrow); toolbar.inflateMenu(R.menu.queue); + + MenuItem queueItem = toolbar.getMenu().findItem(R.id.queue_sort); + MenuInflater menuInflater = getActivity().getMenuInflater(); + menuInflater.inflate(R.menu.sort_menu, queueItem.getSubMenu()); refreshToolbarState(); progressBar = root.findViewById(R.id.progressBar); progressBar.setVisibility(View.VISIBLE); 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 acdd1e1c7..d5192061c 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/QuickFeedDiscoveryFragment.java @@ -5,7 +5,6 @@ import android.content.SharedPreferences; import android.os.Bundle; import android.text.TextUtils; import android.util.DisplayMetrics; -import androidx.fragment.app.Fragment; import android.util.Log; import android.view.LayoutInflater; @@ -16,20 +15,23 @@ import android.widget.Button; import android.widget.GridView; import android.widget.LinearLayout; import android.widget.TextView; - +import androidx.fragment.app.Fragment; import de.danoeh.antennapod.BuildConfig; -import de.danoeh.antennapod.net.discovery.ItunesTopListLoader; -import de.danoeh.antennapod.net.discovery.PodcastSearchResult; -import org.greenrobot.eventbus.EventBus; -import org.greenrobot.eventbus.Subscribe; -import org.greenrobot.eventbus.ThreadMode; - import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.activity.OnlineFeedViewActivity; import de.danoeh.antennapod.adapter.FeedDiscoverAdapter; +import de.danoeh.antennapod.core.storage.DBReader; import de.danoeh.antennapod.event.DiscoveryDefaultUpdateEvent; +import de.danoeh.antennapod.net.discovery.ItunesTopListLoader; +import de.danoeh.antennapod.net.discovery.PodcastSearchResult; +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; @@ -138,26 +140,29 @@ public class QuickFeedDiscoveryFragment extends Fragment implements AdapterView. return; } - disposable = loader.loadToplist(countryCode, NUM_SUGGESTIONS) + disposable = Observable.fromCallable(() -> + loader.loadToplist(countryCode, NUM_SUGGESTIONS, DBReader.getFeedList())) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) .subscribe( - podcasts -> { - errorView.setVisibility(View.GONE); - if (podcasts.size() == 0) { - errorTextView.setText(getResources().getText(R.string.search_status_no_results)); - errorView.setVisibility(View.VISIBLE); - discoverGridLayout.setVisibility(View.INVISIBLE); - } else { - discoverGridLayout.setVisibility(View.VISIBLE); - adapter.updateData(podcasts); - } - }, error -> { - Log.e(TAG, Log.getStackTraceString(error)); - errorTextView.setText(error.getLocalizedMessage()); + podcasts -> { + errorView.setVisibility(View.GONE); + if (podcasts.size() == 0) { + errorTextView.setText(getResources().getText(R.string.search_status_no_results)); errorView.setVisibility(View.VISIBLE); discoverGridLayout.setVisibility(View.INVISIBLE); - errorRetry.setVisibility(View.VISIBLE); - errorRetry.setOnClickListener((listener) -> loadToplist()); - }); + } else { + discoverGridLayout.setVisibility(View.VISIBLE); + adapter.updateData(podcasts); + } + }, error -> { + Log.e(TAG, Log.getStackTraceString(error)); + errorTextView.setText(error.getLocalizedMessage()); + errorView.setVisibility(View.VISIBLE); + discoverGridLayout.setVisibility(View.INVISIBLE); + errorRetry.setVisibility(View.VISIBLE); + errorRetry.setOnClickListener(v -> loadToplist()); + }); } @Override 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 77fec7a8f..d2aa35549 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/SearchFragment.java @@ -54,6 +54,9 @@ import org.greenrobot.eventbus.ThreadMode; import java.util.Collections; import java.util.List; +import de.danoeh.antennapod.menuhandler.FeedMenuHandler; +import de.danoeh.antennapod.event.FeedListUpdateEvent; + /** * Performs a search operation on all feeds or one specific feed and displays the search result. @@ -132,6 +135,7 @@ public class SearchFragment extends Fragment { recyclerView = layout.findViewById(R.id.recyclerView); recyclerView.setRecycledViewPool(((MainActivity) getActivity()).getRecycledViewPool()); + registerForContextMenu(recyclerView); adapter = new EpisodeItemListAdapter((MainActivity) getActivity()) { @Override public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { @@ -146,7 +150,14 @@ public class SearchFragment extends Fragment { LinearLayoutManager layoutManagerFeeds = new LinearLayoutManager(getActivity()); layoutManagerFeeds.setOrientation(RecyclerView.HORIZONTAL); recyclerViewFeeds.setLayoutManager(layoutManagerFeeds); - adapterFeeds = new HorizontalFeedListAdapter((MainActivity) getActivity()); + adapterFeeds = new HorizontalFeedListAdapter((MainActivity) getActivity()) { + @Override + public void onCreateContextMenu(ContextMenu contextMenu, View view, + ContextMenu.ContextMenuInfo contextMenuInfo) { + super.onCreateContextMenu(contextMenu, view, contextMenuInfo); + MenuItemUtils.setOnClickListeners(contextMenu, SearchFragment.this::onContextItemSelected); + } + }; recyclerViewFeeds.setAdapter(adapterFeeds); emptyViewHandler = new EmptyViewHandler(getContext()); @@ -241,12 +252,21 @@ public class SearchFragment extends Fragment { @Override public boolean onContextItemSelected(@NonNull MenuItem item) { + Feed selectedFeedItem = adapterFeeds.getLongPressedItem(); + if (selectedFeedItem != null + && FeedMenuHandler.onMenuItemClicked(this, item.getItemId(), selectedFeedItem, () -> { })) { + return true; + } FeedItem selectedItem = adapter.getLongPressedItem(); - if (selectedItem == null) { - Log.i(TAG, "Selected item at current position was null, ignoring selection"); - return super.onContextItemSelected(item); + if (selectedItem != null && FeedItemMenuHandler.onMenuItemClicked(this, item.getItemId(), selectedItem)) { + return true; } - return FeedItemMenuHandler.onMenuItemClicked(this, item.getItemId(), selectedItem); + return super.onContextItemSelected(item); + } + + @Subscribe(threadMode = ThreadMode.MAIN) + public void onFeedListChanged(FeedListUpdateEvent event) { + search(); } @Subscribe(threadMode = ThreadMode.MAIN) 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 2800537e2..cc13f4ce3 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/SubscriptionFragment.java @@ -1,8 +1,6 @@ package de.danoeh.antennapod.fragment; -import android.annotation.SuppressLint; import android.content.Context; -import android.content.DialogInterface; import android.content.SharedPreferences; import android.os.Bundle; import android.os.Handler; @@ -17,7 +15,6 @@ import android.widget.ProgressBar; import android.widget.TextView; import androidx.annotation.NonNull; -import androidx.annotation.StringRes; import com.google.android.material.appbar.MaterialToolbar; import androidx.fragment.app.Fragment; import androidx.recyclerview.widget.GridLayoutManager; @@ -28,7 +25,6 @@ import com.google.android.material.floatingactionbutton.FloatingActionButton; import com.joanzapata.iconify.Iconify; import com.leinardi.android.speeddial.SpeedDialView; -import de.danoeh.antennapod.dialog.TagSettingsDialog; import de.danoeh.antennapod.ui.statistics.StatisticsFragment; import de.danoeh.antennapod.view.LiftOnScrollListener; import org.greenrobot.eventbus.EventBus; @@ -36,15 +32,12 @@ import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; import java.util.ArrayList; -import java.util.Collections; import java.util.List; import java.util.Locale; -import java.util.concurrent.Callable; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.MainActivity; import de.danoeh.antennapod.adapter.SubscriptionsRecyclerAdapter; -import de.danoeh.antennapod.core.dialog.ConfirmationDialog; import de.danoeh.antennapod.core.event.DownloadEvent; import de.danoeh.antennapod.event.FeedListUpdateEvent; import de.danoeh.antennapod.event.UnreadItemsUpdateEvent; @@ -52,16 +45,15 @@ import de.danoeh.antennapod.core.menuhandler.MenuItemUtils; import de.danoeh.antennapod.storage.preferences.UserPreferences; import de.danoeh.antennapod.core.service.download.DownloadService; import de.danoeh.antennapod.core.storage.DBReader; -import de.danoeh.antennapod.core.storage.DBWriter; import de.danoeh.antennapod.core.storage.NavDrawerData; import de.danoeh.antennapod.core.util.download.AutoUpdateManager; import de.danoeh.antennapod.dialog.FeedSortDialog; -import de.danoeh.antennapod.dialog.RemoveFeedDialog; import de.danoeh.antennapod.dialog.RenameItemDialog; import de.danoeh.antennapod.dialog.SubscriptionsFilterDialog; import de.danoeh.antennapod.fragment.actions.FeedMultiSelectActionHandler; import de.danoeh.antennapod.model.feed.Feed; import de.danoeh.antennapod.view.EmptyViewHandler; +import de.danoeh.antennapod.menuhandler.FeedMenuHandler; import io.reactivex.Observable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.Disposable; @@ -150,7 +142,6 @@ public class SubscriptionFragment extends Fragment } subscriptionRecycler = root.findViewById(R.id.subscriptions_grid); - setColumnNumber(prefs.getInt(PREF_NUM_COLUMNS, getDefaultNumOfColumns())); subscriptionRecycler.addItemDecoration(new SubscriptionsRecyclerAdapter.GridDividerItemDecorator()); registerForContextMenu(subscriptionRecycler); subscriptionRecycler.addOnScrollListener(new LiftOnScrollListener(root.findViewById(R.id.appbar))); @@ -161,6 +152,7 @@ public class SubscriptionFragment extends Fragment MenuItemUtils.setOnClickListeners(menu, SubscriptionFragment.this::onContextItemSelected); } }; + setColumnNumber(prefs.getInt(PREF_NUM_COLUMNS, getDefaultNumOfColumns())); subscriptionAdapter.setOnSelectModeListener(this); subscriptionRecycler.setAdapter(subscriptionAdapter); setupEmptyView(); @@ -259,6 +251,7 @@ public class SubscriptionFragment extends Fragment private void setColumnNumber(int columns) { GridLayoutManager gridLayoutManager = new GridLayoutManager(getContext(), columns, RecyclerView.VERTICAL, false); + subscriptionAdapter.setColumnCount(columns); subscriptionRecycler.setLayoutManager(gridLayoutManager); prefs.edit().putInt(PREF_NUM_COLUMNS, columns).apply(); refreshToolbarState(); @@ -350,43 +343,11 @@ public class SubscriptionFragment extends Fragment } Feed feed = ((NavDrawerData.FeedDrawerItem) drawerItem).feed; - if (itemId == R.id.remove_all_inbox_item) { - displayConfirmationDialog( - R.string.remove_all_inbox_label, - R.string.remove_all_inbox_confirmation_msg, - () -> DBWriter.removeFeedNewFlag(feed.getId())); - return true; - } else if (itemId == R.id.edit_tags) { - TagSettingsDialog.newInstance(Collections.singletonList(feed.getPreferences())) - .show(getChildFragmentManager(), TagSettingsDialog.TAG); - return true; - } else if (itemId == R.id.rename_item) { - new RenameItemDialog(getActivity(), feed).show(); - return true; - } else if (itemId == R.id.remove_feed) { - RemoveFeedDialog.show(getContext(), feed); - return true; - } else if (itemId == R.id.multi_select) { + if (itemId == R.id.multi_select) { speedDialView.setVisibility(View.VISIBLE); return subscriptionAdapter.onContextItemSelected(item); } - return super.onContextItemSelected(item); - } - - private <T> void displayConfirmationDialog(@StringRes int title, @StringRes int message, Callable<? extends T> task) { - ConfirmationDialog dialog = new ConfirmationDialog(getActivity(), title, message) { - @Override - @SuppressLint("CheckResult") - public void onConfirmButtonPressed(DialogInterface clickedDialog) { - clickedDialog.dismiss(); - Observable.fromCallable(task) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(result -> loadSubscriptions(), - error -> Log.e(TAG, Log.getStackTraceString(error))); - } - }; - dialog.createNewDialog().show(); + return FeedMenuHandler.onMenuItemClicked(this, item.getItemId(), feed, this::loadSubscriptions); } @Subscribe(threadMode = ThreadMode.MAIN) 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 caf555964..66f592af2 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 @@ -5,18 +5,19 @@ import android.os.Build; import android.os.Bundle; import android.widget.ListView; import androidx.appcompat.app.AlertDialog; -import com.google.android.material.snackbar.Snackbar; -import com.google.android.material.dialog.MaterialAlertDialogBuilder; import androidx.core.app.ActivityCompat; +import androidx.preference.Preference; import androidx.preference.PreferenceFragmentCompat; +import com.google.android.material.dialog.MaterialAlertDialogBuilder; +import com.google.android.material.snackbar.Snackbar; import de.danoeh.antennapod.R; import de.danoeh.antennapod.activity.PreferenceActivity; -import de.danoeh.antennapod.storage.preferences.UserPreferences; import de.danoeh.antennapod.dialog.DrawerPreferencesDialog; import de.danoeh.antennapod.dialog.FeedSortDialog; import de.danoeh.antennapod.dialog.SubscriptionsFilterDialog; import de.danoeh.antennapod.event.PlayerStatusEvent; import de.danoeh.antennapod.event.UnreadItemsUpdateEvent; +import de.danoeh.antennapod.storage.preferences.UserPreferences; import org.greenrobot.eventbus.EventBus; import java.util.List; @@ -37,22 +38,16 @@ public class UserInterfacePreferencesFragment extends PreferenceFragmentCompat { } private void setupInterfaceScreen() { - findPreference(UserPreferences.PREF_THEME) - .setOnPreferenceChangeListener( - (preference, newValue) -> { - ActivityCompat.recreate(getActivity()); - return true; - }); - + Preference.OnPreferenceChangeListener restartApp = (preference, newValue) -> { + ActivityCompat.recreate(getActivity()); + return true; + }; + findPreference(UserPreferences.PREF_THEME).setOnPreferenceChangeListener(restartApp); + findPreference(UserPreferences.PREF_THEME_BLACK).setOnPreferenceChangeListener(restartApp); + findPreference(UserPreferences.PREF_TINTED_COLORS).setOnPreferenceChangeListener(restartApp); if (Build.VERSION.SDK_INT < 31) { findPreference(UserPreferences.PREF_TINTED_COLORS).setVisible(false); } - findPreference(UserPreferences.PREF_TINTED_COLORS) - .setOnPreferenceChangeListener( - (preference, newValue) -> { - ActivityCompat.recreate(getActivity()); - return true; - }); findPreference(UserPreferences.PREF_SHOW_TIME_LEFT) .setOnPreferenceChangeListener( 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 de6056063..7aee499da 100644 --- a/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java +++ b/app/src/main/java/de/danoeh/antennapod/menuhandler/FeedMenuHandler.java @@ -14,6 +14,19 @@ import de.danoeh.antennapod.dialog.IntraFeedSortDialog; import de.danoeh.antennapod.model.feed.Feed; import de.danoeh.antennapod.model.feed.SortOrder; import org.apache.commons.lang3.StringUtils; +import android.content.DialogInterface; +import android.annotation.SuppressLint; +import androidx.fragment.app.Fragment; +import de.danoeh.antennapod.core.dialog.ConfirmationDialog; +import de.danoeh.antennapod.dialog.RemoveFeedDialog; +import de.danoeh.antennapod.dialog.RenameItemDialog; +import de.danoeh.antennapod.dialog.TagSettingsDialog; +import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.schedulers.Schedulers; +import java.util.Collections; +import java.util.concurrent.Callable; +import java.util.concurrent.Future; /** * Handles interactions with the FeedItemMenu. @@ -75,4 +88,38 @@ public class FeedMenuHandler { sortDialog.openDialog(); } + public static boolean onMenuItemClicked(@NonNull Fragment fragment, int menuItemId, + @NonNull Feed selectedFeed, Runnable callback) { + @NonNull Context context = fragment.requireContext(); + if (menuItemId == R.id.rename_folder_item) { + new RenameItemDialog(fragment.getActivity(), selectedFeed).show(); + } else if (menuItemId == R.id.remove_all_inbox_item) { + ConfirmationDialog dialog = new ConfirmationDialog(fragment.getActivity(), + R.string.remove_all_inbox_label, R.string.remove_all_inbox_confirmation_msg) { + @Override + @SuppressLint("CheckResult") + public void onConfirmButtonPressed(DialogInterface clickedDialog) { + clickedDialog.dismiss(); + Observable.fromCallable((Callable<Future>) () -> DBWriter.removeFeedNewFlag(selectedFeed.getId())) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(result -> callback.run(), + error -> Log.e(TAG, Log.getStackTraceString(error))); + } + }; + dialog.createNewDialog().show(); + + } else if (menuItemId == R.id.edit_tags) { + TagSettingsDialog.newInstance(Collections.singletonList(selectedFeed.getPreferences())) + .show(fragment.getChildFragmentManager(), TagSettingsDialog.TAG); + } else if (menuItemId == R.id.rename_item) { + new RenameItemDialog(fragment.getActivity(), selectedFeed).show(); + } else if (menuItemId == R.id.remove_feed) { + RemoveFeedDialog.show(context, selectedFeed); + } else { + return false; + } + return true; + } + } 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 e093c067d..79d06de59 100644 --- a/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java +++ b/app/src/main/java/de/danoeh/antennapod/preferences/PreferenceUpgrader.java @@ -133,5 +133,13 @@ public class PreferenceUpgrader { "" + UserPreferences.EPISODE_CACHE_SIZE_UNLIMITED).apply(); } } + if (oldVersion < 3010000) { + if (prefs.getString(UserPreferences.PREF_THEME, "system").equals("2")) { + prefs.edit() + .putString(UserPreferences.PREF_THEME, "1") + .putBoolean(UserPreferences.PREF_THEME_BLACK, true) + .apply(); + } + } } } diff --git a/app/src/main/java/de/danoeh/antennapod/preferences/ThemePreference.java b/app/src/main/java/de/danoeh/antennapod/preferences/ThemePreference.java new file mode 100644 index 000000000..30cbeb523 --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/preferences/ThemePreference.java @@ -0,0 +1,53 @@ +package de.danoeh.antennapod.preferences; + +import android.content.Context; +import android.util.AttributeSet; +import androidx.cardview.widget.CardView; +import androidx.preference.Preference; +import androidx.preference.PreferenceViewHolder; +import com.google.android.material.elevation.SurfaceColors; +import de.danoeh.antennapod.R; +import de.danoeh.antennapod.databinding.ThemePreferenceBinding; +import de.danoeh.antennapod.storage.preferences.UserPreferences; + +public class ThemePreference extends Preference { + ThemePreferenceBinding viewBinding; + + public ThemePreference(Context context) { + super(context); + setLayoutResource(R.layout.theme_preference); + } + + public ThemePreference(Context context, AttributeSet attrs) { + super(context, attrs); + setLayoutResource(R.layout.theme_preference); + } + + @Override + public void onBindViewHolder(PreferenceViewHolder holder) { + super.onBindViewHolder(holder); + viewBinding = ThemePreferenceBinding.bind(holder.itemView); + updateUi(); + } + + void updateThemeCard(CardView card, UserPreferences.ThemePreference theme) { + float density = getContext().getResources().getDisplayMetrics().density; + int surfaceColor = SurfaceColors.getColorForElevation(getContext(), 1 * density); + int surfaceColorActive = SurfaceColors.getColorForElevation(getContext(), 32 * density); + UserPreferences.ThemePreference activeTheme = UserPreferences.getTheme(); + card.setCardBackgroundColor(theme == activeTheme ? surfaceColorActive : surfaceColor); + card.setOnClickListener(v -> { + UserPreferences.setTheme(theme); + if (getOnPreferenceChangeListener() != null) { + getOnPreferenceChangeListener().onPreferenceChange(this, UserPreferences.getTheme()); + } + updateUi(); + }); + } + + void updateUi() { + updateThemeCard(viewBinding.themeSystemCard, UserPreferences.ThemePreference.SYSTEM); + updateThemeCard(viewBinding.themeLightCard, UserPreferences.ThemePreference.LIGHT); + updateThemeCard(viewBinding.themeDarkCard, UserPreferences.ThemePreference.DARK); + } +} diff --git a/app/src/main/java/de/danoeh/antennapod/ui/home/sections/DownloadsSection.java b/app/src/main/java/de/danoeh/antennapod/ui/home/sections/DownloadsSection.java index 78c336335..0053b0e82 100644 --- a/app/src/main/java/de/danoeh/antennapod/ui/home/sections/DownloadsSection.java +++ b/app/src/main/java/de/danoeh/antennapod/ui/home/sections/DownloadsSection.java @@ -23,6 +23,8 @@ import de.danoeh.antennapod.fragment.CompletedDownloadsFragment; import de.danoeh.antennapod.fragment.swipeactions.SwipeActions; import de.danoeh.antennapod.model.feed.FeedItem; import de.danoeh.antennapod.model.feed.FeedItemFilter; +import de.danoeh.antennapod.model.feed.SortOrder; +import de.danoeh.antennapod.storage.preferences.UserPreferences; import de.danoeh.antennapod.ui.home.HomeSection; import de.danoeh.antennapod.view.viewholder.EpisodeItemViewHolder; import io.reactivex.Observable; @@ -121,7 +123,8 @@ public class DownloadsSection extends HomeSection { if (disposable != null) { disposable.dispose(); } - disposable = Observable.fromCallable(DBReader::getDownloadedItems) + SortOrder sortOrder = UserPreferences.getDownloadsSortedOrder(); + disposable = Observable.fromCallable(() -> DBReader.getDownloadedItems(sortOrder)) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(downloads -> { diff --git a/app/src/main/java/de/danoeh/antennapod/view/viewholder/EpisodeItemViewHolder.java b/app/src/main/java/de/danoeh/antennapod/view/viewholder/EpisodeItemViewHolder.java index cdf688502..fd3ec9299 100644 --- a/app/src/main/java/de/danoeh/antennapod/view/viewholder/EpisodeItemViewHolder.java +++ b/app/src/main/java/de/danoeh/antennapod/view/viewholder/EpisodeItemViewHolder.java @@ -226,6 +226,10 @@ public class EpisodeItemViewHolder extends RecyclerView.ViewHolder { } private void updateDuration(PlaybackPositionEvent event) { + if (getFeedItem().getMedia() != null) { + getFeedItem().getMedia().setPosition(event.getPosition()); + getFeedItem().getMedia().setDuration(event.getDuration()); + } int currentPosition = event.getPosition(); int timeDuration = event.getDuration(); int remainingTime = Math.max(timeDuration - currentPosition, 0); diff --git a/app/src/main/res/drawable-nodpi/theme_preview_dark.png b/app/src/main/res/drawable-nodpi/theme_preview_dark.png Binary files differnew file mode 100644 index 000000000..b4e1e0376 --- /dev/null +++ b/app/src/main/res/drawable-nodpi/theme_preview_dark.png diff --git a/app/src/main/res/drawable-nodpi/theme_preview_light.png b/app/src/main/res/drawable-nodpi/theme_preview_light.png Binary files differnew file mode 100644 index 000000000..39ef47b4f --- /dev/null +++ b/app/src/main/res/drawable-nodpi/theme_preview_light.png diff --git a/app/src/main/res/drawable-nodpi/theme_preview_system.png b/app/src/main/res/drawable-nodpi/theme_preview_system.png Binary files differnew file mode 100644 index 000000000..cc6403a98 --- /dev/null +++ b/app/src/main/res/drawable-nodpi/theme_preview_system.png diff --git a/app/src/main/res/layout/cover_fragment.xml b/app/src/main/res/layout/cover_fragment.xml index d6664b56c..19f149f2c 100644 --- a/app/src/main/res/layout/cover_fragment.xml +++ b/app/src/main/res/layout/cover_fragment.xml @@ -90,6 +90,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginHorizontal="8dp" + android:paddingHorizontal="8dp" android:background="@drawable/grey_border" android:clickable="true" android:focusable="true" diff --git a/app/src/main/res/layout/edit_tags_dialog.xml b/app/src/main/res/layout/edit_tags_dialog.xml index 7ad938777..b20facbf0 100644 --- a/app/src/main/res/layout/edit_tags_dialog.xml +++ b/app/src/main/res/layout/edit_tags_dialog.xml @@ -7,6 +7,12 @@ android:orientation="vertical" android:padding="16dp"> + <CheckBox + android:id="@+id/rootFolderCheckbox" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:text="@string/feed_folders_include_root" /> + <com.joanzapata.iconify.widget.IconTextView android:id="@+id/commonTagsInfo" android:layout_width="match_parent" @@ -22,12 +28,6 @@ android:layout_width="match_parent" android:layout_height="wrap_content" /> - <CheckBox - android:id="@+id/rootFolderCheckbox" - android:layout_width="match_parent" - android:layout_height="wrap_content" - android:text="@string/feed_folders_include_root" /> - <com.google.android.material.textfield.TextInputLayout android:id="@+id/newTagTextInput" android:layout_width="match_parent" diff --git a/app/src/main/res/layout/subscription_item.xml b/app/src/main/res/layout/subscription_item.xml index 50cb9745f..0b689fa28 100644 --- a/app/src/main/res/layout/subscription_item.xml +++ b/app/src/main/res/layout/subscription_item.xml @@ -1,64 +1,107 @@ <?xml version="1.0" encoding="utf-8"?> -<RelativeLayout +<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:squareImageView="http://schemas.android.com/apk/de.danoeh.antennapod" xmlns:tools="http://schemas.android.com/tools" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:foreground="?attr/selectableItemBackground"> + android:padding="4dp"> - <de.danoeh.antennapod.ui.common.SquareImageView - android:id="@+id/imgvCover" + <androidx.cardview.widget.CardView + android:id="@+id/outerContainer" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/non_square_icon_background" - android:scaleType="fitCenter" - squareImageView:direction="width" - tools:src="@tools:sample/avatars" /> + android:clickable="false" + android:foreground="?attr/selectableItemBackground" + app:cardCornerRadius="12dp" + app:cardElevation="0dp"> - <com.joanzapata.iconify.widget.IconTextView - android:id="@+id/txtvTitle" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_alignStart="@+id/imgvCover" - android:layout_alignLeft="@+id/imgvCover" - android:layout_alignTop="@+id/imgvCover" - android:layout_alignEnd="@+id/imgvCover" - android:layout_alignRight="@+id/imgvCover" - android:layout_alignBottom="@+id/imgvCover" - android:background="@color/non_square_icon_background" - android:ellipsize="end" - android:gravity="center" - android:textColor="?android:attr/textColorPrimary" - tools:text="@string/app_name" /> - - <de.danoeh.antennapod.ui.common.TriangleLabelView - android:id="@+id/triangleCountView" - android:layout_width="match_parent" - android:layout_height="match_parent" - android:layout_alignParentTop="true" - android:layout_alignParentEnd="true" - android:layout_alignParentRight="true" - app:backgroundColor="?attr/colorSecondary" - app:corner="rightTop" - app:primaryText="Test" - app:primaryTextColor="?attr/colorOnSecondary" - app:primaryTextSize="12sp" /> - - <FrameLayout - android:id="@+id/selectView" - android:layout_width="wrap_content" - android:layout_height="wrap_content"> - - <CheckBox - android:id="@+id/selectCheckBox" + <LinearLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:orientation="vertical"> + + <androidx.cardview.widget.CardView + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_margin="1px" + android:clickable="false" + app:cardBackgroundColor="@color/non_square_icon_background" + app:cardCornerRadius="12dp" + app:cardElevation="0dp"> + + <RelativeLayout + android:layout_width="wrap_content" + android:layout_height="wrap_content"> + + <de.danoeh.antennapod.ui.common.SquareImageView + android:id="@+id/coverImage" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:scaleType="fitCenter" + android:outlineProvider="background" + squareImageView:direction="width" + tools:src="@tools:sample/avatars" /> + + <TextView + android:id="@+id/fallbackTitleLabel" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_alignStart="@+id/coverImage" + android:layout_alignLeft="@+id/coverImage" + android:layout_alignTop="@+id/coverImage" + android:layout_alignEnd="@+id/coverImage" + android:layout_alignRight="@+id/coverImage" + android:layout_alignBottom="@+id/coverImage" + android:background="@color/feed_text_bg" + android:gravity="center" + android:ellipsize="end" + android:padding="6dp" + android:textColor="#fff" + tools:text="@sample/episodes.json/data/title" /> + + <TextView + android:id="@+id/countViewPill" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="3" + android:layout_alignParentEnd="true" + android:textSize="14sp" + style="@style/TextPill" /> + + </RelativeLayout> + + </androidx.cardview.widget.CardView> + + <TextView + android:id="@+id/titleLabel" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:ellipsize="end" + android:gravity="start" + android:textColor="?android:attr/textColorPrimary" + android:lines="2" + tools:text="@sample/episodes.json/data/title" /> + + </LinearLayout> + + <FrameLayout + android:id="@+id/selectContainer" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:minWidth="0dp" - android:minHeight="0dp" - android:layout_margin="8dp" /> + android:clickable="false"> + + <CheckBox + android:id="@+id/selectCheckBox" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:minWidth="0dp" + android:minHeight="0dp" + android:layout_margin="8dp" /> + + </FrameLayout> - </FrameLayout> + </androidx.cardview.widget.CardView> -</RelativeLayout> +</FrameLayout> diff --git a/app/src/main/res/layout/theme_preference.xml b/app/src/main/res/layout/theme_preference.xml new file mode 100644 index 000000000..32a7ed1e8 --- /dev/null +++ b/app/src/main/res/layout/theme_preference.xml @@ -0,0 +1,123 @@ +<?xml version="1.0" encoding="utf-8"?> +<LinearLayout + xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="horizontal" + android:background="?android:attr/windowBackground" + android:gravity="top" + android:padding="8dp"> + + <androidx.cardview.widget.CardView + android:id="@+id/themeSystemCard" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_margin="4dp" + android:clickable="true" + android:foreground="?android:attr/selectableItemBackground" + android:layout_weight="1" + app:cardElevation="0dp" + app:cardCornerRadius="16dp" + app:contentPadding="16dp"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:gravity="center"> + + <ImageView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:adjustViewBounds="true" + android:src="@drawable/theme_preview_system" /> + + <TextView + android:id="@+id/themeSystemRadio" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="4dp" + android:textColor="?android:attr/textColorPrimary" + android:text="@string/pref_theme_title_automatic" + android:clickable="false" /> + + </LinearLayout> + + </androidx.cardview.widget.CardView> + + <androidx.cardview.widget.CardView + android:id="@+id/themeLightCard" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_margin="4dp" + android:clickable="true" + android:foreground="?android:attr/selectableItemBackground" + android:layout_weight="1" + app:cardElevation="0dp" + app:cardCornerRadius="16dp" + app:contentPadding="16dp"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:gravity="center"> + + <ImageView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:adjustViewBounds="true" + android:src="@drawable/theme_preview_light" /> + + <TextView + android:id="@+id/themeLightRadio" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="4dp" + android:textColor="?android:attr/textColorPrimary" + android:text="@string/pref_theme_title_light" + android:clickable="false" /> + + </LinearLayout> + + </androidx.cardview.widget.CardView> + + <androidx.cardview.widget.CardView + android:id="@+id/themeDarkCard" + android:layout_width="0dp" + android:layout_height="wrap_content" + android:layout_margin="4dp" + android:clickable="true" + android:foreground="?android:attr/selectableItemBackground" + android:layout_weight="1" + app:cardElevation="0dp" + app:cardCornerRadius="16dp" + app:contentPadding="16dp"> + + <LinearLayout + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:orientation="vertical" + android:gravity="center"> + + <ImageView + android:layout_width="match_parent" + android:layout_height="wrap_content" + android:adjustViewBounds="true" + android:src="@drawable/theme_preview_dark" /> + + <TextView + android:id="@+id/themeDarkCardRadio" + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="4dp" + android:textColor="?android:attr/textColorPrimary" + android:text="@string/pref_theme_title_dark" + android:clickable="false" /> + + </LinearLayout> + + </androidx.cardview.widget.CardView> + +</LinearLayout> diff --git a/app/src/main/res/menu/downloads_completed.xml b/app/src/main/res/menu/downloads_completed.xml index 61a0e6578..08179de58 100644 --- a/app/src/main/res/menu/downloads_completed.xml +++ b/app/src/main/res/menu/downloads_completed.xml @@ -22,5 +22,10 @@ android:menuCategory="container" android:icon="@drawable/ic_refresh" app:showAsAction="always" /> + <item + android:id="@+id/downloads_sort" + android:title="@string/sort"> + <menu></menu> + </item> </menu> diff --git a/app/src/main/res/menu/queue.xml b/app/src/main/res/menu/queue.xml index c1c2d982c..e4bb63808 100644 --- a/app/src/main/res/menu/queue.xml +++ b/app/src/main/res/menu/queue.xml @@ -24,88 +24,7 @@ <item android:id="@+id/queue_sort" android:title="@string/sort"> - - <menu> - <item - android:id="@+id/queue_sort_date" - android:title="@string/date"> - - <menu> - <item - android:id="@+id/queue_sort_date_asc" - android:title="@string/sort_old_new"/> - <item - android:id="@+id/queue_sort_date_desc" - android:title="@string/sort_new_old"/> - </menu> - </item> - - <item - android:id="@+id/queue_sort_duration" - android:title="@string/duration"> - - <menu> - <item - android:id="@+id/queue_sort_duration_asc" - android:title="@string/sort_short_long"/> - <item - android:id="@+id/queue_sort_duration_desc" - android:title="@string/sort_long_short"/> - </menu> - </item> - - <item - android:id="@+id/queue_sort_episode_title" - android:title="@string/episode_title"> - - <menu> - <item - android:id="@+id/queue_sort_episode_title_asc" - android:title="@string/sort_a_z"/> - <item - android:id="@+id/queue_sort_episode_title_desc" - android:title="@string/sort_z_a"/> - </menu> - </item> - - <item - android:id="@+id/queue_sort_feed_title" - android:title="@string/feed_title"> - - <menu> - <item - android:id="@+id/queue_sort_feed_title_asc" - android:title="@string/sort_a_z"/> - <item - android:id="@+id/queue_sort_feed_title_desc" - android:title="@string/sort_z_a"/> - </menu> - </item> - - <item - android:id="@+id/queue_sort_random" - android:title="@string/random"> - </item> - - <item - android:id="@+id/queue_sort_smart_shuffle" - android:title="@string/smart_shuffle"> - - <menu> - <item - android:id="@+id/queue_sort_smart_shuffle_asc" - android:title="@string/sort_old_new"/> - <item - android:id="@+id/queue_sort_smart_shuffle_desc" - android:title="@string/sort_new_old"/> - </menu> - </item> - - <item - android:id="@+id/queue_keep_sorted" - android:title="@string/keep_sorted" - android:checkable="true" /> - </menu> + <menu></menu> </item> <item diff --git a/app/src/main/res/menu/sort_menu.xml b/app/src/main/res/menu/sort_menu.xml new file mode 100644 index 000000000..f0a22042b --- /dev/null +++ b/app/src/main/res/menu/sort_menu.xml @@ -0,0 +1,83 @@ +<?xml version="1.0" encoding="utf-8"?> +<menu xmlns:android="http://schemas.android.com/apk/res/android"> + + <item + android:id="@+id/sort_date" + android:title="@string/date"> + + <menu> + <item + android:id="@+id/sort_date_asc" + android:title="@string/sort_old_new"/> + <item + android:id="@+id/sort_date_desc" + android:title="@string/sort_new_old"/> + </menu> + </item> + + <item + android:id="@+id/sort_duration" + android:title="@string/duration"> + + <menu> + <item + android:id="@+id/sort_duration_asc" + android:title="@string/sort_short_long"/> + <item + android:id="@+id/sort_duration_desc" + android:title="@string/sort_long_short"/> + </menu> + </item> + + <item + android:id="@+id/sort_episode_title" + android:title="@string/episode_title"> + + <menu> + <item + android:id="@+id/sort_episode_title_asc" + android:title="@string/sort_a_z"/> + <item + android:id="@+id/sort_episode_title_desc" + android:title="@string/sort_z_a"/> + </menu> + </item> + + <item + android:id="@+id/sort_feed_title" + android:title="@string/feed_title"> + + <menu> + <item + android:id="@+id/sort_feed_title_asc" + android:title="@string/sort_a_z"/> + <item + android:id="@+id/sort_feed_title_desc" + android:title="@string/sort_z_a"/> + </menu> + </item> + + <item + android:id="@+id/sort_random" + android:title="@string/random"> + </item> + + <item + android:id="@+id/sort_smart_shuffle" + android:title="@string/smart_shuffle"> + + <menu> + <item + android:id="@+id/sort_smart_shuffle_asc" + android:title="@string/sort_old_new"/> + <item + android:id="@+id/sort_smart_shuffle_desc" + android:title="@string/sort_new_old"/> + </menu> + </item> + <item + android:id="@+id/keep_sorted" + android:title="@string/keep_sorted" + android:checkable="true" /> + +</menu> diff --git a/app/src/main/res/xml/preferences_user_interface.xml b/app/src/main/res/xml/preferences_user_interface.xml index f11b89be7..ed3f5777a 100644 --- a/app/src/main/res/xml/preferences_user_interface.xml +++ b/app/src/main/res/xml/preferences_user_interface.xml @@ -4,13 +4,13 @@ xmlns:search="http://schemas.android.com/apk/com.bytehamster.lib.preferencesearch"> <PreferenceCategory android:title="@string/appearance"> - <de.danoeh.antennapod.preferences.MaterialListPreference - android:entryValues="@array/theme_values" - android:entries="@array/theme_options" - android:title="@string/pref_set_theme_title" - android:key="prefTheme" - android:summary="@string/pref_set_theme_sum" - android:defaultValue="system"/> + <de.danoeh.antennapod.preferences.ThemePreference + android:key="prefTheme" /> + <SwitchPreferenceCompat + android:title="@string/pref_black_theme_title" + android:key="prefThemeBlack" + android:summary="@string/pref_black_theme_message" + android:defaultValue="false" /> <SwitchPreferenceCompat android:title="@string/pref_tinted_theme_title" android:key="prefTintedColors" |