Skip to content

Commit

Permalink
Merge pull request #9 from typ-AhmedSleem/KhatmaFeature
Browse files Browse the repository at this point in the history
KhatmaFeature 1.0
  • Loading branch information
typ-AhmedSleem authored Mar 30, 2023
2 parents 4b04de2 + 9950382 commit def8d00
Show file tree
Hide file tree
Showing 52 changed files with 1,140 additions and 812 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -82,3 +82,4 @@ lint/generated/
lint/outputs/
lint/tmp/
# lint/reports/
AnaMuslim-ChatGPT ideas.txt
49 changes: 49 additions & 0 deletions AnaMuslim-ChatGPT ideas.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
An Islamic mobile application can have a variety of features, depending on its purpose and target audience. Here are some of the most common features that you may want to consider including in your Islamic mobile application:

Quran: The ability to read and listen to the Quran, with multiple translations and recitations.

Prayer Times: A feature that displays the five daily prayer times based on the user's location, along with alerts and reminders for each prayer.

Qibla Direction: A compass that helps users determine the direction of the Kaaba in Mecca for prayer.

Hadith: A collection of authentic sayings of Prophet Muhammad (PBUH), along with commentary and explanations.

Islamic Calendar: A calendar that displays Islamic holidays, important dates, and events.

Zakat Calculator: A tool that helps users calculate and keep track of their Zakat payments.

Islamic Videos: A collection of videos related to Islamic teachings, history, and culture.

Duas and Supplications: A collection of prayers and supplications that users can recite for different occasions and situations.

Community Features: A platform that allows users to connect with other Muslims, join groups, and share content.

Arabic Language Learning: A feature that teaches users how to read, write, and speak Arabic, which is the language of the Quran.

Halal Food Finder: A feature that helps users find Halal restaurants and food options in their area.

News and Updates: A section that provides users with the latest news and updates related to Islam and Muslim communities around the world.

These are just a few of the features that can be included in an Islamic mobile application. Ultimately, the most important thing is to ensure that your application provides value to its users and helps them in their spiritual and daily lives.

========================

The Khatma feature in an Islamic mobile application is typically used to help users participate in group recitations of the Quran, particularly during the holy month of Ramadan. Here are some of the most common features that you may want to include in a Khatma feature for an Islamic mobile app:

Join a Khatma: The ability for users to join an existing Khatma group or create their own group, and invite others to join.

Track Progress: A feature that allows users to track their progress and see how much of the Quran they have recited, along with reminders and alerts to complete their assigned portions on time.

Recitation Options: A feature that provides users with various recitation options, such as choosing a specific reciter, selecting a recitation style, and adjusting the playback speed.

Personalized Settings: A feature that allows users to personalize their Khatma experience, such as choosing the time of day for their recitations and selecting the portion of the Quran they want to recite.

Social Features: A platform that allows users to connect with other members of their Khatma group, share progress updates, and offer encouragement and support.

Leaderboards and Awards: A feature that rewards users for completing their Khatma, such as badges, certificates, and leaderboard rankings.

Community Support: A feature that provides users with access to community support and resources, such as guidance on proper recitation techniques, tips for staying focused during recitations, and advice for overcoming common challenges.

These are just a few of the features that can be included in a Khatma feature for an Islamic mobile application. Ultimately, the most important thing is to ensure that your Khatma feature is easy to use, engaging, and helps users deepen their spiritual practice and connection to the Quran.

========================
7 changes: 5 additions & 2 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,11 @@
android:name=".ui.names.HolyNameOfAllahDescActivity"
android:theme="@style/Theme.AnaMuslim" />

<activity
android:name=".ui.khatma.KhatmaPlannerActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:windowSoftInputMode="adjustResize" />

<activity
android:name=".ui.khatma.KhatmaActivity"
android:theme="@style/Theme.AnaMuslim" />
Expand All @@ -79,8 +84,6 @@

<activity android:name=".ui.setup.QuickSettingsActivity" />

<activity android:name=".ui.khatma.KhatmaPlannerActivity" />

</application>

</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ class KhatmaBuilder(
plan = this.plan,
reminder = this.reminder,
createdIn = Timestamp.NOW().toMillis(),
werdLength = Quran.QURAN_JUZ2S_COUNT / plan.duration
werdLength = Quran.QURAN_PARTS_COUNT / plan.duration
)

