# 🛠️ KẾ HOẠCH FIX BUG: CHUẨN HÓA LOGIC SLIDERS & FIX LỖI TRÀN KHUNG HIỂN THỊ

Mục tiêu:

1. Khắc phục triệt để lỗi gán nhầm thông số xử lý ảnh (ví dụ: kéo Brightness lại đổi Temperature).
2. Chuyển đổi dải giá trị từ số thập phân nhỏ sang thang đo nguyên trực quan từ `-100` đến `+100` để người dùng thấy rõ sự thay đổi.
3. Cô lập nội dung cuộn bên trong phạm vi bo góc của `MaterialCardView` để không bị tràn đè lên khung viền của bảng.

---

## 📐 1. Khắc Phục Lỗi Giao Diện Tràn Khung Biển (`activity_main.xml`)

**Nguyên nhân lỗi:** Do tệp cấu hình cũ đặt thuộc tính `padding` trực tiếp lên thẻ cha hoặc thẻ `LinearLayout` bên trong mà không giới hạn vùng hiển thị đồ họa của thẻ cuộn `ScrollView`. Khi vuốt, các phần tử chữ và thanh kéo sẽ trượt đè qua các góc bo tròn của hộp Floating Card.

**Giải pháp:** Di chuyển toàn bộ cấu hình `padding` vào bên trong thẻ `ScrollView` và ép cấu trúc cắt vùng đồ họa thừa bằng thuộc tính `android:clipToPadding="true"`.

```xml
<!-- LỚP 3: BẢNG SLIDERS FLOATING (Nằm gọn bên trong ảnh cameraContainer) -->
<com.google.android.material.card.MaterialCardView
    android:id="@+id/cardAdvancedSliders"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:layout_margin="24dp"
    app:cardCornerRadius="12dp"
    app:cardElevation="8dp"
    android:visibility="gone"
    app:strokeWidth="0dp"
    android:backgroundTint="#A6000000">

    <!-- FIX LỖI ĐÈ KHUNG: Đưa padding và clipToPadding vào ScrollView -->
    <ScrollView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fillViewport="true"
        android:scrollbars="vertical"
        android:padding="16dp"
        android:clipToPadding="true"> <!-- Ép nội dung cuộn phải biến mất khi chạm tới viền bo góc -->

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <!-- Tiêu đề & Nút X (Đóng) -->
            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginBottom="16dp">
                <TextView
                    android:id="@+id/tvEditPresetName"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:textColor="#FFFFFF"
                    android:textStyle="bold"
                    android:textSize="15sp" />
                <ImageButton
                    android:id="@+id/btnCancelEdit"
                    android:layout_width="32dp"
                    android:layout_height="32dp"
                    android:layout_alignParentEnd="true"
                    android:background="@null"
                    android:src="@drawable/ic_close_white" />
            </RelativeLayout>

            <!-- ================= CẬP NHẬT DẢI GIÁ TRỊ TOÀN BỘ SLIDERS (-100 đến 100) ================= -->
            <!-- Brightness -->
            <TextView android:id="@+id/tvLabelBrightness" android:layout_width="match_parent" android:layout_height="wrap_content" android:textColor="#FFFFFF" android:textSize="11sp"/>
            <com.google.android.material.slider.Slider 
                android:id="@+id/sliderBrightness" 
                android:layout_width="match_parent" 
                android:layout_height="wrap_content" 
                android:valueFrom="-100.0" 
                android:valueTo="100.0" 
                android:value="0.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"/>
            <com.google.android.material.slider.Slider 
                android:id="@+id/sliderTemperature" 
                android:layout_width="match_parent" 
                android:layout_height="wrap_content" 
                android:valueFrom="-100.0" 
                android:valueTo="100.0" 
                android:value="0.0"
                app:thumbColor="#FF9800" 
                app:trackColorActive="#FF9800"/>

            <!-- Áp dụng tương tự cho tất cả 12 thanh Sliders còn lại cấu hình valueFrom="-100.0" và valueTo="100.0" -->
            <!-- Đối với các thuộc tính đi từ 0 (Vignette, Grain, Faded Black) thì cấu hình valueFrom="0.0" và valueTo="100.0" -->

            <!-- NÚT LƯU THÀNH PRESET MỚI -->
            <Button
                android:id="@+id/btnSaveCustomPreset"
                android:layout_width="match_parent"
                android:layout_height="48dp"
                android:layout_marginTop="20dp"
                android:text="LƯU THÀNH PRESET MỚI"
                android:textColor="#FFFFFF"
                android:backgroundTint="#FF9800"
                android:textStyle="bold" />

        </LinearLayout>
    </ScrollView>
</com.google.android.material.card.MaterialCardView>

```

---

## 💻 2. Chuẩn Hóa Logic Gán Hàm Độc Lập & Quy Đổi Tỷ Lệ Giá Trị (Kotlin)

**Giải pháp sửa lỗi gán sai thông số:** Quy hoạch lại chính xác từng sự kiện lắng nghe để tránh việc copy-paste mã lệnh bị trùng lặp biến.

**Giải pháp dải giá trị lớn:** Người dùng nhìn thấy và kéo trượt trên thang đo nguyên trực quan từ `-100` đến `+100` (để thấy tác động lớn, rõ rệt trên UI). Tuy nhiên, trước khi gán dữ liệu vào hàm `applyLiveFilterToCamera` để GPU xử lý, ta phải **chia cho 100** để đưa về dải số thực chuẩn hóa (`-1.0` đến `1.0`) giúp lõi phần cứng đồ họa không bị lỗi tính toán (vì Shader/CoreImage nhận giá trị số thực từ `-1` đến `1`).

