@technotoil/image-video-editor 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ImageVideoEditor.podspec +21 -0
- package/README.md +136 -0
- package/android/build.gradle +76 -0
- package/android/gradle.properties +5 -0
- package/android/src/main/AndroidManifest.xml +13 -0
- package/android/src/main/java/com/technotoil/image_videoeditor/FrameGrabberModule.kt +67 -0
- package/android/src/main/java/com/technotoil/image_videoeditor/MediaEditorModule.kt +548 -0
- package/android/src/main/java/com/technotoil/image_videoeditor/MediaFileUtils.kt +29 -0
- package/android/src/main/java/com/technotoil/image_videoeditor/MediaLibraryModule.kt +305 -0
- package/android/src/main/java/com/technotoil/image_videoeditor/MediaPackage.kt +26 -0
- package/android/src/main/java/com/technotoil/image_videoeditor/MediaPickerModule.kt +111 -0
- package/android/src/main/java/com/technotoil/image_videoeditor/MediaPlayerModule.kt +34 -0
- package/android/src/main/java/com/technotoil/image_videoeditor/RNCameraViewManager.kt +761 -0
- package/android/src/main/java/com/technotoil/image_videoeditor/RNVideoPreviewManager.kt +317 -0
- package/ios/PrivacyInfo.xcprivacy +38 -0
- package/ios/RNCameraViewManager.m +420 -0
- package/ios/RNFrameGrabber.m +61 -0
- package/ios/RNMediaEditor.m +905 -0
- package/ios/RNMediaLibrary.m +389 -0
- package/ios/RNMediaPicker.m +144 -0
- package/ios/RNMediaPlayer.m +73 -0
- package/ios/RNVideoPreviewManager.m +263 -0
- package/ios/frames/film_vintage.png +0 -0
- package/ios/frames/floral_gold.png +0 -0
- package/ios/frames/minimal_double.png +0 -0
- package/ios/frames/polaroid_white.png +0 -0
- package/ios/frames/watercolor_floral.png +0 -0
- package/lib/module/assets/frames/film_vintage.png +0 -0
- package/lib/module/assets/frames/floral_gold.png +0 -0
- package/lib/module/assets/frames/minimal_double.png +0 -0
- package/lib/module/assets/frames/polaroid_white.png +0 -0
- package/lib/module/assets/frames/watercolor_floral.png +0 -0
- package/lib/module/components/VideoEditor.js +156 -0
- package/lib/module/components/VideoEditor.js.map +1 -0
- package/lib/module/index.js +4 -0
- package/lib/module/index.js.map +1 -0
- package/lib/module/native/CameraView.js +104 -0
- package/lib/module/native/CameraView.js.map +1 -0
- package/lib/module/native/FrameGrabber.js +13 -0
- package/lib/module/native/FrameGrabber.js.map +1 -0
- package/lib/module/native/MediaEditor.js +19 -0
- package/lib/module/native/MediaEditor.js.map +1 -0
- package/lib/module/native/MediaLibrary.js +37 -0
- package/lib/module/native/MediaLibrary.js.map +1 -0
- package/lib/module/native/MediaPicker.js +13 -0
- package/lib/module/native/MediaPicker.js.map +1 -0
- package/lib/module/native/MediaPlayer.js +13 -0
- package/lib/module/native/MediaPlayer.js.map +1 -0
- package/lib/module/native/VideoPreview.js +12 -0
- package/lib/module/native/VideoPreview.js.map +1 -0
- package/lib/module/package.json +1 -0
- package/lib/module/screens/CropScreen.js +1211 -0
- package/lib/module/screens/CropScreen.js.map +1 -0
- package/lib/module/screens/EditorScreen.js +5752 -0
- package/lib/module/screens/EditorScreen.js.map +1 -0
- package/lib/module/screens/ExportScreen.js +289 -0
- package/lib/module/screens/ExportScreen.js.map +1 -0
- package/lib/module/screens/GalleryScreen.js +505 -0
- package/lib/module/screens/GalleryScreen.js.map +1 -0
- package/lib/module/screens/PickScreen.js +1195 -0
- package/lib/module/screens/PickScreen.js.map +1 -0
- package/lib/module/types.js +2 -0
- package/lib/module/types.js.map +1 -0
- package/lib/typescript/src/components/VideoEditor.d.ts +13 -0
- package/lib/typescript/src/index.d.ts +2 -0
- package/lib/typescript/src/native/CameraView.d.ts +23 -0
- package/lib/typescript/src/native/FrameGrabber.d.ts +2 -0
- package/lib/typescript/src/native/MediaEditor.d.ts +3 -0
- package/lib/typescript/src/native/MediaLibrary.d.ts +16 -0
- package/lib/typescript/src/native/MediaPicker.d.ts +2 -0
- package/lib/typescript/src/native/MediaPlayer.d.ts +1 -0
- package/lib/typescript/src/native/VideoPreview.d.ts +19 -0
- package/lib/typescript/src/screens/CropScreen.d.ts +9 -0
- package/lib/typescript/src/screens/EditorScreen.d.ts +10 -0
- package/lib/typescript/src/screens/ExportScreen.d.ts +9 -0
- package/lib/typescript/src/screens/GalleryScreen.d.ts +8 -0
- package/lib/typescript/src/screens/PickScreen.d.ts +13 -0
- package/lib/typescript/src/types.d.ts +58 -0
- package/package.json +101 -0
- package/src/assets/frames/film_vintage.png +0 -0
- package/src/assets/frames/floral_gold.png +0 -0
- package/src/assets/frames/minimal_double.png +0 -0
- package/src/assets/frames/polaroid_white.png +0 -0
- package/src/assets/frames/watercolor_floral.png +0 -0
- package/src/components/VideoEditor.tsx +182 -0
- package/src/index.tsx +2 -0
- package/src/native/CameraView.tsx +95 -0
- package/src/native/FrameGrabber.ts +21 -0
- package/src/native/MediaEditor.ts +33 -0
- package/src/native/MediaLibrary.ts +69 -0
- package/src/native/MediaPicker.ts +17 -0
- package/src/native/MediaPlayer.ts +16 -0
- package/src/native/VideoPreview.tsx +20 -0
- package/src/screens/CropScreen.tsx +968 -0
- package/src/screens/EditorScreen.tsx +4517 -0
- package/src/screens/ExportScreen.tsx +282 -0
- package/src/screens/GalleryScreen.tsx +412 -0
- package/src/screens/PickScreen.tsx +1094 -0
- package/src/types.ts +58 -0
|
@@ -0,0 +1,317 @@
|
|
|
1
|
+
package com.technotoil.image_videoeditor
|
|
2
|
+
|
|
3
|
+
import android.net.Uri
|
|
4
|
+
import android.widget.FrameLayout
|
|
5
|
+
import android.widget.VideoView
|
|
6
|
+
import com.facebook.react.bridge.ReactApplicationContext
|
|
7
|
+
import com.facebook.react.uimanager.SimpleViewManager
|
|
8
|
+
import com.facebook.react.uimanager.ThemedReactContext
|
|
9
|
+
import com.facebook.react.uimanager.annotations.ReactProp
|
|
10
|
+
import com.facebook.react.uimanager.events.RCTEventEmitter
|
|
11
|
+
|
|
12
|
+
class RNVideoViewSubclass(context: android.content.Context) : VideoView(context) {
|
|
13
|
+
var resizeMode: String = "cover"
|
|
14
|
+
var videoW: Int = 0
|
|
15
|
+
var videoH: Int = 0
|
|
16
|
+
|
|
17
|
+
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
|
18
|
+
val width = MeasureSpec.getSize(widthMeasureSpec)
|
|
19
|
+
val height = MeasureSpec.getSize(heightMeasureSpec)
|
|
20
|
+
setMeasuredDimension(width, height)
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
class VideoProgressEvent(surfaceId: Int, viewTag: Int, private val name: String, private val eventData: com.facebook.react.bridge.WritableMap) :
|
|
25
|
+
com.facebook.react.uimanager.events.Event<VideoProgressEvent>(surfaceId, viewTag) {
|
|
26
|
+
override fun getEventName(): String = name
|
|
27
|
+
override fun getEventData(): com.facebook.react.bridge.WritableMap? = eventData
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
class RNVideoView(context: android.content.Context) : FrameLayout(context) {
|
|
31
|
+
val videoView = RNVideoViewSubclass(context)
|
|
32
|
+
var uri: String? = null
|
|
33
|
+
var isPaused: Boolean = false
|
|
34
|
+
var isMuted: Boolean = true
|
|
35
|
+
var mediaPlayer: android.media.MediaPlayer? = null
|
|
36
|
+
var trimStartMs: Int = 0
|
|
37
|
+
var trimEndMs: Int = 0
|
|
38
|
+
var lastEmittedTime: Int = -1
|
|
39
|
+
var isSeeking: Boolean = false
|
|
40
|
+
|
|
41
|
+
private val mainHandler = android.os.Handler(android.os.Looper.getMainLooper())
|
|
42
|
+
|
|
43
|
+
private val checkProgressRunnable = object : Runnable {
|
|
44
|
+
override fun run() {
|
|
45
|
+
try {
|
|
46
|
+
android.util.Log.d("RNVideoPreview", "runnable tick: isPlaying = ${videoView.isPlaying}")
|
|
47
|
+
if (videoView.isPlaying) {
|
|
48
|
+
val current = videoView.currentPosition
|
|
49
|
+
if (trimEndMs > 0 && current >= trimEndMs) {
|
|
50
|
+
if (!isSeeking) {
|
|
51
|
+
isSeeking = true
|
|
52
|
+
videoView.seekTo(trimStartMs)
|
|
53
|
+
}
|
|
54
|
+
} else if (current < trimStartMs) {
|
|
55
|
+
if (!isSeeking) {
|
|
56
|
+
isSeeking = true
|
|
57
|
+
videoView.seekTo(trimStartMs)
|
|
58
|
+
}
|
|
59
|
+
} else {
|
|
60
|
+
isSeeking = false
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (id != android.view.View.NO_ID && current != lastEmittedTime) {
|
|
64
|
+
lastEmittedTime = current
|
|
65
|
+
android.util.Log.d("RNVideoPreview", "Emitting progress: $current, tag: $id")
|
|
66
|
+
val event = com.facebook.react.bridge.Arguments.createMap().apply {
|
|
67
|
+
putInt("currentTimeMs", current)
|
|
68
|
+
}
|
|
69
|
+
val reactContext = context as? com.facebook.react.bridge.ReactContext
|
|
70
|
+
if (reactContext != null) {
|
|
71
|
+
try {
|
|
72
|
+
val surfaceId = com.facebook.react.uimanager.UIManagerHelper.getSurfaceId(reactContext)
|
|
73
|
+
val eventDispatcher = com.facebook.react.uimanager.UIManagerHelper.getEventDispatcherForReactTag(reactContext, id)
|
|
74
|
+
if (eventDispatcher != null) {
|
|
75
|
+
eventDispatcher.dispatchEvent(VideoProgressEvent(surfaceId, id, "topChange", event))
|
|
76
|
+
} else {
|
|
77
|
+
reactContext.getJSModule(com.facebook.react.uimanager.events.RCTEventEmitter::class.java)
|
|
78
|
+
?.receiveEvent(id, "topChange", event)
|
|
79
|
+
}
|
|
80
|
+
} catch (e: Exception) {
|
|
81
|
+
try {
|
|
82
|
+
reactContext.getJSModule(com.facebook.react.uimanager.events.RCTEventEmitter::class.java)
|
|
83
|
+
?.receiveEvent(id, "topChange", event)
|
|
84
|
+
} catch (e2: Exception) {
|
|
85
|
+
// ignore
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
} catch (e: Exception) {
|
|
92
|
+
// ignore
|
|
93
|
+
}
|
|
94
|
+
mainHandler.postDelayed(this, 100)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
override fun onAttachedToWindow() {
|
|
99
|
+
super.onAttachedToWindow()
|
|
100
|
+
android.util.Log.d("RNVideoPreview", "onAttachedToWindow called! tag: $id")
|
|
101
|
+
mainHandler.post(checkProgressRunnable)
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
override fun onDetachedFromWindow() {
|
|
105
|
+
super.onDetachedFromWindow()
|
|
106
|
+
android.util.Log.d("RNVideoPreview", "onDetachedFromWindow called! tag: $id")
|
|
107
|
+
mainHandler.removeCallbacks(checkProgressRunnable)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
private val mLayoutRunnable = Runnable {
|
|
111
|
+
measure(
|
|
112
|
+
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
|
|
113
|
+
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
|
|
114
|
+
)
|
|
115
|
+
layout(left, top, right, bottom)
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
override fun requestLayout() {
|
|
119
|
+
super.requestLayout()
|
|
120
|
+
post(mLayoutRunnable)
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
|
124
|
+
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
|
125
|
+
|
|
126
|
+
val parentWidth = measuredWidth
|
|
127
|
+
val parentHeight = measuredHeight
|
|
128
|
+
|
|
129
|
+
val videoW = videoView.videoW
|
|
130
|
+
val videoH = videoView.videoH
|
|
131
|
+
|
|
132
|
+
if (videoW > 0 && videoH > 0 && parentWidth > 0 && parentHeight > 0) {
|
|
133
|
+
val videoAspect = videoW.toFloat() / videoH.toFloat()
|
|
134
|
+
val parentAspect = parentWidth.toFloat() / parentHeight.toFloat()
|
|
135
|
+
|
|
136
|
+
var childWidth = parentWidth
|
|
137
|
+
var childHeight = parentHeight
|
|
138
|
+
|
|
139
|
+
if (videoView.resizeMode == "contain") {
|
|
140
|
+
if (videoAspect > parentAspect) {
|
|
141
|
+
childHeight = (parentWidth / videoAspect).toInt()
|
|
142
|
+
} else {
|
|
143
|
+
childWidth = (parentHeight * videoAspect).toInt()
|
|
144
|
+
}
|
|
145
|
+
} else {
|
|
146
|
+
if (videoAspect > parentAspect) {
|
|
147
|
+
childWidth = (parentHeight * videoAspect).toInt()
|
|
148
|
+
childHeight = parentHeight
|
|
149
|
+
} else {
|
|
150
|
+
childWidth = parentWidth
|
|
151
|
+
childHeight = (parentWidth / videoAspect).toInt()
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
videoView.measure(
|
|
156
|
+
MeasureSpec.makeMeasureSpec(childWidth, MeasureSpec.EXACTLY),
|
|
157
|
+
MeasureSpec.makeMeasureSpec(childHeight, MeasureSpec.EXACTLY)
|
|
158
|
+
)
|
|
159
|
+
} else {
|
|
160
|
+
videoView.measure(widthMeasureSpec, heightMeasureSpec)
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
|
|
165
|
+
val parentWidth = right - left
|
|
166
|
+
val parentHeight = bottom - top
|
|
167
|
+
|
|
168
|
+
val childWidth = videoView.measuredWidth
|
|
169
|
+
val childHeight = videoView.measuredHeight
|
|
170
|
+
|
|
171
|
+
val childLeft = (parentWidth - childWidth) / 2
|
|
172
|
+
val childTop = (parentHeight - childHeight) / 2
|
|
173
|
+
|
|
174
|
+
videoView.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight)
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
init {
|
|
178
|
+
android.util.Log.d("RNVideoPreview", "RNVideoView init called! tag: $id")
|
|
179
|
+
clipChildren = true
|
|
180
|
+
videoView.setZOrderMediaOverlay(false)
|
|
181
|
+
videoView.setOnPreparedListener { mp ->
|
|
182
|
+
mediaPlayer = mp
|
|
183
|
+
mp.isLooping = true
|
|
184
|
+
val volume = if (isMuted) 0f else 1f
|
|
185
|
+
mp.setVolume(volume, volume)
|
|
186
|
+
// Seek to trim start before playing
|
|
187
|
+
if (trimStartMs > 0) {
|
|
188
|
+
mp.seekTo(trimStartMs)
|
|
189
|
+
}
|
|
190
|
+
if (!isPaused) {
|
|
191
|
+
android.util.Log.d("RNVideoPreview", "onPrepared: starting playback, isPaused=$isPaused")
|
|
192
|
+
videoView.start()
|
|
193
|
+
} else {
|
|
194
|
+
android.util.Log.d("RNVideoPreview", "onPrepared: isPaused=true, not starting")
|
|
195
|
+
}
|
|
196
|
+
videoView.videoW = mp.videoWidth
|
|
197
|
+
videoView.videoH = mp.videoHeight
|
|
198
|
+
requestLayout()
|
|
199
|
+
}
|
|
200
|
+
videoView.setOnCompletionListener { mp ->
|
|
201
|
+
mp.seekTo(trimStartMs)
|
|
202
|
+
mp.start()
|
|
203
|
+
}
|
|
204
|
+
videoView.setOnErrorListener { mp, what, extra ->
|
|
205
|
+
android.util.Log.e("RNVideoPreview", "VideoView error: what=$what, extra=$extra")
|
|
206
|
+
true // Return true to prevent default "Can't play this video" dialog
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
val lp = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, android.view.Gravity.CENTER)
|
|
210
|
+
addView(videoView, lp)
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
fun updateVolume() {
|
|
214
|
+
try {
|
|
215
|
+
val volume = if (isMuted) 0f else 1f
|
|
216
|
+
mediaPlayer?.setVolume(volume, volume)
|
|
217
|
+
} catch (e: Exception) {
|
|
218
|
+
e.printStackTrace()
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
class RNVideoPreviewManager(private val reactContext: ReactApplicationContext) :
|
|
224
|
+
SimpleViewManager<RNVideoView>() {
|
|
225
|
+
|
|
226
|
+
override fun getName(): String = "RNVideoPreview"
|
|
227
|
+
|
|
228
|
+
override fun createViewInstance(reactContext: ThemedReactContext): RNVideoView {
|
|
229
|
+
return RNVideoView(reactContext)
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
override fun getExportedCustomBubblingEventTypeConstants(): Map<String, Any>? {
|
|
233
|
+
return com.facebook.react.common.MapBuilder.builder<String, Any>()
|
|
234
|
+
.put("topChange", com.facebook.react.common.MapBuilder.of(
|
|
235
|
+
"phasedRegistrationNames", com.facebook.react.common.MapBuilder.of("bubbled", "onChange")
|
|
236
|
+
))
|
|
237
|
+
.build()
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
@ReactProp(name = "uri")
|
|
241
|
+
fun setUri(view: RNVideoView, uri: String?) {
|
|
242
|
+
if (uri == view.uri) return
|
|
243
|
+
view.uri = uri
|
|
244
|
+
if (uri.isNullOrEmpty()) {
|
|
245
|
+
view.mediaPlayer = null
|
|
246
|
+
view.videoView.stopPlayback()
|
|
247
|
+
return
|
|
248
|
+
}
|
|
249
|
+
try {
|
|
250
|
+
view.mediaPlayer = null
|
|
251
|
+
val parsedUri = Uri.parse(uri)
|
|
252
|
+
if (parsedUri.scheme == "file" || uri.startsWith("/")) {
|
|
253
|
+
val path = parsedUri.path ?: if (uri.startsWith("file://")) uri.substring(7) else uri
|
|
254
|
+
view.videoView.setVideoPath(path)
|
|
255
|
+
} else {
|
|
256
|
+
view.videoView.setVideoURI(parsedUri)
|
|
257
|
+
}
|
|
258
|
+
} catch (e: Exception) {
|
|
259
|
+
e.printStackTrace()
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
@ReactProp(name = "paused")
|
|
264
|
+
fun setPaused(view: RNVideoView, paused: Boolean) {
|
|
265
|
+
android.util.Log.d("RNVideoPreview", "setPaused: paused=$paused, mediaPlayer=${view.mediaPlayer}")
|
|
266
|
+
view.isPaused = paused
|
|
267
|
+
if (paused) {
|
|
268
|
+
try { view.videoView.pause() } catch (e: Exception) { /* ignore if not yet prepared */ }
|
|
269
|
+
} else {
|
|
270
|
+
// Only call start() if mediaPlayer is prepared (not null)
|
|
271
|
+
if (view.mediaPlayer != null) {
|
|
272
|
+
try {
|
|
273
|
+
if (!view.videoView.isPlaying) {
|
|
274
|
+
view.videoView.start()
|
|
275
|
+
}
|
|
276
|
+
} catch (e: Exception) {
|
|
277
|
+
android.util.Log.w("RNVideoPreview", "setPaused: start() failed: ${e.message}")
|
|
278
|
+
}
|
|
279
|
+
} else {
|
|
280
|
+
android.util.Log.d("RNVideoPreview", "setPaused: mediaPlayer null, will auto-start when prepared")
|
|
281
|
+
// isPaused is already false so onPrepared will call start()
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
@ReactProp(name = "muted")
|
|
287
|
+
fun setMuted(view: RNVideoView, muted: Boolean) {
|
|
288
|
+
view.isMuted = muted
|
|
289
|
+
view.updateVolume()
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
@ReactProp(name = "resizeMode")
|
|
293
|
+
fun setResizeMode(view: RNVideoView, resizeMode: String?) {
|
|
294
|
+
view.videoView.resizeMode = resizeMode ?: "cover"
|
|
295
|
+
view.videoView.requestLayout()
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
@ReactProp(name = "trimStartMs")
|
|
299
|
+
fun setTrimStartMs(view: RNVideoView, trimStartMs: Int) {
|
|
300
|
+
view.trimStartMs = trimStartMs
|
|
301
|
+
if (view.videoView.currentPosition < trimStartMs) {
|
|
302
|
+
view.videoView.seekTo(trimStartMs)
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
@ReactProp(name = "trimEndMs")
|
|
307
|
+
fun setTrimEndMs(view: RNVideoView, trimEndMs: Int) {
|
|
308
|
+
view.trimEndMs = trimEndMs
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
@ReactProp(name = "seekToMs")
|
|
312
|
+
fun setSeekToMs(view: RNVideoView, seekToMs: Int) {
|
|
313
|
+
if (seekToMs >= 0) {
|
|
314
|
+
view.videoView.seekTo(seekToMs)
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<key>NSPrivacyAccessedAPITypes</key>
|
|
6
|
+
<array>
|
|
7
|
+
<dict>
|
|
8
|
+
<key>NSPrivacyAccessedAPIType</key>
|
|
9
|
+
<string>NSPrivacyAccessedAPICategoryFileTimestamp</string>
|
|
10
|
+
<key>NSPrivacyAccessedAPITypeReasons</key>
|
|
11
|
+
<array>
|
|
12
|
+
<string>3B52.1</string>
|
|
13
|
+
<string>C617.1</string>
|
|
14
|
+
</array>
|
|
15
|
+
</dict>
|
|
16
|
+
<dict>
|
|
17
|
+
<key>NSPrivacyAccessedAPIType</key>
|
|
18
|
+
<string>NSPrivacyAccessedAPICategoryUserDefaults</string>
|
|
19
|
+
<key>NSPrivacyAccessedAPITypeReasons</key>
|
|
20
|
+
<array>
|
|
21
|
+
<string>CA92.1</string>
|
|
22
|
+
</array>
|
|
23
|
+
</dict>
|
|
24
|
+
<dict>
|
|
25
|
+
<key>NSPrivacyAccessedAPIType</key>
|
|
26
|
+
<string>NSPrivacyAccessedAPICategorySystemBootTime</string>
|
|
27
|
+
<key>NSPrivacyAccessedAPITypeReasons</key>
|
|
28
|
+
<array>
|
|
29
|
+
<string>35F9.1</string>
|
|
30
|
+
</array>
|
|
31
|
+
</dict>
|
|
32
|
+
</array>
|
|
33
|
+
<key>NSPrivacyCollectedDataTypes</key>
|
|
34
|
+
<array/>
|
|
35
|
+
<key>NSPrivacyTracking</key>
|
|
36
|
+
<false/>
|
|
37
|
+
</dict>
|
|
38
|
+
</plist>
|