diff --git a/android/app/src/main/kotlin/com/jamshalat/app/MainActivity.kt b/android/app/src/main/kotlin/com/jamshalat/app/MainActivity.kt index ac1074e..7d2bda1 100644 --- a/android/app/src/main/kotlin/com/jamshalat/app/MainActivity.kt +++ b/android/app/src/main/kotlin/com/jamshalat/app/MainActivity.kt @@ -1,21 +1,29 @@ package com.jamshalat.app +import android.content.Intent +import android.content.pm.PackageManager import android.hardware.GeomagneticField +import androidx.core.content.FileProvider import com.ryanheise.audioservice.AudioServiceActivity import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.MethodCall import io.flutter.plugin.common.MethodChannel +import java.io.File class MainActivity : AudioServiceActivity() { companion object { private const val GEOMAGNETIC_CHANNEL = "com.jamshalat.app/geomagnetic" + private const val SHARE_CHANNEL = "com.jamshalat.app/share" private const val DECLINATION_METHOD = "getDeclination" + private const val SHARE_IMAGE_METHOD = "shareImage" } override fun configureFlutterEngine(flutterEngine: FlutterEngine) { super.configureFlutterEngine(flutterEngine) MethodChannel(flutterEngine.dartExecutor.binaryMessenger, GEOMAGNETIC_CHANNEL) .setMethodCallHandler(::handleGeomagneticMethodCall) + MethodChannel(flutterEngine.dartExecutor.binaryMessenger, SHARE_CHANNEL) + .setMethodCallHandler(::handleShareMethodCall) } private fun handleGeomagneticMethodCall(call: MethodCall, result: MethodChannel.Result) { @@ -41,4 +49,53 @@ class MainActivity : AudioServiceActivity() { ) result.success(field.declination.toDouble()) } + + private fun handleShareMethodCall(call: MethodCall, result: MethodChannel.Result) { + if (call.method != SHARE_IMAGE_METHOD) { + result.notImplemented() + return + } + + val path = call.argument("path") + if (path.isNullOrBlank()) { + result.error("INVALID_ARGS", "Path is required.", null) + return + } + + val file = File(path) + if (!file.exists()) { + result.error("FILE_NOT_FOUND", "Shared image file does not exist.", null) + return + } + + try { + val mimeType = call.argument("mimeType") ?: "image/png" + val chooserTitle = call.argument("chooserTitle") + val authority = "${applicationContext.packageName}.flutter.share_provider" + val fileUri = FileProvider.getUriForFile(this, authority, file) + + val shareIntent = Intent(Intent.ACTION_SEND).apply { + type = mimeType + putExtra(Intent.EXTRA_STREAM, fileUri) + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + } + val chooserIntent = Intent.createChooser(shareIntent, chooserTitle) + + packageManager.queryIntentActivities( + chooserIntent, + PackageManager.MATCH_DEFAULT_ONLY, + ).forEach { resolveInfo -> + grantUriPermission( + resolveInfo.activityInfo.packageName, + fileUri, + Intent.FLAG_GRANT_READ_URI_PERMISSION, + ) + } + + startActivity(chooserIntent) + result.success(null) + } catch (error: Throwable) { + result.error("SHARE_FAILED", error.message, null) + } + } } diff --git a/android/app/upload-keystore.p12 b/android/app/upload-keystore.p12 index 5d2289b..eee0d50 100644 Binary files a/android/app/upload-keystore.p12 and b/android/app/upload-keystore.p12 differ diff --git a/android/app/upload-keystore.p12.bak b/android/app/upload-keystore.p12.bak new file mode 100644 index 0000000..35100bf Binary files /dev/null and b/android/app/upload-keystore.p12.bak differ diff --git a/docs/google-play-form-guide-id.md b/docs/google-play-form-guide-id.md new file mode 100644 index 0000000..6b5c1b4 --- /dev/null +++ b/docs/google-play-form-guide-id.md @@ -0,0 +1,222 @@ +# Panduan Isi Form Google Play Console + +Dokumen ini berisi isi yang disarankan untuk form Play Console dalam Bahasa Indonesia. Isi ini disusun dari kondisi project saat ini. + +## Status yang sudah ada di repo + +- [x] Nama aplikasi sudah ditetapkan: `JamShalat` +- [x] Branding aplikasi sudah konsisten di Android, iOS, macOS, dan web +- [x] Package name Android sudah diset ke `com.jamshalat.app` +- [x] Aplikasi sudah punya keystore rilis +- [x] Aplikasi sudah punya ikon custom + +## Identitas aplikasi + +### Nama aplikasi + +`JamShalat` + +### Kategori yang disarankan + +`Gaya Hidup` + +Alasan: +- aplikasi ini dipakai untuk ibadah harian +- fitur utamanya adalah shalat, Qur'an, dzikir, doa, qibla, dan laporan progres + +### Tag atau kata kunci yang disarankan + +- shalat +- jadwal shalat +- qibla +- kiblat +- al-quran +- qur'an +- dzikir +- doa +- hadits +- murattal +- ibadah +- muslim +- ramadan +- tafsir + +## Deskripsi singkat + +Gunakan salah satu versi ini. + +### Opsi 1 + +Teman ibadah harian untuk shalat, Qur'an, dzikir, doa, dan laporan progres. + +### Opsi 2 + +Pantau shalat, baca Qur'an, dan jaga rutinitas ibadah dalam satu aplikasi. + +## Deskripsi lengkap + +Berikut draft deskripsi lengkap yang bisa langsung dipakai. + +JamShalat adalah aplikasi pendamping ibadah harian untuk membantu pengguna menjaga rutinitas shalat, membaca Al-Qur'an, berdzikir, berdoa, dan memantau progres ibadah dengan lebih rapi. + +Fitur utama: + +- jadwal shalat harian dan kalender shalat +- hitung mundur menuju shalat berikutnya +- arah kiblat +- notifikasi adzan, iqamah, dan pengingat ibadah +- baca Al-Qur'an per surah dan per ayat +- lanjutkan bacaan dari ayat terakhir +- favorit ayat dan marka bacaan +- murattal per surah dengan pemutar audio +- tafsir, juz, halaman, tema, dan Asmaul Husna +- dzikir pagi, petang, dan sesudah shalat +- target dzikir harian yang bisa dicicil sepanjang hari +- daftar doa dan hadits +- checklist ibadah harian +- laporan progres ibadah mingguan, bulanan, dan tahunan +- ayat hari ini yang bisa dibagikan sebagai teks atau gambar + +JamShalat dibuat agar: + +- ringan dipakai setiap hari +- mudah dipakai dalam mode simpel maupun mode lengkap +- membantu pengguna fokus ke ibadah tanpa UI yang berlebihan +- menyimpan progres secara lokal di perangkat + +Catatan: + +- aplikasi ini dapat memakai lokasi perangkat untuk arah kiblat dan jadwal shalat +- aplikasi ini dapat memakai notifikasi untuk pengingat ibadah +- sebagian konten juga dapat diambil dari layanan online agar data lebih lengkap + +## Ikon aplikasi + +Gunakan ikon app yang konsisten dengan branding `JamShalat`. + +Status di repo: + +- [x] ikon custom sudah tersedia +- [ ] pastikan versi store-ready tanpa elemen yang terlalu kecil atau terlalu ramai + +## Screenshot Play Store + +Siapkan minimal beberapa screenshot yang menunjukkan: + +- Beranda mode simpel +- Beranda mode lengkap +- Al-Qur'an +- Dzikir +- Arah Kiblat +- Laporan + +Saran: + +- pakai screenshot yang bersih dan terang +- jangan terlalu banyak teks kecil +- tampilkan UI yang paling menarik + +## Feature graphic + +Siapkan banner feature graphic untuk Play Store. + +Saran isi: + +- nama `JamShalat` +- visual ibadah harian +- warna utama teal / hijau kebiruan +- tanpa terlalu banyak teks + +## Privacy policy + +Wajib siapkan URL privacy policy yang bisa dibuka publik. + +Isi minimum yang disarankan: + +- data apa saja yang dipakai aplikasi +- lokasi dipakai untuk apa +- apakah data disimpan lokal atau dikirim ke server +- apakah ada notifikasi +- apakah ada login atau akun +- apakah ada pihak ketiga seperti API konten atau gambar + +## Data safety + +Isi form sesuai perilaku aplikasi yang benar-benar kamu upload. + +Yang sebaiknya dijelaskan: + +- lokasi dipakai untuk qibla dan jadwal shalat +- notifikasi dipakai untuk pengingat ibadah +- progres ibadah disimpan lokal di perangkat +- bila konten diambil dari API eksternal, jelaskan data apa yang dikirim dan untuk apa +- bila tidak ada login, nyatakan bahwa aplikasi tidak memerlukan akun + +Catatan penting: + +- isi Data safety jangan asal +- kalau aplikasi nanti mengirim koordinat lokasi ke server, itu harus dinyatakan +- kalau hanya dipakai di perangkat dan tidak disimpan/kirim, jelaskan sebagai penggunaan lokal + +## Content rating + +Jawab kuesioner dengan asumsi berikut: + +- tidak ada kekerasan +- tidak ada judi +- tidak ada konten seksual +- tidak ada chat antar pengguna +- tidak ada konten buatan pengguna +- tidak ada iklan yang mengganggu isi utama + +Biasanya hasilnya akan tetap rendah karena ini aplikasi ibadah, tetapi ikuti hasil kuesioner resmi di Play Console. + +## App access + +Jika aplikasi tidak punya login: + +- pilih bahwa tidak ada kredensial yang harus diberikan +- tulis bahwa semua fitur utama bisa diakses langsung + +Jika nanti ada login: + +- siapkan akun demo +- tulis langkah akses yang jelas di form App access + +## Target audience + +Saran: + +- umum +- semua usia + +Kalau kamu ingin sangat aman, gunakan bahasa yang netral dan hindari klaim yang berlebihan. + +## Store metadata yang disarankan + +### Nama + +`JamShalat` + +### Ringkasan + +Teman ibadah harian untuk shalat, Qur'an, dzikir, doa, dan laporan progres. + +### Kata pembuka deskripsi + +JamShalat adalah aplikasi pendamping ibadah harian untuk membantu pengguna menjaga rutinitas shalat, membaca Al-Qur'an, berdzikir, berdoa, dan memantau progres ibadah dengan lebih rapi. + +## Checklist sebelum submit + +- [x] nama aplikasi sudah final +- [x] package name sudah final +- [x] keystore rilis sudah tersedia +- [ ] `android/key.properties` sudah dibuat +- [ ] privacy policy URL sudah siap +- [ ] screenshot sudah siap +- [ ] feature graphic sudah siap +- [ ] data safety sudah diisi sesuai data nyata +- [ ] content rating sudah diisi +- [ ] AAB release sudah dibangun +- [ ] file `app-release.aab` sudah diupload ke track testing + diff --git a/docs/google-play-release-checklist.md b/docs/google-play-release-checklist.md new file mode 100644 index 0000000..60be921 --- /dev/null +++ b/docs/google-play-release-checklist.md @@ -0,0 +1,127 @@ +# Checklist Rilis Google Play + +Dokumen ini menjelaskan langkah teknis dari repo ini sampai file `AAB` siap diupload ke Google Play Console. + +## Status proyek saat ini + +- [x] Nama aplikasi sudah diganti menjadi `JamShalat` +- [x] `applicationId` Android sudah diset ke `com.jamshalat.app` +- [x] File keystore rilis sudah ada di `android/app/upload-keystore.p12` +- [x] Konfigurasi signing release sudah disiapkan di `android/app/build.gradle.kts` +- [x] Versi aplikasi sudah ada di `pubspec.yaml` +- [x] Ikon dan branding aplikasi sudah ada +- [ ] File `android/key.properties` belum ada di repo +- [ ] URL privacy policy belum disiapkan +- [ ] Screenshot Play Store belum disiapkan +- [ ] Store listing dan form Play Console belum diisi + +## Catatan penting soal signing + +- Teks `Releases signed by Google Play` di Play Console berarti app signing dikelola Google Play. +- Itu berbeda dari upload key yang dipakai saat kamu membangun file `AAB` dari mesin lokal. +- `applicationId` ada di [android/app/build.gradle.kts](/Users/dwindown/Applications/jamshalat-diary/android/app/build.gradle.kts) dan saat ini bernilai `com.jamshalat.app`. +- File `android/key.properties` adalah file lokal di mesin rilis, bukan file yang di-upload ke Play Console. +- Pada repo ini, file keystore ada di `android/app/upload-keystore.p12`, dan isi `storeFile` di `key.properties` harus mengarah ke file itu dari folder `android/`. + +## Yang perlu dipersiapkan + +1. Siapkan kredensial signing release + - Buat file `android/key.properties` + - Isi dengan lokasi file keystore, alias, dan password + - Contoh isi: + +```properties +storeFile=../app/upload-keystore.p12 +storePassword=PASSWORD_STORE_ANDA +keyAlias=upload +keyPassword=PASSWORD_KEY_ANDA +``` + +Catatan: +- alias pada keystore yang ada di repo ini adalah `upload` +- password `storePassword` dan `keyPassword` adalah password yang kamu tentukan saat membuat keystore + +2. Naikkan versi aplikasi setiap kali rilis + - Edit `pubspec.yaml` + - Ubah `version` menjadi format `major.minor.patch+versionCode` + - Contoh: + +```yaml +version: 1.0.1+2 +``` + +3. Jalankan build release + - Dari root project, jalankan: + +```bash +flutter pub get +flutter build appbundle --release +``` + +4. Ambil file hasil build + - File yang diupload ke Play Console adalah: + +```text +build/app/outputs/bundle/release/app-release.aab +``` + +5. Upload ke Play Console + - Buka Google Play Console + - Masuk ke app `JamShalat` + - Upload `app-release.aab` ke track yang sesuai + - Saran alur: + - Internal testing untuk cek awal + - Closed testing jika diperlukan oleh akun baru + - Production setelah siap rilis + +## Alur step by step + +1. Pastikan file signing sudah lengkap + - `android/app/upload-keystore.p12` ada + - `android/key.properties` sudah dibuat + - password dan alias benar + +2. Pastikan versi sudah dinaikkan + - Jangan upload `versionCode` yang sama dua kali + - Play Store menolak upload dengan `versionCode` yang sama atau lebih kecil + +3. Build bundle release + - Jalankan `flutter build appbundle --release` + - Tunggu sampai selesai + +4. Verifikasi file output + - Cari `build/app/outputs/bundle/release/app-release.aab` + - Inilah file utama untuk upload + +5. Siapkan akun Play Console + - Login ke akun developer yang sudah diverifikasi + - Pastikan payment/profile/account sudah lengkap + +6. Isi store listing + - Nama aplikasi + - Deskripsi singkat + - Deskripsi lengkap + - Ikon + - Screenshot + - Feature graphic + - Privacy policy + +7. Isi form compliance + - Data safety + - Content rating + - Target audience + - App access jika ada login + +8. Upload ke testing track dulu + - Disarankan jangan langsung produksi + - Tes install, buka app, login jika ada, dan semua fitur utama + +9. Promosikan ke production + - Setelah semua aman, promote release ke production + +## Catatan penting + +- Karena `applicationId` saat ini sudah berubah menjadi `com.jamshalat.app`, upload ini akan dianggap sebagai app baru di Play Store jika sebelumnya ada app lain dengan package berbeda. +- Jangan gunakan APK untuk upload ke Play Store utama. Gunakan AAB. +- Kalau file `android/key.properties` belum ada, build lokal mungkin tetap jalan dengan debug signing, tetapi itu tidak boleh dipakai untuk upload ke Play Store. +- Untuk akun developer personal baru, biasanya perlu closed testing lebih dulu sebelum production. Cek aturan akun di Play Console jika diminta. diff --git a/docs/google-play-release-tracks-id.md b/docs/google-play-release-tracks-id.md new file mode 100644 index 0000000..bffc1d6 --- /dev/null +++ b/docs/google-play-release-tracks-id.md @@ -0,0 +1,219 @@ +# Panduan Rilis per Track Google Play + +Dokumen ini menjelaskan langkah detail untuk tiap jenis rilis: Internal Testing, Closed Testing, dan Production. + +## Status proyek saat ini + +- [x] Nama aplikasi sudah final: `JamShalat` +- [x] Package name Android sudah final: `com.jamshalat.app` +- [x] Keystore rilis sudah ada: `android/app/upload-keystore.p12` +- [ ] `android/key.properties` masih perlu disiapkan di mesin rilis +- [ ] URL privacy policy perlu disiapkan +- [ ] Screenshot Play Store perlu disiapkan +- [ ] Feature graphic perlu disiapkan + +## Sebelum semua rilis + +1. Naikkan versi aplikasi + - Edit `pubspec.yaml` + - Tambahkan `versionCode` baru + - Contoh: + +```yaml +version: 1.0.1+2 +``` + +2. Pastikan signing release aktif +- File `android/key.properties` harus ada +- Isi password dan alias harus benar +- Contoh isi yang sesuai dengan repo ini: + +```properties +storeFile=../app/upload-keystore.p12 +storePassword=PASSWORD_STORE_ANDA +keyAlias=upload +keyPassword=PASSWORD_KEY_ANDA +``` + +Catatan: +- alias pada keystore yang ada di repo ini adalah `upload` +- password keystore tidak diambil dari Play Console, tapi dari saat keystore itu dibuat + +3. Build bundle release + - Jalankan: + +```bash +flutter pub get +flutter build appbundle --release +``` + +4. Ambil file hasil build + - File upload ada di: + +```text +build/app/outputs/bundle/release/app-release.aab +``` + +## 1. Internal Testing + +Gunakan track ini untuk cek awal sebelum rilis lebih luas. + +### Tujuan + +- memastikan app bisa di-install +- memastikan login, beranda, jadwal, qibla, quran, dzikir, dan share berjalan +- menangkap crash atau bug awal + +### Langkah + +1. Buka Google Play Console +2. Pilih app `JamShalat` +3. Masuk ke `Testing` lalu `Internal testing` +4. Klik `Create new release` +5. Upload file `app-release.aab` +6. Tulis release notes + - contoh: + +```text +Perbaikan stabilitas, penyempurnaan tampilan, dan pembaruan fitur ibadah harian. +``` + +7. Simpan release +8. Review ringkas halaman release +9. Klik `Save` atau `Publish` ke internal testing +10. Tambahkan tester internal jika diminta +11. Install dari link testing +12. Cek: + - buka aplikasi + - navigasi utama + - push notification + - audio murattal + - share gambar/tulisan + +### Kapan dipakai + +- setiap kali ada perubahan kecil +- saat ingin tes cepat setelah build baru +- sebelum closed testing + +## 2. Closed Testing + +Gunakan track ini untuk tes yang lebih serius dengan tester terbatas. + +### Tujuan + +- validasi sebelum produksi +- cek aplikasi pada beberapa device nyata +- kumpulkan feedback dari tester terbatas + +### Langkah + +1. Buka Google Play Console +2. Pilih app `JamShalat` +3. Masuk ke `Testing` lalu `Closed testing` +4. Buat tester list atau Google Group jika belum ada +5. Tambahkan alamat email tester +6. Klik `Create new release` +7. Upload `app-release.aab` +8. Tulis release notes + - contoh: + +```text +Rilis uji tertutup untuk validasi stabilitas, performa, dan kesiapan produksi. +``` + +9. Simpan release +10. Pastikan tester menerima link opt-in +11. Minta tester install dari link closed test +12. Tunggu hasil tes dan feedback + +### Jika akun developer personal baru + +Kalau Google Play Console meminta syarat tes 14 hari: + +1. Jalankan closed testing sesuai aturan akun +2. Pastikan jumlah tester sesuai permintaan Google +3. Pastikan testing berjalan kontinu sesuai masa yang diminta +4. Setelah lolos, baru lanjut ke production + +### Kapan dipakai + +- sebelum production pertama kali +- saat perlu validasi lebih luas dari internal testing + +## 3. Production + +Gunakan track ini untuk rilis publik. + +### Tujuan + +- app tersedia untuk publik di Google Play + +### Langkah + +1. Pastikan internal testing dan closed testing sudah aman +2. Pastikan versionCode sudah naik +3. Pastikan listing sudah lengkap + - nama + - deskripsi + - screenshot + - feature graphic + - privacy policy + - data safety + - content rating +4. Masuk ke `Release` atau `Production` di Play Console +5. Klik `Create new release` +6. Upload file `app-release.aab` +7. Isi release notes publik + - contoh: + +```text +Rilis awal JamShalat dengan fitur jadwal shalat, Al-Qur'an, dzikir, kiblat, laporan ibadah, dan sharing ayat. +``` + +8. Review summary +9. Pastikan tidak ada error atau warning fatal +10. Kirim ke review Google Play +11. Tunggu approval +12. Setelah disetujui, app akan tayang ke publik + +### Kapan dipakai + +- hanya ketika kamu siap publikasi +- jangan dipakai untuk tes harian + +## Checklist per rilis + +### Internal Testing + +- [ ] versionCode dinaikkan +- [ ] `flutter build appbundle --release` berhasil +- [ ] `app-release.aab` siap +- [ ] release notes ditulis +- [ ] tester internal sudah ada +- [ ] install dari Play Console berhasil + +### Closed Testing + +- [ ] versionCode dinaikkan +- [ ] `app-release.aab` siap +- [ ] tester list sudah dibuat +- [ ] opt-in link sudah dibagikan +- [ ] feedback tester sudah dicek + +### Production + +- [ ] semua testing track aman +- [ ] store listing lengkap +- [ ] data safety diisi +- [ ] content rating selesai +- [ ] privacy policy aktif +- [ ] release notes publik ditulis +- [ ] review produksi sudah dikirim + +## Catatan penting + +- Jangan upload APK ke production. Pakai AAB. +- Jangan lupa menaikkan `versionCode` untuk setiap upload baru. +- Jika kamu mengganti `applicationId`, Play Console menganggapnya sebagai app baru. +- Simpan file keystore dan `android/key.properties` dengan aman. diff --git a/docs/privacy-policy-gutenberg-id.html b/docs/privacy-policy-gutenberg-id.html new file mode 100644 index 0000000..5a6024e --- /dev/null +++ b/docs/privacy-policy-gutenberg-id.html @@ -0,0 +1,109 @@ +

