offline manga fix

This commit is contained in:
Finnley Somdahl 2023-12-01 00:58:58 -06:00
parent c7bc1ffe9e
commit f792296f78
6 changed files with 227 additions and 104 deletions

View file

@ -26,6 +26,7 @@ import ani.dantotsu.R
import ani.dantotsu.connections.discord.serializers.Activity import ani.dantotsu.connections.discord.serializers.Activity
import ani.dantotsu.connections.discord.serializers.Presence import ani.dantotsu.connections.discord.serializers.Presence
import ani.dantotsu.connections.discord.serializers.User import ani.dantotsu.connections.discord.serializers.User
import ani.dantotsu.isOnline
import com.google.gson.JsonArray import com.google.gson.JsonArray
import com.google.gson.JsonObject import com.google.gson.JsonObject
import com.google.gson.JsonParser import com.google.gson.JsonParser
@ -112,6 +113,7 @@ class DiscordService : Service() {
}else{ }else{
log("Service onStartCommand() no presence") log("Service onStartCommand() no presence")
DiscordServiceRunningSingleton.running = false DiscordServiceRunningSingleton.running = false
client.dispatcher.executorService.shutdown()
stopSelf() stopSelf()
} }
if (intent.hasExtra(ACTION_STOP_SERVICE)) { if (intent.hasExtra(ACTION_STOP_SERVICE)) {
@ -123,8 +125,6 @@ class DiscordService : Service() {
} }
override fun onDestroy() { override fun onDestroy() {
super.onDestroy()
log("Service Destroyed") log("Service Destroyed")
if (DiscordServiceRunningSingleton.running){ if (DiscordServiceRunningSingleton.running){
log("Accidental Service Destruction, restarting service") log("Accidental Service Destruction, restarting service")
@ -135,6 +135,7 @@ class DiscordService : Service() {
baseContext.startService(intent) baseContext.startService(intent)
} }
} else { } else {
if(this::webSocket.isInitialized)
setPresence(json.encodeToString( setPresence(json.encodeToString(
Presence.Response( Presence.Response(
3, 3,
@ -145,6 +146,7 @@ class DiscordService : Service() {
} }
SERVICE_RUNNING = false SERVICE_RUNNING = false
if(this::webSocket.isInitialized) webSocket.close(1000, "Closed by user") if(this::webSocket.isInitialized) webSocket.close(1000, "Closed by user")
super.onDestroy()
//saveLogToFile() //saveLogToFile()
} }
@ -167,6 +169,9 @@ class DiscordService : Service() {
override fun onBind(p0: Intent?): IBinder? = null override fun onBind(p0: Intent?): IBinder? = null
inner class DiscordWebSocketListener : WebSocketListener() { inner class DiscordWebSocketListener : WebSocketListener() {
var retryAttempts = 0
val maxRetryAttempts = 10
override fun onOpen(webSocket: WebSocket, response: Response) { override fun onOpen(webSocket: WebSocket, response: Response) {
super.onOpen(webSocket, response) super.onOpen(webSocket, response)
this@DiscordService.webSocket = webSocket this@DiscordService.webSocket = webSocket
@ -256,6 +261,18 @@ class DiscordService : Service() {
} }
override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) { override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
super.onFailure(webSocket, t, response) super.onFailure(webSocket, t, response)
if(!isOnline(baseContext)) {
log("WebSocket: Error, onFailure() reason: No Internet")
errorNotification("Could not set the presence", "No Internet")
return
} else{
retryAttempts++
if(retryAttempts >= maxRetryAttempts) {
log("WebSocket: Error, onFailure() reason: Max Retry Attempts")
errorNotification("Could not set the presence", "Max Retry Attempts")
return
}
}
t.message?.let { Log.d("WebSocket", "onFailure() $it") } t.message?.let { Log.d("WebSocket", "onFailure() $it") }
log("WebSocket: Error, onFailure() reason: ${t.message}") log("WebSocket: Error, onFailure() reason: ${t.message}")
client = OkHttpClient() client = OkHttpClient()
@ -264,13 +281,13 @@ class DiscordService : Service() {
DiscordWebSocketListener() DiscordWebSocketListener()
) )
client.dispatcher.executorService.shutdown() client.dispatcher.executorService.shutdown()
if(!heartbeatThread.isInterrupted) { heartbeatThread.interrupt() } if(::heartbeatThread.isInitialized && !heartbeatThread.isInterrupted) { heartbeatThread.interrupt() }
} }
override fun onClosing(webSocket: WebSocket, code: Int, reason: String) { override fun onClosing(webSocket: WebSocket, code: Int, reason: String) {
super.onClosing(webSocket, code, reason) super.onClosing(webSocket, code, reason)
Log.d("WebSocket", "onClosing() $code $reason") Log.d("WebSocket", "onClosing() $code $reason")
if(!heartbeatThread.isInterrupted) { heartbeatThread.interrupt() } if(::heartbeatThread.isInitialized && !heartbeatThread.isInterrupted) { heartbeatThread.interrupt() }
} }
override fun onClosed(webSocket: WebSocket, code: Int, reason: String) { override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {

View file

@ -98,14 +98,17 @@ class MangaReaderActivity : AppCompatActivity() {
var sliding = false var sliding = false
var isAnimating = false var isAnimating = false
private var rpc : RPC? = null private var rpc: RPC? = null
override fun onAttachedToWindow() { override fun onAttachedToWindow() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && !settings.showSystemBars) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P && !settings.showSystemBars) {
val displayCutout = window.decorView.rootWindowInsets.displayCutout val displayCutout = window.decorView.rootWindowInsets.displayCutout
if (displayCutout != null) { if (displayCutout != null) {
if (displayCutout.boundingRects.size > 0) { if (displayCutout.boundingRects.size > 0) {
notchHeight = min(displayCutout.boundingRects[0].width(), displayCutout.boundingRects[0].height()) notchHeight = min(
displayCutout.boundingRects[0].width(),
displayCutout.boundingRects[0].height()
)
checkNotch() checkNotch()
} }
} }
@ -128,15 +131,17 @@ class MangaReaderActivity : AppCompatActivity() {
val stopIntent = Intent(this, DiscordService::class.java).apply { val stopIntent = Intent(this, DiscordService::class.java).apply {
putExtra(DiscordService.ACTION_STOP_SERVICE, true) putExtra(DiscordService.ACTION_STOP_SERVICE, true)
} }
DiscordServiceRunningSingleton.running = false if (!isOnline(this)) { //TODO:
startService(stopIntent) DiscordServiceRunningSingleton.running = false
startService(stopIntent)
}
super.onDestroy() super.onDestroy()
} }
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
LangSet.setLocale(this) LangSet.setLocale(this)
ThemeManager(this).applyTheme() ThemeManager(this).applyTheme()
binding = ActivityMangaReaderBinding.inflate(layoutInflater) binding = ActivityMangaReaderBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
@ -148,8 +153,14 @@ ThemeManager(this).applyTheme()
progress { finish() } progress { finish() }
} }
settings = loadData("reader_settings", this) ?: ReaderSettings().apply { saveData("reader_settings", this) } settings = loadData("reader_settings", this)
uiSettings = loadData("ui_settings", this) ?: UserInterfaceSettings().apply { saveData("ui_settings", this) } ?: ReaderSettings().apply { saveData("reader_settings", this) }
uiSettings = loadData("ui_settings", this) ?: UserInterfaceSettings().apply {
saveData(
"ui_settings",
this
)
}
controllerDuration = (uiSettings.animationSpeed * 200).toLong() controllerDuration = (uiSettings.animationSpeed * 200).toLong()
hideBars() hideBars()
@ -174,9 +185,11 @@ ThemeManager(this).applyTheme()
if (fromUser) { if (fromUser) {
sliding = true sliding = true
if (settings.default.layout != PAGED) if (settings.default.layout != PAGED)
binding.mangaReaderRecycler.scrollToPosition((value.toInt() - 1) / (dualPage { 2 } ?: 1)) binding.mangaReaderRecycler.scrollToPosition((value.toInt() - 1) / (dualPage { 2 }
?: 1))
else else
binding.mangaReaderPager.currentItem = (value.toInt() - 1) / (dualPage { 2 } ?: 1) binding.mangaReaderPager.currentItem =
(value.toInt() - 1) / (dualPage { 2 } ?: 1)
pageSliderHide() pageSliderHide()
} }
} }
@ -202,25 +215,25 @@ ThemeManager(this).applyTheme()
model.mangaReadSources = if (media.isAdult) HMangaSources else MangaSources model.mangaReadSources = if (media.isAdult) HMangaSources else MangaSources
binding.mangaReaderSource.visibility = if (settings.showSource) View.VISIBLE else View.GONE binding.mangaReaderSource.visibility = if (settings.showSource) View.VISIBLE else View.GONE
if(model.mangaReadSources!!.names.isEmpty()){ if (model.mangaReadSources!!.names.isEmpty()) {
//try to reload sources //try to reload sources
try { try {
if (media.isAdult) { if (media.isAdult) {
val mangaSources = MangaSources val mangaSources = MangaSources
val scope = lifecycleScope val scope = lifecycleScope
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
mangaSources.init(Injekt.get<MangaExtensionManager>().installedExtensionsFlow) mangaSources.init(Injekt.get<MangaExtensionManager>().installedExtensionsFlow)
}
model.mangaReadSources = mangaSources
} else {
val mangaSources = HMangaSources
val scope = lifecycleScope
scope.launch(Dispatchers.IO) {
mangaSources.init(Injekt.get<MangaExtensionManager>().installedExtensionsFlow)
}
model.mangaReadSources = mangaSources
} }
model.mangaReadSources = mangaSources } catch (e: Exception) {
}else{
val mangaSources = HMangaSources
val scope = lifecycleScope
scope.launch(Dispatchers.IO) {
mangaSources.init(Injekt.get<MangaExtensionManager>().installedExtensionsFlow)
}
model.mangaReadSources = mangaSources
}
}catch (e: Exception){
Firebase.crashlytics.recordException(e) Firebase.crashlytics.recordException(e)
logError(e) logError(e)
} }
@ -229,7 +242,8 @@ ThemeManager(this).applyTheme()
if (media.selected!!.sourceIndex >= model.mangaReadSources!!.names.size) { if (media.selected!!.sourceIndex >= model.mangaReadSources!!.names.size) {
media.selected!!.sourceIndex = 0 media.selected!!.sourceIndex = 0
} }
binding.mangaReaderSource.text = model.mangaReadSources!!.names[media.selected!!.sourceIndex] binding.mangaReaderSource.text =
model.mangaReadSources!!.names[media.selected!!.sourceIndex]
binding.mangaReaderTitle.text = media.userPreferredName binding.mangaReaderTitle.text = media.userPreferredName
@ -242,10 +256,12 @@ ThemeManager(this).applyTheme()
chaptersTitleArr.add("${if (!chapter.title.isNullOrEmpty() && chapter.title != "null") "" else "Chapter "}${chapter.number}${if (!chapter.title.isNullOrEmpty() && chapter.title != "null") " : " + chapter.title else ""}") chaptersTitleArr.add("${if (!chapter.title.isNullOrEmpty() && chapter.title != "null") "" else "Chapter "}${chapter.number}${if (!chapter.title.isNullOrEmpty() && chapter.title != "null") " : " + chapter.title else ""}")
} }
showProgressDialog = if (settings.askIndividual) loadData<Boolean>("${media.id}_progressDialog") != true else false showProgressDialog =
if (settings.askIndividual) loadData<Boolean>("${media.id}_progressDialog") != true else false
progressDialog = progressDialog =
if (showProgressDialog && Anilist.userid != null && if (media.isAdult) settings.updateForH else true) if (showProgressDialog && Anilist.userid != null && if (media.isAdult) settings.updateForH else true)
AlertDialog.Builder(this, R.style.MyPopup).setTitle(getString(R.string.title_update_progress)).apply { AlertDialog.Builder(this, R.style.MyPopup)
.setTitle(getString(R.string.title_update_progress)).apply {
setMultiChoiceItems( setMultiChoiceItems(
arrayOf(getString(R.string.dont_ask_again, media.userPreferredName)), arrayOf(getString(R.string.dont_ask_again, media.userPreferredName)),
booleanArrayOf(false) booleanArrayOf(false)
@ -262,19 +278,27 @@ ThemeManager(this).applyTheme()
fun change(index: Int) { fun change(index: Int) {
mangaCache.clear() mangaCache.clear()
saveData("${media.id}_${chaptersArr[currentChapterIndex]}", currentChapterPage, this) saveData("${media.id}_${chaptersArr[currentChapterIndex]}", currentChapterPage, this)
ChapterLoaderDialog.newInstance(chapters[chaptersArr[index]]!!).show(supportFragmentManager, "dialog") ChapterLoaderDialog.newInstance(chapters[chaptersArr[index]]!!)
.show(supportFragmentManager, "dialog")
} }
//ChapterSelector //ChapterSelector
binding.mangaReaderChapterSelect.adapter = NoPaddingArrayAdapter(this, R.layout.item_dropdown, chaptersTitleArr) binding.mangaReaderChapterSelect.adapter =
NoPaddingArrayAdapter(this, R.layout.item_dropdown, chaptersTitleArr)
binding.mangaReaderChapterSelect.setSelection(currentChapterIndex) binding.mangaReaderChapterSelect.setSelection(currentChapterIndex)
binding.mangaReaderChapterSelect.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { binding.mangaReaderChapterSelect.onItemSelectedListener =
override fun onItemSelected(p0: AdapterView<*>?, p1: View?, position: Int, p3: Long) { object : AdapterView.OnItemSelectedListener {
if (position != currentChapterIndex) change(position) override fun onItemSelected(
} p0: AdapterView<*>?,
p1: View?,
position: Int,
p3: Long
) {
if (position != currentChapterIndex) change(position)
}
override fun onNothingSelected(parent: AdapterView<*>) {} override fun onNothingSelected(parent: AdapterView<*>) {}
} }
binding.mangaReaderSettings.setSafeOnClickListener { binding.mangaReaderSettings.setSafeOnClickListener {
ReaderSettingsDialogFragment.newInstance().show(supportFragmentManager, "settings") ReaderSettingsDialogFragment.newInstance().show(supportFragmentManager, "settings")
@ -305,57 +329,69 @@ ThemeManager(this).applyTheme()
saveData("${media.id}_current_chp", chap.number, this) saveData("${media.id}_current_chp", chap.number, this)
currentChapterIndex = chaptersArr.indexOf(chap.number) currentChapterIndex = chaptersArr.indexOf(chap.number)
binding.mangaReaderChapterSelect.setSelection(currentChapterIndex) binding.mangaReaderChapterSelect.setSelection(currentChapterIndex)
binding.mangaReaderNextChap.text = chaptersTitleArr.getOrNull(currentChapterIndex + 1) ?: "" binding.mangaReaderNextChap.text =
binding.mangaReaderPrevChap.text = chaptersTitleArr.getOrNull(currentChapterIndex - 1) ?: "" chaptersTitleArr.getOrNull(currentChapterIndex + 1) ?: ""
binding.mangaReaderPrevChap.text =
chaptersTitleArr.getOrNull(currentChapterIndex - 1) ?: ""
applySettings() applySettings()
val context = this val context = this
lifecycleScope.launch { if (isOnline(context)) {
val presence = RPC.createPresence(RPC.Companion.RPCData( lifecycleScope.launch {
applicationId = Discord.application_Id, val presence = RPC.createPresence(
type = RPC.Type.WATCHING, RPC.Companion.RPCData(
activityName = media.userPreferredName, applicationId = Discord.application_Id,
details = chap.title?.takeIf { it.isNotEmpty() } ?: getString(R.string.chapter_num, chap.number), type = RPC.Type.WATCHING,
state = "${chap.number}/${media.manga?.totalChapters ?: "??"}", activityName = media.userPreferredName,
largeImage = media.cover?.let { cover -> details = chap.title?.takeIf { it.isNotEmpty() }
RPC.Link(media.userPreferredName, cover) ?: getString(R.string.chapter_num, chap.number),
}, state = "${chap.number}/${media.manga?.totalChapters ?: "??"}",
smallImage = RPC.Link( largeImage = media.cover?.let { cover ->
"Dantotsu", RPC.Link(media.userPreferredName, cover)
Discord.small_Image },
), smallImage = RPC.Link(
buttons = mutableListOf( "Dantotsu",
RPC.Link(getString(R.string.view_manga), media.shareLink ?: ""), Discord.small_Image
RPC.Link( ),
"Stream on Dantotsu", buttons = mutableListOf(
"https://github.com/rebelonion/Dantotsu/" RPC.Link(getString(R.string.view_manga), media.shareLink ?: ""),
RPC.Link(
"Stream on Dantotsu",
"https://github.com/rebelonion/Dantotsu/"
)
)
) )
) )
) val intent = Intent(context, DiscordService::class.java).apply {
) putExtra("presence", presence)
}
val intent = Intent(context, DiscordService::class.java).apply { DiscordServiceRunningSingleton.running = true
putExtra("presence", presence) startService(intent)
} }
DiscordServiceRunningSingleton.running = true
startService(intent)
} }
} }
} }
scope.launch(Dispatchers.IO) { model.loadMangaChapterImages(chapter, media.selected!!, media.nameMAL?:media.nameRomaji) } scope.launch(Dispatchers.IO) {
model.loadMangaChapterImages(
chapter,
media.selected!!,
media.nameMAL ?: media.nameRomaji
)
}
} }
private val snapHelper = PagerSnapHelper() private val snapHelper = PagerSnapHelper()
fun <T> dualPage(callback: () -> T): T? { fun <T> dualPage(callback: () -> T): T? {
return when (settings.default.dualPageMode) { return when (settings.default.dualPageMode) {
No -> null No -> null
Automatic -> { Automatic -> {
val orientation = resources.configuration.orientation val orientation = resources.configuration.orientation
if (orientation == Configuration.ORIENTATION_LANDSCAPE) callback.invoke() if (orientation == Configuration.ORIENTATION_LANDSCAPE) callback.invoke()
else null else null
} }
Force -> callback.invoke()
Force -> callback.invoke()
} }
} }
@ -386,7 +422,8 @@ ThemeManager(this).applyTheme()
maxChapterPage = chapImages.size.toLong() maxChapterPage = chapImages.size.toLong()
saveData("${media.id}_${chapter.number}_max", maxChapterPage) saveData("${media.id}_${chapter.number}_max", maxChapterPage)
imageAdapter = dualPage { DualPageAdapter(this, chapter) } ?: ImageAdapter(this, chapter) imageAdapter =
dualPage { DualPageAdapter(this, chapter) } ?: ImageAdapter(this, chapter)
if (chapImages.size > 1) { if (chapImages.size > 1) {
binding.mangaReaderSlider.apply { binding.mangaReaderSlider.apply {
@ -407,8 +444,10 @@ ThemeManager(this).applyTheme()
if ((settings.default.direction == TOP_TO_BOTTOM || settings.default.direction == BOTTOM_TO_TOP)) { if ((settings.default.direction == TOP_TO_BOTTOM || settings.default.direction == BOTTOM_TO_TOP)) {
binding.mangaReaderSwipy.vertical = true binding.mangaReaderSwipy.vertical = true
if (settings.default.direction == TOP_TO_BOTTOM) { if (settings.default.direction == TOP_TO_BOTTOM) {
binding.BottomSwipeText.text = chaptersTitleArr.getOrNull(currentChapterIndex + 1) ?: getString(R.string.no_chapter) binding.BottomSwipeText.text = chaptersTitleArr.getOrNull(currentChapterIndex + 1)
binding.TopSwipeText.text = chaptersTitleArr.getOrNull(currentChapterIndex - 1) ?: getString(R.string.no_chapter) ?: getString(R.string.no_chapter)
binding.TopSwipeText.text = chaptersTitleArr.getOrNull(currentChapterIndex - 1)
?: getString(R.string.no_chapter)
binding.mangaReaderSwipy.onTopSwiped = { binding.mangaReaderSwipy.onTopSwiped = {
binding.mangaReaderPreviousChapter.performClick() binding.mangaReaderPreviousChapter.performClick()
} }
@ -416,8 +455,10 @@ ThemeManager(this).applyTheme()
binding.mangaReaderNextChapter.performClick() binding.mangaReaderNextChapter.performClick()
} }
} else { } else {
binding.BottomSwipeText.text = chaptersTitleArr.getOrNull(currentChapterIndex - 1) ?: getString(R.string.no_chapter) binding.BottomSwipeText.text = chaptersTitleArr.getOrNull(currentChapterIndex - 1)
binding.TopSwipeText.text = chaptersTitleArr.getOrNull(currentChapterIndex + 1) ?: getString(R.string.no_chapter) ?: getString(R.string.no_chapter)
binding.TopSwipeText.text = chaptersTitleArr.getOrNull(currentChapterIndex + 1)
?: getString(R.string.no_chapter)
binding.mangaReaderSwipy.onTopSwiped = { binding.mangaReaderSwipy.onTopSwiped = {
binding.mangaReaderNextChapter.performClick() binding.mangaReaderNextChapter.performClick()
} }
@ -440,8 +481,10 @@ ThemeManager(this).applyTheme()
} else { } else {
binding.mangaReaderSwipy.vertical = false binding.mangaReaderSwipy.vertical = false
if (settings.default.direction == RIGHT_TO_LEFT) { if (settings.default.direction == RIGHT_TO_LEFT) {
binding.LeftSwipeText.text = chaptersTitleArr.getOrNull(currentChapterIndex + 1) ?: getString(R.string.no_chapter) binding.LeftSwipeText.text = chaptersTitleArr.getOrNull(currentChapterIndex + 1)
binding.RightSwipeText.text = chaptersTitleArr.getOrNull(currentChapterIndex - 1) ?: getString(R.string.no_chapter) ?: getString(R.string.no_chapter)
binding.RightSwipeText.text = chaptersTitleArr.getOrNull(currentChapterIndex - 1)
?: getString(R.string.no_chapter)
binding.mangaReaderSwipy.onLeftSwiped = { binding.mangaReaderSwipy.onLeftSwiped = {
binding.mangaReaderNextChapter.performClick() binding.mangaReaderNextChapter.performClick()
} }
@ -449,8 +492,10 @@ ThemeManager(this).applyTheme()
binding.mangaReaderPreviousChapter.performClick() binding.mangaReaderPreviousChapter.performClick()
} }
} else { } else {
binding.LeftSwipeText.text = chaptersTitleArr.getOrNull(currentChapterIndex - 1) ?: getString(R.string.no_chapter) binding.LeftSwipeText.text = chaptersTitleArr.getOrNull(currentChapterIndex - 1)
binding.RightSwipeText.text = chaptersTitleArr.getOrNull(currentChapterIndex + 1) ?: getString(R.string.no_chapter) ?: getString(R.string.no_chapter)
binding.RightSwipeText.text = chaptersTitleArr.getOrNull(currentChapterIndex + 1)
?: getString(R.string.no_chapter)
binding.mangaReaderSwipy.onLeftSwiped = { binding.mangaReaderSwipy.onLeftSwiped = {
binding.mangaReaderPreviousChapter.performClick() binding.mangaReaderPreviousChapter.performClick()
} }
@ -475,7 +520,8 @@ ThemeManager(this).applyTheme()
if (settings.default.layout != PAGED) { if (settings.default.layout != PAGED) {
binding.mangaReaderRecyclerContainer.visibility = View.VISIBLE binding.mangaReaderRecyclerContainer.visibility = View.VISIBLE
binding.mangaReaderRecyclerContainer.controller.settings.isRotationEnabled = settings.default.rotation binding.mangaReaderRecyclerContainer.controller.settings.isRotationEnabled =
settings.default.rotation
val detector = GestureDetectorCompat(this, object : GesturesListener() { val detector = GestureDetectorCompat(this, object : GesturesListener() {
override fun onLongPress(e: MotionEvent) { override fun onLongPress(e: MotionEvent) {
@ -483,18 +529,31 @@ ThemeManager(this).applyTheme()
child ?: return@let false child ?: return@let false
val pos = binding.mangaReaderRecycler.getChildAdapterPosition(child) val pos = binding.mangaReaderRecycler.getChildAdapterPosition(child)
val callback: (ImageViewDialog) -> Unit = { dialog -> val callback: (ImageViewDialog) -> Unit = { dialog ->
lifecycleScope.launch { imageAdapter?.loadImage(pos, child as GestureFrameLayout) } lifecycleScope.launch {
binding.mangaReaderRecycler.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS) imageAdapter?.loadImage(
pos,
child as GestureFrameLayout
)
}
binding.mangaReaderRecycler.performHapticFeedback(
HapticFeedbackConstants.LONG_PRESS
)
dialog.dismiss() dialog.dismiss()
} }
dualPage { dualPage {
val page = chapter.dualPages().getOrNull(pos) ?: return@dualPage false val page =
chapter.dualPages().getOrNull(pos) ?: return@dualPage false
val nextPage = page.second val nextPage = page.second
if (settings.default.direction != LEFT_TO_RIGHT && nextPage != null) if (settings.default.direction != LEFT_TO_RIGHT && nextPage != null)
onImageLongClicked(pos * 2, nextPage, page.first, callback) onImageLongClicked(pos * 2, nextPage, page.first, callback)
else else
onImageLongClicked(pos * 2, page.first, nextPage, callback) onImageLongClicked(pos * 2, page.first, nextPage, callback)
} ?: onImageLongClicked(pos, chapImages.getOrNull(pos) ?: return@let false, null, callback) } ?: onImageLongClicked(
pos,
chapImages.getOrNull(pos) ?: return@let false,
null,
callback
)
} }
) binding.mangaReaderRecycler.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS) ) binding.mangaReaderRecycler.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS)
super.onLongPress(e) super.onLongPress(e)
@ -536,12 +595,16 @@ ThemeManager(this).applyTheme()
&& (!v.canScrollVertically(-1) || !v.canScrollVertically(1))) && (!v.canScrollVertically(-1) || !v.canScrollVertically(1)))
|| ||
((direction == LEFT_TO_RIGHT || direction == RIGHT_TO_LEFT) ((direction == LEFT_TO_RIGHT || direction == RIGHT_TO_LEFT)
&& (!v.canScrollHorizontally(-1) || !v.canScrollHorizontally(1))) && (!v.canScrollHorizontally(-1) || !v.canScrollHorizontally(
1
)))
) { ) {
handleController(true) handleController(true)
} else handleController(false) } else handleController(false)
} }
updatePageNumber(manager.findLastVisibleItemPosition().toLong() * (dualPage { 2 } ?: 1) + 1) updatePageNumber(
manager.findLastVisibleItemPosition().toLong() * (dualPage { 2 }
?: 1) + 1)
super.onScrolled(v, dx, dy) super.onScrolled(v, dx, dy)
} }
}) })
@ -603,7 +666,7 @@ ThemeManager(this).applyTheme()
private var onVolumeDown: (() -> Unit)? = null private var onVolumeDown: (() -> Unit)? = null
override fun dispatchKeyEvent(event: KeyEvent): Boolean { override fun dispatchKeyEvent(event: KeyEvent): Boolean {
return when (event.keyCode) { return when (event.keyCode) {
KEYCODE_VOLUME_UP, KEYCODE_DPAD_UP, KEYCODE_PAGE_UP -> { KEYCODE_VOLUME_UP, KEYCODE_DPAD_UP, KEYCODE_PAGE_UP -> {
if (event.keyCode == KEYCODE_VOLUME_UP) if (event.keyCode == KEYCODE_VOLUME_UP)
if (!settings.default.volumeButtons) if (!settings.default.volumeButtons)
return false return false
@ -612,6 +675,7 @@ ThemeManager(this).applyTheme()
true true
} else false } else false
} }
KEYCODE_VOLUME_DOWN, KEYCODE_DPAD_DOWN, KEYCODE_PAGE_DOWN -> { KEYCODE_VOLUME_DOWN, KEYCODE_DPAD_DOWN, KEYCODE_PAGE_DOWN -> {
if (event.keyCode == KEYCODE_VOLUME_DOWN) if (event.keyCode == KEYCODE_VOLUME_DOWN)
if (!settings.default.volumeButtons) if (!settings.default.volumeButtons)
@ -621,7 +685,8 @@ ThemeManager(this).applyTheme()
true true
} else false } else false
} }
else -> {
else -> {
super.dispatchKeyEvent(event) super.dispatchKeyEvent(event)
} }
} }
@ -695,8 +760,14 @@ ThemeManager(this).applyTheme()
isContVisible = false isContVisible = false
if (!isAnimating) { if (!isAnimating) {
isAnimating = true isAnimating = true
ObjectAnimator.ofFloat(binding.mangaReaderCont, "alpha", 1f, 0f).setDuration(controllerDuration).start() ObjectAnimator.ofFloat(binding.mangaReaderCont, "alpha", 1f, 0f)
ObjectAnimator.ofFloat(binding.mangaReaderBottomLayout, "translationY", 0f, 128f) .setDuration(controllerDuration).start()
ObjectAnimator.ofFloat(
binding.mangaReaderBottomLayout,
"translationY",
0f,
128f
)
.apply { interpolator = overshoot;duration = controllerDuration;start() } .apply { interpolator = overshoot;duration = controllerDuration;start() }
ObjectAnimator.ofFloat(binding.mangaReaderTopLayout, "translationY", 0f, -128f) ObjectAnimator.ofFloat(binding.mangaReaderTopLayout, "translationY", 0f, -128f)
.apply { interpolator = overshoot;duration = controllerDuration;start() } .apply { interpolator = overshoot;duration = controllerDuration;start() }
@ -705,7 +776,8 @@ ThemeManager(this).applyTheme()
} else { } else {
isContVisible = true isContVisible = true
binding.mangaReaderCont.visibility = View.VISIBLE binding.mangaReaderCont.visibility = View.VISIBLE
ObjectAnimator.ofFloat(binding.mangaReaderCont, "alpha", 0f, 1f).setDuration(controllerDuration).start() ObjectAnimator.ofFloat(binding.mangaReaderCont, "alpha", 0f, 1f)
.setDuration(controllerDuration).start()
ObjectAnimator.ofFloat(binding.mangaReaderTopLayout, "translationY", -128f, 0f) ObjectAnimator.ofFloat(binding.mangaReaderTopLayout, "translationY", -128f, 0f)
.apply { interpolator = overshoot;duration = controllerDuration;start() } .apply { interpolator = overshoot;duration = controllerDuration;start() }
ObjectAnimator.ofFloat(binding.mangaReaderBottomLayout, "translationY", 128f, 0f) ObjectAnimator.ofFloat(binding.mangaReaderBottomLayout, "translationY", 128f, 0f)
@ -731,7 +803,7 @@ ThemeManager(this).applyTheme()
model.loadMangaChapterImages( model.loadMangaChapterImages(
chapters[chaptersArr.getOrNull(currentChapterIndex + 1) ?: return@launch]!!, chapters[chaptersArr.getOrNull(currentChapterIndex + 1) ?: return@launch]!!,
media.selected!!, media.selected!!,
media.nameMAL?:media.nameRomaji, media.nameMAL ?: media.nameRomaji,
false false
) )
loading = false loading = false
@ -744,7 +816,11 @@ ThemeManager(this).applyTheme()
progressDialog?.setCancelable(false) progressDialog?.setCancelable(false)
?.setPositiveButton(getString(R.string.yes)) { dialog, _ -> ?.setPositiveButton(getString(R.string.yes)) { dialog, _ ->
saveData("${media.id}_save_progress", true) saveData("${media.id}_save_progress", true)
updateProgress(media, MangaNameAdapter.findChapterNumber(media.manga!!.selectedChapter!!).toString()) updateProgress(
media,
MangaNameAdapter.findChapterNumber(media.manga!!.selectedChapter!!)
.toString()
)
dialog.dismiss() dialog.dismiss()
runnable.run() runnable.run()
} }
@ -756,7 +832,11 @@ ThemeManager(this).applyTheme()
progressDialog?.show() progressDialog?.show()
} else { } else {
if (loadData<Boolean>("${media.id}_save_progress") != false && if (media.isAdult) settings.updateForH else true) if (loadData<Boolean>("${media.id}_save_progress") != false && if (media.isAdult) settings.updateForH else true)
updateProgress(media, MangaNameAdapter.findChapterNumber(media.manga!!.selectedChapter!!).toString()) updateProgress(
media,
MangaNameAdapter.findChapterNumber(media.manga!!.selectedChapter!!)
.toString()
)
runnable.run() runnable.run()
} }
} else { } else {

View file

@ -24,6 +24,7 @@ import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import kotlinx.serialization.json.JsonArray import kotlinx.serialization.json.JsonArray
import kotlinx.serialization.json.decodeFromJsonElement import kotlinx.serialization.json.decodeFromJsonElement
import rx.android.BuildConfig
import java.io.File import java.io.File
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*

View file

@ -231,6 +231,15 @@ internal class NovelExtensionInstaller(private val context: Context) {
val destination = File(destinationPath) val destination = File(destinationPath)
destination.setWritable(true) destination.setWritable(true)
//delete the file if it already exists
if (destination.exists()) {
if (destination.delete()) {
Log.i("File Copy", "File deleted successfully.")
} else {
Log.e("File Copy", "Failed to delete file.")
}
}
var inputChannel: FileChannel? = null var inputChannel: FileChannel? = null
var outputChannel: FileChannel? = null var outputChannel: FileChannel? = null
try { try {

View file

@ -2,9 +2,13 @@ package ani.dantotsu.parsers.novel
import android.content.Context import android.content.Context
import android.content.pm.PackageInfo import android.content.pm.PackageInfo
import android.content.pm.PackageManager.GET_SIGNATURES
import android.content.pm.PackageManager.GET_SIGNING_CERTIFICATES
import android.os.Build
import android.util.Log import android.util.Log
import ani.dantotsu.logger import ani.dantotsu.logger
import ani.dantotsu.parsers.NovelInterface import ani.dantotsu.parsers.NovelInterface
import ani.dantotsu.snackString
import com.google.firebase.crashlytics.FirebaseCrashlytics import com.google.firebase.crashlytics.FirebaseCrashlytics
import dalvik.system.PathClassLoader import dalvik.system.PathClassLoader
import eu.kanade.tachiyomi.extension.anime.util.AnimeExtensionLoader import eu.kanade.tachiyomi.extension.anime.util.AnimeExtensionLoader
@ -60,9 +64,15 @@ internal object NovelExtensionLoader {
return loadExtension(context, File(path)) return loadExtension(context, File(path))
} }
@Suppress("DEPRECATION")
fun loadExtension(context: Context, file: File): NovelLoadResult { fun loadExtension(context: Context, file: File): NovelLoadResult {
val packageInfo = context.packageManager.getPackageArchiveInfo(file.absolutePath, 0) val packageInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
?: return NovelLoadResult.Error(Exception("Failed to load extension")) context.packageManager.getPackageArchiveInfo(file.absolutePath, GET_SIGNING_CERTIFICATES)
?: return NovelLoadResult.Error(Exception("Failed to load extension"))
} else {
context.packageManager.getPackageArchiveInfo(file.absolutePath, GET_SIGNATURES)
?: return NovelLoadResult.Error(Exception("Failed to load extension"))
}
val appInfo = packageInfo.applicationInfo val appInfo = packageInfo.applicationInfo
?: return NovelLoadResult.Error(Exception("Failed to load Extension Info")) ?: return NovelLoadResult.Error(Exception("Failed to load Extension Info"))
appInfo.sourceDir = file.absolutePath; appInfo.sourceDir = file.absolutePath;
@ -70,10 +80,11 @@ internal object NovelExtensionLoader {
val signatureHash = getSignatureHash(packageInfo) val signatureHash = getSignatureHash(packageInfo)
if (signatureHash == null || signatureHash != officialSignature) { if ((signatureHash == null) || !signatureHash.contains(officialSignature)) {
logger("Package ${packageInfo.packageName} isn't signed") logger("Package ${packageInfo.packageName} isn't signed")
logger("signatureHash: $signatureHash") logger("signatureHash: $signatureHash")
//return NovelLoadResult.Error(Exception("Extension not signed")) snackString("Package ${packageInfo.packageName} isn't signed")
return NovelLoadResult.Error(Exception("Extension not signed"))
} }
val extension = NovelExtension.Installed( val extension = NovelExtension.Installed(
@ -92,10 +103,15 @@ internal object NovelExtensionLoader {
return NovelLoadResult.Success(extension) return NovelLoadResult.Success(extension)
} }
private fun getSignatureHash(pkgInfo: PackageInfo): String? { @Suppress("DEPRECATION")
val signatures = pkgInfo.signatures private fun getSignatureHash(pkgInfo: PackageInfo): List<String>? {
val signatures = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
pkgInfo.signingInfo.apkContentsSigners
} else {
pkgInfo.signatures
}
return if (signatures != null && signatures.isNotEmpty()) { return if (signatures != null && signatures.isNotEmpty()) {
Hash.sha256(signatures.first().toByteArray()) signatures.map { Hash.sha256(it.toByteArray()) }
} else { } else {
null null
} }

View file

@ -206,7 +206,7 @@ class NovelExtensionManager(private val context: Context) {
override fun onExtensionFileCreated(file: File) { override fun onExtensionFileCreated(file: File) {
NovelExtensionLoader.loadExtension(context, file).let { NovelExtensionLoader.loadExtension(context, file).let {
if (it is NovelLoadResult.Success) { if (it is NovelLoadResult.Success) {
registerNewExtension(it.extension) registerNewExtension(it.extension.withUpdateCheck())
} }
} }
} }
@ -217,7 +217,7 @@ class NovelExtensionManager(private val context: Context) {
override fun onExtensionFileModified(file: File) { override fun onExtensionFileModified(file: File) {
NovelExtensionLoader.loadExtension(context, file).let { NovelExtensionLoader.loadExtension(context, file).let {
if (it is NovelLoadResult.Success) { if (it is NovelLoadResult.Success) {
registerUpdatedExtension(it.extension) registerUpdatedExtension(it.extension.withUpdateCheck())
} }
} }
} }