```kotlin
private fun setupSliderListeners() {

    // FIX 1: Chuẩn hóa gán đúng hàm cho Brightness
    binding.sliderBrightness.addOnChangeListener { _, value, _ ->
        // Hiển thị số nguyên (-100 đến 100) lên nhãn văn bản cho người dùng thấy
        binding.tvLabelBrightness.text = String.format("Brightness (Độ sáng): %.0f", value)
        
        // Quy đổi tỉ lệ về dải số thực (-1.0 đến 1.0) để gán cho Object cấu hình
        val normalizedValue = value / 100f
        editingPreset?.basic?.brightness = normalizedValue
        
        // Đẩy giá trị ĐÚNG của Brightness sang bộ lọc GPU
        applyLiveFilterToCamera(editingPreset)
    }

    // FIX 2: Chuẩn hóa gán đúng hàm cho Temperature (Không bị nhầm với Brightness nữa)
    binding.sliderTemperature.addOnChangeListener { _, value, _ ->
        binding.tvLabelTemperature.text = String.format("Temperature (Nhiệt màu): %.0f", value)
        
        val normalizedValue = value / 100f
        editingPreset?.basic?.temperature = normalizedValue // Gán chuẩn xác cho biến temperature
        
        applyLiveFilterToCamera(editingPreset)
    }

    // FIX 3: Chuẩn hóa gán đúng hàm cho Vibrance
    binding.sliderVibrance.addOnChangeListener { _, value, _ ->
        binding.tvLabelVibrance.text = String.format("Vibrance (Bão hòa thông minh): %.0f", value)
        
        val normalizedValue = value / 100f
        editingPreset?.basic?.vibrance = normalizedValue // Gán chuẩn xác cho biến vibrance
        
        applyLiveFilterToCamera(editingPreset)
    }

    // FIX 4: Chuẩn hóa cho hiệu ứng nhiễu hạt Film Grain (Dải giá trị từ 0 đến 100)
    binding.sliderGrainAmount.addOnChangeListener { _, value, _ ->
        binding.tvLabelGrainAmount.text = String.format("Grain Amount (Mật độ hạt): %.0f%%", value)
        
        val normalizedValue = value / 100f
        editingPreset?.grain?.grainAmount = normalizedValue // Gán chuẩn xác cho biến grainAmount
        
        applyLiveFilterToCamera(editingPreset)
    }
    
    // Tiến hành rà soát lại tất cả các khối listener còn lại đảm bảo đúng biến nào - hàm đó!
}

```

---

## 🔄 3. Cơ Chế Nạp Lại Giá Trị Gốc Lên Sliders (Quy Đổi Ngược Nhân 100)

Khi người dùng mở bảng chỉnh sửa, chúng ta phải làm một phép tính ngược lại: Lấy giá trị số thực từ tệp JSON, **nhân với 100** để hiển thị đúng vị trí con trỏ trên thanh kéo `-100 -> +100`.

```kotlin
fun openAdvancedSliders(selectedPreset: ColorPreset) {
    editingPreset = selectedPreset.deepCopy()

    binding.tvEditPresetName.text = "Chỉnh sửa: ${editingPreset!!.name}"

    // Đọc giá trị số thực từ JSON, nhân 100 để hiển thị dải số nguyên rõ rệt trên thanh trượt
    binding.sliderBrightness.value = editingPreset!!.basic.brightness * 100f
    binding.sliderTemperature.value = editingPreset!!.basic.temperature * 100f
    binding.sliderVibrance.value = editingPreset!!.basic.vibrance * 100f
    binding.sliderClarity.value = editingPreset!!.advanced.clarity * 100f
    binding.sliderDehaze.value = editingPreset!!.advanced.dehaze * 100f
    
    binding.sliderGrainAmount.value = editingPreset!!.grain.grainAmount * 100f
    binding.sliderGrainSize.value = editingPreset!!.grain.grainSize * 100f

    // Cập nhật text nhãn ban đầu
    binding.tvLabelBrightness.text = String.format("Brightness (Độ sáng): %.0f", binding.sliderBrightness.value)
    binding.tvLabelTemperature.text = String.format("Temperature (Nhiệt màu): %.0f", binding.sliderTemperature.value)

    setupSliderListeners()
    binding.cardAdvancedSliders.visibility = View.VISIBLE
}

```

---

## 📅 4. Các Đầu Việc Cần Hoàn Thành (Checklist)

* [ ] Thực hiện thay đổi cấu trúc `ScrollView` trong tệp XML để cô lập hoàn toàn nội dung cuộn, giải quyết triệt để lỗi tràn khung hiển thị.
* [ ] Tiến hành cập nhật lại toàn bộ 14 hàm lắng nghe sự kiện `addOnChangeListener`, rà soát kỹ lưỡng từng dòng lệnh để đảm bảo tính độc lập tuyệt đối giữa các thông số.
* [ ] Chạy thử nghiệm kéo trượt mạnh về hai cực đầu mút (`-100` và `100`) trên thiết bị thật để kiểm tra độ trễ phản hồi hình ảnh và biên độ thay đổi của màu ảnh xem đã đủ rõ rệt chưa.