Kebijakan Privasi JamShalat

+

Terakhir diperbarui: 19 Maret 2026

+ +

JamShalat menghormati privasi pengguna. Kebijakan ini menjelaskan data apa saja yang digunakan oleh aplikasi, bagaimana data tersebut diproses, dan untuk tujuan apa data dipakai.

+ +

Dengan menggunakan JamShalat, Anda menyetujui praktik yang dijelaskan dalam kebijakan ini.

+ +

1. Informasi yang digunakan aplikasi

+

JamShalat dapat menggunakan informasi berikut:

+
    +
  • lokasi perangkat
  • +
  • data ibadah yang disimpan di perangkat
  • +
  • pengaturan aplikasi
  • +
  • data notifikasi
  • +
  • data konten yang diambil dari layanan pihak ketiga
  • +
+ +

2. Lokasi perangkat

+

JamShalat dapat menggunakan lokasi perangkat untuk:

+
    +
  • menampilkan arah kiblat
  • +
  • menghitung atau menampilkan jadwal shalat
  • +
  • membantu menyesuaikan informasi ibadah berdasarkan wilayah pengguna
  • +
+

Lokasi dipakai hanya untuk fitur yang membutuhkan lokasi. Jika izin lokasi tidak diberikan, fitur yang bergantung pada lokasi mungkin tidak bekerja penuh.

