202310715130-Dwifebbryanti-EAS/CODE_SNIPPETS_REFERENCE.md
2026-01-14 21:33:58 +07:00

13 KiB

💾 CODE SNIPPETS REFERENCE - Cepat Copy-Paste

📌 Useful Code Blocks untuk Development


🔐 PASSWORD HASHING (Future Enhancement)

Jika ingin menambahkan password hashing di Phase 2:

Method Hashing

import java.security.MessageDigest
import android.util.Base64

fun hashPassword(password: String): String {
    val md = MessageDigest.getInstance("SHA-256")
    val hashedBytes = md.digest(password.toByteArray())
    return Base64.encodeToString(hashedBytes, Base64.NO_WRAP)
}

fun verifyPassword(password: String, hash: String): Boolean {
    return hashPassword(password) == hash
}

Usage di DatabaseHelper

fun addUser(username: String, npm: String, pass: String): Boolean {
    return try {
        val db = this.writableDatabase
        val values = ContentValues()
        values.put(COLUMN_USERNAME, username)
        values.put(COLUMN_NPM, npm)
        values.put(COLUMN_PASSWORD, hashPassword(pass))  // ← Hash sebelum simpan
        val result = db.insert(TABLE_USERS, null, values)
        result != -1L
    } catch (e: Exception) {
        android.util.Log.e("DatabaseHelper", "Error adding user: ${e.message}")
        false
    }
}

fun checkUser(npm: String, pass: String): Boolean {
    return try {
        val db = this.readableDatabase
        val cursor = db.rawQuery(
            "SELECT $COLUMN_PASSWORD FROM $TABLE_USERS WHERE $COLUMN_NPM=?", 
            arrayOf(npm)
        )
        var matches = false
        if (cursor.moveToFirst()) {
            val storedHash = cursor.getString(0)
            matches = hashPassword(pass) == storedHash  // ← Verify hash
        }
        cursor.close()
        matches
    } catch (e: Exception) {
        android.util.Log.e("DatabaseHelper", "Error checking user: ${e.message}")
        false
    }
}

📊 DATABASE INSPECTION

Via ADB Terminal

# Connect device/emulator
adb shell

# Navigate to database
cd /data/data/id.ac.ubharajaya.sistemakademik/databases/

# Open SQLite
sqlite3 Akademik.db

# View all users
SELECT * FROM users;

# View specific user
SELECT * FROM users WHERE npm='20231071513';

# View attendance records
SELECT * FROM attendance;

# Count records
SELECT COUNT(*) FROM users;
SELECT COUNT(*) FROM attendance;

# Exit
.exit

Via Android Studio (Built-in)

1. View → Tool Windows → App Inspection
2. Select Device
3. Navigate to Database section
4. Browse tables visually

🔍 LOGGING EXAMPLES

Current Logging (Already Added)

android.util.Log.e("DatabaseHelper", "Error adding user: ${e.message}")
android.util.Log.e("DatabaseHelper", "Error checking user exists: ${e.message}")
android.util.Log.i("RegisterScreen", "Registration started")

Advanced Logging (Optional Enhancement)

// Add different log levels
android.util.Log.v("TAG", "Verbose message")     // Least important
android.util.Log.d("TAG", "Debug message")       // Debug info
android.util.Log.i("TAG", "Info message")        // General info
android.util.Log.w("TAG", "Warning message")     // Warnings
android.util.Log.e("TAG", "Error message")       // Errors (Most important)

// Example in context
fun addUser(username: String, npm: String, pass: String): Boolean {
    android.util.Log.i("DatabaseHelper", "Starting user registration: $npm")
    return try {
        val db = this.writableDatabase
        // ... code ...
        android.util.Log.d("DatabaseHelper", "User added successfully: $npm")
        result != -1L
    } catch (e: SQLiteIntegrityConstraintException) {
        android.util.Log.w("DatabaseHelper", "Duplicate NPM: $npm")
        false
    } catch (e: Exception) {
        android.util.Log.e("DatabaseHelper", "Error adding user: ${e.message}", e)
        false
    }
}

🧪 TEST DATA

Valid Test Data

// Success case
val testData1 = mapOf(
    "nama" to "Febby Dwiss",
    "npm" to "20231071513",
    "password" to "password123"
)

// Another valid case
val testData2 = mapOf(
    "nama" to "Test User",
    "npm" to "20231071234",
    "password" to "test1234"
)

// Edge case - minimum valid
val testData3 = mapOf(
    "nama" to "A",
    "npm" to "10000000",
    "password" to "123456"
)

