Skip to content

Commit

Permalink
Image picker (#5272)
Browse files Browse the repository at this point in the history
* Image picker

* Code clean

* Check fragment state

---------

Co-authored-by: Crossle Song <[email protected]>
  • Loading branch information
SeniorZhai and crossle authored Jan 23, 2025
1 parent 436fb78 commit 5491cec
Show file tree
Hide file tree
Showing 7 changed files with 61 additions and 171 deletions.
2 changes: 0 additions & 2 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,6 @@
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES"/>
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />
<uses-permission android:name="android.permission.READ_MEDIA_AUDIO" />
<uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_REMOTE_MESSAGING" />
Expand Down
19 changes: 17 additions & 2 deletions app/src/main/java/one/mixin/android/extension/UrlExtension.kt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ import one.mixin.android.vo.ShareCategory
import one.mixin.android.vo.User
import one.mixin.android.vo.generateConversationId
import one.mixin.android.vo.getShareCategory
import one.mixin.android.widget.gallery.MimeType
import timber.log.Timber

fun String.openAsUrlOrWeb(
Expand Down Expand Up @@ -169,7 +170,7 @@ User-agent: ${WebView(context).settings.userAgentString}
ConfirmBottomFragment.show(MixinApplication.appContext, supportFragmentManager, this)
} else if (isUserScheme() || isAppScheme()) {
checkUserOrApp(context, supportFragmentManager, scope)
} else if(isConversationScheme()) {
} else if (isConversationScheme()) {
checkConversation(context, scope) {
if (isMixinUrl() || isExternalScheme(context) || isExternalTransferUrl()) {
LinkBottomSheetDialogFragment.newInstance(this)
Expand Down Expand Up @@ -255,7 +256,7 @@ fun String.checkUserOrApp(
fun String.checkConversation(
context: Context,
scope: CoroutineScope,
elseAction: () -> Unit
elseAction: () -> Unit,
) {
val uri = Uri.parse(this)
val segments = uri.pathSegments
Expand Down Expand Up @@ -413,8 +414,22 @@ fun Uri.getCapturedImage(contentResolver: ContentResolver): Bitmap =
@Suppress("DEPRECATION")
MediaStore.Images.Media.getBitmap(contentResolver, this)
}

else -> {
val source = ImageDecoder.createSource(contentResolver, this)
ImageDecoder.decodeBitmap(source)
}
}

fun Uri.isVideo(context: Context) : Boolean{
val mimeType = context.contentResolver.getType(this)
return mimeType.equals(MimeType.MPEG.toString())
|| mimeType.equals(MimeType.MP4.toString())
|| mimeType.equals(MimeType.QUICKTIME.toString())
|| mimeType.equals(MimeType.THREEGPP.toString())
|| mimeType.equals(MimeType.THREEGPP2.toString())
|| mimeType.equals(MimeType.MKV.toString())
|| mimeType.equals(MimeType.WEBM.toString())
|| mimeType.equals(MimeType.TS.toString())
|| mimeType.equals(MimeType.AVI.toString())
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import android.widget.PopupWindow
import android.widget.TextView
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.ActivityResultRegistry
import androidx.activity.result.PickVisualMediaRequest
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.VisibleForTesting
import androidx.appcompat.app.AlertDialog
Expand Down Expand Up @@ -121,6 +122,7 @@ import one.mixin.android.extension.isAuto
import one.mixin.android.extension.isBluetoothHeadsetOrWiredHeadset
import one.mixin.android.extension.isImageSupport
import one.mixin.android.extension.isStickerSupport
import one.mixin.android.extension.isVideo
import one.mixin.android.extension.lateOneHours
import one.mixin.android.extension.networkConnected
import one.mixin.android.extension.nowInUtc
Expand Down Expand Up @@ -1043,6 +1045,7 @@ class ConversationFragment() :
private lateinit var getMediaResult: ActivityResultLauncher<MediaPagerActivity.MediaParam>
private lateinit var getShareMediaResult: ActivityResultLauncher<Pair<String, Boolean>>
private lateinit var pickFileLauncher: ActivityResultLauncher<Array<String>>
private lateinit var pickMedia: ActivityResultLauncher<PickVisualMediaRequest>
lateinit var getEditorResult: ActivityResultLauncher<Pair<Uri, String?>>

override fun onAttach(context: Context) {
Expand All @@ -1060,6 +1063,15 @@ class ConversationFragment() :
handleFile(uri)
}
}
pickMedia = registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri ->
uri?.let {
if (it.isVideo(requireContext())) {
showPreview(it, isVideo = true) { videoUri, start, end -> sendVideoMessage(videoUri, start, end) }
} else {
sendImageMessage(it)
}
}
}
}

override fun onCreate(savedInstanceState: Bundle?) {
Expand Down Expand Up @@ -2531,94 +2543,9 @@ class ConversationFragment() :
}

private fun clickGallery() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE &&
arrayOf(Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED, Manifest.permission.READ_MEDIA_IMAGES, Manifest.permission.READ_MEDIA_VIDEO).any { RxPermissions(requireActivity()).isGranted(it) }
) {
initGalleryLayout()
return
if (isAdded) {
pickMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageAndVideo))
}
RxPermissions(requireActivity())
.request(
*if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
arrayOf(Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED, Manifest.permission.READ_MEDIA_IMAGES, Manifest.permission.READ_MEDIA_VIDEO)
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
arrayOf(Manifest.permission.READ_MEDIA_IMAGES, Manifest.permission.READ_MEDIA_VIDEO)
} else {
arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE)
},
)
.autoDispose(stopScope)
.subscribe(
{ granted ->
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE &&
arrayOf(Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED, Manifest.permission.READ_MEDIA_IMAGES, Manifest.permission.READ_MEDIA_VIDEO).any { RxPermissions(requireActivity()).isGranted(it) }
) {
initGalleryLayout()
} else if (granted) {
initGalleryLayout()
} else {
context?.openPermissionSetting()
}
},
{
},
)
}

