Compare commits

..

No commits in common. "8ea8dc66a612de72e4195494966a2b9878c2db38" and "14c4c19a4f0b56fabb25d2cd44d5e395224ce7c6" have entirely different histories.

31 changed files with 1993 additions and 2339 deletions

View File

@ -1,65 +0,0 @@
kotlin version: 2.0.21
error message: Daemon compilation failed: Connection to the Kotlin daemon has been unexpectedly lost. This might be caused by the daemon being killed by another process or the operating system, or by JVM crash.
org.jetbrains.kotlin.gradle.tasks.DaemonCrashedException: Connection to the Kotlin daemon has been unexpectedly lost. This might be caused by the daemon being killed by another process or the operating system, or by JVM crash.
at org.jetbrains.kotlin.gradle.tasks.TasksUtilsKt.wrapAndRethrowCompilationException(tasksUtils.kt:55)
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemon(GradleKotlinCompilerWork.kt:243)
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemonOrFallbackImpl(GradleKotlinCompilerWork.kt:159)
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.run(GradleKotlinCompilerWork.kt:111)
at org.jetbrains.kotlin.compilerRunner.GradleCompilerRunnerWithWorkers$GradleKotlinCompilerWorkAction.execute(GradleCompilerRunnerWithWorkers.kt:76)
at org.gradle.workers.internal.DefaultWorkerServer.execute(DefaultWorkerServer.java:63)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:66)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1$1.create(NoIsolationWorkerFactory.java:62)
at org.gradle.internal.classloader.ClassLoaderUtils.executeInClassloader(ClassLoaderUtils.java:100)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.lambda$execute$0(NoIsolationWorkerFactory.java:62)
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:44)
at org.gradle.workers.internal.AbstractWorker$1.call(AbstractWorker.java:41)
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:210)
at org.gradle.internal.operations.DefaultBuildOperationRunner$CallableBuildOperationWorker.execute(DefaultBuildOperationRunner.java:205)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:67)
at org.gradle.internal.operations.DefaultBuildOperationRunner$2.execute(DefaultBuildOperationRunner.java:60)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:167)
at org.gradle.internal.operations.DefaultBuildOperationRunner.execute(DefaultBuildOperationRunner.java:60)
at org.gradle.internal.operations.DefaultBuildOperationRunner.call(DefaultBuildOperationRunner.java:54)
at org.gradle.workers.internal.AbstractWorker.executeWrappedInBuildOperation(AbstractWorker.java:41)
at org.gradle.workers.internal.NoIsolationWorkerFactory$1.execute(NoIsolationWorkerFactory.java:59)
at org.gradle.workers.internal.DefaultWorkerExecutor.lambda$submitWork$0(DefaultWorkerExecutor.java:174)
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runExecution(DefaultConditionalExecutionQueue.java:194)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.access$700(DefaultConditionalExecutionQueue.java:127)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner$1.run(DefaultConditionalExecutionQueue.java:169)
at org.gradle.internal.Factories$1.create(Factories.java:31)
at org.gradle.internal.work.DefaultWorkerLeaseService.withLocks(DefaultWorkerLeaseService.java:263)
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:127)
at org.gradle.internal.work.DefaultWorkerLeaseService.runAsWorkerThread(DefaultWorkerLeaseService.java:132)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.runBatch(DefaultConditionalExecutionQueue.java:164)
at org.gradle.internal.work.DefaultConditionalExecutionQueue$ExecutionRunner.run(DefaultConditionalExecutionQueue.java:133)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)
at java.base/java.util.concurrent.FutureTask.run(Unknown Source)
at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:64)
at org.gradle.internal.concurrent.AbstractManagedExecutor$1.run(AbstractManagedExecutor.java:48)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)
at java.base/java.lang.Thread.run(Unknown Source)
Caused by: java.rmi.UnmarshalException: Error unmarshaling return header; nested exception is:
java.net.SocketException: Connection reset
at java.rmi/sun.rmi.transport.StreamRemoteCall.executeCall(Unknown Source)
at java.rmi/sun.rmi.server.UnicastRef.invoke(Unknown Source)
at java.rmi/java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(Unknown Source)
at java.rmi/java.rmi.server.RemoteObjectInvocationHandler.invoke(Unknown Source)
at jdk.proxy22/jdk.proxy22.$Proxy449.compile(Unknown Source)
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.incrementalCompilationWithDaemon(GradleKotlinCompilerWork.kt:331)
at org.jetbrains.kotlin.compilerRunner.GradleKotlinCompilerWork.compileWithDaemon(GradleKotlinCompilerWork.kt:235)
... 37 more
Caused by: java.net.SocketException: Connection reset
at java.base/sun.nio.ch.NioSocketImpl.implRead(Unknown Source)
at java.base/sun.nio.ch.NioSocketImpl.read(Unknown Source)
at java.base/sun.nio.ch.NioSocketImpl$1.read(Unknown Source)
at java.base/java.net.Socket$SocketInputStream.read(Unknown Source)
at java.base/java.io.BufferedInputStream.fill(Unknown Source)
at java.base/java.io.BufferedInputStream.implRead(Unknown Source)
at java.base/java.io.BufferedInputStream.read(Unknown Source)
at java.base/java.io.DataInputStream.readUnsignedByte(Unknown Source)
at java.base/java.io.DataInputStream.readByte(Unknown Source)
... 44 more

