Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

🌁 feat: Preview Media #186

Merged
merged 4 commits into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion MultipleImagePicker.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ Pod::Spec.new do |s|
}


s.dependency "HXPhotoPicker/Picker/Lite", "4.2.3"
s.dependency "HXPhotoPicker/Picker", "4.2.3"
s.dependency "HXPhotoPicker/Editor/Lite", "4.2.3"

s.pod_target_xcconfig = {
Expand Down
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ React Native Multiple Image Picker **(RNMIP)** enables application to pick image

## Documentation 📚


## Features 🔥

| 🤩 | ![Logo][Logo] |
Expand Down Expand Up @@ -78,10 +77,10 @@ const onPicker = async () => {
- [x] Multiple Crop Image.
- [x] Multiple Preview Image.
- [x] Dynamic Theme.
- [x] Dynamic Language.
- [ ] Open Camera Controller.
- [x] Dynamic Language
- [x] Open Crop Controller.
- [ ] Open Preview Controller.
- [x] Open Preview Controller.
- [ ] Open Camera Controller.

## Sponsor & Support ☕️

Expand Down
3 changes: 3 additions & 0 deletions android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,9 @@ dependencies {
// simple camerax library (Not necessary)
implementation 'io.github.lucksiege:camerax:v3.11.2'

// exoplayer
implementation "com.google.android.exoplayer:exoplayer:2.19.1"


implementation "com.facebook.react:react-native:+"

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package com.margelo.nitro.multipleimagepicker

import android.content.Context
import android.net.Uri
import android.view.View
import com.google.android.exoplayer2.ExoPlayer
import com.google.android.exoplayer2.MediaItem
import com.google.android.exoplayer2.PlaybackException
import com.google.android.exoplayer2.Player
import com.google.android.exoplayer2.ui.StyledPlayerView
import com.luck.picture.lib.config.PictureMimeType
import com.luck.picture.lib.config.SelectorProviders
import com.luck.picture.lib.engine.VideoPlayerEngine
import com.luck.picture.lib.entity.LocalMedia
import com.luck.picture.lib.interfaces.OnPlayerListener
import java.io.File
import java.util.concurrent.CopyOnWriteArrayList


class ExoPlayerEngine : VideoPlayerEngine<StyledPlayerView> {
private val listeners = CopyOnWriteArrayList<OnPlayerListener>()

override fun onCreateVideoPlayer(context: Context): View {
val exoPlayer = StyledPlayerView(context)
exoPlayer.useController = true
return exoPlayer
}

override fun onStarPlayer(exoPlayer: StyledPlayerView, media: LocalMedia) {
val player = exoPlayer.player
if (player != null) {
val mediaItem: MediaItem
val path = media.availablePath
mediaItem = if (PictureMimeType.isContent(path)) {
MediaItem.fromUri(Uri.parse(path))
} else if (PictureMimeType.isHasHttp(path)) {
MediaItem.fromUri(path)
} else {
MediaItem.fromUri(Uri.fromFile(File(path)))
}
val config = SelectorProviders.getInstance().selectorConfig
player.repeatMode =
if (config.isLoopAutoPlay) Player.REPEAT_MODE_ALL else Player.REPEAT_MODE_OFF
player.setMediaItem(mediaItem)
player.prepare()
player.play()
}
}

override fun onResume(exoPlayer: StyledPlayerView) {
val player = exoPlayer.player
player?.play()
}

override fun onPause(exoPlayer: StyledPlayerView) {
val player = exoPlayer.player
player?.pause()
}

override fun isPlaying(exoPlayer: StyledPlayerView): Boolean {
val player = exoPlayer.player
return player != null && player.isPlaying
}


override fun addPlayListener(playerListener: OnPlayerListener) {
if (!listeners.contains(playerListener)) {
listeners.add(playerListener)
}
}

override fun removePlayListener(playerListener: OnPlayerListener) {
listeners.remove(playerListener)
}

override fun onPlayerAttachedToWindow(exoPlayer: StyledPlayerView) {
val player: Player = ExoPlayer.Builder(exoPlayer.context).build()
exoPlayer.player = player
player.addListener(mPlayerListener)
}

override fun onPlayerDetachedFromWindow(exoPlayer: StyledPlayerView) {
val player = exoPlayer.player
if (player != null) {
player.removeListener(mPlayerListener)
player.release()
exoPlayer.player = null
}
}

override fun destroy(exoPlayer: StyledPlayerView) {
val player = exoPlayer.player
if (player != null) {
player.removeListener(mPlayerListener)
player.release()
}
}

private val mPlayerListener: Player.Listener = object : Player.Listener {
override fun onPlayerError(error: PlaybackException) {
for (i in listeners.indices) {
val playerListener = listeners[i]
playerListener.onPlayerError()
}
}

override fun onPlaybackStateChanged(playbackState: Int) {
if (playbackState == Player.STATE_READY) {
for (i in listeners.indices) {
val playerListener = listeners[i]
playerListener.onPlayerReady()
}
} else if (playbackState == Player.STATE_BUFFERING) {
for (i in listeners.indices) {
val playerListener = listeners[i]
playerListener.onPlayerLoading()
}
} else if (playbackState == Player.STATE_ENDED) {
for (i in listeners.indices) {
val playerListener = listeners[i]
playerListener.onPlayerEnd()
}
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.margelo.nitro.multipleimagepicker


import android.app.Dialog
import android.content.Context
import android.os.Bundle
import android.view.Gravity
import android.view.ViewGroup


class LoadingDialog(context: Context?) :
Dialog(context!!, R.style.Picture_Theme_AlertDialog) {
init {
setCancelable(true)
setCanceledOnTouchOutside(false)
}

override fun onCreate(savedInstanceState: Bundle) {
super.onCreate(savedInstanceState)
setContentView(R.layout.loading_dialog)
setDialogSize()
}

private fun setDialogSize() {
val params = window!!.attributes
params.width = ViewGroup.LayoutParams.WRAP_CONTENT
params.height = ViewGroup.LayoutParams.WRAP_CONTENT
params.gravity = Gravity.CENTER
window!!.setWindowAnimations(R.style.PictureThemeDialogWindowStyle)
window!!.attributes = params
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,15 @@ class MultipleImagePicker : HybridMultipleImagePickerSpec() {
resolved: (result: CropResult) -> Unit,
rejected: (reject: Double) -> Unit
) {

pickerModule.openCrop(image, config, resolved, rejected)
}

override fun openPreview(
media: Array<MediaPreview>,
index: Double,
config: NitroPreviewConfig
) {
pickerModule.openPreview(media, index.toInt(), config)
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import com.luck.picture.lib.basic.PictureSelector
import com.luck.picture.lib.config.PictureMimeType
import com.luck.picture.lib.config.SelectMimeType
import com.luck.picture.lib.config.SelectModeConfig
import com.luck.picture.lib.engine.ImageEngine
import com.luck.picture.lib.engine.PictureSelectorEngine
import com.luck.picture.lib.entity.LocalMedia
import com.luck.picture.lib.interfaces.OnMediaEditInterceptListener
Expand Down Expand Up @@ -92,11 +93,13 @@ class MultipleImagePickerImp(reactContext: ReactApplicationContext?) :
val isMultiple = config.selectMode == SelectMode.MULTIPLE
val selectMode = if (isMultiple) SelectModeConfig.MULTIPLE else SelectModeConfig.SINGLE


val isCrop = config.crop != null

PictureSelector.create(activity).openGallery(chooseMode).setImageEngine(imageEngine)
.setSelectedData(dataList).setSelectorUIStyle(style).apply {
PictureSelector.create(activity)
.openGallery(chooseMode)
.setImageEngine(imageEngine)
.setSelectedData(dataList)
.setSelectorUIStyle(style).apply {
if (isCrop) {
setCropOption(config.crop)
// Disabled force crop engine for multiple
Expand All @@ -123,18 +126,27 @@ class MultipleImagePickerImp(reactContext: ReactApplicationContext?) :
if (videoQuality != null && videoQuality != 1.0) {
setVideoQuality(if (videoQuality > 0.5) 1 else 0)
}
}.setImageSpanCount(config.numberOfColumn?.toInt() ?: 3).setMaxSelectNum(maxSelect)
.isDirectReturnSingle(true).isSelectZoomAnim(true).isPageStrategy(true, 50)
}.setImageSpanCount(config.numberOfColumn?.toInt() ?: 3)
.setMaxSelectNum(maxSelect)
.isDirectReturnSingle(true)
.isSelectZoomAnim(true)
.isPageStrategy(true, 50)
.isWithSelectVideoImage(true)
.setMaxVideoSelectNum(if (maxVideo != 20) maxVideo else maxSelect)
.isMaxSelectEnabledMask(true).isAutoVideoPlay(true)
.isFastSlidingSelect(allowSwipeToSelect).isPageSyncAlbumCount(true)
.isMaxSelectEnabledMask(true)
.isAutoVideoPlay(true)
.isFastSlidingSelect(allowSwipeToSelect)
.isPageSyncAlbumCount(true)
// isPreview
.isPreviewImage(isPreview).isPreviewVideo(isPreview)
.isPreviewImage(isPreview)
.isPreviewVideo(isPreview)
//
.isDisplayCamera(config.allowedCamera ?: true).isDisplayTimeAxis(true)
.setSelectionMode(selectMode).isOriginalControl(config.isHiddenOriginalButton == false)
.setLanguage(getLanguage()).isPreviewFullScreenMode(true)
.isDisplayCamera(config.allowedCamera ?: true)
.isDisplayTimeAxis(true)
.setSelectionMode(selectMode)
.isOriginalControl(config.isHiddenOriginalButton == false)
.setLanguage(getLanguage(config.language))
.isPreviewFullScreenMode(true)
.forResult(object : OnResultCallbackListener<LocalMedia?> {
override fun onResult(localMedia: ArrayList<LocalMedia?>?) {
var data: Array<Result> = arrayOf()
Expand Down Expand Up @@ -168,18 +180,6 @@ class MultipleImagePickerImp(reactContext: ReactApplicationContext?) :
resolved: (result: CropResult) -> Unit,
rejected: (reject: Double) -> Unit
) {


fun isImage(uri: Uri, contentResolver: ContentResolver): Boolean {
val mimeType: String? = contentResolver.getType(uri)
return mimeType?.startsWith("image/") == true
}

val uri = Uri.parse(image)
val isImageFile = isImage(uri, appContext.contentResolver)

if (!isImageFile) return rejected(0.0)

cropOption = Options()

setCropOption(
Expand Down Expand Up @@ -211,17 +211,12 @@ class MultipleImagePickerImp(reactContext: ReactApplicationContext?) :
Uri.fromFile(file)
}


else -> {
Uri.parse(image)
}
else -> Uri.parse(image)
}


val destinationUri = Uri.fromFile(
File(getSandboxPath(appContext), DateUtils.getCreateFileName("CROP_") + ".jpeg")
)

val uCrop = UCrop.of<Any>(uri, destinationUri).withOptions(cropOption)

// set engine
Expand Down Expand Up @@ -267,8 +262,23 @@ class MultipleImagePickerImp(reactContext: ReactApplicationContext?) :
}
}

private fun getLanguage(): Int {
return when (config.language) {
@ReactMethod
fun openPreview(media: Array<MediaPreview>, config: NitroPreviewConfig) {

val imageEngine = GlideEngine.createGlideEngine()

var list: ArrayList<LocalMedia> = arrayListOf()

PictureSelector
.create(currentActivity)
.openPreview()
.setImageEngine(imageEngine)
.setLanguage(getLanguage(config.language))
.startFragmentPreview(config.index.toInt(), false, list)
}

private fun getLanguage(language: Language): Int {
return when (language) {
Language.VI -> LanguageConfig.VIETNAM // -> 🇻🇳 My country. Yeahhh
Language.EN -> LanguageConfig.ENGLISH
Language.ZH_HANS -> LanguageConfig.CHINESE
Expand Down
19 changes: 19 additions & 0 deletions android/src/main/res/anim/anim_modal_in.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>

<set xmlns:android="http://schemas.android.com/apk/res/android"
android:interpolator="@android:anim/linear_interpolator"
android:shareInterpolator="true">
<alpha
android:fromAlpha="0.2"
android:toAlpha="1"
android:duration="90"/>

<scale
android:fromXScale="0.7"
android:toXScale="1.0"
android:fromYScale="0.7"
android:toYScale="1.0"
android:pivotX="50%"
android:pivotY="50%"
android:duration="135"/>
</set>
18 changes: 18 additions & 0 deletions android/src/main/res/layout/loading_dialog.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/loading"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/ps_dialog_loading_bg"
android:orientation="vertical"
android:padding="10dp">

<ProgressBar
android:layout_width="25dp"
android:layout_height="25dp"
android:layout_gravity="center_horizontal"
android:indeterminateBehavior="repeat"

/>

</LinearLayout>
Loading