From 899c9cdf219d59d299164c37b67f7423f8922d35 Mon Sep 17 00:00:00 2001 From: sk Date: Thu, 5 May 2022 19:28:27 +0200 Subject: [PATCH 1/5] implement alt text as toast messages --- .../joinmastodon/android/ui/photoviewer/PhotoViewer.java | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/photoviewer/PhotoViewer.java b/mastodon/src/main/java/org/joinmastodon/android/ui/photoviewer/PhotoViewer.java index d9c0c8334..73abcda10 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/photoviewer/PhotoViewer.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/photoviewer/PhotoViewer.java @@ -39,6 +39,7 @@ import android.view.WindowManager; import android.widget.FrameLayout; import android.widget.ImageButton; import android.widget.ImageView; +import android.widget.PopupWindow; import android.widget.ProgressBar; import android.widget.SeekBar; import android.widget.TextView; @@ -54,6 +55,7 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.OutputStream; +import java.time.Duration; import java.util.ArrayList; import java.util.List; import java.util.Locale; @@ -697,6 +699,10 @@ public class PhotoViewer implements ZoomPanView.Listener{ @Override public void onBind(Attachment item){ zoomPanView.setScrollDirections(getAbsoluteAdapterPosition()>0, getAbsoluteAdapterPosition() { + Toast.makeText(activity, item.description, Toast.LENGTH_LONG).show(); + return true; + }); } } From 11d88aed27870542c55296e41bc87f3c5ad952ab Mon Sep 17 00:00:00 2001 From: sk Date: Thu, 5 May 2022 23:26:48 +0200 Subject: [PATCH 2/5] implement alt text as bottom sheet --- .../android/ui/ImageDescriptionSheet.java | 78 +++++++++++++++++++ .../android/ui/photoviewer/PhotoViewer.java | 27 ++++--- .../src/main/res/values-de-rDE/strings.xml | 3 +- mastodon/src/main/res/values/strings.xml | 1 + 4 files changed, 99 insertions(+), 10 deletions(-) create mode 100644 mastodon/src/main/java/org/joinmastodon/android/ui/ImageDescriptionSheet.java diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/ImageDescriptionSheet.java b/mastodon/src/main/java/org/joinmastodon/android/ui/ImageDescriptionSheet.java new file mode 100644 index 000000000..e80a3574c --- /dev/null +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/ImageDescriptionSheet.java @@ -0,0 +1,78 @@ +package org.joinmastodon.android.ui; + +import android.app.Activity; +import android.graphics.Typeface; +import android.graphics.drawable.ColorDrawable; +import android.os.Build; +import android.view.View; +import android.view.ViewGroup; +import android.view.WindowInsets; +import android.widget.FrameLayout; +import android.widget.LinearLayout; +import android.widget.TextView; + +import androidx.annotation.NonNull; + +import org.joinmastodon.android.R; +import org.joinmastodon.android.model.Attachment; +import org.joinmastodon.android.ui.utils.UiUtils; + +import me.grishka.appkit.utils.V; +import me.grishka.appkit.views.BottomSheet; + +public class ImageDescriptionSheet extends BottomSheet{ + private LinearLayout layout; + + public ImageDescriptionSheet(@NonNull Activity activity, Attachment attachment){ + super(activity); + + View handleView=new View(activity); + handleView.setBackgroundResource(R.drawable.bg_bottom_sheet_handle); + ViewGroup handle=new FrameLayout(activity); + handle.addView(handleView); + handle.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, V.dp(24))); + + TextView textView = new TextView(activity); + if (attachment.description == null || attachment.description.isEmpty()) { + textView.setText(R.string.media_no_description); + textView.setTypeface(null, Typeface.ITALIC); + } else { + textView.setText(attachment.description); + } + + TextView header = new TextView(activity); + header.setText(R.string.image_description); + header.setAllCaps(true); + header.setTypeface(null, Typeface.BOLD); + header.setPadding(0, V.dp(24), 0, V.dp(8)); + + layout = new LinearLayout(activity); + layout.setOrientation(LinearLayout.VERTICAL); + layout.addView(header); + layout.addView(textView); + + FrameLayout content=new FrameLayout(activity); + content.setBackgroundResource(R.drawable.bg_bottom_sheet); + content.addView(handle); + content.addView(layout); + content.setPadding(V.dp(24), V.dp(0), V.dp(24), V.dp(0)); + + setContentView(content); + setNavigationBarBackground(new ColorDrawable(UiUtils.getThemeColor(activity, R.attr.colorWindowBackground)), !UiUtils.isDarkTheme()); + } + + @Override + protected void onWindowInsetsUpdated(WindowInsets insets){ + if(Build.VERSION.SDK_INT>=29){ + int tappableBottom=insets.getTappableElementInsets().bottom; + int insetBottom=insets.getSystemWindowInsetBottom(); + if(tappableBottom==0 && insetBottom>0){ + layout.setPadding(0, 0, 0, V.dp(48)-insetBottom); + }else{ + layout.setPadding(0, 0, 0, V.dp(24)); + } + }else{ + layout.setPadding(0, 0, 0, V.dp(24)); + } + } +} diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/photoviewer/PhotoViewer.java b/mastodon/src/main/java/org/joinmastodon/android/ui/photoviewer/PhotoViewer.java index 73abcda10..7f91baee0 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/photoviewer/PhotoViewer.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/photoviewer/PhotoViewer.java @@ -49,6 +49,7 @@ import android.widget.Toolbar; import org.joinmastodon.android.R; import org.joinmastodon.android.api.MastodonAPIController; import org.joinmastodon.android.model.Attachment; +import org.joinmastodon.android.ui.ImageDescriptionSheet; import org.joinmastodon.android.ui.M3AlertDialogBuilder; import java.io.File; @@ -71,6 +72,7 @@ import me.grishka.appkit.imageloader.requests.UrlImageLoaderRequest; import me.grishka.appkit.utils.BindableViewHolder; import me.grishka.appkit.utils.CubicBezierInterpolator; import me.grishka.appkit.utils.V; +import me.grishka.appkit.views.BottomSheet; import me.grishka.appkit.views.FragmentRootLinearLayout; import okio.BufferedSink; import okio.Okio; @@ -176,11 +178,22 @@ public class PhotoViewer implements ZoomPanView.Listener{ toolbarWrap=uiOverlay.findViewById(R.id.toolbar_wrap); toolbar=uiOverlay.findViewById(R.id.toolbar); toolbar.setNavigationOnClickListener(v->onStartSwipeToDismissTransition(0)); - toolbar.getMenu().add(R.string.download).setIcon(R.drawable.ic_fluent_arrow_download_24_regular).setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); - toolbar.setOnMenuItemClickListener(item->{ - saveCurrentFile(); - return true; - }); + toolbar.getMenu() + .add(R.string.image_description) + .setIcon(R.drawable.ic_fluent_image_alt_text_24_regular) + .setOnMenuItemClickListener(item -> { + new ImageDescriptionSheet(activity,attachments.get(pager.getCurrentItem())).show(); + return true; + }) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); + toolbar.getMenu() + .add(R.string.download) + .setIcon(R.drawable.ic_fluent_arrow_download_24_regular) + .setOnMenuItemClickListener(item -> { + saveCurrentFile(); + return true; + }) + .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); uiOverlay.setAlpha(0f); videoControls=uiOverlay.findViewById(R.id.video_player_controls); videoSeekBar=uiOverlay.findViewById(R.id.seekbar); @@ -699,10 +712,6 @@ public class PhotoViewer implements ZoomPanView.Listener{ @Override public void onBind(Attachment item){ zoomPanView.setScrollDirections(getAbsoluteAdapterPosition()>0, getAbsoluteAdapterPosition() { - Toast.makeText(activity, item.description, Toast.LENGTH_LONG).show(); - return true; - }); } } diff --git a/mastodon/src/main/res/values-de-rDE/strings.xml b/mastodon/src/main/res/values-de-rDE/strings.xml index 4d3fd1493..62e92cd13 100644 --- a/mastodon/src/main/res/values-de-rDE/strings.xml +++ b/mastodon/src/main/res/values-de-rDE/strings.xml @@ -203,6 +203,7 @@ Bestätigungs-E-Mail gesendet Was gibt\'s Neues? Inhaltswarnung + Bildbeschreibung Füge eine Bildbeschreibung hinzu… Upload erneut versuchen Fehler beim Hochladen des Bildes @@ -315,7 +316,7 @@ Genehmigt Folgende manuell Aktuelles Konto %s ausloggen - + %,d Follower %,d Follower diff --git a/mastodon/src/main/res/values/strings.xml b/mastodon/src/main/res/values/strings.xml index 939ac102c..708a487d6 100644 --- a/mastodon/src/main/res/values/strings.xml +++ b/mastodon/src/main/res/values/strings.xml @@ -209,6 +209,7 @@ Type or paste what\'s on your mind Content warning Add image description… + Image description Retry upload Image failed to upload Video failed to upload From a0a28a0cb7a10b74e8c27730b1df130b2cc60bbc Mon Sep 17 00:00:00 2001 From: sk Date: Thu, 5 May 2022 23:39:24 +0200 Subject: [PATCH 3/5] implement scroll-to-close --- .../main/res/drawable/ic_fluent_image_alt_text_24_regular.xml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 mastodon/src/main/res/drawable/ic_fluent_image_alt_text_24_regular.xml diff --git a/mastodon/src/main/res/drawable/ic_fluent_image_alt_text_24_regular.xml b/mastodon/src/main/res/drawable/ic_fluent_image_alt_text_24_regular.xml new file mode 100644 index 000000000..037d623c3 --- /dev/null +++ b/mastodon/src/main/res/drawable/ic_fluent_image_alt_text_24_regular.xml @@ -0,0 +1,3 @@ + + + From 9aa95413e6212b9dfcd1df24c3391e48055669e5 Mon Sep 17 00:00:00 2001 From: sk Date: Fri, 6 May 2022 00:15:56 +0200 Subject: [PATCH 4/5] make text selectable --- .../android/ui/ImageDescriptionSheet.java | 47 +++++++++++-------- 1 file changed, 28 insertions(+), 19 deletions(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/ImageDescriptionSheet.java b/mastodon/src/main/java/org/joinmastodon/android/ui/ImageDescriptionSheet.java index e80a3574c..fa2ab5338 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/ImageDescriptionSheet.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/ImageDescriptionSheet.java @@ -12,16 +12,19 @@ import android.widget.LinearLayout; import android.widget.TextView; import androidx.annotation.NonNull; +import androidx.recyclerview.widget.LinearLayoutManager; import org.joinmastodon.android.R; import org.joinmastodon.android.model.Attachment; import org.joinmastodon.android.ui.utils.UiUtils; +import me.grishka.appkit.utils.SingleViewRecyclerAdapter; import me.grishka.appkit.utils.V; import me.grishka.appkit.views.BottomSheet; +import me.grishka.appkit.views.UsableRecyclerView; public class ImageDescriptionSheet extends BottomSheet{ - private LinearLayout layout; + private UsableRecyclerView list; public ImageDescriptionSheet(@NonNull Activity activity, Attachment attachment){ super(activity); @@ -38,26 +41,32 @@ public class ImageDescriptionSheet extends BottomSheet{ textView.setTypeface(null, Typeface.ITALIC); } else { textView.setText(attachment.description); + textView.setTextIsSelectable(true); } - TextView header = new TextView(activity); - header.setText(R.string.image_description); - header.setAllCaps(true); - header.setTypeface(null, Typeface.BOLD); - header.setPadding(0, V.dp(24), 0, V.dp(8)); + TextView heading=new TextView(activity); + heading.setText(R.string.image_description); + heading.setAllCaps(true); + heading.setTypeface(null, Typeface.BOLD); + heading.setPadding(0, V.dp(24), 0, V.dp(8)); - layout = new LinearLayout(activity); - layout.setOrientation(LinearLayout.VERTICAL); - layout.addView(header); - layout.addView(textView); + LinearLayout linearLayout = new LinearLayout(activity); + linearLayout.setOrientation(LinearLayout.VERTICAL); + linearLayout.setPadding(V.dp(24), 0, V.dp(24), 0); + linearLayout.addView(heading); + linearLayout.addView(textView); - FrameLayout content=new FrameLayout(activity); - content.setBackgroundResource(R.drawable.bg_bottom_sheet); - content.addView(handle); - content.addView(layout); - content.setPadding(V.dp(24), V.dp(0), V.dp(24), V.dp(0)); + FrameLayout layout=new FrameLayout(activity); + layout.addView(handle); + layout.addView(linearLayout); - setContentView(content); + list=new UsableRecyclerView(activity); + list.setLayoutManager(new LinearLayoutManager(activity)); + list.setBackgroundResource(R.drawable.bg_bottom_sheet); + list.setAdapter(new SingleViewRecyclerAdapter(layout)); + list.setClipToPadding(false); + + setContentView(list); setNavigationBarBackground(new ColorDrawable(UiUtils.getThemeColor(activity, R.attr.colorWindowBackground)), !UiUtils.isDarkTheme()); } @@ -67,12 +76,12 @@ public class ImageDescriptionSheet extends BottomSheet{ int tappableBottom=insets.getTappableElementInsets().bottom; int insetBottom=insets.getSystemWindowInsetBottom(); if(tappableBottom==0 && insetBottom>0){ - layout.setPadding(0, 0, 0, V.dp(48)-insetBottom); + list.setPadding(0, 0, 0, V.dp(48)-insetBottom); }else{ - layout.setPadding(0, 0, 0, V.dp(24)); + list.setPadding(0, 0, 0, V.dp(24)); } }else{ - layout.setPadding(0, 0, 0, V.dp(24)); + list.setPadding(0, 0, 0, V.dp(24)); } } } From ac81f10ea8458fef99fd8b2460c9ebc660ca08e7 Mon Sep 17 00:00:00 2001 From: sk Date: Fri, 6 May 2022 00:16:25 +0200 Subject: [PATCH 5/5] make button disappear when no description --- .../android/ui/photoviewer/PhotoViewer.java | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/mastodon/src/main/java/org/joinmastodon/android/ui/photoviewer/PhotoViewer.java b/mastodon/src/main/java/org/joinmastodon/android/ui/photoviewer/PhotoViewer.java index 7f91baee0..0e128d53a 100644 --- a/mastodon/src/main/java/org/joinmastodon/android/ui/photoviewer/PhotoViewer.java +++ b/mastodon/src/main/java/org/joinmastodon/android/ui/photoviewer/PhotoViewer.java @@ -19,6 +19,7 @@ import android.media.AudioManager; import android.media.MediaPlayer; import android.media.MediaScannerConnection; import android.net.Uri; +import android.opengl.Visibility; import android.os.Build; import android.os.Environment; import android.os.SystemClock; @@ -101,6 +102,7 @@ public class PhotoViewer implements ZoomPanView.Listener{ private TextView videoTimeView; private ImageButton videoPlayPauseButton; private View videoControls; + private MenuItem imageDescriptionButton; private boolean uiVisible=true; private AudioManager.OnAudioFocusChangeListener audioFocusListener=this::onAudioFocusChanged; private Runnable uiAutoHider=()->{ @@ -178,14 +180,16 @@ public class PhotoViewer implements ZoomPanView.Listener{ toolbarWrap=uiOverlay.findViewById(R.id.toolbar_wrap); toolbar=uiOverlay.findViewById(R.id.toolbar); toolbar.setNavigationOnClickListener(v->onStartSwipeToDismissTransition(0)); - toolbar.getMenu() + imageDescriptionButton = toolbar.getMenu() .add(R.string.image_description) .setIcon(R.drawable.ic_fluent_image_alt_text_24_regular) + .setVisible(attachments.get(pager.getCurrentItem()).description != null + && !attachments.get(pager.getCurrentItem()).description.isEmpty()) .setOnMenuItemClickListener(item -> { new ImageDescriptionSheet(activity,attachments.get(pager.getCurrentItem())).show(); return true; - }) - .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); + }); + imageDescriptionButton.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); toolbar.getMenu() .add(R.string.download) .setIcon(R.drawable.ic_fluent_arrow_download_24_regular) @@ -389,6 +393,8 @@ public class PhotoViewer implements ZoomPanView.Listener{ private void onPageChanged(int index){ currentIndex=index; Attachment att=attachments.get(index); + imageDescriptionButton.setVisible(att.description != null && !att.description.isEmpty()); + toolbar.invalidate(); V.setVisibilityAnimated(videoControls, att.type==Attachment.Type.VIDEO ? View.VISIBLE : View.GONE); if(att.type==Attachment.Type.VIDEO){ videoSeekBar.setSecondaryProgress(0);