From 2308fe6fa9f6d5689dc985ed3c95c06d5c86cf4d Mon Sep 17 00:00:00 2001 From: 3dtours Date: Sun, 5 Jul 2026 17:55:32 +0700 Subject: [PATCH] Feat: Implement advanced sliders real-time color overlay panel (8_SLIDER_TABLE.md) --- .../java/com/photobooth/app/MainActivity.kt | 170 +++++++++--------- .../src/main/res/drawable/ic_close_white.xml | 9 + .../app/src/main/res/layout/activity_main.xml | 109 +++++++++++ assets/frames/instaxframe.png | Bin 0 -> 421105 bytes 4 files changed, 203 insertions(+), 85 deletions(-) create mode 100644 android-project/app/src/main/res/drawable/ic_close_white.xml create mode 100644 assets/frames/instaxframe.png diff --git a/android-project/app/src/main/java/com/photobooth/app/MainActivity.kt b/android-project/app/src/main/java/com/photobooth/app/MainActivity.kt index b7661cd..ab2a7ee 100644 --- a/android-project/app/src/main/java/com/photobooth/app/MainActivity.kt +++ b/android-project/app/src/main/java/com/photobooth/app/MainActivity.kt @@ -43,6 +43,7 @@ class MainActivity : AppCompatActivity() { private var isPresetModeOpen = false private var currentSelectedFramePath: String? = null private var currentSelectedPreset: ColorPreset? = null + private var editingPreset: ColorPreset? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -136,13 +137,16 @@ class MainActivity : AppCompatActivity() { val saturation = extractJsonDoubleField(content, "saturation").toFloat() val vibrance = extractJsonDoubleField(content, "vibrance").toFloat() + val clarity = extractJsonDoubleField(content, "clarity").toFloat() + val dehaze = extractJsonDoubleField(content, "dehaze").toFloat() + val preset = ColorPreset( id = id, name = name, themeCategory = category, isEditable = true, basic = BasicAdjustments(brightness, contrast, saturation, vibrance, 0f, 0f), - advanced = AdvancedEffects(0f, 0f, 0f), + advanced = AdvancedEffects(clarity, dehaze, 0f), toneCurve = ToneCurveEmulation(0f, 0f, 0f, 0f), grain = FilmGrain(0f, 0f) ) @@ -177,7 +181,7 @@ class MainActivity : AppCompatActivity() { applyPresetFilter(preset) updatePresetModeIcon(preset) }, { preset -> - showEditPresetDialog(preset) + openAdvancedSliders(preset) }) } @@ -209,11 +213,11 @@ 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 - val alpha = ((preset.basic.vibrance + 1.0f) * 40 + 20).toInt().coerceIn(10, 100) - val r = ((preset.basic.brightness + 1.0f) * 127).toInt().coerceIn(0, 255) + // 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.basic.saturation + 1.0f) * 80 + 10).toInt().coerceIn(0, 255) + val b = ((preset.advanced.dehaze + 1.0f) * 95 + 15).toInt().coerceIn(0, 255) android.graphics.Color.argb(alpha, r, g, b) } } @@ -302,6 +306,39 @@ class MainActivity : AppCompatActivity() { binding.btnShutterCapture.setOnClickListener { onCaptureClick() } + + // Advanced Sliders listeners + binding.sliderVibrance.addOnChangeListener { _, value, _ -> + editingPreset?.let { preset -> + preset.basic.vibrance = value + binding.lblVibrance.text = "Vibrance (Bão hòa thông minh): ${String.format("%.2f", value)}" + applyPresetFilter(preset) + } + } + binding.sliderClarity.addOnChangeListener { _, value, _ -> + editingPreset?.let { preset -> + preset.advanced.clarity = value + binding.lblClarity.text = "Clarity (Độ rõ nét vùng trung tính): ${String.format("%.2f", value)}" + applyPresetFilter(preset) + } + } + binding.sliderDehaze.addOnChangeListener { _, value, _ -> + editingPreset?.let { preset -> + preset.advanced.dehaze = value + binding.lblDehaze.text = "Dehaze (Độ trong suốt / Sương mù): ${String.format("%.2f", value)}" + applyPresetFilter(preset) + } + } + + binding.btnCancelEdit.setOnClickListener { + closeAdvancedSliders() + } + + binding.btnSaveCustomPreset.setOnClickListener { + editingPreset?.let { preset -> + showSavePresetDialog(preset) + } + } } private fun toggleFrameMode() { @@ -385,87 +422,49 @@ class MainActivity : AppCompatActivity() { } } - private fun showEditPresetDialog(preset: ColorPreset) { - val dialogView = layoutInflater.inflate(R.layout.dialog_edit_preset, null) - - val txtBrightness: android.widget.TextView = dialogView.findViewById(R.id.txtLabelBrightness) - val seekBrightness: android.widget.SeekBar = dialogView.findViewById(R.id.seekBrightness) - - val txtContrast: android.widget.TextView = dialogView.findViewById(R.id.txtLabelContrast) - val seekContrast: android.widget.SeekBar = dialogView.findViewById(R.id.seekContrast) - - val txtSaturation: android.widget.TextView = dialogView.findViewById(R.id.txtLabelSaturation) - val seekSaturation: android.widget.SeekBar = dialogView.findViewById(R.id.seekSaturation) - - val txtVibrance: android.widget.TextView = dialogView.findViewById(R.id.txtLabelVibrance) - val seekVibrance: android.widget.SeekBar = dialogView.findViewById(R.id.seekVibrance) - - // Set current values mapping (-1.0f..1.0f -> 0..200) - seekBrightness.progress = ((preset.basic.brightness + 1.0f) * 100).toInt() - seekContrast.progress = ((preset.basic.contrast + 1.0f) * 100).toInt() - seekSaturation.progress = ((preset.basic.saturation + 1.0f) * 100).toInt() - seekVibrance.progress = ((preset.basic.vibrance + 1.0f) * 100).toInt() - - txtBrightness.text = "Độ sáng (Brightness): ${String.format("%.2f", preset.basic.brightness)}" - txtContrast.text = "Độ tương phản (Contrast): ${String.format("%.2f", preset.basic.contrast)}" - txtSaturation.text = "Độ bão hòa (Saturation): ${String.format("%.2f", preset.basic.saturation)}" - txtVibrance.text = "Độ sống động (Vibrance): ${String.format("%.2f", preset.basic.vibrance)}" - - // Clone current preset to modify values temporarily - val tempPreset = ColorPreset( - id = preset.id, - name = preset.name, - themeCategory = preset.themeCategory, - isEditable = preset.isEditable, - basic = BasicAdjustments(preset.basic.brightness, preset.basic.contrast, preset.basic.saturation, preset.basic.vibrance, preset.basic.temperature, preset.basic.tint), - advanced = AdvancedEffects(preset.advanced.clarity, preset.advanced.dehaze, preset.advanced.vignetteAmount), - toneCurve = ToneCurveEmulation(preset.toneCurve.fadedBlackLevel, preset.toneCurve.shadowsTintR, preset.toneCurve.shadowsTintG, preset.toneCurve.shadowsTintB), - grain = FilmGrain(preset.grain.grainAmount, preset.grain.grainSize) + private fun openAdvancedSliders(selectedPreset: ColorPreset) { + editingPreset = selectedPreset.copy( + basic = selectedPreset.basic.copy(), + advanced = selectedPreset.advanced.copy(), + toneCurve = selectedPreset.toneCurve.copy(), + grain = selectedPreset.grain.copy() ) - val seekBarListener = object : android.widget.SeekBar.OnSeekBarChangeListener { - override fun onProgressChanged(seekBar: android.widget.SeekBar?, progress: Int, fromUser: Boolean) { - val valFloat = (progress - 100) / 100f - when (seekBar?.id) { - R.id.seekBrightness -> { - tempPreset.basic.brightness = valFloat - txtBrightness.text = "Độ sáng (Brightness): ${String.format("%.2f", valFloat)}" - } - R.id.seekContrast -> { - tempPreset.basic.contrast = valFloat - txtContrast.text = "Độ tương phản (Contrast): ${String.format("%.2f", valFloat)}" - } - R.id.seekSaturation -> { - tempPreset.basic.saturation = valFloat - txtSaturation.text = "Độ bão hòa (Saturation): ${String.format("%.2f", valFloat)}" - } - R.id.seekVibrance -> { - tempPreset.basic.vibrance = valFloat - txtVibrance.text = "Độ sống động (Vibrance): ${String.format("%.2f", valFloat)}" - } - } - // Live preview camera filter overlay while sliding seekbar! - applyPresetFilter(tempPreset) - } - override fun onStartTrackingTouch(seekBar: android.widget.SeekBar?) {} - override fun onStopTrackingTouch(seekBar: android.widget.SeekBar?) {} + binding.tvEditPresetName.text = "Chỉnh sửa: ${selectedPreset.name}" + + // Update Sliders visual values + binding.sliderVibrance.value = editingPreset!!.basic.vibrance + binding.sliderClarity.value = editingPreset!!.advanced.clarity + binding.sliderDehaze.value = editingPreset!!.advanced.dehaze + + // Update text labels + binding.lblVibrance.text = "Vibrance (Bão hòa thông minh): ${String.format("%.2f", editingPreset!!.basic.vibrance)}" + binding.lblClarity.text = "Clarity (Độ rõ nét vùng trung tính): ${String.format("%.2f", editingPreset!!.advanced.clarity)}" + binding.lblDehaze.text = "Dehaze (Độ trong suốt / Sương mù): ${String.format("%.2f", editingPreset!!.advanced.dehaze)}" + + // Slide up overlay panel animation + binding.layoutAdvancedSliders.apply { + visibility = android.view.View.VISIBLE + alpha = 0f + translationY = 200f + animate() + .alpha(1f) + .translationY(0f) + .setDuration(300) + .start() } + } - seekBrightness.setOnSeekBarChangeListener(seekBarListener) - seekContrast.setOnSeekBarChangeListener(seekBarListener) - seekSaturation.setOnSeekBarChangeListener(seekBarListener) - seekVibrance.setOnSeekBarChangeListener(seekBarListener) - - androidx.appcompat.app.AlertDialog.Builder(this) - .setView(dialogView) - .setPositiveButton("Lưu") { _, _ -> - showSavePresetDialog(tempPreset) + private fun closeAdvancedSliders() { + binding.layoutAdvancedSliders.animate() + .alpha(0f) + .translationY(200f) + .setDuration(250) + .withEndAction { + binding.layoutAdvancedSliders.visibility = android.view.View.GONE + currentSelectedPreset?.let { applyPresetFilter(it) } } - .setNegativeButton("Hủy") { dialog, _ -> - applyPresetFilter(preset) // Restore - dialog.dismiss() - } - .show() + .start() } private fun showSavePresetDialog(tempPreset: ColorPreset) { @@ -501,7 +500,7 @@ class MainActivity : AppCompatActivity() { themeCategory = "Custom", isEditable = true, basic = BasicAdjustments(tempPreset.basic.brightness, tempPreset.basic.contrast, tempPreset.basic.saturation, tempPreset.basic.vibrance, 0f, 0f), - advanced = AdvancedEffects(0f, 0f, 0f), + advanced = AdvancedEffects(tempPreset.advanced.clarity, tempPreset.advanced.dehaze, 0f), toneCurve = ToneCurveEmulation(0f, 0f, 0f, 0f), grain = FilmGrain(0f, 0f) ) @@ -522,8 +521,8 @@ class MainActivity : AppCompatActivity() { "tint": 0.0 }, "advanced_effects": { - "clarity": 0.0, - "dehaze": 0.0, + "clarity": ${customPreset.advanced.clarity}, + "dehaze": ${customPreset.advanced.dehaze}, "vignette_amount": 0.0 }, "tone_curve_emulation": { @@ -547,6 +546,7 @@ class MainActivity : AppCompatActivity() { colorPresets = colorPresets + customPreset presetAdapter.updatePresets(colorPresets) Toast.makeText(this, "Đã lưu preset: $newName", Toast.LENGTH_SHORT).show() + closeAdvancedSliders() } catch (e: Exception) { Toast.makeText(this, "Không thể lưu Preset file", Toast.LENGTH_SHORT).show() } diff --git a/android-project/app/src/main/res/drawable/ic_close_white.xml b/android-project/app/src/main/res/drawable/ic_close_white.xml new file mode 100644 index 0000000..6dd59cc --- /dev/null +++ b/android-project/app/src/main/res/drawable/ic_close_white.xml @@ -0,0 +1,9 @@ + + + diff --git a/android-project/app/src/main/res/layout/activity_main.xml b/android-project/app/src/main/res/layout/activity_main.xml index a9ec0f7..402eab8 100644 --- a/android-project/app/src/main/res/layout/activity_main.xml +++ b/android-project/app/src/main/res/layout/activity_main.xml @@ -103,6 +103,115 @@ android:text="3" /> + + + + + + + + + + + + + + + + + + + + + + + +