react-native-photo-video-editor 0.1.0 → 0.2.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/CHANGELOG.md +20 -0
- package/PhotoVideoEditor.podspec +1 -0
- package/README.md +10 -6
- package/THIRD_PARTY_NOTICES.md +20 -0
- package/android/build.gradle +1 -1
- package/android/src/main/AndroidManifest.xml +1 -1
- package/android/src/main/assets/Stickers/AUTUMN_1.png +0 -0
- package/android/src/main/assets/Stickers/AUTUMN_10.png +0 -0
- package/android/src/main/assets/Stickers/AUTUMN_11.png +0 -0
- package/android/src/main/assets/Stickers/AUTUMN_12.png +0 -0
- package/android/src/main/assets/Stickers/AUTUMN_13.png +0 -0
- package/android/src/main/assets/Stickers/AUTUMN_14.png +0 -0
- package/android/src/main/assets/Stickers/AUTUMN_15.png +0 -0
- package/android/src/main/assets/Stickers/AUTUMN_16.png +0 -0
- package/android/src/main/assets/Stickers/AUTUMN_17.png +0 -0
- package/android/src/main/assets/Stickers/AUTUMN_18.png +0 -0
- package/android/src/main/assets/Stickers/AUTUMN_2.png +0 -0
- package/android/src/main/assets/Stickers/AUTUMN_3.png +0 -0
- package/android/src/main/assets/Stickers/AUTUMN_4.png +0 -0
- package/android/src/main/assets/Stickers/AUTUMN_5.png +0 -0
- package/android/src/main/assets/Stickers/AUTUMN_6.png +0 -0
- package/android/src/main/assets/Stickers/AUTUMN_7.png +0 -0
- package/android/src/main/assets/Stickers/AUTUMN_8.png +0 -0
- package/android/src/main/assets/Stickers/AUTUMN_9.png +0 -0
- package/android/src/main/assets/Stickers/LOL_1.png +0 -0
- package/android/src/main/assets/Stickers/LOL_10.png +0 -0
- package/android/src/main/assets/Stickers/LOL_11.png +0 -0
- package/android/src/main/assets/Stickers/LOL_12.png +0 -0
- package/android/src/main/assets/Stickers/LOL_13.png +0 -0
- package/android/src/main/assets/Stickers/LOL_14.png +0 -0
- package/android/src/main/assets/Stickers/LOL_15.png +0 -0
- package/android/src/main/assets/Stickers/LOL_16.png +0 -0
- package/android/src/main/assets/Stickers/LOL_17.png +0 -0
- package/android/src/main/assets/Stickers/LOL_18.png +0 -0
- package/android/src/main/assets/Stickers/LOL_19.png +0 -0
- package/android/src/main/assets/Stickers/LOL_2.png +0 -0
- package/android/src/main/assets/Stickers/LOL_3.png +0 -0
- package/android/src/main/assets/Stickers/LOL_4.png +0 -0
- package/android/src/main/assets/Stickers/LOL_5.png +0 -0
- package/android/src/main/assets/Stickers/LOL_6.png +0 -0
- package/android/src/main/assets/Stickers/LOL_7.png +0 -0
- package/android/src/main/assets/Stickers/LOL_8.png +0 -0
- package/android/src/main/assets/Stickers/LOL_9.png +0 -0
- package/android/src/main/assets/Stickers/SPRING_1.png +0 -0
- package/android/src/main/assets/Stickers/SPRING_2.png +0 -0
- package/android/src/main/assets/Stickers/SPRING_3.png +0 -0
- package/android/src/main/assets/Stickers/SPRING_4.png +0 -0
- package/android/src/main/assets/Stickers/SPRING_5.png +0 -0
- package/android/src/main/assets/Stickers/SPRING_6.png +0 -0
- package/android/src/main/assets/Stickers/SUMMER_1.png +0 -0
- package/android/src/main/assets/Stickers/SUMMER_10.png +0 -0
- package/android/src/main/assets/Stickers/SUMMER_11.png +0 -0
- package/android/src/main/assets/Stickers/SUMMER_12.png +0 -0
- package/android/src/main/assets/Stickers/SUMMER_13.png +0 -0
- package/android/src/main/assets/Stickers/SUMMER_14.png +0 -0
- package/android/src/main/assets/Stickers/SUMMER_15.png +0 -0
- package/android/src/main/assets/Stickers/SUMMER_16.png +0 -0
- package/android/src/main/assets/Stickers/SUMMER_17.png +0 -0
- package/android/src/main/assets/Stickers/SUMMER_18.png +0 -0
- package/android/src/main/assets/Stickers/SUMMER_19.png +0 -0
- package/android/src/main/assets/Stickers/SUMMER_2.png +0 -0
- package/android/src/main/assets/Stickers/SUMMER_20.png +0 -0
- package/android/src/main/assets/Stickers/SUMMER_21.png +0 -0
- package/android/src/main/assets/Stickers/SUMMER_3.png +0 -0
- package/android/src/main/assets/Stickers/SUMMER_4.png +0 -0
- package/android/src/main/assets/Stickers/SUMMER_5.png +0 -0
- package/android/src/main/assets/Stickers/SUMMER_6.png +0 -0
- package/android/src/main/assets/Stickers/SUMMER_7.png +0 -0
- package/android/src/main/assets/Stickers/SUMMER_8.png +0 -0
- package/android/src/main/assets/Stickers/SUMMER_9.png +0 -0
- package/android/src/main/assets/Stickers/TEXT_1.png +0 -0
- package/android/src/main/assets/Stickers/TEXT_12.png +0 -0
- package/android/src/main/assets/Stickers/TEXT_13.png +0 -0
- package/android/src/main/assets/Stickers/TEXT_14.png +0 -0
- package/android/src/main/assets/Stickers/TEXT_15.png +0 -0
- package/android/src/main/assets/Stickers/TEXT_16.png +0 -0
- package/android/src/main/assets/Stickers/TEXT_2.png +0 -0
- package/android/src/main/assets/Stickers/TEXT_25.png +0 -0
- package/android/src/main/assets/Stickers/TEXT_26.png +0 -0
- package/android/src/main/assets/Stickers/TEXT_27.png +0 -0
- package/android/src/main/assets/Stickers/TEXT_28.png +0 -0
- package/android/src/main/assets/Stickers/TEXT_29.png +0 -0
- package/android/src/main/assets/Stickers/TEXT_3.png +0 -0
- package/android/src/main/assets/Stickers/TEXT_30.png +0 -0
- package/android/src/main/assets/Stickers/TEXT_31.png +0 -0
- package/android/src/main/assets/Stickers/TEXT_32.png +0 -0
- package/android/src/main/assets/Stickers/TEXT_7.png +0 -0
- package/android/src/main/assets/Stickers/TEXT_8.png +0 -0
- package/android/src/main/assets/Stickers/TEXT_9.png +0 -0
- package/android/src/main/java/com/photovideoeditor/editor/PhotoVideoEditorActivity.kt +901 -651
- package/android/src/main/java/com/photovideoeditor/photo/export/PhotoExporter.kt +8 -23
- package/android/src/main/java/com/photovideoeditor/photo/render/CropGeometry.kt +71 -0
- package/android/src/main/java/com/photovideoeditor/photo/render/OverlayGeometry.kt +36 -0
- package/android/src/main/java/com/photovideoeditor/photo/render/PhotoEditSession.kt +5 -7
- package/android/src/main/java/com/photovideoeditor/photo/render/PhotoFilterPresets.kt +71 -41
- package/android/src/main/java/com/photovideoeditor/photo/render/PhotoLayer.kt +5 -1
- package/android/src/main/java/com/photovideoeditor/photo/render/PhotoLayerRenderer.kt +24 -1
- package/android/src/main/java/com/photovideoeditor/photo/render/PhotoTransformRenderer.kt +33 -0
- package/android/src/main/java/com/photovideoeditor/photo/render/PhotoTransformState.kt +6 -1
- package/android/src/main/java/com/photovideoeditor/photo/ui/CropOverlayView.kt +112 -122
- package/android/src/main/java/com/photovideoeditor/photo/ui/DrawOverlayView.kt +66 -0
- package/android/src/main/java/com/photovideoeditor/photo/ui/LayerOverlayView.kt +63 -14
- package/android/src/main/java/com/photovideoeditor/photo/ui/ZoomableImageView.kt +10 -0
- package/android/src/main/java/com/photovideoeditor/stickers/OnlineStickerSheet.kt +135 -104
- package/android/src/main/java/com/photovideoeditor/ui/components/CropPanel.kt +122 -0
- package/android/src/main/java/com/photovideoeditor/ui/components/EditorPanel.kt +250 -0
- package/android/src/main/java/com/photovideoeditor/ui/components/EditorToolSlider.kt +103 -0
- package/android/src/main/java/com/photovideoeditor/video/export/VideoExporter.kt +38 -11
- package/android/src/main/java/com/photovideoeditor/video/render/VideoCropEffects.kt +35 -0
- package/android/src/main/java/com/photovideoeditor/video/render/VideoEditSession.kt +10 -0
- package/android/src/main/java/com/photovideoeditor/video/render/VideoTransformState.kt +1 -0
- package/android/src/main/res/drawable/ic_draw.xml +6 -0
- package/android/src/main/res/drawable/ic_pause.xml +7 -0
- package/android/src/main/res/drawable/ic_play.xml +7 -0
- package/docs/api.md +1 -1
- package/docs/editor-ui-validation.md +22 -0
- package/docs/photo-editor.md +5 -6
- package/ios/Editor/PhotoVideoEditorViewController.swift +918 -494
- package/ios/Editor/UI/CropPanel.swift +97 -0
- package/ios/Editor/UI/EditorPanel.swift +334 -0
- package/ios/Editor/UI/EditorToolSlider.swift +22 -0
- package/ios/Editor/UI/TextEditorSheet.swift +84 -0
- package/ios/Photo/Export/PhotoExporter.swift +3 -3
- package/ios/Photo/Rendering/CropGeometry.swift +48 -0
- package/ios/Photo/Rendering/PhotoEditSession.swift +21 -10
- package/ios/Photo/Rendering/PhotoFilterPresets.swift +53 -23
- package/ios/Photo/Rendering/PhotoLayer.swift +14 -2
- package/ios/Photo/Rendering/PhotoLayerRenderer.swift +16 -1
- package/ios/Photo/Rendering/PhotoTransformState.swift +5 -0
- package/ios/Photo/UI/CropOverlayView.swift +98 -134
- package/ios/Photo/UI/DrawOverlayView.swift +48 -0
- package/ios/Photo/UI/FilterThumbnailLoader.swift +62 -0
- package/ios/Photo/UI/LayerOverlayView.swift +36 -11
- package/ios/Photo/UI/ZoomableImageView.swift +19 -1
- package/ios/Resources/Stickers/AUTUMN_1.png +0 -0
- package/ios/Resources/Stickers/AUTUMN_10.png +0 -0
- package/ios/Resources/Stickers/AUTUMN_11.png +0 -0
- package/ios/Resources/Stickers/AUTUMN_12.png +0 -0
- package/ios/Resources/Stickers/AUTUMN_13.png +0 -0
- package/ios/Resources/Stickers/AUTUMN_14.png +0 -0
- package/ios/Resources/Stickers/AUTUMN_15.png +0 -0
- package/ios/Resources/Stickers/AUTUMN_16.png +0 -0
- package/ios/Resources/Stickers/AUTUMN_17.png +0 -0
- package/ios/Resources/Stickers/AUTUMN_18.png +0 -0
- package/ios/Resources/Stickers/AUTUMN_2.png +0 -0
- package/ios/Resources/Stickers/AUTUMN_3.png +0 -0
- package/ios/Resources/Stickers/AUTUMN_4.png +0 -0
- package/ios/Resources/Stickers/AUTUMN_5.png +0 -0
- package/ios/Resources/Stickers/AUTUMN_6.png +0 -0
- package/ios/Resources/Stickers/AUTUMN_7.png +0 -0
- package/ios/Resources/Stickers/AUTUMN_8.png +0 -0
- package/ios/Resources/Stickers/AUTUMN_9.png +0 -0
- package/ios/Resources/Stickers/LOL_1.png +0 -0
- package/ios/Resources/Stickers/LOL_10.png +0 -0
- package/ios/Resources/Stickers/LOL_11.png +0 -0
- package/ios/Resources/Stickers/LOL_12.png +0 -0
- package/ios/Resources/Stickers/LOL_13.png +0 -0
- package/ios/Resources/Stickers/LOL_14.png +0 -0
- package/ios/Resources/Stickers/LOL_15.png +0 -0
- package/ios/Resources/Stickers/LOL_16.png +0 -0
- package/ios/Resources/Stickers/LOL_17.png +0 -0
- package/ios/Resources/Stickers/LOL_18.png +0 -0
- package/ios/Resources/Stickers/LOL_19.png +0 -0
- package/ios/Resources/Stickers/LOL_2.png +0 -0
- package/ios/Resources/Stickers/LOL_3.png +0 -0
- package/ios/Resources/Stickers/LOL_4.png +0 -0
- package/ios/Resources/Stickers/LOL_5.png +0 -0
- package/ios/Resources/Stickers/LOL_6.png +0 -0
- package/ios/Resources/Stickers/LOL_7.png +0 -0
- package/ios/Resources/Stickers/LOL_8.png +0 -0
- package/ios/Resources/Stickers/LOL_9.png +0 -0
- package/ios/Resources/Stickers/SPRING_1.png +0 -0
- package/ios/Resources/Stickers/SPRING_2.png +0 -0
- package/ios/Resources/Stickers/SPRING_3.png +0 -0
- package/ios/Resources/Stickers/SPRING_4.png +0 -0
- package/ios/Resources/Stickers/SPRING_5.png +0 -0
- package/ios/Resources/Stickers/SPRING_6.png +0 -0
- package/ios/Resources/Stickers/SUMMER_1.png +0 -0
- package/ios/Resources/Stickers/SUMMER_10.png +0 -0
- package/ios/Resources/Stickers/SUMMER_11.png +0 -0
- package/ios/Resources/Stickers/SUMMER_12.png +0 -0
- package/ios/Resources/Stickers/SUMMER_13.png +0 -0
- package/ios/Resources/Stickers/SUMMER_14.png +0 -0
- package/ios/Resources/Stickers/SUMMER_15.png +0 -0
- package/ios/Resources/Stickers/SUMMER_16.png +0 -0
- package/ios/Resources/Stickers/SUMMER_17.png +0 -0
- package/ios/Resources/Stickers/SUMMER_18.png +0 -0
- package/ios/Resources/Stickers/SUMMER_19.png +0 -0
- package/ios/Resources/Stickers/SUMMER_2.png +0 -0
- package/ios/Resources/Stickers/SUMMER_20.png +0 -0
- package/ios/Resources/Stickers/SUMMER_21.png +0 -0
- package/ios/Resources/Stickers/SUMMER_3.png +0 -0
- package/ios/Resources/Stickers/SUMMER_4.png +0 -0
- package/ios/Resources/Stickers/SUMMER_5.png +0 -0
- package/ios/Resources/Stickers/SUMMER_6.png +0 -0
- package/ios/Resources/Stickers/SUMMER_7.png +0 -0
- package/ios/Resources/Stickers/SUMMER_8.png +0 -0
- package/ios/Resources/Stickers/SUMMER_9.png +0 -0
- package/ios/Resources/Stickers/TEXT_1.png +0 -0
- package/ios/Resources/Stickers/TEXT_12.png +0 -0
- package/ios/Resources/Stickers/TEXT_13.png +0 -0
- package/ios/Resources/Stickers/TEXT_14.png +0 -0
- package/ios/Resources/Stickers/TEXT_15.png +0 -0
- package/ios/Resources/Stickers/TEXT_16.png +0 -0
- package/ios/Resources/Stickers/TEXT_2.png +0 -0
- package/ios/Resources/Stickers/TEXT_25.png +0 -0
- package/ios/Resources/Stickers/TEXT_26.png +0 -0
- package/ios/Resources/Stickers/TEXT_27.png +0 -0
- package/ios/Resources/Stickers/TEXT_28.png +0 -0
- package/ios/Resources/Stickers/TEXT_29.png +0 -0
- package/ios/Resources/Stickers/TEXT_3.png +0 -0
- package/ios/Resources/Stickers/TEXT_30.png +0 -0
- package/ios/Resources/Stickers/TEXT_31.png +0 -0
- package/ios/Resources/Stickers/TEXT_32.png +0 -0
- package/ios/Resources/Stickers/TEXT_7.png +0 -0
- package/ios/Resources/Stickers/TEXT_8.png +0 -0
- package/ios/Resources/Stickers/TEXT_9.png +0 -0
- package/ios/Resources/Stickers/a_pety_icon.png +0 -0
- package/ios/Stickers/OnlineStickerSheetViewController.swift +102 -159
- package/ios/Video/Export/VideoExporter.swift +10 -1
- package/ios/Video/Render/VideoCropGeometry.swift +48 -0
- package/ios/Video/Render/VideoTransformState.swift +1 -0
- package/lib/module/index.js +2 -2
- package/lib/module/index.js.map +1 -1
- package/lib/typescript/src/types.d.ts +2 -2
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/package.json +2 -1
- package/src/index.ts +1 -1
- package/src/types.ts +2 -2
- package/android/src/main/java/com/photovideoeditor/stickers/OpenMojiCatalog.kt +0 -172
- package/android/src/main/java/com/photovideoeditor/stickers/RemoteImageLoader.kt +0 -110
- package/ios/Stickers/OpenMojiCatalog.swift +0 -188
- package/ios/Stickers/RemoteImageLoader.swift +0 -127
|
@@ -3,12 +3,14 @@ package com.photovideoeditor.stickers
|
|
|
3
3
|
import android.app.Dialog
|
|
4
4
|
import android.content.Context
|
|
5
5
|
import android.graphics.Color
|
|
6
|
+
import android.graphics.BitmapFactory
|
|
6
7
|
import android.os.Bundle
|
|
7
8
|
import android.os.Handler
|
|
8
9
|
import android.os.Looper
|
|
9
10
|
import android.text.Editable
|
|
10
11
|
import android.text.TextWatcher
|
|
11
12
|
import android.view.Gravity
|
|
13
|
+
import android.view.MotionEvent
|
|
12
14
|
import android.view.View
|
|
13
15
|
import android.view.ViewGroup
|
|
14
16
|
import android.view.Window
|
|
@@ -18,7 +20,6 @@ import android.widget.FrameLayout
|
|
|
18
20
|
import android.widget.HorizontalScrollView
|
|
19
21
|
import android.widget.ImageView
|
|
20
22
|
import android.widget.LinearLayout
|
|
21
|
-
import android.widget.ProgressBar
|
|
22
23
|
import android.widget.TextView
|
|
23
24
|
import android.widget.Toast
|
|
24
25
|
import androidx.recyclerview.widget.GridLayoutManager
|
|
@@ -26,41 +27,50 @@ import androidx.recyclerview.widget.RecyclerView
|
|
|
26
27
|
import com.photovideoeditor.ui.DesignTokens
|
|
27
28
|
import com.photovideoeditor.ui.components.dpToPx
|
|
28
29
|
import com.photovideoeditor.ui.components.editorChip
|
|
29
|
-
import com.photovideoeditor.ui.components.editorPrimaryButton
|
|
30
30
|
import com.photovideoeditor.ui.components.roundedDrawable
|
|
31
|
+
import com.photovideoeditor.files.SourceResolver
|
|
32
|
+
import java.io.File
|
|
33
|
+
import java.io.FileOutputStream
|
|
34
|
+
import java.net.URL
|
|
35
|
+
import java.util.concurrent.Executors
|
|
36
|
+
|
|
37
|
+
data class RuntimeSticker(val id: String, val uri: String)
|
|
31
38
|
|
|
32
39
|
/**
|
|
33
|
-
* Bottom sheet listing
|
|
40
|
+
* Bottom sheet listing bundled sticker assets (search + category filter + grid),
|
|
34
41
|
* built as a plain [Dialog] (no Material `BottomSheetDialog`, which isn't a dependency here) that
|
|
35
42
|
* slides up from the bottom and covers ~85% of the screen height.
|
|
36
43
|
*/
|
|
37
44
|
class OnlineStickerSheet(
|
|
38
45
|
context: Context,
|
|
46
|
+
private val runtimeStickers: List<RuntimeSticker> = emptyList(),
|
|
39
47
|
private val onStickerPicked: (localFilePath: String) -> Unit
|
|
40
48
|
) : Dialog(context, android.R.style.Theme_Black_NoTitleBar) {
|
|
41
49
|
|
|
42
50
|
private val mainHandler = Handler(Looper.getMainLooper())
|
|
51
|
+
private val ioExecutor = Executors.newFixedThreadPool(3)
|
|
43
52
|
private var searchDebounce: Runnable? = null
|
|
44
53
|
|
|
45
|
-
private
|
|
54
|
+
private val localAssets: List<String> by lazy {
|
|
55
|
+
context.assets.list("Stickers")?.filter { it.endsWith(".png", ignoreCase = true) }?.sorted() ?: emptyList()
|
|
56
|
+
}
|
|
46
57
|
private var selectedGroup: String? = null // null == "All"
|
|
47
58
|
private var searchQuery: String = ""
|
|
48
|
-
private var loadError: Exception? = null
|
|
49
|
-
private var loading = true
|
|
50
59
|
|
|
51
60
|
private lateinit var searchInput: EditText
|
|
52
61
|
private lateinit var chipRow: LinearLayout
|
|
53
62
|
private lateinit var recycler: RecyclerView
|
|
54
|
-
private lateinit var progress: ProgressBar
|
|
55
|
-
private lateinit var statusText: TextView
|
|
56
|
-
private lateinit var retryButton: View
|
|
57
63
|
private lateinit var adapter: StickerAdapter
|
|
58
64
|
|
|
59
65
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
60
66
|
super.onCreate(savedInstanceState)
|
|
61
67
|
setContentView(buildRootView())
|
|
62
68
|
configureWindow()
|
|
63
|
-
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
override fun dismiss() {
|
|
72
|
+
ioExecutor.shutdownNow()
|
|
73
|
+
super.dismiss()
|
|
64
74
|
}
|
|
65
75
|
|
|
66
76
|
private fun configureWindow() {
|
|
@@ -76,7 +86,7 @@ class OnlineStickerSheet(
|
|
|
76
86
|
|
|
77
87
|
private fun buildRootView(): View {
|
|
78
88
|
val screenHeight = context.resources.displayMetrics.heightPixels
|
|
79
|
-
val sheetHeight = (screenHeight * 0.
|
|
89
|
+
val sheetHeight = (screenHeight * 0.78f).toInt()
|
|
80
90
|
|
|
81
91
|
val dimBackground = FrameLayout(context).apply {
|
|
82
92
|
setBackgroundColor(Color.argb(140, 0, 0, 0))
|
|
@@ -90,17 +100,47 @@ class OnlineStickerSheet(
|
|
|
90
100
|
setPadding(dp(DesignTokens.spaceLg), dp(DesignTokens.spaceMd), dp(DesignTokens.spaceLg), dp(DesignTokens.spaceLg))
|
|
91
101
|
}
|
|
92
102
|
|
|
93
|
-
//
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
103
|
+
// Functional drag handle with a generous touch target. The sheet follows
|
|
104
|
+
// the finger, dismisses past the threshold, or springs back into place.
|
|
105
|
+
var dragStartY = 0f
|
|
106
|
+
var sheetStartTranslation = 0f
|
|
107
|
+
val dragHandle = FrameLayout(context).apply {
|
|
108
|
+
isClickable = true
|
|
109
|
+
contentDescription = "Drag down to close stickers"
|
|
110
|
+
addView(
|
|
111
|
+
View(context).apply { setBackgroundColor(DesignTokens.outlineVariant) },
|
|
112
|
+
FrameLayout.LayoutParams(dp(40), dp(4), Gravity.CENTER)
|
|
113
|
+
)
|
|
114
|
+
setOnTouchListener { _, event ->
|
|
115
|
+
when (event.actionMasked) {
|
|
116
|
+
MotionEvent.ACTION_DOWN -> {
|
|
117
|
+
dragStartY = event.rawY
|
|
118
|
+
sheetStartTranslation = sheet.translationY
|
|
119
|
+
true
|
|
120
|
+
}
|
|
121
|
+
MotionEvent.ACTION_MOVE -> {
|
|
122
|
+
sheet.translationY = (sheetStartTranslation + event.rawY - dragStartY).coerceAtLeast(0f)
|
|
123
|
+
true
|
|
124
|
+
}
|
|
125
|
+
MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {
|
|
126
|
+
val shouldDismiss = sheet.translationY >= minOf(sheet.height * 0.18f, dp(140).toFloat())
|
|
127
|
+
if (shouldDismiss) {
|
|
128
|
+
sheet.animate().translationY(sheet.height.toFloat()).setDuration(180).withEndAction { dismiss() }.start()
|
|
129
|
+
} else {
|
|
130
|
+
sheet.animate().translationY(0f).setDuration(180).start()
|
|
131
|
+
}
|
|
132
|
+
true
|
|
133
|
+
}
|
|
134
|
+
else -> false
|
|
135
|
+
}
|
|
99
136
|
}
|
|
100
|
-
|
|
137
|
+
}
|
|
138
|
+
sheet.addView(dragHandle, LinearLayout.LayoutParams(MATCH, dp(28)).apply {
|
|
139
|
+
bottomMargin = dp(DesignTokens.spaceSm)
|
|
140
|
+
})
|
|
101
141
|
|
|
102
142
|
sheet.addView(TextView(context).apply {
|
|
103
|
-
text = "
|
|
143
|
+
text = "Stickers"
|
|
104
144
|
setTextColor(DesignTokens.textPrimary)
|
|
105
145
|
textSize = 16f
|
|
106
146
|
setTypeface(typeface, android.graphics.Typeface.BOLD)
|
|
@@ -140,43 +180,19 @@ class OnlineStickerSheet(
|
|
|
140
180
|
val gridContainer = FrameLayout(context)
|
|
141
181
|
|
|
142
182
|
recycler = RecyclerView(context).apply {
|
|
143
|
-
layoutManager = GridLayoutManager(context,
|
|
183
|
+
layoutManager = GridLayoutManager(context, 5)
|
|
184
|
+
setHasFixedSize(true)
|
|
144
185
|
}
|
|
145
186
|
adapter = StickerAdapter()
|
|
146
187
|
recycler.adapter = adapter
|
|
147
188
|
gridContainer.addView(recycler, FrameLayout.LayoutParams(MATCH, MATCH))
|
|
148
189
|
|
|
149
|
-
progress = ProgressBar(context)
|
|
150
|
-
gridContainer.addView(progress, FrameLayout.LayoutParams(WRAP, WRAP, Gravity.CENTER))
|
|
151
|
-
|
|
152
|
-
val statusContainer = LinearLayout(context).apply {
|
|
153
|
-
orientation = LinearLayout.VERTICAL
|
|
154
|
-
gravity = Gravity.CENTER
|
|
155
|
-
}
|
|
156
|
-
statusText = TextView(context).apply {
|
|
157
|
-
setTextColor(DesignTokens.textSecondary)
|
|
158
|
-
textSize = 13f
|
|
159
|
-
gravity = Gravity.CENTER
|
|
160
|
-
}
|
|
161
|
-
statusContainer.addView(statusText, LinearLayout.LayoutParams(WRAP, WRAP))
|
|
162
|
-
retryButton = editorPrimaryButton(context, "Retry") { loadCatalog() }
|
|
163
|
-
statusContainer.addView(retryButton, LinearLayout.LayoutParams(WRAP, dp(DesignTokens.touchTargetMin)).apply {
|
|
164
|
-
topMargin = dp(DesignTokens.spaceMd)
|
|
165
|
-
})
|
|
166
|
-
gridContainer.addView(statusContainer, FrameLayout.LayoutParams(WRAP, WRAP, Gravity.CENTER))
|
|
167
|
-
|
|
168
190
|
sheet.addView(gridContainer, LinearLayout.LayoutParams(MATCH, 0, 1f).apply {
|
|
169
191
|
topMargin = dp(DesignTokens.spaceMd)
|
|
170
192
|
})
|
|
171
193
|
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
setTextColor(DesignTokens.textSubtle)
|
|
175
|
-
textSize = 10f
|
|
176
|
-
gravity = Gravity.CENTER
|
|
177
|
-
}, LinearLayout.LayoutParams(MATCH, WRAP).apply { topMargin = dp(DesignTokens.spaceSm) })
|
|
178
|
-
|
|
179
|
-
updateStatusViews()
|
|
194
|
+
rebuildChips()
|
|
195
|
+
refreshList()
|
|
180
196
|
|
|
181
197
|
val root = FrameLayout(context)
|
|
182
198
|
root.addView(dimBackground, FrameLayout.LayoutParams(MATCH, MATCH))
|
|
@@ -194,27 +210,10 @@ class OnlineStickerSheet(
|
|
|
194
210
|
)
|
|
195
211
|
}
|
|
196
212
|
|
|
197
|
-
private fun loadCatalog() {
|
|
198
|
-
loading = true
|
|
199
|
-
loadError = null
|
|
200
|
-
updateStatusViews()
|
|
201
|
-
OpenMojiCatalog.load(context) { entries, error ->
|
|
202
|
-
loading = false
|
|
203
|
-
if (entries != null) {
|
|
204
|
-
allEntries = entries
|
|
205
|
-
loadError = null
|
|
206
|
-
rebuildChips()
|
|
207
|
-
refreshList()
|
|
208
|
-
} else {
|
|
209
|
-
loadError = error
|
|
210
|
-
}
|
|
211
|
-
updateStatusViews()
|
|
212
|
-
}
|
|
213
|
-
}
|
|
214
|
-
|
|
215
213
|
private fun rebuildChips() {
|
|
216
214
|
chipRow.removeAllViews()
|
|
217
|
-
val
|
|
215
|
+
val localGroups = localAssets.map { "local:" + it.substringBefore('_').lowercase() }.distinct()
|
|
216
|
+
val groups = listOf<String?>(null) + (if (runtimeStickers.isNotEmpty()) listOf("runtime") else emptyList()) + localGroups
|
|
218
217
|
groups.forEach { group ->
|
|
219
218
|
val label = group?.let { humanizeGroup(it) } ?: "All"
|
|
220
219
|
val chip = editorChip(context, label, selected = group == selectedGroup) {
|
|
@@ -227,32 +226,20 @@ class OnlineStickerSheet(
|
|
|
227
226
|
}
|
|
228
227
|
|
|
229
228
|
private fun humanizeGroup(group: String): String =
|
|
230
|
-
group.split("-").joinToString(" ") { it.replaceFirstChar(Char::uppercase) }
|
|
229
|
+
if (group == "runtime") "My stickers" else group.removePrefix("local:").split("-").joinToString(" ") { it.replaceFirstChar(Char::uppercase) }
|
|
231
230
|
|
|
232
231
|
private fun refreshList() {
|
|
233
232
|
val query = searchQuery.trim()
|
|
234
|
-
val
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
val group =
|
|
238
|
-
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
private fun updateStatusViews() {
|
|
245
|
-
progress.visibility = if (loading) View.VISIBLE else View.GONE
|
|
246
|
-
val error = loadError
|
|
247
|
-
val empty = !loading && error == null && adapter.itemCount == 0
|
|
248
|
-
recycler.visibility = if (!loading && error == null && !empty) View.VISIBLE else View.GONE
|
|
249
|
-
statusText.visibility = if (!loading && (error != null || empty)) View.VISIBLE else View.GONE
|
|
250
|
-
retryButton.visibility = if (!loading && error != null) View.VISIBLE else View.GONE
|
|
251
|
-
statusText.text = when {
|
|
252
|
-
error != null -> "Couldn't load stickers — check your connection."
|
|
253
|
-
empty -> "No stickers found."
|
|
254
|
-
else -> ""
|
|
255
|
-
}
|
|
233
|
+
val group = selectedGroup
|
|
234
|
+
val local = localAssets.filter { asset ->
|
|
235
|
+
val matchesQuery = query.isEmpty() || asset.contains(query, ignoreCase = true)
|
|
236
|
+
val matchesGroup = group == null || (group.startsWith("local:") && asset.startsWith(group.removePrefix("local:"), ignoreCase = true))
|
|
237
|
+
matchesQuery && matchesGroup
|
|
238
|
+
}.map { StickerItem(assetName = it) }
|
|
239
|
+
val runtime = runtimeStickers.filter {
|
|
240
|
+
(group == null || group == "runtime") && (query.isEmpty() || it.id.contains(query, ignoreCase = true))
|
|
241
|
+
}.map { StickerItem(runtime = it) }
|
|
242
|
+
adapter.submit(runtime + local)
|
|
256
243
|
}
|
|
257
244
|
|
|
258
245
|
private inner class StickerViewHolder(itemView: FrameLayout) : RecyclerView.ViewHolder(itemView) {
|
|
@@ -260,9 +247,9 @@ class OnlineStickerSheet(
|
|
|
260
247
|
}
|
|
261
248
|
|
|
262
249
|
private inner class StickerAdapter : RecyclerView.Adapter<StickerViewHolder>() {
|
|
263
|
-
private var items: List<
|
|
250
|
+
private var items: List<StickerItem> = emptyList()
|
|
264
251
|
|
|
265
|
-
fun submit(newItems: List<
|
|
252
|
+
fun submit(newItems: List<StickerItem>) {
|
|
266
253
|
items = newItems
|
|
267
254
|
notifyDataSetChanged()
|
|
268
255
|
}
|
|
@@ -270,12 +257,13 @@ class OnlineStickerSheet(
|
|
|
270
257
|
override fun getItemCount(): Int = items.size
|
|
271
258
|
|
|
272
259
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): StickerViewHolder {
|
|
273
|
-
val
|
|
260
|
+
val availableWidth = context.resources.displayMetrics.widthPixels - dp(DesignTokens.spaceLg * 2) - dp(8 * 5)
|
|
261
|
+
val cardSize = (availableWidth / 5).coerceAtLeast(dp(52))
|
|
274
262
|
val image = ImageView(context).apply {
|
|
275
263
|
scaleType = ImageView.ScaleType.CENTER_INSIDE
|
|
276
264
|
}
|
|
277
265
|
val cell = FrameLayout(context).apply {
|
|
278
|
-
background = roundedDrawable(DesignTokens.
|
|
266
|
+
background = roundedDrawable(DesignTokens.surfaceContainerLow, DesignTokens.radiusMd, context)
|
|
279
267
|
addView(image, FrameLayout.LayoutParams(cardSize - dp(16), cardSize - dp(16), Gravity.CENTER))
|
|
280
268
|
}
|
|
281
269
|
cell.layoutParams = RecyclerView.LayoutParams(cardSize, cardSize).apply {
|
|
@@ -285,24 +273,67 @@ class OnlineStickerSheet(
|
|
|
285
273
|
}
|
|
286
274
|
|
|
287
275
|
override fun onBindViewHolder(holder: StickerViewHolder, position: Int) {
|
|
288
|
-
val
|
|
289
|
-
|
|
290
|
-
|
|
276
|
+
val item = items[position]
|
|
277
|
+
holder.imageView.setImageDrawable(null)
|
|
278
|
+
item.assetName?.let { asset ->
|
|
279
|
+
holder.imageView.setImageBitmap(context.assets.open("Stickers/$asset").use { BitmapFactory.decodeStream(it) })
|
|
280
|
+
holder.itemView.setOnClickListener { onLocalStickerTapped(asset) }
|
|
281
|
+
}
|
|
282
|
+
item.runtime?.let { runtime ->
|
|
283
|
+
loadRuntimeThumbnail(runtime, holder.imageView)
|
|
284
|
+
holder.itemView.setOnClickListener { onRuntimeStickerTapped(runtime) }
|
|
285
|
+
}
|
|
291
286
|
}
|
|
292
287
|
}
|
|
293
288
|
|
|
294
|
-
private
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
289
|
+
private data class StickerItem(val assetName: String? = null, val runtime: RuntimeSticker? = null)
|
|
290
|
+
|
|
291
|
+
private fun loadRuntimeThumbnail(sticker: RuntimeSticker, imageView: ImageView) {
|
|
292
|
+
imageView.tag = sticker.uri
|
|
293
|
+
ioExecutor.execute {
|
|
294
|
+
val bitmap = try {
|
|
295
|
+
if (sticker.uri.startsWith("https://")) downloadRuntimeSticker(sticker.uri).inputStream().use(BitmapFactory::decodeStream)
|
|
296
|
+
else SourceResolver.resolvePath(context, sticker.uri, "pve_runtime_sticker")?.let(BitmapFactory::decodeFile)
|
|
297
|
+
} catch (_: Exception) { null }
|
|
298
|
+
mainHandler.post { if (imageView.tag == sticker.uri) imageView.setImageBitmap(bitmap) }
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
private fun onRuntimeStickerTapped(sticker: RuntimeSticker) {
|
|
303
|
+
ioExecutor.execute {
|
|
304
|
+
val path = try {
|
|
305
|
+
if (sticker.uri.startsWith("https://")) {
|
|
306
|
+
val output = File.createTempFile("pve_runtime_sticker_", ".img", context.cacheDir)
|
|
307
|
+
output.writeBytes(downloadRuntimeSticker(sticker.uri))
|
|
308
|
+
output.absolutePath
|
|
309
|
+
} else SourceResolver.resolvePath(context, sticker.uri, "pve_runtime_sticker")
|
|
310
|
+
} catch (_: Exception) { null }
|
|
311
|
+
mainHandler.post {
|
|
312
|
+
if (path != null) { onStickerPicked(path); dismiss() }
|
|
313
|
+
else Toast.makeText(context, "Couldn't open that sticker.", Toast.LENGTH_SHORT).show()
|
|
302
314
|
}
|
|
303
315
|
}
|
|
304
316
|
}
|
|
305
317
|
|
|
318
|
+
private fun downloadRuntimeSticker(uri: String): ByteArray {
|
|
319
|
+
val connection = URL(uri).openConnection().apply {
|
|
320
|
+
connectTimeout = 10_000
|
|
321
|
+
readTimeout = 15_000
|
|
322
|
+
}
|
|
323
|
+
return connection.getInputStream().use { it.readBytes() }
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
private fun onLocalStickerTapped(assetName: String) {
|
|
327
|
+
try {
|
|
328
|
+
val output = File(context.cacheDir, "sticker-${assetName.lowercase()}")
|
|
329
|
+
context.assets.open("Stickers/$assetName").use { input -> FileOutputStream(output).use(input::copyTo) }
|
|
330
|
+
onStickerPicked(output.absolutePath)
|
|
331
|
+
dismiss()
|
|
332
|
+
} catch (_: Exception) {
|
|
333
|
+
Toast.makeText(context, "Couldn't open that sticker.", Toast.LENGTH_SHORT).show()
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
306
337
|
companion object {
|
|
307
338
|
private const val MATCH = ViewGroup.LayoutParams.MATCH_PARENT
|
|
308
339
|
private const val WRAP = ViewGroup.LayoutParams.WRAP_CONTENT
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
package com.photovideoeditor.ui.components
|
|
2
|
+
|
|
3
|
+
import android.content.Context
|
|
4
|
+
import android.graphics.Canvas
|
|
5
|
+
import android.graphics.Color
|
|
6
|
+
import android.graphics.Paint
|
|
7
|
+
import android.graphics.drawable.GradientDrawable
|
|
8
|
+
import android.view.Gravity
|
|
9
|
+
import android.widget.*
|
|
10
|
+
import com.photovideoeditor.R
|
|
11
|
+
import com.photovideoeditor.photo.render.PhotoTransformState
|
|
12
|
+
import com.photovideoeditor.ui.DesignTokens
|
|
13
|
+
import kotlin.math.roundToInt
|
|
14
|
+
|
|
15
|
+
/** Shared photo/video crop surface. Only UI state lives here; the controller owns the draft. */
|
|
16
|
+
class CropPanel(
|
|
17
|
+
context: Context,
|
|
18
|
+
private val accent: Int,
|
|
19
|
+
surface: Int,
|
|
20
|
+
textColor: Int,
|
|
21
|
+
originalRatio: () -> Float,
|
|
22
|
+
onReset: () -> Unit,
|
|
23
|
+
onDone: () -> Unit,
|
|
24
|
+
onCancel: () -> Unit,
|
|
25
|
+
onStraighten: (Float) -> Unit,
|
|
26
|
+
onRotate: () -> Unit,
|
|
27
|
+
onRatio: (String, Float?) -> Unit
|
|
28
|
+
) : LinearLayout(context) {
|
|
29
|
+
private fun dp(value: Int) = dpToPx(value, context)
|
|
30
|
+
private val valueLabel = TextView(context)
|
|
31
|
+
private val slider: SeekBar
|
|
32
|
+
private val presets = mutableMapOf<String, LinearLayout>()
|
|
33
|
+
private var syncing = false
|
|
34
|
+
|
|
35
|
+
init {
|
|
36
|
+
orientation = VERTICAL
|
|
37
|
+
setPadding(dp(12), dp(8), dp(12), dp(8))
|
|
38
|
+
background = GradientDrawable().apply {
|
|
39
|
+
setColor(surface); cornerRadii = floatArrayOf(dp(20).toFloat(), dp(20).toFloat(), dp(20).toFloat(), dp(20).toFloat(), 0f, 0f, 0f, 0f)
|
|
40
|
+
}
|
|
41
|
+
fun icon(resource: Int, label: String, action: () -> Unit) = ImageButton(context).apply {
|
|
42
|
+
setImageResource(resource); setColorFilter(textColor); contentDescription = label
|
|
43
|
+
background = roundedDrawable(Color.TRANSPARENT, 12, context)
|
|
44
|
+
setOnClickListener { action() }; applyPressScale()
|
|
45
|
+
}
|
|
46
|
+
val header = LinearLayout(context).apply { gravity = Gravity.CENTER_VERTICAL }
|
|
47
|
+
val reset = LinearLayout(context).apply {
|
|
48
|
+
gravity = Gravity.CENTER_VERTICAL; isClickable = true; contentDescription = "Reset crop"
|
|
49
|
+
addView(ImageView(context).apply { setImageResource(R.drawable.ic_undo); setColorFilter(textColor) }, LayoutParams(dp(20), dp(20)))
|
|
50
|
+
addView(TextView(context).apply { text = "Reset"; textSize = 13f; setTextColor(textColor); setPadding(dp(6), 0, 0, 0) })
|
|
51
|
+
setOnClickListener { onReset() }; applyPressScale()
|
|
52
|
+
}
|
|
53
|
+
header.addView(reset, LayoutParams(dp(88), dp(48)))
|
|
54
|
+
header.addView(TextView(context).apply { text = "Crop"; textSize = 17f; gravity = Gravity.CENTER; setTextColor(textColor); setTypeface(typeface, 1) }, LayoutParams(0, dp(48), 1f))
|
|
55
|
+
header.addView(icon(R.drawable.ic_close, "Cancel crop", onCancel), LayoutParams(dp(44), dp(48)))
|
|
56
|
+
header.addView(icon(R.drawable.ic_check, "Apply crop", onDone).apply { setColorFilter(accent) }, LayoutParams(dp(44), dp(48)))
|
|
57
|
+
addView(header)
|
|
58
|
+
val labels = LinearLayout(context).apply { gravity = Gravity.CENTER_VERTICAL }
|
|
59
|
+
labels.addView(TextView(context).apply { text = "Straighten"; textSize = 13f; setTextColor(textColor) }, LayoutParams(0, dp(44), 1f))
|
|
60
|
+
valueLabel.setTextColor(accent); valueLabel.textSize = 13f; valueLabel.gravity = Gravity.CENTER
|
|
61
|
+
labels.addView(valueLabel, LayoutParams(dp(56), dp(44)))
|
|
62
|
+
labels.addView(icon(R.drawable.ic_undo, "Reset straighten") { onStraighten(0f) }, LayoutParams(dp(44), dp(44)))
|
|
63
|
+
labels.addView(icon(R.drawable.ic_rotate_right, "Rotate 90 degrees", onRotate), LayoutParams(dp(44), dp(44)))
|
|
64
|
+
addView(labels)
|
|
65
|
+
slider = object : SeekBar(context) {
|
|
66
|
+
private val tick = Paint(Paint.ANTI_ALIAS_FLAG).apply { strokeWidth = resources.displayMetrics.density; color = DesignTokens.outline }
|
|
67
|
+
override fun onDraw(canvas: Canvas) {
|
|
68
|
+
val start = paddingLeft.toFloat(); val end = width - paddingRight.toFloat()
|
|
69
|
+
for (i in 0..90) {
|
|
70
|
+
val x = start + (end - start) * i / 90
|
|
71
|
+
tick.alpha = if (i % 5 == 0) 160 else 65
|
|
72
|
+
canvas.drawLine(x, 6f, x, if (i % 5 == 0) dp(12).toFloat() else dp(7).toFloat(), tick)
|
|
73
|
+
}
|
|
74
|
+
super.onDraw(canvas)
|
|
75
|
+
}
|
|
76
|
+
}.apply {
|
|
77
|
+
max = 900; progress = 450; contentDescription = "Straighten, minus 45 to plus 45 degrees"
|
|
78
|
+
progressTintList = android.content.res.ColorStateList.valueOf(accent); thumbTintList = progressTintList
|
|
79
|
+
setPadding(dp(12), dp(12), dp(12), 0)
|
|
80
|
+
setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
|
|
81
|
+
override fun onProgressChanged(bar: SeekBar?, progress: Int, fromUser: Boolean) { if (fromUser && !syncing) onStraighten((progress - 450) / 10f) }
|
|
82
|
+
override fun onStartTrackingTouch(bar: SeekBar?) {}
|
|
83
|
+
override fun onStopTrackingTouch(bar: SeekBar?) {}
|
|
84
|
+
})
|
|
85
|
+
}
|
|
86
|
+
addView(slider, LayoutParams(LayoutParams.MATCH_PARENT, dp(48)))
|
|
87
|
+
val rail = LinearLayout(context).apply { gravity = Gravity.CENTER_VERTICAL }
|
|
88
|
+
val ratios = listOf("Free" to null, "Original" to null, "1:1" to 1f, "9:16" to 9f / 16, "16:9" to 16f / 9, "10:16" to 10f / 16, "16:10" to 16f / 10, "4:5" to 4f / 5, "5:4" to 5f / 4, "3:4" to 3f / 4, "4:3" to 4f / 3)
|
|
89
|
+
ratios.forEach { (name, ratio) ->
|
|
90
|
+
val tile = LinearLayout(context).apply {
|
|
91
|
+
orientation = VERTICAL; gravity = Gravity.CENTER; isClickable = true; isFocusable = true; contentDescription = "$name crop ratio"
|
|
92
|
+
addView(object : android.view.View(context) {
|
|
93
|
+
val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply { style = Paint.Style.STROKE; strokeWidth = dp(1).toFloat(); color = textColor }
|
|
94
|
+
override fun onDraw(canvas: Canvas) {
|
|
95
|
+
paint.color = if (this@apply.isSelected) accent else textColor
|
|
96
|
+
val aspect = if (name == "Original") originalRatio() else ratio ?: 1.25f
|
|
97
|
+
val w = minOf(dp(24).toFloat(), dp(24) * aspect); val h = w / aspect
|
|
98
|
+
canvas.drawRoundRect((width-w)/2, (height-h)/2, (width+w)/2, (height+h)/2, dp(2).toFloat(), dp(2).toFloat(), paint)
|
|
99
|
+
}
|
|
100
|
+
}, LayoutParams(dp(40), dp(38)))
|
|
101
|
+
addView(TextView(context).apply { text = name; textSize = 11f; gravity = Gravity.CENTER; setTextColor(textColor) })
|
|
102
|
+
setOnClickListener { onRatio(name, if (name == "Original") originalRatio() else ratio) }; applyPressScale()
|
|
103
|
+
}
|
|
104
|
+
presets[name] = tile
|
|
105
|
+
rail.addView(tile, LayoutParams(dp(64), dp(68)))
|
|
106
|
+
}
|
|
107
|
+
addView(HorizontalScrollView(context).apply { isHorizontalScrollBarEnabled = false; addView(rail) }, LayoutParams(LayoutParams.MATCH_PARENT, dp(72)))
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
fun sync(state: PhotoTransformState) {
|
|
111
|
+
syncing = true
|
|
112
|
+
slider.progress = (state.straightenDegrees * 10 + 450).roundToInt()
|
|
113
|
+
valueLabel.text = String.format(java.util.Locale.US, "%.1f°", state.straightenDegrees)
|
|
114
|
+
presets.forEach { (name, tile) ->
|
|
115
|
+
tile.isSelected = name == state.aspectPreset
|
|
116
|
+
tile.background = roundedDrawable(if (tile.isSelected) (accent and 0x00FFFFFF) or 0x22000000 else Color.TRANSPARENT, 12, context)
|
|
117
|
+
(tile.getChildAt(1) as TextView).setTextColor(if (tile.isSelected) accent else DesignTokens.textSecondary)
|
|
118
|
+
tile.getChildAt(0).invalidate()
|
|
119
|
+
}
|
|
120
|
+
syncing = false
|
|
121
|
+
}
|
|
122
|
+
}
|