Compare commits

..

2 Commits

Author SHA1 Message Date
Dwindi Ramadhana
c32b56c00e FIX Telegram Mime image share 2026-03-22 19:37:49 +07:00
Dwindi Ramadhana
e34dd6cf06 Rename app identity to JamShalat 2026-03-18 00:45:24 +07:00
55 changed files with 1440 additions and 113 deletions

8
PRD.md
View File

@@ -1,5 +1,5 @@
# Product Requirements Document # Product Requirements Document
## Jamshalat Diary — Islamic Worship Companion App ## JamShalat — Islamic Worship Companion App
**Version:** 1.0 **Version:** 1.0
**Date:** March 2026 **Date:** March 2026
@@ -35,7 +35,7 @@
## 1. Overview ## 1. Overview
**Jamshalat Diary** is an offline-first Muslim daily worship companion app built in Flutter. It helps Muslim users track their daily prayers, worship habits, and spiritual growth with a clean, modern UI that supports both light and dark themes. **JamShalat** is an offline-first Muslim daily worship companion app built in Flutter. It helps Muslim users track their daily prayers, worship habits, and spiritual growth with a clean, modern UI that supports both light and dark themes.
**Core value proposition:** **Core value proposition:**
- Never miss a prayer — real-time prayer time countdowns with Adhan/Iqamah notifications - Never miss a prayer — real-time prayer time countdowns with Adhan/Iqamah notifications
@@ -683,7 +683,7 @@ Rows:
Label: "ABOUT" Label: "ABOUT"
Rows: Rows:
- App Version: "Jamshalat Diary v1.0.0" - App Version: "JamShalat v1.0.0"
- Privacy Policy (launches in-app browser) - Privacy Policy (launches in-app browser)
- Rate the App (links to store) - Rate the App (links to store)
- Contact / Feedback - Contact / Feedback
@@ -881,4 +881,4 @@ The following features are **explicitly excluded** from v1.0 to keep scope focus
--- ---
*PRD v1.0 — Jamshalat Diary — March 2026* *PRD v1.0 — JamShalat — March 2026*

View File

@@ -1,4 +1,4 @@
# jamshalat_diary # JamShalat
A new Flutter project. A new Flutter project.

View File

@@ -1,6 +1,6 @@
# Jamshalat Diary — Phase-Based Implementation Tasklist # JamShalat — Phase-Based Implementation Tasklist
**Project:** Jamshalat Diary Flutter App **Project:** JamShalat Flutter App
**PRD Reference:** `PRD.md` **PRD Reference:** `PRD.md`
**Design Reference:** `stitch/` folder (18 screens) **Design Reference:** `stitch/` folder (18 screens)
**Last Updated:** March 2026 **Last Updated:** March 2026
@@ -302,8 +302,8 @@
- [ ] **7.3.1** Create app icon (1024×1024px): mosque/compass motif with `#70df20` primary color - [ ] **7.3.1** Create app icon (1024×1024px): mosque/compass motif with `#70df20` primary color
- [ ] **7.3.2** Apply app icon via `flutter_launcher_icons` package (all densities, adaptive icon for Android) - [ ] **7.3.2** Apply app icon via `flutter_launcher_icons` package (all densities, adaptive icon for Android)
- [ ] **7.3.3** Create splash screen via `flutter_native_splash` package (white/dark bg, centered logo) - [ ] **7.3.3** Create splash screen via `flutter_native_splash` package (white/dark bg, centered logo)
- [ ] **7.3.4** Set app name: "Jamshalat Diary" in `AndroidManifest.xml` and `Info.plist` - [ ] **7.3.4** Set app name: "JamShalat" in `AndroidManifest.xml` and `Info.plist`
- [ ] **7.3.5** Set bundle ID: `com.jamshalat.diary` on both platforms - [ ] **7.3.5** Set bundle ID: `com.jamshalat.app` on both platforms
- [ ] **7.3.6** Configure release signing (Android keystore, iOS certificates) — document in private README - [ ] **7.3.6** Configure release signing (Android keystore, iOS certificates) — document in private README
- [ ] **7.3.7** Build release APK: `flutter build apk --release` — verify no build errors - [ ] **7.3.7** Build release APK: `flutter build apk --release` — verify no build errors
- [ ] **7.3.8** Build iOS release: `flutter build ipa --release` — verify no build errors - [ ] **7.3.8** Build iOS release: `flutter build ipa --release` — verify no build errors
@@ -338,4 +338,4 @@
--- ---
*TASKLIST v1.0 — Jamshalat Diary — March 2026* *TASKLIST v1.0 — JamShalat — March 2026*

View File