View File

@ -1,4 +0,0 @@
kotlin version: 2.0.21
error message: The daemon has terminated unexpectedly on startup attempt #1 with error code: 0. The daemon process output:
1. Kotlin compile daemon is ready

View File

@ -1,71 +0,0 @@
# Changelog
Semua perubahan penting pada proyek Aplikasi Absensi Akademik didokumentasikan dalam file ini.
## [Unreleased] - Versi Terbaru (Redesign & Backend Integration)
Perubahan besar-besaran dilakukan pada arsitektur aplikasi, beralih dari aplikasi *single-screen* sederhana menjadi aplikasi manajemen akademik yang komprehensif dengan autentikasi dan validasi keamanan.
### ✨ Fitur Baru (Added)
- **Sistem Autentikasi**:
- Menambahkan layar **Login** dan **Register**.
- Integrasi **JWT Authentication** (penyimpanan token via `SharedPreferences`).
- Manajemen sesi pengguna (Auto-login jika token tersimpan).
- **Manajemen Jadwal & Mata Kuliah**:
- Menambahkan fitur pengambilan data **Jadwal Kuliah** hari ini dari database.
- Dropdown pemilihan mata kuliah saat melakukan absensi.
- **Validasi Keamanan Tingkat Lanjut**:
- **Geofencing**: Validasi lokasi dalam radius 500m dari koordinat kampus.
- **Anti-Fake GPS**: Logika deteksi aplikasi *Mock Location* untuk mencegah kecurangan.
- **Face Detection**: Integrasi **CameraX + ML Kit** (tersirat dalam logika kamera baru) untuk mewajibkan deteksi wajah saat status "HADIR".
- **Fitur Kamera Kustom**:
- Mengganti intent kamera bawaan (`MediaStore`) dengan **CameraX** tertanam dalam UI.
- Fitur overlay kamera untuk panduan posisi wajah/dokumen.
- Dukungan kamera depan (selfie) dan belakang (dokumen).
- **Riwayat & Profil**:
- Menambahkan tab **Riwayat Absensi** untuk melihat log kehadiran dan bukti foto.
- Menambahkan tab **Profil** yang menampilkan data mahasiswa dan fitur Logout.
- **Multi-Status Absensi**:
- Dukungan untuk status **HADIR**, **SAKIT**, dan **IZIN**.
- Logika validasi berbeda untuk setiap status (Sakit/Izin tidak butuh radius lokasi).
### 🎨 Antarmuka & UX (Changed)
- **UI Overhaul (Material3)**:
- Redesign total menggunakan **Jetpack Compose Material3**.
- Penerapan tema identitas kampus (**Gold & Maroon**).
- Penggunaan komponen UI modern: `Card`, `NavigationBar` (Bottom Nav), `Gradient Button`.
- **Navigasi**:
- Implementasi **Bottom Navigation Bar** dengan 4 menu utama (Absensi, Kelas, Riwayat, Profil).
- Transisi antar layar (Login <-> Main <-> Register).
- **Feedback User**:
- Loading indicator saat proses API berjalan.
- Dialog error dan sukses yang lebih informatif dibandingkan `Toast` sederhana.
### ⚙️ Teknis & Backend (Changed)
- **Migrasi API**:
- **Sebelumnya**: Mengirim data hardcoded langsung ke Webhook N8N via `HttpURLConnection`.
- **Sekarang**: Berkomunikasi dengan **Backend Python Flask** (`/api/auth`, `/api/absensi`, `/api/jadwal`). Backend yang kemudian meneruskan data ke N8N.
- **Struktur Kode**:
- Refactoring dari *Single Activity Monolith* menjadi struktur modular.
- Pemisahan logic ke dalam:
- `AppConstants` (Konfigurasi URL & Koordinat).
- `UserPreferences` (Manajemen sesi lokal).
- `Data Classes` (Mahasiswa, Jadwal, Riwayat).
- Helper functions (Bitmap Converter, Distance Calculation).
### 🔥 Dihapus (Removed)
- Menghapus pengiriman data hardcoded (NPM "12345") pada fungsi `kirimKeN8n`.
- Menghapus penggunaan `MediaStore.ACTION_IMAGE_CAPTURE` (Intent kamera eksternal).
- Menghapus tampilan *single-screen* sederhana tanpa navigasi.
---
## [Legacy] - Versi Awal (Sebelum Redesign)
Versi purwarupa (prototype) untuk pengujian fungsionalitas dasar.
### Fitur
- Pengambilan titik koordinat GPS sederhana (`FusedLocationProvider`).
- Pengambilan foto menggunakan aplikasi kamera bawaan HP (`Intent`).
- Pengiriman data dummy langsung ke Webhook N8N.
- Tampilan UI dasar menggunakan `Column` dan `Button` standar.

