From e0346a8e88d98f5a3c05067c1ac6b136b8619b13 Mon Sep 17 00:00:00 2001 From: Levi Bard Date: Thu, 22 Oct 2020 21:15:46 +0200 Subject: [PATCH] Open photos embedded in preview cards in the image viewer (#1966) * Open photos embedded in preview cards in the internal image viewer instead of opening the browser * Enable toolbar for single image viewer * Apply review feedback --- .../keylesspalace/tusky/AccountActivity.kt | 2 +- .../keylesspalace/tusky/ViewMediaActivity.kt | 48 +++++++++++-------- .../tusky/adapter/StatusBaseViewHolder.java | 11 ++++- .../com/keylesspalace/tusky/entity/Card.kt | 6 ++- .../tusky/fragment/ViewImageFragment.kt | 4 +- .../tusky/fragment/ViewMediaFragment.kt | 6 +-- ...rAdapter.kt => SingleImagePagerAdapter.kt} | 6 +-- 7 files changed, 51 insertions(+), 32 deletions(-) rename app/src/main/java/com/keylesspalace/tusky/pager/{AvatarImagePagerAdapter.kt => SingleImagePagerAdapter.kt} (81%) diff --git a/app/src/main/java/com/keylesspalace/tusky/AccountActivity.kt b/app/src/main/java/com/keylesspalace/tusky/AccountActivity.kt index 097818759..6c42ffc81 100644 --- a/app/src/main/java/com/keylesspalace/tusky/AccountActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/AccountActivity.kt @@ -407,7 +407,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI accountAvatarImageView.setOnClickListener { avatarView -> - val intent = ViewMediaActivity.newAvatarIntent(avatarView.context, account.avatar) + val intent = ViewMediaActivity.newSingleImageIntent(avatarView.context, account.avatar) avatarView.transitionName = account.avatar val options = ActivityOptionsCompat.makeSceneTransitionAnimation(this, avatarView, account.avatar) diff --git a/app/src/main/java/com/keylesspalace/tusky/ViewMediaActivity.kt b/app/src/main/java/com/keylesspalace/tusky/ViewMediaActivity.kt index 447c05905..5d90bd943 100644 --- a/app/src/main/java/com/keylesspalace/tusky/ViewMediaActivity.kt +++ b/app/src/main/java/com/keylesspalace/tusky/ViewMediaActivity.kt @@ -46,7 +46,7 @@ import com.bumptech.glide.request.FutureTarget import com.keylesspalace.tusky.BuildConfig.APPLICATION_ID import com.keylesspalace.tusky.entity.Attachment import com.keylesspalace.tusky.fragment.ViewImageFragment -import com.keylesspalace.tusky.pager.AvatarImagePagerAdapter +import com.keylesspalace.tusky.pager.SingleImagePagerAdapter import com.keylesspalace.tusky.pager.ImagePagerAdapter import com.keylesspalace.tusky.util.getTemporaryMediaFilename import com.keylesspalace.tusky.viewdata.AttachmentViewData @@ -68,7 +68,7 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener companion object { private const val EXTRA_ATTACHMENTS = "attachments" private const val EXTRA_ATTACHMENT_INDEX = "index" - private const val EXTRA_AVATAR_URL = "avatar" + private const val EXTRA_SINGLE_IMAGE_URL = "single_image" private const val TAG = "ViewMediaActivity" @JvmStatic @@ -79,9 +79,10 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener return intent } - fun newAvatarIntent(context: Context, url: String): Intent { + @JvmStatic + fun newSingleImageIntent(context: Context, url: String): Intent { val intent = Intent(context, ViewMediaActivity::class.java) - intent.putExtra(EXTRA_AVATAR_URL, url) + intent.putExtra(EXTRA_SINGLE_IMAGE_URL, url) return intent } } @@ -91,6 +92,7 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener private var attachments: ArrayList? = null private val toolbarVisibilityListeners = mutableListOf() + private var imageUrl: String? = null fun addToolbarVisibilityListener(listener: ToolbarVisibilityListener): Function0 { this.toolbarVisibilityListeners.add(listener) @@ -117,10 +119,10 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener ImagePagerAdapter(this, realAttachs, initialPosition) } else { - val avatarUrl = intent.getStringExtra(EXTRA_AVATAR_URL) - ?: throw IllegalArgumentException("attachment list or avatar url has to be set") + imageUrl = intent.getStringExtra(EXTRA_SINGLE_IMAGE_URL) + ?: throw IllegalArgumentException("attachment list or image url has to be set") - AvatarImagePagerAdapter(this, avatarUrl) + SingleImagePagerAdapter(this, imageUrl!!) } viewPager.adapter = adapter @@ -161,11 +163,10 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener } override fun onCreateOptionsMenu(menu: Menu): Boolean { - if (attachments != null) { - menuInflater.inflate(R.menu.view_media_toolbar, menu) - return true - } - return false + menuInflater.inflate(R.menu.view_media_toolbar, menu) + // We don't support 'open status' from single image views + menu?.findItem(R.id.action_open_status)?.isVisible = (attachments != null) + return true } override fun onPrepareOptionsMenu(menu: Menu?): Boolean { @@ -213,7 +214,7 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener } private fun downloadMedia() { - val url = attachments!![viewPager.currentItem].attachment.url + val url = imageUrl ?: attachments!![viewPager.currentItem].attachment.url val filename = Uri.parse(url).lastPathSegment Toast.makeText(applicationContext, resources.getString(R.string.download_image, filename), Toast.LENGTH_SHORT).show() @@ -240,8 +241,9 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener } private fun copyLink() { + val url = imageUrl ?: attachments!![viewPager.currentItem].attachment.url val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager - clipboard.setPrimaryClip(ClipData.newPlainText(null, attachments!![viewPager.currentItem].attachment.url)) + clipboard.setPrimaryClip(ClipData.newPlainText(null, url)) } private fun shareMedia() { @@ -251,13 +253,17 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener return } - val attachment = attachments!![viewPager.currentItem].attachment - when (attachment.type) { - Attachment.Type.IMAGE -> shareImage(directory, attachment.url) - Attachment.Type.AUDIO, - Attachment.Type.VIDEO, - Attachment.Type.GIFV -> shareMediaFile(directory, attachment.url) - else -> Log.e(TAG, "Unknown media format for sharing.") + if (imageUrl != null) { + shareImage(directory, imageUrl!!) + } else { + val attachment = attachments!![viewPager.currentItem].attachment + when (attachment.type) { + Attachment.Type.IMAGE -> shareImage(directory, attachment.url) + Attachment.Type.AUDIO, + Attachment.Type.VIDEO, + Attachment.Type.GIFV -> shareMediaFile(directory, attachment.url) + else -> Log.e(TAG, "Unknown media format for sharing.") + } } } diff --git a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java index 1a640cc1b..6216ca531 100644 --- a/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java +++ b/app/src/main/java/com/keylesspalace/tusky/adapter/StatusBaseViewHolder.java @@ -31,6 +31,7 @@ import com.bumptech.glide.load.resource.bitmap.CenterCrop; import com.bumptech.glide.load.resource.bitmap.GranularRoundedCorners; import com.google.android.material.button.MaterialButton; import com.keylesspalace.tusky.R; +import com.keylesspalace.tusky.ViewMediaActivity; import com.keylesspalace.tusky.entity.Attachment; import com.keylesspalace.tusky.entity.Attachment.Focus; import com.keylesspalace.tusky.entity.Attachment.MetaData; @@ -1069,7 +1070,15 @@ public abstract class StatusBaseViewHolder extends RecyclerView.ViewHolder { cardImage.setImageResource(R.drawable.card_image_placeholder); } - cardView.setOnClickListener(v -> LinkHelper.openLink(card.getUrl(), v.getContext())); + View.OnClickListener visitLink = v -> LinkHelper.openLink(card.getUrl(), v.getContext()); + View.OnClickListener openImage = v -> cardView.getContext().startActivity(ViewMediaActivity.newSingleImageIntent(cardView.getContext(), card.getEmbed_url())); + + cardInfo.setOnClickListener(visitLink); + // View embedded photos in our image viewer instead of opening the browser + cardImage.setOnClickListener(card.getType().equals(Card.TYPE_PHOTO) && !TextUtils.isEmpty(card.getEmbed_url()) ? + openImage : + visitLink); + cardView.setClipToOutline(true); } else { cardView.setVisibility(View.GONE); diff --git a/app/src/main/java/com/keylesspalace/tusky/entity/Card.kt b/app/src/main/java/com/keylesspalace/tusky/entity/Card.kt index 1b07cea4d..ada9ec205 100644 --- a/app/src/main/java/com/keylesspalace/tusky/entity/Card.kt +++ b/app/src/main/java/com/keylesspalace/tusky/entity/Card.kt @@ -27,7 +27,8 @@ data class Card( val type: String, val width: Int, val height: Int, - val blurhash: String? + val blurhash: String?, + val embed_url: String? ) { override fun hashCode(): Int { @@ -42,4 +43,7 @@ data class Card( return account?.url == this.url } + companion object { + const val TYPE_PHOTO = "photo" + } } diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/ViewImageFragment.kt b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewImageFragment.kt index e61a24e5d..67f0e4e79 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/ViewImageFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewImageFragment.kt @@ -99,9 +99,9 @@ class ViewImageFragment : ViewMediaFragment() { url = attachment.url description = attachment.description } else { - url = arguments.getString(ARG_AVATAR_URL) + url = arguments.getString(ARG_SINGLE_IMAGE_URL) if (url == null) { - throw IllegalArgumentException("attachment or avatar url has to be set") + throw IllegalArgumentException("attachment or image url has to be set") } } diff --git a/app/src/main/java/com/keylesspalace/tusky/fragment/ViewMediaFragment.kt b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewMediaFragment.kt index 9b51f285b..86b3d09be 100644 --- a/app/src/main/java/com/keylesspalace/tusky/fragment/ViewMediaFragment.kt +++ b/app/src/main/java/com/keylesspalace/tusky/fragment/ViewMediaFragment.kt @@ -42,7 +42,7 @@ abstract class ViewMediaFragment : BaseFragment() { @JvmStatic protected val ARG_ATTACHMENT = "attach" @JvmStatic - protected val ARG_AVATAR_URL = "avatarUrl" + protected val ARG_SINGLE_IMAGE_URL = "singleImageUrl" @JvmStatic fun newInstance(attachment: Attachment, shouldStartPostponedTransition: Boolean): ViewMediaFragment { @@ -62,10 +62,10 @@ abstract class ViewMediaFragment : BaseFragment() { } @JvmStatic - fun newAvatarInstance(avatarUrl: String): ViewMediaFragment { + fun newSingleImageInstance(imageUrl: String): ViewMediaFragment { val arguments = Bundle(2) val fragment = ViewImageFragment() - arguments.putString(ARG_AVATAR_URL, avatarUrl) + arguments.putString(ARG_SINGLE_IMAGE_URL, imageUrl) arguments.putBoolean(ARG_START_POSTPONED_TRANSITION, true) fragment.arguments = arguments diff --git a/app/src/main/java/com/keylesspalace/tusky/pager/AvatarImagePagerAdapter.kt b/app/src/main/java/com/keylesspalace/tusky/pager/SingleImagePagerAdapter.kt similarity index 81% rename from app/src/main/java/com/keylesspalace/tusky/pager/AvatarImagePagerAdapter.kt rename to app/src/main/java/com/keylesspalace/tusky/pager/SingleImagePagerAdapter.kt index a3dfcf2ab..c8306f70e 100644 --- a/app/src/main/java/com/keylesspalace/tusky/pager/AvatarImagePagerAdapter.kt +++ b/app/src/main/java/com/keylesspalace/tusky/pager/SingleImagePagerAdapter.kt @@ -5,14 +5,14 @@ import androidx.fragment.app.FragmentActivity import com.keylesspalace.tusky.ViewMediaAdapter import com.keylesspalace.tusky.fragment.ViewMediaFragment -class AvatarImagePagerAdapter( +class SingleImagePagerAdapter( activity: FragmentActivity, - private val avatarUrl: String + private val imageUrl: String ) : ViewMediaAdapter(activity) { override fun createFragment(position: Int): Fragment { return if (position == 0) { - ViewMediaFragment.newAvatarInstance(avatarUrl) + ViewMediaFragment.newSingleImageInstance(imageUrl) } else { throw IllegalStateException() }