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
This commit is contained in:
parent
53bd081802
commit
e0346a8e88
7 changed files with 51 additions and 32 deletions
|
@ -407,7 +407,7 @@ class AccountActivity : BottomSheetActivity(), ActionButtonActivity, HasAndroidI
|
||||||
|
|
||||||
|
|
||||||
accountAvatarImageView.setOnClickListener { avatarView ->
|
accountAvatarImageView.setOnClickListener { avatarView ->
|
||||||
val intent = ViewMediaActivity.newAvatarIntent(avatarView.context, account.avatar)
|
val intent = ViewMediaActivity.newSingleImageIntent(avatarView.context, account.avatar)
|
||||||
|
|
||||||
avatarView.transitionName = account.avatar
|
avatarView.transitionName = account.avatar
|
||||||
val options = ActivityOptionsCompat.makeSceneTransitionAnimation(this, avatarView, account.avatar)
|
val options = ActivityOptionsCompat.makeSceneTransitionAnimation(this, avatarView, account.avatar)
|
||||||
|
|
|
@ -46,7 +46,7 @@ import com.bumptech.glide.request.FutureTarget
|
||||||
import com.keylesspalace.tusky.BuildConfig.APPLICATION_ID
|
import com.keylesspalace.tusky.BuildConfig.APPLICATION_ID
|
||||||
import com.keylesspalace.tusky.entity.Attachment
|
import com.keylesspalace.tusky.entity.Attachment
|
||||||
import com.keylesspalace.tusky.fragment.ViewImageFragment
|
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.pager.ImagePagerAdapter
|
||||||
import com.keylesspalace.tusky.util.getTemporaryMediaFilename
|
import com.keylesspalace.tusky.util.getTemporaryMediaFilename
|
||||||
import com.keylesspalace.tusky.viewdata.AttachmentViewData
|
import com.keylesspalace.tusky.viewdata.AttachmentViewData
|
||||||
|
@ -68,7 +68,7 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
|
||||||
companion object {
|
companion object {
|
||||||
private const val EXTRA_ATTACHMENTS = "attachments"
|
private const val EXTRA_ATTACHMENTS = "attachments"
|
||||||
private const val EXTRA_ATTACHMENT_INDEX = "index"
|
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"
|
private const val TAG = "ViewMediaActivity"
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
|
@ -79,9 +79,10 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
|
||||||
return intent
|
return intent
|
||||||
}
|
}
|
||||||
|
|
||||||
fun newAvatarIntent(context: Context, url: String): Intent {
|
@JvmStatic
|
||||||
|
fun newSingleImageIntent(context: Context, url: String): Intent {
|
||||||
val intent = Intent(context, ViewMediaActivity::class.java)
|
val intent = Intent(context, ViewMediaActivity::class.java)
|
||||||
intent.putExtra(EXTRA_AVATAR_URL, url)
|
intent.putExtra(EXTRA_SINGLE_IMAGE_URL, url)
|
||||||
return intent
|
return intent
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,6 +92,7 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
|
||||||
|
|
||||||
private var attachments: ArrayList<AttachmentViewData>? = null
|
private var attachments: ArrayList<AttachmentViewData>? = null
|
||||||
private val toolbarVisibilityListeners = mutableListOf<ToolbarVisibilityListener>()
|
private val toolbarVisibilityListeners = mutableListOf<ToolbarVisibilityListener>()
|
||||||
|
private var imageUrl: String? = null
|
||||||
|
|
||||||
fun addToolbarVisibilityListener(listener: ToolbarVisibilityListener): Function0<Boolean> {
|
fun addToolbarVisibilityListener(listener: ToolbarVisibilityListener): Function0<Boolean> {
|
||||||
this.toolbarVisibilityListeners.add(listener)
|
this.toolbarVisibilityListeners.add(listener)
|
||||||
|
@ -117,10 +119,10 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
|
||||||
ImagePagerAdapter(this, realAttachs, initialPosition)
|
ImagePagerAdapter(this, realAttachs, initialPosition)
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
val avatarUrl = intent.getStringExtra(EXTRA_AVATAR_URL)
|
imageUrl = intent.getStringExtra(EXTRA_SINGLE_IMAGE_URL)
|
||||||
?: throw IllegalArgumentException("attachment list or avatar url has to be set")
|
?: throw IllegalArgumentException("attachment list or image url has to be set")
|
||||||
|
|
||||||
AvatarImagePagerAdapter(this, avatarUrl)
|
SingleImagePagerAdapter(this, imageUrl!!)
|
||||||
}
|
}
|
||||||
|
|
||||||
viewPager.adapter = adapter
|
viewPager.adapter = adapter
|
||||||
|
@ -161,11 +163,10 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||||
if (attachments != null) {
|
menuInflater.inflate(R.menu.view_media_toolbar, menu)
|
||||||
menuInflater.inflate(R.menu.view_media_toolbar, menu)
|
// We don't support 'open status' from single image views
|
||||||
return true
|
menu?.findItem(R.id.action_open_status)?.isVisible = (attachments != null)
|
||||||
}
|
return true
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPrepareOptionsMenu(menu: Menu?): Boolean {
|
override fun onPrepareOptionsMenu(menu: Menu?): Boolean {
|
||||||
|
@ -213,7 +214,7 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun downloadMedia() {
|
private fun downloadMedia() {
|
||||||
val url = attachments!![viewPager.currentItem].attachment.url
|
val url = imageUrl ?: attachments!![viewPager.currentItem].attachment.url
|
||||||
val filename = Uri.parse(url).lastPathSegment
|
val filename = Uri.parse(url).lastPathSegment
|
||||||
Toast.makeText(applicationContext, resources.getString(R.string.download_image, filename), Toast.LENGTH_SHORT).show()
|
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() {
|
private fun copyLink() {
|
||||||
|
val url = imageUrl ?: attachments!![viewPager.currentItem].attachment.url
|
||||||
val clipboard = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
|
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() {
|
private fun shareMedia() {
|
||||||
|
@ -251,13 +253,17 @@ class ViewMediaActivity : BaseActivity(), ViewImageFragment.PhotoActionsListener
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val attachment = attachments!![viewPager.currentItem].attachment
|
if (imageUrl != null) {
|
||||||
when (attachment.type) {
|
shareImage(directory, imageUrl!!)
|
||||||
Attachment.Type.IMAGE -> shareImage(directory, attachment.url)
|
} else {
|
||||||
Attachment.Type.AUDIO,
|
val attachment = attachments!![viewPager.currentItem].attachment
|
||||||
Attachment.Type.VIDEO,
|
when (attachment.type) {
|
||||||
Attachment.Type.GIFV -> shareMediaFile(directory, attachment.url)
|
Attachment.Type.IMAGE -> shareImage(directory, attachment.url)
|
||||||
else -> Log.e(TAG, "Unknown media format for sharing.")
|
Attachment.Type.AUDIO,
|
||||||
|
Attachment.Type.VIDEO,
|
||||||
|
Attachment.Type.GIFV -> shareMediaFile(directory, attachment.url)
|
||||||
|
else -> Log.e(TAG, "Unknown media format for sharing.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ import com.bumptech.glide.load.resource.bitmap.CenterCrop;
|
||||||
import com.bumptech.glide.load.resource.bitmap.GranularRoundedCorners;
|
import com.bumptech.glide.load.resource.bitmap.GranularRoundedCorners;
|
||||||
import com.google.android.material.button.MaterialButton;
|
import com.google.android.material.button.MaterialButton;
|
||||||
import com.keylesspalace.tusky.R;
|
import com.keylesspalace.tusky.R;
|
||||||
|
import com.keylesspalace.tusky.ViewMediaActivity;
|
||||||
import com.keylesspalace.tusky.entity.Attachment;
|
import com.keylesspalace.tusky.entity.Attachment;
|
||||||
import com.keylesspalace.tusky.entity.Attachment.Focus;
|
import com.keylesspalace.tusky.entity.Attachment.Focus;
|
||||||
import com.keylesspalace.tusky.entity.Attachment.MetaData;
|
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);
|
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);
|
cardView.setClipToOutline(true);
|
||||||
} else {
|
} else {
|
||||||
cardView.setVisibility(View.GONE);
|
cardView.setVisibility(View.GONE);
|
||||||
|
|
|
@ -27,7 +27,8 @@ data class Card(
|
||||||
val type: String,
|
val type: String,
|
||||||
val width: Int,
|
val width: Int,
|
||||||
val height: Int,
|
val height: Int,
|
||||||
val blurhash: String?
|
val blurhash: String?,
|
||||||
|
val embed_url: String?
|
||||||
) {
|
) {
|
||||||
|
|
||||||
override fun hashCode(): Int {
|
override fun hashCode(): Int {
|
||||||
|
@ -42,4 +43,7 @@ data class Card(
|
||||||
return account?.url == this.url
|
return account?.url == this.url
|
||||||
}
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val TYPE_PHOTO = "photo"
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,9 +99,9 @@ class ViewImageFragment : ViewMediaFragment() {
|
||||||
url = attachment.url
|
url = attachment.url
|
||||||
description = attachment.description
|
description = attachment.description
|
||||||
} else {
|
} else {
|
||||||
url = arguments.getString(ARG_AVATAR_URL)
|
url = arguments.getString(ARG_SINGLE_IMAGE_URL)
|
||||||
if (url == null) {
|
if (url == null) {
|
||||||
throw IllegalArgumentException("attachment or avatar url has to be set")
|
throw IllegalArgumentException("attachment or image url has to be set")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ abstract class ViewMediaFragment : BaseFragment() {
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
protected val ARG_ATTACHMENT = "attach"
|
protected val ARG_ATTACHMENT = "attach"
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
protected val ARG_AVATAR_URL = "avatarUrl"
|
protected val ARG_SINGLE_IMAGE_URL = "singleImageUrl"
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun newInstance(attachment: Attachment, shouldStartPostponedTransition: Boolean): ViewMediaFragment {
|
fun newInstance(attachment: Attachment, shouldStartPostponedTransition: Boolean): ViewMediaFragment {
|
||||||
|
@ -62,10 +62,10 @@ abstract class ViewMediaFragment : BaseFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun newAvatarInstance(avatarUrl: String): ViewMediaFragment {
|
fun newSingleImageInstance(imageUrl: String): ViewMediaFragment {
|
||||||
val arguments = Bundle(2)
|
val arguments = Bundle(2)
|
||||||
val fragment = ViewImageFragment()
|
val fragment = ViewImageFragment()
|
||||||
arguments.putString(ARG_AVATAR_URL, avatarUrl)
|
arguments.putString(ARG_SINGLE_IMAGE_URL, imageUrl)
|
||||||
arguments.putBoolean(ARG_START_POSTPONED_TRANSITION, true)
|
arguments.putBoolean(ARG_START_POSTPONED_TRANSITION, true)
|
||||||
|
|
||||||
fragment.arguments = arguments
|
fragment.arguments = arguments
|
||||||
|
|
|
@ -5,14 +5,14 @@ import androidx.fragment.app.FragmentActivity
|
||||||
import com.keylesspalace.tusky.ViewMediaAdapter
|
import com.keylesspalace.tusky.ViewMediaAdapter
|
||||||
import com.keylesspalace.tusky.fragment.ViewMediaFragment
|
import com.keylesspalace.tusky.fragment.ViewMediaFragment
|
||||||
|
|
||||||
class AvatarImagePagerAdapter(
|
class SingleImagePagerAdapter(
|
||||||
activity: FragmentActivity,
|
activity: FragmentActivity,
|
||||||
private val avatarUrl: String
|
private val imageUrl: String
|
||||||
) : ViewMediaAdapter(activity) {
|
) : ViewMediaAdapter(activity) {
|
||||||
|
|
||||||
override fun createFragment(position: Int): Fragment {
|
override fun createFragment(position: Int): Fragment {
|
||||||
return if (position == 0) {
|
return if (position == 0) {
|
||||||
ViewMediaFragment.newAvatarInstance(avatarUrl)
|
ViewMediaFragment.newSingleImageInstance(imageUrl)
|
||||||
} else {
|
} else {
|
||||||
throw IllegalStateException()
|
throw IllegalStateException()
|
||||||
}
|
}
|
Loading…
Reference in a new issue