Protobuf
This commit is contained in:
@@ -1,3 +1,5 @@
|
|||||||
|
import com.google.protobuf.gradle.*
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("com.android.application")
|
id("com.android.application")
|
||||||
kotlin("android")
|
kotlin("android")
|
||||||
@@ -8,6 +10,7 @@ plugins {
|
|||||||
id("com.google.gms.google-services")
|
id("com.google.gms.google-services")
|
||||||
id("com.google.firebase.crashlytics")
|
id("com.google.firebase.crashlytics")
|
||||||
id("com.google.firebase.firebase-perf")
|
id("com.google.firebase.firebase-perf")
|
||||||
|
id("com.google.protobuf")
|
||||||
}
|
}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
@@ -104,6 +107,7 @@ dependencies {
|
|||||||
implementation("androidx.gridlayout:gridlayout:1.0.0")
|
implementation("androidx.gridlayout:gridlayout:1.0.0")
|
||||||
implementation("androidx.biometric:biometric:1.1.0")
|
implementation("androidx.biometric:biometric:1.1.0")
|
||||||
implementation("androidx.work:work-runtime-ktx:2.7.1")
|
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.lifecycle:lifecycle-viewmodel-compose:2.4.0")
|
||||||
|
|
||||||
implementation("androidx.room:room-runtime:2.3.0")
|
implementation("androidx.room:room-runtime:2.3.0")
|
||||||
@@ -118,10 +122,12 @@ dependencies {
|
|||||||
|
|
||||||
implementation("com.google.android.material:material:1.4.0")
|
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-analytics-ktx")
|
||||||
implementation("com.google.firebase:firebase-crashlytics")
|
implementation("com.google.firebase:firebase-crashlytics-ktx")
|
||||||
implementation("com.google.firebase:firebase-perf")
|
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")
|
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")
|
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<Exec>("clearAppCache") {
|
task<Exec>("clearAppCache") {
|
||||||
commandLine("adb", "shell", "pm", "clear", "xyz.quaver.pupil.debug")
|
commandLine("adb", "shell", "pm", "clear", "xyz.quaver.pupil.debug")
|
||||||
}
|
}
|
||||||
5
app/proguard-rules.pro
vendored
5
app/proguard-rules.pro
vendored
@@ -40,5 +40,6 @@
|
|||||||
-keepclasseswithmembers class xyz.quaver.** {
|
-keepclasseswithmembers class xyz.quaver.** {
|
||||||
kotlinx.serialization.KSerializer serializer(...);
|
kotlinx.serialization.KSerializer serializer(...);
|
||||||
}
|
}
|
||||||
-keep class xyz.quaver.pupil.ui.fragment.ManageFavoritesFragment
|
-keepclassmembers class * extends androidx.datastore.preferences.protobuf.GeneratedMessageLite {
|
||||||
-keep class xyz.quaver.pupil.ui.fragment.ManageStorageFragment
|
<fields>;
|
||||||
|
}
|
||||||
47
app/src/main/java/xyz/quaver/pupil/proto/Settings.kt
Normal file
47
app/src/main/java/xyz/quaver/pupil/proto/Settings.kt
Normal file
@@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
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<Settings> {
|
||||||
|
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<Settings> by dataStore(
|
||||||
|
fileName = "settings.proto",
|
||||||
|
serializer = SettingsSerializer
|
||||||
|
)
|
||||||
@@ -185,6 +185,7 @@ class MainActivity : ComponentActivity(), DIAware {
|
|||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
Box(Modifier.fillMaxSize()) {
|
Box(Modifier.fillMaxSize()) {
|
||||||
|
|
||||||
LazyColumn(
|
LazyColumn(
|
||||||
Modifier
|
Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
@@ -203,6 +204,16 @@ class MainActivity : ComponentActivity(), DIAware {
|
|||||||
|
|
||||||
return Offset.Zero
|
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)
|
contentPadding = PaddingValues(0.dp, 56.dp, 0.dp, 0.dp)
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -24,23 +24,22 @@ import androidx.compose.runtime.mutableStateListOf
|
|||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.asLiveData
|
||||||
import androidx.lifecycle.Transformations
|
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
|
import kotlinx.coroutines.flow.first
|
||||||
|
import kotlinx.coroutines.flow.map
|
||||||
import org.kodein.di.DIAware
|
import org.kodein.di.DIAware
|
||||||
import org.kodein.di.android.x.closestDI
|
import org.kodein.di.android.x.closestDI
|
||||||
import org.kodein.di.direct
|
import org.kodein.di.direct
|
||||||
import org.kodein.di.instance
|
import org.kodein.di.instance
|
||||||
import org.kodein.log.LoggerFactory
|
import org.kodein.log.LoggerFactory
|
||||||
import org.kodein.log.newLogger
|
import org.kodein.log.newLogger
|
||||||
|
import xyz.quaver.pupil.proto.settingsDataStore
|
||||||
import xyz.quaver.pupil.sources.History
|
import xyz.quaver.pupil.sources.History
|
||||||
import xyz.quaver.pupil.sources.ItemInfo
|
import xyz.quaver.pupil.sources.ItemInfo
|
||||||
import xyz.quaver.pupil.sources.Source
|
import xyz.quaver.pupil.sources.Source
|
||||||
import xyz.quaver.pupil.util.Preferences
|
|
||||||
import xyz.quaver.pupil.util.source
|
import xyz.quaver.pupil.util.source
|
||||||
import kotlin.math.ceil
|
|
||||||
import kotlin.math.roundToInt
|
|
||||||
import kotlin.random.Random
|
import kotlin.random.Random
|
||||||
|
|
||||||
@Suppress("UNCHECKED_CAST")
|
@Suppress("UNCHECKED_CAST")
|
||||||
@@ -51,6 +50,10 @@ class MainViewModel(app: Application) : AndroidViewModel(app), DIAware {
|
|||||||
|
|
||||||
val searchResults = mutableStateListOf<ItemInfo>()
|
val searchResults = mutableStateListOf<ItemInfo>()
|
||||||
|
|
||||||
|
private val resultsPerPage = app.settingsDataStore.data.map {
|
||||||
|
it.resultsPerPage
|
||||||
|
}
|
||||||
|
|
||||||
var loading by mutableStateOf(false)
|
var loading by mutableStateOf(false)
|
||||||
private set
|
private set
|
||||||
|
|
||||||
@@ -72,13 +75,8 @@ class MainViewModel(app: Application) : AndroidViewModel(app), DIAware {
|
|||||||
|
|
||||||
var currentPage by mutableStateOf(1)
|
var currentPage by mutableStateOf(1)
|
||||||
|
|
||||||
private val totalItems = MutableLiveData<Int>()
|
var totalItems by mutableStateOf(0)
|
||||||
|
private set
|
||||||
val totalPages = Transformations.map(totalItems) {
|
|
||||||
val perPage = Preferences["per_page", "25"].toInt()
|
|
||||||
|
|
||||||
ceil(it / perPage.toDouble()).roundToInt()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setSourceAndReset(sourceName: String) {
|
fun setSourceAndReset(sourceName: String) {
|
||||||
source = sourceFactory(sourceName)
|
source = sourceFactory(sourceName)
|
||||||
@@ -112,8 +110,6 @@ class MainViewModel(app: Application) : AndroidViewModel(app), DIAware {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun query() {
|
fun query() {
|
||||||
val perPage = Preferences["per_page", "25"].toInt()
|
|
||||||
|
|
||||||
suggestionJob?.cancel()
|
suggestionJob?.cancel()
|
||||||
queryJob?.cancel()
|
queryJob?.cancel()
|
||||||
|
|
||||||
@@ -121,13 +117,19 @@ class MainViewModel(app: Application) : AndroidViewModel(app), DIAware {
|
|||||||
searchResults.clear()
|
searchResults.clear()
|
||||||
|
|
||||||
queryJob = viewModelScope.launch {
|
queryJob = viewModelScope.launch {
|
||||||
|
val resultsPerPage = resultsPerPage.first()
|
||||||
|
|
||||||
|
logger.info {
|
||||||
|
resultsPerPage.toString()
|
||||||
|
}
|
||||||
|
|
||||||
val (channel, count) = source.search(
|
val (channel, count) = source.search(
|
||||||
query,
|
query,
|
||||||
(currentPage - 1) * perPage until currentPage * perPage,
|
(currentPage - 1) * resultsPerPage until currentPage * resultsPerPage,
|
||||||
sortModeIndex
|
sortModeIndex
|
||||||
)
|
)
|
||||||
|
|
||||||
totalItems.postValue(count)
|
totalItems = count
|
||||||
|
|
||||||
for (result in channel) {
|
for (result in channel) {
|
||||||
yield()
|
yield()
|
||||||
@@ -139,15 +141,15 @@ class MainViewModel(app: Application) : AndroidViewModel(app), DIAware {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun random(callback: (ItemInfo) -> Unit) {
|
fun random(callback: (ItemInfo) -> Unit) {
|
||||||
if (totalItems.value!! == 0)
|
if (totalItems == 0)
|
||||||
return
|
return
|
||||||
|
|
||||||
val random = Random.Default.nextInt(totalItems.value!!)
|
val random = Random.Default.nextInt(totalItems)
|
||||||
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
source.search(
|
source.search(
|
||||||
query + Preferences["default_query", ""],
|
query,
|
||||||
random .. random,
|
random .. random,
|
||||||
sortModeIndex
|
sortModeIndex
|
||||||
).first.receive()
|
).first.receive()
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import androidx.compose.ui.graphics.ImageBitmap
|
|||||||
import androidx.compose.ui.graphics.asImageBitmap
|
import androidx.compose.ui.graphics.asImageBitmap
|
||||||
import androidx.compose.ui.graphics.toAndroidRect
|
import androidx.compose.ui.graphics.toAndroidRect
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.serialization.json.*
|
import kotlinx.serialization.json.*
|
||||||
import org.kodein.di.DIAware
|
import org.kodein.di.DIAware
|
||||||
import org.kodein.di.DirectDIAware
|
import org.kodein.di.DirectDIAware
|
||||||
|
|||||||
8
app/src/main/proto/settings.proto
Normal file
8
app/src/main/proto/settings.proto
Normal file
@@ -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];
|
||||||
|
}
|
||||||
@@ -13,9 +13,10 @@ buildscript {
|
|||||||
classpath("com.google.gms:google-services:4.3.10")
|
classpath("com.google.gms:google-services:4.3.10")
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
// in the individual module build.gradle files
|
// 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.firebase:perf-plugin:1.4.0")
|
||||||
classpath("com.google.android.gms:oss-licenses-plugin:0.10.4")
|
classpath("com.google.android.gms:oss-licenses-plugin:0.10.4")
|
||||||
|
classpath("com.google.protobuf:protobuf-gradle-plugin:0.8.18")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user