private fun initGalleryLayout() {
val galleryAlbumFragment = GalleryAlbumFragment.newInstance()
galleryAlbumFragment.callback =
object : GalleryCallback {
override fun onItemClick(
pos: Int,
item: Item,
send: Boolean,
) {
val uri = item.uri
if (item.isVideo) {
if (send) {
sendVideoMessage(uri, 0f, 1f)
} else {
showPreview(uri, getString(R.string.Send), true) { uri, start, end -> sendVideoMessage(uri, start, end) }
}
} else if (item.isGif || item.isWebp) {
if (send) {
sendImageMessage(uri)
} else {
showPreview(uri, getString(R.string.Send), false) { uri, _, _ -> sendImageMessage(uri) }
}
} else {
if (send) {
sendImageMessage(uri)
} else {
getEditorResult.launch(Pair(uri, getString(R.string.Send)))
}
}
releaseChatControl(FLING_DOWN)
}

override fun onCameraClick() {
openCamera()
}
}
galleryAlbumFragment.rvCallback =
object : DraggableRecyclerView.Callback {
override fun onScroll(dis: Float) {
val currentContainer = binding.chatControl.getDraggableContainer()
if (currentContainer != null) {
dragChatControl(dis)
}
}

override fun onRelease(fling: Int) {
releaseChatControl(fling)
}
}
activity?.replaceFragment(
galleryAlbumFragment,
R.id.gallery_container,
GalleryAlbumFragment.TAG,
)
}

