diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 352eefa8a..0afb0ac05 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -9,6 +9,7 @@ plugins { alias(libs.plugins.kotlinAndroid) alias(libs.plugins.ksp) alias(libs.plugins.imgly).apply(false) + alias(libs.plugins.kotlin.serialization) } val keystorePropertiesFile: File = rootProject.file("keystore.properties") @@ -128,6 +129,10 @@ dependencies { implementation(libs.bundles.room) ksp(libs.androidx.room.compiler) + + implementation(platform(libs.supabase.bom)) + implementation(libs.supabase.postgrest) + implementation(libs.ktor.client.android) } // Apply the PESDKPlugin diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 698161e19..813ab6a49 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -19,6 +19,8 @@ android:name="android.permission.MANAGE_EXTERNAL_STORAGE" tools:ignore="ScopedStorage" /> + + diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/pro/App.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/App.kt index ef2768318..1b2ad213f 100644 --- a/app/src/main/kotlin/com/simplemobiletools/gallery/pro/App.kt +++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/App.kt @@ -6,6 +6,14 @@ import com.simplemobiletools.commons.extensions.checkUseEnglish import com.squareup.picasso.Downloader import com.squareup.picasso.Picasso import okhttp3.Request +import com.simplemobiletools.gallery.pro.extensions.favoritesDB +import com.simplemobiletools.gallery.pro.helpers.supabase +import com.simplemobiletools.gallery.pro.models.Favorite +import io.github.jan.supabase.postgrest.postgrest +import kotlinx.coroutines.CoroutineScope +import android.util.Log +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import okhttp3.Response class App : Application() { @@ -18,5 +26,17 @@ class App : Application() { override fun shutdown() {} }).build()) + + // Fetches favorites from Supabase on startup. + // This is a simple one-way sync that overwrites the local database. + // A more robust two-way sync would be needed for a production environment. + CoroutineScope(Dispatchers.IO).launch { + try { + val favorites = supabase.postgrest["favorites"].select().decodeList() + favoritesDB.insertAll(favorites) + } catch (e: Exception) { + Log.e("Supabase", "Error fetching favorites", e) + } + } } } diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/pro/extensions/Context.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/extensions/Context.kt index 2ef92ecfc..85b1e4fcf 100644 --- a/app/src/main/kotlin/com/simplemobiletools/gallery/pro/extensions/Context.kt +++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/extensions/Context.kt @@ -47,6 +47,12 @@ import java.io.File import java.io.FileInputStream import java.nio.ByteBuffer import java.nio.channels.FileChannel +import android.util.Log +import com.simplemobiletools.gallery.pro.helpers.supabase +import io.github.jan.supabase.postgrest.postgrest +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import kotlin.collections.set import kotlin.math.max @@ -838,16 +844,28 @@ fun Context.getFavoritePaths(): ArrayList { } } -fun Context.getFavoriteFromPath(path: String) = Favorite(null, path, path.getFilenameFromPath(), path.getParentPath()) +fun Context.getFavoriteFromPath(path: String) = Favorite(id = null, fullPath = path, filename = path.getFilenameFromPath(), parentPath = path.getParentPath()) fun Context.updateFavorite(path: String, isFavorite: Boolean) { try { if (isFavorite) { - favoritesDB.insert(getFavoriteFromPath(path)) + val favorite = getFavoriteFromPath(path) + favoritesDB.insert(favorite) + CoroutineScope(Dispatchers.IO).launch { + supabase.postgrest["favorites"].upsert(favorite, onConflict = "full_path") + } } else { favoritesDB.deleteFavoritePath(path) + CoroutineScope(Dispatchers.IO).launch { + supabase.postgrest["favorites"].delete { + filter { + eq("full_path", path) + } + } + } } } catch (e: Exception) { + Log.e("Supabase", "Error updating favorite", e) toast(com.simplemobiletools.commons.R.string.unknown_error_occurred) } } diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/pro/helpers/Supabase.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/helpers/Supabase.kt new file mode 100644 index 000000000..80969293b --- /dev/null +++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/helpers/Supabase.kt @@ -0,0 +1,11 @@ +package com.simplemobiletools.gallery.pro.helpers + +import io.github.jan.supabase.createSupabaseClient +import io.github.jan.supabase.postgrest.Postgrest + +val supabase = createSupabaseClient( + supabaseUrl = "https://your-project-url.supabase.co", + supabaseKey = "your-anon-key" +) { + install(Postgrest) +} \ No newline at end of file diff --git a/app/src/main/kotlin/com/simplemobiletools/gallery/pro/models/Favorite.kt b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/models/Favorite.kt index a1bd56ac3..7682a445e 100644 --- a/app/src/main/kotlin/com/simplemobiletools/gallery/pro/models/Favorite.kt +++ b/app/src/main/kotlin/com/simplemobiletools/gallery/pro/models/Favorite.kt @@ -4,11 +4,14 @@ import androidx.room.ColumnInfo import androidx.room.Entity import androidx.room.Index import androidx.room.PrimaryKey +import kotlinx.serialization.SerialName +import kotlinx.serialization.Serializable +@Serializable @Entity(tableName = "favorites", indices = [Index(value = ["full_path"], unique = true)]) data class Favorite( - @PrimaryKey(autoGenerate = true) var id: Int?, - @ColumnInfo(name = "full_path") var fullPath: String, + @PrimaryKey(autoGenerate = true) var id: Long?, + @ColumnInfo(name = "full_path") @SerialName("full_path") var fullPath: String, @ColumnInfo(name = "filename") var filename: String, - @ColumnInfo(name = "parent_path") var parentPath: String -) + @ColumnInfo(name = "parent_path") @SerialName("parent_path") var parentPath: String +) \ No newline at end of file diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index a2c137ee6..38556b5f2 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -5,6 +5,7 @@ imgly = "10.7.3" kotlin = "1.8.22" #KSP ksp = "1.8.22-1.0.11" +kotlin-serialization = "1.8.22" #AndroidX androidx-swiperefreshlayout = "1.1.0" androidx-constraintlayout = "2.1.4" @@ -44,6 +45,9 @@ app-version-appId = "com.simplemobiletools.gallery.pro" app-version-versionCode = "396" app-version-versionName = "6.28.1" subsamplingScaleImageView = "80efdaa570" +supabase = "3.2.4" +ktor = "3.3.0" + [libraries] #AndroidX androidx-media3-exoplayer = { module = "androidx.media3:media3-exoplayer", version.ref = "media3Exoplayer" } @@ -74,6 +78,9 @@ awebp = { module = "com.github.penfeizhou.android.animation:awebp", version.ref glide-compiler = { module = "com.github.bumptech.glide:ksp", version.ref = "glideCompiler" } zjupure-webpdecoder = { module = "com.github.zjupure:webpdecoder", version.ref = "zjupureWebpdecoder" } picasso = { module = "com.squareup.picasso:picasso", version.ref = "picasso" } +supabase-bom = { group = "io.github.jan-tennert.supabase", name = "bom", version.ref = "supabase" } +supabase-postgrest = { group = "io.github.jan-tennert.supabase", name = "postgrest-kt" } +ktor-client-android = { module = "io.ktor:ktor-client-android", version.ref = "ktor" } [bundles] room = [ "androidx-room-ktx", @@ -84,3 +91,4 @@ android = { id = "com.android.application", version.ref = "gradlePlugins-agp" } kotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } imgly = { id = "ly.img.android.sdk", version.ref = "imgly" } +kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin-serialization" }