+ +

3. Penyimpanan data

+

Sebagian besar data pengguna disimpan secara lokal di perangkat, termasuk:

+
    +
  • progres checklist ibadah
  • +
  • marka bacaan Al-Qur'an
  • +
  • dzikir counter
  • +
  • pengaturan tampilan
  • +
  • preferensi mode aplikasi
  • +
+

Data lokal ini digunakan untuk menjaga pengalaman pengguna tetap konsisten di perangkat yang sama.

+ +

4. Notifikasi

+

JamShalat dapat menggunakan notifikasi untuk:

+
    +
  • pengingat adzan
  • +
  • pengingat iqamah
  • +
  • pengingat ibadah dan dzikir
  • +
  • notifikasi ringkasan atau pengingat lain yang relevan
  • +
+

Notifikasi dipakai untuk membantu pengguna menjaga rutinitas ibadah.

+ +

5. Konten dari layanan pihak ketiga

+

JamShalat dapat mengambil data dari layanan pihak ketiga untuk menyediakan:

+
    +
  • jadwal shalat
  • +
  • konten Al-Qur'an
  • +
  • tafsir
  • +
  • dzikir
  • +
  • doa
  • +
  • hadits
  • +
  • gambar latar atau konten visual tertentu
  • +
+

Layanan pihak ketiga yang dapat digunakan antara lain API konten dan API gambar.