private fun initMenuLayout(isSelfCreatedBot: Boolean = false) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,6 @@ class GalleryAlbumFragment : Fragment(R.layout.fragment_gallery_album), AlbumCol
override fun onResume() {
super.onResume()
binding.permissionTv.isVisible = when {
Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE && RxPermissions(requireActivity()).isGranted(Manifest.permission.READ_MEDIA_IMAGES) && RxPermissions(requireActivity()).isGranted(Manifest.permission.READ_MEDIA_VIDEO) -> false
Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE && RxPermissions(requireActivity()).isGranted(Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED) -> true
else -> false
}
Expand Down Expand Up @@ -144,12 +143,10 @@ class GalleryAlbumFragment : Fragment(R.layout.fragment_gallery_album), AlbumCol
}
viewBinding.select.setOnClickListener {
RxPermissions(requireActivity()).request(
*arrayOf(Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED, Manifest.permission.READ_MEDIA_IMAGES, Manifest.permission.READ_MEDIA_VIDEO)
*arrayOf(Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED)
).autoDispose(stopScope).subscribe(
{ granted ->
if (RxPermissions(requireActivity()).isGranted(Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED)) {
albumAdapter.getFragment(binding.viewPager.currentItem)?.reloadAlbum()
} else if (granted) {
if (granted) {
albumAdapter.getFragment(binding.viewPager.currentItem)?.reloadAlbum()
} else {
requireActivity().openPermissionSetting()
Expand Down Expand Up @@ -201,7 +198,7 @@ class GalleryAlbumFragment : Fragment(R.layout.fragment_gallery_album), AlbumCol
val album = Album.valueOf(cursor)
albums.add(album)
}
if (albums.isNullOrEmpty()) return@post
if (albums.isEmpty()) return@post

if (albumTl.tabCount == 0) {
albums.forEach { album ->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,20 +212,22 @@ class PreviewDialogFragment : DialogFragment(), VideoTimelinePlayView.VideoTimel
okText: String? = null,
action: (Uri, Float, Float) -> Unit,
) {
try {
super.showNow(
fragmentManager,
if (isVideo) {
"PreviewVideoDialogFragment"
} else {
"PreviewDialogFragment"
},
)
} catch (ignored: IllegalStateException) {
if (fragmentManager.isDestroyed) {
return
}

this.uri = uri
this.okText = okText
this.action = action

val tag = if (isVideo) "PreviewVideoDialogFragment" else "PreviewDialogFragment"
try {
if (!isAdded && !isStateSaved) {
show(fragmentManager, tag)
}
} catch (e: IllegalStateException) {
Timber.e(e)
}
}

private val videoListener =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ import one.mixin.android.util.rxpermission.RxPermissions
import one.mixin.android.util.viewBinding
import one.mixin.android.vo.Sticker
import one.mixin.android.widget.lottie.RLottieImageView
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.PickVisualMediaRequest
import androidx.activity.result.contract.ActivityResultContracts

@AndroidEntryPoint
class StickerManagementFragment : BaseFragment() {
Expand All @@ -67,6 +70,16 @@ class StickerManagementFragment : BaseFragment() {
StickerAdapter(stickers)
}

private val pickMedia = registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri ->
uri?.let {
requireActivity().addFragment(
this@StickerManagementFragment,
StickerAddFragment.newInstance(it.toString(), true),
StickerAddFragment.TAG,
)
}
}

override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
Expand Down Expand Up @@ -109,41 +122,7 @@ class StickerManagementFragment : BaseFragment() {
stickerAdapter.setOnStickerListener(
object : StickerListener {
override fun onAddClick() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE &&
arrayOf(Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED, Manifest.permission.READ_MEDIA_IMAGES).any { RxPermissions(requireActivity()).isGranted(it) }
) {
openGalleryFromSticker()
return
}
RxPermissions(requireActivity())
.request(
*(if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
mutableListOf(Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED, Manifest.permission.READ_MEDIA_IMAGES)
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
mutableListOf(Manifest.permission.READ_MEDIA_IMAGES)
} else {
mutableListOf(Manifest.permission.READ_EXTERNAL_STORAGE)
}).apply {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) add(Manifest.permission.WRITE_EXTERNAL_STORAGE)
}.toTypedArray()
)
.autoDispose(stopScope)
.subscribe(
{ granted ->
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE &&
arrayOf(Manifest.permission.READ_MEDIA_VISUAL_USER_SELECTED, Manifest.permission.READ_MEDIA_IMAGES).any { RxPermissions(requireActivity()).isGranted(it) }
) {
openGalleryFromSticker()
} else if (granted) {
openGalleryFromSticker()
} else {
context?.openPermissionSetting()
}
},
{
reportException(it)
},
)
pickMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly))
}

override fun onDelete() {
Expand Down Expand Up @@ -186,15 +165,6 @@ class StickerManagementFragment : BaseFragment() {
data: Intent?,
) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == REQUEST_GALLERY && resultCode == Activity.RESULT_OK) {
data?.data?.let {
requireActivity().addFragment(
this@StickerManagementFragment,
StickerAddFragment.newInstance(it.toString(), true),
StickerAddFragment.TAG,
)
}
}
}

@SuppressLint("NotifyDataSetChanged")
Expand Down
21 changes: 1 addition & 20 deletions app/src/main/java/one/mixin/android/widget/ChatControlView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,6 @@ class ChatControlView : LinearLayout, ActionMode.Callback {
EXPANDED_KEYBOARD, // + ☺ i
EXPANDED_MENU, // x ☺ i
EXPANDED_STICKER, // + k i
EXPANDED_GALLERY, // + ☺ i[√]
COLLAPSED, // + ☺ i
}

Expand Down Expand Up @@ -155,14 +154,6 @@ class ChatControlView : LinearLayout, ActionMode.Callback {
stickerContainer.isVisible = true
galleryContainer.isVisible = false
}
STATUS.EXPANDED_GALLERY -> {
menuStatus = MenuStatus.COLLAPSED
stickerStatus = StickerStatus.STICKER
binding.chatImgIv.setImageResource(R.drawable.ic_chat_img_checked)
menuContainer.isVisible = false
stickerContainer.isVisible = false
galleryContainer.isVisible = true
}
STATUS.COLLAPSED -> {
menuStatus = MenuStatus.COLLAPSED
stickerStatus = StickerStatus.STICKER
Expand Down Expand Up @@ -373,9 +364,6 @@ class ChatControlView : LinearLayout, ActionMode.Callback {
} else if (controlState == STATUS.EXPANDED_STICKER && !stickerContainer.isVisible) {
controlState = STATUS.COLLAPSED
inputLayout.closeInputArea(binding.chatEt)
} else if (controlState == STATUS.EXPANDED_GALLERY && !galleryContainer.isVisible) {
controlState = STATUS.COLLAPSED
inputLayout.closeInputArea(binding.chatEt)
}
}
}
Expand Down Expand Up @@ -759,14 +747,7 @@ class ChatControlView : LinearLayout, ActionMode.Callback {
}

private fun clickGallery() {
if (controlState == STATUS.EXPANDED_GALLERY) {
controlState = STATUS.COLLAPSED
inputLayout.closeInputArea(binding.chatEt)
} else {
controlState = STATUS.EXPANDED_GALLERY
inputLayout.openInputArea(binding.chatEt)
callback.onGalleryClick()
}
callback.onGalleryClick()
remainFocusable()
}

Expand Down

0 comments on commit 5491cec

Please sign in to comment.