diff --git a/.idea/appInsightsSettings.xml b/.idea/appInsightsSettings.xml
new file mode 100644
index 0000000..aa46aec
--- /dev/null
+++ b/.idea/appInsightsSettings.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml
new file mode 100644
index 0000000..b268ef3
--- /dev/null
+++ b/.idea/deploymentTargetSelector.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/.idea/gradle.xml b/.idea/gradle.xml
index ae388c2..0897082 100644
--- a/.idea/gradle.xml
+++ b/.idea/gradle.xml
@@ -4,16 +4,15 @@
diff --git a/.idea/kotlinScripting.xml b/.idea/kotlinScripting.xml
deleted file mode 100644
index 26da98e..0000000
--- a/.idea/kotlinScripting.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
- 2147483647
-
-
-
\ No newline at end of file
diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
index f8467b4..fe63bb6 100644
--- a/.idea/kotlinc.xml
+++ b/.idea/kotlinc.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/.idea/migrations.xml b/.idea/migrations.xml
new file mode 100644
index 0000000..f8051a6
--- /dev/null
+++ b/.idea/migrations.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 7c7f825..8499f04 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -1,4 +1,5 @@
import org.jetbrains.kotlin.gradle.plugin.mpp.pm20.util.archivesName
+
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
@@ -12,11 +13,28 @@ android {
applicationId = "com.d4rk.musicsleeptimer.plus"
minSdk = 26
targetSdk = 34
- versionCode = 26
- versionName = "1.0.0"
+ versionCode = 27
+ versionName = "3.0.0"
archivesName = "${applicationId}-v${versionName}"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
- resourceConfigurations += listOf("en", "de", "es", "fr", "hi", "hu", "in", "it", "ja", "ro", "ru", "tr", "sv", "bg", "pl", "uk")
+ resourceConfigurations += listOf(
+ "en" ,
+ "de" ,
+ "es" ,
+ "fr" ,
+ "hi" ,
+ "hu" ,
+ "in" ,
+ "it" ,
+ "ja" ,
+ "ro" ,
+ "ru" ,
+ "tr" ,
+ "sv" ,
+ "bg" ,
+ "pl" ,
+ "uk"
+ )
}
buildTypes {
release {
@@ -24,14 +42,18 @@ android {
isMinifyEnabled = true
isShrinkResources = true
isDebuggable = false
- proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
+ proguardFiles(
+ getDefaultProguardFile("proguard-android-optimize.txt") , "proguard-rules.pro"
+ )
}
debug {
multiDexEnabled = true
isMinifyEnabled = true
isShrinkResources = true
isDebuggable = true
- proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
+ proguardFiles(
+ getDefaultProguardFile("proguard-android-optimize.txt") , "proguard-rules.pro"
+ )
}
}
compileOptions {
@@ -52,12 +74,14 @@ android {
}
}
dependencies {
- implementation("com.google.firebase:firebase-crashlytics-ktx:18.5.1")
- implementation("com.google.firebase:firebase-analytics-ktx:21.5.0")
- implementation("com.google.firebase:firebase-perf:20.5.0")
- implementation("androidx.appcompat:appcompat:1.6.1")
- implementation("androidx.multidex:multidex:2.0.1")
- testImplementation("junit:junit:4.13.2")
- androidTestImplementation("androidx.test.ext:junit:1.1.5")
- androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
+ implementation(platform(libs.firebase.bom))
+ implementation(libs.firebase.crashlytics.ktx)
+ implementation(libs.firebase.analytics.ktx)
+ implementation(libs.firebase.perf)
+ implementation(libs.appcompat)
+ implementation(libs.work.runtime.ktx)
+ implementation(libs.multidex)
+ testImplementation(libs.junit)
+ androidTestImplementation(libs.ext.junit)
+ androidTestImplementation(libs.espresso.core)
}
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 7c515de..dda7cee 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,51 +1,54 @@
-
-
-
-
-
+
+
+
+
+
+
+ android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
-
+
+
+ android:value="true" />
+ android:value="true" />
-
+
+ android:value="true" />
+
+ android:value="true" />
+ android:value="@integer/google_play_services_version" />
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/d4rk/musicsleeptimer/plus/notifications/SleepNotification.kt b/app/src/main/kotlin/com/d4rk/musicsleeptimer/plus/notifications/SleepNotification.kt
index 0621ff4..cb98063 100644
--- a/app/src/main/kotlin/com/d4rk/musicsleeptimer/plus/notifications/SleepNotification.kt
+++ b/app/src/main/kotlin/com/d4rk/musicsleeptimer/plus/notifications/SleepNotification.kt
@@ -1,4 +1,5 @@
package com.d4rk.musicsleeptimer.plus.notifications
+
import android.app.Notification
import android.app.Notification.CATEGORY_EVENT
import android.app.Notification.VISIBILITY_PUBLIC
@@ -22,57 +23,93 @@ import java.text.DateFormat.SHORT
import java.util.Date
import java.util.concurrent.TimeUnit.MILLISECONDS
import java.util.concurrent.TimeUnit.MINUTES
+
object SleepNotification {
private val TIMEOUT_INITIAL_MILLIS = MINUTES.toMillis(30)
private val TIMEOUT_INCREMENT_MILLIS = MINUTES.toMillis(10)
private val TIMEOUT_DECREMENT_MILLIS = MINUTES.toMillis(10)
- private enum class Action(private val value: String) {
+
+ private enum class Action(private val value : String) {
CANCEL("com.d4rk.musicsleeptimer.plus.action.CANCEL") {
- override fun title(context: Context) = context.getText(android.R.string.cancel)
- },
+ override fun title(context : Context) = context.getText(android.R.string.cancel)
+ } ,
INCREMENT("com.d4rk.musicsleeptimer.plus.action.INCREMENT") {
- override fun title(context: Context) = context.getString(R.string.notification_action_increment, MILLISECONDS.toMinutes(TIMEOUT_INCREMENT_MILLIS))
- },
+ override fun title(context : Context) = context.getString(
+ R.string.notification_action_increment ,
+ MILLISECONDS.toMinutes(TIMEOUT_INCREMENT_MILLIS)
+ )
+ } ,
DECREMENT("com.d4rk.musicsleeptimer.plus.action.DECREMENT") {
- override fun title(context: Context) = context.getString(R.string.notification_action_decrement, MILLISECONDS.toMinutes(TIMEOUT_DECREMENT_MILLIS))
- },;
+ override fun title(context : Context) = context.getString(
+ R.string.notification_action_decrement ,
+ MILLISECONDS.toMinutes(TIMEOUT_DECREMENT_MILLIS)
+ )
+ } , ;
+
companion object {
- fun parse(value: String?): Action? = values().firstOrNull { it.value == value }
+ fun parse(value : String?) : Action? = entries.firstOrNull { it.value == value }
}
- fun intent(context: Context): Intent = Intent(context, SleepTileService::class.java).setAction(value)
- fun pendingIntent(context: Context, cancel: Boolean = false): PendingIntent? = PendingIntent.getService(context, 0, intent(context), FLAG_IMMUTABLE).apply { if (cancel) cancel() }
- fun action(context: Context, cancel: Boolean = false): Notification.Action.Builder = Notification.Action.Builder(Icon.createWithResource(context, 0), title(context), pendingIntent(context, cancel))
- abstract fun title(context: Context): CharSequence?
+
+ fun intent(context : Context) : Intent =
+ Intent(context , SleepTileService::class.java).setAction(value)
+
+ fun pendingIntent(context : Context , cancel : Boolean = false) : PendingIntent? =
+ PendingIntent.getService(context , 0 , intent(context) , FLAG_IMMUTABLE)
+ .apply { if (cancel) cancel() }
+
+ fun action(context : Context , cancel : Boolean = false) : Notification.Action.Builder =
+ Notification.Action.Builder(
+ Icon.createWithResource(context , 0) ,
+ title(context) ,
+ pendingIntent(context , cancel)
+ )
+
+ abstract fun title(context : Context) : CharSequence?
}
- fun Context.notificationManager(): NotificationManager? = getSystemService(NotificationManager::class.java)
- fun Context.find() = notificationManager()?.activeNotifications?.firstOrNull { it.id == R.id.notification_id }?.notification
- fun Context.handle(intent: Intent?) = when (Action.parse(intent?.action)) {
+
+ fun Context.notificationManager() : NotificationManager? =
+ getSystemService(NotificationManager::class.java)
+
+ fun Context.find() =
+ notificationManager()?.activeNotifications?.firstOrNull { it.id == R.id.notification_id }?.notification
+
+ fun Context.handle(intent : Intent?) = when (Action.parse(intent?.action)) {
INCREMENT -> update(TIMEOUT_INCREMENT_MILLIS)
- DECREMENT -> update(-TIMEOUT_DECREMENT_MILLIS)
+ DECREMENT -> update(- TIMEOUT_DECREMENT_MILLIS)
CANCEL -> cancel()
null -> Unit
}
+
fun Context.toggle() = if (find() == null) show() else cancel()
private fun Context.cancel() = notificationManager()?.cancel(R.id.notification_id) ?: Unit
- private fun Context.update(timeout: Long) = find()?.let {
+ private fun Context.update(timeout : Long) = find()?.let {
it.`when` - currentTimeMillis()
+ }?.let {
+ if (it > - timeout) it + timeout else it
+ }?.let {
+ show(it)
}
- ?.let {
- if (it > -timeout) it + timeout else it
- }
- ?.let { show(it)
- }
- private fun Context.show(timeout: Long = TIMEOUT_INITIAL_MILLIS) {
+
+ private fun Context.show(timeout : Long = TIMEOUT_INITIAL_MILLIS) {
require(timeout > 0)
val eta = currentTimeMillis() + timeout
- val notification = Notification.Builder(this, getString(R.string.notification_channel_id)).setCategory(CATEGORY_EVENT).setVisibility(VISIBILITY_PUBLIC).setOnlyAlertOnce(true).setOngoing(true).setSmallIcon(R.drawable.ic_music_off).setSubText(DateFormat.getTimeInstance(SHORT).format(Date(eta))).setShowWhen(true).setWhen(eta).setUsesChronometer(true).setChronometerCountDown(true).setTimeoutAfter(timeout).setDeleteIntent(SleepAudioService.pendingIntent(this)).addAction(INCREMENT.action(this).build()).addAction(DECREMENT.action(this, cancel = timeout <= TIMEOUT_DECREMENT_MILLIS).build()).addAction(CANCEL.action(this).build()).build()
+ val notification = Notification.Builder(this , getString(R.string.notification_channel_id))
+ .setCategory(CATEGORY_EVENT).setVisibility(VISIBILITY_PUBLIC).setOnlyAlertOnce(true)
+ .setOngoing(true).setSmallIcon(R.drawable.ic_music_off)
+ .setSubText(DateFormat.getTimeInstance(SHORT).format(Date(eta))).setShowWhen(true)
+ .setWhen(eta).setUsesChronometer(true).setChronometerCountDown(true)
+ .setTimeoutAfter(timeout).setDeleteIntent(SleepAudioService.pendingIntent(this))
+ .addAction(INCREMENT.action(this).build()).addAction(
+ DECREMENT.action(this , cancel = timeout <= TIMEOUT_DECREMENT_MILLIS).build()
+ ).addAction(CANCEL.action(this).build()).build()
createNotificationChannel()
- notificationManager()?.notify(R.id.notification_id, notification)
+ notificationManager()?.notify(R.id.notification_id , notification)
}
+
private fun Context.createNotificationChannel() {
val id = getString(R.string.notification_channel_id)
- val name: CharSequence = getString(R.string.app_name)
- val channel = NotificationChannel(id, name, IMPORTANCE_LOW).apply {
+ val name : CharSequence = getString(R.string.app_name)
+ val channel = NotificationChannel(id , name , IMPORTANCE_LOW).apply {
setBypassDnd(true)
lockscreenVisibility = VISIBILITY_PUBLIC
}
diff --git a/app/src/main/kotlin/com/d4rk/musicsleeptimer/plus/services/SleepAudioService.kt b/app/src/main/kotlin/com/d4rk/musicsleeptimer/plus/services/SleepAudioService.kt
index ef17737..8331651 100644
--- a/app/src/main/kotlin/com/d4rk/musicsleeptimer/plus/services/SleepAudioService.kt
+++ b/app/src/main/kotlin/com/d4rk/musicsleeptimer/plus/services/SleepAudioService.kt
@@ -1,4 +1,5 @@
package com.d4rk.musicsleeptimer.plus.services
+
import android.app.PendingIntent
import android.app.PendingIntent.FLAG_IMMUTABLE
import android.content.Context
@@ -13,27 +14,34 @@ import android.media.AudioManager.AUDIOFOCUS_GAIN
import android.media.AudioManager.STREAM_MUSIC
import com.d4rk.musicsleeptimer.plus.services.SleepTileService.Companion.requestTileUpdate
import java.util.concurrent.TimeUnit.SECONDS
+
@Suppress("DEPRECATION")
class SleepAudioService : android.app.IntentService("SleepAudioService") {
companion object {
private val FADE_STEP_MILLIS = SECONDS.toMillis(1)
private val RESTORE_VOLUME_MILLIS = SECONDS.toMillis(2)
- private fun intent(context: Context) = Intent(context, SleepAudioService::class.java)
- fun pendingIntent(context: Context): PendingIntent? = PendingIntent.getService(context, 0, intent(context), FLAG_IMMUTABLE)
+ private fun intent(context : Context) = Intent(context , SleepAudioService::class.java)
+ fun pendingIntent(context : Context) : PendingIntent? =
+ PendingIntent.getService(context , 0 , intent(context) , FLAG_IMMUTABLE)
}
+
@Deprecated("Deprecated in Java")
- override fun onHandleIntent(intent: Intent?) = getSystemService(AudioManager::class.java)?.run {
- val volumeIndex = getStreamVolume(STREAM_MUSIC)
- do {
- adjustStreamVolume(STREAM_MUSIC, ADJUST_LOWER, 0)
- Thread.sleep(FADE_STEP_MILLIS)
- } while (getStreamVolume(STREAM_MUSIC) > 0)
- val attributes = AudioAttributes.Builder().setUsage(USAGE_MEDIA).setContentType(CONTENT_TYPE_MUSIC).build()
- val focusRequest = AudioFocusRequest.Builder(AUDIOFOCUS_GAIN).setAudioAttributes(attributes).setOnAudioFocusChangeListener {}.build()
- requestAudioFocus(focusRequest)
- Thread.sleep(RESTORE_VOLUME_MILLIS)
- setStreamVolume(STREAM_MUSIC, volumeIndex, 0)
- abandonAudioFocusRequest(focusRequest)
- requestTileUpdate()
- } ?: Unit
+ override fun onHandleIntent(intent : Intent?) =
+ getSystemService(AudioManager::class.java)?.run {
+ val volumeIndex = getStreamVolume(STREAM_MUSIC)
+ do {
+ adjustStreamVolume(STREAM_MUSIC , ADJUST_LOWER , 0)
+ Thread.sleep(FADE_STEP_MILLIS)
+ } while (getStreamVolume(STREAM_MUSIC) > 0)
+ val attributes = AudioAttributes.Builder().setUsage(USAGE_MEDIA)
+ .setContentType(CONTENT_TYPE_MUSIC).build()
+ val focusRequest =
+ AudioFocusRequest.Builder(AUDIOFOCUS_GAIN).setAudioAttributes(attributes)
+ .setOnAudioFocusChangeListener {}.build()
+ requestAudioFocus(focusRequest)
+ Thread.sleep(RESTORE_VOLUME_MILLIS)
+ setStreamVolume(STREAM_MUSIC , volumeIndex , 0)
+ abandonAudioFocusRequest(focusRequest)
+ requestTileUpdate()
+ } ?: Unit
}
\ No newline at end of file
diff --git a/app/src/main/kotlin/com/d4rk/musicsleeptimer/plus/services/SleepTileService.kt b/app/src/main/kotlin/com/d4rk/musicsleeptimer/plus/services/SleepTileService.kt
index 1c71017..6315ade 100644
--- a/app/src/main/kotlin/com/d4rk/musicsleeptimer/plus/services/SleepTileService.kt
+++ b/app/src/main/kotlin/com/d4rk/musicsleeptimer/plus/services/SleepTileService.kt
@@ -1,4 +1,5 @@
package com.d4rk.musicsleeptimer.plus.services
+
import android.content.ComponentName
import android.content.Context
import android.content.Intent
@@ -16,37 +17,46 @@ import com.d4rk.musicsleeptimer.plus.notifications.SleepNotification.toggle
import java.text.DateFormat.SHORT
import java.text.DateFormat.getTimeInstance
import java.util.Date
+
class SleepTileService : TileService() {
companion object {
- fun Context.requestTileUpdate() = requestListeningState(this, ComponentName(this, SleepTileService::class.java))
+ fun Context.requestTileUpdate() =
+ requestListeningState(this , ComponentName(this , SleepTileService::class.java))
}
+
override fun onStartListening() = refreshTile()
override fun onClick() = when (notificationManager()?.areNotificationsEnabled()) {
true -> toggle().also { refreshTile() }
else -> requestNotificationsPermission()
}
- override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
+
+ override fun onStartCommand(intent : Intent? , flags : Int , startId : Int) : Int {
handle(intent)
requestTileUpdate()
stopSelfResult(startId)
return START_NOT_STICKY
}
+
private fun refreshTile() = qsTile?.run {
when (val notification = find()) {
null -> {
state = STATE_INACTIVE
if (SDK_INT >= Q) subtitle = resources.getText(R.string.tile_subtitle)
}
+
else -> {
state = STATE_ACTIVE
- if (SDK_INT >= Q) subtitle = getTimeInstance(SHORT).format(Date(notification.`when`))
+ if (SDK_INT >= Q) subtitle =
+ getTimeInstance(SHORT).format(Date(notification.`when`))
}
}
updateTile()
} ?: Unit
+
@Suppress("DEPRECATION")
- private fun requestNotificationsPermission() = Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS).apply {
- addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- putExtra(Settings.EXTRA_APP_PACKAGE, packageName)
- }.let(::startActivityAndCollapse)
+ private fun requestNotificationsPermission() =
+ Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS).apply {
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ putExtra(Settings.EXTRA_APP_PACKAGE , packageName)
+ }.let(::startActivityAndCollapse)
}
\ No newline at end of file
diff --git a/app/src/main/res/drawable-anydpi/ic_launcher_foreground.xml b/app/src/main/res/drawable-anydpi/ic_launcher_foreground.xml
index 626dcb7..0f8a155 100644
--- a/app/src/main/res/drawable-anydpi/ic_launcher_foreground.xml
+++ b/app/src/main/res/drawable-anydpi/ic_launcher_foreground.xml
@@ -1,6 +1,20 @@
-
-
-
-
-
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable-anydpi/ic_music_off.xml b/app/src/main/res/drawable-anydpi/ic_music_off.xml
index d78f581..1a172e9 100644
--- a/app/src/main/res/drawable-anydpi/ic_music_off.xml
+++ b/app/src/main/res/drawable-anydpi/ic_music_off.xml
@@ -1,3 +1,10 @@
-
-
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-anydpi/ic_launcher.xml b/app/src/main/res/mipmap-anydpi/ic_launcher.xml
index 27e87f2..90d9271 100644
--- a/app/src/main/res/mipmap-anydpi/ic_launcher.xml
+++ b/app/src/main/res/mipmap-anydpi/ic_launcher.xml
@@ -1,5 +1,5 @@
-
-
-
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/ids.xml b/app/src/main/res/values/ids.xml
index 660576b..474534f 100644
--- a/app/src/main/res/values/ids.xml
+++ b/app/src/main/res/values/ids.xml
@@ -1,4 +1,4 @@
-
+ sleep_timer
\ No newline at end of file
diff --git a/app/src/main/res/xml/config_locales.xml b/app/src/main/res/xml/config_locales.xml
index 4a4f72b..e0f0e7c 100644
--- a/app/src/main/res/xml/config_locales.xml
+++ b/app/src/main/res/xml/config_locales.xml
@@ -1,19 +1,18 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/build.gradle.kts b/build.gradle.kts
index ddd1216..cf539d5 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -1,7 +1,7 @@
plugins {
- id("com.android.application") version "8.1.3" apply false
- id("com.android.library") version "8.1.3" apply false
- id("org.jetbrains.kotlin.android") version "1.9.10" apply false
- id("com.google.gms.google-services") version "4.4.0" apply false
- id("com.google.firebase.crashlytics") version "2.9.2" apply false
+ alias(libs.plugins.androidApplication) apply false
+ alias(libs.plugins.androidLibrary) apply false
+ alias(libs.plugins.jetbrainsKotlinAndroid) apply false
+ alias(libs.plugins.googlePlayServices) apply false
+ alias(libs.plugins.googleFirebase) apply false
}
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
new file mode 100644
index 0000000..30fc54d
--- /dev/null
+++ b/gradle/libs.versions.toml
@@ -0,0 +1,31 @@
+[versions]
+agp = "8.4.1"
+appcompat = "1.7.0"
+espressoCore = "3.5.1"
+firebaseBom = "33.1.0"
+kotlin = "1.9.23"
+google-firebase-crashlytics = "3.0.1"
+google-services = "4.4.2"
+junit = "4.13.2"
+junitVersion = "1.1.5"
+multidex = "2.0.1"
+workRuntimeKtx = "2.9.0"
+
+[libraries]
+appcompat = { module = "androidx.appcompat:appcompat", version.ref = "appcompat" }
+espresso-core = { module = "androidx.test.espresso:espresso-core", version.ref = "espressoCore" }
+ext-junit = { module = "androidx.test.ext:junit", version.ref = "junitVersion" }
+firebase-analytics-ktx = { module = "com.google.firebase:firebase-analytics-ktx" }
+firebase-bom = { module = "com.google.firebase:firebase-bom", version.ref = "firebaseBom" }
+firebase-crashlytics-ktx = { module = "com.google.firebase:firebase-crashlytics-ktx" }
+firebase-perf = { module = "com.google.firebase:firebase-perf" }
+multidex = { module = "androidx.multidex:multidex", version.ref = "multidex" }
+junit = { module = "junit:junit", version.ref = "junit" }
+work-runtime-ktx = { module = "androidx.work:work-runtime-ktx", version.ref = "workRuntimeKtx" }
+
+[plugins]
+androidApplication = { id = "com.android.application", version.ref = "agp" }
+androidLibrary = { id = "com.android.library", version.ref = "agp" }
+jetbrainsKotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
+googlePlayServices = { id = "com.google.gms.google-services", version.ref = "google-services" }
+googleFirebase = { id = "com.google.firebase.crashlytics", version.ref = "google-firebase-crashlytics" }
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index de4a76c..cc54d81 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
-#Thu Nov 09 10:16:15 EET 2023
+#Tue Jun 04 08:10:55 EEST 2024
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists