Skip to content

Commit 1ecd8e2

Browse files
committed
✨ feat: handle result for camera
1 parent 9abf88d commit 1ecd8e2

27 files changed

+505
-219
lines changed

MultipleImagePicker.podspec

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,9 @@ Pod::Spec.new do |s|
2727
}
2828

2929

30-
s.dependency "HXPhotoPicker/Picker", "4.2.3"
31-
s.dependency "HXPhotoPicker/Editor", "4.2.3"
32-
s.dependency "HXPhotoPicker/Camera", "4.2.3"
30+
s.dependency "HXPhotoPicker/Picker", "4.2.4"
31+
s.dependency "HXPhotoPicker/Editor", "4.2.4"
32+
s.dependency "HXPhotoPicker/Camera/Lite", "4.2.4"
3333

3434
s.pod_target_xcconfig = {
3535
# C++ compiler flags, mainly for folly.

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

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import com.facebook.react.bridge.ReactMethod
1414
import com.luck.picture.lib.app.IApp
1515
import com.luck.picture.lib.app.PictureAppMaster
1616
import com.luck.picture.lib.basic.PictureSelector
17+
import com.luck.picture.lib.config.Crop
1718
import com.luck.picture.lib.config.PictureMimeType
1819
import com.luck.picture.lib.config.SelectMimeType
1920
import com.luck.picture.lib.config.SelectModeConfig
@@ -323,8 +324,10 @@ class MultipleImagePickerImp(reactContext: ReactApplicationContext?) :
323324
PictureSelector
324325
.create(currentActivity)
325326
.openCamera(chooseMode)
326-
// .setLanguage(getLanguage(config.language))
327+
.setLanguage(getLanguage(config.language))
327328
.isQuickCapture(true)
329+
.setCropEngine(CropEngine(cropOption))
330+
// .setOfAllCameraType(chooseMode)
328331
.setCameraInterceptListener(CameraEngine(appContext, cameraConfig))
329332
.forResult(object : OnResultCallbackListener<LocalMedia?> {
330333
override fun onResult(result: java.util.ArrayList<LocalMedia?>?) {
@@ -556,9 +559,8 @@ class MultipleImagePickerImp(reactContext: ReactApplicationContext?) :
556559
height = item.cropImageHeight.toDouble()
557560
}
558561

562+
559563
val media = Result(
560-
path,
561-
fileName = item.fileName,
562564
localIdentifier = item.id.toString(),
563565
width,
564566
height,
@@ -568,10 +570,12 @@ class MultipleImagePickerImp(reactContext: ReactApplicationContext?) :
568570
realPath = item.realPath,
569571
parentFolderName = item.parentFolderName,
570572
creationDate = item.dateAddedTime.toDouble(),
573+
crop = item.isCut,
574+
path,
571575
type,
572-
duration = item.duration.toDouble(),
576+
fileName = item.fileName,
573577
thumbnail = item.videoThumbnailPath,
574-
crop = item.isCut
578+
duration = item.duration.toDouble()
575579
)
576580

577581
return media

example/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
"gradle": "cd android && ./gradlew clean && ./gradlew build"
1414
},
1515
"dependencies": {
16-
"@baronha/react-native-multiple-image-picker": "file:../",
1716
"@baronha/react-native-image-grid": "^0.2.7",
1817
"@react-native-segmented-control/segmented-control": "2.5.2",
1918
"expo": "~51.0.38",

example/src/index.tsx

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ export default function App() {
8383
try {
8484
const response = await openPicker({
8585
...options,
86-
selectedAssets: images,
86+
selectedAssets: images.filter((item) => item.localIdentifier),
8787
})
8888

8989
setImages(Array.isArray(response) ? response : [response])
@@ -96,10 +96,17 @@ export default function App() {
9696
const onCamera = async () => {
9797
try {
9898
const response = await openCamera({
99-
crop: false,
99+
crop: true,
100+
isSaveSystemAlbum: false,
101+
mediaType: 'all',
102+
videoMaximumDuration: 5,
100103
})
101104

102-
console.log('response: ', response)
105+
setImages((prev) => {
106+
return [response as Result, ...prev]
107+
})
108+
109+
console.log('camera response: ', response)
103110

104111
layoutEffect()
105112
} catch (e) {
@@ -430,7 +437,7 @@ export default function App() {
430437
<Switch
431438
value={options.crop !== undefined}
432439
onValueChange={(value) =>
433-
setOptions('crop', value ? {} : undefined)
440+
setOptions('crop', value ? true : undefined)
434441
}
435442
/>
436443
</SectionView>

example/yarn.lock

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1151,9 +1151,6 @@
11511151
resolved "https://registry.npmjs.org/@baronha/react-native-image-grid/-/react-native-image-grid-0.2.7.tgz"
11521152
integrity sha512-F5q+hJ1p0+hfWYhOK4uL2EgseG89hpiMu0rMIaiw7lMpEA3sKC1AUOhOTKS58LeX1xE/XoqZ0P7tzyntKN+EoQ==
11531153

1154-
"@baronha/react-native-multiple-image-picker@file:..":
1155-
version "2.0.4"
1156-
11571154
"@expo/bunyan@^4.0.0":
11581155
version "4.0.1"
11591156
resolved "https://registry.npmjs.org/@expo/bunyan/-/bunyan-4.0.1.tgz"

ios/Assets.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ class Assets {
2020

2121
extension UIImage {
2222
static var close = UIImage(name: "close")
23+
2324
convenience init(name: String) {
2425
self.init(named: name, in: Assets.bundle(), compatibleWith: nil)!
2526
}

ios/HybridMultipleImagePicker+Camera.swift

Lines changed: 71 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,25 @@
55
// Created by BAO HA on 13/12/24.
66
//
77

8+
import AVFoundation
89
import HXPhotoPicker
10+
import Photos
911

1012
extension HybridMultipleImagePicker {
1113
func openCamera(config: NitroCameraConfig, resolved: @escaping ((CameraResult) -> Void), rejected: @escaping ((Double) -> Void)) throws {
14+
var captureType: CameraController.CaptureType = .all
15+
16+
// check media type
17+
switch config.mediaType {
18+
case .image:
19+
captureType = .photo
20+
case .video:
21+
captureType = .video
22+
default:
23+
break
24+
}
25+
26+
// config
1227
var cameraConfig = CameraConfiguration()
1328

1429
cameraConfig.videoMaximumDuration = config.videoMaximumDuration ?? 60
@@ -26,8 +41,7 @@ extension HybridMultipleImagePicker {
2641

2742
cameraConfig.languageType = setLocale(language: config.language)
2843
cameraConfig.isSaveSystemAlbum = config.isSaveSystemAlbum ?? false
29-
cameraConfig.allowLocation = config.allowLocation ?? true
30-
cameraConfig.sessionPreset = .hd1920x1080
44+
cameraConfig.sessionPreset = .hd4K3840x2160
3145
cameraConfig.aspectRatio = .fullScreen
3246

3347
if let color = config.color, let focusColor = getReactColor(Int(color)) {
@@ -41,9 +55,62 @@ extension HybridMultipleImagePicker {
4155
cameraConfig.position = .back
4256
}
4357

58+
func getCameraResult(_ result: CameraController.Result, _ asset: PHAsset?) {
59+
if let asset {
60+
Task {
61+
let photoAsset = PhotoAsset(asset)
62+
let urlResult = try await photoAsset.urlResult()
63+
let path = urlResult.url.absoluteString
64+
65+
let phAsset = photoAsset.phAsset
66+
let thumbnail = phAsset?.getVideoAssetThumbnail(from: path, in: 1)
67+
68+
resolved(CameraResult(path: path, type: photoAsset.mediaType == .photo ? ResultType.image : ResultType.video, width: photoAsset.imageSize.width, height: photoAsset.imageSize.height, duration: photoAsset.videoDuration, thumbnail: thumbnail, fileName: phAsset?.fileName))
69+
}
70+
71+
} else {
72+
switch result {
73+
case .image(let uiImage):
74+
75+
let fileName = "IMG_\(Int(Date().timeIntervalSince1970)).jpg"
76+
let filePath = uiImage.getPath(fileName: fileName, quality: 1.0)
77+
78+
if let filePath {
79+
resolved(CameraResult(path: filePath, type: ResultType.image, width: uiImage.size.width, height: uiImage.size.height, duration: nil, thumbnail: nil, fileName: fileName))
80+
} else {
81+
rejected(0)
82+
}
83+
84+
case .video(let url):
85+
86+
let asset = AVAsset(url: url)
87+
88+
let thumbnail = getVideoThumbnail(from: url.absoluteString, in: 1)
89+
90+
var result = CameraResult(path: url.absoluteString,
91+
type: ResultType.video,
92+
width: nil,
93+
height: nil,
94+
duration: asset.duration.seconds,
95+
thumbnail: thumbnail,
96+
fileName: url.lastPathComponent)
97+
98+
if let track = asset.tracks(withMediaType: AVMediaType.video).first {
99+
let trackSize = track.naturalSize.applying(track.preferredTransform)
100+
let size = CGSize(width: abs(trackSize.width), height: abs(trackSize.height))
101+
102+
result.width = Double(size.width)
103+
result.height = Double(size.height)
104+
}
105+
106+
resolved(result)
107+
}
108+
}
109+
}
110+
44111
DispatchQueue.main.async {
45-
Photo.capture(cameraConfig) { result, _, _ in
46-
print("result: ", result)
112+
Photo.capture(cameraConfig, type: captureType) { result, asset, _ in
113+
getCameraResult(result, asset)
47114
}
48115
}
49116
}

ios/HybridMultipleImagePicker+Config.swift

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -124,9 +124,7 @@ extension HybridMultipleImagePicker {
124124
if let cameraOption = options.camera {
125125
photoList.allowAddCamera = true
126126

127-
var cameraConfig = setCameraConfig(cameraOption)
128-
129-
photoList.cameraType = .system(cameraConfig)
127+
photoList.cameraType = .system(setCameraConfig(cameraOption))
130128
} else {
131129
photoList.allowAddCamera = false
132130
}

ios/HybridMultipleImagePicker+Result.swift

Lines changed: 12 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,31 +6,23 @@
66
//
77

88
import HXPhotoPicker
9-
import Photos
9+
// import Photos
1010

1111
extension HybridMultipleImagePicker {
1212
func getResult(_ asset: PhotoAsset) async throws -> Result {
1313
let urlResult = try await asset.urlResult()
1414
let url = urlResult.url
15-
15+
1616
let creationDate = Int(asset.phAsset?.creationDate?.timeIntervalSince1970 ?? 0)
17-
17+
1818
let mime = url.getMimeType()
19-
20-
let fileName = {
21-
if let phAsset = asset.phAsset, let resources = PHAssetResource.assetResources(for: phAsset).first {
22-
return resources.originalFilename
23-
}
24-
25-
return ""
26-
}()
27-
19+
20+
let phAsset = asset.phAsset
21+
2822
let type: ResultType = .init(fromString: asset.mediaType == .video ? "video" : "image")!
29-
let thumbnail = asset.phAsset?.getVideoThumbnail(from: url.absoluteString, in: 1)
30-
31-
return Result(path: url.absoluteString,
32-
fileName: fileName,
33-
localIdentifier: asset.phAsset!.localIdentifier,
23+
let thumbnail = asset.phAsset?.getVideoAssetThumbnail(from: url.absoluteString, in: 1)
24+
25+
return Result(localIdentifier: phAsset!.localIdentifier,
3426
width: asset.imageSize.width,
3527
height: asset.imageSize.height,
3628
mime: mime,
@@ -39,9 +31,11 @@ extension HybridMultipleImagePicker {
3931
realPath: nil,
4032
parentFolderName: nil,
4133
creationDate: creationDate > 0 ? Double(creationDate) : nil,
34+
crop: false,
35+
path: url.absoluteString,
4236
type: type,
4337
duration: asset.videoDuration,
4438
thumbnail: thumbnail,
45-
crop: false)
39+
fileName: phAsset?.fileName)
4640
}
4741
}

ios/PHAsset+Thumbnail.swift

Lines changed: 29 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -8,52 +8,46 @@
88
import Photos
99

1010
extension PHAsset {
11-
func getVideoThumbnail(from moviePath: String, in seconds: Double) -> String? {
11+
func getVideoAssetThumbnail(from moviePath: String, in seconds: Double) -> String? {
1212
if mediaType == .video {
13-
let filepath = moviePath.replacingOccurrences(of: "file://", with: "")
14-
let vidURL = URL(fileURLWithPath: filepath)
15-
16-
let asset = AVURLAsset(url: vidURL, options: nil)
17-
let generator = AVAssetImageGenerator(asset: asset)
18-
generator.appliesPreferredTrackTransform = true
19-
20-
let time = CMTime(seconds: seconds, preferredTimescale: 600)
21-
22-
var thumbnail: UIImage?
13+
return getVideoThumbnail(from: moviePath, in: seconds)
14+
}
2315

24-
do {
25-
let imgRef = try generator.copyCGImage(at: time, actualTime: nil)
26-
thumbnail = UIImage(cgImage: imgRef)
27-
} catch {
28-
print("Error create thumbnail: \(error)")
29-
return nil
30-
}
16+
return nil
17+
}
3118

32-
if let thumbnail {
33-
return getImagePathFromUIImage(uiImage: thumbnail, prefix: "thumb")
34-
}
19+
var fileName: String {
20+
if let resources = PHAssetResource.assetResources(for: self).first {
21+
return resources.originalFilename
3522
}
3623

37-
return nil
24+
return ""
3825
}
26+
}
3927

40-
private func getImagePathFromUIImage(uiImage: UIImage, prefix: String? = "thumb") -> String? {
41-
let fileManager = FileManager.default
28+
func getVideoThumbnail(from moviePath: String, in seconds: Double) -> String? {
29+
let filepath = moviePath.replacingOccurrences(of: "file://", with: "")
30+
let vidURL = URL(fileURLWithPath: filepath)
4231

43-
guard
44-
let tempDirectory = FileManager.default.urls(
45-
for: .cachesDirectory,
46-
in: .userDomainMask).map(\.path).last
47-
else {
48-
return nil
49-
}
32+
let asset = AVURLAsset(url: vidURL, options: nil)
33+
let generator = AVAssetImageGenerator(asset: asset)
34+
generator.appliesPreferredTrackTransform = true
5035

51-
let data = uiImage.jpegData(compressionQuality: 0.9)
36+
let time = CMTime(seconds: seconds, preferredTimescale: 600)
5237

53-
let fullPath = URL(fileURLWithPath: tempDirectory).appendingPathComponent("\(prefix ?? "thumb")-\(ProcessInfo.processInfo.globallyUniqueString).jpg").path
38+
var thumbnail: UIImage?
5439

55-
fileManager.createFile(atPath: fullPath, contents: data, attributes: nil)
40+
do {
41+
let imgRef = try generator.copyCGImage(at: time, actualTime: nil)
42+
thumbnail = UIImage(cgImage: imgRef)
43+
} catch {
44+
print("Error create thumbnail: \(error)")
45+
return nil
46+
}
5647

57-
return "file://" + fullPath
48+
if let thumbnail {
49+
return thumbnail.getPath(fileName: nil, quality: 0.8)
5850
}
51+
52+
return nil
5953
}

0 commit comments

Comments
 (0)