fun reset() {
Expand Down
39 changes: 26 additions & 13 deletions app/src/main/java/com/typ/muslim/features/khatma/KhatmaManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -16,39 +16,49 @@ class KhatmaManager private constructor(
val callback: KhatmaManagerCallback
) {

private var managing = false
val holdsKhatma: Boolean
get() = khatma != null

val holdsActiveKhatma: Boolean
get() = khatma?.isActive == true

init {
callback.onPrepareManager()
if (holdsKhatma) {
// NOTE: khatma will always be non-null in this scope
if (holdsActiveKhatma) callback.onManageKhatma(khatma!!)
else callback.onFinishKhatma()
} else callback.onHaveNoKhatma()
fun putKhatmaUnderManagement() {
if (!managing) {
callback.onPrepareManager()
if (holdsKhatma) {
// NOTE: khatma will always be non-null in this scope
if (holdsActiveKhatma) callback.onManageKhatma(khatma!!)
else callback.onFinishKhatma()
} else callback.onHaveNoKhatma()
managing = true
}
}

fun release() {
if (managing) managing = false
// todo: Call onReleaseKhatma
}

/**
* Deletes this khatma from DB
*
* @return `true` if deleted successfully. `false` if failed.
*/
fun delete() {
khatma?.let { LocalDatabase.getInstance(context)?.getKhatmaDao()?.deleteKhatma(it) } ?: callback.onHaveNoKhatma()
fun deleteKhatma() {
khatma?.let { if (deleteKhatma(context, it)) callback.onHaveNoKhatma() }
}

/**
* Mark the current werd as done, calculate next werd then refresh Khatma runtime
*/
fun saveProgress() {
khatma?.let {
it.saveProgress() // Save progress.
val nextWerd = it.nextWerd // Calculate next werd (if available).
if (nextWerd != null) callback.onProgressUpdated(nextWerd) // Notify UI about progress update.
else callback.onFinishKhatma() // Khatma has finished.
if (nextWerd != null) {
it.saveProgress() // Save progress.
if (updateKhatma(context, it)) callback.onProgressUpdated(nextWerd) // Notify UI about progress update.
} else callback.onFinishKhatma() // Khatma has finished.
}
}

Expand All @@ -61,7 +71,7 @@ class KhatmaManager private constructor(
* @param callback Callback to async send updates to UI.
*/
@JvmStatic
fun manageKhatma(
fun newInstance(
context: Context,
khatma: Khatma?,
callback: KhatmaManagerCallback
Expand Down Expand Up @@ -117,6 +127,9 @@ class KhatmaManager private constructor(
@JvmStatic
fun updateKhatma(context: Context, khatma: Khatma) = LocalDatabase.getInstance(context)?.getKhatmaDao()?.updateKhatma(khatma) != null

@JvmStatic
fun deleteKhatma(context: Context, khatma: Khatma): Boolean = LocalDatabase.getInstance(context)?.getKhatmaDao()?.deleteKhatma(khatma) != null

}

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.typ.muslim.features.khatma
package com.typ.muslim.features.khatma.data

import com.typ.muslim.features.khatma.models.KhatmaAchievement

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -39,4 +39,6 @@ interface KhatmaManagerCallback {

fun onFinishKhatma()

// fun onReleaseKhatma()

}
54 changes: 41 additions & 13 deletions app/src/main/java/com/typ/muslim/features/khatma/models/Khatma.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import androidx.room.Entity
import androidx.room.Ignore
import androidx.room.PrimaryKey
import androidx.room.TypeConverters
import com.typ.muslim.enums.FormatPatterns
import com.typ.muslim.features.khatma.utils.KhatmaConverters
import com.typ.muslim.features.quran.Quran
import com.typ.muslim.models.Timestamp
Expand Down Expand Up @@ -32,13 +33,19 @@ class Khatma(
@ColumnInfo()
var reminder: ReminderPlan?,

@ColumnInfo(name = "progress")
var completedWerds: Int = 0,
@ColumnInfo()
var progress: Int = 0,

@ColumnInfo(name = "step")
val werdLength: Int = Quran.QURAN_JUZ2S_COUNT / plan.duration
val werdLength: Int = Quran.QURAN_PARTS_COUNT / plan.duration
) : Serializable {

var completedParts: Int
get() = (progress * werdLength).coerceAtMost(Quran.QURAN_PARTS_COUNT)
set(value) {
progress = value
}

val startedIn: Timestamp
get() = Timestamp(createdIn)

Expand All @@ -62,8 +69,8 @@ class Khatma(
val currentWerd: KhatmaWerd
get() {
return KhatmaWerd(
Quran.getJuz2((completedWerds * werdLength) + 1).start,
Quran.getJuz2((completedWerds * werdLength) + werdLength).end
Quran.getPart(completedParts + 1).start,
Quran.getPart(completedParts + werdLength).end
)
}

Expand All @@ -73,34 +80,34 @@ class Khatma(
val nextWerd: KhatmaWerd?
get() {
return KhatmaWerd(
Quran.getJuz2(((completedWerds + 1) * werdLength) + 1).start,
Quran.getJuz2(((completedWerds + 1) * werdLength) + werdLength).end
Quran.getPart((completedParts + werdLength) + 1).start,
Quran.getPart((completedParts + werdLength) + werdLength).end
).takeIf { hasRemainingWerds }
}

/**
* `true` if khatma is still active, `false` otherwise
*/
val isActive: Boolean
get() = expectedEnd.isAfter(Timestamp.NOW()) and (completedWerds < plan.duration)
get() = expectedEnd.isAfter(Timestamp.NOW()) and (completedParts < Quran.QURAN_PARTS_COUNT)

/**
* Completed werds as a percentage
*/
val progressPercentage: Int
get() = (completedWerds / plan.duration).coerceAtMost(100) // <= 100
val progressPercentage: Float
get() = (completedParts / Quran.QURAN_PARTS_COUNT.toFloat() * 100f).coerceAtMost(100f) // <= 100

/**
* `true` if there are remaining werds, `false` otherwise
*/
val hasRemainingWerds: Boolean
get() = plan.duration > completedWerds
get() = remainingWerds > 0

/**
* Number of werds remaining
*/
val remainingWerds: Int
get() = (plan.duration - completedWerds).coerceAtLeast(0)
get() = (Quran.QURAN_PARTS_COUNT - completedParts).coerceAtLeast(0)

/**
* List that holds records of user progress in this khatma
Expand Down Expand Up @@ -133,7 +140,7 @@ class Khatma(
* Mark the current werd as done, calculate next werd then refresh Khatma runtime
*/
fun saveProgress() {
if (hasRemainingWerds) completedWerds++
if (hasRemainingWerds) progress++
}

/**
Expand All @@ -146,4 +153,25 @@ class Khatma(
TODO("[WILL BE IMPLEMENTED IN FUTURE DEVELOPMENT].")
}

override fun toString(): String {
return """
Khatma={
id=${id},
name=${name},
plan=${plan},
createdIn=${createdIn.asTimestamp().getFormatted(FormatPatterns.DATE_MONTH)},
reminder=${reminder},
completedWerds=${completedParts},
werdLength=${werdLength},
expectedEnd=${expectedEnd},
currentWerd=${currentWerd},
isActive=${isActive},
todayNumber=${todayNumber},
progressPercentage=${progressPercentage},
}
""".trimIndent()
}

fun Long.asTimestamp() = Timestamp(this)

}
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
package com.typ.muslim.features.khatma.models

import com.typ.muslim.features.quran.models.QuranAyah
import com.typ.muslim.interfaces.Exportable

class KhatmaWerd(
val start: QuranAyah,
val end: QuranAyah
) : java.io.Serializable {
) : java.io.Serializable, Exportable<String> {

constructor(fromAyah: Int, fromSurah: Int, toAyah: Int, toSurah: Int) : this(
QuranAyah(fromAyah, fromSurah, ""),
QuranAyah(toAyah, toSurah, "")
)

/**
* @return JSON-like
*/
override fun export(): String = "${start.export()}:${end.export()}"

override fun toString(): String {
return "KhatmaWerd{" +
"start=" + start +
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,33 @@ package com.typ.muslim.features.khatma.utils

import androidx.room.TypeConverter
import com.typ.muslim.features.khatma.models.KhatmaPlan
import com.typ.muslim.features.khatma.models.KhatmaWerd
import com.typ.muslim.features.khatma.models.ReminderPlan
import com.typ.muslim.features.quran.models.QuranAyah

class KhatmaConverters {
@TypeConverter
fun fromPlan(duration: Int): KhatmaPlan = KhatmaPlan(duration)
fun importPlan(duration: Int): KhatmaPlan = KhatmaPlan(duration)

@TypeConverter
fun planToDuration(plan: KhatmaPlan): Int = plan.duration
fun exportPlan(plan: KhatmaPlan): Int = plan.duration

@TypeConverter
fun toReminder(reminderPlan: String?): ReminderPlan? = ReminderPlan.fromJson(reminderPlan)
fun exportReminder(reminderPlan: ReminderPlan): String = reminderPlan.toJson()

@TypeConverter
fun fromReminderToPlan(reminderPlan: ReminderPlan): String = reminderPlan.toJson()
fun importReminder(reminderPlan: String?): ReminderPlan? = ReminderPlan.fromJson(reminderPlan)

@TypeConverter
fun exportWerd(werd: KhatmaWerd) = werd.export()

@TypeConverter
fun importWerd(werd: String) {
return werd.split(':').let { werd ->
val start = werd[0].split(',').let { ayah -> QuranAyah(ayah[0].toInt(), ayah[1].toInt()) }
val end = werd[1].split(',').let { ayah -> QuranAyah(ayah[0].toInt(), ayah[1].toInt()) }
KhatmaWerd(start, end)
}
}

}
Loading

0 comments on commit def8d00

Please sign in to comment.