From 5f9b6292d71a59f7de6ad2ef41fade9d4adc6dd9 Mon Sep 17 00:00:00 2001 From: nuclearfog Date: Thu, 6 Jul 2023 22:09:52 +0200 Subject: [PATCH] added image description support, restructured image viewer activity --- .../twidda/backend/async/ImageDownloader.java | 4 +- .../twidda/backend/async/MessageUpdater.java | 4 +- .../twidda/backend/helper/MediaStatus.java | 59 ++++-- .../backend/helper/update/MessageUpdate.java | 29 +-- .../backend/helper/update/StatusUpdate.java | 28 ++- .../twidda/ui/activities/ImageViewer.java | 191 +++++++++++++----- .../twidda/ui/activities/MediaActivity.java | 2 +- .../twidda/ui/activities/MessageEditor.java | 10 +- .../twidda/ui/activities/ProfileActivity.java | 8 +- .../twidda/ui/activities/StatusActivity.java | 19 +- .../twidda/ui/activities/StatusEditor.java | 44 ++-- .../twidda/ui/dialogs/DescriptionDialog.java | 93 +++++++++ .../twidda/ui/dialogs/FilterDialog.java | 2 +- .../twidda/ui/dialogs/MetricsDialog.java | 2 +- .../twidda/ui/dialogs/PollDialog.java | 2 +- .../twidda/ui/dialogs/ReportDialog.java | 2 +- .../ui/dialogs/StatusPreferenceDialog.java | 2 +- .../twidda/ui/dialogs/WebPushDialog.java | 2 +- .../twidda/ui/fragments/MessageFragment.java | 4 +- .../main/res/layout/dialog_description.xml | 41 ++++ app/src/main/res/layout/page_image.xml | 1 + app/src/main/res/menu/image.xml | 8 +- app/src/main/res/menu/video.xml | 9 +- app/src/main/res/values/dimens.xml | 5 + app/src/main/res/values/strings.xml | 2 + app/src/main/res/values/styles.xml | 33 +-- 26 files changed, 430 insertions(+), 176 deletions(-) create mode 100644 app/src/main/java/org/nuclearfog/twidda/ui/dialogs/DescriptionDialog.java create mode 100644 app/src/main/res/layout/dialog_description.xml diff --git a/app/src/main/java/org/nuclearfog/twidda/backend/async/ImageDownloader.java b/app/src/main/java/org/nuclearfog/twidda/backend/async/ImageDownloader.java index 19e8317c..79a2aba9 100644 --- a/app/src/main/java/org/nuclearfog/twidda/backend/async/ImageDownloader.java +++ b/app/src/main/java/org/nuclearfog/twidda/backend/async/ImageDownloader.java @@ -44,8 +44,8 @@ public class ImageDownloader extends AsyncExecutor(mediaStatuses); } + /** + * update existing media status + * + * @param mediaStatus media status to update + */ + public void updateMediaStatus(MediaStatus mediaStatus) { + int index = mediaStatuses.indexOf(mediaStatus); + if (index >= 0) { + mediaStatuses.set(index, mediaStatus); + } + } + /** * get attached poll if any * diff --git a/app/src/main/java/org/nuclearfog/twidda/ui/activities/ImageViewer.java b/app/src/main/java/org/nuclearfog/twidda/ui/activities/ImageViewer.java index 8df4db8f..d5712d62 100644 --- a/app/src/main/java/org/nuclearfog/twidda/ui/activities/ImageViewer.java +++ b/app/src/main/java/org/nuclearfog/twidda/ui/activities/ImageViewer.java @@ -1,6 +1,8 @@ package org.nuclearfog.twidda.ui.activities; import android.content.Context; +import android.content.Intent; +import android.graphics.Bitmap; import android.location.Location; import android.net.Uri; import android.os.Bundle; @@ -13,55 +15,70 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.appcompat.widget.Toolbar; +import com.wolt.blurhashkt.BlurHashDecoder; + import org.nuclearfog.twidda.BuildConfig; import org.nuclearfog.twidda.R; import org.nuclearfog.twidda.backend.async.AsyncExecutor.AsyncCallback; import org.nuclearfog.twidda.backend.async.ImageDownloader; import org.nuclearfog.twidda.backend.async.ImageDownloader.ImageLoaderParam; import org.nuclearfog.twidda.backend.async.ImageDownloader.ImageLoaderResult; +import org.nuclearfog.twidda.backend.helper.MediaStatus; import org.nuclearfog.twidda.backend.utils.AppStyles; import org.nuclearfog.twidda.backend.utils.ErrorUtils; import org.nuclearfog.twidda.config.GlobalSettings; +import org.nuclearfog.twidda.model.Media; +import org.nuclearfog.twidda.ui.dialogs.DescriptionDialog; +import org.nuclearfog.twidda.ui.dialogs.DescriptionDialog.DescriptionCallback; import org.nuclearfog.twidda.ui.views.AnimatedImageView; import org.nuclearfog.twidda.ui.views.DescriptionView; import org.nuclearfog.zoomview.ZoomView; import java.io.File; +import java.io.Serializable; /** * Activity to show online and local images * * @author nuclearfog */ -public class ImageViewer extends MediaActivity implements AsyncCallback { +public class ImageViewer extends MediaActivity implements AsyncCallback, DescriptionCallback { + + public static final int IMAGE_LOCAL = 900; + + public static final int GIF_LOCAL = 901; + + public static final int MEDIA_LOCAL = 902; + + public static final int IMAGE_ONLINE = 903; + + public static final int MEDIA_ONLINE = 904; + + public static final int RETURN_MEDIA_STATUS_UPDATE = 0x5895; /** - * indicates a default image (jpg, png, etc.) + * key to set image format (image or gif) + * value type is Integer {@link #IMAGE_LOCAL,#IMAGE_ONLINE,#GIF_LOCAL,#MEDIA_LOCAL} */ - public static final int IMAGE_DEFAULT = 1; - - /** - * indicates an animated image (gif) - */ - public static final int IMAGE_GIF = 2; + public static final String TYPE = "image-type"; /** * key to add URI of the image (online or local) * value type is {@link Uri} */ - public static final String LINK = "image-uri"; + public static final String KEY_MEDIA_URL = "image-url"; /** - * key to set image format (image or gif) - * value type is Integer {@link #IMAGE_DEFAULT,#IMAGE_GIF} + * key to add offline media + * value type is {@link MediaStatus} */ - public static final String TYPE = "image-type"; + public static final String KEY_MEDIA_LOCAL = "media-status"; /** - * key to set image description - * value type is String + * key to add online media + * value type is {@link Media} */ - public static final String DESCRIPTION = "image-description"; + public static final String KEY_MEDIA_ONLINE = "media-online"; /** * name of the cache folder where online images will be stored @@ -70,16 +87,19 @@ public class ImageViewer extends MediaActivity implements AsyncCallback, OnClickListener, OnProgressStopListener, OnConfirmListener, OnMediaClickListener, TextWatcher, PollUpdateCallback, OnEmojiSelectListener { /** @@ -86,6 +90,7 @@ public class StatusEditor extends MediaActivity implements OnClickListener, OnPr */ private static final String KEY_SAVE = "status_update"; + private ActivityResultLauncher activityResultLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), this); private AsyncCallback statusUpdateResult = this::onStatusUpdated; private AsyncCallback instanceResult = this::onInstanceResult; @@ -241,6 +246,20 @@ public class StatusEditor extends MediaActivity implements OnClickListener, OnPr } + @Override + public void onActivityResult(ActivityResult result) { + if (result.getResultCode() == ImageViewer.RETURN_MEDIA_STATUS_UPDATE && result.getData() != null) { + Serializable data = result.getData().getSerializableExtra(ImageViewer.KEY_MEDIA_LOCAL); + if (data instanceof MediaStatus) { + MediaStatus mediaStatus = (MediaStatus) data; + statusUpdate.updateMediaStatus(mediaStatus); + } + } else { + super.onActivityResult(result); + } + } + + @Override public void onClick(View v) { // send status @@ -337,7 +356,8 @@ public class StatusEditor extends MediaActivity implements OnClickListener, OnPr @Override protected void onMediaFetched(int resultType, @NonNull Uri uri) { - int mediaType = statusUpdate.addMedia(this, uri); + MediaStatus mediaStatus = new MediaStatus(getApplicationContext(), uri, ""); + int mediaType = statusUpdate.addMedia(mediaStatus); addMedia(mediaType); } @@ -366,19 +386,15 @@ public class StatusEditor extends MediaActivity implements OnClickListener, OnPr if (statusUpdate.getMediaStatuses().isEmpty()) return; MediaStatus media = statusUpdate.getMediaStatuses().get(index); + if (media.getPath() == null) + return; switch (media.getMediaType()) { - case MediaStatus.IMAGE: - Intent intent = new Intent(this, ImageViewer.class); - intent.putExtra(ImageViewer.LINK, Uri.parse(media.getPath())); - intent.putExtra(ImageViewer.TYPE, ImageViewer.IMAGE_DEFAULT); - startActivity(intent); - break; - + case MediaStatus.PHOTO: case MediaStatus.GIF: - intent = new Intent(this, ImageViewer.class); - intent.putExtra(ImageViewer.LINK, Uri.parse(media.getPath())); - intent.putExtra(ImageViewer.TYPE, ImageViewer.IMAGE_GIF); - startActivity(intent); + Intent intent = new Intent(this, ImageViewer.class); + intent.putExtra(ImageViewer.KEY_MEDIA_LOCAL, media); + intent.putExtra(ImageViewer.TYPE, ImageViewer.MEDIA_LOCAL); + activityResultLauncher.launch(intent); break; case MediaStatus.VIDEO: @@ -422,7 +438,7 @@ public class StatusEditor extends MediaActivity implements OnClickListener, OnPr private void addMedia(int mediaType) { switch (mediaType) { case Media.PHOTO: - case MediaStatus.IMAGE: + case MediaStatus.PHOTO: adapter.addImageItem(); break; diff --git a/app/src/main/java/org/nuclearfog/twidda/ui/dialogs/DescriptionDialog.java b/app/src/main/java/org/nuclearfog/twidda/ui/dialogs/DescriptionDialog.java new file mode 100644 index 00000000..51346f65 --- /dev/null +++ b/app/src/main/java/org/nuclearfog/twidda/ui/dialogs/DescriptionDialog.java @@ -0,0 +1,93 @@ +package org.nuclearfog.twidda.ui.dialogs; + +import android.app.Dialog; +import android.content.Context; +import android.os.Bundle; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.ViewGroup; +import android.widget.EditText; + +import androidx.annotation.NonNull; + +import org.nuclearfog.twidda.R; +import org.nuclearfog.twidda.backend.utils.AppStyles; + + +public class DescriptionDialog extends Dialog implements OnClickListener { + + private static final String KEY_SAVE = " description-save"; + + private DescriptionCallback callback; + + private EditText descriptionEdit; + + + public DescriptionDialog(Context context, DescriptionCallback callback) { + super(context, R.style.DefaultDialog); + this.callback = callback; + } + + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(R.layout.dialog_description); + ViewGroup root = findViewById(R.id.dialog_description_root); + View applyButton = findViewById(R.id.dialog_description_apply); + descriptionEdit = findViewById(R.id.dialog_description_input); + AppStyles.setTheme(root); + + applyButton.setOnClickListener(this); + } + + + @NonNull + @Override + public Bundle onSaveInstanceState() { + String description = descriptionEdit.getText().toString(); + Bundle bundle = super.onSaveInstanceState(); + bundle.putString(KEY_SAVE, description); + return bundle; + } + + + @Override + public void onRestoreInstanceState(@NonNull Bundle savedInstanceState) { + String description = savedInstanceState.getString(KEY_SAVE, ""); + descriptionEdit.setText(description); + super.onRestoreInstanceState(savedInstanceState); + } + + + @Override + public void show() { + if (!isShowing()) { + super.show(); + } + } + + + @Override + public void dismiss() { + if (isShowing()) { + super.dismiss(); + } + } + + + @Override + public void onClick(View v) { + if (v.getId() == R.id.dialog_description_apply) { + String description = descriptionEdit.getText().toString(); + callback.onDescriptionSet(description); + dismiss(); + } + } + + + public interface DescriptionCallback { + + void onDescriptionSet(String description); + } +} \ No newline at end of file diff --git a/app/src/main/java/org/nuclearfog/twidda/ui/dialogs/FilterDialog.java b/app/src/main/java/org/nuclearfog/twidda/ui/dialogs/FilterDialog.java index 735590ca..0fd65789 100644 --- a/app/src/main/java/org/nuclearfog/twidda/ui/dialogs/FilterDialog.java +++ b/app/src/main/java/org/nuclearfog/twidda/ui/dialogs/FilterDialog.java @@ -51,7 +51,7 @@ public class FilterDialog extends Dialog implements OnClickListener, OnCheckedCh * */ public FilterDialog(Activity activity, FilterDialogCallback callback) { - super(activity, R.style.FilterDialog); + super(activity, R.style.DefaultDialog); this.callback = callback; update = new FilterUpdate(); filterAction = new StatusFilterAction(activity); diff --git a/app/src/main/java/org/nuclearfog/twidda/ui/dialogs/MetricsDialog.java b/app/src/main/java/org/nuclearfog/twidda/ui/dialogs/MetricsDialog.java index 2f11fe49..bbaf1466 100644 --- a/app/src/main/java/org/nuclearfog/twidda/ui/dialogs/MetricsDialog.java +++ b/app/src/main/java/org/nuclearfog/twidda/ui/dialogs/MetricsDialog.java @@ -34,7 +34,7 @@ public class MetricsDialog extends Dialog { * */ public MetricsDialog(Activity activity) { - super(activity, R.style.MetricsDialog); + super(activity, R.style.DefaultDialog); } diff --git a/app/src/main/java/org/nuclearfog/twidda/ui/dialogs/PollDialog.java b/app/src/main/java/org/nuclearfog/twidda/ui/dialogs/PollDialog.java index 93d1d4b7..1997c234 100644 --- a/app/src/main/java/org/nuclearfog/twidda/ui/dialogs/PollDialog.java +++ b/app/src/main/java/org/nuclearfog/twidda/ui/dialogs/PollDialog.java @@ -52,7 +52,7 @@ public class PollDialog extends Dialog implements OnClickListener { * */ public PollDialog(Activity activity, PollUpdateCallback callback) { - super(activity, R.style.PollDialog); + super(activity, R.style.DefaultDialog); this.callback = callback; optionAdapter = new EditOptionsAdapter(); timeUnitAdapter = new DropdownAdapter(activity.getApplicationContext()); diff --git a/app/src/main/java/org/nuclearfog/twidda/ui/dialogs/ReportDialog.java b/app/src/main/java/org/nuclearfog/twidda/ui/dialogs/ReportDialog.java index 4655be58..02fc37c7 100644 --- a/app/src/main/java/org/nuclearfog/twidda/ui/dialogs/ReportDialog.java +++ b/app/src/main/java/org/nuclearfog/twidda/ui/dialogs/ReportDialog.java @@ -49,7 +49,7 @@ public class ReportDialog extends Dialog implements OnClickListener, AsyncCallba * */ public ReportDialog(Activity activity) { - super(activity, R.style.ReportDialog); + super(activity, R.style.DefaultDialog); adapter = new DropdownAdapter(activity.getApplicationContext()); reportUpdater = new ReportUpdater(activity.getApplicationContext()); adapter.setItems(R.array.reports); diff --git a/app/src/main/java/org/nuclearfog/twidda/ui/dialogs/StatusPreferenceDialog.java b/app/src/main/java/org/nuclearfog/twidda/ui/dialogs/StatusPreferenceDialog.java index 27b89e03..862c52e9 100644 --- a/app/src/main/java/org/nuclearfog/twidda/ui/dialogs/StatusPreferenceDialog.java +++ b/app/src/main/java/org/nuclearfog/twidda/ui/dialogs/StatusPreferenceDialog.java @@ -41,7 +41,7 @@ public class StatusPreferenceDialog extends Dialog implements OnCheckedChangeLis * @param statusUpdate status information from status editor */ public StatusPreferenceDialog(Activity activity, StatusUpdate statusUpdate) { - super(activity, R.style.StatusDialog); + super(activity, R.style.DefaultDialog); this.statusUpdate = statusUpdate; visibility_adapter = new DropdownAdapter(activity.getApplicationContext()); language_adapter = new DropdownAdapter(activity.getApplicationContext()); diff --git a/app/src/main/java/org/nuclearfog/twidda/ui/dialogs/WebPushDialog.java b/app/src/main/java/org/nuclearfog/twidda/ui/dialogs/WebPushDialog.java index 62ee2fc9..62dd5884 100644 --- a/app/src/main/java/org/nuclearfog/twidda/ui/dialogs/WebPushDialog.java +++ b/app/src/main/java/org/nuclearfog/twidda/ui/dialogs/WebPushDialog.java @@ -53,7 +53,7 @@ public class WebPushDialog extends Dialog implements OnCheckedChangeListener, On * */ public WebPushDialog(Activity activity) { - super(activity, R.style.WebPushDialog); + super(activity, R.style.DefaultDialog); adapter = new DropdownAdapter(activity.getApplicationContext()); settings = GlobalSettings.get(getContext()); pushUpdater = new PushUpdater(getContext()); diff --git a/app/src/main/java/org/nuclearfog/twidda/ui/fragments/MessageFragment.java b/app/src/main/java/org/nuclearfog/twidda/ui/fragments/MessageFragment.java index c3569963..cbd4c9bd 100644 --- a/app/src/main/java/org/nuclearfog/twidda/ui/fragments/MessageFragment.java +++ b/app/src/main/java/org/nuclearfog/twidda/ui/fragments/MessageFragment.java @@ -142,8 +142,8 @@ public class MessageFragment extends ListFragment implements OnMessageClickListe int mediaIndex = extras[0]; if (mediaIndex >= 0 && mediaIndex < message.getMedia().length) { Intent intent = new Intent(requireContext(), ImageViewer.class); - intent.putExtra(ImageViewer.LINK, Uri.parse(message.getMedia()[mediaIndex].getUrl())); - intent.putExtra(ImageViewer.TYPE, ImageViewer.IMAGE_DEFAULT); + intent.putExtra(ImageViewer.KEY_MEDIA_URL, Uri.parse(message.getMedia()[mediaIndex].getUrl())); + intent.putExtra(ImageViewer.TYPE, ImageViewer.IMAGE_ONLINE); startActivity(intent); } } diff --git a/app/src/main/res/layout/dialog_description.xml b/app/src/main/res/layout/dialog_description.xml new file mode 100644 index 00000000..2b634021 --- /dev/null +++ b/app/src/main/res/layout/dialog_description.xml @@ -0,0 +1,41 @@ + + + + + + + +