diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 806b8603..a6ca5e5d 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -1,3 +1,5 @@ +import com.google.protobuf.gradle.* + plugins { id("com.android.application") kotlin("android") @@ -8,6 +10,7 @@ plugins { id("com.google.gms.google-services") id("com.google.firebase.crashlytics") id("com.google.firebase.firebase-perf") + id("com.google.protobuf") } android { @@ -104,6 +107,7 @@ dependencies { implementation("androidx.gridlayout:gridlayout:1.0.0") implementation("androidx.biometric:biometric:1.1.0") implementation("androidx.work:work-runtime-ktx:2.7.1") + implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.4.0") implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.4.0") implementation("androidx.room:room-runtime:2.3.0") @@ -118,10 +122,12 @@ dependencies { implementation("com.google.android.material:material:1.4.0") - implementation(platform("com.google.firebase:firebase-bom:28.3.0")) + implementation(platform("com.google.firebase:firebase-bom:29.0.3")) implementation("com.google.firebase:firebase-analytics-ktx") - implementation("com.google.firebase:firebase-crashlytics") - implementation("com.google.firebase:firebase-perf") + implementation("com.google.firebase:firebase-crashlytics-ktx") + implementation("com.google.firebase:firebase-perf-ktx") + + implementation("com.google.protobuf:protobuf-javalite:3.19.1") implementation("com.google.android.gms:play-services-oss-licenses:17.0.0") @@ -147,6 +153,21 @@ dependencies { androidTestImplementation("androidx.compose.ui:ui-test-junit4:1.0.5") } +protobuf { + protoc { + artifact = "com.google.protobuf:protoc:3.19.1" + } + generateProtoTasks { + all().forEach { task -> + task.builtins { + id("java") { + option("lite") + } + } + } + } +} + task("clearAppCache") { commandLine("adb", "shell", "pm", "clear", "xyz.quaver.pupil.debug") } \ No newline at end of file diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 0b092737..36ca71bf 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -40,5 +40,6 @@ -keepclasseswithmembers class xyz.quaver.** { kotlinx.serialization.KSerializer serializer(...); } --keep class xyz.quaver.pupil.ui.fragment.ManageFavoritesFragment --keep class xyz.quaver.pupil.ui.fragment.ManageStorageFragment \ No newline at end of file +-keepclassmembers class * extends androidx.datastore.preferences.protobuf.GeneratedMessageLite { + ; +} \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/proto/Settings.kt b/app/src/main/java/xyz/quaver/pupil/proto/Settings.kt new file mode 100644 index 00000000..4a521be0 --- /dev/null +++ b/app/src/main/java/xyz/quaver/pupil/proto/Settings.kt @@ -0,0 +1,47 @@ +/* + * Pupil, Hitomi.la viewer for Android + * Copyright (C) 2021 tom5079 + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +package xyz.quaver.pupil.proto + +import android.content.Context +import androidx.datastore.core.CorruptionException +import androidx.datastore.core.DataStore +import androidx.datastore.core.Serializer +import androidx.datastore.dataStore +import com.google.protobuf.InvalidProtocolBufferException +import java.io.InputStream +import java.io.OutputStream + +object SettingsSerializer : Serializer { + override val defaultValue: Settings = Settings.getDefaultInstance() + + override suspend fun readFrom(input: InputStream): Settings { + try { + return Settings.parseFrom(input) + } catch (exception: InvalidProtocolBufferException) { + throw CorruptionException("Cannot read proto.", exception) + } + } + + override suspend fun writeTo(t: Settings, output: OutputStream) = t.writeTo(output) +} + +val Context.settingsDataStore: DataStore by dataStore( + fileName = "settings.proto", + serializer = SettingsSerializer +) \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt index 0aa6ce82..26254e90 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt @@ -185,6 +185,7 @@ class MainActivity : ComponentActivity(), DIAware { } ) { Box(Modifier.fillMaxSize()) { + LazyColumn( Modifier .fillMaxSize() @@ -203,6 +204,16 @@ class MainActivity : ComponentActivity(), DIAware { return Offset.Zero } + + override fun onPostScroll( + consumed: Offset, + available: Offset, + source: NestedScrollSource + ): Offset { + + + return super.onPostScroll(consumed, available, source) + } }), contentPadding = PaddingValues(0.dp, 56.dp, 0.dp, 0.dp) ) { diff --git a/app/src/main/java/xyz/quaver/pupil/ui/viewmodel/MainViewModel.kt b/app/src/main/java/xyz/quaver/pupil/ui/viewmodel/MainViewModel.kt index 27db167b..dca337e6 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/viewmodel/MainViewModel.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/viewmodel/MainViewModel.kt @@ -24,23 +24,22 @@ import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.AndroidViewModel -import androidx.lifecycle.MutableLiveData -import androidx.lifecycle.Transformations +import androidx.lifecycle.asLiveData import androidx.lifecycle.viewModelScope import kotlinx.coroutines.* +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.map import org.kodein.di.DIAware import org.kodein.di.android.x.closestDI import org.kodein.di.direct import org.kodein.di.instance import org.kodein.log.LoggerFactory import org.kodein.log.newLogger +import xyz.quaver.pupil.proto.settingsDataStore import xyz.quaver.pupil.sources.History import xyz.quaver.pupil.sources.ItemInfo import xyz.quaver.pupil.sources.Source -import xyz.quaver.pupil.util.Preferences import xyz.quaver.pupil.util.source -import kotlin.math.ceil -import kotlin.math.roundToInt import kotlin.random.Random @Suppress("UNCHECKED_CAST") @@ -51,6 +50,10 @@ class MainViewModel(app: Application) : AndroidViewModel(app), DIAware { val searchResults = mutableStateListOf() + private val resultsPerPage = app.settingsDataStore.data.map { + it.resultsPerPage + } + var loading by mutableStateOf(false) private set @@ -72,13 +75,8 @@ class MainViewModel(app: Application) : AndroidViewModel(app), DIAware { var currentPage by mutableStateOf(1) - private val totalItems = MutableLiveData() - - val totalPages = Transformations.map(totalItems) { - val perPage = Preferences["per_page", "25"].toInt() - - ceil(it / perPage.toDouble()).roundToInt() - } + var totalItems by mutableStateOf(0) + private set fun setSourceAndReset(sourceName: String) { source = sourceFactory(sourceName) @@ -112,8 +110,6 @@ class MainViewModel(app: Application) : AndroidViewModel(app), DIAware { } fun query() { - val perPage = Preferences["per_page", "25"].toInt() - suggestionJob?.cancel() queryJob?.cancel() @@ -121,13 +117,19 @@ class MainViewModel(app: Application) : AndroidViewModel(app), DIAware { searchResults.clear() queryJob = viewModelScope.launch { + val resultsPerPage = resultsPerPage.first() + + logger.info { + resultsPerPage.toString() + } + val (channel, count) = source.search( query, - (currentPage - 1) * perPage until currentPage * perPage, + (currentPage - 1) * resultsPerPage until currentPage * resultsPerPage, sortModeIndex ) - totalItems.postValue(count) + totalItems = count for (result in channel) { yield() @@ -139,15 +141,15 @@ class MainViewModel(app: Application) : AndroidViewModel(app), DIAware { } fun random(callback: (ItemInfo) -> Unit) { - if (totalItems.value!! == 0) + if (totalItems == 0) return - val random = Random.Default.nextInt(totalItems.value!!) + val random = Random.Default.nextInt(totalItems) viewModelScope.launch { withContext(Dispatchers.IO) { source.search( - query + Preferences["default_query", ""], + query, random .. random, sortModeIndex ).first.receive() diff --git a/app/src/main/java/xyz/quaver/pupil/util/misc.kt b/app/src/main/java/xyz/quaver/pupil/util/misc.kt index 2911f76b..0a53cded 100644 --- a/app/src/main/java/xyz/quaver/pupil/util/misc.kt +++ b/app/src/main/java/xyz/quaver/pupil/util/misc.kt @@ -29,6 +29,7 @@ import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.asImageBitmap import androidx.compose.ui.graphics.toAndroidRect import androidx.lifecycle.MutableLiveData +import kotlinx.coroutines.flow.Flow import kotlinx.serialization.json.* import org.kodein.di.DIAware import org.kodein.di.DirectDIAware diff --git a/app/src/main/proto/settings.proto b/app/src/main/proto/settings.proto new file mode 100644 index 00000000..306259ae --- /dev/null +++ b/app/src/main/proto/settings.proto @@ -0,0 +1,8 @@ +syntax = "proto2"; + +option java_package = "xyz.quaver.pupil.proto"; +option java_multiple_files = true; + +message Settings { + optional int32 results_per_page = 1 [default = 25]; +} \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index a8bcab60..cdaf60d3 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -13,9 +13,10 @@ buildscript { classpath("com.google.gms:google-services:4.3.10") // NOTE: Do not place your application dependencies here; they belong // in the individual module build.gradle files - classpath("com.google.firebase:firebase-crashlytics-gradle:2.8.0") + classpath("com.google.firebase:firebase-crashlytics-gradle:2.8.1") classpath("com.google.firebase:perf-plugin:1.4.0") classpath("com.google.android.gms:oss-licenses-plugin:0.10.4") + classpath("com.google.protobuf:protobuf-gradle-plugin:0.8.18") } }