132
README.md
View File

@ -1,101 +1,95 @@
# 📱 Aplikasi Absensi Akademik UBHARA Jaya (Mobile & Backend) # 📱 Aplikasi Absensi Akademik Berbasis Koordinat dan Foto (Mobile)
[📋 Changelog](./CHANGELOG.md)
## 📌 Deskripsi Proyek ## 📌 Deskripsi Proyek
Proyek ini merupakan **Tugas Project Akhir Mata Kuliah Pemrograman Mobile** yang mengimplementasikan sistem absensi akademik berbasis **Client-Server**. Aplikasi mobile dibangun menggunakan **Kotlin (Jetpack Compose)**, sedangkan sisi server menggunakan **Python Flask** dengan database **MySQL**. Proyek ini merupakan **Tugas Project Akhir Mata Kuliah Pemrograman Mobile** yang bertujuan untuk membangun **aplikasi akademik berbasis mobile** dengan fokus pada **fitur absensi menggunakan data koordinat (GPS) dan pengambilan foto mahasiswa**.
Sistem ini dirancang dengan validasi ketat menggunakan **Geofencing (GPS)** dan **Deteksi Wajah (Face Detection)** untuk status kehadiran "Hadir", serta mendukung pengiriman bukti dokumen untuk status "Sakit/Izin". Seluruh data terintegrasi secara real-time ke **N8N Webhook** untuk pelaporan otomatis. Aplikasi ini dirancang untuk meningkatkan **validitas kehadiran mahasiswa**, dengan memastikan bahwa absensi hanya dapat dilakukan apabila mahasiswa:
1. Berada pada **lokasi yang telah ditentukan**, dan
2. Melakukan **pengambilan foto (selfie) secara langsung saat absensi**
--- ---
## 🎯 Tujuan Proyek ## 🎯 Tujuan Proyek
- Mengimplementasikan arsitektur **REST API** antara Android dan Python Flask. - Mengimplementasikan **Location-Based Service (LBS)** pada aplikasi mobile
- Menerapkan **Face Detection** menggunakan CameraX untuk memvalidasi kehadiran fisik mahasiswa. - Mengintegrasikan **kamera perangkat** untuk dokumentasi absensi
- Mencegah kecurangan absensi dengan fitur deteksi **Fake GPS**. - Mencegah kecurangan absensi (titip absen)
- Mengelola status kehadiran otomatis (**Auto-Alfa**) pada server jika mahasiswa lupa absen. - Mengembangkan aplikasi mobile akademik berbasis Android
- Melatih kemampuan Fullstack Mobile Development (Backend, Database, & Mobile UI). - Melatih kemampuan perancangan dan implementasi aplikasi mobile
--- ---
## 🚀 Fitur Utama ## 🚀 Fitur Utama
- 🔐 **Login Pengguna (Mahasiswa)**
### 📱 Sisi Mobile (Android - Jetpack Compose) - 📍 **Pengambilan Koordinat Lokasi (Latitude & Longitude)**
- **Modern UI**: Antarmuka modern menggunakan **Material3** & **Jetpack Compose**. - 🏫 **Validasi Lokasi Absensi (Radius Area)**
- **Smart Attendance**: - 📸 **Pengambilan Foto Mahasiswa Saat Absensi**
- **Hadir**: Wajib berada di radius kampus & Wajib deteksi wajah (Real-time). - 🕒 **Pencatatan Waktu Absensi**
- **Sakit/Izin**: Wajib upload foto surat dokter/bukti (Tanpa validasi radius). - 📄 **Riwayat Kehadiran Mahasiswa**
- **Anti-Fraud**: Sistem mendeteksi dan menolak jika pengguna mengaktifkan **Fake GPS/Mock Location**. - ⚠️ **Notifikasi Absensi Ditolak jika Tidak Valid**
- **Riwayat & Bukti**: Mahasiswa dapat melihat riwayat kehadiran beserta foto bukti yang tersimpan di server.
- **Profil Mahasiswa**: Menampilkan data akademik dan fitur logout.
### 💻 Sisi Backend (Python Flask)
- **JWT Authentication**: Keamanan berbasis token untuk setiap request.
- **Auto-Alfa Trigger**: Sistem otomatis menandai "TIDAK HADIR" jika jam kuliah berakhir dan mahasiswa belum absen.
- **N8N Integration**: Data absensi yang valid dikirim otomatis ke webhook N8N untuk notifikasi/reporting.
- **Image Handling**: Kompresi foto (Base64) otomatis untuk efisiensi penyimpanan database.
--- ---
## 🗺️ Mekanisme Absensi ## 🗺️ Mekanisme Absensi Berbasis Lokasi dan Foto
1. Mahasiswa melakukan **login**
2. Memilih menu **Absensi**
3. Sistem meminta:
- Izin **akses lokasi**
- Izin **akses kamera**
4. Aplikasi mengambil:
- 📍 **Koordinat lokasi mahasiswa**
- 📸 **Foto mahasiswa secara real-time**
5. Sistem melakukan validasi:
- Lokasi berada dalam **radius absensi**
- Foto berhasil diambil
6. Jika valid → **Absensi berhasil**
7. Jika tidak valid → **Absensi ditolak**
### 1. Absensi Status "HADIR" ---
1. Mahasiswa memilih Jadwal Kelas yang aktif.
2. Sistem mengecek **GPS**:
- Apakah di dalam radius **500m** dari kampus?
- Apakah terdeteksi aplikasi **Fake GPS**?
3. Sistem membuka **Kamera Deteksi Wajah**:
- Tombol shutter hanya aktif jika wajah terdeteksi.
4. Data (Lokasi + Foto Wajah) dikirim ke server.
### 2. Absensi Status "SAKIT / IZIN" ## 📸 Pengambilan Foto Saat Absensi
1. Mahasiswa memilih status Sakit atau Izin. - Foto diambil menggunakan **kamera depan (selfie)**
2. Validasi lokasi dilewati (bisa absen dari rumah/RS). - Foto hanya dapat diambil **saat proses absensi**
3. Sistem membuka **Kamera Dokumen** (Tanpa deteksi wajah). - Foto disimpan sebagai **bukti kehadiran**
4. Mahasiswa memfoto surat keterangan. - Foto dapat digunakan untuk:
5. Data dikirim ke server. - Verifikasi manual oleh dosen
- Dokumentasi akademik
### 3. Logika Auto-Alfa (Server)
- Setiap kali jadwal dimuat, server mengecek jadwal yang `jam_selesai < jam_sekarang`.
- Jika mahasiswa belum ada record absensi pada jam tersebut, server otomatis menginput status **"TIDAK HADIR"**.
--- ---
## 🛠️ Teknologi yang Digunakan ## 🛠️ Teknologi yang Digunakan
- **Mobile Platform**: Android (Kotlin) - **Platform** : Android
- **UI Framework**: Jetpack Compose - **Bahasa Pemrograman** : Kotlin / Java
- **Camera Engine**: CameraX + ML Kit (Face Analysis) - **Location Service** :
- **Backend Framework**: Python Flask - Google Maps API
- **Database**: MySQL - Fused Location Provider
- **Integrasi**: N8N Webhook - **Camera API** : CameraX / Camera2
- **Protocol**: HTTP/REST (JSON) - **Database** : Firebase / SQLite / MySQL
- **Storage** : Firebase Storage / Local Storage
- **IDE** : Android Studio
--- ---
## 🔐 Izin Aplikasi (Permissions) ## 🔐 Izin Aplikasi (Permissions)
Aplikasi memerlukan izin berikut: Aplikasi memerlukan izin berikut:
- `INTERNET`: Koneksi ke Server API. - `ACCESS_FINE_LOCATION`
- `ACCESS_FINE_LOCATION`: Validasi koordinat presisi tinggi. - `ACCESS_COARSE_LOCATION`
- `ACCESS_COARSE_LOCATION`: Validasi koordinat jaringan. - `CAMERA`
- `CAMERA`: Pengambilan foto bukti kehadiran & deteksi wajah. - `INTERNET`
- `WRITE_EXTERNAL_STORAGE` (jika diperlukan)
--- ---
## 📂 Mockup ## 📂 Mockup
![mockup](Mockup.png) ![mockup](Mockup.png)
## Catatan Konfigurasi: ## Catatan:
- **Server**: Pastikan `app.py` berjalan (`python app.py`) pada jaringan yang sama dengan HP. - Kembangkan project dari starter yang sudah disediakan, tidak membuat dari awal.
- **Endpoint**: Sesuaikan `BASE_URL` pada file `MainActivity.kt` (Object `AppConstants`) dengan IP Laptop Anda. - Untuk koordinat bisa ditambah/kurangi angka tertentu agar tidak memunculkan koordinat rumah masing-masing, data awal tetap dari GPS.
```kotlin
const val BASE_URL = "[http://192.168.](http://192.168.)x.x:5000" ## Pengecekan:
``` - https://ntfy.ubharajaya.ac.id/EAS
- **Fake GPS**: Pastikan mematikan aplikasi Mock Location saat pengujian fitur Hadir. - https://docs.google.com/spreadsheets/d/1jH15MfnNgpPGuGeid0hYfY7fFUHCEFbCmg8afTyyLZs/edit?gid=0#gid=0
## Pengecekan & Monitoring: ## Webhook:
- **Monitoring**: https://ntfy.ubharajaya.ac.id/EAS - test: https://n8n.lab.ubharajaya.ac.id/webhook-test/23c6993d-1792-48fb-ad1c-ffc78a3e6254
- **Data Spreadsheet**: https://docs.google.com/spreadsheets/d/1jH15MfnNgpPGuGeid0hYfY7fFUHCEFbCmg8afTyyLZs/edit?gid=0#gid=0 - production: https://n8n.lab.ubharajaya.ac.id/webhook/23c6993d-1792-48fb-ad1c-ffc78a3e6254
## Webhook N8N:
- **Test**: https://n8n.lab.ubharajaya.ac.id/webhook-test/23c6993d-1792-48fb-ad1c-ffc78a3e6254
- **Production**: https://n8n.lab.ubharajaya.ac.id/webhook/23c6993d-1792-48fb-ad1c-ffc78a3e6254

