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

577 lines
13 KiB
Markdown

# 💾 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
<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
```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<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
```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 <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
```kotlin
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:
- [Kotlin Documentation](https://kotlinlang.org/docs/)
- [Android Developers Guide](https://developer.android.com/)
- [Jetpack Compose Tutorial](https://developer.android.com/jetpack/compose)