# ๐Ÿ’พ 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 ```kotlin 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 ```kotlin 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 ```bash # 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) ```kotlin 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) ```kotlin // 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 ```kotlin // 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 ```kotlin // 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 ```kotlin 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 ```kotlin 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 ```kotlin 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) ```kotlin // In AndroidManifest.xml // 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 ```kotlin // 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) ```kotlin // 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) ```kotlin // 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 ```kotlin // INSTEAD OF (slow): fun getAllUsers(): List { val users = mutableListOf() 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 { val users = mutableListOf() 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 ```kotlin // 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 ```kotlin inline fun 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 ```kotlin sealed class Result { data class Success(val data: T) : Result() data class Error(val exception: Exception) : Result() object Loading : Result() } fun addUserWithResult(username: String, npm: String, password: String): Result { 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: - [Kotlin Documentation](https://kotlinlang.org/docs/) - [Android Developers Guide](https://developer.android.com/) - [Jetpack Compose Tutorial](https://developer.android.com/jetpack/compose)