View File

@ -55,10 +55,6 @@ dependencies {
implementation("com.google.android.gms:play-services-location:21.0.1") implementation("com.google.android.gms:play-services-location:21.0.1")
implementation("androidx.compose.material:material-icons-extended:1.6.0") implementation("androidx.compose.material:material-icons-extended:1.6.0")
implementation(libs.androidx.compose.animation.core.lint) implementation(libs.androidx.compose.animation.core.lint)
implementation(libs.androidx.compose.foundation)
implementation(libs.androidx.ui)
implementation(libs.androidx.ui.graphics)
implementation(libs.androidx.compose.ui.text)
testImplementation(libs.junit) testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.junit)
@ -68,13 +64,4 @@ dependencies {
debugImplementation(libs.androidx.compose.ui.tooling) debugImplementation(libs.androidx.compose.ui.tooling)
debugImplementation(libs.androidx.compose.ui.test.manifest) debugImplementation(libs.androidx.compose.ui.test.manifest)
// CameraX (Pastikan Anda sudah punya ini)
implementation("androidx.camera:camera-core:1.3.0")
implementation("androidx.camera:camera-camera2:1.3.0")
implementation("androidx.camera:camera-lifecycle:1.3.0")
implementation("androidx.camera:camera-view:1.3.0")
// Google ML Kit Face Detection
implementation("com.google.android.gms:play-services-mlkit-face-detection:17.1.0")
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 172 KiB

View File

@ -1,36 +0,0 @@
package id.ac.ubharajaya.sistemakademik
import androidx.camera.core.ExperimentalGetImage
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.ImageProxy
import com.google.mlkit.vision.common.InputImage
import com.google.mlkit.vision.face.FaceDetection
import com.google.mlkit.vision.face.FaceDetectorOptions
class WajahAnalyzer(private val onFaceDetected: (Boolean) -> Unit) : ImageAnalysis.Analyzer {
// Settingan deteksi cepat (Performance Mode)
private val options = FaceDetectorOptions.Builder()
.setPerformanceMode(FaceDetectorOptions.PERFORMANCE_MODE_FAST)
.setContourMode(FaceDetectorOptions.CONTOUR_MODE_NONE)
.build()
private val detector = FaceDetection.getClient(options)
@ExperimentalGetImage
override fun analyze(imageProxy: ImageProxy) {
val mediaImage = imageProxy.image
if (mediaImage != null) {
val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)
detector.process(image)
.addOnSuccessListener { faces ->
// Callback: True jika ada wajah, False jika kosong
onFaceDetected(faces.isNotEmpty())
}
.addOnFailureListener { onFaceDetected(false) }
.addOnCompleteListener { imageProxy.close() } // Wajib tutup image
} else {
imageProxy.close()
}
}
}

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

