diff --git a/.idea/misc.xml b/.idea/misc.xml index 84da703c..7631aec3 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index a0141594..64729574 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -9,8 +9,8 @@ android { applicationId "xyz.quaver.pupil" minSdkVersion 16 targetSdkVersion 28 - versionCode 3 - versionName "1.2" + versionCode 4 + versionName "1.3" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" } buildTypes { diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2a9ea385..0e380367 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -3,8 +3,6 @@ package="xyz.quaver.pupil"> - - >>() private var query = "" @@ -67,8 +57,6 @@ class MainActivity : AppCompatActivity() { setContentView(R.layout.activity_main) - checkPermission() - checkUpdate() main_appbar_layout.addOnOffsetChangedListener( @@ -99,17 +87,33 @@ class MainActivity : AppCompatActivity() { CoroutineScope(Dispatchers.Main).launch { main_drawer_layout.closeDrawers() - cancelFetch() - clearGalleries() when(it.itemId) { R.id.main_drawer_home -> { + cancelFetch() + clearGalleries() query = query.replace("HISTORY", "") fetchGalleries(query) } R.id.main_drawer_history -> { + cancelFetch() + clearGalleries() query += "HISTORY" fetchGalleries(query) } + R.id.main_drawer_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 -> { + AlertDialog.Builder(this@MainActivity).apply { + + }.show() + } } loadBlocks() } @@ -142,23 +146,6 @@ class MainActivity : AppCompatActivity() { super.onResume() } - private fun checkPermission() { - val permissions = arrayOf( - Manifest.permission.WRITE_EXTERNAL_STORAGE - ) - - if (permissions.any { ContextCompat.checkSelfPermission(this, it) != PackageManager.PERMISSION_GRANTED }) { - if (permissions.any { ActivityCompat.shouldShowRequestPermissionRationale(this, it) }) - AlertDialog.Builder(this).apply { - setTitle(R.string.warning) - setMessage(R.string.permission_explain) - setPositiveButton(android.R.string.ok) { _, _ -> } - }.show() - else - ActivityCompat.requestPermissions(this, permissions, permissionRequestCode) - } - } - private fun checkUpdate() { fun extractReleaseNote(update: JsonObject, locale: String) : String { @@ -179,7 +166,7 @@ class MainActivity : AppCompatActivity() { val result = StringBuilder() - for(line in markdown.split('\n')) { + for(line in markdown.lines()) { if (releaseNote.matches(line)) { releaseNoteFlag = true continue @@ -203,57 +190,16 @@ class MainActivity : AppCompatActivity() { return getString(R.string.update_release_note, update["tag_name"]?.content, result.toString()) } - if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) - return - CoroutineScope(Dispatchers.Default).launch { val update = checkUpdate(getString(R.string.release_url), BuildConfig.VERSION_NAME) ?: return@launch - val (url, fileName) = getApkUrl(update, getString(R.string.release_name)) ?: return@launch - val dialog = AlertDialog.Builder(this@MainActivity).apply { setTitle(R.string.update_title) val msg = extractReleaseNote(update, Locale.getDefault().language) setMessage(Markwon.create(context).toMarkdown(msg)) setPositiveButton(android.R.string.yes) { _, _ -> - Toast.makeText( - context, getString(R.string.update_download_started), Toast.LENGTH_SHORT - ).show() - - val dest = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), fileName) - val desturi = - FileProvider.getUriForFile( - applicationContext, - "xyz.quaver.pupil.provider", - dest - ) - - if (dest.exists()) - dest.delete() - - val request = DownloadManager.Request(Uri.parse(url)).apply { - setDescription(getString(R.string.update_notification_description)) - setTitle(getString(R.string.app_name)) - setDestinationUri(Uri.fromFile(dest)) - setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE) - } - - val manager = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager - val id = manager.enqueue(request) - - registerReceiver(object: BroadcastReceiver() { - override fun onReceive(context: Context?, intent: Intent?) { - val install = Intent(Intent.ACTION_VIEW).apply { - flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_GRANT_READ_URI_PERMISSION - setDataAndType(desturi, manager.getMimeTypeForDownloadedFile(id)) - } - - startActivity(install) - unregisterReceiver(this) - finish() - } - }, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) + startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.home_page)))) } setNegativeButton(android.R.string.no) { _, _ ->} } @@ -404,9 +350,11 @@ class MainActivity : AppCompatActivity() { if (query != this@MainActivity.query) { this@MainActivity.query = query - cancelFetch() - clearGalleries() - fetchGalleries(query) + CoroutineScope(Dispatchers.Main).launch { + cancelFetch() + clearGalleries() + fetchGalleries(query) + } } } }) diff --git a/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt b/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt index 260d8bdc..80471c11 100644 --- a/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ReaderActivity.kt @@ -1,6 +1,7 @@ package xyz.quaver.pupil import android.os.Bundle +import android.util.Log import android.view.* import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity @@ -112,13 +113,13 @@ class ReaderActivity : AppCompatActivity() { when(item?.itemId) { R.id.reader_menu_page_indicator -> { val view = LayoutInflater.from(this).inflate(R.layout.dialog_numberpicker, findViewById(android.R.id.content), false) + with(view.reader_dialog_number_picker) { + minValue=1 + maxValue=gallerySize + value=currentPage + } val dialog = AlertDialog.Builder(this).apply { setView(view) - with(view.reader_dialog_number_picker) { - minValue=1 - maxValue=gallerySize - value=currentPage - } }.create() view.reader_dialog_ok.setOnClickListener { (reader_recyclerview.layoutManager as LinearLayoutManager?)?.scrollToPositionWithOffset(view.reader_dialog_number_picker.value-1, 0) @@ -182,7 +183,7 @@ class ReaderActivity : AppCompatActivity() { reader_recyclerview.layoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) } - (reader_recyclerview.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(currentPage, 0) + (reader_recyclerview.layoutManager as LinearLayoutManager).scrollToPositionWithOffset(currentPage-1, 0) preferences.edit().putBoolean("reader_one_by_one", !oneByOne).apply() diff --git a/app/src/main/java/xyz/quaver/pupil/SettingsActivity.kt b/app/src/main/java/xyz/quaver/pupil/SettingsActivity.kt index d5c301ce..1640e5d5 100644 --- a/app/src/main/java/xyz/quaver/pupil/SettingsActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/SettingsActivity.kt @@ -2,12 +2,22 @@ package xyz.quaver.pupil import android.os.Bundle import android.preference.PreferenceManager +import android.text.Editable +import android.text.TextWatcher +import android.view.LayoutInflater import android.view.MenuItem +import android.view.View import android.view.WindowManager +import android.widget.AdapterView +import android.widget.ArrayAdapter +import android.widget.TextView import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat +import kotlinx.android.synthetic.main.dialog_default_query.* +import kotlinx.android.synthetic.main.dialog_default_query.view.* +import xyz.quaver.pupil.types.Tags import xyz.quaver.pupil.util.Histories import java.io.File @@ -109,6 +119,120 @@ class SettingsActivity : AppCompatActivity() { true } } + + with(findPreference("default_query")) { + this ?: return@with + + val preferences = PreferenceManager.getDefaultSharedPreferences(context) + + summary = preferences.getString("default_query", "") ?: "" + + val languages = resources.getStringArray(R.array.languages).map { + it.split("|").let { split -> + Pair(split[0], split[1]) + } + }.toMap() + val reverseLanguages = languages.entries.associate { (k, v) -> v to k } + + val excludeBL = "-male:yaoi" + val excludeGuro = listOf("-female:guro", "-male:guro") + + setOnPreferenceClickListener { + val dialogView = LayoutInflater.from(context).inflate( + R.layout.dialog_default_query, + null + ) + + val tags = Tags.parse( + preferences.getString("default_query", "") ?: "" + ) + + summary = tags.toString() + + with(dialogView.default_query_dialog_language_selector) { + adapter = + ArrayAdapter( + context, + android.R.layout.simple_spinner_dropdown_item, + arrayListOf( + getString(R.string.default_query_dialog_language_selector_none) + ).apply { + addAll(languages.values) + } + ) + if (tags.any { it.area == "language" }) { + val tag = languages[tags.first { it.area == "language" }.tag] + if (tag != null) { + setSelection( + (adapter as ArrayAdapter).getPosition(tag) + ) + tags.removeByArea("language") + } + } + } + + with(dialogView.default_query_dialog_BL_checkbox) { + isChecked = tags.contains(excludeBL) + if (tags.contains(excludeBL)) + tags.remove(excludeBL) + } + + with(dialogView.default_query_dialog_guro_checkbox) { + isChecked = excludeGuro.all { tags.contains(it) } + if (excludeGuro.all { tags.contains(it) }) + excludeGuro.forEach { + tags.remove(it) + } + } + + with(dialogView.default_query_dialog_edittext) { + setText(tags.toString(), TextView.BufferType.EDITABLE) + 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()) + } + }) + } + + val dialog = AlertDialog.Builder(context!!).apply { + setView(dialogView) + }.create() + + dialogView.default_query_dialog_ok.setOnClickListener { + val newTags = Tags.parse(dialogView.default_query_dialog_edittext.text.toString()) + + with(dialogView.default_query_dialog_language_selector) { + if (selectedItemPosition != 0) + newTags.add("language:${reverseLanguages[selectedItem]}") + } + + if (dialogView.default_query_dialog_BL_checkbox.isChecked) + newTags.add(excludeBL) + + if (dialogView.default_query_dialog_guro_checkbox.isChecked) + excludeGuro.forEach { tag -> + newTags.add(tag) + } + + preferenceManager.sharedPreferences.edit().putString("default_query", newTags.toString()).apply() + summary = preferences.getString("default_query", "") ?: "" + tags.clear() + tags.addAll(newTags) + dialog.dismiss() + } + + dialog.show() + + true + } + } } } diff --git a/app/src/main/java/xyz/quaver/pupil/types/Tags.kt b/app/src/main/java/xyz/quaver/pupil/types/Tags.kt new file mode 100644 index 00000000..7abc6c97 --- /dev/null +++ b/app/src/main/java/xyz/quaver/pupil/types/Tags.kt @@ -0,0 +1,96 @@ +package xyz.quaver.pupil.types + +data class Tag(val area: String?, val tag: String, val isNegative: Boolean = false) { + companion object { + fun parseTag(tag: String) : Tag { + if (tag.first() == '-') { + tag.substring(1).split(Regex(":"), 2).let { + return when(it.size) { + 2 -> Tag(it[0], it[1], true) + else -> Tag(null, tag, true) + } + } + } + tag.split(Regex(":"), 2).let { + return when(it.size) { + 2 -> Tag(it[0], it[1]) + else -> Tag(null, tag) + } + } + } + } + + override fun toString(): String { + return (if (isNegative) "-" else "") + when(area) { + null -> tag + else -> "$area:$tag" + } + } + + override fun equals(other: Any?): Boolean { + if (other !is Tag) + return false + + if (other.area == area && other.tag == tag) + return true + + return false + } + + override fun hashCode(): Int { + return super.hashCode() + } +} + +class Tags(tag: List?) : ArrayList() { + + companion object { + fun parse(tags: String) : Tags { + return Tags( + tags.split(' ').map { + if (it.isNotEmpty()) + Tag.parseTag(it) + else + null + } + ) + } + } + + init { + tag?.forEach { + if (it != null) + add(it) + } + } + + fun contains(element: String): Boolean { + forEach { + if (it.toString() == element) + return true + } + + return false + } + + fun add(element: String): Boolean { + return super.add(Tag.parseTag(element)) + } + + fun remove(element: String) { + filter { it.toString() == element }.forEach { + remove(it) + } + } + + fun removeByArea(area: String) { + filter { it.area == area }.forEach { + remove(it) + } + } + + override fun toString(): String { + return joinToString(" ") { it.toString() } + } + +} \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/util/update.kt b/app/src/main/java/xyz/quaver/pupil/util/update.kt index e5037094..b25e03e0 100644 --- a/app/src/main/java/xyz/quaver/pupil/util/update.kt +++ b/app/src/main/java/xyz/quaver/pupil/util/update.kt @@ -18,14 +18,5 @@ fun checkUpdate(url: String, currentVersion: String) : JsonObject? { if (currentVersion != releases[0].jsonObject["tag_name"]?.content) return releases[0].jsonObject - return null -} - -fun getApkUrl(releases: JsonObject, releaseName: String) : Pair? { - releases["assets"]?.jsonArray?.forEach { - if (Regex(releaseName).matches(it.jsonObject["name"]?.content ?: "")) - return Pair(it.jsonObject["browser_download_url"]?.content, it.jsonObject["name"]?.content) - } - return null } \ No newline at end of file diff --git a/app/src/main/res/drawable-anydpi/ic_email.xml b/app/src/main/res/drawable-anydpi/ic_email.xml new file mode 100644 index 00000000..4163eb94 --- /dev/null +++ b/app/src/main/res/drawable-anydpi/ic_email.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable-anydpi/ic_help.xml b/app/src/main/res/drawable-anydpi/ic_help.xml new file mode 100644 index 00000000..785b2478 --- /dev/null +++ b/app/src/main/res/drawable-anydpi/ic_help.xml @@ -0,0 +1,10 @@ + + + diff --git a/app/src/main/res/drawable-hdpi/ic_email.png b/app/src/main/res/drawable-hdpi/ic_email.png new file mode 100644 index 00000000..6cf75e2a Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_email.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_help.png b/app/src/main/res/drawable-hdpi/ic_help.png new file mode 100644 index 00000000..64941638 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_help.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_email.png b/app/src/main/res/drawable-mdpi/ic_email.png new file mode 100644 index 00000000..d305e584 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_email.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_help.png b/app/src/main/res/drawable-mdpi/ic_help.png new file mode 100644 index 00000000..b2b8c636 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_help.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_email.png b/app/src/main/res/drawable-xhdpi/ic_email.png new file mode 100644 index 00000000..6854119d Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_email.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_help.png b/app/src/main/res/drawable-xhdpi/ic_help.png new file mode 100644 index 00000000..6ef7a89d Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_help.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_email.png b/app/src/main/res/drawable-xxhdpi/ic_email.png new file mode 100644 index 00000000..ed17daf2 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_email.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_help.png b/app/src/main/res/drawable-xxhdpi/ic_help.png new file mode 100644 index 00000000..760e70d4 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_help.png differ diff --git a/app/src/main/res/layout/dialog_default_query.xml b/app/src/main/res/layout/dialog_default_query.xml new file mode 100644 index 00000000..ca1bd29d --- /dev/null +++ b/app/src/main/res/layout/dialog_default_query.xml @@ -0,0 +1,111 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +