From c32b56c00e4dbb617ad5e7ecb444a6b1642c345d Mon Sep 17 00:00:00 2001 From: Dwindi Ramadhana Date: Sun, 22 Mar 2026 19:37:49 +0700 Subject: [PATCH] FIX Telegram Mime image share --- .../kotlin/com/jamshalat/app/MainActivity.kt | 57 +++++ android/app/upload-keystore.p12 | Bin 2664 -> 2808 bytes android/app/upload-keystore.p12.bak | Bin 0 -> 2808 bytes docs/google-play-form-guide-id.md | 222 ++++++++++++++++++ docs/google-play-release-checklist.md | 127 ++++++++++ docs/google-play-release-tracks-id.md | 219 +++++++++++++++++ docs/privacy-policy-gutenberg-id.html | 109 +++++++++ docs/privacy-policy-id.md | 129 ++++++++++ lib/core/widgets/ayat_share_sheet.dart | 43 +++- .../widgets/bottom_sheet_content_padding.dart | 23 ++ lib/features/doa/presentation/doa_screen.dart | 4 +- .../dzikir/presentation/dzikir_screen.dart | 4 +- .../hadits/presentation/hadits_screen.dart | 4 +- .../presentation/quran_bookmarks_screen.dart | 10 +- .../presentation/quran_enrichment_screen.dart | 4 +- .../presentation/quran_reading_screen.dart | 4 +- .../quran/presentation/quran_screen.dart | 4 +- pubspec.lock | 2 +- pubspec.yaml | 1 + 19 files changed, 942 insertions(+), 24 deletions(-) create mode 100644 android/app/upload-keystore.p12.bak create mode 100644 docs/google-play-form-guide-id.md create mode 100644 docs/google-play-release-checklist.md create mode 100644 docs/google-play-release-tracks-id.md create mode 100644 docs/privacy-policy-gutenberg-id.html create mode 100644 docs/privacy-policy-id.md create mode 100644 lib/core/widgets/bottom_sheet_content_padding.dart 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 5d2289b9d4e0d2bb4ea673493ea99711af1a3b45..eee0d50e32749a82c5e6378b3d1bf2fd52bc3d3c 100644 GIT binary patch literal 2808 zcma);X*d)N_r}eJu_t5+lbwl~VM0tsGKj_&rjjkj(xNdEjf5dfGATk?hv*?B4>D5L zVrCGsQ?e!7*hWd{?SK8>tLJ(@z3+!}o$LJWbDxj*b&ym6E)W1hQU!8(p;~cPao>dj ze1Lqaz&$WkAn7-bM^Ygb{~KSJR zAcDXEEwI=J<+tQ1oWF_m?6N_`$;mb_72I=OI)8c|#Mad<3L;p5I&Y8_=M9H;sc9P0 zYDK`f$a1ZaVfhP}ww5&`ZfaGGQOpFTmf2~~s|;>=Qj>R@r+wUH%<0Yh_ISGRq+UcT zi`K-qoy~7QMXMiooto{B!(0n0vASef@&im~AC_|`Mz52iNBSxe3b|}w7;oiFCp;w8 z+)%SlfxpU#WLpTjWl$1ex?`3@(Si8%0mvHkY2 zjz*tf{bNjTN?%W(*E4+NPJCuy zJ4k-Bl#=_y08UJ@A_Pw8d76)#fz*cTFP#YPwe`XP6E__GDi2!r7UK}EMO`aO*R@T}?#oVE8;R?M^!BSe zvuWq+y)ZuYDdtJ$r+m&KO@4|^Nt;~<`nfjCKY9PaKI64osm7GlqSuhd>)h0uKJAn2 znp+}dTA@&j<>{ROKI`DTHG`ln1AP&txLN*2kHolH&UEH|aR#FOmyt7Z^TIZ5`djP+y7Vm$s6f^xjx=ZA`1} z@ZDu7Q-2LAhMu)Ls}E<8jLp?8a|<6<1WM9Npd5s9_WVNC^U~uQRhU(s#Sz5L&$}r` z=seL&$p!g>EX`yKlZx2YkD*VGdtlgG$Bw;-_Le6X-Nn|syz;V(@M(zG1JQx>i1)?U z+(a$CO+!D3(BMrQqaebF_TOqr?oNn}4gOp!sB_LONqiW6w4YZHF<6V)-u0_ns9a^8 z7s%Y|GpweI6iV~)?~8)g7?4SEax)CdkPYQ<#4i}ma_GVSd97bhk!X;bkR>( zgNNN^l*!)rtDlVk&Q^B27?9G)_fFV(E6BAp`L-qfAeDea-Maf9&dK-R7X%$f*Z0Z9 zoHpO_aSOcsaHMd=DOs*MIrPN`y?0Zwu^ve!64-?aC6pBX4r@3{0svG$sMrnOlVQKO zVJf+r8^0pwpcvIU1yP&o^E%N`+Z19MpI*%MiBZN?nbqX*C!Ol6U51yjFuTZ+vWc7! zr}ByCEssbjQ>5IVxP(feL==R0LU>3#mwB%8c=7lk5B(!bq9nlLP61blFqDCT-cemc z12ht4fJ9Qky?>VYc=M^?mfz?N5Ww@hz5GW3{I{4EkaN!nu<&-FroS~ur0-MZiVd#* zFEO2j>~+B#F7I~ctK;>MsW+miVAJB*xm#T|{-cQ6v_lMAY1q4Gi@U^H-0ce|lm_y& z@hTzr827nvn}3nVINghoW`{>Benwp!*^Tnk>;d`kdkrG#_cJCE`j3aB>jl=P6Sc(Z zXv5?o3+80krPJ!m7b2=2*E;X*CewTs&I^WPBic*n!pba?Dj(rYEWG`{&6%kZgEB4DK=)_+nabs@m3w**l?DFud{b4jQ zHdeiJPfZ^z(;lg=?-A@eMx24qym+uVC-Akj&l2S$L2K9LK4pttBtPpoMPZ4J%p?@=ukN>} z@<_dXfE1kv#Q6cJ26x6`#r;D0p2%rheG0DeMloVYEpg8C=UL^7sg(AKkZw)5A@#g| zDg}F*P}PtXm=ToAb%YpF4vHM9W;75mzY^UvlWYj%1P%V|^9#S!9`)0|-We@i(<`tg zYvI0dyUWx26hGhrdDlc#7iD7ltl6&d&NPIIQ|JCo_t;XjH>Yg^tl~V_D*TjXY-hJh z8a$!mv$j7-J*WFNqt)}ldfV<9
7w7}M5Hy8V;lzdpvSScKb^Di_#PsY@n^nc;N&bMY2N%8EA%6FHhyw_TUjX?Q*qxLM@9cH!u|z0jv1=} literal 2664 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 literal 0 HcmV?d00001 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