+

JamShalat tidak menjual data pribadi pengguna.

+ +

6. Data yang tidak dikumpulkan

+

JamShalat tidak mengharuskan pengguna membuat akun untuk memakai fitur utama.

+

JamShalat tidak secara khusus meminta:

+
    +
  • nama lengkap
  • +
  • alamat email
  • +
  • nomor telepon
  • +
  • data kartu kredit
  • +
  • kontak pribadi
  • +
+

Jika suatu saat ada fitur tambahan yang membutuhkan data lain, kebijakan ini akan diperbarui.

+ +

7. Penggunaan data

+

Data yang digunakan JamShalat dipakai untuk:

+
    +
  • menjalankan fitur aplikasi
  • +
  • menyimpan progres ibadah
  • +
  • menampilkan jadwal, arah kiblat, dan konten ibadah
  • +
  • mengirim pengingat yang dipilih pengguna
  • +
  • menjaga pengalaman pengguna tetap nyaman dan konsisten
  • +
+ +

8. Pembagian data

+

JamShalat tidak membagikan data pribadi pengguna kepada pihak lain untuk tujuan iklan atau penjualan.

+

Data dapat diproses oleh layanan pihak ketiga yang memang dibutuhkan untuk menampilkan konten atau gambar, misalnya API jadwal shalat, API Al-Qur'an, atau layanan gambar.

