Feat: Complete 14 sliders integration, remove pen button and implement custom 3-second long press (10_FIX_SLIDER.md)

This commit is contained in:
2026-07-05 18:16:39 +07:00
parent 1a74887fe0
commit bcbb853558
5 changed files with 530 additions and 112 deletions
+221
View File
@@ -0,0 +1,221 @@
Dưới đây là kế hoạch hoàn thiện toàn bộ danh sách các thanh kéo (Sliders) trong bảng Floating Card mờ cho tệp cấu hình JSON đầy đủ của bạn. Chúng ta sẽ đưa tất cả 14 thông số chia theo đúng 4 nhóm cấu trúc của tệp cấu hình vào trong giao diện cuộn dọc của `ScrollView`.
---
## 📐 1. Cấu Trúc Đầy Đủ Cho Layout XML (`activity_main.xml`)
Thay thế phần nội dung bên trong `LinearLayout` của bảng Floating Card (nằm dưới tiêu đề) để bổ sung đầy đủ tất cả các thanh kéo, phân chia theo từng nhóm trực quan giúp người dùng dễ thao tác.
```xml
<!-- Đặt bên trong LinearLayout của bảng Floating Card mờ (Lớp 3 của cameraContainer) -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<!-- ================= NHÓM 1: CƠ BẢN (BASIC ADJUSTMENTS) ================= -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="ĐIỀU CHỈNH CƠ BẢN"
android:textColor="#FF9800"
android:textStyle="bold"
android:textSize="11sp"
android:layout_marginTop="8dp"
android:layout_marginBottom="4dp"/>
<!-- Brightness -->
<TextView android:id="@+id/tvLabelBrightness" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Brightness (Độ sáng): 0.00"/>
<com.google.android.material.slider.Slider android:id="@+id/sliderBrightness" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="-1.0" android:valueTo="1.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800"/>
<!-- Contrast -->
<TextView android:id="@+id/tvLabelContrast" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Contrast (Tương phản): 0.00"/>
<com.google.android.material.slider.Slider android:id="@+id/sliderContrast" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="-1.0" android:valueTo="1.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800"/>
<!-- Saturation -->
<TextView android:id="@+id/tvLabelSaturation" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Saturation (Bão hòa màu): 0.00"/>
<com.google.android.material.slider.Slider android:id="@+id/sliderSaturation" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="-1.0" android:valueTo="1.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800"/>
<!-- Vibrance -->
<TextView android:id="@+id/tvLabelVibrance" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Vibrance (Bão hòa thông minh): 0.00"/>
<com.google.android.material.slider.Slider android:id="@+id/sliderVibrance" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="-1.0" android:valueTo="1.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800"/>
<!-- Temperature -->
<TextView android:id="@+id/tvLabelTemperature" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Temperature (Nhiệt màu): 0.00"/>
<com.google.android.material.slider.Slider android:id="@+id/sliderTemperature" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="-1.0" android:valueTo="1.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800"/>
<!-- Tint -->
<TextView android:id="@+id/tvLabelTint" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Tint (Sắc độ hồng/xanh): 0.00"/>
<com.google.android.material.slider.Slider android:id="@+id/sliderTint" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="-1.0" android:valueTo="1.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800"/>
<!-- ================= NHÓM 2: NÂNG CAO (ADVANCED EFFECTS) ================= -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="HIỆU ỨNG NÂNG CAO"
android:textColor="#FF9800"
android:textStyle="bold"
android:textSize="11sp"
android:layout_marginTop="12dp"
android:layout_marginBottom="4dp"/>
<!-- Clarity -->
<TextView android:id="@+id/tvLabelClarity" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Clarity (Độ rõ nét vùng trung tính): 0.00"/>
<com.google.android.material.slider.Slider android:id="@+id/sliderClarity" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="-1.0" android:valueTo="1.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800"/>
<!-- Dehaze -->
<TextView android:id="@+id/tvLabelDehaze" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Dehaze (Độ trong suốt / Sương mù): 0.00"/>
<com.google.android.material.slider.Slider android:id="@+id/sliderDehaze" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="-1.0" android:valueTo="1.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800"/>
<!-- Vignette Amount -->
<TextView android:id="@+id/tvLabelVignetteAmount" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Vignette Amount (Bo tối góc): 0.00"/>
<!-- Hiệu ứng bo tối góc đi từ 0.0 (Không bo) đến 1.0 (Bo đen tối đa) -->
<com.google.android.material.slider.Slider android:id="@+id/sliderVignetteAmount" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="0.0" android:valueTo="1.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800"/>
<!-- ================= NHÓM 3: GIẢ LẬP ĐƯỜNG CONG MÀU (TONE CURVE EMULATION) ================= -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="GIẢ LẬP TONE CURVE &amp; MÀU VÙNG TỐI"
android:textColor="#FF9800"
android:textStyle="bold"
android:textSize="11sp"
android:layout_marginTop="12dp"
android:layout_marginBottom="4dp"/>
<!-- Faded Black Level -->
<TextView android:id="@+id/tvLabelFadedBlack" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Faded Black Level (Độ mờ vùng đen): 0.00"/>
<com.google.android.material.slider.Slider android:id="@+id/sliderFadedBlack" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="0.0" android:valueTo="1.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800"/>
<!-- Shadows Tint R -->
<TextView android:id="@+id/tvLabelShadowsTintR" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Shadows Tint R (Sắc Đỏ vùng tối): 0.00"/>
<com.google.android.material.slider.Slider android:id="@+id/sliderShadowsTintR" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="0.0" android:valueTo="1.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800"/>
<!-- Shadows Tint G -->
<TextView android:id="@+id/tvLabelShadowsTintG" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Shadows Tint G (Sắc Lục vùng tối): 0.00"/>
<com.google.android.material.slider.Slider android:id="@+id/sliderShadowsTintG" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="0.0" android:valueTo="1.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800"/>
<!-- Shadows Tint B -->
<TextView android:id="@+id/tvLabelShadowsTintB" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Shadows Tint B (Sắc Lam vùng tối): 0.00"/>
<com.google.android.material.slider.Slider android:id="@+id/sliderShadowsTintB" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="0.0" android:valueTo="1.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800"/>
<!-- ================= NHÓM 4: HẠT NHIỄU PHIM (FILM GRAIN) ================= -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="HẠT NHIỄU PHIM"
android:textColor="#FF9800"
android:textStyle="bold"
android:textSize="11sp"
android:layout_marginTop="12dp"
android:layout_marginBottom="4dp"/>
<!-- Grain Amount -->
<TextView android:id="@+id/tvLabelGrainAmount" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Grain Amount (Mật độ hạt): 0.00"/>
<com.google.android.material.slider.Slider android:id="@+id/sliderGrainAmount" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="0.0" android:valueTo="1.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800"/>
<!-- Grain Size -->
<TextView android:id="@+id/tvLabelGrainSize" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Grain Size (Kích thước hạt): 0.00"/>
<com.google.android.material.slider.Slider android:id="@+id/sliderGrainSize" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="0.0" android:valueTo="1.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800"/>
</LinearLayout>
```
---
## 💻 2. Logic Khởi Tạo & Đồng Bộ Giá Trị (Kotlin trong `MainActivity.kt`)
Khi người dùng mở bảng chỉnh sửa cho preset, hàm `openAdvancedSliders` sẽ thực hiện gán các giá trị ban đầu từ tệp JSON lên các thanh Slider phần cứng đồng thời thiết lập lắng nghe cử chỉ:
```kotlin
fun openAdvancedSliders(selectedPreset: ColorPreset) {
editingPreset = selectedPreset.deepCopy() // Nhân bản dữ liệu để tránh ghi đè bản gốc
binding.tvEditPresetName.text = "Chỉnh sửa: ${editingPreset!!.name}"
// 1. Nạp dữ liệu Nhóm Basic
binding.sliderBrightness.value = editingPreset!!.basic.brightness
binding.sliderContrast.value = editingPreset!!.basic.contrast
binding.sliderSaturation.value = editingPreset!!.basic.saturation
binding.sliderVibrance.value = editingPreset!!.basic.vibrance
binding.sliderTemperature.value = editingPreset!!.basic.temperature
binding.sliderTint.value = editingPreset!!.basic.tint
// 2. Nạp dữ liệu Nhóm Advanced
binding.sliderClarity.value = editingPreset!!.advanced.clarity
binding.sliderDehaze.value = editingPreset!!.advanced.dehaze
binding.sliderVignetteAmount.value = editingPreset!!.advanced.vignetteAmount
// 3. Nạp dữ liệu Nhóm Tone Curve Emulation
binding.sliderFadedBlack.value = editingPreset!!.toneCurve.fadedBlackLevel
binding.sliderShadowsTintR.value = editingPreset!!.toneCurve.shadowsTintR
binding.sliderShadowsTintG.value = editingPreset!!.toneCurve.shadowsTintG
binding.sliderShadowsTintB.value = editingPreset!!.toneCurve.shadowsTintB
// 4. Nạp dữ liệu Nhóm Film Grain
binding.sliderGrainAmount.value = editingPreset!!.grain.grainAmount
binding.sliderGrainSize.value = editingPreset!!.grain.grainSize
// Gọi hàm thiết lập sự kiện lắng nghe chuyển động cho toàn bộ Sliders
setupSliderListeners()
// Hiển thị Card mờ floating lọt lòng màn hình camera như ảnh image_2be399.jpg
binding.cardAdvancedSliders.visibility = View.VISIBLE
}
```
---
## 🔄 3. Cơ Chế Cập Nhật Giá Trị Thời Gian Thực (Real-time Pipeline)
Viết một hàm tập trung gán sự kiện thay đổi (`addOnChangeListener`) cho tất cả các thanh kéo. Khi con trỏ dịch chuyển, chữ số nhãn text sẽ đổi ngay lập tức và đẩy cấu trúc Object mới sang GPU xử lý trực tiếp trên kính ngắm.
```kotlin
private fun setupSliderListeners() {
// Ví dụ gán cho Brightness
binding.sliderBrightness.addOnChangeListener { _, value, _ ->
binding.tvLabelBrightness.text = String.format("Brightness (Độ sáng): %.2f", value)
editingPreset?.basic?.brightness = value
applyLiveFilterToCamera(editingPreset) // Đẩy ma trận màu sang GPU render
}
// Ví dụ gán cho Vibrance
binding.sliderVibrance.addOnChangeListener { _, value, _ ->
binding.tvLabelVibrance.text = String.format("Vibrance (Bão hòa thông minh): %.2f", value)
editingPreset?.basic?.vibrance = value
applyLiveFilterToCamera(editingPreset)
}
// Ví dụ gán cho Clarity (Nhóm Advanced)
binding.sliderClarity.addOnChangeListener { _, value, _ ->
binding.tvLabelClarity.text = String.format("Clarity (Độ rõ nét vùng trung tính): %.2f", value)
editingPreset?.advanced?.clarity = value
applyLiveFilterToCamera(editingPreset)
}
// Ví dụ gán cho Faded Black Level (Nhóm Tone Curve)
binding.sliderFadedBlack.addOnChangeListener { _, value, _ ->
binding.tvLabelFadedBlack.text = String.format("Faded Black Level (Độ mờ vùng đen): %.2f", value)
editingPreset?.toneCurve?.fadedBlackLevel = value
applyLiveFilterToCamera(editingPreset)
}
// Ví dụ gán cho Grain Amount (Nhóm Film Grain)
binding.sliderGrainAmount.addOnChangeListener { _, value, _ ->
binding.tvLabelGrainAmount.text = String.format("Grain Amount (Mật độ hạt): %.2f", value)
editingPreset?.grain?.grainAmount = value
applyLiveFilterToCamera(editingPreset)
}
// Thực hiện viết gán tương tự cho toàn bộ các thanh Sliders còn lại trong danh sách...
}
```
Bảng cấu trúc 14 thông số này sẽ tạo ra khả năng tùy biến bộ lọc ảnh mạnh mẽ không thua kém gì các bộ công cụ chỉnh ảnh phim chuyên nghiệp như VSCO hay Lightroom Mobile.
@@ -136,19 +136,30 @@ class MainActivity : AppCompatActivity() {
val contrast = extractJsonDoubleField(content, "contrast").toFloat()
val saturation = extractJsonDoubleField(content, "saturation").toFloat()
val vibrance = extractJsonDoubleField(content, "vibrance").toFloat()
val temperature = extractJsonDoubleField(content, "temperature").toFloat()
val tint = extractJsonDoubleField(content, "tint").toFloat()
val clarity = extractJsonDoubleField(content, "clarity").toFloat()
val dehaze = extractJsonDoubleField(content, "dehaze").toFloat()
val vignetteAmount = extractJsonDoubleField(content, "vignette_amount").toFloat()
val fadedBlackLevel = extractJsonDoubleField(content, "faded_black_level").toFloat()
val shadowsTintR = extractJsonDoubleField(content, "shadows_tint_r").toFloat()
val shadowsTintG = extractJsonDoubleField(content, "shadows_tint_g").toFloat()
val shadowsTintB = extractJsonDoubleField(content, "shadows_tint_b").toFloat()
val grainAmount = extractJsonDoubleField(content, "grain_amount").toFloat()
val grainSize = extractJsonDoubleField(content, "grain_size").toFloat()
val preset = ColorPreset(
id = id,
name = name,
themeCategory = category,
isEditable = true,
basic = BasicAdjustments(brightness, contrast, saturation, vibrance, 0f, 0f),
advanced = AdvancedEffects(clarity, dehaze, 0f),
toneCurve = ToneCurveEmulation(0f, 0f, 0f, 0f),
grain = FilmGrain(0f, 0f)
basic = BasicAdjustments(brightness, contrast, saturation, vibrance, temperature, tint),
advanced = AdvancedEffects(clarity, dehaze, vignetteAmount),
toneCurve = ToneCurveEmulation(fadedBlackLevel, shadowsTintR, shadowsTintG, shadowsTintB),
grain = FilmGrain(grainAmount, grainSize)
)
customList.add(preset)
} catch (e: Exception) {
@@ -213,11 +224,22 @@ class MainActivity : AppCompatActivity() {
"instax_bw_cool" -> android.graphics.Color.argb(80, 128, 128, 128)
"instax_cool_summer" -> android.graphics.Color.argb(45, 0, 188, 212)
else -> {
// Map user configured RGB overlay incorporating Vibrance, Clarity, and Dehaze
val alpha = ((preset.basic.vibrance + 1.0f) * 45 + 15).toInt().coerceIn(10, 110)
val r = ((preset.advanced.clarity + 1.0f) * 115 + 20).toInt().coerceIn(0, 255)
val g = ((preset.basic.contrast + 1.0f) * 100 + 20).toInt().coerceIn(0, 255)
val b = ((preset.advanced.dehaze + 1.0f) * 95 + 15).toInt().coerceIn(0, 255)
// Sophisticated ARGB mapping to simulate film filters using adjustment parameters
// Vibrance and Saturation increase opacity of the filter color
val alpha = (((preset.basic.vibrance + preset.basic.saturation) / 2f + 1.0f) * 40 + 20).toInt().coerceIn(10, 120)
// Temperature (+ is warm/red-yellow), Brightness, Clarity, and ShadowsTintR affect red
val tempWarmth = if (preset.basic.temperature > 0) preset.basic.temperature * 50f else 0f
val r = (((preset.advanced.clarity + preset.basic.brightness) / 2f + 1.0f) * 100 + 30 + tempWarmth + (preset.toneCurve.shadowsTintR * 30)).toInt().coerceIn(0, 255)
// Contrast, Tint (- is green), and ShadowsTintG affect green
val tintGreen = if (preset.basic.tint < 0) -preset.basic.tint * 50f else 0f
val g = ((preset.basic.contrast + 1.0f) * 90 + 20 + tintGreen + (preset.toneCurve.shadowsTintG * 30)).toInt().coerceIn(0, 255)
// Dehaze, cool Temperature (- is blue), and ShadowsTintB affect blue
val tempCoolness = if (preset.basic.temperature < 0) -preset.basic.temperature * 60f else 0f
val b = (((preset.advanced.dehaze + 1.0f) * 80) + 20 + tempCoolness + (preset.toneCurve.shadowsTintB * 30)).toInt().coerceIn(0, 255)
android.graphics.Color.argb(alpha, r, g, b)
}
}
@@ -308,6 +330,27 @@ class MainActivity : AppCompatActivity() {
}
// Advanced Sliders listeners
binding.sliderBrightness.addOnChangeListener { _, value, _ ->
editingPreset?.let { preset ->
preset.basic.brightness = value
binding.tvLabelBrightness.text = "Brightness (Độ sáng): ${String.format("%.2f", value)}"
applyPresetFilter(preset)
}
}
binding.sliderContrast.addOnChangeListener { _, value, _ ->
editingPreset?.let { preset ->
preset.basic.contrast = value
binding.tvLabelContrast.text = "Contrast (Tương phản): ${String.format("%.2f", value)}"
applyPresetFilter(preset)
}
}
binding.sliderSaturation.addOnChangeListener { _, value, _ ->
editingPreset?.let { preset ->
preset.basic.saturation = value
binding.tvLabelSaturation.text = "Saturation (Bão hòa màu): ${String.format("%.2f", value)}"
applyPresetFilter(preset)
}
}
binding.sliderVibrance.addOnChangeListener { _, value, _ ->
editingPreset?.let { preset ->
preset.basic.vibrance = value
@@ -315,6 +358,20 @@ class MainActivity : AppCompatActivity() {
applyPresetFilter(preset)
}
}
binding.sliderTemperature.addOnChangeListener { _, value, _ ->
editingPreset?.let { preset ->
preset.basic.temperature = value
binding.tvLabelTemperature.text = "Temperature (Nhiệt màu): ${String.format("%.2f", value)}"
applyPresetFilter(preset)
}
}
binding.sliderTint.addOnChangeListener { _, value, _ ->
editingPreset?.let { preset ->
preset.basic.tint = value
binding.tvLabelTint.text = "Tint (Sắc độ hồng/xanh): ${String.format("%.2f", value)}"
applyPresetFilter(preset)
}
}
binding.sliderClarity.addOnChangeListener { _, value, _ ->
editingPreset?.let { preset ->
preset.advanced.clarity = value
@@ -329,6 +386,55 @@ class MainActivity : AppCompatActivity() {
applyPresetFilter(preset)
}
}
binding.sliderVignetteAmount.addOnChangeListener { _, value, _ ->
editingPreset?.let { preset ->
preset.advanced.vignetteAmount = value
binding.tvLabelVignetteAmount.text = "Vignette Amount (Bo tối góc): ${String.format("%.2f", value)}"
applyPresetFilter(preset)
}
}
binding.sliderFadedBlack.addOnChangeListener { _, value, _ ->
editingPreset?.let { preset ->
preset.toneCurve.fadedBlackLevel = value
binding.tvLabelFadedBlack.text = "Faded Black Level (Độ mờ vùng đen): ${String.format("%.2f", value)}"
applyPresetFilter(preset)
}
}
binding.sliderShadowsTintR.addOnChangeListener { _, value, _ ->
editingPreset?.let { preset ->
preset.toneCurve.shadowsTintR = value
binding.tvLabelShadowsTintR.text = "Shadows Tint R (Sắc Đỏ vùng tối): ${String.format("%.2f", value)}"
applyPresetFilter(preset)
}
}
binding.sliderShadowsTintG.addOnChangeListener { _, value, _ ->
editingPreset?.let { preset ->
preset.toneCurve.shadowsTintG = value
binding.tvLabelShadowsTintG.text = "Shadows Tint G (Sắc Lục vùng tối): ${String.format("%.2f", value)}"
applyPresetFilter(preset)
}
}
binding.sliderShadowsTintB.addOnChangeListener { _, value, _ ->
editingPreset?.let { preset ->
preset.toneCurve.shadowsTintB = value
binding.tvLabelShadowsTintB.text = "Shadows Tint B (Sắc Lam vùng tối): ${String.format("%.2f", value)}"
applyPresetFilter(preset)
}
}
binding.sliderGrainAmount.addOnChangeListener { _, value, _ ->
editingPreset?.let { preset ->
preset.grain.grainAmount = value
binding.tvLabelGrainAmount.text = "Grain Amount (Mật độ hạt): ${String.format("%.2f", value)}"
applyPresetFilter(preset)
}
}
binding.sliderGrainSize.addOnChangeListener { _, value, _ ->
editingPreset?.let { preset ->
preset.grain.grainSize = value
binding.tvLabelGrainSize.text = "Grain Size (Kích thước hạt): ${String.format("%.2f", value)}"
applyPresetFilter(preset)
}
}
binding.btnCancelEdit.setOnClickListener {
closeAdvancedSliders()
@@ -433,14 +539,38 @@ class MainActivity : AppCompatActivity() {
binding.tvEditPresetName.text = "Chỉnh sửa: ${selectedPreset.name}"
// Update Sliders visual values
binding.sliderBrightness.value = editingPreset!!.basic.brightness
binding.sliderContrast.value = editingPreset!!.basic.contrast
binding.sliderSaturation.value = editingPreset!!.basic.saturation
binding.sliderVibrance.value = editingPreset!!.basic.vibrance
binding.sliderTemperature.value = editingPreset!!.basic.temperature
binding.sliderTint.value = editingPreset!!.basic.tint
binding.sliderClarity.value = editingPreset!!.advanced.clarity
binding.sliderDehaze.value = editingPreset!!.advanced.dehaze
binding.sliderVignetteAmount.value = editingPreset!!.advanced.vignetteAmount
binding.sliderFadedBlack.value = editingPreset!!.toneCurve.fadedBlackLevel
binding.sliderShadowsTintR.value = editingPreset!!.toneCurve.shadowsTintR
binding.sliderShadowsTintG.value = editingPreset!!.toneCurve.shadowsTintG
binding.sliderShadowsTintB.value = editingPreset!!.toneCurve.shadowsTintB
binding.sliderGrainAmount.value = editingPreset!!.grain.grainAmount
binding.sliderGrainSize.value = editingPreset!!.grain.grainSize
// Update text labels
binding.tvLabelBrightness.text = "Brightness (Độ sáng): ${String.format("%.2f", editingPreset!!.basic.brightness)}"
binding.tvLabelContrast.text = "Contrast (Tương phản): ${String.format("%.2f", editingPreset!!.basic.contrast)}"
binding.tvLabelSaturation.text = "Saturation (Bão hòa màu): ${String.format("%.2f", editingPreset!!.basic.saturation)}"
binding.tvLabelVibrance.text = "Vibrance (Bão hòa thông minh): ${String.format("%.2f", editingPreset!!.basic.vibrance)}"
binding.tvLabelTemperature.text = "Temperature (Nhiệt màu): ${String.format("%.2f", editingPreset!!.basic.temperature)}"
binding.tvLabelTint.text = "Tint (Sắc độ hồng/xanh): ${String.format("%.2f", editingPreset!!.basic.tint)}"
binding.tvLabelClarity.text = "Clarity (Độ rõ nét vùng trung tính): ${String.format("%.2f", editingPreset!!.advanced.clarity)}"
binding.tvLabelDehaze.text = "Dehaze (Độ trong suốt / Sương mù): ${String.format("%.2f", editingPreset!!.advanced.dehaze)}"
binding.tvLabelVignetteAmount.text = "Vignette Amount (Bo tối góc): ${String.format("%.2f", editingPreset!!.advanced.vignetteAmount)}"
binding.tvLabelFadedBlack.text = "Faded Black Level (Độ mờ vùng đen): ${String.format("%.2f", editingPreset!!.toneCurve.fadedBlackLevel)}"
binding.tvLabelShadowsTintR.text = "Shadows Tint R (Sắc Đỏ vùng tối): ${String.format("%.2f", editingPreset!!.toneCurve.shadowsTintR)}"
binding.tvLabelShadowsTintG.text = "Shadows Tint G (Sắc Lục vùng tối): ${String.format("%.2f", editingPreset!!.toneCurve.shadowsTintG)}"
binding.tvLabelShadowsTintB.text = "Shadows Tint B (Sắc Lam vùng tối): ${String.format("%.2f", editingPreset!!.toneCurve.shadowsTintB)}"
binding.tvLabelGrainAmount.text = "Grain Amount (Mật độ hạt): ${String.format("%.2f", editingPreset!!.grain.grainAmount)}"
binding.tvLabelGrainSize.text = "Grain Size (Kích thước hạt): ${String.format("%.2f", editingPreset!!.grain.grainSize)}"
// Scale animation from center
binding.cardAdvancedSliders.apply {
@@ -503,13 +633,32 @@ class MainActivity : AppCompatActivity() {
name = newName,
themeCategory = "Custom",
isEditable = true,
basic = BasicAdjustments(tempPreset.basic.brightness, tempPreset.basic.contrast, tempPreset.basic.saturation, tempPreset.basic.vibrance, 0f, 0f),
advanced = AdvancedEffects(tempPreset.advanced.clarity, tempPreset.advanced.dehaze, 0f),
toneCurve = ToneCurveEmulation(0f, 0f, 0f, 0f),
grain = FilmGrain(0f, 0f)
basic = BasicAdjustments(
tempPreset.basic.brightness,
tempPreset.basic.contrast,
tempPreset.basic.saturation,
tempPreset.basic.vibrance,
tempPreset.basic.temperature,
tempPreset.basic.tint
),
advanced = AdvancedEffects(
tempPreset.advanced.clarity,
tempPreset.advanced.dehaze,
tempPreset.advanced.vignetteAmount
),
toneCurve = ToneCurveEmulation(
tempPreset.toneCurve.fadedBlackLevel,
tempPreset.toneCurve.shadowsTintR,
tempPreset.toneCurve.shadowsTintG,
tempPreset.toneCurve.shadowsTintB
),
grain = FilmGrain(
tempPreset.grain.grainAmount,
tempPreset.grain.grainSize
)
)
// Serialize to JSON format manually
// Serialize to JSON format manually (incorporating all 14 parameters)
val jsonString = """
{
"id": "$customId",
@@ -521,23 +670,23 @@ class MainActivity : AppCompatActivity() {
"contrast": ${customPreset.basic.contrast},
"saturation": ${customPreset.basic.saturation},
"vibrance": ${customPreset.basic.vibrance},
"temperature": 0.0,
"tint": 0.0
"temperature": ${customPreset.basic.temperature},
"tint": ${customPreset.basic.tint}
},
"advanced_effects": {
"clarity": ${customPreset.advanced.clarity},
"dehaze": ${customPreset.advanced.dehaze},
"vignette_amount": 0.0
"vignette_amount": ${customPreset.advanced.vignetteAmount}
},
"tone_curve_emulation": {
"faded_black_level": 0.0,
"shadows_tint_r": 0.0,
"shadows_tint_g": 0.0,
"shadows_tint_b": 0.0
"faded_black_level": ${customPreset.toneCurve.fadedBlackLevel},
"shadows_tint_r": ${customPreset.toneCurve.shadowsTintR},
"shadows_tint_g": ${customPreset.toneCurve.shadowsTintG},
"shadows_tint_b": ${customPreset.toneCurve.shadowsTintB}
},
"film_grain": {
"grain_amount": 0.0,
"grain_size": 0.0
"grain_amount": ${customPreset.grain.grainAmount},
"grain_size": ${customPreset.grain.grainSize}
}
}
""".trimIndent()
@@ -18,13 +18,11 @@ class PresetAdapter(
) : RecyclerView.Adapter<PresetAdapter.ViewHolder>() {
private var selectedPosition = 0
private var editingPosition = -1
inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
val cardPreset: MaterialCardView = view.findViewById(R.id.cardPreset)
val viewPresetColor: View = view.findViewById(R.id.viewPresetColor)
val txtPresetName: TextView = view.findViewById(R.id.txtPresetName)
val btnSmallEdit: ImageButton = view.findViewById(R.id.btnSmallEdit)
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
@@ -55,13 +53,6 @@ class PresetAdapter(
holder.viewPresetColor.background = circleDrawable
}
// Show edit button on long press
if (position == editingPosition && preset.isEditable) {
holder.btnSmallEdit.visibility = View.VISIBLE
} else {
holder.btnSmallEdit.visibility = View.GONE
}
// Highlight selection
val strokeWidthPx = (3 * holder.itemView.context.resources.displayMetrics.density).toInt()
if (position == selectedPosition) {
@@ -72,30 +63,53 @@ class PresetAdapter(
holder.cardPreset.strokeWidth = 0
}
holder.itemView.setOnClickListener {
val previousSelected = selectedPosition
selectedPosition = position
notifyItemChanged(previousSelected)
notifyItemChanged(selectedPosition)
onPresetSelected(preset)
// Setup custom 3-second long press detector with touch slop cancellation
val handler = android.os.Handler(android.os.Looper.getMainLooper())
var isLongPressTriggered = false
var startX = 0f
var startY = 0f
val touchSlop = 15f // pixels
val longPressRunnable = Runnable {
if (preset.isEditable) {
isLongPressTriggered = true
holder.itemView.performHapticFeedback(android.view.HapticFeedbackConstants.LONG_PRESS)
onEditClicked(preset)
}
}
holder.itemView.setOnLongClickListener {
if (preset.isEditable) {
val prevEditing = editingPosition
editingPosition = if (editingPosition == position) -1 else position
notifyItemChanged(prevEditing)
if (editingPosition != -1) {
notifyItemChanged(editingPosition)
holder.itemView.setOnTouchListener { view, event ->
when (event.action) {
android.view.MotionEvent.ACTION_DOWN -> {
isLongPressTriggered = false
startX = event.x
startY = event.y
handler.postDelayed(longPressRunnable, 3000)
}
android.view.MotionEvent.ACTION_MOVE -> {
if (Math.abs(event.x - startX) > touchSlop || Math.abs(event.y - startY) > touchSlop) {
handler.removeCallbacks(longPressRunnable)
}
}
android.view.MotionEvent.ACTION_UP -> {
handler.removeCallbacks(longPressRunnable)
if (!isLongPressTriggered) {
val previousSelected = selectedPosition
selectedPosition = holder.adapterPosition
if (selectedPosition != RecyclerView.NO_POSITION) {
notifyItemChanged(previousSelected)
notifyItemChanged(selectedPosition)
onPresetSelected(preset)
view.performClick()
}
}
}
android.view.MotionEvent.ACTION_CANCEL -> {
handler.removeCallbacks(longPressRunnable)
}
holder.itemView.performHapticFeedback(android.view.HapticFeedbackConstants.LONG_PRESS)
}
true
}
holder.btnSmallEdit.setOnClickListener {
onEditClicked(preset)
}
}
override fun getItemCount(): Int = presets.size
@@ -108,7 +122,6 @@ class PresetAdapter(
fun resetSelection() {
val previousSelected = selectedPosition
selectedPosition = 0
editingPosition = -1
notifyItemChanged(previousSelected)
notifyItemChanged(selectedPosition)
}
@@ -151,65 +151,112 @@
android:contentDescription="Cancel Edit" />
</RelativeLayout>
<!-- ================= DANH SÁCH CÁC CỤM SLIDERS ĐỂ CUỘN ================= -->
<!-- 1. VIBRANCE -->
<!-- ================= NHÓM 1: CƠ BẢN (BASIC ADJUSTMENTS) ================= -->
<TextView
android:id="@+id/tvLabelVibrance"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Vibrance (Bão hòa thông minh): 0.0"
android:textColor="#FFFFFF"
android:textSize="12sp" />
<com.google.android.material.slider.Slider
android:id="@+id/sliderVibrance"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:valueFrom="-1.0"
android:valueTo="1.0"
android:value="0.0"
app:thumbColor="#FF9800"
app:trackColorActive="#FF9800"
app:trackColorInactive="#333333" />
android:text="ĐIỀU CHỈNH CƠ BẢN"
android:textColor="#FF9800"
android:textStyle="bold"
android:textSize="11sp"
android:layout_marginTop="8dp"
android:layout_marginBottom="4dp"/>
<!-- 2. CLARITY -->
<TextView
android:id="@+id/tvLabelClarity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Clarity (Độ rõ nét vùng trung tính): 0.0"
android:textColor="#FFFFFF"
android:textSize="12sp"
android:layout_marginTop="8dp" />
<com.google.android.material.slider.Slider
android:id="@+id/sliderClarity"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:valueFrom="-1.0"
android:valueTo="1.0"
android:value="0.0"
app:thumbColor="#FF9800"
app:trackColorActive="#FF9800"
app:trackColorInactive="#333333" />
<!-- Brightness -->
<TextView android:id="@+id/tvLabelBrightness" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Brightness (Độ sáng): 0.00"/>
<com.google.android.material.slider.Slider android:id="@+id/sliderBrightness" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="-1.0" android:valueTo="1.0" android:value="0.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800" app:trackColorInactive="#333333"/>
<!-- 3. DEHAZE -->
<!-- Contrast -->
<TextView android:id="@+id/tvLabelContrast" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Contrast (Tương phản): 0.00"/>
<com.google.android.material.slider.Slider android:id="@+id/sliderContrast" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="-1.0" android:valueTo="1.0" android:value="0.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800" app:trackColorInactive="#333333"/>
<!-- Saturation -->
<TextView android:id="@+id/tvLabelSaturation" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Saturation (Bão hòa màu): 0.00"/>
<com.google.android.material.slider.Slider android:id="@+id/sliderSaturation" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="-1.0" android:valueTo="1.0" android:value="0.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800" app:trackColorInactive="#333333"/>
<!-- Vibrance -->
<TextView android:id="@+id/tvLabelVibrance" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Vibrance (Bão hòa thông minh): 0.00"/>
<com.google.android.material.slider.Slider android:id="@+id/sliderVibrance" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="-1.0" android:valueTo="1.0" android:value="0.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800" app:trackColorInactive="#333333"/>
<!-- Temperature -->
<TextView android:id="@+id/tvLabelTemperature" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Temperature (Nhiệt màu): 0.00"/>
<com.google.android.material.slider.Slider android:id="@+id/sliderTemperature" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="-1.0" android:valueTo="1.0" android:value="0.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800" app:trackColorInactive="#333333"/>
<!-- Tint -->
<TextView android:id="@+id/tvLabelTint" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Tint (Sắc độ hồng/xanh): 0.00"/>
<com.google.android.material.slider.Slider android:id="@+id/sliderTint" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="-1.0" android:valueTo="1.0" android:value="0.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800" app:trackColorInactive="#333333"/>
<!-- ================= NHÓM 2: NÂNG CAO (ADVANCED EFFECTS) ================= -->
<TextView
android:id="@+id/tvLabelDehaze"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Dehaze (Độ trong suốt / Sương mù): 0.0"
android:textColor="#FFFFFF"
android:textSize="12sp"
android:layout_marginTop="8dp" />
<com.google.android.material.slider.Slider
android:id="@+id/sliderDehaze"
android:text="HIỆU ỨNG NÂNG CAO"
android:textColor="#FF9800"
android:textStyle="bold"
android:textSize="11sp"
android:layout_marginTop="12dp"
android:layout_marginBottom="4dp"/>
<!-- Clarity -->
<TextView android:id="@+id/tvLabelClarity" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Clarity (Độ rõ nét vùng trung tính): 0.00"/>
<com.google.android.material.slider.Slider android:id="@+id/sliderClarity" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="-1.0" android:valueTo="1.0" android:value="0.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800" app:trackColorInactive="#333333"/>
<!-- Dehaze -->
<TextView android:id="@+id/tvLabelDehaze" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Dehaze (Độ trong suốt / Sương mù): 0.00"/>
<com.google.android.material.slider.Slider android:id="@+id/sliderDehaze" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="-1.0" android:valueTo="1.0" android:value="0.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800" app:trackColorInactive="#333333"/>
<!-- Vignette Amount -->
<TextView android:id="@+id/tvLabelVignetteAmount" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Vignette Amount (Bo tối góc): 0.00"/>
<com.google.android.material.slider.Slider android:id="@+id/sliderVignetteAmount" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="0.0" android:valueTo="1.0" android:value="0.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800" app:trackColorInactive="#333333"/>
<!-- ================= NHÓM 3: GIẢ LẬP ĐƯỜNG CONG MÀU (TONE CURVE EMULATION) ================= -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:valueFrom="-1.0"
android:valueTo="1.0"
android:value="0.0"
app:thumbColor="#FF9800"
app:trackColorActive="#FF9800"
app:trackColorInactive="#333333" />
android:text="GIẢ LẬP TONE CURVE &amp; MÀU VÙNG TỐI"
android:textColor="#FF9800"
android:textStyle="bold"
android:textSize="11sp"
android:layout_marginTop="12dp"
android:layout_marginBottom="4dp"/>
<!-- Faded Black Level -->
<TextView android:id="@+id/tvLabelFadedBlack" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Faded Black Level (Độ mờ vùng đen): 0.00"/>
<com.google.android.material.slider.Slider android:id="@+id/sliderFadedBlack" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="0.0" android:valueTo="1.0" android:value="0.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800" app:trackColorInactive="#333333"/>
<!-- Shadows Tint R -->
<TextView android:id="@+id/tvLabelShadowsTintR" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Shadows Tint R (Sắc Đỏ vùng tối): 0.00"/>
<com.google.android.material.slider.Slider android:id="@+id/sliderShadowsTintR" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="0.0" android:valueTo="1.0" android:value="0.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800" app:trackColorInactive="#333333"/>
<!-- Shadows Tint G -->
<TextView android:id="@+id/tvLabelShadowsTintG" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Shadows Tint G (Sắc Lục vùng tối): 0.00"/>
<com.google.android.material.slider.Slider android:id="@+id/sliderShadowsTintG" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="0.0" android:valueTo="1.0" android:value="0.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800" app:trackColorInactive="#333333"/>
<!-- Shadows Tint B -->
<TextView android:id="@+id/tvLabelShadowsTintB" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Shadows Tint B (Sắc Lam vùng tối): 0.00"/>
<com.google.android.material.slider.Slider android:id="@+id/sliderShadowsTintB" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="0.0" android:valueTo="1.0" android:value="0.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800" app:trackColorInactive="#333333"/>
<!-- ================= NHÓM 4: HẠT NHIỄU PHIM (FILM GRAIN) ================= -->
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="HẠT NHIỄU PHIM"
android:textColor="#FF9800"
android:textStyle="bold"
android:textSize="11sp"
android:layout_marginTop="12dp"
android:layout_marginBottom="4dp"/>
<!-- Grain Amount -->
<TextView android:id="@+id/tvLabelGrainAmount" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Grain Amount (Mật độ hạt): 0.00"/>
<com.google.android.material.slider.Slider android:id="@+id/sliderGrainAmount" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="0.0" android:valueTo="1.0" android:value="0.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800" app:trackColorInactive="#333333"/>
<!-- Grain Size -->
<TextView android:id="@+id/tvLabelGrainSize" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp" android:text="Grain Size (Kích thước hạt): 0.00"/>
<com.google.android.material.slider.Slider android:id="@+id/sliderGrainSize" android:layout_width="match_parent" android:layout_height="wrap_content" android:valueFrom="0.0" android:valueTo="1.0" android:value="0.0" app:thumbColor="#FF9800" app:trackColorActive="#FF9800" app:trackColorInactive="#333333"/>
<!-- NÚT LƯU THÀNH PRESET MỚI -->
<Button
@@ -41,16 +41,4 @@
android:text="Preset" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
<!-- Nút edit nhỏ ở góc trên bên phải, hiển thị đè đục lên card -->
<ImageButton
android:id="@+id/btnSmallEdit"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="top|end"
android:background="@android:color/transparent"
android:src="@drawable/ic_edit_small"
android:visibility="gone"
app:tint="@color/theme_primary"
android:contentDescription="Edit Preset" />
</FrameLayout>