Invalid Test Data

// NPM too short
val invalidData1 = mapOf(
    "nama" to "Test",
    "npm" to "2023107",  // 7 digits
    "password" to "pass123"
)

// NPM with letters
val invalidData2 = mapOf(
    "nama" to "Test",
    "npm" to "2023ABC78",  // Contains letters
    "password" to "pass123"
)

// Password too short
val invalidData3 = mapOf(
    "nama" to "Test",
    "npm" to "20231071513",
    "password" to "pass1"  // 5 chars
)

// Empty field
val invalidData4 = mapOf(
    "nama" to "",  // Empty
    "npm" to "20231071513",
    "password" to "pass123"
)

🎨 UI IMPROVEMENTS (Optional)

Add Loading State

var isLoading by remember { mutableStateOf(false) }

Button(
    onClick = {
        if (/* all validations pass */) {
            isLoading = true
            try {
                if (db.addUser(username, npm, password)) {
                    // Success
                    isLoading = false
                }
            } catch (e: Exception) {
                isLoading = false
            }
        }
    },
    enabled = !isLoading,  // Disable button during loading
    modifier = Modifier.fillMaxWidth(),
) {
    if (isLoading) {
        CircularProgressIndicator(modifier = Modifier.size(20.dp))
    } else {
        Text("Daftar")
    }
}

Add Input Error States

var usernameError by remember { mutableStateOf("") }
var npmError by remember { mutableStateOf("") }
var passwordError by remember { mutableStateOf("") }

OutlinedTextField(
    value = username,
    onValueChange = { 
        username = it
        usernameError = if (it.isBlank()) "Nama tidak boleh kosong" else ""
    },
    label = { Text("Nama Lengkap") },
    isError = usernameError.isNotEmpty(),  // Show error state
    supportingText = { 
        if (usernameError.isNotEmpty()) {
            Text(usernameError, color = Color.Red)
        }
    },
    // ... other properties
)

🔄 API INTEGRATION (Future)

Send to Server

fun sendRegistrationToServer(
    username: String,
    npm: String,
    password: String,
    onSuccess: () -> Unit,
    onError: (String) -> Unit
) {
    thread {
        try {
            val url = URL("https://your-api.com/register")
            val conn = url.openConnection() as HttpURLConnection
            
            conn.requestMethod = "POST"
            conn.setRequestProperty("Content-Type", "application/json")
            conn.doOutput = true
            
            val json = JSONObject().apply {
                put("nama", username)
                put("npm", npm)
                put("password", password)
            }
            
            conn.outputStream.use {
                it.write(json.toString().toByteArray())
            }
            
            val responseCode = conn.responseCode
            
            if (responseCode == 200 || responseCode == 201) {
                onSuccess()
            } else {
                onError("Server error: $responseCode")
            }
            
            conn.disconnect()
        } catch (e: Exception) {
            onError("Network error: ${e.message}")
        }
    }
}

📱 PERMISSION HANDLING

For Absensi Feature (GPS + Camera)

// In AndroidManifest.xml
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />

// In Compose
val locationPermissionLauncher = rememberLauncherForActivityResult(
    ActivityResultContracts.RequestPermission()
) { granted ->
    if (granted) {
        // Request location
    } else {
        Toast.makeText(context, "Location permission denied", Toast.LENGTH_SHORT).show()
    }
}

Button(onClick = {
    locationPermissionLauncher.launch(Manifest.permission.ACCESS_FINE_LOCATION)
}) {
    Text("Enable Location")
}

🎯 VALIDATION HELPERS

Reusable Validation Functions

// NPM validation
fun isValidNPM(npm: String): Boolean {
    return npm.length >= 8 && npm.all { it.isDigit() }
}

// Password validation
fun isValidPassword(password: String): Boolean {
    return password.length >= 6
}

// Email validation (if needed)
fun isValidEmail(email: String): Boolean {
    return email.matches(Regex("[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}"))
}

// Usage
if (!isValidNPM(npm)) {
    Toast.makeText(context, "NPM tidak valid", Toast.LENGTH_SHORT).show()
    return
}

🔐 SHARED PREFERENCES

Save User Session (Optional)

// Save session
val sharedPref = context.getSharedPreferences("user_session", Context.MODE_PRIVATE)
sharedPref.edit().apply {
    putString("npm", npm)
    putString("nama", nama)
    putBoolean("is_logged_in", true)
    apply()
}

// Retrieve session
val npm = sharedPref.getString("npm", null)
val nama = sharedPref.getString("nama", null)
val isLoggedIn = sharedPref.getBoolean("is_logged_in", false)

