Initial commit
13
.gitignore
vendored
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
*.iml
|
||||||
|
.gradle
|
||||||
|
/local.properties
|
||||||
|
/.idea/caches
|
||||||
|
/.idea/libraries
|
||||||
|
/.idea/modules.xml
|
||||||
|
/.idea/workspace.xml
|
||||||
|
/.idea/navEditor.xml
|
||||||
|
/.idea/assetWizardSettings.xml
|
||||||
|
.DS_Store
|
||||||
|
/build
|
||||||
|
/captures
|
||||||
|
.externalNativeBuild
|
||||||
122
.idea/codeStyles/Project.xml
generated
Normal file
@@ -0,0 +1,122 @@
|
|||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<code_scheme name="Project" version="173">
|
||||||
|
<AndroidXmlCodeStyleSettings>
|
||||||
|
<option name="USE_CUSTOM_SETTINGS" value="true" />
|
||||||
|
</AndroidXmlCodeStyleSettings>
|
||||||
|
<JetCodeStyleSettings>
|
||||||
|
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||||
|
</JetCodeStyleSettings>
|
||||||
|
<XML>
|
||||||
|
<option name="XML_KEEP_LINE_BREAKS" value="false" />
|
||||||
|
<option name="XML_ALIGN_ATTRIBUTES" value="false" />
|
||||||
|
<option name="XML_SPACE_INSIDE_EMPTY_TAG" value="true" />
|
||||||
|
</XML>
|
||||||
|
<codeStyleSettings language="XML">
|
||||||
|
<option name="FORCE_REARRANGE_MODE" value="1" />
|
||||||
|
<indentOptions>
|
||||||
|
<option name="CONTINUATION_INDENT_SIZE" value="4" />
|
||||||
|
</indentOptions>
|
||||||
|
<arrangement>
|
||||||
|
<rules>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>xmlns:android</NAME>
|
||||||
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>xmlns:.*</NAME>
|
||||||
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
<order>BY_NAME</order>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*:id</NAME>
|
||||||
|
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*:name</NAME>
|
||||||
|
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>name</NAME>
|
||||||
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>style</NAME>
|
||||||
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*</NAME>
|
||||||
|
<XML_NAMESPACE>^$</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
<order>BY_NAME</order>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*</NAME>
|
||||||
|
<XML_NAMESPACE>http://schemas.android.com/apk/res/android</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
<order>ANDROID_ATTRIBUTE_ORDER</order>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
<section>
|
||||||
|
<rule>
|
||||||
|
<match>
|
||||||
|
<AND>
|
||||||
|
<NAME>.*</NAME>
|
||||||
|
<XML_NAMESPACE>.*</XML_NAMESPACE>
|
||||||
|
</AND>
|
||||||
|
</match>
|
||||||
|
<order>BY_NAME</order>
|
||||||
|
</rule>
|
||||||
|
</section>
|
||||||
|
</rules>
|
||||||
|
</arrangement>
|
||||||
|
</codeStyleSettings>
|
||||||
|
<codeStyleSettings language="kotlin">
|
||||||
|
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
|
||||||
|
</codeStyleSettings>
|
||||||
|
</code_scheme>
|
||||||
|
</component>
|
||||||
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<component name="ProjectCodeStyleConfiguration">
|
||||||
|
<state>
|
||||||
|
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||||
|
</state>
|
||||||
|
</component>
|
||||||
4
.idea/encodings.xml
generated
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="Encoding" addBOMForNewFiles="with NO BOM" />
|
||||||
|
</project>
|
||||||
20
.idea/gradle.xml
generated
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="GradleSettings">
|
||||||
|
<option name="linkedExternalProjectsSettings">
|
||||||
|
<GradleProjectSettings>
|
||||||
|
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||||
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
|
<option name="modules">
|
||||||
|
<set>
|
||||||
|
<option value="$PROJECT_DIR$" />
|
||||||
|
<option value="$PROJECT_DIR$/app" />
|
||||||
|
<option value="$PROJECT_DIR$/libpupil" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
<option name="resolveModulePerSourceSet" value="false" />
|
||||||
|
<option name="useQualifiedModuleNames" value="true" />
|
||||||
|
</GradleProjectSettings>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
10
.idea/inspectionProfiles/Project_Default.xml
generated
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<component name="InspectionProjectProfileManager">
|
||||||
|
<profile version="1.0">
|
||||||
|
<option name="myName" value="Project Default" />
|
||||||
|
<inspection_tool class="SpellCheckingInspection" enabled="false" level="TYPO" enabled_by_default="false">
|
||||||
|
<option name="processCode" value="true" />
|
||||||
|
<option name="processLiterals" value="true" />
|
||||||
|
<option name="processComments" value="true" />
|
||||||
|
</inspection_tool>
|
||||||
|
</profile>
|
||||||
|
</component>
|
||||||
6
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||||
|
<output url="file://$PROJECT_DIR$/classes" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
12
.idea/runConfigurations.xml
generated
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="RunConfigurationProducerService">
|
||||||
|
<option name="ignoredProducers">
|
||||||
|
<set>
|
||||||
|
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
|
||||||
|
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
|
||||||
|
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
|
||||||
|
</set>
|
||||||
|
</option>
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<project version="4">
|
||||||
|
<component name="VcsDirectoryMappings">
|
||||||
|
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||||
|
</component>
|
||||||
|
</project>
|
||||||
1
app/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/build
|
||||||
44
app/build.gradle
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
apply plugin: 'com.android.application'
|
||||||
|
apply plugin: 'kotlin-android'
|
||||||
|
apply plugin: 'kotlin-android-extensions'
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion 28
|
||||||
|
defaultConfig {
|
||||||
|
applicationId "xyz.quaver.pupil"
|
||||||
|
minSdkVersion 15
|
||||||
|
targetSdkVersion 28
|
||||||
|
versionCode 1
|
||||||
|
versionName "1.0"
|
||||||
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
}
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled false
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||||
|
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.2.1'
|
||||||
|
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.2.1'
|
||||||
|
implementation 'androidx.appcompat:appcompat:1.0.2'
|
||||||
|
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||||
|
implementation 'androidx.preference:preference:1.1.0-alpha05'
|
||||||
|
implementation 'com.google.android.material:material:1.0.0'
|
||||||
|
implementation 'com.github.arimorty:floatingsearchview:2.1.1'
|
||||||
|
implementation 'androidx.appcompat:appcompat:1.0.2'
|
||||||
|
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||||
|
testImplementation 'junit:junit:4.12'
|
||||||
|
androidTestImplementation 'androidx.test.ext:junit:1.1.0'
|
||||||
|
androidTestImplementation 'androidx.test:runner:1.1.1'
|
||||||
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
|
||||||
|
implementation project(path: ':libpupil')
|
||||||
|
}
|
||||||
|
|
||||||
|
androidExtensions {
|
||||||
|
experimental = true
|
||||||
|
}
|
||||||
21
app/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
# Add project specific ProGuard rules here.
|
||||||
|
# You can control the set of applied configuration files using the
|
||||||
|
# proguardFiles setting in build.gradle.
|
||||||
|
#
|
||||||
|
# For more details, see
|
||||||
|
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||||
|
|
||||||
|
# If your project uses WebView with JS, uncomment the following
|
||||||
|
# and specify the fully qualified class name to the JavaScript interface
|
||||||
|
# class:
|
||||||
|
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||||
|
# public *;
|
||||||
|
#}
|
||||||
|
|
||||||
|
# Uncomment this to preserve the line number information for
|
||||||
|
# debugging stack traces.
|
||||||
|
#-keepattributes SourceFile,LineNumberTable
|
||||||
|
|
||||||
|
# If you keep the line number information, uncomment this to
|
||||||
|
# hide the original source file name.
|
||||||
|
#-renamesourcefileattribute SourceFile
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package xyz.quaver.pupil
|
||||||
|
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.runBlocking
|
||||||
|
import org.junit.Assert.assertEquals
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instrumented test, which will execute on an Android device.
|
||||||
|
*
|
||||||
|
* See [testing documentation](http://d.android.com/tools/testing).
|
||||||
|
*/
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class ExampleInstrumentedTest {
|
||||||
|
@Test
|
||||||
|
fun useAppContext() {
|
||||||
|
// Context of the app under test.
|
||||||
|
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||||
|
assertEquals("xyz.quaver.pupil", appContext.packageName)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun checkCacheDir() {
|
||||||
|
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||||
|
val file = File(appContext.cacheDir, "imageCache/1412251/01.jpg.webp")
|
||||||
|
|
||||||
|
val bitmap = BitmapFactory.decodeFile(file.absolutePath)
|
||||||
|
|
||||||
|
Log.d("Pupil", bitmap.byteCount.toString())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@ExperimentalUnsignedTypes
|
||||||
|
fun test_doSearch() {
|
||||||
|
Log.d("TEST", "Starting...")
|
||||||
|
|
||||||
|
runBlocking {
|
||||||
|
CoroutineScope(Dispatchers.Main).launch {
|
||||||
|
Log.d("TEST", "This is started! wow")
|
||||||
|
}.join()
|
||||||
|
}
|
||||||
|
|
||||||
|
Log.d("TEST", "Finished! ...Really?")
|
||||||
|
}
|
||||||
|
}
|
||||||
33
app/src/main/AndroidManifest.xml
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="xyz.quaver.pupil">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
|
||||||
|
<application
|
||||||
|
android:allowBackup="true"
|
||||||
|
android:fullBackupContent="true"
|
||||||
|
android:icon="@mipmap/ic_launcher"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
|
android:supportsRtl="true"
|
||||||
|
android:theme="@style/AppTheme">
|
||||||
|
<activity android:name=".GalleryActivity"
|
||||||
|
android:configChanges="keyboardHidden|orientation|screenSize"/>
|
||||||
|
<activity
|
||||||
|
android:name=".SettingsActivity"
|
||||||
|
android:label="@string/settings_title" />
|
||||||
|
<activity
|
||||||
|
android:name=".MainActivity"
|
||||||
|
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||||
|
android:theme="@style/NoActionBarAppTheme">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN" />
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
|
</application>
|
||||||
|
|
||||||
|
</manifest>
|
||||||
BIN
app/src/main/ic_launcher-web.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
127
app/src/main/java/xyz/quaver/pupil/GalleryActivity.kt
Normal file
@@ -0,0 +1,127 @@
|
|||||||
|
package xyz.quaver.pupil
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.View
|
||||||
|
import android.view.WindowManager
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import kotlinx.android.synthetic.main.activity_gallery.*
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import xyz.quaver.hitomi.Reader
|
||||||
|
import xyz.quaver.hitomi.getReader
|
||||||
|
import xyz.quaver.hitomi.getReferer
|
||||||
|
import xyz.quaver.pupil.adapters.GalleryAdapter
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
import java.net.URL
|
||||||
|
import javax.net.ssl.HttpsURLConnection
|
||||||
|
|
||||||
|
class GalleryActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
private val images = ArrayList<String>()
|
||||||
|
private var galleryID = 0
|
||||||
|
private lateinit var reader: Deferred<Reader>
|
||||||
|
private var loadJob: Job? = null
|
||||||
|
private var screenMode = 0
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setContentView(R.layout.activity_gallery)
|
||||||
|
|
||||||
|
galleryID = intent.getIntExtra("GALLERY_ID", 0)
|
||||||
|
CoroutineScope(Dispatchers.Unconfined).launch {
|
||||||
|
reader = async(Dispatchers.IO) {
|
||||||
|
getReader(galleryID)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
initView()
|
||||||
|
loadImages()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
loadJob?.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initView() {
|
||||||
|
gallery_recyclerview.adapter = GalleryAdapter(images).apply {
|
||||||
|
setOnClick {
|
||||||
|
val attrs = window.attributes
|
||||||
|
|
||||||
|
screenMode = (screenMode+1)%2
|
||||||
|
|
||||||
|
when(screenMode) {
|
||||||
|
0 -> {
|
||||||
|
attrs.flags = attrs.flags and WindowManager.LayoutParams.FLAG_FULLSCREEN.inv()
|
||||||
|
supportActionBar?.show()
|
||||||
|
}
|
||||||
|
1 -> {
|
||||||
|
attrs.flags = attrs.flags or WindowManager.LayoutParams.FLAG_FULLSCREEN
|
||||||
|
supportActionBar?.hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
window.attributes = attrs
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun loadImages() {
|
||||||
|
|
||||||
|
fun webpUrlFromUrl(url: URL) = URL(url.toString().replace("/galleries/", "/webp/") + ".webp")
|
||||||
|
|
||||||
|
loadJob = CoroutineScope(Dispatchers.Default).launch {
|
||||||
|
val reader = reader.await()
|
||||||
|
|
||||||
|
launch(Dispatchers.Main) {
|
||||||
|
supportActionBar?.title = reader.title
|
||||||
|
|
||||||
|
with(gallery_progressbar) {
|
||||||
|
max = reader.images.size
|
||||||
|
progress = 0
|
||||||
|
|
||||||
|
visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
reader.images.chunked(8).forEach { chunked ->
|
||||||
|
chunked.map {
|
||||||
|
async(Dispatchers.IO) {
|
||||||
|
val url = if (it.second?.haswebp == 1) webpUrlFromUrl(it.first) else it.first
|
||||||
|
|
||||||
|
val fileName: String
|
||||||
|
|
||||||
|
with(url.path) {
|
||||||
|
fileName = substring(lastIndexOf('/')+1)
|
||||||
|
}
|
||||||
|
|
||||||
|
val cache = File(cacheDir, "/imageCache/$galleryID/$fileName")
|
||||||
|
|
||||||
|
if (!cache.exists())
|
||||||
|
with(url.openConnection() as HttpsURLConnection) {
|
||||||
|
setRequestProperty("Referer", getReferer(galleryID))
|
||||||
|
|
||||||
|
if (!cache.parentFile.exists())
|
||||||
|
cache.parentFile.mkdirs()
|
||||||
|
|
||||||
|
inputStream.copyTo(FileOutputStream(cache))
|
||||||
|
}
|
||||||
|
|
||||||
|
cache.absolutePath
|
||||||
|
}
|
||||||
|
}.forEach {
|
||||||
|
val cache = it.await()
|
||||||
|
|
||||||
|
launch(Dispatchers.Main) {
|
||||||
|
images.add(cache)
|
||||||
|
gallery_recyclerview.adapter?.notifyItemInserted(images.size - 1)
|
||||||
|
gallery_progressbar.progress++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
launch(Dispatchers.Main) {
|
||||||
|
gallery_progressbar.visibility = View.GONE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
296
app/src/main/java/xyz/quaver/pupil/MainActivity.kt
Normal file
@@ -0,0 +1,296 @@
|
|||||||
|
package xyz.quaver.pupil
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.preference.PreferenceManager
|
||||||
|
import android.text.*
|
||||||
|
import android.text.style.AlignmentSpan
|
||||||
|
import android.view.View
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.core.content.res.ResourcesCompat
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.arlib.floatingsearchview.FloatingSearchView
|
||||||
|
import com.arlib.floatingsearchview.suggestions.model.SearchSuggestion
|
||||||
|
import com.arlib.floatingsearchview.util.view.SearchInputView
|
||||||
|
import com.google.android.material.appbar.AppBarLayout
|
||||||
|
import kotlinx.android.synthetic.main.activity_main.*
|
||||||
|
import kotlinx.coroutines.*
|
||||||
|
import xyz.quaver.hitomi.*
|
||||||
|
import xyz.quaver.pupil.adapters.GalleryBlockAdapter
|
||||||
|
import xyz.quaver.pupil.types.TagSuggestion
|
||||||
|
import xyz.quaver.pupil.util.SetLineOverlap
|
||||||
|
import javax.net.ssl.HttpsURLConnection
|
||||||
|
|
||||||
|
class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
private val galleries = ArrayList<Pair<GalleryBlock, Bitmap?>>()
|
||||||
|
|
||||||
|
private var isLoading = false
|
||||||
|
private var query = ""
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setContentView(R.layout.activity_main)
|
||||||
|
|
||||||
|
main_appbar_layout.addOnOffsetChangedListener(
|
||||||
|
AppBarLayout.OnOffsetChangedListener { _, p1 ->
|
||||||
|
main_searchview.translationY = p1.toFloat()
|
||||||
|
main_recyclerview.translationY = p1.toFloat()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
with(main_swipe_layout) {
|
||||||
|
setProgressViewOffset(false, 0, resources.getDimensionPixelSize(R.dimen.progress_view_offset))
|
||||||
|
|
||||||
|
setOnRefreshListener {
|
||||||
|
runBlocking {
|
||||||
|
cleanJob?.join()
|
||||||
|
}
|
||||||
|
fetchGalleries(query, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setupRecyclerView()
|
||||||
|
setupSearchBar()
|
||||||
|
fetchGalleries(query)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setupRecyclerView() {
|
||||||
|
with(main_recyclerview) {
|
||||||
|
adapter = GalleryBlockAdapter(galleries).apply {
|
||||||
|
setClickListener {
|
||||||
|
val intent = Intent(this@MainActivity, GalleryActivity::class.java)
|
||||||
|
intent.putExtra("GALLERY_ID", it)
|
||||||
|
|
||||||
|
//TODO: Maybe sprinke some transitions will be nice :D
|
||||||
|
startActivity(intent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addOnScrollListener(
|
||||||
|
object: RecyclerView.OnScrollListener() {
|
||||||
|
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||||
|
super.onScrolled(recyclerView, dx, dy)
|
||||||
|
|
||||||
|
val layoutManager = recyclerView.layoutManager as LinearLayoutManager
|
||||||
|
|
||||||
|
if (!isLoading)
|
||||||
|
if (layoutManager.findLastCompletelyVisibleItemPosition() == galleries.size)
|
||||||
|
fetchGalleries(query)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private var suggestionJob : Job? = null
|
||||||
|
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())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
with(main_searchview as FloatingSearchView) {
|
||||||
|
setOnMenuItemClickListener {
|
||||||
|
when(it.itemId) {
|
||||||
|
R.id.main_menu_settings -> startActivity(Intent(this@MainActivity, SettingsActivity::class.java))
|
||||||
|
R.id.main_menu_search -> setSearchFocused(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setOnQueryChangeListener { _, query ->
|
||||||
|
clearSuggestions()
|
||||||
|
|
||||||
|
if (query.isEmpty() or query.endsWith(' '))
|
||||||
|
return@setOnQueryChangeListener
|
||||||
|
|
||||||
|
val currentQuery = query.split(" ").last().replace('_', ' ')
|
||||||
|
|
||||||
|
suggestionJob?.cancel()
|
||||||
|
|
||||||
|
suggestionJob = CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
val suggestions = getSuggestionsForQuery(currentQuery).map { TagSuggestion(it) }
|
||||||
|
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
swapSuggestions(suggestions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setOnBindSuggestionCallback { _, leftIcon, textView, item, _ ->
|
||||||
|
val suggestion = item as TagSuggestion
|
||||||
|
|
||||||
|
leftIcon.setImageDrawable(
|
||||||
|
ResourcesCompat.getDrawable(
|
||||||
|
resources,
|
||||||
|
when(suggestion.n) {
|
||||||
|
"female" -> R.drawable.ic_gender_female
|
||||||
|
"male" -> R.drawable.ic_gender_male
|
||||||
|
"language" -> R.drawable.ic_translate
|
||||||
|
"group" -> R.drawable.ic_account_group
|
||||||
|
"character" -> R.drawable.ic_account_star
|
||||||
|
"series" -> R.drawable.ic_book_open
|
||||||
|
"artist" -> R.drawable.ic_brush
|
||||||
|
else -> R.drawable.ic_tag
|
||||||
|
},
|
||||||
|
null)
|
||||||
|
)
|
||||||
|
|
||||||
|
val text = "${suggestion.s}\n ${suggestion.t}"
|
||||||
|
|
||||||
|
val len = text.length
|
||||||
|
val left = suggestion.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?) {
|
||||||
|
val suggestion = searchSuggestion as TagSuggestion
|
||||||
|
|
||||||
|
with(searchInputView.text) {
|
||||||
|
delete(if (lastIndexOf(' ') == -1) 0 else lastIndexOf(' ')+1, length)
|
||||||
|
append("${suggestion.n}:${suggestion.s.replace(Regex("\\s"), "_")} ")
|
||||||
|
}
|
||||||
|
|
||||||
|
clearSuggestions()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSearchAction(currentQuery: String?) {
|
||||||
|
//Do search on onFocusCleared()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
setOnFocusChangeListener(object: FloatingSearchView.OnFocusChangeListener {
|
||||||
|
override fun onFocus() {
|
||||||
|
//Do Nothing
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFocusCleared() {
|
||||||
|
suggestionJob?.cancel()
|
||||||
|
|
||||||
|
val query = searchInputView.text.toString()
|
||||||
|
|
||||||
|
if (query != this@MainActivity.query) {
|
||||||
|
this@MainActivity.query = query
|
||||||
|
|
||||||
|
fetchGalleries(query, true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val cache = ArrayList<Int>()
|
||||||
|
private var currentFetchingJob: Job? = null
|
||||||
|
private var cleanJob: Job? = null
|
||||||
|
|
||||||
|
private fun cancelFetch() {
|
||||||
|
isLoading = false
|
||||||
|
|
||||||
|
runBlocking {
|
||||||
|
cleanJob?.join()
|
||||||
|
currentFetchingJob?.cancelAndJoin()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun fetchGalleries(query: String, clear: Boolean = false) {
|
||||||
|
val preference = PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
|
val perPage = preference.getString("per_page", "25")?.toInt() ?: 25
|
||||||
|
val defaultQuery = preference.getString("default_query", "")!!
|
||||||
|
|
||||||
|
if (clear) {
|
||||||
|
cancelFetch()
|
||||||
|
cleanJob = CoroutineScope(Dispatchers.Main).launch {
|
||||||
|
cache.clear()
|
||||||
|
galleries.clear()
|
||||||
|
|
||||||
|
main_recyclerview.adapter?.notifyDataSetChanged()
|
||||||
|
|
||||||
|
main_noresult.visibility = View.INVISIBLE
|
||||||
|
main_progressbar.show()
|
||||||
|
main_swipe_layout.isRefreshing = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isLoading)
|
||||||
|
return
|
||||||
|
|
||||||
|
isLoading = true
|
||||||
|
|
||||||
|
currentFetchingJob = CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
try {
|
||||||
|
val galleryIDs: List<Int>
|
||||||
|
|
||||||
|
cleanJob?.join()
|
||||||
|
|
||||||
|
if (query.isEmpty() && defaultQuery.isEmpty())
|
||||||
|
galleryIDs = fetchNozomi(start = galleries.size, count = perPage)
|
||||||
|
else {
|
||||||
|
if (cache.isEmpty())
|
||||||
|
cache.addAll(doSearch("$defaultQuery $query"))
|
||||||
|
|
||||||
|
galleryIDs = cache.slice(galleries.size until Math.min(galleries.size + perPage, cache.size))
|
||||||
|
|
||||||
|
with(main_recyclerview.adapter as GalleryBlockAdapter) {
|
||||||
|
noMore = galleries.size + perPage >= cache.size
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (query.isNotEmpty() and defaultQuery.isNotEmpty() and cache.isNullOrEmpty()) {
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
main_noresult.visibility = View.VISIBLE
|
||||||
|
main_progressbar.hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
galleryIDs.chunked(4).forEach { chunked ->
|
||||||
|
chunked.map {
|
||||||
|
async {
|
||||||
|
val galleryBlock = getGalleryBlock(it)
|
||||||
|
val thumbnail: Bitmap
|
||||||
|
|
||||||
|
with(galleryBlock.thumbnails[0].openConnection() as HttpsURLConnection) {
|
||||||
|
thumbnail = BitmapFactory.decodeStream(inputStream)
|
||||||
|
}
|
||||||
|
|
||||||
|
Pair(galleryBlock, thumbnail)
|
||||||
|
}
|
||||||
|
}.forEach {
|
||||||
|
val galleryBlock = it.await()
|
||||||
|
|
||||||
|
withContext(Dispatchers.Main) {
|
||||||
|
main_progressbar.hide()
|
||||||
|
|
||||||
|
galleries.add(galleryBlock)
|
||||||
|
main_recyclerview.adapter?.notifyItemInserted(galleries.size - 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
isLoading = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
80
app/src/main/java/xyz/quaver/pupil/SettingsActivity.kt
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
package xyz.quaver.pupil
|
||||||
|
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.MenuItem
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import androidx.preference.Preference
|
||||||
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
|
|
||||||
|
class SettingsActivity : AppCompatActivity() {
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
setContentView(R.layout.settings_activity)
|
||||||
|
supportFragmentManager
|
||||||
|
.beginTransaction()
|
||||||
|
.replace(R.id.settings, SettingsFragment())
|
||||||
|
.commit()
|
||||||
|
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
class SettingsFragment : PreferenceFragmentCompat() {
|
||||||
|
|
||||||
|
private val suffix = listOf(
|
||||||
|
"B",
|
||||||
|
"kB",
|
||||||
|
"MB",
|
||||||
|
"GB",
|
||||||
|
"TB" //really?
|
||||||
|
)
|
||||||
|
|
||||||
|
private fun getCacheSize() : String {
|
||||||
|
var size = context!!.cacheDir.walk().map { it.length() }.sum()
|
||||||
|
var suffixIndex = 0
|
||||||
|
|
||||||
|
while (size >= 1024) {
|
||||||
|
size /= 1024
|
||||||
|
suffixIndex++
|
||||||
|
}
|
||||||
|
|
||||||
|
return getString(R.string.settings_delete_cache_summary, size, suffix[suffixIndex])
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
|
setPreferencesFromResource(R.xml.root_preferences, rootKey)
|
||||||
|
|
||||||
|
with(findPreference<Preference>("delete_cache")) {
|
||||||
|
this ?: return@with
|
||||||
|
|
||||||
|
summary = getCacheSize()
|
||||||
|
|
||||||
|
setOnPreferenceClickListener {
|
||||||
|
AlertDialog.Builder(context).apply {
|
||||||
|
setTitle(getString(R.string.settings_delete_cache_alert_title))
|
||||||
|
setMessage(getString(R.string.settings_delete_cache_alert_message))
|
||||||
|
setPositiveButton(android.R.string.yes) { _, _ ->
|
||||||
|
with(context.cacheDir) {
|
||||||
|
if (exists())
|
||||||
|
deleteRecursively()
|
||||||
|
}
|
||||||
|
|
||||||
|
summary = getCacheSize()
|
||||||
|
}
|
||||||
|
setNegativeButton(android.R.string.no) { _, _ -> }
|
||||||
|
}.show()
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
|
||||||
|
when (item?.itemId) {
|
||||||
|
android.R.id.home -> onBackPressed()
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package xyz.quaver.pupil.adapters
|
||||||
|
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.ImageView
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import xyz.quaver.pupil.R
|
||||||
|
|
||||||
|
class GalleryAdapter(private val images: List<String>) : RecyclerView.Adapter<GalleryAdapter.ViewHolder>() {
|
||||||
|
|
||||||
|
class ViewHolder(val view: ImageView) : RecyclerView.ViewHolder(view)
|
||||||
|
|
||||||
|
private var onClick: (() -> Unit)? = null
|
||||||
|
fun setOnClick(callback: (() -> Unit)?) {
|
||||||
|
this.onClick = callback
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||||
|
LayoutInflater.from(parent.context).inflate(
|
||||||
|
R.layout.item_gallery, parent, false
|
||||||
|
).let {
|
||||||
|
return ViewHolder(it as ImageView)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||||
|
with(holder.view) {
|
||||||
|
setOnClickListener {
|
||||||
|
onClick?.invoke()
|
||||||
|
}
|
||||||
|
|
||||||
|
CoroutineScope(Dispatchers.Default).launch {
|
||||||
|
val options = BitmapFactory.Options()
|
||||||
|
|
||||||
|
options.inJustDecodeBounds = true
|
||||||
|
BitmapFactory.decodeFile(images[position], options)
|
||||||
|
|
||||||
|
options.inSampleSize = options.outWidth /
|
||||||
|
context.resources.displayMetrics.widthPixels
|
||||||
|
|
||||||
|
options.inJustDecodeBounds = false
|
||||||
|
|
||||||
|
val image = BitmapFactory.decodeFile(images[position], options)
|
||||||
|
|
||||||
|
launch(Dispatchers.Main) {
|
||||||
|
setImageBitmap(image)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount() = images.size
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,109 @@
|
|||||||
|
package xyz.quaver.pupil.adapters
|
||||||
|
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.LinearLayout
|
||||||
|
import androidx.cardview.widget.CardView
|
||||||
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
|
import com.google.android.material.chip.Chip
|
||||||
|
import kotlinx.android.synthetic.main.item_galleryblock.view.*
|
||||||
|
import xyz.quaver.hitomi.GalleryBlock
|
||||||
|
import xyz.quaver.hitomi.toTag
|
||||||
|
import xyz.quaver.pupil.R
|
||||||
|
|
||||||
|
class GalleryBlockAdapter(private val galleries: List<Pair<GalleryBlock, Bitmap?>>) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||||
|
|
||||||
|
private enum class ViewType {
|
||||||
|
VIEW_ITEM,
|
||||||
|
VIEW_PROG
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun String.wordCapitalize() : String {
|
||||||
|
val result = ArrayList<String>()
|
||||||
|
|
||||||
|
for (word in this.split(" "))
|
||||||
|
result.add(word.capitalize())
|
||||||
|
|
||||||
|
return result.joinToString(" ")
|
||||||
|
}
|
||||||
|
|
||||||
|
var noMore = false
|
||||||
|
|
||||||
|
class ViewHolder(val view: CardView) : RecyclerView.ViewHolder(view)
|
||||||
|
class ProgressViewHolder(view: LinearLayout) : RecyclerView.ViewHolder(view)
|
||||||
|
|
||||||
|
private var callback: ((Int) -> Unit)? = null
|
||||||
|
fun setClickListener(callback: ((Int) -> Unit)?) {
|
||||||
|
this.callback = callback
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||||
|
when(viewType) {
|
||||||
|
ViewType.VIEW_ITEM.ordinal -> {
|
||||||
|
val view = LayoutInflater.from(parent.context).inflate(
|
||||||
|
R.layout.item_galleryblock, parent, false
|
||||||
|
) as CardView
|
||||||
|
|
||||||
|
return ViewHolder(view)
|
||||||
|
}
|
||||||
|
ViewType.VIEW_PROG.ordinal -> {
|
||||||
|
val view = LayoutInflater.from(parent.context).inflate(
|
||||||
|
R.layout.item_progressbar, parent, false
|
||||||
|
) as LinearLayout
|
||||||
|
|
||||||
|
return ProgressViewHolder(view)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw Exception("Unexpected ViewType")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||||
|
if (holder is ViewHolder) {
|
||||||
|
with(holder.view) {
|
||||||
|
val resources = context.resources
|
||||||
|
val languages = resources.getStringArray(R.array.languages).map {
|
||||||
|
it.split("|").let { split ->
|
||||||
|
Pair(split[0], split[1])
|
||||||
|
}
|
||||||
|
}.toMap()
|
||||||
|
val (gallery, thumbnail) = galleries[position]
|
||||||
|
|
||||||
|
val artists = gallery.artists.ifEmpty { listOf("N/A") }
|
||||||
|
val series = gallery.series.ifEmpty { listOf("N/A") }
|
||||||
|
|
||||||
|
setOnClickListener {
|
||||||
|
callback?.invoke(gallery.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
galleryblock_thumbnail.setImageBitmap(thumbnail)
|
||||||
|
galleryblock_title.text = gallery.title
|
||||||
|
galleryblock_artist.text = artists.joinToString(", ") { it.wordCapitalize() }
|
||||||
|
galleryblock_series.text =
|
||||||
|
resources.getString(R.string.galleryblock_series, series.joinToString(", ") { it.wordCapitalize() })
|
||||||
|
galleryblock_type.text = resources.getString(R.string.galleryblock_type, gallery.type).wordCapitalize()
|
||||||
|
galleryblock_language.text =
|
||||||
|
resources.getString(R.string.galleryblock_language, languages[gallery.language])
|
||||||
|
|
||||||
|
galleryblock_tag_group.removeAllViews()
|
||||||
|
gallery.relatedTags.forEach {
|
||||||
|
galleryblock_tag_group.addView(
|
||||||
|
Chip(context).apply {
|
||||||
|
text = it.toTag().wordCapitalize()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getItemCount() = if (galleries.isEmpty()) 0 else galleries.size+(if (noMore) 0 else 1)
|
||||||
|
|
||||||
|
override fun getItemViewType(position: Int): Int {
|
||||||
|
return when {
|
||||||
|
galleries.getOrNull(position) == null -> ViewType.VIEW_PROG.ordinal
|
||||||
|
else -> ViewType.VIEW_ITEM.ordinal
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
14
app/src/main/java/xyz/quaver/pupil/types/TagSuggestion.kt
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
package xyz.quaver.pupil.types
|
||||||
|
|
||||||
|
import com.arlib.floatingsearchview.suggestions.model.SearchSuggestion
|
||||||
|
import kotlinx.android.parcel.Parcelize
|
||||||
|
import xyz.quaver.hitomi.Suggestion
|
||||||
|
|
||||||
|
@Parcelize
|
||||||
|
data class TagSuggestion constructor(val s: String, val t: Int, val u: String, val n: String) : SearchSuggestion {
|
||||||
|
constructor(s: Suggestion) : this(s.s, s.t, s.u, s.n)
|
||||||
|
|
||||||
|
override fun getBody(): String {
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
}
|
||||||
37
app/src/main/java/xyz/quaver/pupil/util/SetLineOverlap.kt
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
package xyz.quaver.pupil.util
|
||||||
|
|
||||||
|
import android.graphics.Paint
|
||||||
|
import android.text.style.LineHeightSpan
|
||||||
|
|
||||||
|
class SetLineOverlap(private val overlap: Boolean) : LineHeightSpan {
|
||||||
|
companion object {
|
||||||
|
private var originalBottom = 15
|
||||||
|
private var originalDescent = 13
|
||||||
|
private var overlapSaved = false
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun chooseHeight(
|
||||||
|
text: CharSequence?,
|
||||||
|
start: Int,
|
||||||
|
end: Int,
|
||||||
|
spanstartv: Int,
|
||||||
|
lineHeight: Int,
|
||||||
|
fm: Paint.FontMetricsInt?
|
||||||
|
) {
|
||||||
|
fm ?: return
|
||||||
|
|
||||||
|
if (overlap) {
|
||||||
|
if (overlapSaved) {
|
||||||
|
originalBottom = fm.bottom
|
||||||
|
originalDescent = fm.descent
|
||||||
|
overlapSaved = true
|
||||||
|
}
|
||||||
|
fm.bottom += fm.top
|
||||||
|
fm.descent += fm.top
|
||||||
|
} else {
|
||||||
|
fm.bottom = originalBottom
|
||||||
|
fm.descent = originalDescent
|
||||||
|
overlapSaved = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
10
app/src/main/res/drawable-anydpi/ic_expand.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="#333333">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M16.59,8.59L12,13.17 7.41,8.59 6,10l6,6 6,-6z"/>
|
||||||
|
</vector>
|
||||||
10
app/src/main/res/drawable-anydpi/ic_search.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="#FFFFFF">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M15.5,14h-0.79l-0.28,-0.27C15.41,12.59 16,11.11 16,9.5 16,5.91 13.09,3 9.5,3S3,5.91 3,9.5 5.91,16 9.5,16c1.61,0 3.09,-0.59 4.23,-1.57l0.27,0.28v0.79l5,4.99L20.49,19l-4.99,-5zM9.5,14C7.01,14 5,11.99 5,9.5S7.01,5 9.5,5 14,7.01 14,9.5 11.99,14 9.5,14z"/>
|
||||||
|
</vector>
|
||||||
10
app/src/main/res/drawable-anydpi/ic_settings.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="24dp"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportWidth="24"
|
||||||
|
android:viewportHeight="24"
|
||||||
|
android:tint="#FFFFFF">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FF000000"
|
||||||
|
android:pathData="M19.1,12.9a2.8,2.8 0,0 0,0.1 -0.9,2.8 2.8,0 0,0 -0.1,-0.9l2.1,-1.6a0.7,0.7 0,0 0,0.1 -0.6L19.4,5.5a0.7,0.7 0,0 0,-0.6 -0.2l-2.4,1a6.5,6.5 0,0 0,-1.6 -0.9l-0.4,-2.6a0.5,0.5 0,0 0,-0.5 -0.4H10.1a0.5,0.5 0,0 0,-0.5 0.4L9.3,5.4a5.6,5.6 0,0 0,-1.7 0.9l-2.4,-1a0.4,0.4 0,0 0,-0.5 0.2l-2,3.4c-0.1,0.2 0,0.4 0.2,0.6l2,1.6a2.8,2.8 0,0 0,-0.1 0.9,2.8 2.8,0 0,0 0.1,0.9L2.8,14.5a0.7,0.7 0,0 0,-0.1 0.6l1.9,3.4a0.7,0.7 0,0 0,0.6 0.2l2.4,-1a6.5,6.5 0,0 0,1.6 0.9l0.4,2.6a0.5,0.5 0,0 0,0.5 0.4h3.8a0.5,0.5 0,0 0,0.5 -0.4l0.3,-2.6a5.6,5.6 0,0 0,1.7 -0.9l2.4,1a0.4,0.4 0,0 0,0.5 -0.2l2,-3.4c0.1,-0.2 0,-0.4 -0.2,-0.6ZM12,15.6A3.6,3.6 0,1 1,15.6 12,3.6 3.6,0 0,1 12,15.6Z"/>
|
||||||
|
</vector>
|
||||||
BIN
app/src/main/res/drawable-hdpi/ic_account_group.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
app/src/main/res/drawable-hdpi/ic_account_star.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
app/src/main/res/drawable-hdpi/ic_book_open.png
Normal file
|
After Width: | Height: | Size: 620 B |
BIN
app/src/main/res/drawable-hdpi/ic_brush.png
Normal file
|
After Width: | Height: | Size: 975 B |
BIN
app/src/main/res/drawable-hdpi/ic_expand.png
Normal file
|
After Width: | Height: | Size: 197 B |
BIN
app/src/main/res/drawable-hdpi/ic_gender_female.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
app/src/main/res/drawable-hdpi/ic_gender_male.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
app/src/main/res/drawable-hdpi/ic_search.png
Normal file
|
After Width: | Height: | Size: 470 B |
BIN
app/src/main/res/drawable-hdpi/ic_settings.png
Normal file
|
After Width: | Height: | Size: 469 B |
BIN
app/src/main/res/drawable-hdpi/ic_tag.png
Normal file
|
After Width: | Height: | Size: 965 B |
BIN
app/src/main/res/drawable-hdpi/ic_translate.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
app/src/main/res/drawable-ldpi/ic_account_group.png
Normal file
|
After Width: | Height: | Size: 793 B |
BIN
app/src/main/res/drawable-ldpi/ic_account_star.png
Normal file
|
After Width: | Height: | Size: 802 B |
BIN
app/src/main/res/drawable-ldpi/ic_book_open.png
Normal file
|
After Width: | Height: | Size: 495 B |
BIN
app/src/main/res/drawable-ldpi/ic_brush.png
Normal file
|
After Width: | Height: | Size: 639 B |
BIN
app/src/main/res/drawable-ldpi/ic_gender_female.png
Normal file
|
After Width: | Height: | Size: 733 B |
BIN
app/src/main/res/drawable-ldpi/ic_gender_male.png
Normal file
|
After Width: | Height: | Size: 817 B |
BIN
app/src/main/res/drawable-ldpi/ic_tag.png
Normal file
|
After Width: | Height: | Size: 670 B |
BIN
app/src/main/res/drawable-ldpi/ic_translate.png
Normal file
|
After Width: | Height: | Size: 934 B |
BIN
app/src/main/res/drawable-mdpi/ic_account_group.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
app/src/main/res/drawable-mdpi/ic_account_star.png
Normal file
|
After Width: | Height: | Size: 979 B |
BIN
app/src/main/res/drawable-mdpi/ic_book_open.png
Normal file
|
After Width: | Height: | Size: 636 B |
BIN
app/src/main/res/drawable-mdpi/ic_brush.png
Normal file
|
After Width: | Height: | Size: 760 B |
BIN
app/src/main/res/drawable-mdpi/ic_expand.png
Normal file
|
After Width: | Height: | Size: 145 B |
BIN
app/src/main/res/drawable-mdpi/ic_gender_female.png
Normal file
|
After Width: | Height: | Size: 947 B |
BIN
app/src/main/res/drawable-mdpi/ic_gender_male.png
Normal file
|
After Width: | Height: | Size: 1001 B |
BIN
app/src/main/res/drawable-mdpi/ic_search.png
Normal file
|
After Width: | Height: | Size: 236 B |
BIN
app/src/main/res/drawable-mdpi/ic_settings.png
Normal file
|
After Width: | Height: | Size: 352 B |
BIN
app/src/main/res/drawable-mdpi/ic_tag.png
Normal file
|
After Width: | Height: | Size: 848 B |
BIN
app/src/main/res/drawable-mdpi/ic_translate.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
34
app/src/main/res/drawable-v24/ic_launcher_foreground.xml
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:aapt="http://schemas.android.com/aapt"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportHeight="108"
|
||||||
|
android:viewportWidth="108">
|
||||||
|
<path
|
||||||
|
android:fillType="evenOdd"
|
||||||
|
android:pathData="M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z"
|
||||||
|
android:strokeColor="#00000000"
|
||||||
|
android:strokeWidth="1">
|
||||||
|
<aapt:attr name="android:fillColor">
|
||||||
|
<gradient
|
||||||
|
android:endX="78.5885"
|
||||||
|
android:endY="90.9159"
|
||||||
|
android:startX="48.7653"
|
||||||
|
android:startY="61.0927"
|
||||||
|
android:type="linear">
|
||||||
|
<item
|
||||||
|
android:color="#44000000"
|
||||||
|
android:offset="0.0"/>
|
||||||
|
<item
|
||||||
|
android:color="#00000000"
|
||||||
|
android:offset="1.0"/>
|
||||||
|
</gradient>
|
||||||
|
</aapt:attr>
|
||||||
|
</path>
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:fillType="nonZero"
|
||||||
|
android:pathData="M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z"
|
||||||
|
android:strokeColor="#00000000"
|
||||||
|
android:strokeWidth="1"/>
|
||||||
|
</vector>
|
||||||
BIN
app/src/main/res/drawable-xhdpi/ic_account_group.png
Normal file
|
After Width: | Height: | Size: 1.7 KiB |
BIN
app/src/main/res/drawable-xhdpi/ic_account_star.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
app/src/main/res/drawable-xhdpi/ic_book_open.png
Normal file
|
After Width: | Height: | Size: 892 B |
BIN
app/src/main/res/drawable-xhdpi/ic_brush.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
app/src/main/res/drawable-xhdpi/ic_expand.png
Normal file
|
After Width: | Height: | Size: 226 B |
BIN
app/src/main/res/drawable-xhdpi/ic_gender_female.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
app/src/main/res/drawable-xhdpi/ic_gender_male.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
app/src/main/res/drawable-xhdpi/ic_search.png
Normal file
|
After Width: | Height: | Size: 416 B |
BIN
app/src/main/res/drawable-xhdpi/ic_settings.png
Normal file
|
After Width: | Height: | Size: 628 B |
BIN
app/src/main/res/drawable-xhdpi/ic_tag.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
app/src/main/res/drawable-xhdpi/ic_translate.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
app/src/main/res/drawable-xxhdpi/ic_account_group.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
app/src/main/res/drawable-xxhdpi/ic_account_star.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
app/src/main/res/drawable-xxhdpi/ic_book_open.png
Normal file
|
After Width: | Height: | Size: 948 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_brush.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
app/src/main/res/drawable-xxhdpi/ic_expand.png
Normal file
|
After Width: | Height: | Size: 294 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_gender_female.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
app/src/main/res/drawable-xxhdpi/ic_gender_male.png
Normal file
|
After Width: | Height: | Size: 2.4 KiB |
BIN
app/src/main/res/drawable-xxhdpi/ic_search.png
Normal file
|
After Width: | Height: | Size: 687 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_settings.png
Normal file
|
After Width: | Height: | Size: 914 B |
BIN
app/src/main/res/drawable-xxhdpi/ic_tag.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
app/src/main/res/drawable-xxhdpi/ic_translate.png
Normal file
|
After Width: | Height: | Size: 2.6 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/ic_account_group.png
Normal file
|
After Width: | Height: | Size: 3.2 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/ic_account_star.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/ic_book_open.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/ic_brush.png
Normal file
|
After Width: | Height: | Size: 2.0 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/ic_gender_female.png
Normal file
|
After Width: | Height: | Size: 2.8 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/ic_gender_male.png
Normal file
|
After Width: | Height: | Size: 3.1 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/ic_tag.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/ic_translate.png
Normal file
|
After Width: | Height: | Size: 3.5 KiB |
74
app/src/main/res/drawable/ic_launcher_background.xml
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<vector
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:height="108dp"
|
||||||
|
android:width="108dp"
|
||||||
|
android:viewportHeight="108"
|
||||||
|
android:viewportWidth="108">
|
||||||
|
<path android:fillColor="#008577"
|
||||||
|
android:pathData="M0,0h108v108h-108z"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M9,0L9,108"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M19,0L19,108"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M29,0L29,108"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M39,0L39,108"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M49,0L49,108"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M59,0L59,108"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M69,0L69,108"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M79,0L79,108"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M89,0L89,108"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M99,0L99,108"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M0,9L108,9"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M0,19L108,19"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M0,29L108,29"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M0,39L108,39"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M0,49L108,49"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M0,59L108,59"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M0,69L108,69"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M0,79L108,79"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M0,89L108,89"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M0,99L108,99"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M19,29L89,29"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M19,39L89,39"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M19,49L89,49"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M19,59L89,59"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M19,69L89,69"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M19,79L89,79"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M29,19L29,89"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M39,19L39,89"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M49,19L49,89"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M59,19L59,89"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M69,19L69,89"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
<path android:fillColor="#00000000" android:pathData="M79,19L79,89"
|
||||||
|
android:strokeColor="#33FFFFFF" android:strokeWidth="0.8"/>
|
||||||
|
</vector>
|
||||||
17
app/src/main/res/drawable/ic_launcher_foreground.xml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="108dp"
|
||||||
|
android:height="108dp"
|
||||||
|
android:viewportWidth="341.33334"
|
||||||
|
android:viewportHeight="341.33334">
|
||||||
|
<group android:translateX="42.666668"
|
||||||
|
android:translateY="42.666668">
|
||||||
|
<path
|
||||||
|
android:pathData="M203,128C203,169.39 169.39,203 128,203C86.61,203 53,169.39 53,128C53,86.61 86.61,53 128,53C169.39,53 203,86.61 203,128Z"
|
||||||
|
android:fillColor="#4fc3f7"
|
||||||
|
android:fillAlpha="1"/>
|
||||||
|
<path
|
||||||
|
android:pathData="M156.12,128C156.12,143.52 143.52,156.13 128,156.13C112.48,156.13 99.88,143.52 99.88,128C99.88,112.48 112.48,99.88 128,99.88C143.52,99.88 156.12,112.48 156.12,128Z"
|
||||||
|
android:fillColor="#1d1d1d"
|
||||||
|
android:fillAlpha="1"/>
|
||||||
|
</group>
|
||||||
|
</vector>
|
||||||
BIN
app/src/main/res/drawable/img_thumbnail.jpg
Normal file
|
After Width: | Height: | Size: 68 KiB |
30
app/src/main/res/layout/activity_gallery.xml
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context=".GalleryActivity">
|
||||||
|
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/gallery_framelayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="4dp">
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:id="@+id/gallery_progressbar"
|
||||||
|
style="@style/Widget.AppCompat.ProgressBar.Horizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="4dp"
|
||||||
|
android:layout_gravity="center"/>
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
|
android:id="@+id/gallery_recyclerview"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_below="@id/gallery_framelayout"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
83
app/src/main/res/layout/activity_main.xml
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<RelativeLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
tools:context=".MainActivity">
|
||||||
|
|
||||||
|
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<com.google.android.material.appbar.AppBarLayout
|
||||||
|
android:id="@+id/main_appbar_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@color/transparent"
|
||||||
|
android:visibility="invisible"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent">
|
||||||
|
|
||||||
|
<View
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="64dp"
|
||||||
|
android:visibility="invisible"
|
||||||
|
android:background="@color/transparent"
|
||||||
|
app:layout_scrollFlags="scroll|enterAlways"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
|
||||||
|
|
||||||
|
</com.google.android.material.appbar.AppBarLayout>
|
||||||
|
|
||||||
|
<androidx.core.widget.ContentLoadingProgressBar
|
||||||
|
style="?android:attr/progressBarStyle"
|
||||||
|
android:id="@+id/main_progressbar"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:indeterminate="true"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/main_noresult"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:text="@string/main_no_result"
|
||||||
|
android:visibility="invisible"/>
|
||||||
|
|
||||||
|
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||||
|
android:id="@+id/main_swipe_layout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:layout_marginBottom="-64dp">
|
||||||
|
|
||||||
|
<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"
|
||||||
|
android:scrollbars="vertical"
|
||||||
|
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/>
|
||||||
|
|
||||||
|
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||||
|
|
||||||
|
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||||
|
|
||||||
|
<com.arlib.floatingsearchview.FloatingSearchView
|
||||||
|
android:id="@+id/main_searchview"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:floatingSearch_searchBarMarginLeft="8dp"
|
||||||
|
app:floatingSearch_searchBarMarginRight="8dp"
|
||||||
|
app:floatingSearch_searchBarMarginTop="8dp"
|
||||||
|
app:floatingSearch_searchHint="@string/search_hint"
|
||||||
|
app:floatingSearch_suggestionsListAnimDuration="250"
|
||||||
|
app:floatingSearch_showSearchKey="true"
|
||||||
|
app:floatingSearch_leftActionMode="noLeftAction"
|
||||||
|
app:floatingSearch_menu="@menu/main"
|
||||||
|
app:floatingSearch_dismissOnOutsideTouch="true"
|
||||||
|
app:floatingSearch_close_search_on_keyboard_dismiss="true"/>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
9
app/src/main/res/layout/item_gallery.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:contentDescription="@string/reader_imageview_description"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingBottom="8dp"
|
||||||
|
android:adjustViewBounds="true"
|
||||||
|
android:clickable="true"
|
||||||
|
android:focusable="true"/>
|
||||||
98
app/src/main/res/layout/item_galleryblock.xml
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<androidx.cardview.widget.CardView
|
||||||
|
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_height="wrap_content"
|
||||||
|
android:layout_margin="8dp"
|
||||||
|
android:paddingStart="0dp"
|
||||||
|
android:paddingLeft="0dp"
|
||||||
|
android:paddingEnd="8dp"
|
||||||
|
android:paddingRight="8dp"
|
||||||
|
app:cardCornerRadius="8dp"
|
||||||
|
android:foreground="?attr/selectableItemBackground"
|
||||||
|
android:focusable="true"
|
||||||
|
android:clickable="true">
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/galleryblock_thumbnail"
|
||||||
|
android:layout_width="150dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:contentDescription="@string/galleryblock_thumbnail_description"
|
||||||
|
android:adjustViewBounds="true"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/TextAppearance.AppCompat.Headline"
|
||||||
|
android:id="@+id/galleryblock_title"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginLeft="8dp"
|
||||||
|
app:layout_constraintLeft_toRightOf="@id/galleryblock_thumbnail"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toTopOf="parent"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/TextAppearance.AppCompat.Medium"
|
||||||
|
android:id="@+id/galleryblock_artist"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginLeft="8dp"
|
||||||
|
app:layout_constraintLeft_toRightOf="@id/galleryblock_thumbnail"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/galleryblock_title"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/galleryblock_series"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginLeft="8dp"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/galleryblock_artist"
|
||||||
|
app:layout_constraintStart_toEndOf="@id/galleryblock_thumbnail"
|
||||||
|
app:layout_constraintEnd_toEndOf="parent"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/galleryblock_type"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:layout_marginLeft="8dp"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/galleryblock_series"
|
||||||
|
app:layout_constraintStart_toEndOf="@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_constraintStart_toEndOf="@id/galleryblock_thumbnail" />
|
||||||
|
|
||||||
|
<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:layout_constraintLeft_toRightOf="@id/galleryblock_thumbnail"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/galleryblock_language"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"/>
|
||||||
|
|
||||||
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
|
|
||||||
|
</androidx.cardview.widget.CardView>
|
||||||
10
app/src/main/res/layout/item_progressbar.xml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical"
|
||||||
|
android:layout_width="match_parent" android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<ProgressBar
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center_horizontal"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
9
app/src/main/res/layout/settings_activity.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
<FrameLayout
|
||||||
|
android:id="@+id/settings"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"/>
|
||||||
|
</LinearLayout>
|
||||||
17
app/src/main/res/menu/main.xml
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/main_menu_search"
|
||||||
|
android:icon="@drawable/ic_search"
|
||||||
|
android:title="@string/main_search"
|
||||||
|
app:showAsAction="ifRoom"/>
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/main_menu_settings"
|
||||||
|
android:icon="@drawable/ic_settings"
|
||||||
|
android:title="@string/main_settings"
|
||||||
|
app:showAsAction="always"/>
|
||||||
|
|
||||||
|
</menu>
|
||||||
5
app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@color/ic_launcher_background"/>
|
||||||
|
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||||
|
</adaptive-icon>
|
||||||
5
app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<background android:drawable="@color/ic_launcher_background"/>
|
||||||
|
<foreground android:drawable="@drawable/ic_launcher_foreground"/>
|
||||||
|
</adaptive-icon>
|
||||||
BIN
app/src/main/res/mipmap-hdpi/ic_launcher.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
app/src/main/res/mipmap-hdpi/ic_launcher_round.png
Normal file
|
After Width: | Height: | Size: 2.9 KiB |