View File

@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
</adaptive-icon>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.1 KiB

After

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.7 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="ic_launcher_background">#FFFFFF</color>
</resources>

File diff suppressed because it is too large Load Diff

View File

@ -5,5 +5,4 @@ mysql-connector-python==8.2.0
PyJWT==2.8.0 PyJWT==2.8.0
bcrypt==4.1.2 bcrypt==4.1.2
python-dotenv==1.0.0 python-dotenv==1.0.0
requests==2.31.0 requests==2.31.0
Flask-APScheduler==1.13.1

View File

@ -9,10 +9,6 @@ lifecycleRuntimeKtx = "2.9.4"
activityCompose = "1.11.0" activityCompose = "1.11.0"
composeBom = "2024.09.00" composeBom = "2024.09.00"
animationCoreLint = "1.10.0" animationCoreLint = "1.10.0"
foundation = "1.10.0"
ui = "1.10.0"
uiGraphics = "1.10.0"
uiText = "1.10.0"
[libraries] [libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
@ -30,10 +26,6 @@ androidx-compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-
androidx-compose-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } androidx-compose-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" } androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }
androidx-compose-animation-core-lint = { group = "androidx.compose.animation", name = "animation-core-lint", version.ref = "animationCoreLint" } androidx-compose-animation-core-lint = { group = "androidx.compose.animation", name = "animation-core-lint", version.ref = "animationCoreLint" }
androidx-compose-foundation = { group = "androidx.compose.foundation", name = "foundation", version.ref = "foundation" }
androidx-ui = { group = "androidx.compose.ui", name = "ui", version.ref = "ui" }
androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics", version.ref = "uiGraphics" }
androidx-compose-ui-text = { group = "androidx.compose.ui", name = "ui-text", version.ref = "uiText" }
[plugins] [plugins]
android-application = { id = "com.android.application", version.ref = "agp" } android-application = { id = "com.android.application", version.ref = "agp" }