// Clear session on logout
sharedPref.edit().clear().apply()

📊 UNIT TESTS

Example Test Cases (For advanced users)

// Using JUnit
import org.junit.Test
import org.junit.Before
import androidx.test.ext.junit.runners.AndroidJUnit4
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
class DatabaseHelperTest {
    
    private lateinit var db: DatabaseHelper
    
    @Before
    fun setUp() {
        val context = InstrumentationRegistry.getInstrumentation().context
        db = DatabaseHelper(context)
    }
    
    @Test
    fun testAddUser_ValidData_ReturnTrue() {
        val result = db.addUser("Test User", "20231071513", "password123")
        assertTrue(result)
    }
    
    @Test
    fun testAddUser_DuplicateNPM_ReturnFalse() {
        db.addUser("User 1", "20231071513", "pass123")
        val result = db.addUser("User 2", "20231071513", "pass456")
        assertFalse(result)
    }
    
    @Test
    fun testCheckUser_ValidCredentials_ReturnTrue() {
        db.addUser("Test User", "20231071513", "password123")
        val result = db.checkUser("20231071513", "password123")
        assertTrue(result)
    }
    
    @Test
    fun testCheckUser_InvalidPassword_ReturnFalse() {
        db.addUser("Test User", "20231071513", "password123")
        val result = db.checkUser("20231071513", "wrongpassword")
        assertFalse(result)
    }
}

🚀 PERFORMANCE OPTIMIZATION

Database Query Optimization

// INSTEAD OF (slow):
fun getAllUsers(): List<User> {
    val users = mutableListOf<User>()
    val cursor = db.rawQuery("SELECT * FROM users", null)
    if (cursor.moveToFirst()) {
        do {
            users.add(User(...))
        } while (cursor.moveToNext())
    }
    cursor.close()
    return users
}

// USE (faster):
fun getAllUsers(): List<User> {
    val users = mutableListOf<User>()
    val cursor = db.query(
        "users",
        null,
        null,
        null,
        null,
        null,
        null
    )
    cursor?.use {
        if (it.moveToFirst()) {
            do {
                users.add(User(...))
            } while (it.moveToNext())
        }
    }
    return users
}

🎨 COLOR & THEME CUSTOMIZATION

In themes/colors

// colors.kt
val PrimaryPink = Color(0xFFE91E63)
val SecondaryPink = Color(0xFFF48FB1)
val ErrorRed = Color(0xFFE53935)
val SuccessGreen = Color(0xFF43A047)

// Usage in UI
OutlinedTextField(
    colors = OutlinedTextFieldDefaults.colors(
        focusedBorderColor = PrimaryPink,
        unfocusedBorderColor = SecondaryPink,
        focusedLabelColor = PrimaryPink,
        unfocusedLabelColor = SecondaryPink
    )
)

💡 COMMON PATTERNS

Safe Database Access

inline fun <T> withDatabase(block: (DatabaseHelper) -> T): T? {
    return try {
        block(db)
    } catch (e: Exception) {
        android.util.Log.e("Database", "Error: ${e.message}")
        null
    }
}

// Usage
withDatabase { db ->
    db.addUser("Name", "npm", "pass")
} ?: run {
    Toast.makeText(context, "Database error", Toast.LENGTH_SHORT).show()
}

Result Wrapper

sealed class Result<out T> {
    data class Success<T>(val data: T) : Result<T>()
    data class Error(val exception: Exception) : Result<Nothing>()
    object Loading : Result<Nothing>()
}

fun addUserWithResult(username: String, npm: String, password: String): Result<Boolean> {
    return try {
        val result = db.addUser(username, npm, password)
        Result.Success(result)
    } catch (e: Exception) {
        Result.Error(e)
    }
}

// Usage
when (val result = addUserWithResult(name, npm, pass)) {
    is Result.Success -> {
        Toast.makeText(context, "Success", Toast.LENGTH_SHORT).show()
    }
    is Result.Error -> {
        Toast.makeText(context, result.exception.message, Toast.LENGTH_SHORT).show()
    }
    is Result.Loading -> {
        // Show loading
    }
}

📝 NOTES

  • Gunakan snippet ini dengan bijak - Pastikan memahami apa yang dilakukan
  • Test setiap perubahan - Jangan langsung ke production
  • Keep backups - Simpan versi working
  • Follow patterns - Konsisten dengan codebase yang ada
  • Add comments - Document why, not what

Happy Coding! 💻

Untuk lebih lanjut, lihat: