diff --git a/.idea/misc.xml b/.idea/misc.xml index 37a75096..7bfef59d 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,6 +1,6 @@ - + diff --git a/app/build.gradle b/app/build.gradle index f9d1a7e6..a6c8a954 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -13,8 +13,8 @@ android { applicationId "xyz.quaver.pupil" minSdkVersion 16 targetSdkVersion 29 - versionCode 25 - versionName "3.1" + versionCode 26 + versionName "3.2" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" multiDexEnabled true vectorDrawables.useSupportLibrary = true @@ -31,6 +31,10 @@ android { kotlinOptions { freeCompilerArgs += '-Xuse-experimental=kotlin.Experimental' } + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } } dependencies { diff --git a/app/src/main/java/xyz/quaver/pupil/adapters/ReaderAdapter.kt b/app/src/main/java/xyz/quaver/pupil/adapters/ReaderAdapter.kt index a393b0ac..f0e789c9 100644 --- a/app/src/main/java/xyz/quaver/pupil/adapters/ReaderAdapter.kt +++ b/app/src/main/java/xyz/quaver/pupil/adapters/ReaderAdapter.kt @@ -18,6 +18,7 @@ package xyz.quaver.pupil.adapters +import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -25,6 +26,11 @@ import android.widget.ImageView import androidx.recyclerview.widget.RecyclerView import com.bumptech.glide.RequestManager import com.bumptech.glide.load.engine.DiskCacheStrategy +import com.bumptech.glide.request.target.Target +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.runBlocking import xyz.quaver.pupil.R class ReaderAdapter(private val glide: RequestManager, private val images: List) : RecyclerView.Adapter() { @@ -34,21 +40,27 @@ class ReaderAdapter(private val glide: RequestManager, private val images: List< class ViewHolder(val view: View) : RecyclerView.ViewHolder(view) override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { - LayoutInflater.from(parent.context).inflate( + return LayoutInflater.from(parent.context).inflate( R.layout.item_reader, parent, false ).let { - return ViewHolder(it) + ViewHolder(it) } } override fun onBindViewHolder(holder: ViewHolder, position: Int) { - glide - .load(images[position]) - .diskCacheStrategy(DiskCacheStrategy.NONE) - .skipMemoryCache(true) - .error(R.drawable.image_broken_variant) - .dontTransform() - .into(holder.view as ImageView) + runBlocking { + CoroutineScope(Dispatchers.Default).launch { + val image = glide + .load(images[position]) + .diskCacheStrategy(DiskCacheStrategy.NONE) + .skipMemoryCache(true) + .error(R.drawable.image_broken_variant) + .submit() + .get() + + (holder.view as ImageView).setImageDrawable(image) + }.join() + } } override fun getItemCount() = images.size diff --git a/app/src/main/java/xyz/quaver/pupil/ui/GalleryDialog.kt b/app/src/main/java/xyz/quaver/pupil/ui/GalleryDialog.kt new file mode 100644 index 00000000..5628379c --- /dev/null +++ b/app/src/main/java/xyz/quaver/pupil/ui/GalleryDialog.kt @@ -0,0 +1,68 @@ +/* + * 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 . + */ + +package xyz.quaver.pupil.ui + +import android.app.Dialog +import android.content.Context +import android.os.Bundle +import android.widget.LinearLayout +import androidx.core.content.ContextCompat +import com.bumptech.glide.Glide +import com.bumptech.glide.request.target.Target +import com.google.android.material.snackbar.Snackbar +import kotlinx.android.synthetic.main.dialog_galleryblock.* +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import xyz.quaver.hitomi.getGallery +import xyz.quaver.pupil.R + +class GalleryDialog(context: Context, private val galleryID: Int) : Dialog(context) { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setContentView(R.layout.dialog_galleryblock) + + window?.attributes.apply { + this ?: return@apply + + width = LinearLayout.LayoutParams.MATCH_PARENT + height = LinearLayout.LayoutParams.MATCH_PARENT + } + + gallery_fab.setImageDrawable(ContextCompat.getDrawable(context, R.drawable.arrow_right)) + + CoroutineScope(Dispatchers.IO).launch { + try { + val gallery = getGallery(galleryID) + + launch(Dispatchers.Main) { + gallery_toolbar.title = gallery.title + + Glide.with(context) + .load(gallery.thumbnails[0]) + .into(gallery_thumbnail) + } + } catch (e: Exception) { + Snackbar.make(gallery_layout, R.string.unable_to_connect, Snackbar.LENGTH_INDEFINITE).show() + } + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/xyz/quaver/pupil/ui/LockActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/LockActivity.kt index 81f7622d..e457a90f 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/LockActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/LockActivity.kt @@ -19,6 +19,7 @@ package xyz.quaver.pupil.ui import android.app.Activity +import android.app.AlertDialog import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import com.andrognito.patternlockview.PatternLockView @@ -35,7 +36,18 @@ class LockActivity : AppCompatActivity() { super.onCreate(savedInstanceState) setContentView(R.layout.activity_lock) - val lockManager = LockManager(this) + val lockManager = try { + LockManager(this) + } catch (e: Exception) { + AlertDialog.Builder(this).apply { + setTitle(R.string.warning) + setMessage(R.string.lock_corrupted) + setPositiveButton(android.R.string.ok) { _, _ -> + finish() + } + }.show() + return + } val mode = intent.getStringExtra("mode") @@ -49,6 +61,7 @@ class LockActivity : AppCompatActivity() { if (lockManager.empty()) { setResult(RESULT_OK) finish() + return } } "add_lock" -> { diff --git a/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt index 6f5516a3..5b12a123 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt @@ -25,14 +25,16 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter -import android.content.pm.PackageManager import android.graphics.drawable.Animatable import android.net.Uri import android.os.Bundle import android.os.Environment import android.text.* import android.text.style.AlignmentSpan -import android.view.* +import android.view.KeyEvent +import android.view.MotionEvent +import android.view.View +import android.view.WindowManager import android.widget.EditText import android.widget.ImageView import android.widget.LinearLayout @@ -54,7 +56,6 @@ import com.google.android.material.appbar.AppBarLayout import com.google.android.material.snackbar.Snackbar import kotlinx.android.synthetic.main.activity_main.* import kotlinx.android.synthetic.main.activity_main_content.* -import kotlinx.android.synthetic.main.dialog_galleryblock.view.* import kotlinx.coroutines.* import kotlinx.serialization.ImplicitReflectionSerializer import kotlinx.serialization.json.Json @@ -243,6 +244,12 @@ class MainActivity : AppCompatActivity() { private fun checkUpdate() { + val preferences = PreferenceManager.getDefaultSharedPreferences(this) + val ignoreUpdateUntil = preferences.getLong("ignore_update_until", 0) + + if (ignoreUpdateUntil > System.currentTimeMillis()) + return + fun extractReleaseNote(update: JsonObject, locale: String) : String { val markdown = update["body"]!!.content @@ -297,6 +304,16 @@ class MainActivity : AppCompatActivity() { val msg = extractReleaseNote(update, Locale.getDefault().language) setMessage(Markwon.create(context).toMarkdown(msg)) setPositiveButton(android.R.string.yes) { _, _ -> + if (!this@MainActivity.hasPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)) { + AlertDialog.Builder(this@MainActivity).apply { + setTitle(R.string.warning) + setMessage(R.string.update_no_permission) + setPositiveButton(android.R.string.ok) { _, _ -> } + }.show() + + return@setPositiveButton + } + val request = DownloadManager.Request(Uri.parse(url)).apply { setDescription(getString(R.string.update_notification_description)) setTitle(getString(R.string.app_name)) @@ -308,17 +325,29 @@ class MainActivity : AppCompatActivity() { 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(manager.getUriForDownloadedFile(id), manager.getMimeTypeForDownloadedFile(id)) - } + try { + val install = Intent(Intent.ACTION_VIEW).apply { + flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_GRANT_READ_URI_PERMISSION + setDataAndType(manager.getUriForDownloadedFile(id), manager.getMimeTypeForDownloadedFile(id)) + } - startActivity(install) - unregisterReceiver(this) + startActivity(install) + unregisterReceiver(this) + } catch (e: Exception) { + AlertDialog.Builder(this@MainActivity).apply { + setTitle(R.string.update_failed) + setMessage(R.string.update_failed_message) + setPositiveButton(android.R.string.ok) { _, _ -> } + }.show() + } } }, IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE)) } - setNegativeButton(android.R.string.no) { _, _ ->} + setNegativeButton(R.string.ignore_update) { _, _ -> + preferences.edit() + .putLong("ignore_update_until", System.currentTimeMillis() + 604800000) + .apply() + } } launch(Dispatchers.Main) { @@ -328,7 +357,7 @@ class MainActivity : AppCompatActivity() { } private fun checkPermissions() { - if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) + if (this.hasPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)) ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE), 13489) } @@ -507,66 +536,15 @@ class MainActivity : AppCompatActivity() { startActivity(intent) histories.add(gallery.id) - }.setOnItemLongClickListener { recyclerView, position, v -> + }.setOnItemLongClickListener { _, position, v -> if (v !is CardView) return@setOnItemLongClickListener true - val gallery = galleries[position].first - val view = LayoutInflater.from(this@MainActivity) - .inflate(R.layout.dialog_galleryblock, recyclerView, false) + val galleryID = galleries[position].first.id - val dialog = AlertDialog.Builder(this@MainActivity).apply { - setView(view) - }.create() - - with(view.main_dialog_download) { - text = when(GalleryDownloader.get(gallery.id)) { - null -> getString(R.string.reader_fab_download) - else -> getString(R.string.reader_fab_download_cancel) - } - isEnabled = !(adapter as GalleryBlockAdapter).completeFlag.get(gallery.id, false) - setOnClickListener { - val downloader = GalleryDownloader.get(gallery.id) - if (downloader == null) - GalleryDownloader(context, gallery.id, true).start() - else { - downloader.cancel() - downloader.clearNotification() - } - - dialog.dismiss() - } - } - - view.main_dialog_delete.setOnClickListener { - CoroutineScope(Dispatchers.Default).launch { - with(GalleryDownloader[gallery.id]) { - this?.cancelAndJoin() - this?.clearNotification() - } - val cache = File(cacheDir, "imageCache/${gallery.id}") - val data = getCachedGallery(context, gallery.id) - cache.deleteRecursively() - data.deleteRecursively() - - downloads.remove(gallery.id) - - if (mode == Mode.DOWNLOAD) { - runOnUiThread { - cancelFetch() - clearGalleries() - fetchGalleries(query, sortMode) - loadBlocks() - } - } - - (adapter as GalleryBlockAdapter).completeFlag.put(gallery.id, false) - } - dialog.dismiss() - } - - dialog.show() + GalleryDialog(this@MainActivity, galleryID) + .show() true } @@ -722,6 +700,7 @@ class MainActivity : AppCompatActivity() { val text = next.findViewById(R.id.text_next).apply { text = getString(R.string.main_move, currentPage+2) } + if (absDist < 360) { next.layoutParams.height = (absDist/2).roundToInt() icon.layoutParams.height = (absDist/2).roundToInt() @@ -729,8 +708,7 @@ class MainActivity : AppCompatActivity() { text.layoutParams.width = absDist.roundToInt() target = -1 - } - else { + } else { next.layoutParams.height = 180 icon.layoutParams.height = 180 icon.rotation = 0f @@ -905,8 +883,6 @@ class MainActivity : AppCompatActivity() { rotation = 0f isEnabled = true - setColorFilter(ContextCompat.getColor(context, R.color.material_orange_500)) - isClickable = true setOnClickListener { val favorites = Tags(json.parse(serializer, favoritesFile.readText())) diff --git a/app/src/main/java/xyz/quaver/pupil/ui/ReaderActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/ReaderActivity.kt index 9ed757eb..d342f915 100644 --- a/app/src/main/java/xyz/quaver/pupil/ui/ReaderActivity.kt +++ b/app/src/main/java/xyz/quaver/pupil/ui/ReaderActivity.kt @@ -18,6 +18,7 @@ package xyz.quaver.pupil.ui +import android.Manifest import android.content.Intent import android.graphics.drawable.Animatable import android.graphics.drawable.Drawable @@ -47,6 +48,7 @@ import xyz.quaver.pupil.adapters.ReaderAdapter import xyz.quaver.pupil.util.GalleryDownloader import xyz.quaver.pupil.util.Histories import xyz.quaver.pupil.util.ItemClickSupport +import xyz.quaver.pupil.util.hasPermission class ReaderActivity : AppCompatActivity() { @@ -346,6 +348,17 @@ class ReaderActivity : AppCompatActivity() { with(reader_fab_download) { setImageResource(R.drawable.ic_download) setOnClickListener { + + if (!this@ReaderActivity.hasPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)) { + AlertDialog.Builder(this@ReaderActivity).apply { + setTitle(R.string.warning) + setMessage(R.string.update_no_permission) + setPositiveButton(android.R.string.ok) { _, _ -> } + }.show() + + return@setOnClickListener + } + downloader.download = !downloader.download if (!downloader.download) diff --git a/app/src/main/java/xyz/quaver/pupil/util/misc.kt b/app/src/main/java/xyz/quaver/pupil/util/misc.kt new file mode 100644 index 00000000..1c7dba9a --- /dev/null +++ b/app/src/main/java/xyz/quaver/pupil/util/misc.kt @@ -0,0 +1,26 @@ +/* + * 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 . + */ + +package xyz.quaver.pupil.util + +import android.content.Context +import android.content.pm.PackageManager +import androidx.core.content.ContextCompat + +fun Context.hasPermission(permission: String) = + ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED \ No newline at end of file diff --git a/app/src/main/res/drawable/arrow_right.xml b/app/src/main/res/drawable/arrow_right.xml new file mode 100644 index 00000000..c1a08d8a --- /dev/null +++ b/app/src/main/res/drawable/arrow_right.xml @@ -0,0 +1,8 @@ + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/avd_star.xml b/app/src/main/res/drawable/avd_star.xml index 6ae49f20..08381227 100644 --- a/app/src/main/res/drawable/avd_star.xml +++ b/app/src/main/res/drawable/avd_star.xml @@ -13,14 +13,14 @@ + android:fillColor="@color/material_orange_500"/> + android:fillColor="@color/material_orange_500"/> diff --git a/app/src/main/res/drawable/ic_star_empty.xml b/app/src/main/res/drawable/ic_star_empty.xml index 2231c7f8..028ac629 100644 --- a/app/src/main/res/drawable/ic_star_empty.xml +++ b/app/src/main/res/drawable/ic_star_empty.xml @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_star_filled.xml b/app/src/main/res/drawable/ic_star_filled.xml index ae9a8278..c2614e6d 100644 --- a/app/src/main/res/drawable/ic_star_filled.xml +++ b/app/src/main/res/drawable/ic_star_filled.xml @@ -1,4 +1,4 @@ - + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_lock.xml b/app/src/main/res/layout/activity_lock.xml index b570d386..4aab2840 100644 --- a/app/src/main/res/layout/activity_lock.xml +++ b/app/src/main/res/layout/activity_lock.xml @@ -1,4 +1,22 @@ + + + + + + + + + + -. + --> + + + android:orientation="vertical"> -