feat: 选图
This commit is contained in:
parent
f552e48c77
commit
d63d7a5a13
|
|
@ -54,6 +54,9 @@ import com.google.accompanist.navigation.material.ModalBottomSheetLayout
|
|||
import com.google.accompanist.systemuicontroller.SystemUiController
|
||||
import com.huanchengfly.tieba.post.api.retrofit.exception.getErrorMessage
|
||||
import com.huanchengfly.tieba.post.arch.BaseComposeActivity
|
||||
import com.huanchengfly.tieba.post.arch.GlobalEvent
|
||||
import com.huanchengfly.tieba.post.arch.emitGlobalEvent
|
||||
import com.huanchengfly.tieba.post.arch.onGlobalEvent
|
||||
import com.huanchengfly.tieba.post.services.NotifyJobService
|
||||
import com.huanchengfly.tieba.post.ui.common.theme.compose.ExtendedTheme
|
||||
import com.huanchengfly.tieba.post.ui.page.NavGraphs
|
||||
|
|
@ -69,9 +72,11 @@ import com.huanchengfly.tieba.post.utils.AccountUtil
|
|||
import com.huanchengfly.tieba.post.utils.ClientUtils
|
||||
import com.huanchengfly.tieba.post.utils.JobServiceUtil
|
||||
import com.huanchengfly.tieba.post.utils.PermissionUtils
|
||||
import com.huanchengfly.tieba.post.utils.PickMediasRequest
|
||||
import com.huanchengfly.tieba.post.utils.TiebaUtil
|
||||
import com.huanchengfly.tieba.post.utils.isIgnoringBatteryOptimizations
|
||||
import com.huanchengfly.tieba.post.utils.newIntentFilter
|
||||
import com.huanchengfly.tieba.post.utils.registerPickMediasLauncher
|
||||
import com.huanchengfly.tieba.post.utils.requestIgnoreBatteryOptimizations
|
||||
import com.huanchengfly.tieba.post.utils.requestPermission
|
||||
import com.ramcosta.composedestinations.DestinationsNavHost
|
||||
|
|
@ -124,6 +129,11 @@ class MainActivityV2 : BaseComposeActivity() {
|
|||
private val notificationCountFlow: MutableSharedFlow<Int> =
|
||||
MutableSharedFlow(replay = 1, onBufferOverflow = BufferOverflow.DROP_OLDEST)
|
||||
|
||||
private val pickMediasLauncher =
|
||||
registerPickMediasLauncher {
|
||||
emitGlobalEvent(GlobalEvent.SelectedImages(it.id, it.uris))
|
||||
}
|
||||
|
||||
private val devicePostureFlow: StateFlow<DevicePosture> by lazy {
|
||||
WindowInfoTracker.getOrCreate(this)
|
||||
.windowLayoutInfo(this)
|
||||
|
|
@ -251,6 +261,11 @@ class MainActivityV2 : BaseComposeActivity() {
|
|||
okSignAlertDialogState.show()
|
||||
}
|
||||
}
|
||||
onGlobalEvent<GlobalEvent.StartSelectImages> {
|
||||
pickMediasLauncher.launch(
|
||||
PickMediasRequest(it.id, it.maxCount, it.mediaType)
|
||||
)
|
||||
}
|
||||
CompositionLocalProvider(
|
||||
LocalNotificationCountFlow provides notificationCountFlow,
|
||||
LocalDevicePosture provides devicePostureFlow.collectAsState(),
|
||||
|
|
|
|||
|
|
@ -99,9 +99,9 @@ class ReplyActivity : BaseActivity(), View.OnClickListener,
|
|||
get() = false
|
||||
|
||||
@JvmField
|
||||
val pickMediasLauncher = registerPickMediasLauncher {
|
||||
val pickMediasLauncher = registerPickMediasLauncher { (_, uris) ->
|
||||
val photoInfoBeans = insertPhotoAdapter.getFileList().toMutableList()
|
||||
for (uri in it) {
|
||||
for (uri in uris) {
|
||||
photoInfoBeans.add(PhotoInfoBean(this, uri))
|
||||
}
|
||||
insertPhotoAdapter.setFileList(photoInfoBeans)
|
||||
|
|
|
|||
|
|
@ -97,9 +97,9 @@ class TranslucentThemeActivity : BaseActivity(), View.OnClickListener, OnSeekBar
|
|||
@BindView(R.id.color_theme)
|
||||
lateinit var colorTheme: ViewGroup
|
||||
|
||||
private val selectImageLauncher = registerPickMediasLauncher {
|
||||
if (it.isNotEmpty()) {
|
||||
val sourceUri = it[0]
|
||||
private val selectImageLauncher = registerPickMediasLauncher { (_, uris) ->
|
||||
if (uris.isNotEmpty()) {
|
||||
val sourceUri = uris[0]
|
||||
launchUCrop(sourceUri)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,10 +70,14 @@ class InsertPhotoAdapter(private val mContext: Context) : RecyclerView.Adapter<M
|
|||
holder.setItemOnClickListener {
|
||||
if (mContext is AppCompatActivity && mContext is PickMediasLauncherProvider) {
|
||||
askPermission {
|
||||
mContext.getPickMediasLauncher().launch(PickMediasRequest().apply {
|
||||
maxItems = 10 - fileList.size
|
||||
mediaType = PickMediasRequest.ImageOnly
|
||||
})
|
||||
mContext.getPickMediasLauncher()
|
||||
.launch(
|
||||
PickMediasRequest(
|
||||
"",
|
||||
10 - fileList.size,
|
||||
mediaType = PickMediasRequest.ImageOnly
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,35 +1,60 @@
|
|||
package com.huanchengfly.tieba.post.arch
|
||||
|
||||
import android.net.Uri
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import com.huanchengfly.tieba.post.utils.PickMediasRequest
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.cancellable
|
||||
import kotlinx.coroutines.flow.filterIsInstance
|
||||
import kotlinx.coroutines.flow.receiveAsFlow
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
sealed interface GlobalEvent {
|
||||
object AccountSwitched : GlobalEvent
|
||||
|
||||
data class StartSelectImages(
|
||||
val id: String,
|
||||
val maxCount: Int,
|
||||
val mediaType: PickMediasRequest.MediaType
|
||||
) : GlobalEvent
|
||||
|
||||
data class SelectedImages(
|
||||
val id: String,
|
||||
val images: List<Uri>
|
||||
) : GlobalEvent
|
||||
}
|
||||
|
||||
private val mutableGlobalEventFlow by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) { MutableSharedFlow<GlobalEvent>() }
|
||||
private val globalEventChannel: Channel<GlobalEvent> = Channel()
|
||||
|
||||
val GlobalEventFlow by lazy { mutableGlobalEventFlow }
|
||||
val GlobalEventFlow: Flow<GlobalEvent>
|
||||
get() = globalEventChannel.receiveAsFlow()
|
||||
|
||||
fun emitGlobalEvent(event: GlobalEvent) {
|
||||
mutableGlobalEventFlow.tryEmit(event)
|
||||
globalEventChannel.trySend(event)
|
||||
}
|
||||
|
||||
inline fun <reified Event : GlobalEvent> CoroutineScope.onGlobalEvent(
|
||||
@Composable
|
||||
inline fun <reified Event : GlobalEvent> onGlobalEvent(
|
||||
coroutineScope: CoroutineScope = rememberCoroutineScope(),
|
||||
noinline listener: suspend (Event) -> Unit
|
||||
): Job {
|
||||
return launch {
|
||||
GlobalEventFlow
|
||||
.filterIsInstance<Event>()
|
||||
.cancellable()
|
||||
.collect {
|
||||
launch {
|
||||
listener(it)
|
||||
) {
|
||||
DisposableEffect(listener) {
|
||||
val job = coroutineScope.launch {
|
||||
GlobalEventFlow
|
||||
.filterIsInstance<Event>()
|
||||
.cancellable()
|
||||
.collect {
|
||||
launch {
|
||||
listener(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
onDispose {
|
||||
job.cancel()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -69,9 +69,9 @@ class EditProfileActivity : BaseActivity() {
|
|||
}
|
||||
|
||||
private val pickMediasLauncher =
|
||||
registerPickMediasLauncher {
|
||||
if (it.isNotEmpty()) {
|
||||
val sourceUri = it[0]
|
||||
registerPickMediasLauncher { (_, uris) ->
|
||||
if (uris.isNotEmpty()) {
|
||||
val sourceUri = uris[0]
|
||||
Glide.with(this)
|
||||
.asFile()
|
||||
.load(sourceUri)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
package com.huanchengfly.tieba.post.utils
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
|
|
@ -16,9 +17,9 @@ import com.zhihu.matisse.MimeType
|
|||
import com.zhihu.matisse.engine.impl.GlideEngine
|
||||
import com.zhihu.matisse.ui.MatisseActivity
|
||||
|
||||
fun AppCompatActivity.registerPickMediasLauncher(callback: (List<Uri>) -> Unit): ActivityResultLauncher<PickMediasRequest> {
|
||||
fun AppCompatActivity.registerPickMediasLauncher(callback: (PickMediasResult) -> Unit): ActivityResultLauncher<PickMediasRequest> {
|
||||
return registerForActivityResult(
|
||||
PickMediasContract()
|
||||
PickMediasContract
|
||||
) {
|
||||
callback(it)
|
||||
}
|
||||
|
|
@ -55,15 +56,17 @@ private fun Intent.getClipDataUris(): List<Uri> {
|
|||
return ArrayList(resultSet)
|
||||
}
|
||||
|
||||
@SuppressLint("NewApi")
|
||||
private fun getMaxItems() = if (isPhotoPickerAvailable()) {
|
||||
MediaStore.getPickImagesMaxLimit()
|
||||
} else {
|
||||
Integer.MAX_VALUE
|
||||
}
|
||||
|
||||
class PickMediasRequest(
|
||||
var maxItems: Int = 1,
|
||||
var mediaType: MediaType = ImageAndVideo
|
||||
data class PickMediasRequest(
|
||||
val id: String = "",
|
||||
val maxItems: Int = 1,
|
||||
val mediaType: MediaType = ImageAndVideo
|
||||
) {
|
||||
sealed interface MediaType
|
||||
|
||||
|
|
@ -73,7 +76,7 @@ class PickMediasRequest(
|
|||
|
||||
object ImageAndVideo : MediaType
|
||||
|
||||
class SingleMimeType(val mimeType: String) : MediaType
|
||||
data class SingleMimeType(val mimeType: String) : MediaType
|
||||
|
||||
companion object {
|
||||
internal fun getMimeType(input: MediaType): String? {
|
||||
|
|
@ -87,8 +90,19 @@ class PickMediasRequest(
|
|||
}
|
||||
}
|
||||
|
||||
class PickMediasContract : ActivityResultContract<PickMediasRequest, List<Uri>>() {
|
||||
data class PickMediasResult(
|
||||
val id: String,
|
||||
val uris: List<Uri>
|
||||
)
|
||||
|
||||
object PickMediasContract : ActivityResultContract<PickMediasRequest, PickMediasResult>() {
|
||||
private var curRequestId: String? = null
|
||||
|
||||
val hasCurrentRequest: Boolean
|
||||
get() = curRequestId != null
|
||||
|
||||
override fun createIntent(context: Context, input: PickMediasRequest): Intent {
|
||||
curRequestId = input.id
|
||||
if (isPhotoPickerAvailable()) {
|
||||
return Intent(MediaStore.ACTION_PICK_IMAGES).apply {
|
||||
type = PickMediasRequest.getMimeType(input.mediaType)
|
||||
|
|
@ -112,13 +126,15 @@ class PickMediasContract : ActivityResultContract<PickMediasRequest, List<Uri>>(
|
|||
return Intent(context, MatisseActivity::class.java)
|
||||
}
|
||||
|
||||
override fun parseResult(resultCode: Int, intent: Intent?): List<Uri> {
|
||||
override fun parseResult(resultCode: Int, intent: Intent?): PickMediasResult {
|
||||
val id = curRequestId + ""
|
||||
curRequestId = null
|
||||
if (resultCode != Activity.RESULT_OK || intent == null) {
|
||||
return emptyList()
|
||||
return PickMediasResult(id, emptyList())
|
||||
}
|
||||
if (isPhotoPickerAvailable()) {
|
||||
return intent.getClipDataUris()
|
||||
return PickMediasResult(id, intent.getClipDataUris())
|
||||
}
|
||||
return Matisse.obtainResult(intent)
|
||||
return PickMediasResult(id, Matisse.obtainResult(intent))
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue