Compare commits

...

32 Commits

Author SHA1 Message Date
tom5079
1521bc1223 Downloader Bug fix
UI Optimized
Scroller autohide, track disable
2020-09-13 16:19:32 +09:00
tom5079
df3a478ef3 Bug fix 2020-09-12 20:22:34 +09:00
tom5079
974ddf69d5 Bug fix 2020-09-12 19:33:13 +09:00
tom5079
56a91268de Fixed fastscroll 2020-09-12 19:10:15 +09:00
tom5079
3dda2f9a1c Dependency update 2020-09-12 12:35:10 +09:00
tom5079
ed20456f9f VD Optimization 2020-09-12 12:17:02 +09:00
tom5079
281d4a0023 Adjusted Fastscroll UI
VD Optimized
2020-09-12 11:23:38 +09:00
tom5079
2170403662 Fixed Image UI 2020-09-12 10:32:23 +09:00
tom5079
b1c1e96135 Smol fix 2020-09-11 20:45:31 +09:00
tom5079
a8de1429c1 Bug fix 2020-09-11 19:53:49 +09:00
tom5079
3ba6cb81ae Bug fix 2020-09-11 19:40:56 +09:00
tom5079
acc85da80f Bug fix 2020-09-10 22:47:57 +09:00
tom5079
b53de8624d Bug fix 2020-09-10 22:45:27 +09:00
tom5079
6e2eeb29cc Bug fix 2020-09-10 21:41:57 +09:00
tom5079
62eb28ac01 Bug fix 2020-09-10 19:44:08 +09:00
tom5079
fd298529bf Memory usage optimization 2020-09-10 19:16:42 +09:00
tom5079
297ce506b1 Bug fix 2020-09-10 17:03:17 +09:00
tom5079
18c6954be3 Improved Suggestions
resolves #100
2020-09-10 16:50:26 +09:00
tom5079
cea3fb1e65 Bug fix 2020-09-09 16:58:01 +09:00
tom5079
7f274fd238 Added OSS Notice 2020-09-09 13:06:32 +09:00
tom5079
439a8e93ec App built 2020-09-09 11:25:53 +09:00
tom5079
83801feee9 Bug fix 2020-09-09 11:23:22 +09:00
tom5079
8a6860c96e Import cleanup 2020-09-09 09:29:33 +09:00
tom5079
5c959f2987 Bug fix 2020-09-09 09:29:33 +09:00
tom5079
4e4397287a Implemented fast scroll 2020-09-09 09:22:17 +09:00
tom5079
fe02abc9e8 Bug fix 2020-09-08 20:04:12 +09:00
tom5079
59347ab317 Bug fix 2020-09-08 19:16:15 +09:00
tom5079
f408a91176 Bug fix 2020-09-07 10:00:10 +09:00
tom5079
6f6956ce27 Fixed DownloadLocationDialogFragment keep showing up when any button is clicked 2020-09-06 17:19:18 +09:00
tom5079
4ecad8eccc Fixed migration 2020-09-05 18:31:53 +09:00
tom5079
486fbe46a0 Fixed migration 2020-09-05 18:11:20 +09:00
tom5079
1ddb636dd0 Fixed migration 2020-09-05 18:00:15 +09:00
100 changed files with 1322 additions and 1483 deletions

View File

@@ -56,5 +56,10 @@
<option name="name" value="MavenLocal" /> <option name="name" value="MavenLocal" />
<option name="url" value="file:/$USER_HOME$/.m2/repository/" /> <option name="url" value="file:/$USER_HOME$/.m2/repository/" />
</remote-repository> </remote-repository>
<remote-repository>
<option name="id" value="MavenLocal" />
<option name="name" value="MavenLocal" />
<option name="url" value="file:/$USER_HOME$/.m2/repository" />
</remote-repository>
</component> </component>
</project> </project>

View File