+ +

9. Penyimpanan dan retensi

+

Data lokal disimpan di perangkat pengguna selama aplikasi masih digunakan atau sampai pengguna menghapus data aplikasi.

+

Jika pengguna menghapus aplikasi atau membersihkan data aplikasi, data lokal yang tersimpan di perangkat dapat ikut terhapus.

+ +

10. Keamanan

+

JamShalat berusaha menggunakan cara yang wajar untuk menjaga data pengguna. Namun, tidak ada metode penyimpanan atau transmisi data yang sepenuhnya bebas risiko.

+ +

11. Hak pengguna

+

Pengguna dapat:

+
    +
  • menolak izin lokasi atau notifikasi
  • +
  • menghapus data aplikasi dari perangkat
  • +
  • mencopot pemasangan aplikasi
  • +
+

Jika izin tertentu ditolak, beberapa fitur mungkin tidak berfungsi penuh.

+ +

12. Perubahan kebijakan

+

Kebijakan privasi ini dapat diperbarui dari waktu ke waktu. Perubahan terbaru akan ditampilkan di halaman ini.

+ +

13. Kontak

+

Jika ada pertanyaan tentang kebijakan privasi ini, silakan hubungi pengembang aplikasi melalui kontak resmi yang disediakan di Play Store atau situs aplikasi.