@@ -24,7 +24,7 @@ val hasReleaseKeystore = listOf(
} }
android { android {
namespace = "com.jamshalat.diary" namespace = "com.jamshalat.app"
compileSdk = flutter.compileSdkVersion compileSdk = flutter.compileSdkVersion
ndkVersion = flutter.ndkVersion ndkVersion = flutter.ndkVersion
@@ -52,7 +52,7 @@ android {
defaultConfig { defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId = "com.jamshalat.diary" applicationId = "com.jamshalat.app"
// You can update the following values to match your application needs. // You can update the following values to match your application needs.
// For more information, see: https://flutter.dev/to/review-gradle-config. // For more information, see: https://flutter.dev/to/review-gradle-config.
minSdk = flutter.minSdkVersion minSdk = flutter.minSdkVersion

View File

@@ -8,7 +8,7 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/> <uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"/> <uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PLAYBACK"/>
<application <application
android:label="Jamshalat Diary" android:label="JamShalat"
android:name="${applicationName}" android:name="${applicationName}"
android:icon="@mipmap/ic_launcher"> android:icon="@mipmap/ic_launcher">
<activity <activity

View File

@@ -0,0 +1,101 @@
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) {
if (call.method != DECLINATION_METHOD) {
result.notImplemented()
return
}
val latitude = call.argument<Double>("latitude")
val longitude = call.argument<Double>("longitude")
if (latitude == null || longitude == null) {
result.error("INVALID_ARGS", "Latitude and longitude are required.", null)
return
}
val altitude = call.argument<Double>("altitude") ?: 0.0
val timestamp = call.argument<Long>("timestamp") ?: System.currentTimeMillis()
val field = GeomagneticField(
latitude.toFloat(),
longitude.toFloat(),
altitude.toFloat(),
timestamp,
)
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<String>("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<String>("mimeType") ?: "image/png"
val chooserTitle = call.argument<String>("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)
}
}
}

View File

@@ -1,44 +0,0 @@
package com.jamshalat.diary
import android.hardware.GeomagneticField
import com.ryanheise.audioservice.AudioServiceActivity
import io.flutter.embedding.engine.FlutterEngine
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
class MainActivity : AudioServiceActivity() {
companion object {
private const val GEOMAGNETIC_CHANNEL = "com.jamshalat.diary/geomagnetic"
private const val DECLINATION_METHOD = "getDeclination"
}
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, GEOMAGNETIC_CHANNEL)
.setMethodCallHandler(::handleGeomagneticMethodCall)
}
private fun handleGeomagneticMethodCall(call: MethodCall, result: MethodChannel.Result) {
if (call.method != DECLINATION_METHOD) {
result.notImplemented()
return
}
val latitude = call.argument<Double>("latitude")
val longitude = call.argument<Double>("longitude")
if (latitude == null || longitude == null) {
result.error("INVALID_ARGS", "Latitude and longitude are required.", null)
return
}
val altitude = call.argument<Double>("altitude") ?: 0.0
val timestamp = call.argument<Long>("timestamp") ?: System.currentTimeMillis()
val field = GeomagneticField(
latitude.toFloat(),
longitude.toFloat(),
altitude.toFloat(),
timestamp,
)
result.success(field.declination.toDouble())
}
}

View File

@@ -1,5 +0,0 @@
package com.jamshalat.jamshalat_diary
import io.flutter.embedding.android.FlutterActivity
class MainActivity : FlutterActivity()

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,412 @@
# JamShalat: App Feature Overview
## What this app does
JamShalat is a Muslim daily worship companion. It combines prayer schedule utilities, Qur'an reading, dzikir, Islamic reference content, worship tracking, reminders, and personal settings in one app.
The app is designed to help users:
- know the current and upcoming prayer times
- keep worship routines more consistent
- continue Qur'an reading from the last ayat
- access daily dzikir, doa, hadits, and Qur'an study tools
- receive adzan, iqamah, and habit reminders
- review weekly, monthly, and yearly worship progress
## Main navigation modes
The app supports 2 navigation modes:
### 1. Mode lengkap
Bottom navigation:
- Beranda
- Jadwal
- Checklist
- Laporan
- Alat Islami
This mode is focused on full habit tracking and reporting.
### 2. Mode simpel
Bottom navigation:
- Beranda
- Jadwal
- Al-Qur'an
- Dzikir
- Lainnya
This mode is focused on faster access to the most-used spiritual features with less UI complexity.
## Core screens and features
## Beranda
Beranda is the main daily dashboard.
Features:
- main hero card with the next prayer time
- live countdown to the next prayer
- city/location label
- quick access button to Arah Kiblat
- sound toggle for adzan reminders
- horizontal prayer schedule cards for today or tomorrow
- automatic highlight for the next relevant prayer card
- `Lanjutkan Tilawah` card when the user has a last-read Qur'an bookmark
- `Poin Ibadah Hari Ini` summary card
- weekly points progress chart
- `Ayat Hari Ini` card
`Ayat Hari Ini` supports:
- deterministic daily ayat
- randomize to another ayat
- restore back to the daily ayat
- share as text
- copy text
- share as generated image card
## Kalender Sholat / Jadwal
This screen is the prayer calendar and city-based schedule browser.
Features:
- current city prayer schedule
- date-aware prayer timing
- support for changing city/kabupaten
- schedule browsing for upcoming days
- layout optimized for simple mode and full mode
## Checklist Ibadah
This feature is available in mode lengkap.
It acts as the daily worship tracker.
Tracked areas:
- 5 daily sholat fardhu
- rawatib prayers
- tilawah target progress
- dzikir pagi and petang
- puasa sunnah
Features:
- daily completion progress
- configurable rawatib level
- configurable tilawah target
- optional additional worship tracking
- automatic recalculation of total points and completion percentage
- integration with tilawah and dzikir flows
## Laporan / Riwayat Ibadah
This screen summarizes worship quality and history.
Mode lengkap:
- weekly tab
- monthly tab
- yearly tab
- weekly average summary
- daily point bars
- per-day point labels
- insights for strongest and weakest worship consistency
Mode simpel:
- simplified worship history presentation
## Al-Qur'an
This module contains the Qur'an reading experience.
Features:
- list of all surah
- surah metadata
- configurable display:
- Arabic only
- Arabic + Latin
- Arabic + translation
- adaptive Arabic font size support
- direct access to bookmarks
- direct access to enrichment tools
## Qur'an reading screen
This is the detailed surah reading screen.
Features:
- verse-by-verse reading
- Bismillah shown as part of scroll flow instead of fixed header
- accurate jump to a target ayat
- jump support for:
- last read
- favorites
- continue reading
- per-ayat actions:
- play verse audio
- mark as last read
- add to favorites
- open tafsir drawer
- display settings drawer
- quick Arabic font size adjustment
- optional Latin and translation display
- tilawah session tracking
- sync to checklist in mode lengkap
## Qur'an bookmarks
This screen stores reading continuity and personal saved ayat.
Features:
- `Terakhir Dibaca`
- `Ayat Favorit`
- open exact surah and ayat
- cached ayat content refresh when needed
- continue reading CTA
## Qur'an Murattal
This is the full-surah audio recitation player.
Features:
- play murattal by surah
- qari selection
- autoplay support
- previous/next/shuffle controls
- gold-decorated main play button
- blurred themed player panel
- background artwork
- shortcut back to the same surah in reading mode
- background playback through shared app-wide audio player
## Qur'an Enrichment
This screen provides deeper study tools.
Tabs:
- Cari
- Tafsir
- Juz
- Halaman
- Tema
- Asmaul Husna
Features:
- search ayat
- tafsir per surah
- word-by-word expansion
- browse by juz
- browse by page
- browse by topic/theme
- Asmaul Husna reference
- Arabic font-size settings in screen
## Dzikir Harian
This screen organizes daily dzikir routines.
Tabs:
- Sesudah Sholat
- Pagi
- Petang
- Harian
Features:
- card mode and focus/slide mode
- pembuka slide support for pagi and petang
- per-item recitation counter
- source structure aligned to Rumaysho reference
- daily dzikir separated from morning/evening flow where appropriate
- automatic sync between linked dzikir counters
- prayer-window reset for `Sesudah Sholat`
- day-based reset for `Pagi`, `Petang`, and `Harian`
- configurable Arabic font size in-screen
- dedicated simple-mode behavior
## Arah Kiblat
This is the compass-based qibla finder.
Features:
- live compass tracking
- location-based qibla direction calculation
- device support checks
- permission/state handling
- themed background with subtle Unsplash imagery
- Kaaba-centered compass UI
- visual indication when the device is aligned correctly to the qibla direction
## Kumpulan Doa
Reference screen for Islamic prayers.
Features:
- searchable list of doa
- Arabic text
- translation and supporting text
- refresh action
- Arabic font size setting in-screen
## Hadits Arba'in
Reference screen for the 40 hadith collection.
Features:
- searchable hadith list
- Arabic text
- translation/explanation text
- refresh action
- Arabic font size setting in-screen
## Alat Islami / Lainnya
This acts as the tool hub.
Contains shortcuts to:
- Al-Qur'an Terjemahan
- Qur'an Murattal
- Arah Kiblat
- Dzikir Harian
- Kumpulan Doa
- Hadits Arba'in
- Pendalaman Al-Qur'an
It also contains:
- `Ayat Hari Ini`
- share actions for the ayat
- randomize another ayat
## Notifications
The app has a dedicated notification center.
Tabs:
- Alarm
- Pesan
Alarm tab features:
- list of scheduled adzan notifications
- list of scheduled iqamah reminders
- `Akan Datang` filter
- `Sudah Lewat` filter
- refresh action
Pesan tab features:
- inbox-style message list
- read/unread state
- mark all as read
- system and non-prayer message support
## Adzan and reminders
The app schedules local notifications based on the selected city and prayer times.
Supported reminders:
- adzan
- iqamah
- checklist reminder
- other non-prayer habit/system reminders
Implementation highlights:
- local timezone-aware scheduling
- exact alarm support on Android where available
- permission handling for notification and exact alarm access
- per-prayer enable/disable behavior
## Settings and personalization
The app includes a broad settings screen.
Sections include:
- Preferensi
- Pemberitahuan
- Checklist Ibadah
- Tampilan Dzikir
- Waktu Sholat
- Tampilan
- Tentang
Key settings:
- mode lengkap / mode simpel
- light mode / dark mode
- city selection
- prayer calculation method
- iqamah offset per prayer
- adzan notification toggle
- global alert toggle
- inbox/message toggle
- quiet hours
- maximum non-prayer pushes per day
- checklist reminder time
- rawatib level
- tilawah target
- dzikir and puasa tracking toggles
- Arabic font size
- profile editing
- data reset
## Visual and UX features
The app also includes a number of presentation-focused improvements:
- custom bottom navigation with premium gold active state
- hidden theme toggle revealed by sliding the bottom bar
- HugeIcons-based icon system
- dark and light mode support
- adaptive Arabic typography
- reusable share card generation for ayat
- route-aware bottom bar visibility in focus-heavy screens
## Data and persistence
The app stores user state locally for continuity.
Persisted examples:
- settings
- worship logs
- dzikir progress
- tilawah progress
- Qur'an last read
- Qur'an favorites
- notification inbox state
## In short
JamShalat is not only a prayer-time app. It is a daily Islamic companion that combines:
- prayer schedule
- adzan and iqamah reminders
- qibla finder
- Qur'an reading and murattal
- dzikir, doa, and hadith references
- worship checklist and reporting
- personal spiritual continuity through bookmarks, reminders, and progress tracking

View File

@@ -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

View File

@@ -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.

View File

@@ -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.

View File

@@ -2,7 +2,7 @@
Last updated: 2026-03-16 Last updated: 2026-03-16
Owner: Product + Mobile Owner: Product + Mobile
Scope: `jamshalat_diary` (Flutter) Scope: `JamShalat` (Flutter)
## Implementation Status (2026-03-17) ## Implementation Status (2026-03-17)
- Phase 1: Implemented. - Phase 1: Implemented.

View File

@@ -0,0 +1,109 @@
<h1>Kebijakan Privasi JamShalat</h1>
<p>Terakhir diperbarui: 19 Maret 2026</p>
<p>JamShalat menghormati privasi pengguna. Kebijakan ini menjelaskan data apa saja yang digunakan oleh aplikasi, bagaimana data tersebut diproses, dan untuk tujuan apa data dipakai.</p>
<p>Dengan menggunakan JamShalat, Anda menyetujui praktik yang dijelaskan dalam kebijakan ini.</p>
<h2>1. Informasi yang digunakan aplikasi</h2>
<p>JamShalat dapat menggunakan informasi berikut:</p>
<ul>
<li>lokasi perangkat</li>
<li>data ibadah yang disimpan di perangkat</li>
<li>pengaturan aplikasi</li>
<li>data notifikasi</li>
<li>data konten yang diambil dari layanan pihak ketiga</li>
</ul>
<h2>2. Lokasi perangkat</h2>
<p>JamShalat dapat menggunakan lokasi perangkat untuk:</p>
<ul>
<li>menampilkan arah kiblat</li>
<li>menghitung atau menampilkan jadwal shalat</li>
<li>membantu menyesuaikan informasi ibadah berdasarkan wilayah pengguna</li>
</ul>
<p>Lokasi dipakai hanya untuk fitur yang membutuhkan lokasi. Jika izin lokasi tidak diberikan, fitur yang bergantung pada lokasi mungkin tidak bekerja penuh.</p>
<h2>3. Penyimpanan data</h2>
<p>Sebagian besar data pengguna disimpan secara lokal di perangkat, termasuk:</p>
<ul>
<li>progres checklist ibadah</li>
<li>marka bacaan Al-Qur'an</li>
<li>dzikir counter</li>
<li>pengaturan tampilan</li>
<li>preferensi mode aplikasi</li>
</ul>
<p>Data lokal ini digunakan untuk menjaga pengalaman pengguna tetap konsisten di perangkat yang sama.</p>
<h2>4. Notifikasi</h2>
<p>JamShalat dapat menggunakan notifikasi untuk:</p>
<ul>
<li>pengingat adzan</li>
<li>pengingat iqamah</li>
<li>pengingat ibadah dan dzikir</li>
<li>notifikasi ringkasan atau pengingat lain yang relevan</li>
</ul>
<p>Notifikasi dipakai untuk membantu pengguna menjaga rutinitas ibadah.</p>
<h2>5. Konten dari layanan pihak ketiga</h2>
<p>JamShalat dapat mengambil data dari layanan pihak ketiga untuk menyediakan:</p>
<ul>
<li>jadwal shalat</li>
<li>konten Al-Qur'an</li>
<li>tafsir</li>
<li>dzikir</li>
<li>doa</li>
<li>hadits</li>
<li>gambar latar atau konten visual tertentu</li>
</ul>
<p>Layanan pihak ketiga yang dapat digunakan antara lain API konten dan API gambar.</p>
<p>JamShalat tidak menjual data pribadi pengguna.</p>
<h2>6. Data yang tidak dikumpulkan</h2>
<p>JamShalat tidak mengharuskan pengguna membuat akun untuk memakai fitur utama.</p>
<p>JamShalat tidak secara khusus meminta:</p>
<ul>
<li>nama lengkap</li>
<li>alamat email</li>
<li>nomor telepon</li>
<li>data kartu kredit</li>
<li>kontak pribadi</li>
</ul>
<p>Jika suatu saat ada fitur tambahan yang membutuhkan data lain, kebijakan ini akan diperbarui.</p>
<h2>7. Penggunaan data</h2>
<p>Data yang digunakan JamShalat dipakai untuk:</p>
<ul>
<li>menjalankan fitur aplikasi</li>
<li>menyimpan progres ibadah</li>
<li>menampilkan jadwal, arah kiblat, dan konten ibadah</li>
<li>mengirim pengingat yang dipilih pengguna</li>
<li>menjaga pengalaman pengguna tetap nyaman dan konsisten</li>
</ul>
<h2>8. Pembagian data</h2>
<p>JamShalat tidak membagikan data pribadi pengguna kepada pihak lain untuk tujuan iklan atau penjualan.</p>
<p>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.</p>
<h2>9. Penyimpanan dan retensi</h2>
<p>Data lokal disimpan di perangkat pengguna selama aplikasi masih digunakan atau sampai pengguna menghapus data aplikasi.</p>
<p>Jika pengguna menghapus aplikasi atau membersihkan data aplikasi, data lokal yang tersimpan di perangkat dapat ikut terhapus.</p>
<h2>10. Keamanan</h2>
<p>JamShalat berusaha menggunakan cara yang wajar untuk menjaga data pengguna. Namun, tidak ada metode penyimpanan atau transmisi data yang sepenuhnya bebas risiko.</p>
<h2>11. Hak pengguna</h2>
<p>Pengguna dapat:</p>
<ul>
<li>menolak izin lokasi atau notifikasi</li>
<li>menghapus data aplikasi dari perangkat</li>
<li>mencopot pemasangan aplikasi</li>
</ul>
<p>Jika izin tertentu ditolak, beberapa fitur mungkin tidak berfungsi penuh.</p>
<h2>12. Perubahan kebijakan</h2>
<p>Kebijakan privasi ini dapat diperbarui dari waktu ke waktu. Perubahan terbaru akan ditampilkan di halaman ini.</p>
<h2>13. Kontak</h2>
<p>Jika ada pertanyaan tentang kebijakan privasi ini, silakan hubungi pengembang aplikasi melalui kontak resmi yang disediakan di Play Store atau situs aplikasi.</p>

129
docs/privacy-policy-id.md Normal file
View File

@@ -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.

View File

@@ -1,4 +1,4 @@
# Jamshalat Diary — Handoff Document # JamShalat — Handoff Document
> Last updated: 2026-03-15 > Last updated: 2026-03-15

View File

@@ -372,7 +372,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.jamshalat.jamshalatDiary; PRODUCT_BUNDLE_IDENTIFIER = com.jamshalat.app;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
@@ -388,7 +388,7 @@
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.jamshalat.jamshalatDiary.RunnerTests; PRODUCT_BUNDLE_IDENTIFIER = com.jamshalat.app.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -405,7 +405,7 @@
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.jamshalat.jamshalatDiary.RunnerTests; PRODUCT_BUNDLE_IDENTIFIER = com.jamshalat.app.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
@@ -420,7 +420,7 @@
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.jamshalat.jamshalatDiary.RunnerTests; PRODUCT_BUNDLE_IDENTIFIER = com.jamshalat.app.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner";
@@ -551,7 +551,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.jamshalat.jamshalatDiary; PRODUCT_BUNDLE_IDENTIFIER = com.jamshalat.app;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -573,7 +573,7 @@
"$(inherited)", "$(inherited)",
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
PRODUCT_BUNDLE_IDENTIFIER = com.jamshalat.jamshalatDiary; PRODUCT_BUNDLE_IDENTIFIER = com.jamshalat.app;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 295 B

After

Width:  |  Height:  |  Size: 949 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 B

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 450 B

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 282 B

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 462 B

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 704 B

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 406 B

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 586 B

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 862 B

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 862 B

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 762 B

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

@@ -7,7 +7,7 @@
<key>CFBundleDevelopmentRegion</key> <key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string> <string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key> <key>CFBundleDisplayName</key>
<string>Jamshalat Diary</string> <string>JamShalat</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string> <string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
@@ -15,7 +15,7 @@
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string> <string>6.0</string>
<key>CFBundleName</key> <key>CFBundleName</key>
<string>jamshalat_diary</string> <string>JamShalat</string>
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>

View File

@@ -78,7 +78,7 @@ class _AppState extends ConsumerState<App> with WidgetsBindingObserver {
final themeMode = ref.watch(themeProvider); final themeMode = ref.watch(themeProvider);
return MaterialApp.router( return MaterialApp.router(
title: 'Jamshalat Diary', title: 'JamShalat',
debugShowCheckedModeBanner: false, debugShowCheckedModeBanner: false,
theme: AppTheme.light, theme: AppTheme.light,
darkTheme: AppTheme.dark, darkTheme: AppTheme.dark,

View File

@@ -5,12 +5,17 @@ import 'package:flutter/material.dart';
import 'package:flutter/rendering.dart'; import 'package:flutter/rendering.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:lucide_icons/lucide_icons.dart'; import 'package:lucide_icons/lucide_icons.dart';
import 'package:path_provider/path_provider.dart';
import 'package:share_plus/share_plus.dart'; import 'package:share_plus/share_plus.dart';
import '../../app/icons/app_icons.dart'; import '../../app/icons/app_icons.dart';
import '../../app/theme/app_colors.dart'; import '../../app/theme/app_colors.dart';
import '../../core/widgets/arabic_text.dart'; import '../../core/widgets/arabic_text.dart';
const MethodChannel _androidShareChannel = MethodChannel(
'com.jamshalat.app/share',
);
String buildAyatShareText(Map<String, dynamic> ayat) { String buildAyatShareText(Map<String, dynamic> ayat) {
final arabic = (ayat['teksArab'] ?? '').toString().trim(); final arabic = (ayat['teksArab'] ?? '').toString().trim();
final translation = (ayat['teksIndonesia'] ?? '').toString().trim(); final translation = (ayat['teksIndonesia'] ?? '').toString().trim();
@@ -24,7 +29,7 @@ String buildAyatShareText(Map<String, dynamic> ayat) {
if (arabic.isNotEmpty) arabic, if (arabic.isNotEmpty) arabic,
if (translation.isNotEmpty) '"$translation"', if (translation.isNotEmpty) '"$translation"',
reference, reference,
'Dibagikan dari Jam Shalat Diary', 'Dibagikan dari JamShalat',
]; ];
return parts.join('\n\n'); return parts.join('\n\n');
@@ -50,11 +55,7 @@ Future<void> showAyatShareSheet(
try { try {
final pngBytes = await _captureAyatShareCardPng(context, ayat); final pngBytes = await _captureAyatShareCardPng(context, ayat);
final file = await _writeAyatShareImage(pngBytes); final file = await _writeAyatShareImage(pngBytes);
await Share.shareXFiles( await _shareAyatImage(file);
[XFile(file.path)],
text: 'Ayat Hari Ini',
subject: 'Ayat Hari Ini',
);
} catch (_) { } catch (_) {
if (!context.mounted) return; if (!context.mounted) return;
ScaffoldMessenger.of(context) ScaffoldMessenger.of(context)
@@ -224,12 +225,40 @@ Future<Uint8List> _captureAyatShareCardPng(
} }
Future<File> _writeAyatShareImage(Uint8List pngBytes) async { Future<File> _writeAyatShareImage(Uint8List pngBytes) async {
final directory = await Directory.systemTemp.createTemp('jamshalat_ayat_'); final tempDirectory = await getTemporaryDirectory();
final file = File('${directory.path}/ayat_hari_ini.png'); 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); await file.writeAsBytes(pngBytes, flush: true);
return file; return file;
} }
Future<void> _shareAyatImage(File file) async {
if (Platform.isAndroid) {
await _androidShareChannel.invokeMethod<void>('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 { class _AyatShareCard extends StatelessWidget {
const _AyatShareCard({ const _AyatShareCard({
required this.ayat, required this.ayat,
@@ -447,7 +476,7 @@ class _AyatShareCard extends StatelessWidget {
borderRadius: BorderRadius.circular(999), borderRadius: BorderRadius.circular(999),
), ),
child: const Text( child: const Text(
'Jam Shalat Diary', 'JamShalat',
style: TextStyle( style: TextStyle(
fontSize: 12, fontSize: 12,
fontWeight: FontWeight.w700, fontWeight: FontWeight.w700,

View File

@@ -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,
);
}

View File

@@ -3,6 +3,7 @@ import 'package:hive_flutter/hive_flutter.dart';
import 'package:lucide_icons/lucide_icons.dart'; import 'package:lucide_icons/lucide_icons.dart';
import '../../../app/theme/app_colors.dart'; import '../../../app/theme/app_colors.dart';
import '../../../core/widgets/arabic_text.dart'; import '../../../core/widgets/arabic_text.dart';
import '../../../core/widgets/bottom_sheet_content_padding.dart';
import '../../../data/local/hive_boxes.dart'; import '../../../data/local/hive_boxes.dart';
import '../../../data/local/models/app_settings.dart'; import '../../../data/local/models/app_settings.dart';
import '../../../data/services/muslim_api_service.dart'; import '../../../data/services/muslim_api_service.dart';
@@ -91,9 +92,8 @@ class _DoaScreenState extends State<DoaScreen> {
), ),
builder: (ctx) => StatefulBuilder( builder: (ctx) => StatefulBuilder(
builder: (context, setModalState) { builder: (context, setModalState) {
final keyboardInset = MediaQuery.of(context).viewInsets.bottom;
return Padding( return Padding(
padding: EdgeInsets.fromLTRB(16, 20, 16, 20 + keyboardInset), padding: bottomSheetContentPadding(context),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,

View File

@@ -10,6 +10,7 @@ import 'package:lucide_icons/lucide_icons.dart';
import '../../../app/theme/app_colors.dart'; import '../../../app/theme/app_colors.dart';
import '../../../core/widgets/arabic_text.dart'; import '../../../core/widgets/arabic_text.dart';
import '../../../core/widgets/bottom_sheet_content_padding.dart';
import '../../../data/local/hive_boxes.dart'; import '../../../data/local/hive_boxes.dart';
import '../../../data/local/models/app_settings.dart'; import '../../../data/local/models/app_settings.dart';
import '../../../data/local/models/daily_worship_log.dart'; import '../../../data/local/models/daily_worship_log.dart';
@@ -642,9 +643,8 @@ class _DzikirScreenState extends ConsumerState<DzikirScreen>
), ),
builder: (ctx) => StatefulBuilder( builder: (ctx) => StatefulBuilder(
builder: (context, setModalState) { builder: (context, setModalState) {
final keyboardInset = MediaQuery.of(context).viewInsets.bottom;
return Padding( return Padding(
padding: EdgeInsets.fromLTRB(16, 20, 16, 20 + keyboardInset), padding: bottomSheetContentPadding(context),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,

View File

@@ -3,6 +3,7 @@ import 'package:hive_flutter/hive_flutter.dart';
import 'package:lucide_icons/lucide_icons.dart'; import 'package:lucide_icons/lucide_icons.dart';
import '../../../app/theme/app_colors.dart'; import '../../../app/theme/app_colors.dart';
import '../../../core/widgets/arabic_text.dart'; import '../../../core/widgets/arabic_text.dart';
import '../../../core/widgets/bottom_sheet_content_padding.dart';
import '../../../data/local/hive_boxes.dart'; import '../../../data/local/hive_boxes.dart';
import '../../../data/local/models/app_settings.dart'; import '../../../data/local/models/app_settings.dart';
import '../../../data/services/muslim_api_service.dart'; import '../../../data/services/muslim_api_service.dart';
@@ -96,9 +97,8 @@ class _HaditsScreenState extends State<HaditsScreen> {
), ),
builder: (ctx) => StatefulBuilder( builder: (ctx) => StatefulBuilder(
builder: (context, setModalState) { builder: (context, setModalState) {
final keyboardInset = MediaQuery.of(context).viewInsets.bottom;
return Padding( return Padding(
padding: EdgeInsets.fromLTRB(16, 20, 16, 20 + keyboardInset), padding: bottomSheetContentPadding(context),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,

View File

@@ -20,7 +20,7 @@ class QiblaScreen extends ConsumerStatefulWidget {
class _QiblaScreenState extends ConsumerState<QiblaScreen> { class _QiblaScreenState extends ConsumerState<QiblaScreen> {
static const _geomagneticChannel = static const _geomagneticChannel =
MethodChannel('com.jamshalat.diary/geomagnetic'); MethodChannel('com.jamshalat.app/geomagnetic');
static const double _alignmentThreshold = 3.0; static const double _alignmentThreshold = 3.0;
// Fallback simulated data for environments without compass hardware (like macOS emulator) // Fallback simulated data for environments without compass hardware (like macOS emulator)

View File

@@ -5,6 +5,7 @@ import 'package:go_router/go_router.dart';
import 'package:intl/intl.dart'; import 'package:intl/intl.dart';
import '../../../app/theme/app_colors.dart'; import '../../../app/theme/app_colors.dart';
import '../../../core/widgets/arabic_text.dart'; import '../../../core/widgets/arabic_text.dart';
import '../../../core/widgets/bottom_sheet_content_padding.dart';
import '../../../data/local/hive_boxes.dart'; import '../../../data/local/hive_boxes.dart';
import '../../../data/local/models/quran_bookmark.dart'; import '../../../data/local/models/quran_bookmark.dart';
import '../../../data/local/models/app_settings.dart'; import '../../../data/local/models/app_settings.dart';
@@ -25,7 +26,8 @@ class _QuranBookmarksScreenState extends State<QuranBookmarksScreen> {
bool _showLatin = true; bool _showLatin = true;
bool _showTerjemahan = true; bool _showTerjemahan = true;
final Map<int, Future<Map<String, dynamic>?>> _surahFutureCache = {}; final Map<int, Future<Map<String, dynamic>?>> _surahFutureCache = {};
final Map<dynamic, Future<_ResolvedBookmarkContent?>> _bookmarkFutureCache = {}; final Map<dynamic, Future<_ResolvedBookmarkContent?>> _bookmarkFutureCache =
{};
String _readingRoute(int surahId, int verseId) { String _readingRoute(int surahId, int verseId) {
final isSimple = final isSimple =
@@ -54,9 +56,8 @@ class _QuranBookmarksScreenState extends State<QuranBookmarksScreen> {
), ),
builder: (ctx) => StatefulBuilder( builder: (ctx) => StatefulBuilder(
builder: (context, setModalState) { builder: (context, setModalState) {
final keyboardInset = MediaQuery.of(context).viewInsets.bottom;
return Padding( return Padding(
padding: EdgeInsets.fromLTRB(16, 20, 16, 20 + keyboardInset), padding: bottomSheetContentPadding(context),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
@@ -147,7 +148,8 @@ class _QuranBookmarksScreenState extends State<QuranBookmarksScreen> {
Future<_ResolvedBookmarkContent?> _getResolvedBookmarkContent( Future<_ResolvedBookmarkContent?> _getResolvedBookmarkContent(
QuranBookmark bookmark, QuranBookmark bookmark,
) { ) {
final bookmarkKey = bookmark.key ?? '${bookmark.surahId}_${bookmark.verseId}'; final bookmarkKey =
bookmark.key ?? '${bookmark.surahId}_${bookmark.verseId}';
return _bookmarkFutureCache.putIfAbsent( return _bookmarkFutureCache.putIfAbsent(
bookmarkKey, bookmarkKey,
() => _loadResolvedBookmarkContent(bookmark), () => _loadResolvedBookmarkContent(bookmark),

View File

@@ -3,6 +3,7 @@ import 'package:hive_flutter/hive_flutter.dart';
import 'package:lucide_icons/lucide_icons.dart'; import 'package:lucide_icons/lucide_icons.dart';
import '../../../app/theme/app_colors.dart'; import '../../../app/theme/app_colors.dart';
import '../../../core/widgets/arabic_text.dart'; import '../../../core/widgets/arabic_text.dart';
import '../../../core/widgets/bottom_sheet_content_padding.dart';
import '../../../data/local/hive_boxes.dart'; import '../../../data/local/hive_boxes.dart';
import '../../../data/local/models/app_settings.dart'; import '../../../data/local/models/app_settings.dart';
import '../../../data/services/muslim_api_service.dart'; import '../../../data/services/muslim_api_service.dart';
@@ -193,9 +194,8 @@ class _QuranEnrichmentScreenState extends State<QuranEnrichmentScreen>
), ),
builder: (ctx) => StatefulBuilder( builder: (ctx) => StatefulBuilder(
builder: (context, setModalState) { builder: (context, setModalState) {
final keyboardInset = MediaQuery.of(context).viewInsets.bottom;
return Padding( return Padding(
padding: EdgeInsets.fromLTRB(16, 20, 16, 20 + keyboardInset), padding: bottomSheetContentPadding(context),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,

View File

@@ -919,7 +919,7 @@ class _QuranMurattalScreenState extends ConsumerState<QuranMurattalScreen>
final url = _unsplashPhoto!['photographerUrl']; final url = _unsplashPhoto!['photographerUrl'];
if (url != null && url.isNotEmpty) { if (url != null && url.isNotEmpty) {
launchUrl(Uri.parse( launchUrl(Uri.parse(
'$url?utm_source=jamshalat_diary&utm_medium=referral')); '$url?utm_source=jamshalat&utm_medium=referral'));
} }
}, },
child: Text( child: Text(

View File

@@ -10,6 +10,7 @@ import 'package:intl/intl.dart';
import '../../../app/theme/app_colors.dart'; import '../../../app/theme/app_colors.dart';
import '../../../core/services/app_audio_player.dart'; import '../../../core/services/app_audio_player.dart';
import '../../../core/widgets/arabic_text.dart'; import '../../../core/widgets/arabic_text.dart';
import '../../../core/widgets/bottom_sheet_content_padding.dart';
import '../../../data/local/hive_boxes.dart'; import '../../../data/local/hive_boxes.dart';
import '../../../data/local/models/quran_bookmark.dart'; import '../../../data/local/models/quran_bookmark.dart';
import '../../../data/local/models/app_settings.dart'; import '../../../data/local/models/app_settings.dart';
@@ -845,9 +846,8 @@ class _QuranReadingScreenState extends ConsumerState<QuranReadingScreen> {
), ),
builder: (ctx) => StatefulBuilder( builder: (ctx) => StatefulBuilder(
builder: (context, setModalState) { builder: (context, setModalState) {
final keyboardInset = MediaQuery.of(context).viewInsets.bottom;
return Padding( return Padding(
padding: EdgeInsets.fromLTRB(16, 20, 16, 20 + keyboardInset), padding: bottomSheetContentPadding(context),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,

View File

@@ -5,6 +5,7 @@ import 'package:lucide_icons/lucide_icons.dart';
import 'package:hive_flutter/hive_flutter.dart'; import 'package:hive_flutter/hive_flutter.dart';
import '../../../app/theme/app_colors.dart'; import '../../../app/theme/app_colors.dart';
import '../../../core/widgets/arabic_text.dart'; import '../../../core/widgets/arabic_text.dart';
import '../../../core/widgets/bottom_sheet_content_padding.dart';
import '../../../data/local/hive_boxes.dart'; import '../../../data/local/hive_boxes.dart';
import '../../../data/local/models/app_settings.dart'; import '../../../data/local/models/app_settings.dart';
import '../../../data/local/models/quran_bookmark.dart'; import '../../../data/local/models/quran_bookmark.dart';
@@ -55,9 +56,8 @@ class _QuranScreenState extends ConsumerState<QuranScreen> {
), ),
builder: (ctx) => StatefulBuilder( builder: (ctx) => StatefulBuilder(
builder: (context, setModalState) { builder: (context, setModalState) {
final keyboardInset = MediaQuery.of(context).viewInsets.bottom;
return Padding( return Padding(
padding: EdgeInsets.fromLTRB(16, 20, 16, 20 + keyboardInset), padding: bottomSheetContentPadding(context),
child: Column( child: Column(
mainAxisSize: MainAxisSize.min, mainAxisSize: MainAxisSize.min,
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,

View File

@@ -47,7 +47,7 @@ void main() async {
)); ));
await JustAudioBackground.init( await JustAudioBackground.init(
androidNotificationChannelId: 'com.jamshalat.diary.audio', androidNotificationChannelId: 'com.jamshalat.app.audio',
androidNotificationChannelName: 'Murattal Playback', androidNotificationChannelName: 'Murattal Playback',
androidNotificationOngoing: true, androidNotificationOngoing: true,
); );

View File

@@ -479,7 +479,7 @@
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.jamshalat.jamshalatDiary.RunnerTests; PRODUCT_BUNDLE_IDENTIFIER = com.jamshalat.app.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/jamshalat_diary.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/jamshalat_diary"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/jamshalat_diary.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/jamshalat_diary";
@@ -494,7 +494,7 @@
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.jamshalat.jamshalatDiary.RunnerTests; PRODUCT_BUNDLE_IDENTIFIER = com.jamshalat.app.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/jamshalat_diary.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/jamshalat_diary"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/jamshalat_diary.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/jamshalat_diary";
@@ -509,7 +509,7 @@
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.jamshalat.jamshalatDiary.RunnerTests; PRODUCT_BUNDLE_IDENTIFIER = com.jamshalat.app.RunnerTests;
PRODUCT_NAME = "$(TARGET_NAME)"; PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0; SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/jamshalat_diary.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/jamshalat_diary"; TEST_HOST = "$(BUILT_PRODUCTS_DIR)/jamshalat_diary.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/jamshalat_diary";

View File

@@ -8,7 +8,7 @@
PRODUCT_NAME = jamshalat_diary PRODUCT_NAME = jamshalat_diary
// The application's bundle identifier // The application's bundle identifier
PRODUCT_BUNDLE_IDENTIFIER = com.jamshalat.jamshalatDiary PRODUCT_BUNDLE_IDENTIFIER = com.jamshalat.app
// The copyright displayed in application information // The copyright displayed in application information
PRODUCT_COPYRIGHT = Copyright © 2026 com.jamshalat. All rights reserved. PRODUCT_COPYRIGHT = Copyright © 2026 com.jamshalat. All rights reserved.

View File

@@ -6,6 +6,8 @@
<string>$(DEVELOPMENT_LANGUAGE)</string> <string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleExecutable</key> <key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string> <string>$(EXECUTABLE_NAME)</string>
<key>CFBundleDisplayName</key>
<string>JamShalat</string>
<key>CFBundleIconFile</key> <key>CFBundleIconFile</key>
<string></string> <string></string>
<key>CFBundleIdentifier</key> <key>CFBundleIdentifier</key>
@@ -13,7 +15,7 @@
<key>CFBundleInfoDictionaryVersion</key> <key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string> <string>6.0</string>
<key>CFBundleName</key> <key>CFBundleName</key>
<string>$(PRODUCT_NAME)</string> <string>JamShalat</string>
<key>CFBundlePackageType</key> <key>CFBundlePackageType</key>
<string>APPL</string> <string>APPL</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>

View File

@@ -870,7 +870,7 @@ packages:
source: hosted source: hosted
version: "1.1.0" version: "1.1.0"
path_provider: path_provider:
dependency: transitive dependency: "direct main"
description: description:
name: path_provider name: path_provider
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd" sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"

View File

@@ -49,6 +49,7 @@ dependencies:
flutter_dotenv: ^5.1.0 flutter_dotenv: ^5.1.0
cached_network_image: ^3.3.1 cached_network_image: ^3.3.1
share_plus: ^10.1.4 share_plus: ^10.1.4
path_provider: ^2.1.5
url_launcher: ^6.2.5 url_launcher: ^6.2.5
lucide_icons: ^0.257.0 lucide_icons: ^0.257.0
hugeicons: ^1.1.5 hugeicons: ^1.1.5

View File

@@ -18,18 +18,18 @@
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta content="IE=Edge" http-equiv="X-UA-Compatible"> <meta content="IE=Edge" http-equiv="X-UA-Compatible">
<meta name="description" content="A new Flutter project."> <meta name="description" content="JamShalat Muslim daily worship companion.">
<!-- iOS meta tags & icons --> <!-- iOS meta tags & icons -->
<meta name="mobile-web-app-capable" content="yes"> <meta name="mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black"> <meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-title" content="jamshalat_diary"> <meta name="apple-mobile-web-app-title" content="JamShalat">
<link rel="apple-touch-icon" href="icons/Icon-192.png"> <link rel="apple-touch-icon" href="icons/Icon-192.png">
<!-- Favicon --> <!-- Favicon -->
<link rel="icon" type="image/png" href="favicon.png"/> <link rel="icon" type="image/png" href="favicon.png"/>
<title>jamshalat_diary</title> <title>JamShalat</title>
<link rel="manifest" href="manifest.json"> <link rel="manifest" href="manifest.json">
</head> </head>
<body> <body>

View File

@@ -1,11 +1,11 @@
{ {
"name": "jamshalat_diary", "name": "JamShalat",
"short_name": "jamshalat_diary", "short_name": "JamShalat",
"start_url": ".", "start_url": ".",
"display": "standalone", "display": "standalone",
"background_color": "#0175C2", "background_color": "#0175C2",
"theme_color": "#0175C2", "theme_color": "#0175C2",
"description": "A new Flutter project.", "description": "JamShalat Muslim daily worship companion.",
"orientation": "portrait-primary", "orientation": "portrait-primary",
"prefer_related_applications": false, "prefer_related_applications": false,
"icons": [ "icons": [