@@ -3,6 +3,7 @@ apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt' apply plugin: 'kotlin-kapt'
apply plugin: 'kotlin-android-extensions' apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlinx-serialization' apply plugin: 'kotlinx-serialization'
apply plugin: 'com.google.android.gms.oss-licenses-plugin'
if (file("google-services.json").exists() && file("src/debug/google-services.json").exists()) { if (file("google-services.json").exists() && file("src/debug/google-services.json").exists()) {
logger.lifecycle("Firebase Enabled") logger.lifecycle("Firebase Enabled")
@@ -14,13 +15,13 @@ if (file("google-services.json").exists() && file("src/debug/google-services.jso
} }
android { android {
compileSdkVersion 29 compileSdkVersion 30
defaultConfig { defaultConfig {
applicationId "xyz.quaver.pupil" applicationId "xyz.quaver.pupil"
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 29 targetSdkVersion 30
versionCode 57 versionCode 59
versionName "5.0-beta2" versionName "5.0.2"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
} }
@@ -60,10 +61,10 @@ android {
dependencies { dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar']) implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9" implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9"
implementation "org.jetbrains.kotlinx:kotlinx-serialization-core:1.0.0-RC-HOTFIX1" //implementation "org.jetbrains.kotlinx:kotlinx-serialization-core:1.0.0-RC"
implementation 'androidx.appcompat:appcompat:1.2.0' implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.1' implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
implementation 'androidx.preference:preference:1.1.1' implementation 'androidx.preference:preference:1.1.1'
@@ -76,8 +77,10 @@ dependencies {
implementation 'com.google.firebase:firebase-analytics:17.5.0' implementation 'com.google.firebase:firebase-analytics:17.5.0'
implementation 'com.google.firebase:firebase-crashlytics:17.2.1' implementation 'com.google.firebase:firebase-crashlytics:17.2.1'
implementation 'com.google.firebase:firebase-perf:19.0.8' implementation 'com.google.firebase:firebase-perf:19.0.8'
implementation 'com.google.android.gms:play-services-oss-licenses:17.0.0'
implementation 'com.github.arimorty:floatingsearchview:2.1.1' implementation 'com.github.arimorty:floatingsearchview:2.1.1'
implementation 'com.github.clans:fab:1.6.4' implementation 'com.github.clans:fab:1.6.4'
//implementation 'com.quiph.ui:recyclerviewfastscroller:0.2.1'
//noinspection GradleDependency //noinspection GradleDependency
implementation 'com.squareup.okhttp3:okhttp:3.12.12' implementation 'com.squareup.okhttp3:okhttp:3.12.12'
implementation 'com.github.bumptech.glide:glide:4.11.0' implementation 'com.github.bumptech.glide:glide:4.11.0'
@@ -96,10 +99,10 @@ dependencies {
implementation 'com.andrognito.patternlockview:patternlockview:1.0.0' implementation 'com.andrognito.patternlockview:patternlockview:1.0.0'
//implementation 'com.andrognito.pinlockview:pinlockview:2.1.0' //implementation 'com.andrognito.pinlockview:pinlockview:2.1.0'
implementation "ru.noties.markwon:core:3.1.0" implementation "ru.noties.markwon:core:3.1.0"
implementation ("xyz.quaver:libpupil:1.3") { implementation ("xyz.quaver:libpupil:1.6") {
exclude group: 'org.jetbrains.kotlinx', module: 'kotlinx-serialization-core-jvm' exclude group: 'org.jetbrains.kotlinx', module: 'kotlinx-serialization-core-jvm'
} }
implementation "xyz.quaver:documentfilex:0.2.14-alpha2" implementation "xyz.quaver:documentfilex:0.2.15"
testImplementation 'junit:junit:4.13' testImplementation 'junit:junit:4.13'
androidTestImplementation 'androidx.test.ext:junit:1.1.2' androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test:rules:1.3.0' androidTestImplementation 'androidx.test:rules:1.3.0'

Binary file not shown.

Binary file not shown.

View File

@@ -11,8 +11,8 @@
"type": "SINGLE", "type": "SINGLE",
"filters": [], "filters": [],
"properties": [], "properties": [],
"versionCode": 57, "versionCode": 59,
"versionName": "5.0-beta2", "versionName": "5.0.1-hotfix2",
"enabled": true, "enabled": true,
"outputFile": "app-release.apk" "outputFile": "app-release.apk"
} }

View File

@@ -20,26 +20,10 @@
package xyz.quaver.pupil package xyz.quaver.pupil
import android.util.Log
import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.rule.ActivityTestRule
import kotlinx.coroutines.runBlocking
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import xyz.quaver.hitomi.getGalleryIDsFromNozomi
import xyz.quaver.hitomi.getSuggestionsForQuery
import xyz.quaver.hiyobi.cookie
import xyz.quaver.hiyobi.createImgList
import xyz.quaver.hiyobi.getReader
import xyz.quaver.hiyobi.user_agent
import xyz.quaver.pupil.ui.LockActivity
import xyz.quaver.pupil.util.download.Cache
import xyz.quaver.pupil.util.download.DownloadWorker
import xyz.quaver.pupil.util.getDownloadDirectory
import java.io.InputStreamReader
import java.net.URL
import javax.net.ssl.HttpsURLConnection
/** /**
* Instrumented test, which will execute on an Android device. * Instrumented test, which will execute on an Android device.
@@ -54,77 +38,4 @@ class ExampleInstrumentedTest {
// Context of the app under test. // Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext val appContext = InstrumentationRegistry.getInstrumentation().targetContext
} }
@Test
fun checkCacheDir() {
val activityTestRule = ActivityTestRule(LockActivity::class.java)
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
Runtime.getRuntime().exec("du -hs " + getDownloadDirectory(appContext)).let {
InputStreamReader(it.inputStream).readLines().forEach { res ->
Log.i("PUPILD", res)
}
}
}
@Test
fun test_nozomi() {
val nozomi = getGalleryIDsFromNozomi(null, "index", "all")
Log.i("PUPILD", nozomi.size.toString())
}
@Test
fun test_doSearch() {
val reader = getReader( 1426382)
val data: ByteArray
with(URL(createImgList(1426382, reader)[0].path).openConnection() as HttpsURLConnection) {
setRequestProperty("User-Agent", user_agent)
setRequestProperty("Cookie", cookie)
data = inputStream.readBytes()
}
Log.d("Pupil", data.size.toString())
}
@Test
fun test_downloadWorker() {
val context = InstrumentationRegistry.getInstrumentation().targetContext
val galleryID = 515515
val worker = DownloadWorker.getInstance(context)
worker.queue.add(galleryID)
while(worker.progress.indexOfKey(galleryID) < 0 || worker.progress[galleryID] != null) {
Log.i("PUPILD", worker.progress[galleryID]?.joinToString(" ") ?: "null")
if (worker.progress[galleryID]?.all { it.isInfinite() } == true)
break
}
Log.i("PUPILD", "DONE!!")
}
@Test
fun test_getReaderOrNull() {
val context = InstrumentationRegistry.getInstrumentation().targetContext
val galleryID = 1561552
runBlocking {
Log.i("PUPILD", Cache(context).getReader(galleryID)?.galleryInfo?.title ?: "null")
}
Log.i("PUPILD", Cache(context).getReaderOrNull(galleryID)?.galleryInfo?.title ?: "null")
}
@Test
fun test_suggestion() {
getSuggestionsForQuery("female:l")
}
} }

View File

@@ -19,13 +19,199 @@
package com.arlib.floatingsearchview package com.arlib.floatingsearchview
import android.content.Context import android.content.Context
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
import android.graphics.drawable.Animatable
import android.os.Parcelable import android.os.Parcelable
import android.text.Editable
import android.text.TextWatcher
import android.util.AttributeSet import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.view.inputmethod.EditorInfo
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import androidx.swiperefreshlayout.widget.CircularProgressDrawable
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat
import com.arlib.floatingsearchview.suggestions.SearchSuggestionsAdapter
import com.arlib.floatingsearchview.suggestions.model.SearchSuggestion
import com.arlib.floatingsearchview.util.view.SearchInputView
import xyz.quaver.pupil.R
import xyz.quaver.pupil.favoriteTags
import xyz.quaver.pupil.types.*
import java.util.*
class FloatingSearchViewDayNight @JvmOverloads constructor( class FloatingSearchViewDayNight @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
context: Context, FloatingSearchView(context, attrs),
attrs: AttributeSet? = null) FloatingSearchView.OnSearchListener,
: FloatingSearchView(context, attrs) { SearchSuggestionsAdapter.OnBindSuggestionCallback,
TextWatcher
{
private val searchInputView = findViewById<SearchInputView>(R.id.search_bar_text)
var onHistoryDeleteClickedListener: ((String) -> Unit)? = null
var onFavoriteHistorySwitchClickListener: (() -> Unit)? = null
init {
searchInputView.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI
searchInputView.addTextChangedListener(this)
setOnSearchListener(this)
setOnBindSuggestionCallback(this)
}
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
override fun afterTextChanged(s: Editable?) {
s ?: return
if (s.any { it.isUpperCase() })
s.replace(0, s.length, s.toString().toLowerCase(Locale.getDefault()))
}
override fun onSuggestionClicked(searchSuggestion: SearchSuggestion?) {
when (searchSuggestion) {
is TagSuggestion -> {
with(searchInputView.text) {
delete(if (lastIndexOf(' ') == -1) 0 else lastIndexOf(' ')+1, length)
append("${searchSuggestion.n}:${searchSuggestion.s.replace(Regex("\\s"), "_")} ")
}
}
is Suggestion -> {
with(searchInputView.text) {
clear()
append(searchSuggestion.str)
}
}
is FavoriteHistorySwitch -> onFavoriteHistorySwitchClickListener?.invoke()
}
}
override fun onSearchAction(currentQuery: String?) {}
override fun onBindSuggestion(
suggestionView: View?,
leftIcon: ImageView?,
textView: TextView?,
item: SearchSuggestion?,
itemPosition: Int
) {
when(item) {
is TagSuggestion -> {
val tag = "${item.n}:${item.s.replace(Regex("\\s"), "_")}"
leftIcon?.setImageDrawable(
ResourcesCompat.getDrawable(
resources,
when(item.n) {
"female" -> R.drawable.gender_female
"male" -> R.drawable.gender_male
"language" -> R.drawable.translate
"group" -> R.drawable.account_group
"character" -> R.drawable.account_star
"series" -> R.drawable.book_open
"artist" -> R.drawable.brush
else -> R.drawable.tag
},
context.theme)
)
with(suggestionView?.findViewById<ImageView>(R.id.right_icon)) {
this ?: return@with
if (favoriteTags.contains(Tag.parse(tag)))
setImageResource(R.drawable.ic_star_filled)
else
setImageResource(R.drawable.ic_star_empty)
visibility = View.VISIBLE
rotation = 0f
isEnabled = true
isClickable = true
setOnClickListener {
val tag = Tag.parse(tag)
if (favoriteTags.contains(tag)) {
setImageResource(R.drawable.ic_star_empty)
favoriteTags.remove(tag)
}
else {
setImageDrawable(
AnimatedVectorDrawableCompat.create(context,
R.drawable.avd_star
))
(drawable as Animatable).start()
favoriteTags.add(tag)
}
}
}
if (item.t == -1) {
textView?.text = item.s
} else {
(suggestionView as? LinearLayout)?.let {
val count = it.findViewById<TextView>(R.id.count)
if (count == null)
it.addView(
LayoutInflater.from(context).inflate(R.layout.suggestion_count, suggestionView, false)
.apply {
this as TextView
text = item.t.toString()
}, 2
)
else
count.text = item.t.toString()
}
}
}
is FavoriteHistorySwitch -> {
leftIcon?.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.swap_horizontal, context.theme))
}
is Suggestion -> {
leftIcon?.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.history, context.theme))
with(suggestionView?.findViewById<ImageView>(R.id.right_icon)) {
this ?: return@with
setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.delete, context.theme))
visibility = View.VISIBLE
rotation = 0f
isEnabled = true
isClickable = true
setOnClickListener {
onHistoryDeleteClickedListener?.invoke(item.str)
}
}
}
is LoadingSuggestion -> {
leftIcon?.setImageDrawable(CircularProgressDrawable(context).also {
it.setStyle(CircularProgressDrawable.DEFAULT)
it.colorFilter = PorterDuffColorFilter(ContextCompat.getColor(context, R.color.colorAccent), PorterDuff.Mode.SRC_IN)
it.start()
})
}
is NoResultSuggestion -> {
leftIcon?.setImageDrawable(ResourcesCompat.getDrawable(resources, R.drawable.close, context.theme))
}
}
}
// hack to remove color attributes which should not be reused // hack to remove color attributes which should not be reused
override fun onSaveInstanceState(): Parcelable? { override fun onSaveInstanceState(): Parcelable? {

View File

@@ -18,10 +18,7 @@
package xyz.quaver.pupil package xyz.quaver.pupil
import android.app.Application import android.app.*
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.net.Uri import android.net.Uri
@@ -41,7 +38,9 @@ import okhttp3.Interceptor
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Response import okhttp3.Response
import xyz.quaver.io.FileX import xyz.quaver.io.FileX
import xyz.quaver.pupil.types.Tag
import xyz.quaver.pupil.util.* import xyz.quaver.pupil.util.*
import xyz.quaver.pupil.util.downloader.DownloadManager
import xyz.quaver.setClient import xyz.quaver.setClient
import java.io.File import java.io.File
import java.util.* import java.util.*
@@ -50,9 +49,13 @@ import kotlin.reflect.KClass
typealias PupilInterceptor = (Interceptor.Chain) -> Response typealias PupilInterceptor = (Interceptor.Chain) -> Response
lateinit var histories: GalleryList lateinit var histories: SavedSet<Int>
private set private set
lateinit var favorites: GalleryList lateinit var favorites: SavedSet<Int>
private set
lateinit var favoriteTags: SavedSet<Tag>
private set
lateinit var searchHistory: SavedSet<String>
private set private set
val interceptors = mutableMapOf<KClass<out Any>, PupilInterceptor>() val interceptors = mutableMapOf<KClass<out Any>, PupilInterceptor>()
@@ -68,11 +71,9 @@ val client: OkHttpClient
class Pupil : Application() { class Pupil : Application() {
init {
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
}
override fun onCreate() { override fun onCreate() {
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
preferences = PreferenceManager.getDefaultSharedPreferences(this) preferences = PreferenceManager.getDefaultSharedPreferences(this)
val userID = Preferences["user_id", ""].let { userID -> val userID = Preferences["user_id", ""].let { userID ->
@@ -97,7 +98,7 @@ class Pupil : Application() {
try { try {
Preferences.get<String>("download_folder").also { Preferences.get<String>("download_folder").also {
if (Build.VERSION.SDK_INT > 19) if (it.startsWith("content") && Build.VERSION.SDK_INT > 19)
contentResolver.takePersistableUriPermission( contentResolver.takePersistableUriPermission(
Uri.parse(it), Uri.parse(it),
Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
@@ -105,13 +106,17 @@ class Pupil : Application() {
if (!FileX(this, it).canWrite()) if (!FileX(this, it).canWrite())
throw Exception() throw Exception()
DownloadManager.getInstance(this).migrate()
} }
} catch (e: Exception) { } catch (e: Exception) {
Preferences.remove("download_folder") Preferences.remove("download_folder")
} }
histories = GalleryList(File(ContextCompat.getDataDir(this), "histories.json")) histories = SavedSet(File(ContextCompat.getDataDir(this), "histories.json"), 0)
favorites = GalleryList(File(ContextCompat.getDataDir(this), "favorites.json")) favorites = SavedSet(File(ContextCompat.getDataDir(this), "favorites.json"), 0)
favoriteTags = SavedSet(File(ContextCompat.getDataDir(this), "favorites_tags.json"), Tag.parse(""))
searchHistory = SavedSet(File(ContextCompat.getDataDir(this), "search_histories.json"), "")
if (Preferences["new_history"]) { if (Preferences["new_history"]) {
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {

View File

@@ -19,33 +19,34 @@
package xyz.quaver.pupil.adapters package xyz.quaver.pupil.adapters
import android.content.Context import android.content.Context
import android.graphics.Color
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
import android.graphics.drawable.Drawable import android.graphics.drawable.Drawable
import android.util.Log
import android.util.SparseBooleanArray import android.util.SparseBooleanArray
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.LinearLayout import android.widget.LinearLayout
import androidx.cardview.widget.CardView import androidx.cardview.widget.CardView
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.CircularProgressDrawable import androidx.swiperefreshlayout.widget.CircularProgressDrawable
import androidx.vectordrawable.graphics.drawable.Animatable2Compat import androidx.vectordrawable.graphics.drawable.Animatable2Compat
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat
import com.bumptech.glide.RequestManager import com.bumptech.glide.RequestManager
import com.bumptech.glide.load.DataSource
import com.bumptech.glide.load.engine.DiskCacheStrategy import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.bumptech.glide.load.engine.GlideException
import com.bumptech.glide.request.RequestListener
import com.bumptech.glide.request.target.Target
import com.daimajia.swipe.SwipeLayout import com.daimajia.swipe.SwipeLayout
import com.daimajia.swipe.adapters.RecyclerSwipeAdapter import com.daimajia.swipe.adapters.RecyclerSwipeAdapter
import com.daimajia.swipe.interfaces.SwipeAdapterInterface import com.daimajia.swipe.interfaces.SwipeAdapterInterface
import com.google.android.material.chip.Chip
import kotlinx.android.synthetic.main.item_galleryblock.view.* import kotlinx.android.synthetic.main.item_galleryblock.view.*
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import xyz.quaver.hitomi.getReader import xyz.quaver.hitomi.getReader
import xyz.quaver.io.util.getChild
import xyz.quaver.pupil.BuildConfig import xyz.quaver.pupil.BuildConfig
import xyz.quaver.pupil.R import xyz.quaver.pupil.R
import xyz.quaver.pupil.favorites import xyz.quaver.pupil.favorites
@@ -88,11 +89,10 @@ class GalleryBlockAdapter(private val glide: RequestManager, private val galleri
val imageList = cache.metadata.imageList!! val imageList = cache.metadata.imageList!!
progress = imageList.filterNotNull().size progress = imageList.filterNotNull().size
max = imageList.size
if (visibility == View.GONE) { if (visibility == View.GONE)
visibility = View.VISIBLE visibility = View.VISIBLE
max = imageList.size
}
if (progress == max) { if (progress == max) {
val downloadManager = DownloadManager.getInstance(context) val downloadManager = DownloadManager.getInstance(context)
@@ -126,6 +126,8 @@ class GalleryBlockAdapter(private val glide: RequestManager, private val galleri
} }
fun bind(galleryID: Int) { fun bind(galleryID: Int) {
val time = System.currentTimeMillis()
val cache = Cache.getInstance(view.context, galleryID) val cache = Cache.getInstance(view.context, galleryID)
val galleryBlock = cache.metadata.galleryBlock ?: return val galleryBlock = cache.metadata.galleryBlock ?: return
@@ -158,6 +160,28 @@ class GalleryBlockAdapter(private val glide: RequestManager, private val galleri
.skipMemoryCache(true) .skipMemoryCache(true)
.diskCacheStrategy(DiskCacheStrategy.NONE) .diskCacheStrategy(DiskCacheStrategy.NONE)
.error(R.drawable.image_broken_variant) .error(R.drawable.image_broken_variant)
.listener(object: RequestListener<Drawable> {
override fun onLoadFailed(
e: GlideException?,
model: Any?,
target: Target<Drawable>?,
isFirstResource: Boolean
): Boolean {
Cache.getInstance(context, galleryID).let {
it.cacheFolder.getChild(".thumbnail").let { if (it.exists()) it.delete() }
it.downloadFolder?.getChild(".thumbnail")?.let { if (it.exists()) it.delete() }
}
return false
}
override fun onResourceReady(
resource: Drawable?,
model: Any?,
target: Target<Drawable>?,
dataSource: DataSource?,
isFirstResource: Boolean
): Boolean = false
})
.apply { .apply {
if (BuildConfig.CENSOR) if (BuildConfig.CENSOR)
override(5, 8) override(5, 8)
@@ -198,13 +222,15 @@ class GalleryBlockAdapter(private val glide: RequestManager, private val galleri
} }
galleryblock_tag_group.removeAllViews() galleryblock_tag_group.removeAllViews()
galleryBlock.relatedTags.forEach { CoroutineScope(Dispatchers.Default).launch {
galleryblock_tag_group.addView(TagChip(context, Tag.parse(it)).apply { galleryBlock.relatedTags.forEach {
setOnClickListener { view -> TagChip(context, Tag.parse(it)).apply {
for (callback in onChipClickedHandler) setOnClickListener { view ->
callback.invoke((view as TagChip).tag) for (callback in onChipClickedHandler)
} callback.invoke((view as TagChip).tag)
}) }
}.let { launch(Dispatchers.Main) { galleryblock_tag_group.addView(it) } }
}
} }
galleryblock_id.text = galleryBlock.id.toString() galleryblock_id.text = galleryBlock.id.toString()
@@ -253,6 +279,7 @@ class GalleryBlockAdapter(private val glide: RequestManager, private val galleri
galleryblock_tag_group.visibility = View.GONE galleryblock_tag_group.visibility = View.GONE
} }
} }
Log.i("PUPILD", "${System.currentTimeMillis() - time}")
} }
} }
class NextViewHolder(view: LinearLayout) : RecyclerView.ViewHolder(view) class NextViewHolder(view: LinearLayout) : RecyclerView.ViewHolder(view)

View File

@@ -40,10 +40,8 @@ import xyz.quaver.Code
import xyz.quaver.hitomi.Reader import xyz.quaver.hitomi.Reader
import xyz.quaver.hitomi.getReferer import xyz.quaver.hitomi.getReferer
import xyz.quaver.hitomi.imageUrlFromImage import xyz.quaver.hitomi.imageUrlFromImage
import xyz.quaver.hiyobi.cookie
import xyz.quaver.hiyobi.createImgList import xyz.quaver.hiyobi.createImgList
import xyz.quaver.hiyobi.user_agent import xyz.quaver.pupil.BuildConfig
import xyz.quaver.io.util.readBytes
import xyz.quaver.pupil.R import xyz.quaver.pupil.R
import xyz.quaver.pupil.services.DownloadService import xyz.quaver.pupil.services.DownloadService
import xyz.quaver.pupil.ui.ReaderActivity import xyz.quaver.pupil.ui.ReaderActivity
@@ -83,21 +81,19 @@ class ReaderAdapter(private val activity: ReaderActivity,
cache = Cache.getInstance(holder.view.context, galleryID) cache = Cache.getInstance(holder.view.context, galleryID)
if (isFullScreen) { if (isFullScreen) {
holder.view.layoutParams.height = RecyclerView.LayoutParams.MATCH_PARENT holder.view.layoutParams.height = ConstraintLayout.LayoutParams.MATCH_PARENT
holder.view.container.layoutParams.height = ConstraintLayout.LayoutParams.MATCH_PARENT
} else { } else {
holder.view.layoutParams.height = RecyclerView.LayoutParams.WRAP_CONTENT holder.view.layoutParams.height = ConstraintLayout.LayoutParams.WRAP_CONTENT
holder.view.container.layoutParams.height = 0
(holder.view.container.layoutParams as ConstraintLayout.LayoutParams) (holder.view.progress_layout.layoutParams as ConstraintLayout.LayoutParams)
.dimensionRatio = "W,${reader!!.galleryInfo.files[position].width}:${reader!!.galleryInfo.files[position].height}" .dimensionRatio = "${reader!!.galleryInfo.files[position].width}:${reader!!.galleryInfo.files[position].height}"
} }
holder.view.image.setOnPhotoTapListener { _, _, _ -> holder.view.image.setOnPhotoTapListener { _, _, _ ->
onItemClickListener?.invoke(position) onItemClickListener?.invoke(position)
} }
holder.view.container.setOnClickListener { holder.view.setOnClickListener {
onItemClickListener?.invoke(position) onItemClickListener?.invoke(position)
} }
@@ -116,10 +112,7 @@ class ReaderAdapter(private val activity: ReaderActivity,
) )
, LazyHeaders.Builder().addHeader("Referer", getReferer(galleryID)).build()) , LazyHeaders.Builder().addHeader("Referer", getReferer(galleryID)).build())
Code.HIYOBI -> Code.HIYOBI ->
GlideUrl(createImgList(galleryID, reader!!, lowQuality)[position].path, LazyHeaders.Builder() GlideUrl(createImgList(galleryID, reader!!, lowQuality)[position].path)
.addHeader("User-Agent", user_agent)
.addHeader("Cookie", cookie)
.build())
else -> null else -> null
} }
holder.view.image.post { holder.view.image.post {
@@ -127,7 +120,16 @@ class ReaderAdapter(private val activity: ReaderActivity,
.load(url!!) .load(url!!)
.diskCacheStrategy(DiskCacheStrategy.NONE) .diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(false) .skipMemoryCache(false)
.fitCenter() .error(R.drawable.image_broken_variant)
.apply {
if (BuildConfig.CENSOR)
override(5, 8)
else
override(
holder.view.context.resources.displayMetrics.widthPixels,
holder.view.context.resources.getDimensionPixelSize(R.dimen.reader_max_height)
)
}
.error(R.drawable.image_broken_variant) .error(R.drawable.image_broken_variant)
.into(holder.view.image) .into(holder.view.image)
} }
@@ -140,10 +142,18 @@ class ReaderAdapter(private val activity: ReaderActivity,
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
glide glide
.load(image.readBytes()) .load(image.uri)
.diskCacheStrategy(DiskCacheStrategy.NONE) .diskCacheStrategy(DiskCacheStrategy.NONE)
.skipMemoryCache(true) .skipMemoryCache(true)
.fitCenter() .apply {
if (BuildConfig.CENSOR)
override(5, 8)
else
override(
holder.view.context.resources.displayMetrics.widthPixels,
holder.view.context.resources.getDimensionPixelSize(R.dimen.reader_max_height)
)
}
.error(R.drawable.image_broken_variant) .error(R.drawable.image_broken_variant)
.listener(object: RequestListener<Drawable> { .listener(object: RequestListener<Drawable> {
override fun onLoadFailed( override fun onLoadFailed(
@@ -159,8 +169,13 @@ class ReaderAdapter(private val activity: ReaderActivity,
return true return true
} }
override fun onResourceReady(resource: Drawable?, model: Any?, target: Target<Drawable>?, dataSource: DataSource?, isFirstResource: Boolean) = override fun onResourceReady(
false resource: Drawable?,
model: Any?,
target: Target<Drawable>?,
dataSource: DataSource?,
isFirstResource: Boolean
) = false
}).let { launch(Dispatchers.Main) { it.into(holder.view.image) } } }).let { launch(Dispatchers.Main) { it.into(holder.view.image) } }
} }
} else { } else {

View File

@@ -27,6 +27,7 @@ import android.util.SparseArray
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
import androidx.core.app.TaskStackBuilder import androidx.core.app.TaskStackBuilder
import androidx.core.content.ContextCompat
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
@@ -46,7 +47,6 @@ import xyz.quaver.pupil.util.downloader.DownloadManager
import xyz.quaver.pupil.util.ellipsize import xyz.quaver.pupil.util.ellipsize
import xyz.quaver.pupil.util.normalizeID import xyz.quaver.pupil.util.normalizeID
import xyz.quaver.pupil.util.requestBuilders import xyz.quaver.pupil.util.requestBuilders
import xyz.quaver.pupil.util.startForegroundServiceCompat
import java.io.IOException import java.io.IOException
private typealias ProgressListener = (DownloadService.Tag, Long, Long, Boolean) -> Unit private typealias ProgressListener = (DownloadService.Tag, Long, Long, Boolean) -> Unit
@@ -215,7 +215,7 @@ class DownloadService : Service() {
val ext = call.request().url().encodedPath().split('.').last() val ext = call.request().url().encodedPath().split('.').last()
kotlin.runCatching { kotlin.runCatching {
val image = response.body()?.use { it.bytes() } ?: throw Exception() val image = response.also { if (it.code() != 200) throw IOException() }.body()?.use { it.bytes() } ?: throw Exception()
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
kotlin.runCatching { kotlin.runCatching {
@@ -307,11 +307,30 @@ class DownloadService : Service() {
return@launch return@launch
} }
if (progress.indexOfKey(galleryID) < 0) progress.put(galleryID, MutableList(reader.galleryInfo.files.size) { 0F })
progress.put(galleryID, MutableList(reader.galleryInfo.files.size) { 0F })
cache.metadata.imageList?.forEachIndexed { index, image -> cache.metadata.imageList?.let {
progress[galleryID]?.set(index, if (image != null) Float.POSITIVE_INFINITY else 0F) if (progress[galleryID]?.size != it.size) {
cache.metadata.imageList?.filterNotNull()?.forEach { file ->
cache.findFile(file)?.delete()
}
cache.metadata.imageList = MutableList(reader.galleryInfo.files.size) { null }
return@let
}
it.forEachIndexed { index, image ->
progress[galleryID]?.set(index, if (image != null) Float.POSITIVE_INFINITY else 0F)
}
}
if (isCompleted(galleryID)) {
if (DownloadManager.getInstance(this@DownloadService)
.getDownloadFolder(galleryID) != null)
Cache.getInstance(this@DownloadService, galleryID).moveToDownload()
notificationManager.cancel(galleryID)
startId?.let { stopSelf(it) }
return@launch
} }
notification[galleryID]?.setContentTitle(reader.galleryInfo.title?.ellipsize(30)) notification[galleryID]?.setContentTitle(reader.galleryInfo.title?.ellipsize(30))
@@ -328,9 +347,11 @@ class DownloadService : Service() {
} }
} }
reader.requestBuilders.filterIndexed { index, _ -> progress[galleryID]?.get(index)?.isInfinite() != true }.forEachIndexed { index, it -> reader.requestBuilders.forEachIndexed { index, it ->
val request = it.tag(Tag(galleryID, index, startId)).build() if (progress[galleryID]?.get(index)?.isInfinite() != true) {
client.newCall(request).enqueue(callback) val request = it.tag(Tag(galleryID, index, startId)).build()
client.newCall(request).enqueue(callback)
}
} }
queued.forEach { download(it) } queued.forEach { download(it) }
@@ -347,7 +368,7 @@ class DownloadService : Service() {
const val COMMAND_DELETE = "DELETE" const val COMMAND_DELETE = "DELETE"
private fun command(context: Context, extras: Intent.() -> Unit) { private fun command(context: Context, extras: Intent.() -> Unit) {
context.startForegroundServiceCompat(Intent(context, DownloadService::class.java).apply(extras)) ContextCompat.startForegroundService(context, Intent(context, DownloadService::class.java).apply(extras))
} }
fun download(context: Context, galleryID: Int, priority: Boolean = false) { fun download(context: Context, galleryID: Int, priority: Boolean = false) {
@@ -374,6 +395,8 @@ class DownloadService : Service() {
} }
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
startForeground(R.id.downloader_notification_id, serviceNotification.build())
when (intent?.getStringExtra(KEY_COMMAND)) { when (intent?.getStringExtra(KEY_COMMAND)) {
COMMAND_DOWNLOAD -> intent.getIntExtra(KEY_ID, -1).let { if (it > 0) COMMAND_DOWNLOAD -> intent.getIntExtra(KEY_ID, -1).let { if (it > 0)
download(it, intent.getBooleanExtra(KEY_PRIORITY, false), startId) download(it, intent.getBooleanExtra(KEY_PRIORITY, false), startId)

View File

@@ -30,3 +30,24 @@ data class TagSuggestion(val s: String, val t: Int, val u: String, val n: String
return s return s
} }
} }
@Parcelize
class Suggestion(val str: String) : SearchSuggestion {
override fun getBody() = str
}
@Parcelize
class NoResultSuggestion(val str: String) : SearchSuggestion {
override fun getBody() = str
}
@Parcelize
class LoadingSuggestion(val str: String) : SearchSuggestion {
override fun getBody() = str
}
@Parcelize
@Suppress("PARCELABLE_PRIMARY_CONSTRUCTOR_IS_EMPTY")
class FavoriteHistorySwitch(private val body: String) : SearchSuggestion {
override fun getBody() = body
}

View File

@@ -19,13 +19,12 @@
package xyz.quaver.pupil.types package xyz.quaver.pupil.types
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.Transient
@Serializable @Serializable
data class Tag(val area: String?, val tag: String, val isNegative: Boolean = false) { data class Tag(val area: String?, val tag: String, val isNegative: Boolean = false) {
companion object { companion object {
fun parse(tag: String) : Tag { fun parse(tag: String) : Tag {
if (tag.first() == '-') { if (tag.firstOrNull() == '-') {
tag.substring(1).split(Regex(":"), 2).let { tag.substring(1).split(Regex(":"), 2).let {
return when(it.size) { return when(it.size) {
2 -> Tag(it[0], it[1], true) 2 -> Tag(it[0], it[1], true)
@@ -63,9 +62,7 @@ data class Tag(val area: String?, val tag: String, val isNegative: Boolean = fal
return false return false
} }
override fun hashCode(): Int { override fun hashCode() = toString().hashCode()
return super.hashCode()
}
} }
class Tags(val tags: MutableSet<Tag> = mutableSetOf()) : MutableSet<Tag> by tags { class Tags(val tags: MutableSet<Tag> = mutableSetOf()) : MutableSet<Tag> by tags {
@@ -111,7 +108,4 @@ class Tags(val tags: MutableSet<Tag> = mutableSetOf()) : MutableSet<Tag> by tags
override fun toString(): String { override fun toString(): String {
return tags.joinToString(" ") { it.toString() } return tags.joinToString(" ") { it.toString() }
} }
} }

View File

@@ -0,0 +1,72 @@
/*
* Pupil, Hitomi.la viewer for Android
* Copyright (C) 2020 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 <http://www.gnu.org/licenses/>.
*/
package xyz.quaver.pupil.ui
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.os.PersistableBundle
import android.view.WindowManager
import androidx.annotation.CallSuper
import androidx.appcompat.app.AppCompatActivity
import xyz.quaver.pupil.R
import xyz.quaver.pupil.util.LockManager
import xyz.quaver.pupil.util.Preferences
import xyz.quaver.pupil.util.normalizeID
open class BaseActivity : AppCompatActivity() {
private var locked: Boolean = true
@CallSuper
override fun onCreate(savedInstanceState: Bundle?, persistentState: PersistableBundle?) {
super.onCreate(savedInstanceState, persistentState)
locked = !LockManager(this).locks.isNullOrEmpty()
}
@CallSuper
override fun onResume() {
super.onResume()
if (Preferences["security_mode"])
window.setFlags(
WindowManager.LayoutParams.FLAG_SECURE,
WindowManager.LayoutParams.FLAG_SECURE)
else
window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
if (locked)
startActivityForResult(Intent(this, LockActivity::class.java), R.id.request_lock.normalizeID())
}
@CallSuper
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when(requestCode) {
R.id.request_lock.normalizeID() -> {
if (resultCode == Activity.RESULT_OK)
locked = false
else
finish()
}
else -> super.onActivityResult(requestCode, resultCode, data)
}
}
}

View File

@@ -39,6 +39,7 @@ import xyz.quaver.pupil.util.Lock
import xyz.quaver.pupil.util.LockManager import xyz.quaver.pupil.util.LockManager
import xyz.quaver.pupil.util.Preferences import xyz.quaver.pupil.util.Preferences
private var lastUnlocked = 0L
class LockActivity : AppCompatActivity() { class LockActivity : AppCompatActivity() {
private lateinit var lockManager: LockManager private lateinit var lockManager: LockManager
@@ -52,6 +53,7 @@ class LockActivity : AppCompatActivity() {
val result = lockManager.check(it) val result = lockManager.check(it)
if (result == true) { if (result == true) {
lastUnlocked = System.currentTimeMillis()
setResult(Activity.RESULT_OK) setResult(Activity.RESULT_OK)
finish() finish()
} else } else
@@ -86,6 +88,7 @@ class LockActivity : AppCompatActivity() {
val result = lockManager.check(it) val result = lockManager.check(it)
if (result == true) { if (result == true) {
lastUnlocked = System.currentTimeMillis()
setResult(Activity.RESULT_OK) setResult(Activity.RESULT_OK)
finish() finish()
} else { } else {
@@ -157,6 +160,7 @@ class LockActivity : AppCompatActivity() {
override fun onAuthenticationSucceeded( override fun onAuthenticationSucceeded(
result: BiometricPrompt.AuthenticationResult) { result: BiometricPrompt.AuthenticationResult) {
super.onAuthenticationSucceeded(result) super.onAuthenticationSucceeded(result)
lastUnlocked = System.currentTimeMillis()
setResult(RESULT_OK) setResult(RESULT_OK)
finish() finish()
return return
@@ -185,6 +189,7 @@ class LockActivity : AppCompatActivity() {
} }
mode = intent.getStringExtra("mode") mode = intent.getStringExtra("mode")
val force = intent.getBooleanExtra("force", false)
when(mode) { when(mode) {
null -> { null -> {
@@ -194,6 +199,13 @@ class LockActivity : AppCompatActivity() {
return return
} }
if (System.currentTimeMillis() - lastUnlocked < 5*60*1000 && !force) {
lastUnlocked = System.currentTimeMillis()
setResult(RESULT_OK)
finish()
return
}
if ( if (
Preferences["lock_fingerprint"] Preferences["lock_fingerprint"]
&& BiometricManager.from(this).canAuthenticate() == BiometricManager.BIOMETRIC_SUCCESS && BiometricManager.from(this).canAuthenticate() == BiometricManager.BIOMETRIC_SUCCESS

View File

@@ -19,65 +19,50 @@
package xyz.quaver.pupil.ui package xyz.quaver.pupil.ui
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.Activity
import android.content.Intent import android.content.Intent
import android.graphics.drawable.Animatable
import android.net.Uri import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.text.* import android.text.InputType
import android.text.style.AlignmentSpan import android.view.*
import android.util.TypedValue
import android.view.KeyEvent
import android.view.MotionEvent
import android.view.View
import android.view.WindowManager
import android.view.inputmethod.EditorInfo
import android.widget.* import android.widget.*
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatDelegate
import androidx.cardview.widget.CardView import androidx.cardview.widget.CardView
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import androidx.core.view.GravityCompat import androidx.core.view.GravityCompat
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat
import com.arlib.floatingsearchview.FloatingSearchView import com.arlib.floatingsearchview.FloatingSearchView
import com.arlib.floatingsearchview.FloatingSearchViewDayNight import com.arlib.floatingsearchview.FloatingSearchViewDayNight
import com.arlib.floatingsearchview.suggestions.model.SearchSuggestion import com.arlib.floatingsearchview.suggestions.model.SearchSuggestion
import com.arlib.floatingsearchview.util.view.SearchInputView import com.arlib.floatingsearchview.util.view.SearchInputView
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.google.android.material.appbar.AppBarLayout import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.navigation.NavigationView
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.google.firebase.crashlytics.FirebaseCrashlytics import com.google.firebase.crashlytics.FirebaseCrashlytics
import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.activity_main_content.* import kotlinx.android.synthetic.main.activity_main_content.*
import kotlinx.coroutines.* import kotlinx.coroutines.*
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import xyz.quaver.hitomi.doSearch import xyz.quaver.hitomi.doSearch
import xyz.quaver.hitomi.getGalleryIDsFromNozomi import xyz.quaver.hitomi.getGalleryIDsFromNozomi
import xyz.quaver.hitomi.getSuggestionsForQuery import xyz.quaver.hitomi.getSuggestionsForQuery
import xyz.quaver.pupil.R import xyz.quaver.pupil.*
import xyz.quaver.pupil.adapters.GalleryBlockAdapter import xyz.quaver.pupil.adapters.GalleryBlockAdapter
import xyz.quaver.pupil.favorites
import xyz.quaver.pupil.histories
import xyz.quaver.pupil.services.DownloadService import xyz.quaver.pupil.services.DownloadService
import xyz.quaver.pupil.types.TagSuggestion import xyz.quaver.pupil.types.*
import xyz.quaver.pupil.types.Tags
import xyz.quaver.pupil.ui.dialog.DownloadLocationDialogFragment import xyz.quaver.pupil.ui.dialog.DownloadLocationDialogFragment
import xyz.quaver.pupil.ui.dialog.GalleryDialog import xyz.quaver.pupil.ui.dialog.GalleryDialog
import xyz.quaver.pupil.util.* import xyz.quaver.pupil.util.*
import xyz.quaver.pupil.util.downloader.Cache import xyz.quaver.pupil.util.downloader.Cache
import xyz.quaver.pupil.util.downloader.DownloadManager import xyz.quaver.pupil.util.downloader.DownloadManager
import java.io.File
import java.util.*
import kotlin.collections.ArrayList
import kotlin.math.abs import kotlin.math.abs
import kotlin.math.ceil import kotlin.math.ceil
import kotlin.math.min import kotlin.math.min
import kotlin.math.roundToInt import kotlin.math.roundToInt
class MainActivity : AppCompatActivity() { class MainActivity :
BaseActivity(),
FloatingSearchView.OnMenuItemClickListener,
NavigationView.OnNavigationItemSelectedListener
{
enum class Mode { enum class Mode {
SEARCH, SEARCH,
@@ -113,28 +98,13 @@ class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val lockManager = try {
LockManager(this)
} catch (e: Exception) {
android.app.AlertDialog.Builder(this).apply {
setTitle(R.string.warning)
setMessage(R.string.lock_corrupted)
setPositiveButton(android.R.string.ok) { _, _ ->
finish()
}
}.show()
return
}
if (lockManager.isNotEmpty())
startActivityForResult(Intent(this, LockActivity::class.java), R.id.request_lock.normalizeID())
if (intent.action == Intent.ACTION_VIEW) { if (intent.action == Intent.ACTION_VIEW) {
intent.dataString?.let { url -> intent.dataString?.let { url ->
restore(favorites, url, restore(url,
onFailure = { onFailure = {
Snackbar.make(this.main_recyclerview, R.string.settings_backup_failed, Snackbar.LENGTH_LONG).show() Snackbar.make(this.main_recyclerview, R.string.settings_backup_failed, Snackbar.LENGTH_LONG).show()
}, onSuccess = { }, onSuccess = {
@@ -144,8 +114,6 @@ class MainActivity : AppCompatActivity() {
} }
} }
setContentView(R.layout.activity_main)
if (Preferences["download_folder", ""].isEmpty()) if (Preferences["download_folder", ""].isEmpty())
DownloadLocationDialogFragment().show(supportFragmentManager, "Download Location Dialog") DownloadLocationDialogFragment().show(supportFragmentManager, "Download Location Dialog")
@@ -176,17 +144,6 @@ class MainActivity : AppCompatActivity() {
(main_recyclerview?.adapter as? GalleryBlockAdapter)?.timer?.cancel() (main_recyclerview?.adapter as? GalleryBlockAdapter)?.timer?.cancel()
} }
override fun onResume() {
if (Preferences["security_mode"])
window.setFlags(
WindowManager.LayoutParams.FLAG_SECURE,
WindowManager.LayoutParams.FLAG_SECURE)
else
window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
super.onResume()
}
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
val perPage = Preferences["per_page", "25"].toInt() val perPage = Preferences["per_page", "25"].toInt()
val maxPage = ceil(totalItems / perPage.toDouble()).roundToInt() val maxPage = ceil(totalItems / perPage.toDouble()).roundToInt()
@@ -234,10 +191,6 @@ class MainActivity : AppCompatActivity() {
loadBlocks() loadBlocks()
} }
} }
R.id.request_lock.normalizeID() -> {
if (resultCode != Activity.RESULT_OK)
finish()
}
else -> super.onActivityResult(requestCode, resultCode, data) else -> super.onActivityResult(requestCode, resultCode, data)
} }
} }
@@ -261,71 +214,7 @@ class MainActivity : AppCompatActivity() {
) )
//NavigationView //NavigationView
main_nav_view.setNavigationItemSelectedListener { main_nav_view.setNavigationItemSelectedListener(this)
runOnUiThread {
main_drawer_layout.closeDrawers()
when(it.itemId) {
R.id.main_drawer_home -> {
cancelFetch()
clearGalleries()
currentPage = 0
query = ""
queryStack.clear()
mode = Mode.SEARCH
fetchGalleries(query, sortMode)
loadBlocks()
}
R.id.main_drawer_history -> {
cancelFetch()
clearGalleries()
currentPage = 0
query = ""
queryStack.clear()
mode = Mode.HISTORY
fetchGalleries(query, sortMode)
loadBlocks()
}
R.id.main_drawer_downloads -> {
cancelFetch()
clearGalleries()
currentPage = 0
query = ""
queryStack.clear()
mode = Mode.DOWNLOAD
fetchGalleries(query, sortMode)
loadBlocks()
}
R.id.main_drawer_favorite -> {
cancelFetch()
clearGalleries()
currentPage = 0
query = ""
queryStack.clear()
mode = Mode.FAVORITE
fetchGalleries(query, sortMode)
loadBlocks()
}
R.id.main_drawer_help -> {
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.help))))
}
R.id.main_drawer_github -> {
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.github))))
}
R.id.main_drawer_homepage -> {
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.home_page))))
}
R.id.main_drawer_email -> {
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.email))))
}
R.id.main_drawer_kakaotalk -> {
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.discord))))
}
}
}
true
}
with(main_fab_cancel) { with(main_fab_cancel) {
setImageResource(R.drawable.cancel) setImageResource(R.drawable.cancel)
@@ -720,36 +609,24 @@ class MainActivity : AppCompatActivity() {
} }
} }
private var isFavorite = false
private val defaultSuggestions: List<SearchSuggestion>
get() = when {
isFavorite -> {
favoriteTags.map {
TagSuggestion(it.tag, -1, "", it.area ?: "tag")
} + FavoriteHistorySwitch(getString(R.string.search_show_histories))
}
else -> {
searchHistory.map {
Suggestion(it)
}.takeLast(20) + FavoriteHistorySwitch(getString(R.string.search_show_tags))
}
}.reversed()
private var suggestionJob : Job? = null private var suggestionJob : Job? = null
private fun setupSearchBar() { private fun setupSearchBar() {
val searchInputView = findViewById<SearchInputView>(R.id.search_bar_text)
//Change upper case letters to lower case
searchInputView.addTextChangedListener(object: TextWatcher {
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
}
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
}
override fun afterTextChanged(s: Editable?) {
s ?: return
if (s.any { it.isUpperCase() })
s.replace(0, s.length, s.toString().toLowerCase(Locale.getDefault()))
}
})
searchInputView.imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI
with(main_searchview as FloatingSearchViewDayNight) { with(main_searchview as FloatingSearchViewDayNight) {
val favoritesFile = File(ContextCompat.getDataDir(context), "favorites_tags.json")
if (!favoritesFile.exists()) {
favoritesFile.createNewFile()
favoritesFile.writeText("[]")
}
setOnLeftMenuClickListener(object: FloatingSearchView.OnLeftMenuClickListener { setOnLeftMenuClickListener(object: FloatingSearchView.OnLeftMenuClickListener {
override fun onMenuOpened() { override fun onMenuOpened() {
(this@MainActivity.main_recyclerview.adapter as GalleryBlockAdapter).closeAllItems() (this@MainActivity.main_recyclerview.adapter as GalleryBlockAdapter).closeAllItems()
@@ -760,62 +637,30 @@ class MainActivity : AppCompatActivity() {
} }
}) })
setOnMenuItemClickListener { onHistoryDeleteClickedListener = {
when(it.itemId) { searchHistory.remove(it)
R.id.main_menu_settings -> startActivityForResult(Intent(this@MainActivity, SettingsActivity::class.java), R.id.request_settings.normalizeID()) swapSuggestions(defaultSuggestions)
R.id.main_menu_thin -> {
main_recyclerview.apply {
(adapter as GalleryBlockAdapter).apply {
isThin = !isThin
}
adapter = adapter // Force to redraw
}
}
R.id.main_menu_sort_newest -> {
sortMode = SortMode.NEWEST
it.isChecked = true
runOnUiThread {
currentPage = 0
cancelFetch()
clearGalleries()
fetchGalleries(query, sortMode)
loadBlocks()
}
}
R.id.main_menu_sort_popular -> {
sortMode = SortMode.POPULAR
it.isChecked = true
runOnUiThread {
currentPage = 0
cancelFetch()
clearGalleries()
fetchGalleries(query, sortMode)
loadBlocks()
}
}
}
} }
onFavoriteHistorySwitchClickListener = {
isFavorite = !isFavorite
swapSuggestions(defaultSuggestions)
}
setOnMenuItemClickListener(this@MainActivity)
setOnQueryChangeListener { _, query -> setOnQueryChangeListener { _, query ->
this@MainActivity.query = query this@MainActivity.query = query
suggestionJob?.cancel() suggestionJob?.cancel()
clearSuggestions()
if (query.isEmpty() or query.endsWith(' ')) { if (query.isEmpty() or query.endsWith(' ')) {
swapSuggestions(Tags(Json.decodeFromString(favoritesFile.readText())).map { swapSuggestions(defaultSuggestions)
TagSuggestion(it.tag, -1, "", it.area ?: "tag")
})
return@setOnQueryChangeListener return@setOnQueryChangeListener
} }
swapSuggestions(listOf(LoadingSuggestion(getText(R.string.reader_loading).toString())))
val currentQuery = query.split(" ").last().replace('_', ' ') val currentQuery = query.split(" ").last().replace('_', ' ')
suggestionJob = CoroutineScope(Dispatchers.IO).launch { suggestionJob = CoroutineScope(Dispatchers.IO).launch {
@@ -825,113 +670,22 @@ class MainActivity : AppCompatActivity() {
suggestions.filter { suggestions.filter {
val tag = "${it.n}:${it.s.replace(Regex("\\s"), "_")}" val tag = "${it.n}:${it.s.replace(Regex("\\s"), "_")}"
Tags(Json.decodeFromString(favoritesFile.readText())).contains(tag) favoriteTags.contains(Tag.parse(tag))
}.reversed().forEach { }.reversed().forEach {
suggestions.remove(it) suggestions.remove(it)
suggestions.add(0, it) suggestions.add(0, it)
} }
withContext(Dispatchers.Main) { withContext(Dispatchers.Main) {
swapSuggestions(suggestions) swapSuggestions(if (suggestions.isNotEmpty()) suggestions else listOf(NoResultSuggestion(getText(R.string.main_no_result).toString())))
} }
} }
} }
setOnBindSuggestionCallback { suggestionView, leftIcon, textView, item, _ ->
item as TagSuggestion
val tag = "${item.n}:${item.s.replace(Regex("\\s"), "_")}"
val color = TypedValue()
theme.resolveAttribute(R.attr.colorControlNormal, color, true)
leftIcon.setImageDrawable(
ResourcesCompat.getDrawable(
resources,
when(item.n) {
"female" -> R.drawable.gender_female
"male" -> R.drawable.gender_male
"language" -> R.drawable.translate
"group" -> R.drawable.account_group
"character" -> R.drawable.account_star
"series" -> R.drawable.book_open
"artist" -> R.drawable.brush
else -> R.drawable.tag
},
context.theme)
)
with(suggestionView.findViewById<ImageView>(R.id.right_icon)) {
if (Tags(Json.decodeFromString(favoritesFile.readText())).contains(tag))
setImageResource(R.drawable.ic_star_filled)
else
setImageResource(R.drawable.ic_star_empty)
visibility = View.VISIBLE
rotation = 0f
isEnabled = true
isClickable = true
setOnClickListener {
val favorites = Tags(Json.decodeFromString(favoritesFile.readText()))
if (favorites.contains(tag)) {
setImageResource(R.drawable.ic_star_empty)
favorites.remove(tag)
}
else {
setImageDrawable(AnimatedVectorDrawableCompat.create(context,
R.drawable.avd_star
))
(drawable as Animatable).start()
favorites.add(tag)
}
favoritesFile.writeText(Json.encodeToString(favorites.tags))
}
}
if (item.t == -1) {
textView.text = item.s
} else {
val text = "${item.s}\n ${item.t}"
val len = text.length
val left = item.s.length
textView.text = SpannableString(text).apply {
val s = AlignmentSpan.Standard(Layout.Alignment.ALIGN_OPPOSITE)
setSpan(s, left, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
setSpan(SetLineOverlap(true), 1, len-2, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
setSpan(SetLineOverlap(false), len-1, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
}
}
}
setOnSearchListener(object : FloatingSearchView.OnSearchListener {
override fun onSuggestionClicked(searchSuggestion: SearchSuggestion?) {
if (searchSuggestion !is TagSuggestion)
return
with(searchInputView.text) {
delete(if (lastIndexOf(' ') == -1) 0 else lastIndexOf(' ')+1, length)
append("${searchSuggestion.n}:${searchSuggestion.s.replace(Regex("\\s"), "_")} ")
}
}
override fun onSearchAction(currentQuery: String?) {
//Do search on onFocusCleared()
}
})
setOnFocusChangeListener(object: FloatingSearchView.OnFocusChangeListener { setOnFocusChangeListener(object: FloatingSearchView.OnFocusChangeListener {
override fun onFocus() { override fun onFocus() {
if (query.isEmpty() or query.endsWith(' ')) if (query.isEmpty() or query.endsWith(' '))
swapSuggestions(Tags(Json.decodeFromString(favoritesFile.readText())).map { swapSuggestions(defaultSuggestions)
TagSuggestion(it.tag, -1, "", it.area ?: "tag")
})
} }
override fun onFocusCleared() { override fun onFocusCleared() {
@@ -951,6 +705,113 @@ class MainActivity : AppCompatActivity() {
} }
} }
override fun onActionMenuItemSelected(item: MenuItem?) {
when(item?.itemId) {
R.id.main_menu_settings -> startActivityForResult(Intent(this@MainActivity, SettingsActivity::class.java), R.id.request_settings.normalizeID())
R.id.main_menu_thin -> {
main_recyclerview.apply {
(adapter as GalleryBlockAdapter).apply {
isThin = !isThin
}
adapter = adapter // Force to redraw
}
}
R.id.main_menu_sort_newest -> {
sortMode = SortMode.NEWEST
item.isChecked = true
runOnUiThread {
currentPage = 0
cancelFetch()
clearGalleries()
fetchGalleries(query, sortMode)
loadBlocks()
}
}
R.id.main_menu_sort_popular -> {
sortMode = SortMode.POPULAR
item.isChecked = true
runOnUiThread {
currentPage = 0
cancelFetch()
clearGalleries()
fetchGalleries(query, sortMode)
loadBlocks()
}
}
}
}
override fun onNavigationItemSelected(item: MenuItem): Boolean {
runOnUiThread {
main_drawer_layout.closeDrawers()
when(item.itemId) {
R.id.main_drawer_home -> {
cancelFetch()
clearGalleries()
currentPage = 0
query = ""
queryStack.clear()
mode = Mode.SEARCH
fetchGalleries(query, sortMode)
loadBlocks()
}
R.id.main_drawer_history -> {
cancelFetch()
clearGalleries()
currentPage = 0
query = ""
queryStack.clear()
mode = Mode.HISTORY
fetchGalleries(query, sortMode)
loadBlocks()
}
R.id.main_drawer_downloads -> {
cancelFetch()
clearGalleries()
currentPage = 0
query = ""
queryStack.clear()
mode = Mode.DOWNLOAD
fetchGalleries(query, sortMode)
loadBlocks()
}
R.id.main_drawer_favorite -> {
cancelFetch()
clearGalleries()
currentPage = 0
query = ""
queryStack.clear()
mode = Mode.FAVORITE
fetchGalleries(query, sortMode)
loadBlocks()
}
R.id.main_drawer_help -> {
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.help))))
}
R.id.main_drawer_github -> {
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.github))))
}
R.id.main_drawer_homepage -> {
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.home_page))))
}
R.id.main_drawer_email -> {
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.email))))
}
R.id.main_drawer_kakaotalk -> {
startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.discord))))
}
}
}
return true
}
private fun cancelFetch() { private fun cancelFetch() {
galleryIDs?.cancel() galleryIDs?.cancel()
loadingJob?.cancel() loadingJob?.cancel()
@@ -974,6 +835,9 @@ class MainActivity : AppCompatActivity() {
private fun fetchGalleries(query: String, sortMode: SortMode) { private fun fetchGalleries(query: String, sortMode: SortMode) {
val defaultQuery: String = Preferences["default_query"] val defaultQuery: String = Preferences["default_query"]
if (query.isNotBlank())
searchHistory.add(query)
if (query != queryStack.lastOrNull()) { if (query != queryStack.lastOrNull()) {
queryStack.remove(query) queryStack.remove(query)
queryStack.add(query) queryStack.add(query)
@@ -981,7 +845,7 @@ class MainActivity : AppCompatActivity() {
if (query.isNotEmpty() && mode != Mode.SEARCH) { if (query.isNotEmpty() && mode != Mode.SEARCH) {
Snackbar.make(this@MainActivity.main_recyclerview, R.string.search_all, Snackbar.LENGTH_SHORT).apply { Snackbar.make(this@MainActivity.main_recyclerview, R.string.search_all, Snackbar.LENGTH_SHORT).apply {
setAction(android.R.string.yes) { setAction(android.R.string.ok) {
cancelFetch() cancelFetch()
clearGalleries() clearGalleries()
currentPage = 0 currentPage = 0
@@ -1034,12 +898,12 @@ class MainActivity : AppCompatActivity() {
val downloads = DownloadManager.getInstance(this@MainActivity).downloadFolderMap.keys.toList() val downloads = DownloadManager.getInstance(this@MainActivity).downloadFolderMap.keys.toList()
when { when {
query.isEmpty() -> downloads.also { query.isEmpty() -> downloads.reversed().also {
totalItems = it.size totalItems = it.size
} }
else -> { else -> {
val result = doSearch(query).sorted() val result = doSearch(query).sorted()
downloads.filter { result.binarySearch(it) >= 0 }.also { downloads.reversed().filter { result.binarySearch(it) >= 0 }.also {
totalItems = it.size totalItems = it.size
} }
} }
@@ -1052,7 +916,7 @@ class MainActivity : AppCompatActivity() {
} }
else -> { else -> {
val result = doSearch(query).sorted() val result = doSearch(query).sorted()
favorites.filter { result.binarySearch(it) >= 0 }.also { favorites.reversed().filter { result.binarySearch(it) >= 0 }.also {
totalItems = it.size totalItems = it.size
} }
} }
@@ -1105,4 +969,14 @@ class MainActivity : AppCompatActivity() {
} }
} }
} }
override fun onLowMemory() {
super.onLowMemory()
Glide.get(this).onLowMemory()
}
override fun onTrimMemory(level: Int) {
super.onTrimMemory(level)
Glide.get(this).onTrimMemory(level)
}
} }

View File

@@ -28,7 +28,6 @@ import android.os.IBinder
import android.view.* import android.view.*
import android.widget.Toast import android.widget.Toast
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
@@ -36,8 +35,10 @@ import androidx.recyclerview.widget.PagerSnapHelper
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.vectordrawable.graphics.drawable.Animatable2Compat import androidx.vectordrawable.graphics.drawable.Animatable2Compat
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat
import com.bumptech.glide.Glide
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import com.google.firebase.crashlytics.FirebaseCrashlytics import com.google.firebase.crashlytics.FirebaseCrashlytics
import com.qtalk.recyclerviewfastscroller.RecyclerViewFastScroller
import kotlinx.android.synthetic.main.activity_reader.* import kotlinx.android.synthetic.main.activity_reader.*
import kotlinx.android.synthetic.main.activity_reader.view.* import kotlinx.android.synthetic.main.activity_reader.view.*
import kotlinx.android.synthetic.main.dialog_numberpicker.view.* import kotlinx.android.synthetic.main.dialog_numberpicker.view.*
@@ -57,7 +58,7 @@ import java.util.*
import kotlin.concurrent.schedule import kotlin.concurrent.schedule
import kotlin.concurrent.timer import kotlin.concurrent.timer
class ReaderActivity : AppCompatActivity() { class ReaderActivity : BaseActivity() {
private var galleryID = 0 private var galleryID = 0
private var currentPage = 0 private var currentPage = 0
@@ -101,10 +102,6 @@ class ReaderActivity : AppCompatActivity() {
title = getString(R.string.reader_loading) title = getString(R.string.reader_loading)
supportActionBar?.setDisplayHomeAsUpEnabled(false) supportActionBar?.setDisplayHomeAsUpEnabled(false)
window.setFlags(
WindowManager.LayoutParams.FLAG_SECURE,
WindowManager.LayoutParams.FLAG_SECURE)
handleIntent(intent) handleIntent(intent)
cache = Cache.getInstance(this, galleryID) cache = Cache.getInstance(this, galleryID)
FirebaseCrashlytics.getInstance().setCustomKey("GalleryID", galleryID) FirebaseCrashlytics.getInstance().setCustomKey("GalleryID", galleryID)
@@ -113,6 +110,7 @@ class ReaderActivity : AppCompatActivity() {
onBackPressed() onBackPressed()
return return
} }
if (Preferences["cache_disable"]) { if (Preferences["cache_disable"]) {
reader_download_progressbar.visibility = View.GONE reader_download_progressbar.visibility = View.GONE
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
@@ -171,17 +169,6 @@ class ReaderActivity : AppCompatActivity() {
} }
} }
override fun onResume() {
if (Preferences["security_mode"])
window.setFlags(
WindowManager.LayoutParams.FLAG_SECURE,
WindowManager.LayoutParams.FLAG_SECURE)
else
window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
super.onResume()
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean { override fun onCreateOptionsMenu(menu: Menu?): Boolean {
menuInflater.inflate(R.menu.reader, menu) menuInflater.inflate(R.menu.reader, menu)
@@ -196,8 +183,8 @@ class ReaderActivity : AppCompatActivity() {
return true return true
} }
override fun onOptionsItemSelected(item: MenuItem?): Boolean { override fun onOptionsItemSelected(item: MenuItem): Boolean {
when(item?.itemId) { when(item.itemId) {
R.id.reader_menu_page_indicator -> { R.id.reader_menu_page_indicator -> {
val view = LayoutInflater.from(this).inflate(R.layout.dialog_numberpicker, reader_layout, false) val view = LayoutInflater.from(this).inflate(R.layout.dialog_numberpicker, reader_layout, false)
with(view.dialog_number_picker) { with(view.dialog_number_picker) {
@@ -436,10 +423,22 @@ class ReaderActivity : AppCompatActivity() {
flags = flags or WindowManager.LayoutParams.FLAG_FULLSCREEN flags = flags or WindowManager.LayoutParams.FLAG_FULLSCREEN
supportActionBar?.hide() supportActionBar?.hide()
this@ReaderActivity.reader_fab.visibility = View.INVISIBLE this@ReaderActivity.reader_fab.visibility = View.INVISIBLE
this@ReaderActivity.scroller.let {
it.handleWidth = resources.getDimensionPixelSize(R.dimen.thumb_height)
it.handleHeight = resources.getDimensionPixelSize(R.dimen.thumb_width)
it.handleDrawable = ContextCompat.getDrawable(this@ReaderActivity, R.drawable.thumb_horizontal)
it.fastScrollDirection = RecyclerViewFastScroller.FastScrollDirection.HORIZONTAL
}
} else { } else {
flags = flags and WindowManager.LayoutParams.FLAG_FULLSCREEN.inv() flags = flags and WindowManager.LayoutParams.FLAG_FULLSCREEN.inv()
supportActionBar?.show() supportActionBar?.show()
this@ReaderActivity.reader_fab.visibility = View.VISIBLE this@ReaderActivity.reader_fab.visibility = View.VISIBLE
this@ReaderActivity.scroller.let {
it.handleWidth = resources.getDimensionPixelSize(R.dimen.thumb_width)
it.handleHeight = resources.getDimensionPixelSize(R.dimen.thumb_height)
it.handleDrawable = ContextCompat.getDrawable(this@ReaderActivity, R.drawable.thumb)
it.fastScrollDirection = RecyclerViewFastScroller.FastScrollDirection.VERTICAL
}
} }
window.attributes = this window.attributes = this
@@ -488,4 +487,14 @@ class ReaderActivity : AppCompatActivity() {
} }
} }
} }
override fun onLowMemory() {
super.onLowMemory()
Glide.get(this).onLowMemory()
}
override fun onTrimMemory(level: Int) {
super.onTrimMemory(level)
Glide.get(this).onTrimMemory(level)
}
} }

View File

@@ -22,34 +22,25 @@ import android.annotation.SuppressLint
import android.app.Activity import android.app.Activity
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.view.MenuItem import android.view.MenuItem
import android.view.WindowManager import android.view.WindowManager
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.settings_activity.*
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import net.rdrei.android.dirchooser.DirectoryChooserActivity
import xyz.quaver.io.FileX
import xyz.quaver.pupil.R import xyz.quaver.pupil.R
import xyz.quaver.pupil.favorites import xyz.quaver.pupil.favorites
import xyz.quaver.pupil.ui.fragment.LockSettingsFragment import xyz.quaver.pupil.ui.fragment.LockSettingsFragment
import xyz.quaver.pupil.ui.fragment.SettingsFragment import xyz.quaver.pupil.ui.fragment.SettingsFragment
import xyz.quaver.pupil.util.* import xyz.quaver.pupil.util.Preferences
import java.io.File import xyz.quaver.pupil.util.normalizeID
import java.nio.charset.Charset import java.nio.charset.Charset
class SettingsActivity : AppCompatActivity() { class SettingsActivity : BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
window.setFlags(
WindowManager.LayoutParams.FLAG_SECURE,
WindowManager.LayoutParams.FLAG_SECURE)
setContentView(R.layout.settings_activity) setContentView(R.layout.settings_activity)
supportFragmentManager supportFragmentManager
.beginTransaction() .beginTransaction()
@@ -58,66 +49,14 @@ class SettingsActivity : AppCompatActivity() {
supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayHomeAsUpEnabled(true)
} }
override fun onResume() { override fun onOptionsItemSelected(item: MenuItem): Boolean {
if (Preferences["security_mode"]) when (item.itemId) {
window.setFlags(
WindowManager.LayoutParams.FLAG_SECURE,
WindowManager.LayoutParams.FLAG_SECURE)
else
window.clearFlags(WindowManager.LayoutParams.FLAG_SECURE)
super.onResume()
}
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
when (item?.itemId) {
android.R.id.home -> onBackPressed() android.R.id.home -> onBackPressed()
} }
return true return true
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when(requestCode) {
R.id.request_lock.normalizeID() -> {
if (resultCode == Activity.RESULT_OK) {
supportFragmentManager
.beginTransaction()
.replace(R.id.settings, LockSettingsFragment())
.addToBackStack("Lock")
.commitAllowingStateLoss()
}
}
R.id.request_restore.normalizeID() -> {
if (resultCode == Activity.RESULT_OK) {
val uri = data?.data ?: return
try {
val str = contentResolver.openInputStream(uri).use { inputStream ->
inputStream!!
inputStream.readBytes().toString(Charset.defaultCharset())
}
favorites.addAll(Json.decodeFromString<List<Int>>(str).also {
Snackbar.make(
window.decorView,
getString(R.string.settings_restore_success, it.size),
Snackbar.LENGTH_LONG
).show()
})
} catch (e: Exception) {
Snackbar.make(
window.decorView,
R.string.settings_restore_failed,
Snackbar.LENGTH_LONG
).show()
}
}
}
else -> super.onActivityResult(requestCode, resultCode, data)
}
}
@SuppressLint("InlinedApi") @SuppressLint("InlinedApi")
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) { override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
when (requestCode) { when (requestCode) {

View File

@@ -122,6 +122,9 @@ class DownloadLocationDialogFragment : DialogFragment() {
.setTitle(R.string.settings_download_folder) .setTitle(R.string.settings_download_folder)
.setView(build()) .setView(build())
.setPositiveButton(requireContext().getText(android.R.string.ok)) { _, _ -> .setPositiveButton(requireContext().getText(android.R.string.ok)) { _, _ ->
if (Preferences["download_folder", ""].isEmpty())
Preferences["download_folder"] = context?.getExternalFilesDir(null)?.toUri()?.toString() ?: ""
DownloadManager.getInstance(requireContext()).migrate() DownloadManager.getInstance(requireContext()).migrate()
} }

View File

@@ -82,7 +82,7 @@ class MirrorDialog(context: Context) : AlertDialog(context) {
} }
onItemMoved = { onItemMoved = {
Preferences["mirrors", it.joinToString(">")] Preferences["mirrors"] = it.joinToString(">")
} }
} }
} }

View File

@@ -74,11 +74,11 @@ class LockSettingsFragment : PreferenceFragmentCompat() {
setTitle(R.string.warning) setTitle(R.string.warning)
setMessage(R.string.settings_lock_remove_message) setMessage(R.string.settings_lock_remove_message)
setPositiveButton(android.R.string.yes) { _, _ -> setPositiveButton(android.R.string.ok) { _, _ ->
lockManager.remove(Lock.Type.PATTERN) lockManager.remove(Lock.Type.PATTERN)
onResume() onResume()
} }
setNegativeButton(android.R.string.no) { _, _ -> } setNegativeButton(android.R.string.cancel) { _, _ -> }
}.show() }.show()
} else { } else {
val intent = Intent(requireContext(), LockActivity::class.java).apply { val intent = Intent(requireContext(), LockActivity::class.java).apply {
@@ -107,11 +107,11 @@ class LockSettingsFragment : PreferenceFragmentCompat() {
setTitle(R.string.warning) setTitle(R.string.warning)
setMessage(R.string.settings_lock_remove_message) setMessage(R.string.settings_lock_remove_message)
setPositiveButton(android.R.string.yes) { _, _ -> setPositiveButton(android.R.string.ok) { _, _ ->
lockManager.remove(Lock.Type.PIN) lockManager.remove(Lock.Type.PIN)
onResume() onResume()
} }
setNegativeButton(android.R.string.no) { _, _ -> } setNegativeButton(android.R.string.cancel) { _, _ -> }
}.show() }.show()
} else { } else {
val intent = Intent(requireContext(), LockActivity::class.java).apply { val intent = Intent(requireContext(), LockActivity::class.java).apply {

View File

@@ -62,6 +62,11 @@ class ManageFavoritesFragment : PreferenceFragmentCompat() {
} }
override fun onResponse(call: Call, response: Response) { override fun onResponse(call: Call, response: Response) {
if (response.code() != 200) {
response.close()
return
}
Intent(Intent.ACTION_SEND).apply { Intent(Intent.ACTION_SEND).apply {
type = "text/plain" type = "text/plain"
putExtra(Intent.EXTRA_TEXT, response.body()?.use { it.string() }?.replace("\n", "")) putExtra(Intent.EXTRA_TEXT, response.body()?.use { it.string() }?.replace("\n", ""))
@@ -82,7 +87,7 @@ class ManageFavoritesFragment : PreferenceFragmentCompat() {
.setTitle(R.string.settings_restore_title) .setTitle(R.string.settings_restore_title)
.setView(editText) .setView(editText)
.setPositiveButton(android.R.string.ok) { _, _ -> .setPositiveButton(android.R.string.ok) { _, _ ->
restore(favorites, editText.text.toString(), restore(editText.text.toString(),
onFailure = onFailure@{ onFailure = onFailure@{
val view = view ?: return@onFailure val view = view ?: return@onFailure
Snackbar.make(view, R.string.settings_restore_failed, Snackbar.LENGTH_LONG).show() Snackbar.make(view, R.string.settings_restore_failed, Snackbar.LENGTH_LONG).show()

View File

@@ -57,7 +57,7 @@ class ManageStorageFragment : PreferenceFragmentCompat(), Preference.OnPreferenc
AlertDialog.Builder(context).apply { AlertDialog.Builder(context).apply {
setTitle(R.string.warning) setTitle(R.string.warning)
setMessage(R.string.settings_clear_cache_alert_message) setMessage(R.string.settings_clear_cache_alert_message)
setPositiveButton(android.R.string.yes) { _, _ -> setPositiveButton(android.R.string.ok) { _, _ ->
if (dir.exists()) if (dir.exists())
dir.deleteRecursively() dir.deleteRecursively()
@@ -74,7 +74,7 @@ class ManageStorageFragment : PreferenceFragmentCompat(), Preference.OnPreferenc
} }
} }
} }
setNegativeButton(android.R.string.no) { _, _ -> } setNegativeButton(android.R.string.cancel) { _, _ -> }
}.show() }.show()
} }
"delete_downloads" -> { "delete_downloads" -> {
@@ -83,7 +83,7 @@ class ManageStorageFragment : PreferenceFragmentCompat(), Preference.OnPreferenc
AlertDialog.Builder(context).apply { AlertDialog.Builder(context).apply {
setTitle(R.string.warning) setTitle(R.string.warning)
setMessage(R.string.settings_clear_downloads_alert_message) setMessage(R.string.settings_clear_downloads_alert_message)
setPositiveButton(android.R.string.yes) { _, _ -> setPositiveButton(android.R.string.ok) { _, _ ->
CoroutineScope(Dispatchers.IO).launch { CoroutineScope(Dispatchers.IO).launch {
job?.cancel() job?.cancel()
launch(Dispatchers.Main) { launch(Dispatchers.Main) {
@@ -109,18 +109,18 @@ class ManageStorageFragment : PreferenceFragmentCompat(), Preference.OnPreferenc
} }
} }
} }
setNegativeButton(android.R.string.no) { _, _ -> } setNegativeButton(android.R.string.cancel) { _, _ -> }
}.show() }.show()
} }
"clear_history" -> { "clear_history" -> {
AlertDialog.Builder(context).apply { AlertDialog.Builder(context).apply {
setTitle(R.string.warning) setTitle(R.string.warning)
setMessage(R.string.settings_clear_history_alert_message) setMessage(R.string.settings_clear_history_alert_message)
setPositiveButton(android.R.string.yes) { _, _ -> setPositiveButton(android.R.string.ok) { _, _ ->
histories.clear() histories.clear()
summary = context.getString(R.string.settings_clear_history_summary, histories.size) summary = context.getString(R.string.settings_clear_history_summary, histories.size)
} }
setNegativeButton(android.R.string.no) { _, _ -> } setNegativeButton(android.R.string.cancel) { _, _ -> }
}.show() }.show()
} }
else -> return false else -> return false

View File

@@ -18,6 +18,7 @@
package xyz.quaver.pupil.ui.fragment package xyz.quaver.pupil.ui.fragment
import android.app.Activity
import android.content.* import android.content.*
import android.os.Bundle import android.os.Bundle
import android.widget.Toast import android.widget.Toast
@@ -26,14 +27,20 @@ import androidx.preference.Preference
import androidx.preference.PreferenceCategory import androidx.preference.PreferenceCategory
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import androidx.preference.SwitchPreferenceCompat import androidx.preference.SwitchPreferenceCompat
import com.google.android.gms.oss.licenses.OssLicensesMenuActivity
import com.google.android.material.snackbar.Snackbar
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.Json
import xyz.quaver.io.FileX import xyz.quaver.io.FileX
import xyz.quaver.io.util.getChild import xyz.quaver.io.util.getChild
import xyz.quaver.pupil.R import xyz.quaver.pupil.R
import xyz.quaver.pupil.favorites
import xyz.quaver.pupil.ui.LockActivity import xyz.quaver.pupil.ui.LockActivity
import xyz.quaver.pupil.ui.SettingsActivity import xyz.quaver.pupil.ui.SettingsActivity
import xyz.quaver.pupil.ui.dialog.* import xyz.quaver.pupil.ui.dialog.*
import xyz.quaver.pupil.util.* import xyz.quaver.pupil.util.*
import xyz.quaver.pupil.util.downloader.DownloadManager import xyz.quaver.pupil.util.downloader.DownloadManager
import java.nio.charset.Charset
class SettingsFragment : class SettingsFragment :
PreferenceFragmentCompat(), PreferenceFragmentCompat(),
@@ -68,7 +75,7 @@ class SettingsFragment :
checkUpdate(activity as SettingsActivity, true) checkUpdate(activity as SettingsActivity, true)
} }
"download_folder" -> { "download_folder" -> {
DownloadLocationDialogFragment().show(requireActivity().supportFragmentManager, "Download Location Dialog") DownloadLocationDialogFragment().show(parentFragmentManager, "Download Location Dialog")
} }
"default_query" -> { "default_query" -> {
DefaultQueryDialog(requireContext()).apply { DefaultQueryDialog(requireContext()).apply {
@@ -79,8 +86,10 @@ class SettingsFragment :
}.show() }.show()
} }
"app_lock" -> { "app_lock" -> {
val intent = Intent(requireContext(), LockActivity::class.java) val intent = Intent(requireContext(), LockActivity::class.java).apply {
activity?.startActivityForResult(intent, R.id.request_lock.normalizeID()) putExtra("force", true)
}
startActivityForResult(intent, R.id.request_lock.normalizeID())
} }
"mirrors" -> { "mirrors" -> {
MirrorDialog(requireContext()) MirrorDialog(requireContext())
@@ -246,10 +255,31 @@ class SettingsFragment :
summary = Preferences.get<String>("user_id") summary = Preferences.get<String>("user_id")
onPreferenceClickListener = this@SettingsFragment onPreferenceClickListener = this@SettingsFragment
} }
"oss" -> {
setOnPreferenceClickListener {
context?.startActivity(Intent(context, OssLicensesMenuActivity::class.java))
true
}
}
} }
} }
} }
} }
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when(requestCode) {
R.id.request_lock.normalizeID() -> {
if (resultCode == Activity.RESULT_OK) {
parentFragmentManager
.beginTransaction()
.replace(R.id.settings, LockSettingsFragment())
.addToBackStack("Lock")
.commitAllowingStateLoss()
}
}
else -> super.onActivityResult(requestCode, resultCode, data)
}
}
} }

View File

@@ -18,6 +18,7 @@
package xyz.quaver.pupil.ui.view package xyz.quaver.pupil.ui.view
import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import com.google.android.material.chip.Chip import com.google.android.material.chip.Chip
@@ -25,7 +26,16 @@ import xyz.quaver.pupil.R
import xyz.quaver.pupil.types.Tag import xyz.quaver.pupil.types.Tag
import xyz.quaver.pupil.util.wordCapitalize import xyz.quaver.pupil.util.wordCapitalize
class TagChip(context: Context, val tag: Tag) : Chip(context) { @SuppressLint("ViewConstructor")
class TagChip(context: Context, tag: Tag) : Chip(context) {
val tag: Tag =
tag.let {
when {
it.area != null -> it
else -> Tag("tag", tag.tag)
}
}
private val languages = context.resources.getStringArray(R.array.languages).map { private val languages = context.resources.getStringArray(R.array.languages).map {
it.split("|").let { split -> it.split("|").let { split ->
@@ -34,13 +44,6 @@ class TagChip(context: Context, val tag: Tag) : Chip(context) {
}.toMap() }.toMap()
init { init {
val tag = tag.let {
when {
it.area != null -> it
else -> Tag("tag", tag.tag)
}
}
chipIcon = when(tag.area) { chipIcon = when(tag.area) {
"male" -> { "male" -> {
setChipBackgroundColorResource(R.color.material_blue_700) setChipBackgroundColorResource(R.color.material_blue_700)

View File

@@ -1,6 +1,6 @@
/* /*
* Pupil, Hitomi.la viewer for Android * Pupil, Hitomi.la viewer for Android
* Copyright (C) 2019 tom5079 * Copyright (C) 2020 tom5079
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
@@ -18,12 +18,17 @@
package xyz.quaver.pupil.util package xyz.quaver.pupil.util
import kotlinx.serialization.decodeFromString import kotlinx.serialization.*
import kotlinx.serialization.encodeToString import kotlinx.serialization.builtins.ListSerializer
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import java.io.File import java.io.File
class GalleryList(private val file: File, private val list: MutableSet<Int> = mutableSetOf()) : MutableSet<Int> by list { class SavedSet <T: Any> (private val file: File, private val any: T, private val set: MutableSet<T> = mutableSetOf()) : MutableSet<T> by set {
@Suppress("UNCHECKED_CAST")
@OptIn(ExperimentalSerializationApi::class)
val serializer: KSerializer<List<T>>
get() = ListSerializer(serializer(any::class.java) as KSerializer<T>)
init { init {
if (!file.exists()) { if (!file.exists()) {
@@ -35,45 +40,48 @@ class GalleryList(private val file: File, private val list: MutableSet<Int> = mu
fun load() { fun load() {
synchronized(this) { synchronized(this) {
list.clear() set.clear()
list.addAll( kotlin.runCatching {
Json.decodeFromString<List<Int>>(file.bufferedReader().use { it.readText() }) Json.decodeFromString(serializer, file.readText())
) }.onSuccess {
set.addAll(it)
}
} }
} }
@OptIn(ExperimentalSerializationApi::class)
fun save() { fun save() {
synchronized(this) { synchronized(this) {
file.writeText(Json.encodeToString(list)) file.writeText(Json.encodeToString(serializer, set.toList()))
} }
} }
override fun add(element: Int): Boolean { override fun add(element: T): Boolean {
load() load()
return list.add(element).also { return set.add(element).also {
save() save()
} }
} }
override fun addAll(elements: Collection<Int>): Boolean { override fun addAll(elements: Collection<T>): Boolean {
load() load()
return list.addAll(elements).also { return set.addAll(elements).also {
save() save()
} }
} }
override fun remove(element: Int): Boolean { override fun remove(element: T): Boolean {
load() load()
return list.remove(element).also { return set.remove(element).also {
save() save()
} }
} }
override fun clear() { override fun clear() {
list.clear() set.clear()
save() save()
} }

View File

@@ -41,6 +41,7 @@ import java.io.FileOutputStream
import java.io.InputStream import java.io.InputStream
import java.net.URL import java.net.URL
@Suppress("DEPRECATION")
@Deprecated("Use downloader.Cache instead") @Deprecated("Use downloader.Cache instead")
class Cache(context: Context) : ContextWrapper(context) { class Cache(context: Context) : ContextWrapper(context) {

View File

@@ -37,9 +37,7 @@ import xyz.quaver.Code
import xyz.quaver.hitomi.Reader import xyz.quaver.hitomi.Reader
import xyz.quaver.hitomi.getReferer import xyz.quaver.hitomi.getReferer
import xyz.quaver.hitomi.imageUrlFromImage import xyz.quaver.hitomi.imageUrlFromImage
import xyz.quaver.hiyobi.cookie
import xyz.quaver.hiyobi.createImgList import xyz.quaver.hiyobi.createImgList
import xyz.quaver.hiyobi.user_agent
import xyz.quaver.pupil.R import xyz.quaver.pupil.R
import xyz.quaver.pupil.client import xyz.quaver.pupil.client
import xyz.quaver.pupil.interceptors import xyz.quaver.pupil.interceptors
@@ -48,6 +46,7 @@ import java.io.File
import java.io.IOException import java.io.IOException
import java.util.concurrent.LinkedBlockingQueue import java.util.concurrent.LinkedBlockingQueue
@Suppress("DEPRECATION")
@Deprecated("Use DownloadService instead") @Deprecated("Use DownloadService instead")
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
class DownloadWorker private constructor(context: Context) : ContextWrapper(context) { class DownloadWorker private constructor(context: Context) : ContextWrapper(context) {
@@ -219,8 +218,6 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
} }
Code.HIYOBI -> { Code.HIYOBI -> {
url(createImgList(galleryID, reader, lowQuality)[index].path) url(createImgList(galleryID, reader, lowQuality)[index].path)
addHeader("User-Agent", user_agent)
addHeader("Cookie", cookie)
} }
else -> { else -> {
//shouldn't be called anyway //shouldn't be called anyway
@@ -280,6 +277,12 @@ class DownloadWorker private constructor(context: Context) : ContextWrapper(cont
} }
override fun onResponse(call: Call, response: Response) { override fun onResponse(call: Call, response: Response) {
if (response.code() != 200) {
response.close()
onFailure(call, IOException())
return
}
val ext = call.request().url().encodedPath().split('.').last() val ext = call.request().url().encodedPath().split('.').last()
try { try {

View File

@@ -22,6 +22,7 @@ import kotlinx.serialization.Serializable
import xyz.quaver.hitomi.GalleryBlock import xyz.quaver.hitomi.GalleryBlock
import xyz.quaver.hitomi.Reader import xyz.quaver.hitomi.Reader
@Suppress("DEPRECATION")
@Deprecated("Use downloader.Cache.Metadata instead") @Deprecated("Use downloader.Cache.Metadata instead")
@Serializable @Serializable
data class Metadata( data class Metadata(

View File

@@ -34,12 +34,10 @@ import xyz.quaver.Code
import xyz.quaver.hitomi.GalleryBlock import xyz.quaver.hitomi.GalleryBlock
import xyz.quaver.hitomi.Reader import xyz.quaver.hitomi.Reader
import xyz.quaver.io.FileX import xyz.quaver.io.FileX
import xyz.quaver.io.util.getChild import xyz.quaver.io.util.*
import xyz.quaver.io.util.readBytes
import xyz.quaver.io.util.readText
import xyz.quaver.io.util.writeBytes
import xyz.quaver.pupil.client import xyz.quaver.pupil.client
import xyz.quaver.pupil.util.Preferences import xyz.quaver.pupil.util.Preferences
import java.io.IOException
@Serializable @Serializable
data class Metadata( data class Metadata(
@@ -60,6 +58,7 @@ class Cache private constructor(context: Context, val galleryID: Int) : ContextW
instances[galleryID] ?: Cache(context, galleryID).also { instances.put(galleryID, it) } instances[galleryID] ?: Cache(context, galleryID).also { instances.put(galleryID, it) }
} }
@Synchronized
fun delete(galleryID: Int) { fun delete(galleryID: Int) {
instances[galleryID]?.cacheFolder?.deleteRecursively() instances[galleryID]?.cacheFolder?.deleteRecursively()
instances.delete(galleryID) instances.delete(galleryID)
@@ -86,11 +85,11 @@ class Cache private constructor(context: Context, val galleryID: Int) : ContextW
} }
fun findFile(fileName: String): FileX? = fun findFile(fileName: String): FileX? =
cacheFolder.getChild(fileName).let { downloadFolder?.let { downloadFolder -> downloadFolder.getChild(fileName).let {
if (it.exists()) it else null if (it.exists()) it else null
} ?: downloadFolder?.let { downloadFolder -> downloadFolder.getChild(fileName).let { } } ?: cacheFolder.getChild(fileName).let {
if (it.exists()) it else null if (it.exists()) it else null
} } }
@Suppress("BlockingMethodInNonBlockingContext") @Suppress("BlockingMethodInNonBlockingContext")
fun setMetadata(change: (Metadata) -> Unit) { fun setMetadata(change: (Metadata) -> Unit) {
@@ -140,7 +139,7 @@ class Cache private constructor(context: Context, val galleryID: Int) : ContextW
.url(it) .url(it)
.build() .build()
client.newCall(request).execute().body()?.use { it.bytes() } client.newCall(request).execute().also { if (it.code() != 200) throw IOException() }.body()?.use { it.bytes() }
}.getOrNull()?.also { kotlin.run { }.getOrNull()?.also { kotlin.run {
cacheFolder.getChild(".thumbnail").writeBytes(it) cacheFolder.getChild(".thumbnail").writeBytes(it)
} } } }
@@ -201,24 +200,42 @@ class Cache private constructor(context: Context, val galleryID: Int) : ContextW
fun moveToDownload() = CoroutineScope(Dispatchers.IO).launch { fun moveToDownload() = CoroutineScope(Dispatchers.IO).launch {
val downloadFolder = downloadFolder ?: return@launch val downloadFolder = downloadFolder ?: return@launch
if (downloadFolder.getChild(".metadata").exists())
return@launch
metadata.imageList?.forEach { imageName -> metadata.imageList?.forEach { imageName ->
imageName ?: return@forEach imageName ?: return@forEach
val target = downloadFolder.getChild(imageName) val target = downloadFolder.getChild(imageName)
val source = cacheFolder.getChild(imageName) val source = cacheFolder.getChild(imageName)
if (!source.exists()) if (!source.exists() || target.exists())
return@forEach return@forEach
kotlin.runCatching { kotlin.runCatching {
target.createNewFile() target.createNewFile()
source.readBytes()?.let { target.writeBytes(it) } target.outputStream()?.use { target -> source.inputStream()?.use { source ->
source.copyTo(target)
} }
}
}
val cacheThumbnail = cacheFolder.getChild(".thumbnail")
val downloadThumbnail = downloadFolder.getChild(".thumbnail")
if (cacheThumbnail.exists() && !downloadThumbnail.exists()) {
kotlin.runCatching {
downloadThumbnail.createNewFile()
downloadThumbnail.outputStream()?.use { target -> cacheThumbnail.inputStream()?.use { source ->
source.copyTo(target)
} }
cacheThumbnail.delete()
} }
} }
val cacheMetadata = cacheFolder.getChild(".metadata") val cacheMetadata = cacheFolder.getChild(".metadata")
val downloadMetadata = downloadFolder.getChild(".metadata") val downloadMetadata = downloadFolder.getChild(".metadata")
if (cacheMetadata.exists()) { if (cacheMetadata.exists() && !downloadMetadata.exists()) {
kotlin.runCatching { kotlin.runCatching {
downloadMetadata.createNewFile() downloadMetadata.createNewFile()
downloadMetadata.writeText(Json.encodeToString(metadata)) downloadMetadata.writeText(Json.encodeToString(metadata))

View File

@@ -75,7 +75,6 @@ class DownloadManager private constructor(context: Context) : ContextWrapper(con
data ?: { data ?: {
file.createNewFile() file.createNewFile()
file.writeText("{}")
mutableMapOf<Int, String>() mutableMapOf<Int, String>()
}.invoke() }.invoke()
}.invoke() }.invoke()
@@ -99,9 +98,6 @@ class DownloadManager private constructor(context: Context) : ContextWrapper(con
@Synchronized @Synchronized
fun addDownloadFolder(galleryID: Int) { fun addDownloadFolder(galleryID: Int) {
if (downloadFolderMap.containsKey(galleryID))
return
val name = runBlocking { val name = runBlocking {
Cache.getInstance(this@DownloadManager, galleryID).getGalleryBlock() Cache.getInstance(this@DownloadManager, galleryID).getGalleryBlock()
}?.formatDownloadFolder() ?: return }?.formatDownloadFolder() ?: return
@@ -119,12 +115,9 @@ class DownloadManager private constructor(context: Context) : ContextWrapper(con
@Synchronized @Synchronized
fun deleteDownloadFolder(galleryID: Int) { fun deleteDownloadFolder(galleryID: Int) {
if (!downloadFolderMap.containsKey(galleryID))
return
downloadFolderMap[galleryID]?.let { downloadFolderMap[galleryID]?.let {
kotlin.runCatching { kotlin.runCatching {
downloadFolder.getChild(it).delete() downloadFolder.getChild(it).deleteRecursively()
downloadFolderMap.remove(galleryID) downloadFolderMap.remove(galleryID)
downloadFolder.getChild(".download").let { if (!it.exists()) it.createNewFile() } downloadFolder.getChild(".download").let { if (!it.exists()) it.createNewFile() }

View File

@@ -27,6 +27,7 @@ import java.io.FileOutputStream
import java.lang.reflect.Array import java.lang.reflect.Array
import java.net.URL import java.net.URL
@Suppress("DEPRECATION")
@Deprecated("Use downloader.Cache instead") @Deprecated("Use downloader.Cache instead")
fun getCachedGallery(context: Context, galleryID: Int) = fun getCachedGallery(context: Context, galleryID: Int) =
File(getDownloadDirectory(context), galleryID.toString()).let { File(getDownloadDirectory(context), galleryID.toString()).let {
@@ -36,6 +37,7 @@ fun getCachedGallery(context: Context, galleryID: Int) =
File(context.cacheDir, "imageCache/$galleryID") File(context.cacheDir, "imageCache/$galleryID")
} }
@Suppress("DEPRECATION")
@Deprecated("Use downloader.Cache instead") @Deprecated("Use downloader.Cache instead")
fun getDownloadDirectory(context: Context) = fun getDownloadDirectory(context: Context) =
Preferences.get<String>("dl_location").let { Preferences.get<String>("dl_location").let {
@@ -45,6 +47,7 @@ fun getDownloadDirectory(context: Context) =
context.getExternalFilesDir(null)!! context.getExternalFilesDir(null)!!
} }
@Suppress("DEPRECATION")
@Deprecated("Use FileX instead") @Deprecated("Use FileX instead")
fun File.isParentOf(another: File) = fun File.isParentOf(another: File) =
another.absolutePath.startsWith(this.absolutePath) another.absolutePath.startsWith(this.absolutePath)

View File

@@ -22,11 +22,7 @@ import android.annotation.SuppressLint
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.os.Build import android.os.Build
import kotlinx.coroutines.CoroutineScope import androidx.core.content.ContextCompat
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import xyz.quaver.Code import xyz.quaver.Code
@@ -34,11 +30,7 @@ import xyz.quaver.hitomi.GalleryBlock
import xyz.quaver.hitomi.Reader import xyz.quaver.hitomi.Reader
import xyz.quaver.hitomi.getReferer import xyz.quaver.hitomi.getReferer
import xyz.quaver.hitomi.imageUrlFromImage import xyz.quaver.hitomi.imageUrlFromImage
import xyz.quaver.hiyobi.cookie
import xyz.quaver.hiyobi.createImgList import xyz.quaver.hiyobi.createImgList
import xyz.quaver.hiyobi.user_agent
import xyz.quaver.pupil.util.downloader.Cache
import xyz.quaver.pupil.util.downloader.Metadata
import java.util.* import java.util.*
import kotlin.collections.ArrayList import kotlin.collections.ArrayList
@@ -101,21 +93,14 @@ fun GalleryBlock.formatDownloadFolder(): String =
formatMap.entries.fold(it) { str, (k, v) -> formatMap.entries.fold(it) { str, (k, v) ->
str.replace(k, v.invoke(this), true) str.replace(k, v.invoke(this), true)
} }
} }.replace("/", "")
fun GalleryBlock.formatDownloadFolderTest(format: String): String = fun GalleryBlock.formatDownloadFolderTest(format: String): String =
format.let { format.let {
formatMap.entries.fold(it) { str, (k, v) -> formatMap.entries.fold(it) { str, (k, v) ->
str.replace(k, v.invoke(this), true) str.replace(k, v.invoke(this), true)
} }
} }.replace("/", "")
fun Context.startForegroundServiceCompat(service: Intent) {
if (Build.VERSION.SDK_INT >= 26)
startForegroundService(service)
else
startService(service)
}
val Reader.requestBuilders: List<Request.Builder> val Reader.requestBuilders: List<Request.Builder>
get() { get() {
@@ -134,8 +119,6 @@ val Reader.requestBuilders: List<Request.Builder>
createImgList(galleryID, this, lowQuality).map { createImgList(galleryID, this, lowQuality).map {
Request.Builder() Request.Builder()
.url(it.path) .url(it.path)
.header("User-Agent", user_agent)
.header("Cookie", cookie)
} }
} }
} }

View File

@@ -24,6 +24,7 @@ import android.app.PendingIntent
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter
import android.net.Uri import android.net.Uri
import android.util.Base64 import android.util.Base64
import android.util.Log import android.util.Log
@@ -32,11 +33,13 @@ import androidx.appcompat.app.AlertDialog
import androidx.core.app.NotificationCompat import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat import androidx.core.app.NotificationManagerCompat
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.google.firebase.crashlytics.FirebaseCrashlytics
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.serialization.decodeFromString import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.* import kotlinx.serialization.json.*
import okhttp3.Call import okhttp3.Call
import okhttp3.Callback import okhttp3.Callback
@@ -45,14 +48,18 @@ import okhttp3.Response
import ru.noties.markwon.Markwon import ru.noties.markwon.Markwon
import xyz.quaver.hitomi.GalleryBlock import xyz.quaver.hitomi.GalleryBlock
import xyz.quaver.hitomi.Reader import xyz.quaver.hitomi.Reader
import xyz.quaver.hitomi.getGalleryBlock
import xyz.quaver.hitomi.getReader
import xyz.quaver.io.FileX import xyz.quaver.io.FileX
import xyz.quaver.io.util.getChild import xyz.quaver.io.util.getChild
import xyz.quaver.io.util.* import xyz.quaver.io.util.*
import xyz.quaver.pupil.BuildConfig import xyz.quaver.pupil.BuildConfig
import xyz.quaver.pupil.R import xyz.quaver.pupil.R
import xyz.quaver.pupil.client import xyz.quaver.pupil.client
import xyz.quaver.pupil.favorites
import xyz.quaver.pupil.services.DownloadService import xyz.quaver.pupil.services.DownloadService
import xyz.quaver.pupil.util.downloader.Cache import xyz.quaver.pupil.util.downloader.Cache
import xyz.quaver.pupil.util.downloader.Metadata
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.net.URL import java.net.URL
@@ -152,7 +159,7 @@ fun checkUpdate(context: Context, force: Boolean = false) {
setTitle(R.string.update_title) setTitle(R.string.update_title)
val msg = extractReleaseNote(update, Locale.getDefault()) val msg = extractReleaseNote(update, Locale.getDefault())
setMessage(Markwon.create(context).toMarkdown(msg)) setMessage(Markwon.create(context).toMarkdown(msg))
setPositiveButton(android.R.string.yes) { _, _ -> setPositiveButton(android.R.string.ok) { _, _ ->
val downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager val downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
@@ -175,7 +182,7 @@ fun checkUpdate(context: Context, force: Boolean = false) {
Preferences["update_download_id"] = it Preferences["update_download_id"] = it
} }
} }
setNegativeButton(if (force) android.R.string.no else R.string.ignore_update) { _, _ -> setNegativeButton(if (force) android.R.string.cancel else R.string.ignore_update) { _, _ ->
if (!force) if (!force)
preferences.edit() preferences.edit()
.putLong("ignore_update_until", System.currentTimeMillis() + 604800000) .putLong("ignore_update_until", System.currentTimeMillis() + 604800000)
@@ -189,7 +196,7 @@ fun checkUpdate(context: Context, force: Boolean = false) {
} }
} }
fun restore(favorites: GalleryList, url: String, onFailure: ((Throwable) -> Unit)? = null, onSuccess: ((List<Int>) -> Unit)? = null) { fun restore(url: String, onFailure: ((Throwable) -> Unit)? = null, onSuccess: ((List<Int>) -> Unit)? = null) {
if (!URLUtil.isValidUrl(url)) { if (!URLUtil.isValidUrl(url)) {
onFailure?.invoke(IllegalArgumentException()) onFailure?.invoke(IllegalArgumentException())
return return
@@ -207,7 +214,7 @@ fun restore(favorites: GalleryList, url: String, onFailure: ((Throwable) -> Unit
override fun onResponse(call: Call, response: Response) { override fun onResponse(call: Call, response: Response) {
kotlin.runCatching { kotlin.runCatching {
Json.decodeFromString<List<Int>>(response.body().use { it?.string() } ?: "[]").let { Json.decodeFromString<List<Int>>(response.also { if (it.code() != 200) throw IOException() }.body().use { it?.string() } ?: "[]").let {
favorites.addAll(it) favorites.addAll(it)
onSuccess?.invoke(it) onSuccess?.invoke(it)
} }
@@ -226,12 +233,15 @@ private val receiver = object: BroadcastReceiver() {
ACTION_CANCEL -> { ACTION_CANCEL -> {
job?.cancel() job?.cancel()
NotificationManagerCompat.from(context).cancel(R.id.notification_id_import) NotificationManagerCompat.from(context).cancel(R.id.notification_id_import)
context.unregisterReceiver(this)
} }
} }
} }
} }
@SuppressLint("RestrictedApi") @SuppressLint("RestrictedApi")
fun xyz.quaver.pupil.util.downloader.DownloadManager.migrate() { fun xyz.quaver.pupil.util.downloader.DownloadManager.migrate() {
registerReceiver(receiver, IntentFilter().apply { addAction(receiver.ACTION_CANCEL) })
val notificationManager = NotificationManagerCompat.from(this) val notificationManager = NotificationManagerCompat.from(this)
val action = NotificationCompat.Action.Builder(0, getText(android.R.string.cancel), val action = NotificationCompat.Action.Builder(0, getText(android.R.string.cancel),
PendingIntent.getBroadcast(this, R.id.notification_import_cancel_action.normalizeID(), Intent(receiver.ACTION_CANCEL), PendingIntent.FLAG_UPDATE_CURRENT) PendingIntent.getBroadcast(this, R.id.notification_import_cancel_action.normalizeID(), Intent(receiver.ACTION_CANCEL), PendingIntent.FLAG_UPDATE_CURRENT)
@@ -247,55 +257,68 @@ fun xyz.quaver.pupil.util.downloader.DownloadManager.migrate() {
job?.cancel() job?.cancel()
job = CoroutineScope(Dispatchers.IO).launch { job = CoroutineScope(Dispatchers.IO).launch {
val folders = downloadFolder.listFiles { folder -> val downloadFolders = downloadFolder.listFiles { folder ->
(folder as? FileX)?.isDirectory == true && !downloadFolderMap.values.contains(folder.name) folder.isDirectory && !downloadFolderMap.values.contains(folder.name)
}?.map {
if (it !is FileX)
FileX(this@migrate, it)
else
it
} }
if (folders.isNullOrEmpty()) return@launch
folders.forEachIndexed { index, folder -> if (downloadFolders.isNullOrEmpty()) return@launch
downloadFolders.forEachIndexed { index, folder ->
notification notification
.setContentText(getString(R.string.import_old_galleries_notification_text, index, folders.size)) .setContentText(getString(R.string.import_old_galleries_notification_text, index, downloadFolders.size))
.setProgress(index, folders.size, false) .setProgress(index, downloadFolders.size, false)
notificationManager.notify(R.id.notification_id_import, notification.build()) notificationManager.notify(R.id.notification_id_import, notification.build())
kotlin.runCatching { kotlin.runCatching {
val folder = (folder as? FileX) ?: return@runCatching val metadata = kotlin.runCatching {
folder.getChild(".metadata").readText()?.let { Json.parseToJsonElement(it).jsonObject }
}.getOrNull()
val metadata = folder.getChild(".metadata").readText()?.let { Json.parseToJsonElement(it).jsonObject } ?: return@runCatching val galleryID = folder.name.toIntOrNull() ?: return@runCatching
val galleryBlock: GalleryBlock? = val galleryBlock: GalleryBlock? = kotlin.runCatching {
metadata["galleryBlock"]?.let { Json.decodeFromJsonElement<GalleryBlock>(it) } metadata?.get("galleryBlock")?.let { Json.decodeFromJsonElement<GalleryBlock>(it) }
val reader: Reader? = }.getOrNull() ?: getGalleryBlock(galleryID)
metadata["reader"]?.let { Json.decodeFromJsonElement<Reader>(it) } val reader: Reader? = kotlin.runCatching {
metadata?.get("reader")?.let { Json.decodeFromJsonElement<Reader>(it) }
}.getOrNull() ?: getReader(galleryID)
val galleryID = galleryBlock?.id ?: reader?.galleryInfo?.id ?: folder.name.toIntOrNull() ?: return@runCatching metadata?.get("thumbnail")?.jsonPrimitive?.contentOrNull?.also { thumbnail ->
metadata["thumbnail"]?.jsonPrimitive?.contentOrNull.let { thumbnail ->
val file = folder.getChild(".thumbnail").also { val file = folder.getChild(".thumbnail").also {
if (!it.exists()) if (it.exists())
it.createNewFile() it.delete()
it.createNewFile()
} }
file.writeBytes(Base64.decode(thumbnail, Base64.DEFAULT)) file.writeBytes(Base64.decode(thumbnail, Base64.DEFAULT))
} }
downloadFolderMap[galleryID] = folder.name
val cache = Cache.getInstance(this@migrate, galleryID)
val list: MutableList<String?> = val list: MutableList<String?> =
MutableList(cache.getReader()!!.galleryInfo.files.size) { null } MutableList(reader!!.galleryInfo.files.size) { null }
folder.listFiles { dir -> folder.listFiles { file ->
dir?.nameWithoutExtension?.toIntOrNull() != null file?.nameWithoutExtension?.let {
Regex("""\d{5}""").matches(it) && it.toIntOrNull() != null
} == true
}?.forEach { }?.forEach {
list[it.nameWithoutExtension.toInt()] = it.name list[it.nameWithoutExtension.toInt()] = it.name
} }
cache.setMetadata { folder.getChild(".metadata").also { if (it.exists()) it.delete(); it.createNewFile() }.writeText(
it.galleryBlock = galleryBlock Json.encodeToString(Metadata(galleryBlock, reader, list))
it.reader = reader )
it.imageList = list
synchronized(Cache) {
Cache.delete(galleryID)
} }
downloadFolderMap[galleryID] = folder.name
downloadFolder.getChild(".download").let { if (!it.exists()) it.createNewFile(); it.writeText(Json.encodeToString(downloadFolderMap)) }
} }
} }
@@ -305,5 +328,9 @@ fun xyz.quaver.pupil.util.downloader.DownloadManager.migrate() {
.setOngoing(false) .setOngoing(false)
.mActions.clear() .mActions.clear()
notificationManager.notify(R.id.notification_id_import, notification.build()) notificationManager.notify(R.id.notification_id_import, notification.build())
kotlin.runCatching {
unregisterReceiver(receiver)
}
} }
} }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 585 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 366 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 700 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

View File

@@ -1,8 +1,4 @@
<!-- drawable/account_group.xml --> <!--drawable/account_group.xml-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
android:height="24dp" <path android:fillColor="?attr/colorControlNormal" android:pathData="M12 5.5A3.5 3.5 0 0 1 15.5 9a3.5 3.5 0 0 1-3.5 3.5A3.5 3.5 0 0 1 8.5 9 3.5 3.5 0 0 1 12 5.5M5 8c0.56 0 1.08 0.15 1.53 0.42-0.15 1.43 0.27 2.85 1.13 3.96C7.16 13.34 6.16 14 5 14a3 3 0 0 1-3-3 3 3 0 0 1 3-3m14 0a3 3 0 0 1 3 3 3 3 0 0 1-3 3c-1.16 0-2.16-0.66-2.66-1.62 0.86-1.11 1.28-2.53 1.13-3.96C17.92 8.15 18.44 8 19 8M5.5 18.25c0-2.07 2.91-3.75 6.5-3.75s6.5 1.68 6.5 3.75V20h-13v-1.75M0 20v-1.5c0-1.39 1.89-2.56 4.45-2.9-0.59 0.68-0.95 1.62-0.95 2.65V20H0m24 0h-3.5v-1.75c0-1.03-0.36-1.97-0.95-2.65 2.56 0.34 4.45 1.51 4.45 2.9V20z"/>
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="?attr/colorControlNormal" android:pathData="M12,5.5A3.5,3.5 0 0,1 15.5,9A3.5,3.5 0 0,1 12,12.5A3.5,3.5 0 0,1 8.5,9A3.5,3.5 0 0,1 12,5.5M5,8C5.56,8 6.08,8.15 6.53,8.42C6.38,9.85 6.8,11.27 7.66,12.38C7.16,13.34 6.16,14 5,14A3,3 0 0,1 2,11A3,3 0 0,1 5,8M19,8A3,3 0 0,1 22,11A3,3 0 0,1 19,14C17.84,14 16.84,13.34 16.34,12.38C17.2,11.27 17.62,9.85 17.47,8.42C17.92,8.15 18.44,8 19,8M5.5,18.25C5.5,16.18 8.41,14.5 12,14.5C15.59,14.5 18.5,16.18 18.5,18.25V20H5.5V18.25M0,20V18.5C0,17.11 1.89,15.94 4.45,15.6C3.86,16.28 3.5,17.22 3.5,18.25V20H0M24,20H20.5V18.25C20.5,17.22 20.14,16.28 19.55,15.6C22.11,15.94 24,17.11 24,18.5V20Z" />
</vector> </vector>

View File

@@ -1,8 +1,4 @@
<!-- drawable/account_star.xml --> <!--drawable/account_star.xml-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
android:height="24dp" <path android:fillColor="?attr/colorControlNormal" android:pathData="M15 14c-2.67 0-8 1.33-8 4v2h16v-2c0-2.67-5.33-4-8-4m0-2a4 4 0 0 0 4-4 4 4 0 0 0-4-4 4 4 0 0 0-4 4 4 4 0 0 0 4 4M5 13.28l2.45 1.49-0.65-2.81L9 10.08 6.11 9.83 5 7.19 3.87 9.83 1 10.08l2.18 1.88-0.68 2.81L5 13.28z"/>
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="?attr/colorControlNormal" android:pathData="M15,14C12.33,14 7,15.33 7,18V20H23V18C23,15.33 17.67,14 15,14M15,12A4,4 0 0,0 19,8A4,4 0 0,0 15,4A4,4 0 0,0 11,8A4,4 0 0,0 15,12M5,13.28L7.45,14.77L6.8,11.96L9,10.08L6.11,9.83L5,7.19L3.87,9.83L1,10.08L3.18,11.96L2.5,14.77L5,13.28Z" />
</vector> </vector>

View File

@@ -1,8 +1,4 @@
<!-- drawable/arrow_right.xml --> <!--drawable/arrow_right.xml-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
android:height="24dp" <path android:fillColor="#fff" android:pathData="M4 11v2h12l-5.5 5.5 1.42 1.42L19.84 12l-7.92-7.92L10.5 5.5 16 11H4z"/>
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#fff" android:pathData="M4,11V13H16L10.5,18.5L11.92,19.92L19.84,12L11.92,4.08L10.5,5.5L16,11H4Z" />
</vector> </vector>

View File

@@ -1,37 +1,14 @@
<animated-vector <animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt" xmlns:tools="http://schemas.android.com/tools" tools:ignore="newApi">
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
xmlns:tools="http://schemas.android.com/tools"
tools:ignore="newApi">
<aapt:attr name="android:drawable"> <aapt:attr name="android:drawable">
<vector <vector android:name="vector" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
android:name="vector" <path android:name="path" android:pathData="M 12 15.39 L 8.24 17.66 L 9.23 13.38 L 5.91 10.5 L 10.29 10.13 L 12 6.09 L 13.71 10.13 L 18.09 10.5 L 14.77 13.38 L 15.76 17.66 M 22 9.24 L 14.81 8.63 L 12 2 L 9.19 8.63 L 2 9.24 L 7.45 13.97 L 5.82 21 L 12 17.27 L 18.18 21 L 16.54 13.97 L 22 9.24 Z" android:fillColor="@color/material_orange_500"/>
android:width="24dp" <clip-path android:name="clip" android:pathData="M 2 21 L 2 21 L 22 21 L 22 21 Z"/>
android:height="24dp" <path android:name="path_1" android:pathData="M 12 17.27 L 18.18 21 L 16.54 13.97 L 22 9.24 L 14.81 8.62 L 12 2 L 9.19 8.62 L 2 9.24 L 7.45 13.97 L 5.82 21 L 12 17.27 Z" android:fillColor="@color/material_orange_500"/>
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:name="path"
android:pathData="M 12 15.39 L 8.24 17.66 L 9.23 13.38 L 5.91 10.5 L 10.29 10.13 L 12 6.09 L 13.71 10.13 L 18.09 10.5 L 14.77 13.38 L 15.76 17.66 M 22 9.24 L 14.81 8.63 L 12 2 L 9.19 8.63 L 2 9.24 L 7.45 13.97 L 5.82 21 L 12 17.27 L 18.18 21 L 16.54 13.97 L 22 9.24 Z"
android:fillColor="@color/material_orange_500"/>
<clip-path
android:name="clip"
android:pathData="M 2 21 L 2 21 L 22 21 L 22 21 Z"/>
<path
android:name="path_1"
android:pathData="M 12 17.27 L 18.18 21 L 16.54 13.97 L 22 9.24 L 14.81 8.62 L 12 2 L 9.19 8.62 L 2 9.24 L 7.45 13.97 L 5.82 21 L 12 17.27 Z"
android:fillColor="@color/material_orange_500"/>
</vector> </vector>
</aapt:attr> </aapt:attr>
<target android:name="clip"> <target android:name="clip">
<aapt:attr name="android:animation"> <aapt:attr name="android:animation">
<objectAnimator <objectAnimator android:propertyName="pathData" android:duration="500" android:valueFrom="M 2 21 L 2 21 L 22 21 L 22 21 Z" android:valueTo="M 2 2 L 2 21 L 22 21 L 22 2 Z" android:valueType="pathType" android:interpolator="@android:interpolator/fast_out_slow_in"/>
android:propertyName="pathData"
android:duration="500"
android:valueFrom="M 2 21 L 2 21 L 22 21 L 22 21 Z"
android:valueTo="M 2 2 L 2 21 L 22 21 L 22 2 Z"
android:valueType="pathType"
android:interpolator="@android:interpolator/fast_out_slow_in"/>
</aapt:attr> </aapt:attr>
</target> </target>
</animated-vector> </animated-vector>

View File

@@ -1,8 +1,4 @@
<!-- drawable/backspace_outline.xml --> <!--drawable/backspace_outline.xml-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
android:height="24dp" <path android:fillColor="#000" android:pathData="M19 15.59L17.59 17 14 13.41 10.41 17 9 15.59 12.59 12 9 8.41 10.41 7 14 10.59 17.59 7 19 8.41 15.41 12 19 15.59M22 3a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H7c-0.69 0-1.23-0.36-1.59-0.89L0 12l5.41-8.12C5.77 3.35 6.31 3 7 3h15m0 2H7l-4.72 7L7 19h15V5z"/>
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#000" android:pathData="M19,15.59L17.59,17L14,13.41L10.41,17L9,15.59L12.59,12L9,8.41L10.41,7L14,10.59L17.59,7L19,8.41L15.41,12L19,15.59M22,3A2,2 0 0,1 24,5V19A2,2 0 0,1 22,21H7C6.31,21 5.77,20.64 5.41,20.11L0,12L5.41,3.88C5.77,3.35 6.31,3 7,3H22M22,5H7L2.28,12L7,19H22V5Z" />
</vector> </vector>

View File

@@ -1,8 +1,4 @@
<!-- drawable/book_open.xml --> <!--drawable/book_open.xml-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
android:height="24dp" <path android:fillColor="?attr/colorControlNormal" android:pathData="M13 12h7v1.5h-7m0-4h7V11h-7m0 3.5h7V16h-7m8-12H3a2 2 0 0 0-2 2v13a2 2 0 0 0 2 2h18a2 2 0 0 0 2-2V6a2 2 0 0 0-2-2m0 15h-9V6h9"/>
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="?attr/colorControlNormal" android:pathData="M13,12H20V13.5H13M13,9.5H20V11H13M13,14.5H20V16H13M21,4H3A2,2 0 0,0 1,6V19A2,2 0 0,0 3,21H21A2,2 0 0,0 23,19V6A2,2 0 0,0 21,4M21,19H12V6H21" />
</vector> </vector>

View File

@@ -1,8 +1,4 @@
<!-- drawable/brush.xml --> <!--drawable/brush.xml-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
android:height="24dp" <path android:fillColor="?attr/colorControlNormal" android:pathData="M20.71 4.63l-1.34-1.34c-0.37-0.39-1.02-0.39-1.41 0L9 12.25 11.75 15l8.96-8.96c0.39-0.39 0.39-1.04 0-1.41M7 14a3 3 0 0 0-3 3c0 1.31-1.16 2-2 2 0.92 1.22 2.5 2 4 2a4 4 0 0 0 4-4 3 3 0 0 0-3-3z"/>
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="?attr/colorControlNormal" android:pathData="M20.71,4.63L19.37,3.29C19,2.9 18.35,2.9 17.96,3.29L9,12.25L11.75,15L20.71,6.04C21.1,5.65 21.1,5 20.71,4.63M7,14A3,3 0 0,0 4,17C4,18.31 2.84,19 2,19C2.92,20.22 4.5,21 6,21A4,4 0 0,0 10,17A3,3 0 0,0 7,14Z" />
</vector> </vector>

View File

@@ -1,8 +1,4 @@
<!-- drawable/cancel.xml --> <!--drawable/cancel.xml-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
android:height="24dp" <path android:fillColor="#fff" android:pathData="M12 2a10 10 0 0 1 10 10 10 10 0 0 1-10 10A10 10 0 0 1 2 12 10 10 0 0 1 12 2m0 2a8 8 0 0 0-8 8c0 1.85 0.63 3.55 1.68 4.91L16.91 5.68C15.55 4.63 13.85 4 12 4m0 16a8 8 0 0 0 8-8c0-1.85-0.63-3.55-1.68-4.91L7.09 18.32C8.45 19.37 10.15 20 12 20z"/>
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#fff" android:pathData="M12,2A10,10 0 0,1 22,12A10,10 0 0,1 12,22A10,10 0 0,1 2,12A10,10 0 0,1 12,2M12,4A8,8 0 0,0 4,12C4,13.85 4.63,15.55 5.68,16.91L16.91,5.68C15.55,4.63 13.85,4 12,4M12,20A8,8 0 0,0 20,12C20,10.15 19.37,8.45 18.32,7.09L7.09,18.32C8.45,19.37 10.15,20 12,20Z" />
</vector> </vector>

View File

@@ -1,8 +1,4 @@
<!-- drawable/clock_end.xml --> <!--drawable/clock_end.xml-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
android:height="24dp" <path android:fillColor="#fff" android:pathData="M12 1C8.14 1 5 4.14 5 8a7 7 0 0 0 7 7c3.86 0 7-3.13 7-7 0-3.86-3.14-7-7-7m0 2.15c2.67 0 4.85 2.17 4.85 4.85 0 2.68-2.18 4.85-4.85 4.85A4.85 4.85 0 0 1 7.15 8 4.85 4.85 0 0 1 12 3.15M11 5v3.69l3.19 1.84 0.75-1.3-2.44-1.41V5M15 16v3H3v2h12v3l4-4m0 0v4h2v-8h-2"/>
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#fff" android:pathData="M12,1C8.14,1 5,4.14 5,8A7,7 0 0,0 12,15C15.86,15 19,11.87 19,8C19,4.14 15.86,1 12,1M12,3.15C14.67,3.15 16.85,5.32 16.85,8C16.85,10.68 14.67,12.85 12,12.85A4.85,4.85 0 0,1 7.15,8A4.85,4.85 0 0,1 12,3.15M11,5V8.69L14.19,10.53L14.94,9.23L12.5,7.82V5M15,16V19H3V21H15V24L19,20M19,20V24H21V16H19" />
</vector> </vector>

View File

@@ -1,8 +1,4 @@
<!-- drawable/clock_start.xml --> <!--drawable/clock_start.xml-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
android:height="24dp" <path android:fillColor="#fff" android:pathData="M12 1C8.14 1 5 4.14 5 8a7 7 0 0 0 7 7c3.86 0 7-3.13 7-7 0-3.86-3.14-7-7-7m0 2.15c2.67 0 4.85 2.17 4.85 4.85 0 2.68-2.18 4.85-4.85 4.85A4.85 4.85 0 0 1 7.15 8 4.85 4.85 0 0 1 12 3.15M11 5v3.69l3.19 1.84 0.75-1.3-2.44-1.41V5M4 16v8h2v-3h12v3l4-4-4-4v3H6v-3"/>
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#fff" android:pathData="M12,1C8.14,1 5,4.14 5,8A7,7 0 0,0 12,15C15.86,15 19,11.87 19,8C19,4.14 15.86,1 12,1M12,3.15C14.67,3.15 16.85,5.32 16.85,8C16.85,10.68 14.67,12.85 12,12.85A4.85,4.85 0 0,1 7.15,8A4.85,4.85 0 0,1 12,3.15M11,5V8.69L14.19,10.53L14.94,9.23L12.5,7.82V5M4,16V24H6V21H18V24L22,20L18,16V19H6V16" />
</vector> </vector>

View File

@@ -0,0 +1,4 @@
<!--drawable/close.xml-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:fillColor="?attr/colorControlNormal" android:pathData="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12 19 6.41z"/>
</vector>

View File

@@ -0,0 +1,4 @@
<!--drawable/delete.xml-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:fillColor="?attr/colorControlNormal" android:pathData="M19 4h-3.5l-1-1h-5l-1 1H5v2h14M6 19a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2V7H6v12z"/>
</vector>

View File

@@ -1,10 +1,4 @@
<!-- drawable/fingerprint.xml --> <!--drawable/fingerprint.xml-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
xmlns:tools="http://schemas.android.com/tools" <path android:fillColor="#fff" android:pathData="M17.81 4.47c-0.08 0-0.16-0.02-0.23-0.06C15.66 3.42 14 3 12 3c-1.97 0-3.85 0.47-5.56 1.41C6.2 4.54 5.9 4.45 5.76 4.21c-0.13-0.24-0.04-0.55 0.2-0.68C7.82 2.5 9.86 2 12 2s4 0.47 6.04 1.5c0.25 0.15 0.34 0.45 0.21 0.69-0.09 0.18-0.25 0.28-0.44 0.28M3.5 9.72c-0.1 0-0.2-0.03-0.29-0.09C3 9.47 2.93 9.16 3.09 8.93c0.99-1.4 2.25-2.5 3.75-3.27C10 4.04 14 4.03 17.15 5.65c1.5 0.77 2.76 1.85 3.75 3.25 0.16 0.22 0.1 0.54-0.12 0.7-0.23 0.16-0.54 0.11-0.7-0.1-0.9-1.28-2.04-2.27-3.39-2.96-2.87-1.47-6.54-1.47-9.4 0.01-1.36 0.7-2.5 1.7-3.4 2.95C3.81 9.65 3.66 9.72 3.5 9.72m6.25 12.07c-0.13 0-0.25-0.05-0.35-0.15-0.87-0.87-1.34-1.43-2.01-2.64-0.69-1.23-1.05-2.73-1.05-4.34 0-2.97 2.54-5.39 5.66-5.39s5.66 2.42 5.66 5.39a0.5 0.5 0 0 1-0.5 0.5 0.5 0.5 0 0 1-0.5-0.5c0-2.42-2.09-4.39-4.66-4.39-2.57 0-4.66 1.97-4.66 4.39 0 1.44 0.32 2.77 0.93 3.84 0.64 1.16 1.08 1.65 1.85 2.43 0.19 0.2 0.19 0.51 0 0.71-0.12 0.1-0.24 0.15-0.37 0.15m7.17-1.85c-1.19 0-2.24-0.3-3.1-0.89-1.49-1.01-2.38-2.65-2.38-4.39a0.5 0.5 0 0 1 0.5-0.5 0.5 0.5 0 0 1 0.5 0.5c0 1.41 0.72 2.74 1.94 3.56 0.71 0.48 1.54 0.71 2.54 0.71 0.24 0 0.64-0.03 1.04-0.1 0.27-0.05 0.54 0.13 0.58 0.41 0.05 0.26-0.13 0.53-0.41 0.58-0.57 0.11-1.07 0.12-1.21 0.12M14.91 22h-0.13c-1.59-0.46-2.63-1.05-3.72-2.12-1.4-1.38-2.17-3.24-2.17-5.22 0-1.62 1.38-2.94 3.08-2.94 1.7 0 3.08 1.32 3.08 2.94 0 1.07 0.95 1.94 2.08 1.94 1.15 0 2.08-0.87 2.08-1.94 0-3.77-3.25-6.83-7.25-6.83-2.84 0-5.46 1.58-6.61 4.03-0.39 0.81-0.59 1.76-0.59 2.8 0 0.78 0.07 2.01 0.67 3.61 0.1 0.26-0.03 0.55-0.29 0.64C4.88 19 4.59 18.87 4.5 18.62 4 17.31 3.77 16 3.77 14.66c0-1.2 0.23-2.29 0.68-3.24 1.33-2.79 4.28-4.6 7.51-4.6 4.54 0 8.25 3.51 8.25 7.83 0 1.62-1.38 2.94-3.08 2.94-1.7 0-3.08-1.32-3.08-2.94 0-1.07-0.93-1.94-2.08-1.94s-2.08 0.87-2.08 1.94c0 1.71 0.66 3.31 1.87 4.51 0.95 0.94 1.86 1.46 3.27 1.84 0.27 0.08 0.42 0.36 0.35 0.62-0.05 0.23-0.26 0.38-0.47 0.38z" tools:ignore="VectorPath"/>
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#fff" android:pathData="M17.81,4.47C17.73,4.47 17.65,4.45 17.58,4.41C15.66,3.42 14,3 12,3C10.03,3 8.15,3.47 6.44,4.41C6.2,4.54 5.9,4.45 5.76,4.21C5.63,3.97 5.72,3.66 5.96,3.53C7.82,2.5 9.86,2 12,2C14.14,2 16,2.47 18.04,3.5C18.29,3.65 18.38,3.95 18.25,4.19C18.16,4.37 18,4.47 17.81,4.47M3.5,9.72C3.4,9.72 3.3,9.69 3.21,9.63C3,9.47 2.93,9.16 3.09,8.93C4.08,7.53 5.34,6.43 6.84,5.66C10,4.04 14,4.03 17.15,5.65C18.65,6.42 19.91,7.5 20.9,8.9C21.06,9.12 21,9.44 20.78,9.6C20.55,9.76 20.24,9.71 20.08,9.5C19.18,8.22 18.04,7.23 16.69,6.54C13.82,5.07 10.15,5.07 7.29,6.55C5.93,7.25 4.79,8.25 3.89,9.5C3.81,9.65 3.66,9.72 3.5,9.72M9.75,21.79C9.62,21.79 9.5,21.74 9.4,21.64C8.53,20.77 8.06,20.21 7.39,19C6.7,17.77 6.34,16.27 6.34,14.66C6.34,11.69 8.88,9.27 12,9.27C15.12,9.27 17.66,11.69 17.66,14.66A0.5,0.5 0 0,1 17.16,15.16A0.5,0.5 0 0,1 16.66,14.66C16.66,12.24 14.57,10.27 12,10.27C9.43,10.27 7.34,12.24 7.34,14.66C7.34,16.1 7.66,17.43 8.27,18.5C8.91,19.66 9.35,20.15 10.12,20.93C10.31,21.13 10.31,21.44 10.12,21.64C10,21.74 9.88,21.79 9.75,21.79M16.92,19.94C15.73,19.94 14.68,19.64 13.82,19.05C12.33,18.04 11.44,16.4 11.44,14.66A0.5,0.5 0 0,1 11.94,14.16A0.5,0.5 0 0,1 12.44,14.66C12.44,16.07 13.16,17.4 14.38,18.22C15.09,18.7 15.92,18.93 16.92,18.93C17.16,18.93 17.56,18.9 17.96,18.83C18.23,18.78 18.5,18.96 18.54,19.24C18.59,19.5 18.41,19.77 18.13,19.82C17.56,19.93 17.06,19.94 16.92,19.94M14.91,22C14.87,22 14.82,22 14.78,22C13.19,21.54 12.15,20.95 11.06,19.88C9.66,18.5 8.89,16.64 8.89,14.66C8.89,13.04 10.27,11.72 11.97,11.72C13.67,11.72 15.05,13.04 15.05,14.66C15.05,15.73 16,16.6 17.13,16.6C18.28,16.6 19.21,15.73 19.21,14.66C19.21,10.89 15.96,7.83 11.96,7.83C9.12,7.83 6.5,9.41 5.35,11.86C4.96,12.67 4.76,13.62 4.76,14.66C4.76,15.44 4.83,16.67 5.43,18.27C5.53,18.53 5.4,18.82 5.14,18.91C4.88,19 4.59,18.87 4.5,18.62C4,17.31 3.77,16 3.77,14.66C3.77,13.46 4,12.37 4.45,11.42C5.78,8.63 8.73,6.82 11.96,6.82C16.5,6.82 20.21,10.33 20.21,14.65C20.21,16.27 18.83,17.59 17.13,17.59C15.43,17.59 14.05,16.27 14.05,14.65C14.05,13.58 13.12,12.71 11.97,12.71C10.82,12.71 9.89,13.58 9.89,14.65C9.89,16.36 10.55,17.96 11.76,19.16C12.71,20.1 13.62,20.62 15.03,21C15.3,21.08 15.45,21.36 15.38,21.62C15.33,21.85 15.12,22 14.91,22Z"
tools:ignore="VectorPath" />
</vector> </vector>

View File

@@ -1,8 +1,4 @@
<!-- drawable/gender_female.xml --> <!--drawable/gender_female.xml-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
android:height="24dp" <path android:fillColor="?attr/colorControlNormal" android:pathData="M12 4a6 6 0 0 1 6 6c0 2.97-2.16 5.44-5 5.92V18h2v2h-2v2h-2v-2H9v-2h2v-2.08C8.16 15.44 6 12.97 6 10a6 6 0 0 1 6-6m0 2a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4z"/>
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="?attr/colorControlNormal" android:pathData="M12,4A6,6 0 0,1 18,10C18,12.97 15.84,15.44 13,15.92V18H15V20H13V22H11V20H9V18H11V15.92C8.16,15.44 6,12.97 6,10A6,6 0 0,1 12,4M12,6A4,4 0 0,0 8,10A4,4 0 0,0 12,14A4,4 0 0,0 16,10A4,4 0 0,0 12,6Z" />
</vector> </vector>

View File

@@ -1,8 +1,4 @@
<!-- drawable/gender_female.xml --> <!--drawable/gender_female.xml-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
android:height="24dp" <path android:fillColor="#FFF" android:pathData="M12 4a6 6 0 0 1 6 6c0 2.97-2.16 5.44-5 5.92V18h2v2h-2v2h-2v-2H9v-2h2v-2.08C8.16 15.44 6 12.97 6 10a6 6 0 0 1 6-6m0 2a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4z"/>
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#FFF" android:pathData="M12,4A6,6 0 0,1 18,10C18,12.97 15.84,15.44 13,15.92V18H15V20H13V22H11V20H9V18H11V15.92C8.16,15.44 6,12.97 6,10A6,6 0 0,1 12,4M12,6A4,4 0 0,0 8,10A4,4 0 0,0 12,14A4,4 0 0,0 16,10A4,4 0 0,0 12,6Z" />
</vector> </vector>

View File

@@ -1,8 +1,4 @@
<!-- drawable/gender_male.xml --> <!--drawable/gender_male.xml-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
android:height="24dp" <path android:fillColor="?attr/colorControlNormal" android:pathData="M9 9c1.29 0 2.5 0.41 3.47 1.11L17.58 5H13V3h8v8h-2V6.41l-5.11 5.09c0.7 1 1.11 2.2 1.11 3.5a6 6 0 0 1-6 6 6 6 0 0 1-6-6 6 6 0 0 1 6-6m0 2a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4z"/>
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="?attr/colorControlNormal" android:pathData="M9,9C10.29,9 11.5,9.41 12.47,10.11L17.58,5H13V3H21V11H19V6.41L13.89,11.5C14.59,12.5 15,13.7 15,15A6,6 0 0,1 9,21A6,6 0 0,1 3,15A6,6 0 0,1 9,9M9,11A4,4 0 0,0 5,15A4,4 0 0,0 9,19A4,4 0 0,0 13,15A4,4 0 0,0 9,11Z" />
</vector> </vector>

View File

@@ -1,8 +1,4 @@
<!-- drawable/gender_male.xml --> <!--drawable/gender_male.xml-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
android:height="24dp" <path android:fillColor="#FFF" android:pathData="M9 9c1.29 0 2.5 0.41 3.47 1.11L17.58 5H13V3h8v8h-2V6.41l-5.11 5.09c0.7 1 1.11 2.2 1.11 3.5a6 6 0 0 1-6 6 6 6 0 0 1-6-6 6 6 0 0 1 6-6m0 2a4 4 0 0 0-4 4 4 4 0 0 0 4 4 4 4 0 0 0 4-4 4 4 0 0 0-4-4z"/>
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#FFF" android:pathData="M9,9C10.29,9 11.5,9.41 12.47,10.11L17.58,5H13V3H21V11H19V6.41L13.89,11.5C14.59,12.5 15,13.7 15,15A6,6 0 0,1 9,21A6,6 0 0,1 3,15A6,6 0 0,1 9,9M9,11A4,4 0 0,0 5,15A4,4 0 0,0 9,19A4,4 0 0,0 13,15A4,4 0 0,0 9,11Z" />
</vector> </vector>

View File

@@ -1,8 +1,4 @@
<!-- drawable/github-circle.xml --> <!--drawable/github-circle.xml-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
android:height="24dp" <path android:fillColor="#000" android:pathData="M12 2A10 10 0 0 0 2 12c0 4.42 2.87 8.17 6.84 9.5 0.5 0.08 0.66-0.23 0.66-0.5v-1.69c-2.77 0.6-3.36-1.34-3.36-1.34-0.46-1.16-1.11-1.47-1.11-1.47-0.91-0.62 0.07-0.6 0.07-0.6 1 0.07 1.53 1.03 1.53 1.03 0.87 1.52 2.34 1.07 2.91 0.83 0.09-0.65 0.35-1.09 0.63-1.34-2.22-0.25-4.55-1.11-4.55-4.92 0-1.11 0.38-2 1.03-2.71-0.1-0.25-0.45-1.29 0.1-2.64 0 0 0.84-0.27 2.75 1.02 0.79-0.22 1.65-0.33 2.5-0.33 0.85 0 1.71 0.11 2.5 0.33 1.91-1.29 2.75-1.02 2.75-1.02 0.55 1.35 0.2 2.39 0.1 2.64 0.65 0.71 1.03 1.6 1.03 2.71 0 3.82-2.34 4.66-4.57 4.91 0.36 0.31 0.69 0.92 0.69 1.85V21c0 0.27 0.16 0.59 0.67 0.5C19.14 20.16 22 16.42 22 12A10 10 0 0 0 12 2z"/>
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#000" android:pathData="M12,2A10,10 0 0,0 2,12C2,16.42 4.87,20.17 8.84,21.5C9.34,21.58 9.5,21.27 9.5,21C9.5,20.77 9.5,20.14 9.5,19.31C6.73,19.91 6.14,17.97 6.14,17.97C5.68,16.81 5.03,16.5 5.03,16.5C4.12,15.88 5.1,15.9 5.1,15.9C6.1,15.97 6.63,16.93 6.63,16.93C7.5,18.45 8.97,18 9.54,17.76C9.63,17.11 9.89,16.67 10.17,16.42C7.95,16.17 5.62,15.31 5.62,11.5C5.62,10.39 6,9.5 6.65,8.79C6.55,8.54 6.2,7.5 6.75,6.15C6.75,6.15 7.59,5.88 9.5,7.17C10.29,6.95 11.15,6.84 12,6.84C12.85,6.84 13.71,6.95 14.5,7.17C16.41,5.88 17.25,6.15 17.25,6.15C17.8,7.5 17.45,8.54 17.35,8.79C18,9.5 18.38,10.39 18.38,11.5C18.38,15.32 16.04,16.16 13.81,16.41C14.17,16.72 14.5,17.33 14.5,18.26C14.5,19.6 14.5,20.68 14.5,21C14.5,21.27 14.66,21.59 15.17,21.5C19.14,20.16 22,16.42 22,12A10,10 0 0,0 12,2Z" />
</vector> </vector>

View File

@@ -0,0 +1,4 @@
<!--drawable/history.xml-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:fillColor="?attr/colorControlNormal" android:pathData="M13.5 8H12v5l4.28 2.54 0.72-1.21-3.5-2.08V8M13 3a9 9 0 0 0-9 9H1l3.96 4.03L9 12H6a7 7 0 0 1 7-7 7 7 0 0 1 7 7 7 7 0 0 1-7 7c-1.93 0-3.68-0.79-4.94-2.06l-1.42 1.42C8.27 20 10.5 21 13 21a9 9 0 0 0 9-9 9 9 0 0 0-9-9"/>
</vector>

View File

@@ -1,5 +1,3 @@
<vector android:height="24dp" <vector android:height="24dp" android:viewportHeight="24.0" android:viewportWidth="24.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
android:viewportHeight="24.0" android:viewportWidth="24.0" <path android:fillColor="#fff" android:pathData="M19 9h-4V3H9v6H5l7 7 7-7zM5 18v2h14v-2H5z"/>
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#fff" android:pathData="M19,9h-4V3H9v6H5l7,7 7,-7zM5,18v2h14v-2H5z"/>
</vector> </vector>

View File

@@ -1,73 +1,18 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- <!--~ Pupil, Hitomi.la viewer for Android ~ Copyright (C) 2019 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 <http://www.gnu.org/licenses/>.-->
~ Pupil, Hitomi.la viewer for Android <animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:aapt="http://schemas.android.com/aapt" tools:ignore="NewApi">
~ Copyright (C) 2019 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 <http://www.gnu.org/licenses/>.
-->
<animated-vector
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:aapt="http://schemas.android.com/aapt"
tools:ignore="NewApi">
<aapt:attr name="android:drawable"> <aapt:attr name="android:drawable">
<vector <vector android:name="vector" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24">
android:name="vector" <path android:name="path" android:pathData="M 19 9 L 15 9 L 15 3 L 9 3 L 9 9 L 5 9 L 12 16 L 19 9 Z" android:fillColor="#fff" android:strokeWidth="1"/>
android:width="24dp" <path android:name="path_2" android:pathData="M 5 19 L 19 19" android:fillColor="#fff" android:strokeColor="#fff" android:strokeWidth="2"/>
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:name="path"
android:pathData="M 19 9 L 15 9 L 15 3 L 9 3 L 9 9 L 5 9 L 12 16 L 19 9 Z"
android:fillColor="#fff"
android:strokeWidth="1"/>
<path
android:name="path_2"
android:pathData="M 5 19 L 19 19"
android:fillColor="#fff"
android:strokeColor="#fff"
android:strokeWidth="2"
android:strokeLineCap="butt"/>
</vector> </vector>
</aapt:attr> </aapt:attr>
<target android:name="path_2"> <target android:name="path_2">
<aapt:attr name="android:animation"> <aapt:attr name="android:animation">
<set> <set>
<objectAnimator <objectAnimator android:propertyName="trimPathEnd" android:duration="500" android:valueFrom="0" android:valueTo="0.8" android:valueType="floatType" android:interpolator="@android:interpolator/fast_out_slow_in"/>
android:propertyName="trimPathEnd" <objectAnimator android:propertyName="trimPathStart" android:startOffset="500" android:duration="500" android:valueFrom="0" android:valueTo="0.8" android:valueType="floatType" android:interpolator="@android:interpolator/fast_out_slow_in"/>
android:duration="500" <objectAnimator android:propertyName="trimPathOffset" android:duration="1000" android:valueFrom="0" android:valueTo="0.2" android:valueType="floatType" android:interpolator="@android:interpolator/fast_out_slow_in"/>
android:valueFrom="0"
android:valueTo="0.8"
android:valueType="floatType"
android:interpolator="@android:interpolator/fast_out_slow_in"/>
<objectAnimator
android:propertyName="trimPathStart"
android:startOffset="500"
android:duration="500"
android:valueFrom="0"
android:valueTo="0.8"
android:valueType="floatType"
android:interpolator="@android:interpolator/fast_out_slow_in"/>
<objectAnimator
android:propertyName="trimPathOffset"
android:duration="1000"
android:valueFrom="0"
android:valueTo="0.2"
android:valueType="floatType"
android:interpolator="@android:interpolator/fast_out_slow_in"/>
</set> </set>
</aapt:attr> </aapt:attr>
</target> </target>

View File

@@ -1,14 +1,4 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24.0" android:viewportHeight="24.0">
android:width="24dp" <path android:fillColor="#fff" android:pathData="M18.4 10.6C16.55 8.99 14.15 8 11.5 8c-4.65 0-8.58 3.03-9.96 7.22L3.9 16c1.05-3.19 4.05-5.5 7.6-5.5 1.95 0 3.73 0.72 5.12 1.88L13 16h9V7l-3.6 3.6z"/>
android:height="24dp" <path android:fillColor="#fff" android:pathData="M8.5 15a1.5 1.5 0 1 1 1.5 1.5A1.5 1.5 0 0 1 8.5 15z"/>
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#fff"
android:pathData="M18.4,10.6C16.55,8.99 14.15,8 11.5,8c-4.65,0 -8.58,3.03 -9.96,7.22L3.9,16c1.05,-3.19 4.05,-5.5 7.6,-5.5 1.95,0 3.73,0.72 5.12,1.88L13,16h9V7l-3.6,3.6z"/>
<path
android:fillColor="#fff"
android:pathData="M8.5,15
a1.5,1.5 0 1,1 1.5,1.5
a1.5,1.5 0 0,1 -1.5,-1.5 Z"/>
</vector> </vector>

View File

@@ -1,8 +1,4 @@
<!-- drawable/message.xml --> <!--drawable/message.xml-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
android:height="24dp" <path android:fillColor="#000" android:pathData="M20 2H4a2 2 0 0 0-2 2v18l4-4h14a2 2 0 0 0 2-2V4c0-1.11-0.9-2-2-2z"/>
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#000" android:pathData="M20,2H4A2,2 0 0,0 2,4V22L6,18H20A2,2 0 0,0 22,16V4C22,2.89 21.1,2 20,2Z" />
</vector> </vector>

View File

@@ -1,9 +1,3 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="24dp" android:viewportWidth="24.0" android:viewportHeight="24.0">
android:width="24dp" <path android:fillColor="#FF000000" android:pathData="M10 6L8.59 7.41 13.17 12l-4.58 4.59L10 18l6-6z"/>
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M10,6L8.59,7.41 13.17,12l-4.58,4.59L10,18l6,-6z"/>
</vector> </vector>

View File

@@ -1,22 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<vector <vector xmlns:android="http://schemas.android.com/apk/res/android" android:name="vector" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24" android:tintMode="multiply">
xmlns:android="http://schemas.android.com/apk/res/android" <path android:name="path_1" android:pathData="M 0 12 L 24 12" android:fillColor="#000" android:strokeColor="#b9f6ca" android:strokeWidth="24"/>
android:name="vector" <path android:name="path" android:pathData="M 0 12 L 24 12" android:fillColor="#000" android:strokeColor="#00c853" android:strokeWidth="24"/>
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tintMode="multiply">
<path
android:name="path_1"
android:pathData="M 0 12 L 24 12"
android:fillColor="#000"
android:strokeColor="#b9f6ca"
android:strokeWidth="24"/>
<path
android:name="path"
android:pathData="M 0 12 L 24 12"
android:fillColor="#000"
android:strokeColor="#00c853"
android:strokeWidth="24"/>
</vector> </vector>

View File

@@ -1,22 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<vector <vector xmlns:android="http://schemas.android.com/apk/res/android" android:name="vector" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24" android:tintMode="multiply">
xmlns:android="http://schemas.android.com/apk/res/android" <path android:name="path_1" android:pathData="M 0 12 L 24 12" android:fillColor="#000" android:strokeColor="#80d8ff" android:strokeWidth="24"/>
android:name="vector" <path android:name="path" android:pathData="M 0 12 L 24 12" android:fillColor="#000" android:strokeColor="#0091ea" android:strokeWidth="24"/>
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tintMode="multiply">
<path
android:name="path_1"
android:pathData="M 0 12 L 24 12"
android:fillColor="#000"
android:strokeColor="#80d8ff"
android:strokeWidth="24"/>
<path
android:name="path"
android:pathData="M 0 12 L 24 12"
android:fillColor="#000"
android:strokeColor="#0091ea"
android:strokeWidth="24"/>
</vector> </vector>

View File

@@ -1,38 +1,13 @@
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" <animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:aapt="http://schemas.android.com/aapt" tools:ignore="NewApi">
xmlns:tools="http://schemas.android.com/tools"
xmlns:aapt="http://schemas.android.com/aapt"
tools:ignore="NewApi">
<aapt:attr name="android:drawable"> <aapt:attr name="android:drawable">
<vector <vector android:name="vector" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24" android:tintMode="multiply">
android:name="vector" <path android:name="path_1" android:pathData="M 0 12 L 24 12" android:fillColor="#000" android:strokeColor="#b9f6ca" android:strokeWidth="24"/>
android:width="24dp" <path android:name="path" android:pathData="M 0 12 L 24 12" android:fillColor="#000" android:strokeColor="#00c853" android:strokeWidth="24"/>
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tintMode="multiply">
<path
android:name="path_1"
android:pathData="M 0 12 L 24 12"
android:fillColor="#000"
android:strokeColor="#b9f6ca"
android:strokeWidth="24"/>
<path
android:name="path"
android:pathData="M 0 12 L 24 12"
android:fillColor="#000"
android:strokeColor="#00c853"
android:strokeWidth="24"/>
</vector> </vector>
</aapt:attr> </aapt:attr>
<target android:name="path"> <target android:name="path">
<aapt:attr name="android:animation"> <aapt:attr name="android:animation">
<objectAnimator <objectAnimator android:propertyName="trimPathEnd" android:duration="1000" android:valueFrom="0" android:valueTo="1" android:valueType="floatType" android:interpolator="@android:interpolator/fast_out_slow_in"/>
android:propertyName="trimPathEnd"
android:duration="1000"
android:valueFrom="0"
android:valueTo="1"
android:valueType="floatType"
android:interpolator="@android:interpolator/fast_out_slow_in"/>
</aapt:attr> </aapt:attr>
</target> </target>
</animated-vector> </animated-vector>

View File

@@ -1,38 +1,13 @@
<animated-vector xmlns:android="http://schemas.android.com/apk/res/android" <animated-vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:aapt="http://schemas.android.com/aapt" tools:ignore="NewApi">
xmlns:tools="http://schemas.android.com/tools"
xmlns:aapt="http://schemas.android.com/aapt"
tools:ignore="NewApi">
<aapt:attr name="android:drawable"> <aapt:attr name="android:drawable">
<vector <vector android:name="vector" android:width="24dp" android:height="24dp" android:viewportWidth="24" android:viewportHeight="24" android:tintMode="multiply">
android:name="vector" <path android:name="path_1" android:pathData="M 0 12 L 24 12" android:fillColor="#000" android:strokeColor="#80d8ff" android:strokeWidth="24"/>
android:width="24dp" <path android:name="path" android:pathData="M 0 12 L 24 12" android:fillColor="#000" android:strokeColor="#0091ea" android:strokeWidth="24"/>
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tintMode="multiply">
<path
android:name="path_1"
android:pathData="M 0 12 L 24 12"
android:fillColor="#000"
android:strokeColor="#80d8ff"
android:strokeWidth="24"/>
<path
android:name="path"
android:pathData="M 0 12 L 24 12"
android:fillColor="#000"
android:strokeColor="#0091ea"
android:strokeWidth="24"/>
</vector> </vector>
</aapt:attr> </aapt:attr>
<target android:name="path"> <target android:name="path">
<aapt:attr name="android:animation"> <aapt:attr name="android:animation">
<objectAnimator <objectAnimator android:propertyName="trimPathEnd" android:duration="1000" android:valueFrom="0" android:valueTo="1" android:valueType="floatType" android:interpolator="@android:interpolator/fast_out_slow_in"/>
android:propertyName="trimPathEnd"
android:duration="1000"
android:valueFrom="0"
android:valueTo="1"
android:valueType="floatType"
android:interpolator="@android:interpolator/fast_out_slow_in"/>
</aapt:attr> </aapt:attr>
</target> </target>
</animated-vector> </animated-vector>

View File

@@ -1,4 +1,3 @@
<vector android:height="24dp" android:width="24dp" <vector android:height="24dp" android:width="24dp" android:viewportHeight="24.0" android:viewportWidth="24.0" xmlns:android="http://schemas.android.com/apk/res/android">
android:viewportHeight="24.0" android:viewportWidth="24.0" xmlns:android="http://schemas.android.com/apk/res/android"> <path android:fillColor="@color/material_orange_500" android:pathData="M12 15.39l-3.76 2.27 0.99-4.28-3.32-2.88 4.38-0.37L12 6.09l1.71 4.04 4.38 0.37-3.32 2.88 0.99 4.28M22 9.24l-7.19-0.61L12 2 9.19 8.63 2 9.24l5.45 4.73L5.82 21 12 17.27 18.18 21l-1.64-7.03L22 9.24z"/>
<path android:fillColor="@color/material_orange_500" android:pathData="M 12 15.39 L 8.24 17.66 L 9.23 13.38 L 5.91 10.5 L 10.29 10.13 L 12 6.09 L 13.71 10.13 L 18.09 10.5 L 14.77 13.38 L 15.76 17.66 M 22 9.24 L 14.81 8.63 L 12 2 L 9.19 8.63 L 2 9.24 L 7.45 13.97 L 5.82 21 L 12 17.27 L 18.18 21 L 16.54 13.97 L 22 9.24 Z"/>
</vector> </vector>

View File

@@ -1,4 +1,3 @@
<vector android:height="24dp" android:width="24dp" <vector android:height="24dp" android:width="24dp" android:viewportHeight="24.0" android:viewportWidth="24.0" xmlns:android="http://schemas.android.com/apk/res/android">
android:viewportHeight="24.0" android:viewportWidth="24.0" xmlns:android="http://schemas.android.com/apk/res/android"> <path android:fillColor="@color/material_orange_500" android:pathData="M12 17.27L18.18 21l-1.64-7.03L22 9.24l-7.19-0.62L12 2 9.19 8.62 2 9.24l5.45 4.73L5.82 21 12 17.27z"/>
<path android:fillColor="@color/material_orange_500" android:pathData="M 12 17.27 L 18.18 21 L 16.54 13.97 L 22 9.24 L 14.81 8.62 L 12 2 L 9.19 8.62 L 2 9.24 L 7.45 13.97 L 5.82 21 L 12 17.27 Z"/>
</vector> </vector>

View File

@@ -1,8 +1,4 @@
<!-- drawable/image_broken_variant.xml --> <!--drawable/image_broken_variant.xml-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
android:height="24dp" <path android:fillColor="#000" android:pathData="M21 5v6.59l-3-3.01-4 4.01-4-4-4 4-3-3.01V5a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2m-3 6.42l3 3.01V19a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-6.58l3 2.99 4-4 4 4"/>
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#000" android:pathData="M21,5V11.59L18,8.58L14,12.59L10,8.59L6,12.59L3,9.58V5A2,2 0 0,1 5,3H19A2,2 0 0,1 21,5M18,11.42L21,14.43V19A2,2 0 0,1 19,21H5A2,2 0 0,1 3,19V12.42L6,15.41L10,11.41L14,15.41" />
</vector> </vector>

View File

@@ -1,8 +1,4 @@
<!-- drawable/lastpass.xml --> <!--drawable/lastpass.xml-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
android:height="24dp" <path android:fillColor="#fff" android:pathData="M14 12a2 2 0 0 1 2-2 2 2 0 0 1 2 2 2 2 0 0 1-2 2 2 2 0 0 1-2-2m-6 0a2 2 0 0 1 2-2 2 2 0 0 1 2 2 2 2 0 0 1-2 2 2 2 0 0 1-2-2m-6 0a2 2 0 0 1 2-2 2 2 0 0 1 2 2 2 2 0 0 1-2 2 2 2 0 0 1-2-2m20-7h-2v14h2V5z"/>
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#fff" android:pathData="M14,12A2,2 0 0,1 16,10A2,2 0 0,1 18,12A2,2 0 0,1 16,14A2,2 0 0,1 14,12M8,12A2,2 0 0,1 10,10A2,2 0 0,1 12,12A2,2 0 0,1 10,14A2,2 0 0,1 8,12M2,12A2,2 0 0,1 4,10A2,2 0 0,1 6,12A2,2 0 0,1 4,14A2,2 0 0,1 2,12M22,5H20V19H22V5Z" />
</vector> </vector>

View File

@@ -1,8 +1,4 @@
<!-- drawable/lock_pattern.xml --> <!--drawable/lock_pattern.xml-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
android:height="24dp" <path android:fillColor="#fff" android:pathData="M7 3a4 4 0 0 1 4 4c0 1.86-1.27 3.43-3 3.87v2.26c0.37 0.09 0.72 0.24 1.04 0.43l4.52-4.52C13.2 8.44 13 7.75 13 7a4 4 0 0 1 4-4 4 4 0 0 1 4 4 4 4 0 0 1-4 4c-0.74 0-1.43-0.2-2-0.55L10.45 15C10.8 15.57 11 16.26 11 17a4 4 0 0 1-4 4 4 4 0 0 1-4-4c0-1.86 1.27-3.43 3-3.87v-2.26C4.27 10.43 3 8.86 3 7a4 4 0 0 1 4-4m10 10a4 4 0 0 1 4 4 4 4 0 0 1-4 4 4 4 0 0 1-4-4 4 4 0 0 1 4-4m0 2a2 2 0 0 0-2 2 2 2 0 0 0 2 2 2 2 0 0 0 2-2 2 2 0 0 0-2-2z"/>
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#fff" android:pathData="M7,3A4,4 0 0,1 11,7C11,8.86 9.73,10.43 8,10.87V13.13C8.37,13.22 8.72,13.37 9.04,13.56L13.56,9.04C13.2,8.44 13,7.75 13,7A4,4 0 0,1 17,3A4,4 0 0,1 21,7A4,4 0 0,1 17,11C16.26,11 15.57,10.8 15,10.45L10.45,15C10.8,15.57 11,16.26 11,17A4,4 0 0,1 7,21A4,4 0 0,1 3,17C3,15.14 4.27,13.57 6,13.13V10.87C4.27,10.43 3,8.86 3,7A4,4 0 0,1 7,3M17,13A4,4 0 0,1 21,17A4,4 0 0,1 17,21A4,4 0 0,1 13,17A4,4 0 0,1 17,13M17,15A2,2 0 0,0 15,17A2,2 0 0,0 17,19A2,2 0 0,0 19,17A2,2 0 0,0 17,15Z" />
</vector> </vector>

View File

@@ -1,8 +1,4 @@
<!-- drawable/menu.xml --> <!--drawable/menu.xml-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
android:height="24dp" <path android:fillColor="#fff" android:pathData="M3 6h18v2H3V6m0 5h18v2H3v-2m0 5h18v2H3v-2z"/>
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#fff" android:pathData="M3,6H21V8H3V6M3,11H21V13H3V11M3,16H21V18H3V16Z" />
</vector> </vector>

View File

@@ -1,8 +1,4 @@
<!-- drawable/numeric.xml --> <!--drawable/numeric.xml-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
android:height="24dp" <path android:fillColor="#fff" android:pathData="M4 17V9H2V7h4v10H4m18-2c0 1.11-0.9 2-2 2h-4v-2h4v-2h-2v-2h2V9h-4V7h4a2 2 0 0 1 2 2v1.5a1.5 1.5 0 0 1-1.5 1.5 1.5 1.5 0 0 1 1.5 1.5V15m-8 0v2H8v-4c0-1.11 0.9-2 2-2h2V9H8V7h4a2 2 0 0 1 2 2v2c0 1.11-0.9 2-2 2h-2v2h4z"/>
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#fff" android:pathData="M4,17V9H2V7H6V17H4M22,15C22,16.11 21.1,17 20,17H16V15H20V13H18V11H20V9H16V7H20A2,2 0 0,1 22,9V10.5A1.5,1.5 0 0,1 20.5,12A1.5,1.5 0 0,1 22,13.5V15M14,15V17H8V13C8,11.89 8.9,11 10,11H12V9H8V7H12A2,2 0 0,1 14,9V11C14,12.11 13.1,13 12,13H10V15H14Z" />
</vector> </vector>

View File

@@ -1,23 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- <!--~ Pupil, Hitomi.la viewer for Android ~ Copyright (C) 2020 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 <http://www.gnu.org/licenses/>.-->
~ Pupil, Hitomi.la viewer for Android <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
~ Copyright (C) 2020 tom5079 <solid android:color="@color/colorAccent"/>
~
~ 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 <http://www.gnu.org/licenses/>.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/colorAccent" />
</shape> </shape>

View File

@@ -1,34 +1,10 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- <!--~ Pupil, Hitomi.la viewer for Android ~ Copyright (C) 2020 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 <http://www.gnu.org/licenses/>.-->
~ Pupil, Hitomi.la viewer for Android
~ Copyright (C) 2020 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 <http://www.gnu.org/licenses/>.
-->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android"> <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item <item android:bottom="1dp" android:left="1dp" android:right="1dp" android:top="1dp">
android:bottom="1dp" <shape android:shape="rectangle">
android:left="1dp" <stroke android:width="1dp" android:color="#555555"/>
android:right="1dp" <solid android:color="@color/transparent"/>
android:top="1dp">
<shape android:shape="rectangle" >
<stroke
android:width="1dp"
android:color="#555555" />
<solid android:color="@color/transparent" />
</shape> </shape>
</item> </item>
</layer-list> </layer-list>

View File

@@ -1,8 +1,4 @@
<!-- drawable/refresh.xml --> <!--drawable/refresh.xml-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
android:height="24dp" <path android:fillColor="#fff" android:pathData="M17.65 6.35C16.2 4.9 14.21 4 12 4a8 8 0 0 0-8 8 8 8 0 0 0 8 8c3.73 0 6.84-2.55 7.73-6h-2.08c-0.82 2.33-3.04 4-5.65 4a6 6 0 0 1-6-6 6 6 0 0 1 6-6c1.66 0 3.14 0.69 4.22 1.78L13 11h7V4l-2.35 2.35z"/>
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#fff" android:pathData="M17.65,6.35C16.2,4.9 14.21,4 12,4A8,8 0 0,0 4,12A8,8 0 0,0 12,20C15.73,20 18.84,17.45 19.73,14H17.65C16.83,16.33 14.61,18 12,18A6,6 0 0,1 6,12A6,6 0 0,1 12,6C13.66,6 15.14,6.69 16.22,7.78L13,11H20V4L17.65,6.35Z" />
</vector> </vector>

View File

@@ -1,8 +1,4 @@
<!-- drawable/shuffle_variant.xml --> <!--drawable/shuffle_variant.xml-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
android:height="24dp" <path android:fillColor="#fff" android:pathData="M17 3l5.25 4.5L17 12l5.25 4.5L17 21v-3h-2.74l-2.82-2.82 2.12-2.12L15.5 15H17V9h-1.5l-9 9H2v-3h3.26l9-9H17V3M2 6h4.5l2.82 2.82-2.12 2.12L5.26 9H2V6z"/>
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#fff" android:pathData="M17,3L22.25,7.5L17,12L22.25,16.5L17,21V18H14.26L11.44,15.18L13.56,13.06L15.5,15H17V12L17,9H15.5L6.5,18H2V15H5.26L14.26,6H17V3M2,6H6.5L9.32,8.82L7.2,10.94L5.26,9H2V6Z" />
</vector> </vector>

View File

@@ -0,0 +1,4 @@
<!--drawable/swap_horizontal.xml-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
<path android:fillColor="?attr/colorControlNormal" android:pathData="M21 9l-4-4v3h-7v2h7v3M7 11l-4 4 4 4v-3h7v-2H7v-3z"/>
</vector>

View File

@@ -1,8 +1,4 @@
<!-- drawable/tag.xml --> <!--drawable/tag.xml-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
android:height="24dp" <path android:fillColor="?attr/colorControlNormal" android:pathData="M5.5 7A1.5 1.5 0 0 1 4 5.5 1.5 1.5 0 0 1 5.5 4 1.5 1.5 0 0 1 7 5.5 1.5 1.5 0 0 1 5.5 7m15.91 4.58l-9-9C12.05 2.22 11.55 2 11 2H4C2.89 2 2 2.89 2 4v7c0 0.55 0.22 1.05 0.59 1.41l8.99 9C11.95 21.77 12.45 22 13 22c0.55 0 1.05-0.23 1.41-0.59l7-7C21.78 14.05 22 13.55 22 13c0-0.56-0.23-1.06-0.59-1.42z"/>
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="?attr/colorControlNormal" android:pathData="M5.5,7A1.5,1.5 0 0,1 4,5.5A1.5,1.5 0 0,1 5.5,4A1.5,1.5 0 0,1 7,5.5A1.5,1.5 0 0,1 5.5,7M21.41,11.58L12.41,2.58C12.05,2.22 11.55,2 11,2H4C2.89,2 2,2.89 2,4V11C2,11.55 2.22,12.05 2.59,12.41L11.58,21.41C11.95,21.77 12.45,22 13,22C13.55,22 14.05,21.77 14.41,21.41L21.41,14.41C21.78,14.05 22,13.55 22,13C22,12.44 21.77,11.94 21.41,11.58Z" />
</vector> </vector>

View File

@@ -1,34 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <!--~ Pupil, Hitomi.la viewer for Android ~ Copyright (C) 2020 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 <http://www.gnu.org/licenses/>.-->
<!-- <vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="24dp" android:height="72dp" android:viewportWidth="64" android:viewportHeight="192">
~ Pupil, Hitomi.la viewer for Android <path android:pathData="M36 0h1l1 1h3l1 1h1l1 1h2l1 1h1l1 1h1l1 1 1 1 1 1 1 1h1v1l1 1 1 1 1 1v1l1 1 1 1v1l1 1v2l1 1v1l1 1v3l1 1v138l-1 1v3l-1 1v1l-1 1v2l-1 1v1l-1 1v1l-1 1-1 1-1 1-1 1v1h-1l-1 1-1 1-1 1-1 1h-1l-1 1h-1l-1 1h-2l-1 1h-1l-1 1h-3l-1 1H27l-1-1h-3l-1-1h-1l-1-1h-2l-1-1h-1l-1-1h-1l-1-1-1-1-1-1-1-1H9v-1l-1-1-1-1-1-1v-1l-1-1-1-1v-1l-1-1v-2l-1-1v-1l-1-1v-3l-1-1V27l1-1v-3l1-1v-1l1-1v-2l1-1v-1l1-1v-1l1-1 1-1 1-1 1-1V9h1l1-1 1-1 1-1 1-1h1l1-1h1l1-1h2l1-1h1l1-1h3l1-1h9z" android:fillColor="#4fc3f7"/>
~ Copyright (C) 2020 tom5079 <path android:fillColor="#FF000000" android:pathData="M42 80L32 70 22 80m20 32l-10 10-10-10" android:strokeWidth="2" android:fillAlpha="0" android:strokeColor="#000"/>
~ </vector>
~ 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 <http://www.gnu.org/licenses/>.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:topLeftRadius="44dp"
android:topRightRadius="44dp"
android:bottomLeftRadius="44dp" />
<padding
android:paddingLeft="22dp"
android:paddingRight="22dp" />
<solid android:color="@color/colorPrimary" />
</shape>

View File

@@ -1,27 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- <!--~ Pupil, Hitomi.la viewer for Android ~ Copyright (C) 2020 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 <http://www.gnu.org/licenses/>.-->
~ Pupil, Hitomi.la viewer for Android
~ Copyright (C) 2020 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 <http://www.gnu.org/licenses/>.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android"> <selector xmlns:android="http://schemas.android.com/apk/res/android">
<item <item android:state_pressed="true" android:drawable="@drawable/thumb"/>
android:state_pressed="true" <item android:drawable="@drawable/thumb"/>
android:drawable="@drawable/thumb"/>
<item
android:drawable="@drawable/thumb"/>
</selector> </selector>

View File

@@ -0,0 +1,5 @@
<!--~ Pupil, Hitomi.la viewer for Android ~ Copyright (C) 2020 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 <http://www.gnu.org/licenses/>.-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="72dp" android:height="24dp" android:viewportWidth="192" android:viewportHeight="64">
<path android:pathData="M29 64h-2l-1-1h-3l-1-1h-1l-1-1h-2l-1-1h-1l-1-1h-1l-1-1-1-1-1-1-1-1H9v-1l-1-1-1-1-1-1-1-1v-1l-1-1v-1l-1-1v-2l-1-1v-1l-1-1v-3l-1-1V27l1-1v-3l1-1v-1l1-1v-2l1-1v-1l1-1v-1l1-1 1-1 1-1 1-1V9h1l1-1 1-1 1-1 1-1h1l1-1h1l1-1h2l1-1h1l1-1h3l1-1h138l1 1h3l1 1h1l1 1h2l1 1h1l1 1h1l1 1 1 1 1 1 1 1h1v1l1 1 1 1 1 1 1 1v1l1 1v1l1 1v2l1 1v1l1 1v3l1 1v10l-1 1v3l-1 1v1l-1 1v2l-1 1v1l-1 1v1l-1 1-1 1-1 1-1 1v1h-1l-1 1-1 1-1 1-1 1h-1l-1 1h-1l-1 1h-2l-1 1h-1l-1 1h-3l-1 1H29z" android:fillColor="#4fc3f7"/>
<path android:fillColor="#FF000000" android:pathData="M80 42L70 32l10-10m32 20l10-10-10-10" android:strokeWidth="2" android:fillAlpha="0" android:strokeColor="#000"/>
</vector>

View File

@@ -1,30 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- <!--~ Pupil, Hitomi.la viewer for Android ~ Copyright (C) 2020 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 <http://www.gnu.org/licenses/>.-->
~ Pupil, Hitomi.la viewer for Android <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle">
~ Copyright (C) 2020 tom5079 <solid android:color="@android:color/transparent"/>
~ <padding android:top="10dp" android:left="10dp" android:right="10dp" android:bottom="10dp"/>
~ 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 <http://www.gnu.org/licenses/>.
-->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid android:color="@android:color/transparent" />
<padding
android:top="10dp"
android:left="10dp"
android:right="10dp"
android:bottom="10dp"/>
</shape> </shape>

View File

@@ -1,27 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- <!--~ Pupil, Hitomi.la viewer for Android ~ Copyright (C) 2020 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 <http://www.gnu.org/licenses/>.-->
~ Pupil, Hitomi.la viewer for Android
~ Copyright (C) 2020 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 <http://www.gnu.org/licenses/>.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android"> <selector xmlns:android="http://schemas.android.com/apk/res/android">
<item <item android:state_pressed="true" android:drawable="@drawable/track"/>
android:state_pressed="true" <item android:drawable="@drawable/track"/>
android:drawable="@drawable/track"/>
<item
android:drawable="@drawable/track"/>
</selector> </selector>

View File

@@ -1,8 +1,4 @@
<!-- drawable/translate.xml --> <!--drawable/translate.xml-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" <vector xmlns:android="http://schemas.android.com/apk/res/android" android:height="24dp" android:width="24dp" android:viewportWidth="24" android:viewportHeight="24">
android:height="24dp" <path android:fillColor="?attr/colorControlNormal" android:pathData="M12.87 15.07l-2.54-2.51 0.03-0.03c1.74-1.94 2.98-4.17 3.71-6.53H17V4h-7V2H8v2H1v2h11.17C11.5 7.92 10.44 9.75 9 11.35 8.07 10.32 7.3 9.19 6.69 8h-2c0.73 1.63 1.73 3.17 2.98 4.56l-5.09 5.02L4 19l5-5 3.11 3.11 0.76-2.04M18.5 10h-2L12 22h2l1.12-3h4.75L21 22h2l-4.5-12m-2.62 7l1.62-4.33L19.12 17h-3.24z"/>
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="?attr/colorControlNormal" android:pathData="M12.87,15.07L10.33,12.56L10.36,12.53C12.1,10.59 13.34,8.36 14.07,6H17V4H10V2H8V4H1V6H12.17C11.5,7.92 10.44,9.75 9,11.35C8.07,10.32 7.3,9.19 6.69,8H4.69C5.42,9.63 6.42,11.17 7.67,12.56L2.58,17.58L4,19L9,14L12.11,17.11L12.87,15.07M18.5,10H16.5L12,22H14L15.12,19H19.87L21,22H23L18.5,10M15.88,17L17.5,12.67L19.12,17H15.88Z" />
</vector> </vector>

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<!-- <!--
~ Pupil, Hitomi.la viewer for Android ~ Pupil, Hitomi.la viewer for Android
~ Copyright (C) 2020 tom5079 ~ Copyright (C) 2019 tom5079
~ ~
~ This program is free software: you can redistribute it and/or modify ~ 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 ~ it under the terms of the GNU General Public License as published by
@@ -63,26 +63,36 @@
android:text="@string/main_no_result" android:text="@string/main_no_result"
android:visibility="invisible"/> android:visibility="invisible"/>
<androidx.recyclerview.widget.RecyclerView <com.qtalk.recyclerviewfastscroller.RecyclerViewFastScroller
android:id="@+id/main_recyclerview"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:paddingTop="64dp" app:handleDrawable="@drawable/thumb"
android:clipToPadding="false" app:handleHasFixedSize="true"
app:fastScrollEnabled="true" app:handleHeight="72dp"
app:fastScrollHorizontalThumbDrawable="@drawable/thumb_drawable" app:handleWidth="24dp"
app:fastScrollVerticalThumbDrawable="@drawable/thumb_drawable" app:disableTrack="true"
app:fastScrollHorizontalTrackDrawable="@drawable/track_drawable" app:hideHandleAfter="1000"
app:fastScrollVerticalTrackDrawable="@drawable/track_drawable" app:trackMarginStart="64dp"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/> app:addLastItemPadding="true"
app:popupDrawable="@color/transparent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/main_recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="64dp"
android:clipToPadding="false"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/>
</com.qtalk.recyclerviewfastscroller.RecyclerViewFastScroller>
<com.github.clans.fab.FloatingActionMenu <com.github.clans.fab.FloatingActionMenu
android:id="@+id/main_fab" android:id="@+id/main_fab"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_gravity="bottom|end" android:layout_gravity="bottom|end"
android:layout_margin="16dp" android:layout_margin="16dp"
app:menu_colorNormal="@color/colorAccent"> app:menu_colorNormal="@color/colorAccent">
<com.github.clans.fab.FloatingActionButton <com.github.clans.fab.FloatingActionButton
android:id="@+id/main_fab_cancel" android:id="@+id/main_fab_cancel"

View File

@@ -63,18 +63,28 @@
android:text="@string/main_no_result" android:text="@string/main_no_result"
android:visibility="invisible"/> android:visibility="invisible"/>
<androidx.recyclerview.widget.RecyclerView <com.qtalk.recyclerviewfastscroller.RecyclerViewFastScroller
android:id="@+id/main_recyclerview"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:paddingTop="64dp" app:handleDrawable="@drawable/thumb"
android:clipToPadding="false" app:handleHasFixedSize="true"
app:fastScrollEnabled="true" app:handleHeight="72dp"
app:fastScrollHorizontalThumbDrawable="@drawable/thumb_drawable" app:handleWidth="24dp"
app:fastScrollVerticalThumbDrawable="@drawable/thumb_drawable" app:disableTrack="true"
app:fastScrollHorizontalTrackDrawable="@drawable/track_drawable" app:hideHandleAfter="1000"
app:fastScrollVerticalTrackDrawable="@drawable/track_drawable" app:trackMarginStart="64dp"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/> app:addLastItemPadding="true"
app:popupDrawable="@color/transparent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/main_recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingTop="64dp"
android:clipToPadding="false"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/>
</com.qtalk.recyclerviewfastscroller.RecyclerViewFastScroller>
<com.github.clans.fab.FloatingActionMenu <com.github.clans.fab.FloatingActionMenu
android:id="@+id/main_fab" android:id="@+id/main_fab"

View File

@@ -26,16 +26,26 @@
android:background="@color/dark_gray" android:background="@color/dark_gray"
tools:context=".ui.ReaderActivity"> tools:context=".ui.ReaderActivity">
<androidx.recyclerview.widget.RecyclerView <com.qtalk.recyclerviewfastscroller.RecyclerViewFastScroller
android:id="@+id/reader_recyclerview" android:id="@+id/scroller"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
app:fastScrollEnabled="true" app:handleDrawable="@drawable/thumb"
app:fastScrollHorizontalThumbDrawable="@drawable/thumb_drawable" app:handleHeight="72dp"
app:fastScrollVerticalThumbDrawable="@drawable/thumb_drawable" app:handleWidth="24dp"
app:fastScrollHorizontalTrackDrawable="@drawable/track_drawable" app:disableTrack="true"
app:fastScrollVerticalTrackDrawable="@drawable/track_drawable" app:hideHandleAfter="1000"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/> app:handleHasFixedSize="true"
app:addLastItemPadding="true"
app:popupDrawable="@color/transparent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/reader_recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/>
</com.qtalk.recyclerviewfastscroller.RecyclerViewFastScroller>
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@@ -25,7 +25,8 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="8dp" android:layout_margin="8dp"
app:cardCornerRadius="8dp" app:cardCornerRadius="8dp"
android:clipChildren="true"> android:clipChildren="true"
tools:ignore="RtlHardcoded">
<com.daimajia.swipe.SwipeLayout <com.daimajia.swipe.SwipeLayout
android:id="@+id/galleryblock_swipe_layout" android:id="@+id/galleryblock_swipe_layout"
@@ -70,169 +71,156 @@
</LinearLayout> </LinearLayout>
<LinearLayout <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/galleryblock_primary" android:id="@+id/galleryblock_primary"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content">
android:orientation="vertical"
android:foreground="?attr/selectableItemBackground"
android:focusable="true"
android:clickable="true"
tools:ignore="UnusedAttribute">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.core.widget.ContentLoadingProgressBar
style="?android:attr/progressBarStyleHorizontal"
android:id="@+id/galleryblock_progressbar"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="4dp"
android:visibility="gone"
app:layout_constraintTop_toTopOf="parent"/>
<ProgressBar <ImageView
style="@style/Widget.AppCompat.ProgressBar.Horizontal" android:id="@+id/galleryblock_progress_complete"
android:id="@+id/galleryblock_progressbar" android:layout_width="match_parent"
android:layout_width="match_parent" android:layout_height="4dp"
android:layout_height="4dp" android:visibility="invisible"
android:visibility="gone" android:scaleType="fitXY"
app:layout_constraintTop_toTopOf="parent"/> android:contentDescription="@string/reader_imageview_description"
app:layout_constraintTop_toTopOf="parent"/>
<ImageView <ImageView
android:id="@+id/galleryblock_progress_complete" android:id="@+id/galleryblock_thumbnail"
android:layout_width="match_parent" android:layout_width="150dp"
android:layout_height="4dp" android:layout_height="wrap_content"
android:visibility="invisible" android:contentDescription="@string/galleryblock_thumbnail_description"
android:scaleType="fitXY" android:adjustViewBounds="true"
android:contentDescription="@string/reader_imageview_description" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"/> app:layout_constraintTop_toBottomOf="@id/galleryblock_progressbar"/>
<ImageView <TextView
android:id="@+id/galleryblock_thumbnail" style="@style/TextAppearance.AppCompat.Headline"
android:layout_width="150dp" android:id="@+id/galleryblock_title"
android:layout_height="wrap_content" android:layout_width="0dp"
android:contentDescription="@string/galleryblock_thumbnail_description" android:layout_height="wrap_content"
android:adjustViewBounds="true" android:layout_marginTop="8dp"
app:layout_constraintLeft_toLeftOf="parent" android:layout_marginLeft="8dp"
app:layout_constraintTop_toBottomOf="@id/galleryblock_progressbar" app:layout_constraintLeft_toRightOf="@id/galleryblock_thumbnail"
app:layout_constraintBottom_toBottomOf="parent"/> app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/galleryblock_progressbar"/>
<TextView <TextView
style="@style/TextAppearance.AppCompat.Headline" style="@style/TextAppearance.AppCompat.Medium"
android:id="@+id/galleryblock_title" android:id="@+id/galleryblock_artist"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="8dp" android:layout_marginLeft="8dp"
android:layout_marginStart="8dp" app:layout_constraintLeft_toRightOf="@id/galleryblock_thumbnail"
android:layout_marginLeft="8dp" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toRightOf="@id/galleryblock_thumbnail" app:layout_constraintTop_toBottomOf="@id/galleryblock_title"/>
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<TextView <TextView
style="@style/TextAppearance.AppCompat.Medium" android:id="@+id/galleryblock_series"
android:id="@+id/galleryblock_artist" android:layout_width="0dp"
android:layout_width="0dp" android:layout_height="wrap_content"
android:layout_height="wrap_content" android:layout_marginLeft="8dp"
android:layout_marginStart="8dp" app:layout_constraintTop_toBottomOf="@id/galleryblock_artist"
android:layout_marginLeft="8dp" app:layout_constraintLeft_toRightOf="@id/galleryblock_thumbnail"
app:layout_constraintLeft_toRightOf="@id/galleryblock_thumbnail" app:layout_constraintRight_toRightOf="parent"/>
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/galleryblock_title"/>
<TextView <TextView
android:id="@+id/galleryblock_series" android:id="@+id/galleryblock_type"
android:layout_width="0dp" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="8dp" android:layout_marginLeft="8dp"
android:layout_marginLeft="8dp" app:layout_constraintTop_toBottomOf="@id/galleryblock_series"
app:layout_constraintTop_toBottomOf="@id/galleryblock_artist" app:layout_constraintLeft_toRightOf="@id/galleryblock_thumbnail" />
app:layout_constraintStart_toEndOf="@id/galleryblock_thumbnail"
app:layout_constraintEnd_toEndOf="parent"/>
<TextView <TextView
android:id="@+id/galleryblock_type" android:id="@+id/galleryblock_language"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="8dp" android:layout_marginLeft="8dp"
android:layout_marginLeft="8dp" android:layout_marginBottom="8dp"
app:layout_constraintTop_toBottomOf="@id/galleryblock_series" app:layout_constraintTop_toBottomOf="@id/galleryblock_type"
app:layout_constraintStart_toEndOf="@id/galleryblock_thumbnail" /> app:layout_constraintBottom_toTopOf="@id/galleryblock_padding"
app:layout_constraintLeft_toRightOf="@id/galleryblock_thumbnail" />
<TextView
android:id="@+id/galleryblock_language"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintTop_toBottomOf="@id/galleryblock_type"
app:layout_constraintBottom_toTopOf="@id/galleryblock_padding"
app:layout_constraintStart_toEndOf="@id/galleryblock_thumbnail" />
<View
android:id="@+id/galleryblock_padding"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@id/galleryblock_language"
app:layout_constraintBottom_toTopOf="@id/galleryblock_tag_group"/>
<com.google.android.material.chip.ChipGroup
android:id="@+id/galleryblock_tag_group"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
app:chipSpacing="4dp"
app:layout_constraintTop_toBottomOf="@id/galleryblock_padding"
app:layout_constraintLeft_toRightOf="@id/galleryblock_thumbnail"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
<View <View
android:id="@+id/galleryblock_padding"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/galleryblock_language"
app:layout_constraintBottom_toTopOf="@id/galleryblock_tag_group"/>
<com.google.android.material.chip.ChipGroup
android:id="@+id/galleryblock_tag_group"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
app:chipSpacing="4dp"
app:layout_constraintTop_toBottomOf="@id/galleryblock_padding"
app:layout_constraintLeft_toRightOf="@id/galleryblock_thumbnail"
app:layout_constraintRight_toRightOf="parent"/>
<androidx.constraintlayout.widget.Barrier
android:id="@+id/barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:constraint_referenced_ids="galleryblock_thumbnail,galleryblock_tag_group"/>
<View
android:id="@+id/divider"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="1dp" android:layout_height="1dp"
android:layout_margin="8dp" android:background="@color/light_gray"
android:background="@android:color/darker_gray"/> app:layout_constraintTop_toBottomOf="@id/barrier"
android:layout_margin="8dp"/>
<LinearLayout <TextView
android:layout_width="match_parent" android:id="@+id/galleryblock_id"
android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:paddingLeft="8dp" android:layout_marginLeft="8dp"
android:paddingRight="8dp" android:layout_marginTop="8dp"
android:paddingBottom="8dp" android:layout_marginBottom="8dp"
android:orientation="horizontal" app:layout_constraintTop_toBottomOf="@id/divider"
android:gravity="center_vertical"> app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"/>
<TextView <TextView
android:id="@+id/galleryblock_id" android:id="@+id/galleryblock_pagecount"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content"/> android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
app:layout_constraintTop_toBottomOf="@id/divider"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="parent"
app:layout_constraintRight_toLeftOf="parent" />
<View <ImageView
android:layout_width="0dp" android:id="@+id/galleryblock_favorite"
android:layout_height="1dp" android:contentDescription="@string/app_name"
android:layout_weight="1"/> android:layout_width="32dp"
android:layout_height="32dp"
android:layout_marginRight="8dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
app:srcCompat="@drawable/ic_star_empty"
app:layout_constraintTop_toBottomOf="@id/divider"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent" />
<TextView </androidx.constraintlayout.widget.ConstraintLayout>
android:id="@+id/galleryblock_pagecount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<View
android:layout_width="0dp"
android:layout_height="1dp"
android:layout_weight="1"/>
<ImageView
android:id="@+id/galleryblock_favorite"
android:contentDescription="@string/app_name"
android:layout_width="32dp"
android:layout_height="32dp"
app:srcCompat="@drawable/ic_star_empty"/>
</LinearLayout>
</LinearLayout>
</com.daimajia.swipe.SwipeLayout> </com.daimajia.swipe.SwipeLayout>

View File

@@ -17,61 +17,52 @@
~ along with this program. If not, see <http://www.gnu.org/licenses/>. ~ along with this program. If not, see <http://www.gnu.org/licenses/>.
--> -->
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"> app:layout_constraintHeight_max="2000dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:paddingBottom="8dp"
android:background="@drawable/reader_item_boundary">
<androidx.constraintlayout.widget.ConstraintLayout <LinearLayout
android:id="@+id/container" android:id="@+id/progress_layout"
android:layout_width="0dp" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="0dp"
app:layout_constraintHeight_max="2000dp"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintEnd_toEndOf="parent"
android:background="@drawable/reader_item_boundary"> android:gravity="center"
android:orientation="vertical">
<LinearLayout <ProgressBar
android:id="@+id/reader_item_progressbar"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent" style="?android:progressBarStyleHorizontal"
app:layout_constraintBottom_toBottomOf="parent" android:indeterminate="false"
app:layout_constraintStart_toStartOf="parent" android:progress="0"
app:layout_constraintEnd_toEndOf="parent" android:max="100"
android:gravity="center_horizontal" android:visibility="visible"/>
android:orientation="vertical">
<ProgressBar <TextView
android:id="@+id/reader_item_progressbar" android:id="@+id/reader_index"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
style="?android:progressBarStyleHorizontal" style="@style/TextAppearance.AppCompat.Caption"/>
android:indeterminate="false"
android:progress="0"
android:max="100"
android:visibility="visible"/>
<TextView </LinearLayout>
android:id="@+id/reader_index"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/TextAppearance.AppCompat.Caption"/>
</LinearLayout> <com.github.chrisbanes.photoview.PhotoView
android:id="@+id/image"
<com.github.chrisbanes.photoview.PhotoView android:adjustViewBounds="true"
android:id="@+id/image" android:scaleType="fitCenter"
android:adjustViewBounds="true" android:layout_width="match_parent"
android:scaleType="fitCenter" android:layout_height="wrap_content"
android:layout_width="match_parent" app:layout_constraintTop_toTopOf="parent"
android:layout_height="match_parent" app:layout_constraintBottom_toBottomOf="parent"/>
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
android:paddingBottom="8dp"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Pupil, Hitomi.la viewer for Android
~ Copyright (C) 2020 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 <http://www.gnu.org/licenses/>.
-->
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/count"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end|center"
android:layout_marginStart="@dimen/search_bar_search_input_left_margin"
android:gravity="end|center"
android:paddingBottom="4dp"
android:paddingTop="4dp"
android:textSize="@dimen/suggestion_body_text_size"
tools:text="body"
android:layout_marginLeft="@dimen/search_bar_search_input_left_margin" />

View File

@@ -27,7 +27,7 @@
<item android:id="@+id/main_drawer_history" <item android:id="@+id/main_drawer_history"
android:title="@string/main_drawer_history" android:title="@string/main_drawer_history"
android:icon="@drawable/ic_history"/> android:icon="@drawable/history"/>
<item android:id="@+id/main_drawer_downloads" <item android:id="@+id/main_drawer_downloads"
android:title="@string/main_drawer_downloads" android:title="@string/main_drawer_downloads"

View File

@@ -145,4 +145,7 @@
<string name="settings_invalid_download_folder_name">フォルダ名に使用できない文字が含まれています</string> <string name="settings_invalid_download_folder_name">フォルダ名に使用できない文字が含まれています</string>
<string name="settings_download_folder_name_message">%sに含まれている文字列を対応する変数に置換します\n\n%s</string> <string name="settings_download_folder_name_message">%sに含まれている文字列を対応する変数に置換します\n\n%s</string>
<string name="settings_manage_storage">ストレージ管理</string> <string name="settings_manage_storage">ストレージ管理</string>
<string name="settings_oss">オープンソースライセンス</string>
<string name="search_show_tags">お気に入りのタグを見る</string>
<string name="search_show_histories">履歴を見る</string>
</resources> </resources>

View File

@@ -145,4 +145,7 @@
<string name="settings_invalid_download_folder_name">폴더 패턴에 사용할 수 없는 문자가 포함되어 있습니다</string> <string name="settings_invalid_download_folder_name">폴더 패턴에 사용할 수 없는 문자가 포함되어 있습니다</string>
<string name="settings_download_folder_name_message">지원되는 변수는 %s 입니다\n\n%s</string> <string name="settings_download_folder_name_message">지원되는 변수는 %s 입니다\n\n%s</string>
<string name="settings_manage_storage">저장소 관리</string> <string name="settings_manage_storage">저장소 관리</string>
<string name="settings_oss">오픈 소스 라이선스</string>
<string name="search_show_histories">검색 기록 보기</string>
<string name="search_show_tags">즐겨찾기 태그 보기</string>
</resources> </resources>

View File

@@ -5,4 +5,9 @@
<dimen name="activity_vertical_margin">16dp</dimen> <dimen name="activity_vertical_margin">16dp</dimen>
<dimen name="galleryblock_thumbnail_thin">100dp</dimen> <dimen name="galleryblock_thumbnail_thin">100dp</dimen>
<dimen name="reader_max_height">2000dp</dimen>
<dimen name="thumb_width">24dp</dimen>
<dimen name="thumb_height">72dp</dimen>
</resources> </resources>

View File

@@ -75,6 +75,8 @@
<string name="search_hint">Search galleries</string> <string name="search_hint">Search galleries</string>
<string name="search_all">Search all galleries</string> <string name="search_all">Search all galleries</string>
<string name="search_show_histories">Show histories</string>
<string name="search_show_tags">Show favorite tags</string>
<string name="gallery_details">Details</string> <string name="gallery_details">Details</string>
<string name="gallery_thumbnails">Thumbnails</string> <string name="gallery_thumbnails">Thumbnails</string>
@@ -172,6 +174,7 @@
<string name="settings_import_old_galleries">Import old galleries</string> <string name="settings_import_old_galleries">Import old galleries</string>
<string name="settings_user_id">User ID</string> <string name="settings_user_id">User ID</string>
<string name="settings_user_id_toast">User ID is copied to clipboard</string> <string name="settings_user_id_toast">User ID is copied to clipboard</string>
<string name="settings_oss">Open Source Notice</string>
<!-- MANAGE FAVORITES --> <!-- MANAGE FAVORITES -->

View File

@@ -104,6 +104,10 @@
app:key="user_id" app:key="user_id"
app:title="@string/settings_user_id"/> app:title="@string/settings_user_id"/>
<Preference
app:key="oss"
app:title="@string/settings_oss"/>
</PreferenceCategory> </PreferenceCategory>
</PreferenceScreen> </PreferenceScreen>

View File

@@ -26,14 +26,21 @@ package xyz.quaver.pupil
* See [testing documentation](http://d.android.com/tools/testing). * See [testing documentation](http://d.android.com/tools/testing).
*/ */
import kotlinx.serialization.*
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import org.junit.Test import org.junit.Test
import java.lang.reflect.ParameterizedType
import kotlin.reflect.KClass
import kotlin.reflect.KType
import kotlin.reflect.typeOf
class ExampleUnitTest { class ExampleUnitTest {
@Test @Test
fun test() { fun test() {
val a = mutableSetOf<Int>()
print(a::class.java.methods.firstOrNull { it.name == "add" }?.genericParameterTypes?.firstOrNull() as? ParameterizedType)
} }
} }

View File

@@ -15,6 +15,7 @@ buildscript {
// in the individual module build.gradle files // in the individual module build.gradle files
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.2.1' classpath 'com.google.firebase:firebase-crashlytics-gradle:2.2.1'
classpath 'com.google.firebase:perf-plugin:1.3.1' classpath 'com.google.firebase:perf-plugin:1.3.1'
classpath 'com.google.android.gms:oss-licenses-plugin:0.10.2'
} }
} }
@@ -22,7 +23,6 @@ allprojects {
repositories { repositories {
google() google()
jcenter() jcenter()
mavenLocal()
maven { url "https://jitpack.io" } maven { url "https://jitpack.io" }
maven { url 'https://guardian.github.com/maven/repo-releases' } maven { url 'https://guardian.github.com/maven/repo-releases' }
} }

View File

@@ -19,4 +19,4 @@ kotlin.code.style=official
android.enableJetifier=true android.enableJetifier=true
android.useAndroidX=true android.useAndroidX=true
kotlin_version=1.4.0 kotlin_version=1.4.10