Compare commits
12 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
24486d13f2 | ||
|
|
20bc9461de | ||
|
|
c8e94cc295 | ||
|
|
b2bfb0c237 | ||
|
|
0a003da724 | ||
|
|
b4f2a33016 | ||
|
|
ee7ede2885 | ||
|
|
6abc404eb7 | ||
|
|
61afe01e36 | ||
|
|
dfe435c4f3 | ||
|
|
c9bde3c487 | ||
|
|
4f249c07e7 |
1
.idea/gradle.xml
generated
1
.idea/gradle.xml
generated
@@ -4,7 +4,6 @@
|
|||||||
<component name="GradleSettings">
|
<component name="GradleSettings">
|
||||||
<option name="linkedExternalProjectsSettings">
|
<option name="linkedExternalProjectsSettings">
|
||||||
<GradleProjectSettings>
|
<GradleProjectSettings>
|
||||||
<option name="delegatedBuild" value="false" />
|
|
||||||
<option name="testRunner" value="PLATFORM" />
|
<option name="testRunner" value="PLATFORM" />
|
||||||
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
<option name="distributionType" value="DEFAULT_WRAPPED" />
|
||||||
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
<option name="externalProjectPath" value="$PROJECT_DIR$" />
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ android {
|
|||||||
applicationId "xyz.quaver.pupil"
|
applicationId "xyz.quaver.pupil"
|
||||||
minSdkVersion 16
|
minSdkVersion 16
|
||||||
targetSdkVersion 29
|
targetSdkVersion 29
|
||||||
versionCode 46
|
versionCode 50
|
||||||
versionName "4.10"
|
versionName "4.14"
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
multiDexEnabled true
|
multiDexEnabled true
|
||||||
vectorDrawables.useSupportLibrary = true
|
vectorDrawables.useSupportLibrary = true
|
||||||
@@ -35,9 +35,6 @@ android {
|
|||||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
}
|
}
|
||||||
release {
|
release {
|
||||||
minifyEnabled true
|
|
||||||
shrinkResources true
|
|
||||||
|
|
||||||
buildConfigField('Boolean', 'CENSOR', 'false')
|
buildConfigField('Boolean', 'CENSOR', 'false')
|
||||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
}
|
}
|
||||||
@@ -66,7 +63,7 @@ dependencies {
|
|||||||
implementation 'androidx.preference:preference:1.1.0'
|
implementation 'androidx.preference:preference:1.1.0'
|
||||||
implementation 'androidx.gridlayout:gridlayout:1.0.0'
|
implementation 'androidx.gridlayout:gridlayout:1.0.0'
|
||||||
implementation "androidx.biometric:biometric:1.0.1"
|
implementation "androidx.biometric:biometric:1.0.1"
|
||||||
implementation 'com.android.support:multidex:1.0.3'
|
implementation 'androidx.multidex:multidex:2.0.1'
|
||||||
implementation "com.daimajia.swipelayout:library:1.2.0@aar"
|
implementation "com.daimajia.swipelayout:library:1.2.0@aar"
|
||||||
implementation 'com.google.android.material:material:1.2.0-alpha05'
|
implementation 'com.google.android.material:material:1.2.0-alpha05'
|
||||||
implementation 'com.google.firebase:firebase-core:17.2.2'
|
implementation 'com.google.firebase:firebase-core:17.2.2'
|
||||||
|
|||||||
7
app/proguard-rules.pro
vendored
7
app/proguard-rules.pro
vendored
@@ -23,8 +23,13 @@
|
|||||||
-dontobfuscate
|
-dontobfuscate
|
||||||
|
|
||||||
-keep public class * implements com.bumptech.glide.module.GlideModule
|
-keep public class * implements com.bumptech.glide.module.GlideModule
|
||||||
-keep public class * extends com.bumptech.glide.module.AppGlideModule
|
-keep class * extends com.bumptech.glide.module.AppGlideModule {
|
||||||
|
<init>(...);
|
||||||
|
}
|
||||||
-keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
|
-keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
|
||||||
**[] $VALUES;
|
**[] $VALUES;
|
||||||
public *;
|
public *;
|
||||||
}
|
}
|
||||||
|
-keep class com.bumptech.glide.load.data.ParcelFileDescriptorRewinder$InternalRewinder {
|
||||||
|
*** rewind();
|
||||||
|
}
|
||||||
@@ -1 +1 @@
|
|||||||
[{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":46,"versionName":"4.10","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release","dirName":""},"path":"app-release.apk","properties":{}}]
|
[{"outputType":{"type":"APK"},"apkData":{"type":"MAIN","splits":[],"versionCode":50,"versionName":"4.14","enabled":true,"outputFile":"app-release.apk","fullName":"release","baseName":"release","dirName":""},"path":"app-release.apk","properties":{}}]
|
||||||
@@ -8,6 +8,7 @@
|
|||||||
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
|
||||||
|
|
||||||
|
|
||||||
<application
|
<application
|
||||||
@@ -20,7 +21,8 @@
|
|||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/AppTheme"
|
android:theme="@style/AppTheme"
|
||||||
tools:replace="android:theme"
|
tools:replace="android:theme"
|
||||||
android:requestLegacyExternalStorage="true">
|
android:requestLegacyExternalStorage="true"
|
||||||
|
tools:ignore="UnusedAttribute">
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:authorities="${applicationId}.provider"
|
android:authorities="${applicationId}.provider"
|
||||||
@@ -34,6 +36,12 @@
|
|||||||
|
|
||||||
</provider>
|
</provider>
|
||||||
|
|
||||||
|
<receiver android:name=".BroadcastReciever" android:exported="true">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.DOWNLOAD_COMPLETE"/>
|
||||||
|
</intent-filter>
|
||||||
|
</receiver>
|
||||||
|
|
||||||
<activity android:name=".ui.LockActivity" />
|
<activity android:name=".ui.LockActivity" />
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.ReaderActivity"
|
android:name=".ui.ReaderActivity"
|
||||||
|
|||||||
93
app/src/main/java/xyz/quaver/pupil/BroadcastReciever.kt
Normal file
93
app/src/main/java/xyz/quaver/pupil/BroadcastReciever.kt
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
/*
|
||||||
|
* Pupil, Hitomi.la viewer for Android
|
||||||
|
* Copyright (C) 2020 tom5079
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package xyz.quaver.pupil
|
||||||
|
|
||||||
|
import android.app.DownloadManager
|
||||||
|
import android.app.PendingIntent
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.net.Uri
|
||||||
|
import android.webkit.MimeTypeMap
|
||||||
|
import androidx.core.app.NotificationCompat
|
||||||
|
import androidx.core.app.NotificationManagerCompat
|
||||||
|
import androidx.core.content.FileProvider
|
||||||
|
import androidx.preference.PreferenceManager
|
||||||
|
import xyz.quaver.pupil.util.NOTIFICATION_ID_UPDATE
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
class BroadcastReciever : BroadcastReceiver() {
|
||||||
|
|
||||||
|
override fun onReceive(context: Context?, intent: Intent?) {
|
||||||
|
context ?: return
|
||||||
|
|
||||||
|
when (intent?.action) {
|
||||||
|
DownloadManager.ACTION_DOWNLOAD_COMPLETE -> {
|
||||||
|
|
||||||
|
// Validate download
|
||||||
|
|
||||||
|
val preference = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
val downloadID = preference.getLong("update_download_id", -1)
|
||||||
|
val downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
|
||||||
|
|
||||||
|
if (intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1) != downloadID)
|
||||||
|
return
|
||||||
|
|
||||||
|
// Get target uri
|
||||||
|
|
||||||
|
val query = DownloadManager.Query()
|
||||||
|
.setFilterById(downloadID)
|
||||||
|
|
||||||
|
val uri = downloadManager.query(query).use { cursor ->
|
||||||
|
cursor.moveToFirst()
|
||||||
|
|
||||||
|
cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)).let {
|
||||||
|
val uri = Uri.parse(it)
|
||||||
|
|
||||||
|
when (uri.scheme) {
|
||||||
|
"file" ->
|
||||||
|
FileProvider.getUriForFile(context, context.applicationContext.packageName + ".provider", File(uri.path!!))
|
||||||
|
"content" -> uri
|
||||||
|
else -> return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build Notification
|
||||||
|
|
||||||
|
val notificationManager = NotificationManagerCompat.from(context)
|
||||||
|
|
||||||
|
val pendingIntent = PendingIntent.getActivity(context, 0, Intent(Intent.ACTION_VIEW).apply {
|
||||||
|
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_ACTIVITY_NEW_TASK
|
||||||
|
setDataAndType(uri, MimeTypeMap.getSingleton().getMimeTypeFromExtension("apk"))
|
||||||
|
}, 0)
|
||||||
|
|
||||||
|
val notification = NotificationCompat.Builder(context, "update")
|
||||||
|
.setSmallIcon(android.R.drawable.stat_sys_download_done)
|
||||||
|
.setContentTitle(context.getText(R.string.update_download_completed))
|
||||||
|
.setContentText(context.getText(R.string.update_download_completed_description))
|
||||||
|
.setContentIntent(pendingIntent)
|
||||||
|
.build()
|
||||||
|
|
||||||
|
notificationManager.notify(NOTIFICATION_ID_UPDATE, notification)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -51,7 +51,10 @@ class Pupil : MultiDexApplication() {
|
|||||||
proxy = getProxy(this)
|
proxy = getProxy(this)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
preference.getString("dl_location", null)
|
preference.getString("dl_location", null).also {
|
||||||
|
if (!File(it!!).canWrite())
|
||||||
|
throw Exception()
|
||||||
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
preference.edit().remove("dl_location").apply()
|
preference.edit().remove("dl_location").apply()
|
||||||
}
|
}
|
||||||
@@ -72,13 +75,20 @@ class Pupil : MultiDexApplication() {
|
|||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
val channel = NotificationChannel("download", getString(R.string.channel_download), NotificationManager.IMPORTANCE_MIN).apply {
|
|
||||||
|
manager.createNotificationChannel(NotificationChannel("download", getString(R.string.channel_download), NotificationManager.IMPORTANCE_LOW).apply {
|
||||||
description = getString(R.string.channel_download_description)
|
description = getString(R.string.channel_download_description)
|
||||||
enableLights(false)
|
enableLights(false)
|
||||||
enableVibration(false)
|
enableVibration(false)
|
||||||
lockscreenVisibility = Notification.VISIBILITY_SECRET
|
lockscreenVisibility = Notification.VISIBILITY_SECRET
|
||||||
}
|
})
|
||||||
manager.createNotificationChannel(channel)
|
|
||||||
|
manager.createNotificationChannel(NotificationChannel("update", getString(R.string.channel_update), NotificationManager.IMPORTANCE_HIGH).apply {
|
||||||
|
description = getString(R.string.channel_update_description)
|
||||||
|
enableLights(true)
|
||||||
|
enableVibration(true)
|
||||||
|
lockscreenVisibility = Notification.VISIBILITY_SECRET
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
|
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ import androidx.recyclerview.widget.RecyclerView
|
|||||||
import androidx.swiperefreshlayout.widget.CircularProgressDrawable
|
import androidx.swiperefreshlayout.widget.CircularProgressDrawable
|
||||||
import androidx.vectordrawable.graphics.drawable.Animatable2Compat
|
import androidx.vectordrawable.graphics.drawable.Animatable2Compat
|
||||||
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat
|
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.RequestManager
|
||||||
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||||
import com.daimajia.swipe.SwipeLayout
|
import com.daimajia.swipe.SwipeLayout
|
||||||
import com.daimajia.swipe.adapters.RecyclerSwipeAdapter
|
import com.daimajia.swipe.adapters.RecyclerSwipeAdapter
|
||||||
@@ -54,7 +54,7 @@ import java.util.*
|
|||||||
import kotlin.collections.ArrayList
|
import kotlin.collections.ArrayList
|
||||||
import kotlin.concurrent.schedule
|
import kotlin.concurrent.schedule
|
||||||
|
|
||||||
class GalleryBlockAdapter(private val context: Context, private val galleries: List<GalleryBlock>) : RecyclerSwipeAdapter<RecyclerView.ViewHolder>(), SwipeAdapterInterface {
|
class GalleryBlockAdapter(private val glide: RequestManager, private val galleries: List<GalleryBlock>) : RecyclerSwipeAdapter<RecyclerView.ViewHolder>(), SwipeAdapterInterface {
|
||||||
|
|
||||||
enum class ViewType {
|
enum class ViewType {
|
||||||
NEXT,
|
NEXT,
|
||||||
@@ -62,7 +62,6 @@ class GalleryBlockAdapter(private val context: Context, private val galleries: L
|
|||||||
PREV
|
PREV
|
||||||
}
|
}
|
||||||
|
|
||||||
private val glide = Glide.with(context)
|
|
||||||
private lateinit var favorites: Histories
|
private lateinit var favorites: Histories
|
||||||
|
|
||||||
val timer = Timer()
|
val timer = Timer()
|
||||||
@@ -353,7 +352,7 @@ class GalleryBlockAdapter(private val context: Context, private val galleries: L
|
|||||||
mItemManger.closeAllExcept(layout)
|
mItemManger.closeAllExcept(layout)
|
||||||
|
|
||||||
holder.view.galleryblock_download.text =
|
holder.view.galleryblock_download.text =
|
||||||
if (Cache(context).isDownloading(gallery.id))
|
if (Cache(holder.view.context).isDownloading(gallery.id))
|
||||||
holder.view.context.getString(android.R.string.cancel)
|
holder.view.context.getString(android.R.string.cancel)
|
||||||
else
|
else
|
||||||
holder.view.context.getString(R.string.main_download)
|
holder.view.context.getString(R.string.main_download)
|
||||||
|
|||||||
@@ -18,16 +18,13 @@
|
|||||||
|
|
||||||
package xyz.quaver.pupil.adapters
|
package xyz.quaver.pupil.adapters
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import androidx.constraintlayout.widget.ConstraintLayout
|
import androidx.constraintlayout.widget.ConstraintLayout
|
||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import com.bumptech.glide.Glide
|
import com.bumptech.glide.RequestManager
|
||||||
import com.bumptech.glide.ListPreloader
|
import com.bumptech.glide.load.engine.DiskCacheStrategy
|
||||||
import com.bumptech.glide.RequestBuilder
|
|
||||||
import com.bumptech.glide.integration.recyclerview.RecyclerViewPreloader
|
|
||||||
import com.crashlytics.android.Crashlytics
|
import com.crashlytics.android.Crashlytics
|
||||||
import io.fabric.sdk.android.Fabric
|
import io.fabric.sdk.android.Fabric
|
||||||
import kotlinx.android.synthetic.main.item_reader.view.*
|
import kotlinx.android.synthetic.main.item_reader.view.*
|
||||||
@@ -35,45 +32,16 @@ import kotlinx.coroutines.CoroutineScope
|
|||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import xyz.quaver.hitomi.Reader
|
import xyz.quaver.hitomi.Reader
|
||||||
import xyz.quaver.pupil.BuildConfig
|
|
||||||
import xyz.quaver.pupil.R
|
import xyz.quaver.pupil.R
|
||||||
import xyz.quaver.pupil.util.download.Cache
|
import xyz.quaver.pupil.util.download.Cache
|
||||||
import xyz.quaver.pupil.util.download.DownloadWorker
|
import xyz.quaver.pupil.util.download.DownloadWorker
|
||||||
import java.io.File
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.concurrent.schedule
|
import kotlin.concurrent.schedule
|
||||||
import kotlin.math.roundToInt
|
import kotlin.math.roundToInt
|
||||||
|
|
||||||
class ReaderAdapter(private val context: Context,
|
class ReaderAdapter(private val glide: RequestManager,
|
||||||
private val galleryID: Int) : RecyclerView.Adapter<ReaderAdapter.ViewHolder>() {
|
private val galleryID: Int) : RecyclerView.Adapter<ReaderAdapter.ViewHolder>() {
|
||||||
|
|
||||||
val glide = Glide.with(context)
|
|
||||||
|
|
||||||
//region Glide.RecyclerView
|
|
||||||
val sizeProvider = ListPreloader.PreloadSizeProvider<File> { _, _, position ->
|
|
||||||
Cache(context).getReaderOrNull(galleryID)?.galleryInfo?.files?.getOrNull(position)?.let {
|
|
||||||
arrayOf(it.width, it.height).toIntArray()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val modelProvider = object: ListPreloader.PreloadModelProvider<File> {
|
|
||||||
override fun getPreloadItems(position: Int): MutableList<File> {
|
|
||||||
return listOf(Cache(context).getImages(galleryID)?.getOrNull(position)).filterNotNullTo(mutableListOf())
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun getPreloadRequestBuilder(item: File): RequestBuilder<*>? {
|
|
||||||
return glide
|
|
||||||
.load(item)
|
|
||||||
.fitCenter()
|
|
||||||
.error(R.drawable.image_broken_variant)
|
|
||||||
.apply {
|
|
||||||
if (BuildConfig.CENSOR)
|
|
||||||
override(5, 8)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val preloader = RecyclerViewPreloader<File>(glide, modelProvider, sizeProvider, 10)
|
|
||||||
//endregion
|
|
||||||
|
|
||||||
var reader: Reader? = null
|
var reader: Reader? = null
|
||||||
val timer = Timer()
|
val timer = Timer()
|
||||||
|
|
||||||
@@ -115,8 +83,8 @@ class ReaderAdapter(private val context: Context,
|
|||||||
|
|
||||||
holder.view.reader_index.text = (position+1).toString()
|
holder.view.reader_index.text = (position+1).toString()
|
||||||
|
|
||||||
val images = Cache(context).getImage(galleryID, position)
|
val images = Cache(holder.view.context).getImage(galleryID, position)
|
||||||
val progress = DownloadWorker.getInstance(context).progress[galleryID]?.get(position)
|
val progress = DownloadWorker.getInstance(holder.view.context).progress[galleryID]?.get(position)
|
||||||
|
|
||||||
if (progress?.isInfinite() == true && images != null) {
|
if (progress?.isInfinite() == true && images != null) {
|
||||||
holder.view.reader_item_progressbar.visibility = View.INVISIBLE
|
holder.view.reader_item_progressbar.visibility = View.INVISIBLE
|
||||||
@@ -124,6 +92,8 @@ class ReaderAdapter(private val context: Context,
|
|||||||
holder.view.image.post {
|
holder.view.image.post {
|
||||||
glide
|
glide
|
||||||
.load(images)
|
.load(images)
|
||||||
|
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||||
|
.skipMemoryCache(true)
|
||||||
.fitCenter()
|
.fitCenter()
|
||||||
.error(R.drawable.image_broken_variant)
|
.error(R.drawable.image_broken_variant)
|
||||||
.into(holder.view.image)
|
.into(holder.view.image)
|
||||||
@@ -136,7 +106,7 @@ class ReaderAdapter(private val context: Context,
|
|||||||
|
|
||||||
if (progress?.isNaN() == true) {
|
if (progress?.isNaN() == true) {
|
||||||
if (Fabric.isInitialized())
|
if (Fabric.isInitialized())
|
||||||
Crashlytics.logException(DownloadWorker.getInstance(context).exception[galleryID]?.get(position))
|
Crashlytics.logException(DownloadWorker.getInstance(holder.view.context).exception[galleryID]?.get(position))
|
||||||
|
|
||||||
glide
|
glide
|
||||||
.load(R.drawable.image_broken_variant)
|
.load(R.drawable.image_broken_variant)
|
||||||
|
|||||||
@@ -45,6 +45,7 @@ import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat
|
|||||||
import com.arlib.floatingsearchview.FloatingSearchView
|
import com.arlib.floatingsearchview.FloatingSearchView
|
||||||
import com.arlib.floatingsearchview.suggestions.model.SearchSuggestion
|
import com.arlib.floatingsearchview.suggestions.model.SearchSuggestion
|
||||||
import com.arlib.floatingsearchview.util.view.SearchInputView
|
import com.arlib.floatingsearchview.util.view.SearchInputView
|
||||||
|
import com.bumptech.glide.Glide
|
||||||
import com.crashlytics.android.Crashlytics
|
import com.crashlytics.android.Crashlytics
|
||||||
import com.google.android.material.appbar.AppBarLayout
|
import com.google.android.material.appbar.AppBarLayout
|
||||||
import io.fabric.sdk.android.Fabric
|
import io.fabric.sdk.android.Fabric
|
||||||
@@ -195,7 +196,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
|
override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean {
|
||||||
val preference = PreferenceManager.getDefaultSharedPreferences(this)
|
val preference = PreferenceManager.getDefaultSharedPreferences(this)
|
||||||
val perPage = preference.getString("per_page", "25")!!.toInt()
|
val perPage = preference.getString("per_page", "25")!!.toIntOrNull() ?: 25
|
||||||
val maxPage = ceil(totalItems / perPage.toDouble()).roundToInt()
|
val maxPage = ceil(totalItems / perPage.toDouble()).roundToInt()
|
||||||
|
|
||||||
return when(keyCode) {
|
return when(keyCode) {
|
||||||
@@ -341,7 +342,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
setImageResource(R.drawable.ic_jump)
|
setImageResource(R.drawable.ic_jump)
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
val preference = PreferenceManager.getDefaultSharedPreferences(context)
|
val preference = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
val perPage = preference.getString("per_page", "25")!!.toInt()
|
val perPage = preference.getString("per_page", "25")!!.toIntOrNull() ?: 25
|
||||||
val editText = EditText(context)
|
val editText = EditText(context)
|
||||||
|
|
||||||
AlertDialog.Builder(context).apply {
|
AlertDialog.Builder(context).apply {
|
||||||
@@ -369,14 +370,16 @@ class MainActivity : AppCompatActivity() {
|
|||||||
with(main_fab_id) {
|
with(main_fab_id) {
|
||||||
setImageResource(R.drawable.numeric)
|
setImageResource(R.drawable.numeric)
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
val editText = EditText(context)
|
val editText = EditText(context).apply {
|
||||||
|
inputType = InputType.TYPE_CLASS_NUMBER
|
||||||
|
}
|
||||||
|
|
||||||
AlertDialog.Builder(context).apply {
|
AlertDialog.Builder(context).apply {
|
||||||
setView(editText)
|
setView(editText)
|
||||||
setTitle(R.string.main_open_gallery_by_id)
|
setTitle(R.string.main_open_gallery_by_id)
|
||||||
|
|
||||||
setPositiveButton(android.R.string.ok) { _, _ ->
|
setPositiveButton(android.R.string.ok) { _, _ ->
|
||||||
val galleryID = editText.text.toString().toInt()
|
val galleryID = editText.text.toString().toIntOrNull() ?: return@setPositiveButton
|
||||||
val intent = Intent(this@MainActivity, ReaderActivity::class.java).apply {
|
val intent = Intent(this@MainActivity, ReaderActivity::class.java).apply {
|
||||||
putExtra("galleryID", galleryID)
|
putExtra("galleryID", galleryID)
|
||||||
}
|
}
|
||||||
@@ -397,7 +400,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
private fun setupRecyclerView() {
|
private fun setupRecyclerView() {
|
||||||
with(main_recyclerview) {
|
with(main_recyclerview) {
|
||||||
adapter = GalleryBlockAdapter(this@MainActivity, galleries).apply {
|
adapter = GalleryBlockAdapter(Glide.with(this@MainActivity), galleries).apply {
|
||||||
onChipClickedHandler.add {
|
onChipClickedHandler.add {
|
||||||
runOnUiThread {
|
runOnUiThread {
|
||||||
query = it.toQuery()
|
query = it.toQuery()
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import androidx.recyclerview.widget.PagerSnapHelper
|
|||||||
import androidx.recyclerview.widget.RecyclerView
|
import androidx.recyclerview.widget.RecyclerView
|
||||||
import androidx.vectordrawable.graphics.drawable.Animatable2Compat
|
import androidx.vectordrawable.graphics.drawable.Animatable2Compat
|
||||||
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat
|
import androidx.vectordrawable.graphics.drawable.AnimatedVectorDrawableCompat
|
||||||
|
import com.bumptech.glide.Glide
|
||||||
import com.crashlytics.android.Crashlytics
|
import com.crashlytics.android.Crashlytics
|
||||||
import com.google.android.material.snackbar.Snackbar
|
import com.google.android.material.snackbar.Snackbar
|
||||||
import io.fabric.sdk.android.Fabric
|
import io.fabric.sdk.android.Fabric
|
||||||
@@ -286,7 +287,7 @@ class ReaderActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
private fun initView() {
|
private fun initView() {
|
||||||
with(reader_recyclerview) {
|
with(reader_recyclerview) {
|
||||||
adapter = ReaderAdapter(this@ReaderActivity, galleryID).apply {
|
adapter = ReaderAdapter(Glide.with(this@ReaderActivity), galleryID).apply {
|
||||||
onItemClickListener = {
|
onItemClickListener = {
|
||||||
if (isScroll) {
|
if (isScroll) {
|
||||||
isScroll = false
|
isScroll = false
|
||||||
@@ -300,7 +301,6 @@ class ReaderActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
addOnScrollListener((adapter as ReaderAdapter).preloader)
|
|
||||||
addOnScrollListener(object: RecyclerView.OnScrollListener() {
|
addOnScrollListener(object: RecyclerView.OnScrollListener() {
|
||||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||||
super.onScrolled(recyclerView, dx, dy)
|
super.onScrolled(recyclerView, dx, dy)
|
||||||
|
|||||||
@@ -226,7 +226,7 @@ class GalleryDialog(context: Context, private val galleryID: Int) : Dialog(conte
|
|||||||
val inflater = LayoutInflater.from(context)
|
val inflater = LayoutInflater.from(context)
|
||||||
val galleries = ArrayList<GalleryBlock>()
|
val galleries = ArrayList<GalleryBlock>()
|
||||||
|
|
||||||
val adapter = GalleryBlockAdapter(context, galleries).apply {
|
val adapter = GalleryBlockAdapter(Glide.with(ownerActivity ?: return), galleries).apply {
|
||||||
onChipClickedHandler.add { tag ->
|
onChipClickedHandler.add { tag ->
|
||||||
this@GalleryDialog.onChipClickedHandler.forEach { handler ->
|
this@GalleryDialog.onChipClickedHandler.forEach { handler ->
|
||||||
handler.invoke(tag)
|
handler.invoke(tag)
|
||||||
|
|||||||
@@ -27,4 +27,6 @@ const val REQUEST_DOWNLOAD_FOLDER = 3874
|
|||||||
const val REQUEST_DOWNLOAD_FOLDER_OLD = 3425
|
const val REQUEST_DOWNLOAD_FOLDER_OLD = 3425
|
||||||
const val REQUEST_WRITE_PERMISSION_AND_SAF = 13900
|
const val REQUEST_WRITE_PERMISSION_AND_SAF = 13900
|
||||||
|
|
||||||
|
const val NOTIFICATION_ID_UPDATE = 2345
|
||||||
|
|
||||||
val json = Json(JsonConfiguration.Stable)
|
val json = Json(JsonConfiguration.Stable)
|
||||||
@@ -18,16 +18,10 @@
|
|||||||
|
|
||||||
package xyz.quaver.pupil.util
|
package xyz.quaver.pupil.util
|
||||||
|
|
||||||
import android.app.PendingIntent
|
import android.app.DownloadManager
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.net.Uri
|
||||||
import android.webkit.MimeTypeMap
|
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.appcompat.app.AppCompatActivity
|
|
||||||
import androidx.core.app.NotificationCompat
|
|
||||||
import androidx.core.app.NotificationManagerCompat
|
|
||||||
import androidx.core.content.FileProvider
|
|
||||||
import androidx.lifecycle.Lifecycle
|
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
@@ -81,7 +75,7 @@ fun getApkUrl(releases: JsonObject) : String? {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const val UPDATE_NOTIFICATION_ID = 384823
|
const val UPDATE_NOTIFICATION_ID = 384823
|
||||||
fun checkUpdate(context: AppCompatActivity, force: Boolean = false) {
|
fun checkUpdate(context: Context, force: Boolean = false) {
|
||||||
|
|
||||||
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
val ignoreUpdateUntil = preferences.getLong("ignore_update_until", 0)
|
val ignoreUpdateUntil = preferences.getLong("ignore_update_until", 0)
|
||||||
@@ -143,56 +137,27 @@ fun checkUpdate(context: AppCompatActivity, force: Boolean = false) {
|
|||||||
setMessage(Markwon.create(context).toMarkdown(msg))
|
setMessage(Markwon.create(context).toMarkdown(msg))
|
||||||
setPositiveButton(android.R.string.yes) { _, _ ->
|
setPositiveButton(android.R.string.yes) { _, _ ->
|
||||||
|
|
||||||
val notificationManager = NotificationManagerCompat.from(context)
|
val preference = PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
val builder = NotificationCompat.Builder(context, "download").apply {
|
|
||||||
setContentTitle(context.getString(R.string.update_notification_description))
|
val downloadManager = context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
|
||||||
setSmallIcon(android.R.drawable.stat_sys_download)
|
|
||||||
priority = NotificationCompat.PRIORITY_LOW
|
//Cancel any download queued before
|
||||||
setOngoing(true)
|
|
||||||
|
val id = preference.getLong("update_download_id", -1)
|
||||||
|
|
||||||
|
if (id != -1L)
|
||||||
|
downloadManager.remove(id)
|
||||||
|
|
||||||
|
val target = File(context.getExternalFilesDir(null), "Pupil.apk").also {
|
||||||
|
it.delete()
|
||||||
}
|
}
|
||||||
|
|
||||||
CoroutineScope(Dispatchers.IO).launch io@{
|
val request = DownloadManager.Request(Uri.parse(url))
|
||||||
val target = File(getDownloadDirectory(context), "Pupil.apk")
|
.setTitle(context.getText(R.string.update_notification_description))
|
||||||
|
.setDestinationUri(Uri.fromFile(target))
|
||||||
|
|
||||||
try {
|
downloadManager.enqueue(request).also {
|
||||||
URL(url).download(target) { progress, fileSize ->
|
preference.edit().putLong("update_download_id", it).apply()
|
||||||
builder.setProgress(fileSize.toInt(), progress.toInt(), false)
|
|
||||||
notificationManager.notify(UPDATE_NOTIFICATION_ID, builder.build())
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
builder.apply {
|
|
||||||
setContentText(context.getString(R.string.update_failed))
|
|
||||||
setMessage(context.getString(R.string.update_failed_message))
|
|
||||||
setSmallIcon(android.R.drawable.stat_sys_download_done)
|
|
||||||
setOngoing(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
notificationManager.cancel(UPDATE_NOTIFICATION_ID)
|
|
||||||
notificationManager.notify(UPDATE_NOTIFICATION_ID, builder.build())
|
|
||||||
|
|
||||||
return@io
|
|
||||||
}
|
|
||||||
|
|
||||||
val install = Intent(Intent.ACTION_VIEW).apply {
|
|
||||||
flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_GRANT_READ_URI_PERMISSION
|
|
||||||
setDataAndType(FileProvider.getUriForFile(context, context.applicationContext.packageName + ".provider", target), MimeTypeMap.getSingleton().getMimeTypeFromExtension("apk"))
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.apply {
|
|
||||||
setContentIntent(PendingIntent.getActivity(context, 0, install, 0))
|
|
||||||
setProgress(0, 0, false)
|
|
||||||
setSmallIcon(android.R.drawable.stat_sys_download_done)
|
|
||||||
setContentTitle(context.getString(R.string.update_download_completed))
|
|
||||||
setContentText(context.getString(R.string.update_download_completed_description))
|
|
||||||
setOngoing(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
notificationManager.cancel(UPDATE_NOTIFICATION_ID)
|
|
||||||
|
|
||||||
if (context.lifecycle.currentState.isAtLeast(Lifecycle.State.RESUMED))
|
|
||||||
context.startActivity(install)
|
|
||||||
else
|
|
||||||
notificationManager.notify(UPDATE_NOTIFICATION_ID, builder.build())
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
setNegativeButton(if (force) android.R.string.no else R.string.ignore_update) { _, _ ->
|
setNegativeButton(if (force) android.R.string.no else R.string.ignore_update) { _, _ ->
|
||||||
|
|||||||
@@ -132,4 +132,6 @@
|
|||||||
<string name="proxy_dialog_server">サーバー</string>
|
<string name="proxy_dialog_server">サーバー</string>
|
||||||
<string name="main_menu_thin">簡単モード</string>
|
<string name="main_menu_thin">簡単モード</string>
|
||||||
<string name="main_fab_cancel">すべてのダウンロードキャンセル</string>
|
<string name="main_fab_cancel">すべてのダウンロードキャンセル</string>
|
||||||
|
<string name="channel_update">アップデート</string>
|
||||||
|
<string name="channel_update_description">アップデートの進行状態を表示</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -132,4 +132,6 @@
|
|||||||
<string name="proxy_dialog_server">서버</string>
|
<string name="proxy_dialog_server">서버</string>
|
||||||
<string name="main_menu_thin">간단히 보기 모드</string>
|
<string name="main_menu_thin">간단히 보기 모드</string>
|
||||||
<string name="main_fab_cancel">다운로드 모두 취소</string>
|
<string name="main_fab_cancel">다운로드 모두 취소</string>
|
||||||
|
<string name="channel_update">업데이트</string>
|
||||||
|
<string name="channel_update_description">업데이트 진행상황 표시</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -35,6 +35,9 @@
|
|||||||
<string name="channel_download">Download</string>
|
<string name="channel_download">Download</string>
|
||||||
<string name="channel_download_description">Shows download status</string>
|
<string name="channel_download_description">Shows download status</string>
|
||||||
|
|
||||||
|
<string name="channel_update">Update</string>
|
||||||
|
<string name="channel_update_description">Shows update progress</string>
|
||||||
|
|
||||||
<string name="unable_to_connect">Unable to connect to hitomi.la</string>
|
<string name="unable_to_connect">Unable to connect to hitomi.la</string>
|
||||||
|
|
||||||
<string name="lock_corrupted">Lock file corrupted! Please re-install Pupil</string>
|
<string name="lock_corrupted">Lock file corrupted! Please re-install Pupil</string>
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ buildscript {
|
|||||||
maven { url 'https://maven.fabric.io/public' }
|
maven { url 'https://maven.fabric.io/public' }
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:3.6.0'
|
classpath 'com.android.tools.build:gradle:3.6.1'
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version"
|
||||||
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
|
||||||
|
|||||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,4 +1,4 @@
|
|||||||
#Tue Feb 25 09:55:23 KST 2020
|
#Sat Feb 29 09:07:20 KST 2020
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
|
|||||||
@@ -11,8 +11,6 @@ dependencies {
|
|||||||
testImplementation 'junit:junit:4.13'
|
testImplementation 'junit:junit:4.13'
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceCompatibility = "7"
|
|
||||||
targetCompatibility = "7"
|
|
||||||
buildscript {
|
buildscript {
|
||||||
repositories {
|
repositories {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
|
|||||||
@@ -175,9 +175,13 @@ fun getGalleryIDsFromNozomi(area: String?, tag: String, language: String) : List
|
|||||||
else -> "$protocol//$domain/$compressed_nozomi_prefix/$area/$tag-$language$nozomiextension"
|
else -> "$protocol//$domain/$compressed_nozomi_prefix/$area/$tag-$language$nozomiextension"
|
||||||
}
|
}
|
||||||
|
|
||||||
val bytes = URL(nozomiAddress).openConnection(proxy).getInputStream().use {
|
val bytes = try {
|
||||||
|
URL(nozomiAddress).openConnection(proxy).getInputStream().use {
|
||||||
it.readBytes()
|
it.readBytes()
|
||||||
}
|
}
|
||||||
|
} catch (e: Exception) {
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
val nozomi = ArrayList<Int>()
|
val nozomi = ArrayList<Int>()
|
||||||
|
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ import java.net.URL
|
|||||||
import javax.net.ssl.HttpsURLConnection
|
import javax.net.ssl.HttpsURLConnection
|
||||||
|
|
||||||
const val hiyobi = "hiyobi.me"
|
const val hiyobi = "hiyobi.me"
|
||||||
|
const val primary_img_domain = "cdn.hiyobi.me"
|
||||||
const val user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36"
|
const val user_agent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.131 Safari/537.36"
|
||||||
|
|
||||||
var cookie: String = ""
|
var cookie: String = ""
|
||||||
@@ -87,7 +88,7 @@ fun createImgList(galleryID: Int, reader: Reader, lowQuality: Boolean = false) =
|
|||||||
if (lowQuality)
|
if (lowQuality)
|
||||||
reader.galleryInfo.files.map {
|
reader.galleryInfo.files.map {
|
||||||
val name = it.name.replace(Regex("""\.[^/.]+$"""), "")
|
val name = it.name.replace(Regex("""\.[^/.]+$"""), "")
|
||||||
Images("$protocol//$hiyobi/data_r/$galleryID/$name.jpg", galleryID, it.name)
|
Images("$protocol//$primary_img_domain/data_r/$galleryID/$name.jpg", galleryID, it.name)
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
reader.galleryInfo.files.map { Images("$protocol//$hiyobi/data/$galleryID/${it.name}", galleryID, it.name) }
|
reader.galleryInfo.files.map { Images("$protocol//$primary_img_domain/data/$galleryID/${it.name}", galleryID, it.name) }
|
||||||
@@ -75,7 +75,7 @@ class UnitTest {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun test_getReader() {
|
fun test_getReader() {
|
||||||
val reader = getReader(1567569)
|
val reader = getReader(1574736)
|
||||||
|
|
||||||
print(reader)
|
print(reader)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user