diff options
41 files changed, 326 insertions, 272 deletions
diff --git a/.github/workflows/android-emulator.yml b/.github/workflows/android-emulator.yml index 52c6f0f69..2946b2be2 100644 --- a/.github/workflows/android-emulator.yml +++ b/.github/workflows/android-emulator.yml @@ -8,7 +8,7 @@ jobs: build: runs-on: macOS-latest steps: - - uses: actions/checkout@v1 + - uses: actions/checkout@v2 - name: set up JDK 1.8 uses: actions/setup-java@v1 with: diff --git a/app/build.gradle b/app/build.gradle index a2e85317c..44faefeec 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -145,7 +145,7 @@ dependencies { implementation "androidx.media:media:$mediaVersion" implementation "androidx.preference:preference:$preferenceVersion" implementation 'androidx.recyclerview:recyclerview:1.1.0' - implementation 'androidx.viewpager2:viewpager2:1.0.0' + implementation 'androidx.viewpager2:viewpager2:1.1.0-alpha01' implementation "androidx.work:work-runtime:$workManagerVersion" implementation "com.google.android.material:material:$googleMaterialVersion" 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 a0d1c17d5..84a253f0b 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/OnlineFeedViewActivity.java @@ -145,7 +145,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity { } private void showNoPodcastFoundError() { - new AlertDialog.Builder(OnlineFeedViewActivity.this) + runOnUiThread(() -> new AlertDialog.Builder(OnlineFeedViewActivity.this) .setNeutralButton(android.R.string.ok, (dialog, which) -> finish()) .setTitle(R.string.error_label) .setMessage(R.string.null_value_podcast_error) @@ -153,7 +153,7 @@ public class OnlineFeedViewActivity extends AppCompatActivity { setResult(RESULT_ERROR); finish(); }) - .show(); + .show()); } /** diff --git a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportActivity.java b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportActivity.java index f4d312a4f..10292b892 100644 --- a/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportActivity.java +++ b/app/src/main/java/de/danoeh/antennapod/activity/OpmlImportActivity.java @@ -18,8 +18,12 @@ import de.danoeh.antennapod.asynctask.OpmlImportWorker; import de.danoeh.antennapod.core.export.opml.OpmlElement; import de.danoeh.antennapod.core.preferences.UserPreferences; import de.danoeh.antennapod.core.util.LangUtils; +import org.apache.commons.io.ByteOrderMark; +import org.apache.commons.io.IOUtils; +import org.apache.commons.io.input.BOMInputStream; import org.apache.commons.lang3.ArrayUtils; +import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; import java.util.ArrayList; @@ -130,7 +134,12 @@ public class OpmlImportActivity extends AppCompatActivity { /** Starts the import process. */ private void startImport() { try { - Reader reader = new InputStreamReader(getContentResolver().openInputStream(uri), LangUtils.UTF_8); + InputStream opmlFileStream = getContentResolver().openInputStream(uri); + BOMInputStream bomInputStream = new BOMInputStream(opmlFileStream); + ByteOrderMark bom = bomInputStream.getBOM(); + String charsetName = (bom == null) ? "UTF-8" : bom.getCharsetName(); + Reader reader = new InputStreamReader(bomInputStream, charsetName); + OpmlImportWorker importWorker = new OpmlImportWorker(this, reader) { @Override 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 153f4abc3..7d195a9ad 100644 --- a/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java +++ b/app/src/main/java/de/danoeh/antennapod/adapter/DownloadLogAdapter.java @@ -73,11 +73,13 @@ public class DownloadLogAdapter extends BaseAdapter { if (status.isSuccessful()) { holder.icon.setTextColor(ContextCompat.getColor(context, R.color.download_success_green)); holder.icon.setText("{fa-check-circle}"); + holder.icon.setContentDescription(context.getString(R.string.download_successful)); holder.secondaryActionButton.setVisibility(View.INVISIBLE); holder.reason.setVisibility(View.GONE); } else { holder.icon.setTextColor(ContextCompat.getColor(context, R.color.download_failed_red)); holder.icon.setText("{fa-times-circle}"); + holder.icon.setContentDescription(context.getString(R.string.error_label)); String reasonText = status.getReason().getErrorString(context); if (status.getReasonDetailed() != null) { reasonText += ": " + status.getReasonDetailed(); 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 abab94852..ac1e94437 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/EpisodesApplyActionFragment.java @@ -185,6 +185,21 @@ public class EpisodesApplyActionFragment extends Fragment { } } + mSpeedDialView.setOnChangeListener(new SpeedDialView.OnChangeListener() { + @Override + public boolean onMainActionSelected() { + return false; + } + + @Override + public void onToggleChanged(boolean open) { + if (open && checkedIds.size() == 0) { + ((MainActivity) getActivity()).showSnackbarAbovePlayer(R.string.no_items_selected, + Snackbar.LENGTH_SHORT); + mSpeedDialView.close(); + } + } + }); mSpeedDialView.setOnActionSelectedListener(actionItem -> { ActionBinding selectedBinding = null; for (ActionBinding binding : actionBindings) { @@ -204,16 +219,6 @@ public class EpisodesApplyActionFragment extends Fragment { return view; } - private void showSpeedDialIfAnyChecked() { - if (checkedIds.size() > 0) { - if (!mSpeedDialView.isShown()) { - mSpeedDialView.show(); - } - } else { - mSpeedDialView.hide(); // hide() also handles UI, e.g., overlay properly. - } - } - @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); @@ -409,7 +414,6 @@ public class EpisodesApplyActionFragment extends Fragment { mListView.setItemChecked(i, checked); } ActivityCompat.invalidateOptionsMenu(EpisodesApplyActionFragment.this.getActivity()); - showSpeedDialIfAnyChecked(); toolbar.setTitle(getResources().getQuantityString(R.plurals.num_selected_label, checkedIds.size(), checkedIds.size())); } 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 83debaecb..d1ffdc148 100644 --- a/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java +++ b/app/src/main/java/de/danoeh/antennapod/dialog/PlaybackControlsDialog.java @@ -80,7 +80,7 @@ public class PlaybackControlsDialog extends DialogFragment { private void setupUi() { final SeekBar barPlaybackSpeed = dialog.findViewById(R.id.playback_speed); - final Button butDecSpeed = dialog.findViewById(R.id.butDecSpeed); + final TextView butDecSpeed = dialog.findViewById(R.id.butDecSpeed); butDecSpeed.setOnClickListener(v -> { if (controller != null && controller.canSetPlaybackSpeed()) { barPlaybackSpeed.setProgress(barPlaybackSpeed.getProgress() - 2); @@ -88,7 +88,7 @@ public class PlaybackControlsDialog extends DialogFragment { VariableSpeedDialog.showGetPluginDialog(getContext()); } }); - final Button butIncSpeed = (Button) dialog.findViewById(R.id.butIncSpeed); + final TextView butIncSpeed = dialog.findViewById(R.id.butIncSpeed); butIncSpeed.setOnClickListener(v -> { if (controller != null && controller.canSetPlaybackSpeed()) { barPlaybackSpeed.setProgress(barPlaybackSpeed.getProgress() + 2); 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 046a39355..546684f14 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/AddFeedFragment.java @@ -98,7 +98,7 @@ public class AddFeedFragment extends Fragment { private void performSearch() { String query = combinedFeedSearchBox.getText().toString(); - if (query.startsWith("http")) { + if (query.matches("http[s]?://.*")) { addUrl(query); return; } diff --git a/app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java b/app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java index 7df0f5577..5ca8b666a 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/AudioPlayerFragment.java @@ -143,7 +143,7 @@ public class AudioPlayerFragment extends Fragment implements pageIndicator = root.findViewById(R.id.page_indicator); pageIndicator.setViewPager(pager); pageIndicator.setOnClickListener(v -> - pager.setCurrentItem((pager.getCurrentItem() + 1) % pager.getChildCount())); + pager.setCurrentItem((pager.getCurrentItem() + 1) % NUM_CONTENT_FRAGMENTS)); return root; } 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 1f96c12f2..b90da7447 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/FeedItemlistFragment.java @@ -323,7 +323,7 @@ public class FeedItemlistFragment extends Fragment implements AdapterView.OnItem activity.loadChildFragment(ItemPagerFragment.newInstance(ids, position)); } - @Subscribe + @Subscribe(threadMode = ThreadMode.MAIN) public void onEvent(FeedEvent event) { Log.d(TAG, "onEvent() called with: " + "event = [" + event + "]"); if (event.feedId == feedID) { 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 04a62741c..3ae4cc648 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemDescriptionFragment.java @@ -97,7 +97,7 @@ public class ItemDescriptionFragment extends Fragment { @Nullable private String loadData() { - if (controller.getMedia() == null) { + if (controller != null && controller.getMedia() == null) { return null; } Timeline timeline = new Timeline(getActivity(), controller.getMedia()); 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 ee3f2331f..aaf0fc7d4 100644 --- a/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java +++ b/app/src/main/java/de/danoeh/antennapod/fragment/ItemFragment.java @@ -70,6 +70,7 @@ import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.Subscribe; import org.greenrobot.eventbus.ThreadMode; +import java.util.Date; import java.util.List; import java.util.Locale; @@ -288,6 +289,7 @@ public class ItemFragment extends Fragment { if (item.getPubDate() != null) { String pubDateStr = DateUtils.formatAbbrev(getActivity(), item.getPubDate()); txtvPublished.setText(pubDateStr); + txtvPublished.setContentDescription(DateUtils.formatForAccessibility(getContext(), item.getPubDate())); } Glide.with(getActivity()) @@ -321,6 +323,8 @@ public class ItemFragment extends Fragment { } else { if (media.getDuration() > 0) { txtvDuration.setText(Converter.getDurationStringLong(media.getDuration())); + txtvDuration.setContentDescription( + Converter.getDurationStringLocalized(getContext(), media.getDuration())); } if (media.isCurrentlyPlaying()) { actionButton1 = new PauseActionButton(item); 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 7b80c3850..8584befbc 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 @@ -7,11 +7,15 @@ import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.view.accessibility.AccessibilityEvent; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.TextView; import androidx.cardview.widget.CardView; +import androidx.core.view.AccessibilityDelegateCompat; +import androidx.core.view.ViewCompat; +import androidx.core.view.accessibility.AccessibilityNodeInfoCompat; import androidx.recyclerview.widget.RecyclerView; import com.joanzapata.iconify.Iconify; @@ -57,6 +61,7 @@ public class EpisodeItemViewHolder extends RecyclerView.ViewHolder { public final ImageView secondaryActionIcon; private final CircularProgressBar secondaryActionProgress; private final TextView separatorIcons; + private final View leftPadding; public final CardView coverHolder; private final MainActivity activity; @@ -87,6 +92,7 @@ public class EpisodeItemViewHolder extends RecyclerView.ViewHolder { secondaryActionButton = itemView.findViewById(R.id.secondaryActionButton); secondaryActionIcon = itemView.findViewById(R.id.secondaryActionIcon); coverHolder = itemView.findViewById(R.id.coverHolder); + leftPadding = itemView.findViewById(R.id.left_padding); itemView.setTag(this); } @@ -94,11 +100,13 @@ public class EpisodeItemViewHolder extends RecyclerView.ViewHolder { this.item = item; placeholder.setText(item.getFeed().getTitle()); title.setText(item.getTitle()); + leftPadding.setContentDescription(item.getTitle()); pubDate.setText(DateUtils.formatAbbrev(activity, item.getPubDate())); + pubDate.setContentDescription(DateUtils.formatForAccessibility(activity, item.getPubDate())); isNew.setVisibility(item.isNew() ? View.VISIBLE : View.GONE); isFavorite.setVisibility(item.isTagged(FeedItem.TAG_FAVORITE) ? View.VISIBLE : View.GONE); isInQueue.setVisibility(item.isTagged(FeedItem.TAG_QUEUE) ? View.VISIBLE : View.GONE); - itemView.setAlpha(item.isPlayed() ? 0.5f : 1.0f); + itemView.setAlpha(item.isPlayed() ? 0.6f : 1.0f); ItemActionButton actionButton = ItemActionButton.forItem(item, true, true); actionButton.configure(secondaryActionButton, secondaryActionIcon, activity); @@ -128,6 +136,8 @@ public class EpisodeItemViewHolder extends RecyclerView.ViewHolder { isVideo.setVisibility(media.getMediaType() == MediaType.VIDEO ? View.VISIBLE : View.GONE); duration.setVisibility(media.getDuration() > 0 ? View.VISIBLE : View.GONE); duration.setText(Converter.getDurationStringLong(media.getDuration())); + duration.setContentDescription(activity.getString(R.string.chapter_duration, + Converter.getDurationStringLocalized(activity, media.getDuration()))); if (media.isCurrentlyPlaying()) { container.setBackgroundColor(ThemeUtils.getColorFromAttr(activity, R.attr.currently_playing_background)); @@ -149,6 +159,8 @@ public class EpisodeItemViewHolder extends RecyclerView.ViewHolder { int progress = (int) (100.0 * media.getPosition() / media.getDuration()); progressBar.setProgress(progress); position.setText(Converter.getDurationStringLong(media.getPosition())); + position.setContentDescription(activity.getString(R.string.position, + Converter.getDurationStringLocalized(activity, media.getPosition()))); progressBar.setVisibility(View.VISIBLE); position.setVisibility(View.VISIBLE); } else { diff --git a/app/src/main/res/layout/about_teaser.xml b/app/src/main/res/layout/about_teaser.xml index a9e50f6da..4e7f0454f 100644 --- a/app/src/main/res/layout/about_teaser.xml +++ b/app/src/main/res/layout/about_teaser.xml @@ -4,4 +4,6 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:adjustViewBounds="true" - android:layout_height="wrap_content" app:srcCompat="@drawable/teaser" />
\ No newline at end of file + android:layout_height="wrap_content" + app:srcCompat="@drawable/teaser" + android:importantForAccessibility="no"/>
\ No newline at end of file diff --git a/app/src/main/res/layout/audio_controls.xml b/app/src/main/res/layout/audio_controls.xml index 6cb87282f..9a1c525a7 100644 --- a/app/src/main/res/layout/audio_controls.xml +++ b/app/src/main/res/layout/audio_controls.xml @@ -41,18 +41,22 @@ <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" - android:orientation="horizontal"> + android:orientation="horizontal" + android:gravity="center_vertical"> - <Button + <TextView android:id="@+id/butDecSpeed" android:layout_width="32dp" android:layout_height="32dp" android:gravity="center" android:text="-" + android:clickable="true" + android:focusable="true" android:textStyle="bold" - android:textColor="?attr/colorAccent" android:textSize="24sp" - android:background="?attr/selectableItemBackgroundBorderless" /> + android:textColor="?attr/colorSecondary" + android:contentDescription="@string/decrease_volume" + android:background="?attr/selectableItemBackgroundBorderless"/> <SeekBar android:id="@+id/playback_speed" @@ -61,16 +65,18 @@ android:max="70" android:layout_weight="1" /> - <Button + <TextView android:id="@+id/butIncSpeed" android:layout_width="32dp" android:layout_height="32dp" - android:minWidth="0dp" android:gravity="center" android:text="+" + android:clickable="true" + android:focusable="true" android:textStyle="bold" - android:textColor="?attr/colorAccent" android:textSize="24sp" + android:textColor="?attr/colorSecondary" + android:contentDescription="@string/increase_volume" android:background="?attr/selectableItemBackgroundBorderless" /> </LinearLayout> diff --git a/app/src/main/res/layout/audioplayer_fragment.xml b/app/src/main/res/layout/audioplayer_fragment.xml index a9e9137f2..1935ce36c 100644 --- a/app/src/main/res/layout/audioplayer_fragment.xml +++ b/app/src/main/res/layout/audioplayer_fragment.xml @@ -22,6 +22,7 @@ android:layout_marginTop="-12dp" android:padding="4dp" android:layout_below="@id/toolbar" + android:contentDescription="@string/switch_pages" android:layout_centerHorizontal="true"/> <FrameLayout @@ -29,9 +30,8 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentTop="true" - android:background="?android:attr/windowBackground" + android:background="?attr/background_elevated" tools:layout_height="@dimen/external_player_height" - tools:background="@android:color/holo_green_light" android:elevation="8dp"/> <androidx.viewpager2.widget.ViewPager2 @@ -41,7 +41,6 @@ android:layout_above="@id/playtime_layout" android:layout_below="@id/toolbar" android:foreground="?android:windowContentOverlay" - tools:background="@android:color/holo_orange_light" android:layout_marginBottom="12dp"/> <SeekBar @@ -53,8 +52,7 @@ android:layout_above="@id/playtime_layout" android:layout_marginLeft="8dp" android:layout_marginRight="8dp" - android:layoutDirection="ltr" - tools:background="@android:color/holo_green_dark"/> + android:layoutDirection="ltr" /> <LinearLayout android:id="@+id/playtime_layout" @@ -82,8 +80,7 @@ android:layout_marginStart="16dp" android:text="@string/position_default_label" android:textColor="?android:attr/textColorSecondary" - android:textSize="@dimen/text_size_micro" - tools:background="@android:color/holo_green_dark"/> + android:textSize="@dimen/text_size_micro"/> <TextView android:id="@+id/txtvLength" @@ -96,8 +93,7 @@ android:text="@string/position_default_label" android:textColor="?android:attr/textColorSecondary" android:textSize="@dimen/text_size_micro" - android:background="?android:attr/selectableItemBackground" - tools:background="@android:color/holo_green_dark"/> + android:background="?android:attr/selectableItemBackground"/> </RelativeLayout> @@ -105,8 +101,7 @@ android:id="@+id/player_control" android:layout_width="match_parent" android:layout_height="wrap_content" - android:layout_marginBottom="24dp" - tools:background="@android:color/holo_purple"> + android:layout_marginBottom="24dp"> <ImageButton android:id="@+id/butPlay" @@ -123,8 +118,7 @@ android:contentDescription="@string/pause_label" app:srcCompat="?attr/av_play" android:scaleType="fitCenter" - tools:srcCompat="@drawable/ic_av_play_white_24dp" - tools:background="@android:color/holo_green_dark"/> + tools:srcCompat="@drawable/ic_av_play_white_24dp"/> <de.danoeh.antennapod.view.CircularProgressBar android:layout_width="@dimen/audioplayer_playercontrols_length_big" @@ -158,8 +152,7 @@ android:contentDescription="@string/rewind_label" app:srcCompat="?attr/av_rewind" android:scaleType="fitCenter" - tools:srcCompat="@drawable/ic_av_fast_rewind_white_48dp" - tools:background="@android:color/holo_blue_dark"/> + tools:srcCompat="@drawable/ic_av_fast_rewind_white_48dp"/> <TextView android:id="@+id/txtvRev" @@ -185,9 +178,7 @@ android:layout_centerVertical="true" android:background="?attr/selectableItemBackgroundBorderless" android:contentDescription="@string/set_playback_speed_label" - tools:srcCompat="@drawable/ic_playback_speed_white" - tools:visibility="gone" - tools:background="@android:color/holo_green_dark"/> + tools:srcCompat="@drawable/ic_playback_speed_white"/> <TextView android:id="@+id/txtvPlaybackSpeed" @@ -217,8 +208,7 @@ android:contentDescription="@string/fast_forward_label" app:srcCompat="?attr/av_fast_forward" android:scaleType="fitCenter" - tools:srcCompat="@drawable/ic_av_fast_forward_white_48dp" - tools:background="@android:color/holo_blue_dark"/> + tools:srcCompat="@drawable/ic_av_fast_forward_white_48dp"/> <TextView android:id="@+id/txtvFF" @@ -246,8 +236,7 @@ android:scaleType="fitCenter" app:srcCompat="?attr/av_skip" android:contentDescription="@string/skip_episode_label" - tools:srcCompat="@drawable/ic_av_skip_white_48dp" - tools:background="@android:color/holo_green_dark"/> + tools:srcCompat="@drawable/ic_av_skip_white_48dp"/> </RelativeLayout> </LinearLayout> diff --git a/app/src/main/res/layout/cover_fragment.xml b/app/src/main/res/layout/cover_fragment.xml index 31e7dbda7..28321e760 100644 --- a/app/src/main/res/layout/cover_fragment.xml +++ b/app/src/main/res/layout/cover_fragment.xml @@ -13,13 +13,13 @@ android:layout_width="match_parent" android:layout_height="0dp" android:layout_weight="1" - android:contentDescription="@string/cover_label" android:scaleType="fitCenter" android:layout_marginLeft="32dp" android:layout_marginRight="32dp" android:transitionName="coverTransition" tools:src="@android:drawable/sym_def_app_icon" android:foreground="?attr/selectableItemBackgroundBorderless" + android:importantForAccessibility="no" squareImageView:direction="minimum" /> <TextView diff --git a/app/src/main/res/layout/download_authentication_activity.xml b/app/src/main/res/layout/download_authentication_activity.xml index 58c2c10e6..e16a8b3a8 100644 --- a/app/src/main/res/layout/download_authentication_activity.xml +++ b/app/src/main/res/layout/download_authentication_activity.xml @@ -50,21 +50,15 @@ android:id="@+id/butCancel" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:background="?android:attr/selectableItemBackground" - android:textColor="?attr/colorAccent" - android:paddingLeft="8dp" - android:paddingRight="8dp" - android:text="@string/cancel_label"/> + android:text="@string/cancel_label" + style="@style/Widget.MaterialComponents.Button.TextButton"/> <Button android:id="@+id/butConfirm" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:background="?android:attr/selectableItemBackground" - android:textColor="?attr/colorAccent" - android:paddingLeft="8dp" - android:paddingRight="8dp" - android:text="@string/confirm_label"/> + android:text="@string/confirm_label" + style="@style/Widget.MaterialComponents.Button.TextButton"/> </LinearLayout> </LinearLayout>
\ No newline at end of file diff --git a/app/src/main/res/layout/episodes_apply_action_fragment.xml b/app/src/main/res/layout/episodes_apply_action_fragment.xml index 0baa2061a..304588e3e 100644 --- a/app/src/main/res/layout/episodes_apply_action_fragment.xml +++ b/app/src/main/res/layout/episodes_apply_action_fragment.xml @@ -24,6 +24,7 @@ android:id="@+id/fabSDOverlay" android:layout_width="match_parent" android:layout_height="match_parent" + android:importantForAccessibility="no" android:layout_below="@id/toolbar" /> <!-- The FAB SpeedDial 1. MUST be placed at the bottom of the layout xml to ensure it is at the front, @@ -51,7 +52,8 @@ android:layout_marginEnd="16dp" android:layout_marginRight="16dp" android:layout_marginBottom="16dp" - /> + android:accessibilityTraversalBefore="@android:id/list" + android:contentDescription="@string/apply_action" /> </ScrollView> </RelativeLayout> diff --git a/app/src/main/res/layout/external_player_fragment.xml b/app/src/main/res/layout/external_player_fragment.xml index dd8f7fe40..f36baec26 100644 --- a/app/src/main/res/layout/external_player_fragment.xml +++ b/app/src/main/res/layout/external_player_fragment.xml @@ -9,11 +9,6 @@ android:background="?attr/selectableItemBackground" android:orientation="vertical"> - <View - android:layout_width="match_parent" - android:layout_height="1dp" - android:background="?android:attr/dividerHorizontal" /> - <LinearLayout android:layout_width="match_parent" android:layout_height="0dp" @@ -22,13 +17,14 @@ <ImageView android:id="@+id/imgvCover" - android:contentDescription="@string/cover_label" + android:contentDescription="@string/media_player" android:layout_width="wrap_content" android:layout_height="match_parent" android:adjustViewBounds="true" android:cropToPadding="true" android:maxWidth="96dp" android:scaleType="fitCenter" + android:background="@color/non_square_icon_background" tools:src="@tools:sample/avatars"/> diff --git a/app/src/main/res/layout/feeditem_fragment.xml b/app/src/main/res/layout/feeditem_fragment.xml index 8dfbbe562..72effc585 100644 --- a/app/src/main/res/layout/feeditem_fragment.xml +++ b/app/src/main/res/layout/feeditem_fragment.xml @@ -25,7 +25,7 @@ android:layout_width="@dimen/thumbnail_length_queue_item" android:layout_height="@dimen/thumbnail_length_queue_item" android:layout_gravity="center_vertical" - android:contentDescription="@string/cover_label" + android:contentDescription="@string/open_podcast" android:foreground="?attr/selectableItemBackground" tools:src="@tools:sample/avatars" /> @@ -42,6 +42,7 @@ android:layout_width="wrap_content" android:layout_height="wrap_content" android:foreground="?attr/selectableItemBackground" + android:importantForAccessibility="no" tools:text="Podcast title" tools:background="@android:color/holo_green_dark" /> @@ -77,6 +78,7 @@ android:layout_marginStart="4dp" android:layout_marginRight="4dp" android:layout_marginEnd="4dp" + android:importantForAccessibility="no" android:text="ยท" tools:background="@android:color/holo_blue_light" /> diff --git a/app/src/main/res/layout/feeditemlist_header.xml b/app/src/main/res/layout/feeditemlist_header.xml index 14ba6c3d0..e74aeac0a 100644 --- a/app/src/main/res/layout/feeditemlist_header.xml +++ b/app/src/main/res/layout/feeditemlist_header.xml @@ -21,11 +21,10 @@ android:layout_height="100dp" android:layout_marginRight="16dp" android:layout_marginEnd="16dp" - android:contentDescription="@string/cover_label" + android:importantForAccessibility="no" tools:src="@drawable/ic_antenna" tools:background="@android:color/holo_green_dark"/> - <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" @@ -98,7 +97,7 @@ android:layout_width="match_parent" android:layout_height="wrap_content" android:padding="2dp" - android:background="?attr/colorPrimary" + android:background="?android:attr/windowBackground" android:visibility="gone" android:gravity="center" tools:visibility="visible" diff --git a/app/src/main/res/layout/feeditemlist_item.xml b/app/src/main/res/layout/feeditemlist_item.xml index 5a7ba4754..90062bbf6 100644 --- a/app/src/main/res/layout/feeditemlist_item.xml +++ b/app/src/main/res/layout/feeditemlist_item.xml @@ -17,12 +17,13 @@ <LinearLayout android:layout_width="wrap_content" android:layout_height="match_parent" + android:id="@+id/left_padding" android:minWidth="4dp"> <ImageView android:id="@+id/drag_handle" android:layout_width="16dp" android:layout_height="match_parent" - android:contentDescription="@string/drag_handle_content_description" + android:importantForAccessibility="no" android:scaleType="fitCenter" android:src="?attr/dragview_background" android:paddingStart="0dp" @@ -65,7 +66,7 @@ android:layout_width="@dimen/thumbnail_length_queue_item" android:layout_height="@dimen/thumbnail_length_queue_item" android:layout_centerVertical="true" - android:contentDescription="@string/cover_label" + android:importantForAccessibility="no" tools:src="@tools:sample/avatars"/> </RelativeLayout> @@ -104,6 +105,7 @@ android:layout_height="14sp" app:srcCompat="?attr/type_video" tools:srcCompat="@drawable/ic_videocam_black_24dp" + android:contentDescription="@string/media_type_video_label" android:id="@+id/ivIsVideo"/> <ImageView @@ -111,6 +113,7 @@ android:layout_height="14sp" app:srcCompat="?attr/ic_unfav" tools:srcCompat="@drawable/ic_star_black" + android:contentDescription="@string/is_favorite_label" android:id="@+id/isFavorite"/> <ImageView @@ -118,6 +121,7 @@ android:layout_height="14sp" app:srcCompat="?attr/stat_playlist" tools:srcCompat="@drawable/ic_playlist_black" + android:contentDescription="@string/in_queue_label" android:id="@+id/ivInPlaylist"/> <TextView @@ -160,12 +164,19 @@ tools:text="10 MB"/> </LinearLayout> + + <!-- + Warning: android:contentDescription is set to an empty string. + The title is read as contentDescription of left_padding to have it read first. + Keep this in mind when changing the order of this layout! + --> <TextView android:id="@+id/txtvTitle" style="@style/AntennaPod.TextView.ListItemPrimaryTitle" android:layout_width="match_parent" android:layout_height="wrap_content" tools:text="@sample/episodes.json/data/title" + android:importantForAccessibility="no" android:ellipsize="end" tools:background="@android:color/holo_blue_light"/> diff --git a/app/src/main/res/layout/gpodnet_podcast_listitem.xml b/app/src/main/res/layout/gpodnet_podcast_listitem.xml index 4e05f5599..9821f6e17 100644 --- a/app/src/main/res/layout/gpodnet_podcast_listitem.xml +++ b/app/src/main/res/layout/gpodnet_podcast_listitem.xml @@ -20,7 +20,7 @@ android:layout_marginRight="16dp" android:layout_marginEnd="16dp" android:adjustViewBounds="true" - android:contentDescription="@string/cover_label" + android:importantForAccessibility="no" android:cropToPadding="true" android:scaleType="fitXY" tools:src="@drawable/ic_antenna" diff --git a/app/src/main/res/layout/itunes_podcast_listitem.xml b/app/src/main/res/layout/itunes_podcast_listitem.xml index 87d71720a..dcf2face6 100644 --- a/app/src/main/res/layout/itunes_podcast_listitem.xml +++ b/app/src/main/res/layout/itunes_podcast_listitem.xml @@ -19,7 +19,7 @@ android:layout_alignParentStart="true" android:layout_alignParentTop="true" android:adjustViewBounds="true" - android:contentDescription="@string/cover_label" + android:importantForAccessibility="no" android:cropToPadding="true" android:scaleType="fitXY" tools:background="@android:color/holo_green_dark" diff --git a/app/src/main/res/layout/nav_list.xml b/app/src/main/res/layout/nav_list.xml index 504952b08..c26d0b8ed 100644 --- a/app/src/main/res/layout/nav_list.xml +++ b/app/src/main/res/layout/nav_list.xml @@ -13,6 +13,7 @@ android:layout_alignParentBottom="true" android:background="?attr/selectableItemBackground" android:contentDescription="@string/settings_label" + android:accessibilityTraversalBefore="@id/nav_list" android:orientation="horizontal" android:focusable="true"> @@ -25,7 +26,7 @@ android:layout_marginStart="@dimen/listitem_icon_leftpadding" android:layout_marginTop="4dp" android:adjustViewBounds="true" - android:contentDescription="@string/cover_label" + android:importantForAccessibility="no" android:cropToPadding="true" android:padding="8dp" android:scaleType="centerCrop" diff --git a/app/src/main/res/layout/nav_listitem.xml b/app/src/main/res/layout/nav_listitem.xml index 2b7bc5715..3610ce56e 100644 --- a/app/src/main/res/layout/nav_listitem.xml +++ b/app/src/main/res/layout/nav_listitem.xml @@ -9,7 +9,7 @@ <ImageView android:id="@+id/imgvCover" - android:contentDescription="@string/cover_label" + android:importantForAccessibility="no" android:layout_width="@dimen/thumbnail_length_navlist" android:layout_height="@dimen/thumbnail_length_navlist" android:layout_alignParentLeft="true" diff --git a/app/src/main/res/layout/nav_section_item.xml b/app/src/main/res/layout/nav_section_item.xml index fa1db865d..45d0dff59 100644 --- a/app/src/main/res/layout/nav_section_item.xml +++ b/app/src/main/res/layout/nav_section_item.xml @@ -1,11 +1,11 @@ <?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="16dp" android:background="@android:color/transparent" - android:orientation="vertical"> + android:orientation="vertical" + android:importantForAccessibility="no"> <View android:layout_width="match_parent" diff --git a/app/src/main/res/layout/onlinefeedview_activity.xml b/app/src/main/res/layout/onlinefeedview_activity.xml index 34dbeb6d9..cddb1048c 100644 --- a/app/src/main/res/layout/onlinefeedview_activity.xml +++ b/app/src/main/res/layout/onlinefeedview_activity.xml @@ -56,7 +56,7 @@ android:layout_marginLeft="16dp" android:layout_marginStart="16dp" android:layout_marginTop="16dp" - android:contentDescription="@string/cover_label" + android:importantForAccessibility="no" tools:src="@drawable/ic_antenna"/> <TextView diff --git a/app/src/main/res/layout/opml_selection.xml b/app/src/main/res/layout/opml_selection.xml index e018ffde3..1f1d72d76 100644 --- a/app/src/main/res/layout/opml_selection.xml +++ b/app/src/main/res/layout/opml_selection.xml @@ -1,67 +1,39 @@ <?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="match_parent" > - - <RelativeLayout - android:id="@+id/footer" - android:layout_width="fill_parent" - android:layout_height="48dp" - android:layout_alignParentBottom="true" > - - <View - android:layout_width="match_parent" - android:layout_height="1dip" - android:layout_alignParentTop="true" - android:background="?android:attr/dividerVertical" /> - - <View - android:id="@+id/horizontal_divider" - android:layout_width="1dip" - android:layout_height="fill_parent" - android:layout_alignParentTop="true" - android:layout_centerHorizontal="true" - android:layout_marginBottom="4dp" - android:layout_marginTop="4dp" - android:background="?android:attr/dividerVertical" /> +<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="match_parent"> - <Button - android:id="@+id/butCancel" + <Button + android:id="@+id/butConfirm" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" - android:layout_alignParentLeft="true" - android:layout_alignParentStart="true" - android:layout_alignParentTop="true" - android:layout_toLeftOf="@id/horizontal_divider" - android:layout_toStartOf="@id/horizontal_divider" - android:background="?android:attr/selectableItemBackground" - android:textColor="?android:attr/textColorPrimary" - android:text="@string/cancel_label" /> + android:layout_alignParentRight="true" + android:layout_alignParentEnd="true" + style="@style/Widget.MaterialComponents.Button.TextButton" + android:layout_margin="8dp" + android:text="@string/confirm_label"/> - <Button - android:id="@+id/butConfirm" + <Button + android:id="@+id/butCancel" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentBottom="true" - android:layout_alignParentRight="true" - android:layout_alignParentEnd="true" - android:layout_alignParentTop="true" - android:layout_toRightOf="@id/horizontal_divider" - android:layout_toEndOf="@id/horizontal_divider" - android:background="?android:attr/selectableItemBackground" - android:textColor="?android:attr/textColorPrimary" - android:text="@string/confirm_label" /> - </RelativeLayout> + android:layout_toLeftOf="@+id/butConfirm" + android:layout_toStartOf="@+id/butConfirm" + style="@style/Widget.MaterialComponents.Button.TextButton" + android:layout_margin="8dp" + android:text="@string/cancel_label"/> <ListView - android:id="@+id/feedlist" - android:layout_width="match_parent" - android:layout_height="0dp" - android:layout_above="@id/footer" - android:layout_alignParentTop="true" - tools:listitem="@android:layout/simple_list_item_multiple_choice" > + android:id="@+id/feedlist" + android:layout_width="match_parent" + android:layout_height="0dp" + android:layout_above="@id/butConfirm" + android:layout_alignParentTop="true" + tools:listitem="@android:layout/simple_list_item_multiple_choice"> </ListView> -</RelativeLayout>
\ No newline at end of file +</RelativeLayout> diff --git a/app/src/main/res/layout/searchlist_item.xml b/app/src/main/res/layout/searchlist_item.xml index e8ff18c5f..608bfc3bc 100644 --- a/app/src/main/res/layout/searchlist_item.xml +++ b/app/src/main/res/layout/searchlist_item.xml @@ -16,7 +16,7 @@ android:layout_centerVertical="true" android:layout_marginLeft="@dimen/listitem_threeline_horizontalpadding" android:layout_marginStart="@dimen/listitem_threeline_horizontalpadding" - android:contentDescription="@string/cover_label" + android:importantForAccessibility="no" android:scaleType="centerCrop" tools:src="@drawable/ic_antenna" tools:background="@android:color/holo_green_dark"/> diff --git a/app/src/main/res/layout/simplechapter_item.xml b/app/src/main/res/layout/simplechapter_item.xml index 54c607d71..276ce48bc 100644 --- a/app/src/main/res/layout/simplechapter_item.xml +++ b/app/src/main/res/layout/simplechapter_item.xml @@ -14,7 +14,7 @@ android:id="@+id/imgvCover" android:layout_width="@dimen/thumbnail_length_queue_item" android:layout_height="@dimen/thumbnail_length_queue_item" - android:contentDescription="@string/cover_label" + android:importantForAccessibility="no" android:layout_marginLeft="16dp" android:layout_marginStart="16dp" tools:src="@tools:sample/avatars"/> diff --git a/app/src/main/res/layout/statistics_listitem.xml b/app/src/main/res/layout/statistics_listitem.xml index aaa0f666c..c41ace58b 100644 --- a/app/src/main/res/layout/statistics_listitem.xml +++ b/app/src/main/res/layout/statistics_listitem.xml @@ -12,7 +12,7 @@ <ImageView android:id="@+id/imgvCover" - android:contentDescription="@string/cover_label" + android:importantForAccessibility="no" android:layout_width="40dp" android:layout_height="40dp" android:layout_alignParentLeft="true" @@ -20,7 +20,7 @@ android:layout_centerVertical="true" android:adjustViewBounds="true" android:cropToPadding="true" - android:scaleType="centerCrop" + android:scaleType="fitCenter" tools:src="@drawable/ic_antenna" tools:background="@android:color/holo_green_dark"/> diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSRSS20.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSRSS20.java index e391af1b2..45c5d4884 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSRSS20.java +++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/namespace/NSRSS20.java @@ -3,6 +3,7 @@ package de.danoeh.antennapod.core.syndication.namespace; import android.text.TextUtils; import android.util.Log; +import de.danoeh.antennapod.core.syndication.util.SyndStringUtils; import org.xml.sax.Attributes; import de.danoeh.antennapod.core.feed.FeedItem; @@ -13,16 +14,16 @@ import de.danoeh.antennapod.core.util.DateUtils; /** * SAX-Parser for reading RSS-Feeds - * + * * @author daniel - * + * */ public class NSRSS20 extends Namespace { private static final String TAG = "NSRSS20"; public static final String CHANNEL = "channel"; - public static final String ITEM = "item"; + public static final String ITEM = "item"; private static final String GUID = "guid"; private static final String TITLE = "title"; private static final String LINK = "link"; @@ -30,118 +31,119 @@ public class NSRSS20 extends Namespace { private static final String PUBDATE = "pubDate"; private static final String ENCLOSURE = "enclosure"; private static final String IMAGE = "image"; - private static final String URL = "url"; + private static final String URL = "url"; private static final String LANGUAGE = "language"; private static final String ENC_URL = "url"; private static final String ENC_LEN = "length"; private static final String ENC_TYPE = "type"; - @Override - public SyndElement handleElementStart(String localName, HandlerState state, - Attributes attributes) { - if (ITEM.equals(localName)) { - state.setCurrentItem(new FeedItem()); - state.getItems().add(state.getCurrentItem()); - state.getCurrentItem().setFeed(state.getFeed()); + @Override + public SyndElement handleElementStart(String localName, HandlerState state, + Attributes attributes) { + if (ITEM.equals(localName)) { + state.setCurrentItem(new FeedItem()); + state.getItems().add(state.getCurrentItem()); + state.getCurrentItem().setFeed(state.getFeed()); - } else if (ENCLOSURE.equals(localName)) { - String type = attributes.getValue(ENC_TYPE); - String url = attributes.getValue(ENC_URL); + } else if (ENCLOSURE.equals(localName)) { + String type = attributes.getValue(ENC_TYPE); + String url = attributes.getValue(ENC_URL); - boolean validType = SyndTypeUtils.enclosureTypeValid(type); - if(!validType) { + boolean validType = SyndTypeUtils.enclosureTypeValid(type); + if (!validType) { type = SyndTypeUtils.getMimeTypeFromUrl(url); validType = SyndTypeUtils.enclosureTypeValid(type); } boolean validUrl = !TextUtils.isEmpty(url); - if (state.getCurrentItem() != null && state.getCurrentItem().getMedia() == null && - validType && validUrl) { - long size = 0; - try { - size = Long.parseLong(attributes.getValue(ENC_LEN)); - if(size < 16384) { - // less than 16kb is suspicious, check manually - size = 0; - } - } catch (NumberFormatException e) { - Log.d(TAG, "Length attribute could not be parsed."); - } - FeedMedia media = new FeedMedia(state.getCurrentItem(), url, size, type); - state.getCurrentItem().setMedia(media); - } + if (state.getCurrentItem() != null && state.getCurrentItem().getMedia() == null + && validType && validUrl) { + long size = 0; + try { + size = Long.parseLong(attributes.getValue(ENC_LEN)); + if (size < 16384) { + // less than 16kb is suspicious, check manually + size = 0; + } + } catch (NumberFormatException e) { + Log.d(TAG, "Length attribute could not be parsed."); + } + FeedMedia media = new FeedMedia(state.getCurrentItem(), url, size, type); + state.getCurrentItem().setMedia(media); + } - } - return new SyndElement(localName, this); - } + } + return new SyndElement(localName, this); + } - @Override - public void handleElementEnd(String localName, HandlerState state) { - if (ITEM.equals(localName)) { - if (state.getCurrentItem() != null) { - FeedItem currentItem = state.getCurrentItem(); - // the title tag is optional in RSS 2.0. The description is used - // as a - // title if the item has no title-tag. - if (currentItem.getTitle() == null) { - currentItem.setTitle(currentItem.getDescription()); - } + @Override + public void handleElementEnd(String localName, HandlerState state) { + if (ITEM.equals(localName)) { + if (state.getCurrentItem() != null) { + FeedItem currentItem = state.getCurrentItem(); + // the title tag is optional in RSS 2.0. The description is used + // as a + // title if the item has no title-tag. + if (currentItem.getTitle() == null) { + currentItem.setTitle(currentItem.getDescription()); + } if (state.getTempObjects().containsKey(NSITunes.DURATION)) { if (currentItem.hasMedia()) { - Integer duration = (Integer) state.getTempObjects().get(NSITunes.DURATION); - currentItem.getMedia().setDuration(duration); + Integer duration = (Integer) state.getTempObjects().get(NSITunes.DURATION); + currentItem.getMedia().setDuration(duration); } state.getTempObjects().remove(NSITunes.DURATION); } - } - state.setCurrentItem(null); - } else if (state.getTagstack().size() >= 2 && state.getContentBuf() != null) { - String content = state.getContentBuf().toString(); - SyndElement topElement = state.getTagstack().peek(); - String top = topElement.getName(); - SyndElement secondElement = state.getSecondTag(); - String second = secondElement.getName(); - String third = null; - if (state.getTagstack().size() >= 3) { - third = state.getThirdTag().getName(); - } - if (GUID.equals(top) && ITEM.equals(second)) { - // some feed creators include an empty or non-standard guid-element in their feed, which should be ignored - if (!TextUtils.isEmpty(content) && state.getCurrentItem() != null) { - state.getCurrentItem().setItemIdentifier(content); + } + state.setCurrentItem(null); + } else if (state.getTagstack().size() >= 2 && state.getContentBuf() != null) { + String contentRaw = state.getContentBuf().toString(); + String content = SyndStringUtils.trimAllWhitespace(contentRaw); + SyndElement topElement = state.getTagstack().peek(); + String top = topElement.getName(); + SyndElement secondElement = state.getSecondTag(); + String second = secondElement.getName(); + String third = null; + if (state.getTagstack().size() >= 3) { + third = state.getThirdTag().getName(); + } + if (GUID.equals(top) && ITEM.equals(second)) { + // some feed creators include an empty or non-standard guid-element in their feed, + // which should be ignored + if (!TextUtils.isEmpty(contentRaw) && state.getCurrentItem() != null) { + state.getCurrentItem().setItemIdentifier(contentRaw); } - } else if (TITLE.equals(top)) { - String title = content.trim(); - if (ITEM.equals(second) && state.getCurrentItem() != null) { - state.getCurrentItem().setTitle(title); - } else if (CHANNEL.equals(second) && state.getFeed() != null) { - state.getFeed().setTitle(title); - } - } else if (LINK.equals(top)) { - if (CHANNEL.equals(second) && state.getFeed() != null) { - state.getFeed().setLink(content); - } else if (ITEM.equals(second) && state.getCurrentItem() != null) { - state.getCurrentItem().setLink(content); - } - } else if (PUBDATE.equals(top) && ITEM.equals(second) && state.getCurrentItem() != null) { - state.getCurrentItem().setPubDate(DateUtils.parse(content)); - } else if (URL.equals(top) && IMAGE.equals(second) && CHANNEL.equals(third)) { - // prefer itunes:image - if (state.getFeed() != null) { - state.getFeed().setImageUrl(content); - } - } else if (DESCR.equals(localName)) { - if (CHANNEL.equals(second) && state.getFeed() != null) { - state.getFeed().setDescription(content); - } else if (ITEM.equals(second) && state.getCurrentItem() != null) { - state.getCurrentItem().setDescription(content); - } - } else if (LANGUAGE.equals(localName) && state.getFeed() != null) { - state.getFeed().setLanguage(content.toLowerCase()); - } - } - } + } else if (TITLE.equals(top)) { + if (ITEM.equals(second) && state.getCurrentItem() != null) { + state.getCurrentItem().setTitle(content); + } else if (CHANNEL.equals(second) && state.getFeed() != null) { + state.getFeed().setTitle(content); + } + } else if (LINK.equals(top)) { + if (CHANNEL.equals(second) && state.getFeed() != null) { + state.getFeed().setLink(content); + } else if (ITEM.equals(second) && state.getCurrentItem() != null) { + state.getCurrentItem().setLink(content); + } + } else if (PUBDATE.equals(top) && ITEM.equals(second) && state.getCurrentItem() != null) { + state.getCurrentItem().setPubDate(DateUtils.parse(content)); + } else if (URL.equals(top) && IMAGE.equals(second) && CHANNEL.equals(third)) { + // prefer itunes:image + if (state.getFeed() != null && state.getFeed().getImageUrl() == null) { + state.getFeed().setImageUrl(content); + } + } else if (DESCR.equals(localName)) { + if (CHANNEL.equals(second) && state.getFeed() != null) { + state.getFeed().setDescription(content); + } else if (ITEM.equals(second) && state.getCurrentItem() != null) { + state.getCurrentItem().setDescription(content); + } + } else if (LANGUAGE.equals(localName) && state.getFeed() != null) { + state.getFeed().setLanguage(content.toLowerCase()); + } + } + } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/syndication/util/SyndStringUtils.java b/core/src/main/java/de/danoeh/antennapod/core/syndication/util/SyndStringUtils.java new file mode 100644 index 000000000..addcdd4b7 --- /dev/null +++ b/core/src/main/java/de/danoeh/antennapod/core/syndication/util/SyndStringUtils.java @@ -0,0 +1,14 @@ +package de.danoeh.antennapod.core.syndication.util; + +public class SyndStringUtils { + private SyndStringUtils() { + + } + + /** + * Trims all whitespace from beginning and ending of a String. {{@link String#trim()}} only trims spaces. + */ + public static String trimAllWhitespace(String string) { + return string.replaceAll("(^\\s*)|(\\s*$)", ""); + } +} diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/Converter.java b/core/src/main/java/de/danoeh/antennapod/core/util/Converter.java index 4e7e6a6b6..e1e2818cb 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/Converter.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/Converter.java @@ -14,47 +14,57 @@ public final class Converter { /** Logging tag. */ private static final String TAG = "Converter"; - + private static final int HOURS_MIL = 3600000; - private static final int MINUTES_MIL = 60000; - private static final int SECONDS_MIL = 1000; - - /** Converts milliseconds to a string containing hours, minutes and seconds */ - public static String getDurationStringLong(int duration) { - int h = duration / HOURS_MIL; - int rest = duration - h * HOURS_MIL; - int m = rest / MINUTES_MIL; - rest -= m * MINUTES_MIL; - int s = rest / SECONDS_MIL; - - return String.format(Locale.getDefault(), "%02d:%02d:%02d", h, m, s); + private static final int MINUTES_MIL = 60000; + private static final int SECONDS_MIL = 1000; + + /** + * Converts milliseconds to a string containing hours, minutes and seconds. + */ + public static String getDurationStringLong(int duration) { + int[] hms = millisecondsToHms(duration); + return String.format(Locale.getDefault(), "%02d:%02d:%02d", hms[0], hms[1], hms[2]); } - - /** Converts milliseconds to a string containing hours and minutes or minutes and seconds*/ + + private static int[] millisecondsToHms(long duration) { + int h = (int) (duration / HOURS_MIL); + long rest = duration - h * HOURS_MIL; + int m = (int) (rest / MINUTES_MIL); + rest -= m * MINUTES_MIL; + int s = (int) (rest / SECONDS_MIL); + return new int[] {h, m, s}; + } + + /** + * Converts milliseconds to a string containing hours and minutes or minutes and seconds. + */ public static String getDurationStringShort(int duration, boolean durationIsInHours) { int firstPartBase = durationIsInHours ? HOURS_MIL : MINUTES_MIL; int firstPart = duration / firstPartBase; int leftoverFromFirstPart = duration - firstPart * firstPartBase; int secondPart = leftoverFromFirstPart / (durationIsInHours ? MINUTES_MIL : SECONDS_MIL); - return String.format(Locale.getDefault(), "%02d:%02d", firstPart, secondPart); + return String.format(Locale.getDefault(), "%02d:%02d", firstPart, secondPart); } - /** Converts long duration string (HH:MM:SS) to milliseconds. */ + /** + * Converts long duration string (HH:MM:SS) to milliseconds. + */ public static int durationStringLongToMs(String input) { String[] parts = input.split(":"); if (parts.length != 3) { return 0; } - return Integer.parseInt(parts[0]) * 3600 * 1000 + - Integer.parseInt(parts[1]) * 60 * 1000 + - Integer.parseInt(parts[2]) * 1000; + return Integer.parseInt(parts[0]) * 3600 * 1000 + + Integer.parseInt(parts[1]) * 60 * 1000 + + Integer.parseInt(parts[2]) * 1000; } /** * Converts short duration string (XX:YY) to milliseconds. If durationIsInHours is true then the * format is HH:MM, otherwise it's MM:SS. - * */ + */ public static int durationStringShortToMs(String input, boolean durationIsInHours) { String[] parts = input.split(":"); if (parts.length != 2) { @@ -63,18 +73,20 @@ public final class Converter { int modifier = durationIsInHours ? 60 : 1; - return Integer.parseInt(parts[0]) * 60 * 1000 * modifier+ - Integer.parseInt(parts[1]) * 1000 * modifier; + return Integer.parseInt(parts[0]) * 60 * 1000 * modifier + + Integer.parseInt(parts[1]) * 1000 * modifier; } - /** Converts milliseconds to a localized string containing hours and minutes */ + /** + * Converts milliseconds to a localized string containing hours and minutes. + */ public static String getDurationStringLocalized(Context context, long duration) { - int h = (int)(duration / HOURS_MIL); - int rest = (int)(duration - h * HOURS_MIL); + int h = (int) (duration / HOURS_MIL); + int rest = (int) (duration - h * HOURS_MIL); int m = rest / MINUTES_MIL; String result = ""; - if(h > 0) { + if (h > 0) { String hours = context.getResources().getQuantityString(R.plurals.time_hours_quantified, h, h); result += hours + " "; } @@ -84,7 +96,7 @@ public final class Converter { } /** - * Converts seconds to a localized representation + * Converts seconds to a localized representation. * @param time The time in seconds * @return "HH:MM hours" */ @@ -93,16 +105,16 @@ public final class Converter { return String.format(Locale.getDefault(), "%.1f ", hours) + context.getString(R.string.time_hours); } - /** * Converts the volume as read as the progress from a SeekBar scaled to 100 and as saved in * UserPreferences to the format taken by setVolume methods. * @param progress integer between 0 to 100 taken from the SeekBar progress * @return the appropriate volume as float taken by setVolume methods */ - public static float getVolumeFromPercentage(int progress){ - if (progress==100) + public static float getVolumeFromPercentage(int progress) { + if (progress == 100) { return 1f; + } return (float) (1 - (Math.log(101 - progress) / Math.log(101))); } } diff --git a/core/src/main/java/de/danoeh/antennapod/core/util/DateUtils.java b/core/src/main/java/de/danoeh/antennapod/core/util/DateUtils.java index 2454b6a00..e15ab2fdc 100644 --- a/core/src/main/java/de/danoeh/antennapod/core/util/DateUtils.java +++ b/core/src/main/java/de/danoeh/antennapod/core/util/DateUtils.java @@ -5,6 +5,7 @@ import android.util.Log; import org.apache.commons.lang3.StringUtils; +import java.text.DateFormat; import java.text.ParsePosition; import java.text.SimpleDateFormat; import java.util.Calendar; @@ -175,4 +176,11 @@ public class DateUtils { } return android.text.format.DateUtils.formatDateTime(context, date.getTime(), format); } + + public static String formatForAccessibility(final Context context, final Date date) { + if (date == null) { + return ""; + } + return DateFormat.getDateInstance(DateFormat.LONG).format(date); + } } diff --git a/core/src/main/res/values/attrs.xml b/core/src/main/res/values/attrs.xml index f8b545ac0..2d8cbb4cb 100644 --- a/core/src/main/res/values/attrs.xml +++ b/core/src/main/res/values/attrs.xml @@ -56,6 +56,7 @@ <attr name="batch_edit_fab_icon" format="reference"/> <attr name="action_icon_color" format="color"/> <attr name="scrollbar_thumb" format="reference"/> + <attr name="background_elevated" format="color"/> <declare-styleable name="SquareImageView"> <attr name="direction" format="enum"> diff --git a/core/src/main/res/values/colors.xml b/core/src/main/res/values/colors.xml index cc04db2dd..a86d61eba 100644 --- a/core/src/main/res/values/colors.xml +++ b/core/src/main/res/values/colors.xml @@ -13,14 +13,16 @@ <!-- Theme colors --> <color name="background_light">#FFFFFF</color> - <color name="background_darktheme">#303030</color> - <color name="highlight_light">#DDDDDD</color> - <color name="highlight_dark">#414141</color> - <color name="highlight_trueblack">#414141</color> + <color name="background_elevated_light">#EFEEEE</color> + <color name="background_darktheme">#21272b</color> + <color name="background_elevated_darktheme">#2D3337</color> + <color name="highlight_light">#46C6C6C6</color> + <color name="highlight_dark">#43707070</color> + <color name="highlight_trueblack">#43707070</color> <color name="non_square_icon_background">#22777777</color> <color name="accent_light">#0078C2</color> - <color name="accent_dark">#5C9DFF</color> + <color name="accent_dark">#3D8BFF</color> <color name="ic_launcher_background">#008AB8</color> <color name="master_switch_background_light">#DDDDDD</color> diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml index 1c2db3a5b..0168aec38 100644 --- a/core/src/main/res/values/strings.xml +++ b/core/src/main/res/values/strings.xml @@ -217,6 +217,7 @@ <string name="deactivate_auto_download">Deactivate Auto Download</string> <string name="reset_position">Reset Playback Position</string> <string name="removed_item">Item removed</string> + <string name="no_items_selected">No items selected</string> <!-- Download messages and labels --> <string name="download_successful">successful</string> @@ -671,13 +672,19 @@ <!-- Content descriptions for image buttons --> <string name="rewind_label">Rewind</string> <string name="fast_forward_label">Fast forward</string> + <string name="increase_volume">Increase volume</string> + <string name="decrease_volume">Decrease volume</string> <string name="media_type_audio_label">Audio</string> <string name="media_type_video_label">Video</string> <string name="navigate_upwards_label">Navigate upwards</string> <string name="status_downloading_label">Episode is being downloaded</string> <string name="in_queue_label">Episode is in the queue</string> + <string name="is_favorite_label">Episode is marked as favorite</string> <string name="drag_handle_content_description">Drag to change the position of this item</string> <string name="load_next_page_label">Load next page</string> + <string name="switch_pages">Switch pages</string> + <string name="position">Position: %1$s</string> + <string name="apply_action">Apply action</string> <!-- Feed information screen --> <string name="authentication_label">Authentication</string> diff --git a/core/src/main/res/values/styles.xml b/core/src/main/res/values/styles.xml index 2b7be1713..c72cd3e53 100644 --- a/core/src/main/res/values/styles.xml +++ b/core/src/main/res/values/styles.xml @@ -14,6 +14,7 @@ <item name="colorPrimaryDark">@color/accent_light</item> <item name="android:windowBackground">@color/background_light</item> <item name="actionBarStyle">@style/Widget.AntennaPod.ActionBar.Light</item> + <item name="background_elevated">@color/background_elevated_light</item> <item name="master_switch_background">@color/master_switch_background_light</item> <item name="currently_playing_background">@color/highlight_light</item> <item name="action_icon_color">@color/black</item> @@ -85,6 +86,7 @@ <item name="colorPrimaryDark">@color/background_darktheme</item> <item name="android:windowBackground">@color/background_darktheme</item> <item name="actionBarStyle">@style/Widget.AntennaPod.ActionBar.Dark</item> + <item name="background_elevated">@color/background_elevated_darktheme</item> <item name="colorControlNormal">@color/white</item> <item name="progressBarTheme">@style/ProgressBarDark</item> <item name="drawer_activated_color">@color/highlight_dark</item> @@ -159,6 +161,7 @@ <item name="android:colorBackground">@color/black</item> <item name="android:windowBackground">@color/black</item> <item name="android:actionBarStyle">@color/black</item> + <item name="background_elevated">@color/black</item> </style> <style name="Theme.AntennaPod.Light.NoTitle" parent="Theme.AntennaPod.Light"> |