Skip to content

Commit 9359353

Browse files
authored
Merge pull request #186 from baronha/feature/open-preview
🌁 feat: Preview Media
2 parents 5297307 + ec5ba02 commit 9359353

39 files changed

+1079
-60
lines changed

MultipleImagePicker.podspec

+1-1
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ Pod::Spec.new do |s|
2727
}
2828

2929

30-
s.dependency "HXPhotoPicker/Picker/Lite", "4.2.3"
30+
s.dependency "HXPhotoPicker/Picker", "4.2.3"
3131
s.dependency "HXPhotoPicker/Editor/Lite", "4.2.3"
3232

3333
s.pod_target_xcconfig = {

README.md

+3-4
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ React Native Multiple Image Picker **(RNMIP)** enables application to pick image
1414

1515
## Documentation 📚
1616

17-
1817
## Features 🔥
1918

2019
| 🤩 | ![Logo][Logo] |
@@ -78,10 +77,10 @@ const onPicker = async () => {
7877
- [x] Multiple Crop Image.
7978
- [x] Multiple Preview Image.
8079
- [x] Dynamic Theme.
81-
- [x] Dynamic Language.
82-
- [ ] Open Camera Controller.
80+
- [x] Dynamic Language
8381
- [x] Open Crop Controller.
84-
- [ ] Open Preview Controller.
82+
- [x] Open Preview Controller.
83+
- [ ] Open Camera Controller.
8584

8685
## Sponsor & Support ☕️
8786

android/build.gradle

+3
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,9 @@ dependencies {
135135
// simple camerax library (Not necessary)
136136
implementation 'io.github.lucksiege:camerax:v3.11.2'
137137

138+
// exoplayer
139+
implementation "com.google.android.exoplayer:exoplayer:2.19.1"
140+
138141

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

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
package com.margelo.nitro.multipleimagepicker
2+
3+
import android.content.Context
4+
import android.net.Uri
5+
import android.view.View
6+
import com.google.android.exoplayer2.ExoPlayer
7+
import com.google.android.exoplayer2.MediaItem
8+
import com.google.android.exoplayer2.PlaybackException
9+
import com.google.android.exoplayer2.Player
10+
import com.google.android.exoplayer2.ui.StyledPlayerView
11+
import com.luck.picture.lib.config.PictureMimeType
12+
import com.luck.picture.lib.config.SelectorProviders
13+
import com.luck.picture.lib.engine.VideoPlayerEngine
14+
import com.luck.picture.lib.entity.LocalMedia
15+
import com.luck.picture.lib.interfaces.OnPlayerListener
16+
import java.io.File
17+
import java.util.concurrent.CopyOnWriteArrayList
18+
19+
20+
class ExoPlayerEngine : VideoPlayerEngine<StyledPlayerView> {
21+
private val listeners = CopyOnWriteArrayList<OnPlayerListener>()
22+
23+
override fun onCreateVideoPlayer(context: Context): View {
24+
val exoPlayer = StyledPlayerView(context)
25+
exoPlayer.useController = true
26+
return exoPlayer
27+
}
28+
29+
override fun onStarPlayer(exoPlayer: StyledPlayerView, media: LocalMedia) {
30+
val player = exoPlayer.player
31+
if (player != null) {
32+
val mediaItem: MediaItem
33+
val path = media.availablePath
34+
mediaItem = if (PictureMimeType.isContent(path)) {
35+
MediaItem.fromUri(Uri.parse(path))
36+
} else if (PictureMimeType.isHasHttp(path)) {
37+
MediaItem.fromUri(path)
38+
} else {
39+
MediaItem.fromUri(Uri.fromFile(File(path)))
40+
}
41+
val config = SelectorProviders.getInstance().selectorConfig
42+
player.repeatMode =
43+
if (config.isLoopAutoPlay) Player.REPEAT_MODE_ALL else Player.REPEAT_MODE_OFF
44+
player.setMediaItem(mediaItem)
45+
player.prepare()
46+
player.play()
47+
}
48+
}
49+
50+
override fun onResume(exoPlayer: StyledPlayerView) {
51+
val player = exoPlayer.player
52+
player?.play()
53+
}
54+
55+
override fun onPause(exoPlayer: StyledPlayerView) {
56+
val player = exoPlayer.player
57+
player?.pause()
58+
}
59+
60+
override fun isPlaying(exoPlayer: StyledPlayerView): Boolean {
61+
val player = exoPlayer.player
62+
return player != null && player.isPlaying
63+
}
64+
65+
66+
override fun addPlayListener(playerListener: OnPlayerListener) {
67+
if (!listeners.contains(playerListener)) {
68+
listeners.add(playerListener)
69+
}
70+
}
71+
72+
override fun removePlayListener(playerListener: OnPlayerListener) {
73+
listeners.remove(playerListener)
74+
}
75+
76+
override fun onPlayerAttachedToWindow(exoPlayer: StyledPlayerView) {
77+
val player: Player = ExoPlayer.Builder(exoPlayer.context).build()
78+
exoPlayer.player = player
79+
player.addListener(mPlayerListener)
80+
}
81+
82+
override fun onPlayerDetachedFromWindow(exoPlayer: StyledPlayerView) {
83+
val player = exoPlayer.player
84+
if (player != null) {
85+
player.removeListener(mPlayerListener)
86+
player.release()
87+
exoPlayer.player = null
88+
}
89+
}
90+
91+
override fun destroy(exoPlayer: StyledPlayerView) {
92+
val player = exoPlayer.player
93+
if (player != null) {
94+
player.removeListener(mPlayerListener)
95+
player.release()
96+
}
97+
}
98+
99+
private val mPlayerListener: Player.Listener = object : Player.Listener {
100+
override fun onPlayerError(error: PlaybackException) {
101+
for (i in listeners.indices) {
102+
val playerListener = listeners[i]
103+
playerListener.onPlayerError()
104+
}
105+
}
106+
107+
override fun onPlaybackStateChanged(playbackState: Int) {
108+
if (playbackState == Player.STATE_READY) {
109+
for (i in listeners.indices) {
110+
val playerListener = listeners[i]
111+
playerListener.onPlayerReady()
112+
}
113+
} else if (playbackState == Player.STATE_BUFFERING) {
114+
for (i in listeners.indices) {
115+
val playerListener = listeners[i]
116+
playerListener.onPlayerLoading()
117+
}
118+
} else if (playbackState == Player.STATE_ENDED) {
119+
for (i in listeners.indices) {
120+
val playerListener = listeners[i]
121+
playerListener.onPlayerEnd()
122+
}
123+
}
124+
}
125+
}
126+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.margelo.nitro.multipleimagepicker
2+
3+
4+
import android.app.Dialog
5+
import android.content.Context
6+
import android.os.Bundle
7+
import android.view.Gravity
8+
import android.view.ViewGroup
9+
10+
11+
class LoadingDialog(context: Context?) :
12+
Dialog(context!!, R.style.Picture_Theme_AlertDialog) {
13+
init {
14+
setCancelable(true)
15+
setCanceledOnTouchOutside(false)
16+
}
17+
18+
override fun onCreate(savedInstanceState: Bundle) {
19+
super.onCreate(savedInstanceState)
20+
setContentView(R.layout.loading_dialog)
21+
setDialogSize()
22+
}
23+
24+
private fun setDialogSize() {
25+
val params = window!!.attributes
26+
params.width = ViewGroup.LayoutParams.WRAP_CONTENT
27+
params.height = ViewGroup.LayoutParams.WRAP_CONTENT
28+
params.gravity = Gravity.CENTER
29+
window!!.setWindowAnimations(R.style.PictureThemeDialogWindowStyle)
30+
window!!.attributes = params
31+
}
32+
}

android/src/main/java/com/margelo/nitro/multipleimagepicker/MultipleImagePicker.kt

+7-1
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,15 @@ class MultipleImagePicker : HybridMultipleImagePickerSpec() {
2323
resolved: (result: CropResult) -> Unit,
2424
rejected: (reject: Double) -> Unit
2525
) {
26-
2726
pickerModule.openCrop(image, config, resolved, rejected)
2827
}
2928

29+
override fun openPreview(
30+
media: Array<MediaPreview>,
31+
index: Double,
32+
config: NitroPreviewConfig
33+
) {
34+
pickerModule.openPreview(media, index.toInt(), config)
35+
}
3036

3137
}

android/src/main/java/com/margelo/nitro/multipleimagepicker/MultipleImagePickerImp.kt

+41-31
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import com.luck.picture.lib.basic.PictureSelector
1919
import com.luck.picture.lib.config.PictureMimeType
2020
import com.luck.picture.lib.config.SelectMimeType
2121
import com.luck.picture.lib.config.SelectModeConfig
22+
import com.luck.picture.lib.engine.ImageEngine
2223
import com.luck.picture.lib.engine.PictureSelectorEngine
2324
import com.luck.picture.lib.entity.LocalMedia
2425
import com.luck.picture.lib.interfaces.OnMediaEditInterceptListener
@@ -92,11 +93,13 @@ class MultipleImagePickerImp(reactContext: ReactApplicationContext?) :
9293
val isMultiple = config.selectMode == SelectMode.MULTIPLE
9394
val selectMode = if (isMultiple) SelectModeConfig.MULTIPLE else SelectModeConfig.SINGLE
9495

95-
9696
val isCrop = config.crop != null
9797

98-
PictureSelector.create(activity).openGallery(chooseMode).setImageEngine(imageEngine)
99-
.setSelectedData(dataList).setSelectorUIStyle(style).apply {
98+
PictureSelector.create(activity)
99+
.openGallery(chooseMode)
100+
.setImageEngine(imageEngine)
101+
.setSelectedData(dataList)
102+
.setSelectorUIStyle(style).apply {
100103
if (isCrop) {
101104
setCropOption(config.crop)
102105
// Disabled force crop engine for multiple
@@ -123,18 +126,27 @@ class MultipleImagePickerImp(reactContext: ReactApplicationContext?) :
123126
if (videoQuality != null && videoQuality != 1.0) {
124127
setVideoQuality(if (videoQuality > 0.5) 1 else 0)
125128
}
126-
}.setImageSpanCount(config.numberOfColumn?.toInt() ?: 3).setMaxSelectNum(maxSelect)
127-
.isDirectReturnSingle(true).isSelectZoomAnim(true).isPageStrategy(true, 50)
129+
}.setImageSpanCount(config.numberOfColumn?.toInt() ?: 3)
130+
.setMaxSelectNum(maxSelect)
131+
.isDirectReturnSingle(true)
132+
.isSelectZoomAnim(true)
133+
.isPageStrategy(true, 50)
128134
.isWithSelectVideoImage(true)
129135
.setMaxVideoSelectNum(if (maxVideo != 20) maxVideo else maxSelect)
130-
.isMaxSelectEnabledMask(true).isAutoVideoPlay(true)
131-
.isFastSlidingSelect(allowSwipeToSelect).isPageSyncAlbumCount(true)
136+
.isMaxSelectEnabledMask(true)
137+
.isAutoVideoPlay(true)
138+
.isFastSlidingSelect(allowSwipeToSelect)
139+
.isPageSyncAlbumCount(true)
132140
// isPreview
133-
.isPreviewImage(isPreview).isPreviewVideo(isPreview)
141+
.isPreviewImage(isPreview)
142+
.isPreviewVideo(isPreview)
134143
//
135-
.isDisplayCamera(config.allowedCamera ?: true).isDisplayTimeAxis(true)
136-
.setSelectionMode(selectMode).isOriginalControl(config.isHiddenOriginalButton == false)
137-
.setLanguage(getLanguage()).isPreviewFullScreenMode(true)
144+
.isDisplayCamera(config.allowedCamera ?: true)
145+
.isDisplayTimeAxis(true)
146+
.setSelectionMode(selectMode)
147+
.isOriginalControl(config.isHiddenOriginalButton == false)
148+
.setLanguage(getLanguage(config.language))
149+
.isPreviewFullScreenMode(true)
138150
.forResult(object : OnResultCallbackListener<LocalMedia?> {
139151
override fun onResult(localMedia: ArrayList<LocalMedia?>?) {
140152
var data: Array<Result> = arrayOf()
@@ -168,18 +180,6 @@ class MultipleImagePickerImp(reactContext: ReactApplicationContext?) :
168180
resolved: (result: CropResult) -> Unit,
169181
rejected: (reject: Double) -> Unit
170182
) {
171-
172-
173-
fun isImage(uri: Uri, contentResolver: ContentResolver): Boolean {
174-
val mimeType: String? = contentResolver.getType(uri)
175-
return mimeType?.startsWith("image/") == true
176-
}
177-
178-
val uri = Uri.parse(image)
179-
val isImageFile = isImage(uri, appContext.contentResolver)
180-
181-
if (!isImageFile) return rejected(0.0)
182-
183183
cropOption = Options()
184184

185185
setCropOption(
@@ -211,17 +211,12 @@ class MultipleImagePickerImp(reactContext: ReactApplicationContext?) :
211211
Uri.fromFile(file)
212212
}
213213

214-
215-
else -> {
216-
Uri.parse(image)
217-
}
214+
else -> Uri.parse(image)
218215
}
219216

220-
221217
val destinationUri = Uri.fromFile(
222218
File(getSandboxPath(appContext), DateUtils.getCreateFileName("CROP_") + ".jpeg")
223219
)
224-
225220
val uCrop = UCrop.of<Any>(uri, destinationUri).withOptions(cropOption)
226221

227222
// set engine
@@ -267,8 +262,23 @@ class MultipleImagePickerImp(reactContext: ReactApplicationContext?) :
267262
}
268263
}
269264

270-
private fun getLanguage(): Int {
271-
return when (config.language) {
265+
@ReactMethod
266+
fun openPreview(media: Array<MediaPreview>, config: NitroPreviewConfig) {
267+
268+
val imageEngine = GlideEngine.createGlideEngine()
269+
270+
var list: ArrayList<LocalMedia> = arrayListOf()
271+
272+
PictureSelector
273+
.create(currentActivity)
274+
.openPreview()
275+
.setImageEngine(imageEngine)
276+
.setLanguage(getLanguage(config.language))
277+
.startFragmentPreview(config.index.toInt(), false, list)
278+
}
279+
280+
private fun getLanguage(language: Language): Int {
281+
return when (language) {
272282
Language.VI -> LanguageConfig.VIETNAM // -> 🇻🇳 My country. Yeahhh
273283
Language.EN -> LanguageConfig.ENGLISH
274284
Language.ZH_HANS -> LanguageConfig.CHINESE
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
3+
<set xmlns:android="http://schemas.android.com/apk/res/android"
4+
android:interpolator="@android:anim/linear_interpolator"
5+
android:shareInterpolator="true">
6+
<alpha
7+
android:fromAlpha="0.2"
8+
android:toAlpha="1"
9+
android:duration="90"/>
10+
11+
<scale
12+
android:fromXScale="0.7"
13+
android:toXScale="1.0"
14+
android:fromYScale="0.7"
15+
android:toYScale="1.0"
16+
android:pivotX="50%"
17+
android:pivotY="50%"
18+
android:duration="135"/>
19+
</set>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
3+
android:id="@+id/loading"
4+
android:layout_width="wrap_content"
5+
android:layout_height="wrap_content"
6+
android:background="@drawable/ps_dialog_loading_bg"
7+
android:orientation="vertical"
8+
android:padding="10dp">
9+
10+
<ProgressBar
11+
android:layout_width="25dp"
12+
android:layout_height="25dp"
13+
android:layout_gravity="center_horizontal"
14+
android:indeterminateBehavior="repeat"
15+
16+
/>
17+
18+
</LinearLayout>

0 commit comments

Comments
 (0)