+ diff --git a/docs/privacy-policy-id.md b/docs/privacy-policy-id.md new file mode 100644 index 0000000..b1f1454 --- /dev/null +++ b/docs/privacy-policy-id.md @@ -0,0 +1,129 @@ +# Kebijakan Privasi JamShalat + +Terakhir diperbarui: 19 Maret 2026 + +Dokumen ini ditulis agar mudah dipaste ke editor CMS seperti Gutenberg. + +## 1. Pendahuluan + +JamShalat menghormati privasi pengguna. Kebijakan ini menjelaskan data apa saja yang digunakan oleh aplikasi, bagaimana data tersebut diproses, dan untuk tujuan apa data dipakai. + +Dengan menggunakan JamShalat, Anda menyetujui praktik yang dijelaskan dalam kebijakan ini. + +## 2. Informasi yang digunakan aplikasi + +JamShalat dapat menggunakan informasi berikut: + +- lokasi perangkat +- data ibadah yang disimpan di perangkat +- pengaturan aplikasi +- data notifikasi +- data konten yang diambil dari layanan pihak ketiga + +## 3. Lokasi perangkat + +JamShalat dapat menggunakan lokasi perangkat untuk: + +- menampilkan arah kiblat +- menghitung atau menampilkan jadwal shalat +- membantu menyesuaikan informasi ibadah berdasarkan wilayah pengguna + +Lokasi dipakai hanya untuk fitur yang membutuhkan lokasi. Jika izin lokasi tidak diberikan, fitur yang bergantung pada lokasi mungkin tidak bekerja penuh. + +## 4. Penyimpanan data + +Sebagian besar data pengguna disimpan secara lokal di perangkat, termasuk: + +- progres checklist ibadah +- marka bacaan Al-Qur'an +- dzikir counter +- pengaturan tampilan +- preferensi mode aplikasi + +Data lokal ini digunakan untuk menjaga pengalaman pengguna tetap konsisten di perangkat yang sama. + +## 5. Notifikasi + +JamShalat dapat menggunakan notifikasi untuk: + +- pengingat adzan +- pengingat iqamah +- pengingat ibadah dan dzikir +- notifikasi ringkasan atau pengingat lain yang relevan + +Notifikasi dipakai untuk membantu pengguna menjaga rutinitas ibadah. + +## 6. Konten dari layanan pihak ketiga + +JamShalat dapat mengambil data dari layanan pihak ketiga untuk menyediakan: + +- jadwal shalat +- konten Al-Qur'an +- tafsir +- dzikir +- doa +- hadits +- gambar latar atau konten visual tertentu + +Layanan pihak ketiga yang dapat digunakan antara lain API konten dan API gambar. + +JamShalat tidak menjual data pribadi pengguna. + +## 7. Data yang tidak dikumpulkan + +JamShalat tidak mengharuskan pengguna membuat akun untuk memakai fitur utama. + +JamShalat tidak secara khusus meminta: + +- nama lengkap +- alamat email +- nomor telepon +- data kartu kredit +- kontak pribadi + +Jika suatu saat ada fitur tambahan yang membutuhkan data lain, kebijakan ini akan diperbarui. + +## 8. Penggunaan data + +Data yang digunakan JamShalat dipakai untuk: + +- menjalankan fitur aplikasi +- menyimpan progres ibadah +- menampilkan jadwal, arah kiblat, dan konten ibadah +- mengirim pengingat yang dipilih pengguna +- menjaga pengalaman pengguna tetap nyaman dan konsisten + +## 9. Pembagian data + +JamShalat tidak membagikan data pribadi pengguna kepada pihak lain untuk tujuan iklan atau penjualan. + +Data dapat diproses oleh layanan pihak ketiga yang memang dibutuhkan untuk menampilkan konten atau gambar, misalnya API jadwal shalat, API Al-Qur'an, atau layanan gambar. + +## 10. Penyimpanan dan retensi + +Data lokal disimpan di perangkat pengguna selama aplikasi masih digunakan atau sampai pengguna menghapus data aplikasi. + +Jika pengguna menghapus aplikasi atau membersihkan data aplikasi, data lokal yang tersimpan di perangkat dapat ikut terhapus. + +## 11. Keamanan + +JamShalat berusaha menggunakan cara yang wajar untuk menjaga data pengguna. Namun, tidak ada metode penyimpanan atau transmisi data yang sepenuhnya bebas risiko. + +## 12. Hak pengguna + +Pengguna dapat: + +- menolak izin lokasi atau notifikasi +- menghapus data aplikasi dari perangkat +- mencopot pemasangan aplikasi + +Jika izin tertentu ditolak, beberapa fitur mungkin tidak berfungsi penuh. + +## 13. Perubahan kebijakan + +Kebijakan privasi ini dapat diperbarui dari waktu ke waktu. Perubahan terbaru akan ditampilkan di halaman ini. + +## 14. Kontak + +Jika ada pertanyaan tentang kebijakan privasi ini, silakan hubungi pengembang aplikasi melalui kontak resmi yang disediakan di Play Store atau situs aplikasi. + diff --git a/lib/core/widgets/ayat_share_sheet.dart b/lib/core/widgets/ayat_share_sheet.dart index d20d8ea..c1dcf3f 100644 --- a/lib/core/widgets/ayat_share_sheet.dart +++ b/lib/core/widgets/ayat_share_sheet.dart @@ -5,12 +5,17 @@ import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; import 'package:flutter/services.dart'; import 'package:lucide_icons/lucide_icons.dart'; +import 'package:path_provider/path_provider.dart'; import 'package:share_plus/share_plus.dart'; import '../../app/icons/app_icons.dart'; import '../../app/theme/app_colors.dart'; import '../../core/widgets/arabic_text.dart'; +const MethodChannel _androidShareChannel = MethodChannel( + 'com.jamshalat.app/share', +); + String buildAyatShareText(Map ayat) { final arabic = (ayat['teksArab'] ?? '').toString().trim(); final translation = (ayat['teksIndonesia'] ?? '').toString().trim(); @@ -50,11 +55,7 @@ Future showAyatShareSheet( try { final pngBytes = await _captureAyatShareCardPng(context, ayat); final file = await _writeAyatShareImage(pngBytes); - await Share.shareXFiles( - [XFile(file.path)], - text: 'Ayat Hari Ini', - subject: 'Ayat Hari Ini', - ); + await _shareAyatImage(file); } catch (_) { if (!context.mounted) return; ScaffoldMessenger.of(context) @@ -224,12 +225,40 @@ Future _captureAyatShareCardPng( } Future _writeAyatShareImage(Uint8List pngBytes) async { - final directory = await Directory.systemTemp.createTemp('jamshalat_ayat_'); - final file = File('${directory.path}/ayat_hari_ini.png'); + final tempDirectory = await getTemporaryDirectory(); + final shareDirectory = + Directory('${tempDirectory.path}/share_plus/jamshalat'); + if (!await shareDirectory.exists()) { + await shareDirectory.create(recursive: true); + } + + final timestamp = DateTime.now().millisecondsSinceEpoch; + final file = File('${shareDirectory.path}/ayat_hari_ini_$timestamp.png'); await file.writeAsBytes(pngBytes, flush: true); return file; } +Future _shareAyatImage(File file) async { + if (Platform.isAndroid) { + await _androidShareChannel.invokeMethod('shareImage', { + 'path': file.path, + 'mimeType': 'image/png', + 'chooserTitle': 'Bagikan ayat', + }); + return; + } + + await Share.shareXFiles( + [ + XFile( + file.path, + mimeType: 'image/png', + ), + ], + fileNameOverrides: const ['jamshalat_ayat_hari_ini.png'], + ); +} + class _AyatShareCard extends StatelessWidget { const _AyatShareCard({ required this.ayat, diff --git a/lib/core/widgets/bottom_sheet_content_padding.dart b/lib/core/widgets/bottom_sheet_content_padding.dart new file mode 100644 index 0000000..5b0c0fc --- /dev/null +++ b/lib/core/widgets/bottom_sheet_content_padding.dart @@ -0,0 +1,23 @@ +import 'dart:math' as math; + +import 'package:flutter/widgets.dart'; + +EdgeInsets bottomSheetContentPadding( + BuildContext context, { + double horizontal = 16, + double top = 20, + double bottom = 20, +}) { + final mediaQuery = MediaQuery.of(context); + final bottomInset = math.max( + mediaQuery.viewInsets.bottom, + mediaQuery.viewPadding.bottom, + ); + + return EdgeInsets.fromLTRB( + horizontal, + top, + horizontal, + bottom + bottomInset, + ); +} diff --git a/lib/features/doa/presentation/doa_screen.dart b/lib/features/doa/presentation/doa_screen.dart index a9c7a03..d1881e4 100644 --- a/lib/features/doa/presentation/doa_screen.dart +++ b/lib/features/doa/presentation/doa_screen.dart @@ -3,6 +3,7 @@ import 'package:hive_flutter/hive_flutter.dart'; import 'package:lucide_icons/lucide_icons.dart'; import '../../../app/theme/app_colors.dart'; import '../../../core/widgets/arabic_text.dart'; +import '../../../core/widgets/bottom_sheet_content_padding.dart'; import '../../../data/local/hive_boxes.dart'; import '../../../data/local/models/app_settings.dart'; import '../../../data/services/muslim_api_service.dart'; @@ -91,9 +92,8 @@ class _DoaScreenState extends State { ), builder: (ctx) => StatefulBuilder( builder: (context, setModalState) { - final keyboardInset = MediaQuery.of(context).viewInsets.bottom; return Padding( - padding: EdgeInsets.fromLTRB(16, 20, 16, 20 + keyboardInset), + padding: bottomSheetContentPadding(context), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/features/dzikir/presentation/dzikir_screen.dart b/lib/features/dzikir/presentation/dzikir_screen.dart index b8c01c9..f8a8070 100644 --- a/lib/features/dzikir/presentation/dzikir_screen.dart +++ b/lib/features/dzikir/presentation/dzikir_screen.dart @@ -10,6 +10,7 @@ import 'package:lucide_icons/lucide_icons.dart'; import '../../../app/theme/app_colors.dart'; import '../../../core/widgets/arabic_text.dart'; +import '../../../core/widgets/bottom_sheet_content_padding.dart'; import '../../../data/local/hive_boxes.dart'; import '../../../data/local/models/app_settings.dart'; import '../../../data/local/models/daily_worship_log.dart'; @@ -642,9 +643,8 @@ class _DzikirScreenState extends ConsumerState ), builder: (ctx) => StatefulBuilder( builder: (context, setModalState) { - final keyboardInset = MediaQuery.of(context).viewInsets.bottom; return Padding( - padding: EdgeInsets.fromLTRB(16, 20, 16, 20 + keyboardInset), + padding: bottomSheetContentPadding(context), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/features/hadits/presentation/hadits_screen.dart b/lib/features/hadits/presentation/hadits_screen.dart index 5b39668..0abfd83 100644 --- a/lib/features/hadits/presentation/hadits_screen.dart +++ b/lib/features/hadits/presentation/hadits_screen.dart @@ -3,6 +3,7 @@ import 'package:hive_flutter/hive_flutter.dart'; import 'package:lucide_icons/lucide_icons.dart'; import '../../../app/theme/app_colors.dart'; import '../../../core/widgets/arabic_text.dart'; +import '../../../core/widgets/bottom_sheet_content_padding.dart'; import '../../../data/local/hive_boxes.dart'; import '../../../data/local/models/app_settings.dart'; import '../../../data/services/muslim_api_service.dart'; @@ -96,9 +97,8 @@ class _HaditsScreenState extends State { ), builder: (ctx) => StatefulBuilder( builder: (context, setModalState) { - final keyboardInset = MediaQuery.of(context).viewInsets.bottom; return Padding( - padding: EdgeInsets.fromLTRB(16, 20, 16, 20 + keyboardInset), + padding: bottomSheetContentPadding(context), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/features/quran/presentation/quran_bookmarks_screen.dart b/lib/features/quran/presentation/quran_bookmarks_screen.dart index 8f323c0..d2b3c0f 100644 --- a/lib/features/quran/presentation/quran_bookmarks_screen.dart +++ b/lib/features/quran/presentation/quran_bookmarks_screen.dart @@ -5,6 +5,7 @@ import 'package:go_router/go_router.dart'; import 'package:intl/intl.dart'; import '../../../app/theme/app_colors.dart'; import '../../../core/widgets/arabic_text.dart'; +import '../../../core/widgets/bottom_sheet_content_padding.dart'; import '../../../data/local/hive_boxes.dart'; import '../../../data/local/models/quran_bookmark.dart'; import '../../../data/local/models/app_settings.dart'; @@ -25,7 +26,8 @@ class _QuranBookmarksScreenState extends State { bool _showLatin = true; bool _showTerjemahan = true; final Map?>> _surahFutureCache = {}; - final Map> _bookmarkFutureCache = {}; + final Map> _bookmarkFutureCache = + {}; String _readingRoute(int surahId, int verseId) { final isSimple = @@ -54,9 +56,8 @@ class _QuranBookmarksScreenState extends State { ), builder: (ctx) => StatefulBuilder( builder: (context, setModalState) { - final keyboardInset = MediaQuery.of(context).viewInsets.bottom; return Padding( - padding: EdgeInsets.fromLTRB(16, 20, 16, 20 + keyboardInset), + padding: bottomSheetContentPadding(context), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, @@ -147,7 +148,8 @@ class _QuranBookmarksScreenState extends State { Future<_ResolvedBookmarkContent?> _getResolvedBookmarkContent( QuranBookmark bookmark, ) { - final bookmarkKey = bookmark.key ?? '${bookmark.surahId}_${bookmark.verseId}'; + final bookmarkKey = + bookmark.key ?? '${bookmark.surahId}_${bookmark.verseId}'; return _bookmarkFutureCache.putIfAbsent( bookmarkKey, () => _loadResolvedBookmarkContent(bookmark), diff --git a/lib/features/quran/presentation/quran_enrichment_screen.dart b/lib/features/quran/presentation/quran_enrichment_screen.dart index 5b1daf7..1fa602b 100644 --- a/lib/features/quran/presentation/quran_enrichment_screen.dart +++ b/lib/features/quran/presentation/quran_enrichment_screen.dart @@ -3,6 +3,7 @@ import 'package:hive_flutter/hive_flutter.dart'; import 'package:lucide_icons/lucide_icons.dart'; import '../../../app/theme/app_colors.dart'; import '../../../core/widgets/arabic_text.dart'; +import '../../../core/widgets/bottom_sheet_content_padding.dart'; import '../../../data/local/hive_boxes.dart'; import '../../../data/local/models/app_settings.dart'; import '../../../data/services/muslim_api_service.dart'; @@ -193,9 +194,8 @@ class _QuranEnrichmentScreenState extends State ), builder: (ctx) => StatefulBuilder( builder: (context, setModalState) { - final keyboardInset = MediaQuery.of(context).viewInsets.bottom; return Padding( - padding: EdgeInsets.fromLTRB(16, 20, 16, 20 + keyboardInset), + padding: bottomSheetContentPadding(context), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/features/quran/presentation/quran_reading_screen.dart b/lib/features/quran/presentation/quran_reading_screen.dart index 2199899..a396c02 100644 --- a/lib/features/quran/presentation/quran_reading_screen.dart +++ b/lib/features/quran/presentation/quran_reading_screen.dart @@ -10,6 +10,7 @@ import 'package:intl/intl.dart'; import '../../../app/theme/app_colors.dart'; import '../../../core/services/app_audio_player.dart'; import '../../../core/widgets/arabic_text.dart'; +import '../../../core/widgets/bottom_sheet_content_padding.dart'; import '../../../data/local/hive_boxes.dart'; import '../../../data/local/models/quran_bookmark.dart'; import '../../../data/local/models/app_settings.dart'; @@ -845,9 +846,8 @@ class _QuranReadingScreenState extends ConsumerState { ), builder: (ctx) => StatefulBuilder( builder: (context, setModalState) { - final keyboardInset = MediaQuery.of(context).viewInsets.bottom; return Padding( - padding: EdgeInsets.fromLTRB(16, 20, 16, 20 + keyboardInset), + padding: bottomSheetContentPadding(context), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, diff --git a/lib/features/quran/presentation/quran_screen.dart b/lib/features/quran/presentation/quran_screen.dart index 0c7d3cc..17ff169 100644 --- a/lib/features/quran/presentation/quran_screen.dart +++ b/lib/features/quran/presentation/quran_screen.dart @@ -5,6 +5,7 @@ import 'package:lucide_icons/lucide_icons.dart'; import 'package:hive_flutter/hive_flutter.dart'; import '../../../app/theme/app_colors.dart'; import '../../../core/widgets/arabic_text.dart'; +import '../../../core/widgets/bottom_sheet_content_padding.dart'; import '../../../data/local/hive_boxes.dart'; import '../../../data/local/models/app_settings.dart'; import '../../../data/local/models/quran_bookmark.dart'; @@ -55,9 +56,8 @@ class _QuranScreenState extends ConsumerState { ), builder: (ctx) => StatefulBuilder( builder: (context, setModalState) { - final keyboardInset = MediaQuery.of(context).viewInsets.bottom; return Padding( - padding: EdgeInsets.fromLTRB(16, 20, 16, 20 + keyboardInset), + padding: bottomSheetContentPadding(context), child: Column( mainAxisSize: MainAxisSize.min, crossAxisAlignment: CrossAxisAlignment.start, diff --git a/pubspec.lock b/pubspec.lock index 742e14d..4afd396 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -870,7 +870,7 @@ packages: source: hosted version: "1.1.0" path_provider: - dependency: transitive + dependency: "direct main" description: name: path_provider sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" diff --git a/pubspec.yaml b/pubspec.yaml index b06b71b..a0f291f 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -49,6 +49,7 @@ dependencies: flutter_dotenv: ^5.1.0 cached_network_image: ^3.3.1 share_plus: ^10.1.4 + path_provider: ^2.1.5 url_launcher: ^6.2.5 lucide_icons: ^0.257.0 hugeicons: ^1.1.5