mirror of
https://github.com/fumiama/copymanga.git
synced 2026-06-10 02:00:25 +08:00
2.0.beta2
1. 修复最多只能加载20话 2. 增加检查更新
This commit is contained in:
@@ -5,6 +5,7 @@
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
|
||||
@@ -34,7 +34,9 @@ import kotlinx.android.synthetic.main.app_bar_main.*
|
||||
import kotlinx.android.synthetic.main.nav_header_main.*
|
||||
import top.fumiama.dmzj.copymanga.R
|
||||
import top.fumiama.copymanga.tools.PropertiesTools
|
||||
import top.fumiama.copymanga.tools.UITools
|
||||
import top.fumiama.copymanga.ui.download.DownloadFragment
|
||||
import top.fumiama.copymanga.update.Update
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.lang.ref.WeakReference
|
||||
@@ -88,6 +90,7 @@ class MainActivity : AppCompatActivity() {
|
||||
override fun onDrawerSlide(drawerView: View, slideOffset: Float) {}
|
||||
override fun onDrawerStateChanged(newState: Int) {}
|
||||
})
|
||||
checkUpdate(false)
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu): Boolean {
|
||||
@@ -228,6 +231,11 @@ class MainActivity : AppCompatActivity() {
|
||||
)
|
||||
)
|
||||
|
||||
private fun checkUpdate(ignoreSkip: Boolean) {
|
||||
Thread{
|
||||
Update.checkUpdate(this, p, UITools(this), ignoreSkip)
|
||||
}.start()
|
||||
}
|
||||
|
||||
fun showAbout(item: MenuItem) {
|
||||
val dl = android.app.AlertDialog.Builder(this)
|
||||
@@ -235,6 +243,9 @@ class MainActivity : AppCompatActivity() {
|
||||
dl.setTitle(R.string.action_info)
|
||||
dl.setIcon(R.mipmap.ic_launcher)
|
||||
dl.setPositiveButton(android.R.string.ok) { _, _ -> }
|
||||
dl.setNeutralButton(R.string.check_update) {_, _ ->
|
||||
checkUpdate(true)
|
||||
}
|
||||
dl.show()
|
||||
}
|
||||
|
||||
|
||||
@@ -3,4 +3,5 @@ package top.fumiama.copymanga.json;
|
||||
public class ThemeStructure {
|
||||
public String name;
|
||||
public String path_word;
|
||||
public int count;
|
||||
}
|
||||
|
||||
@@ -15,4 +15,5 @@ object CMApi {
|
||||
fun getImgZipFileFromVM(exDir: File?, chapter2Return: Chapter2Return?) = File(exDir, "${chapter2Return?.results?.comic?.name}/${chapter2Return?.results?.chapter?.group_path_word}/${chapter2Return?.results?.chapter?.name}.zip")
|
||||
fun getZipFile(exDir: File?, manga: String, caption: CharSequence, name: CharSequence) = File(exDir, "$manga/$caption/$name.zip")
|
||||
fun getApiUrl(id: Int, arg1: String?, arg2: String?) = MainActivity.mainWeakReference?.get()?.getString(id)?.let { String.format(it, arg1, arg2) }
|
||||
fun getApiUrl(id: Int, arg1: String?, arg2: String?, arg3: Int? = 0) = MainActivity.mainWeakReference?.get()?.getString(id)?.let { String.format(it, arg1, arg2, arg3) }
|
||||
}
|
||||
@@ -1,52 +1,63 @@
|
||||
package top.fumiama.copymanga.tools
|
||||
//PropertiesTools.kt
|
||||
//created by fumiama 20200724
|
||||
import android.util.Log
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
import java.util.*
|
||||
|
||||
class PropertiesTools(private val f: File):Properties() {
|
||||
private val propfile:File
|
||||
get() {
|
||||
if(!f.exists()) {
|
||||
if(f.parentFile?.exists() != true) f.parentFile?.mkdirs()
|
||||
if(f.parentFile?.canWrite() != true) f.parentFile?.setWritable(true)
|
||||
createNew(f)
|
||||
}else if(f.isDirectory) {
|
||||
if(f.parentFile?.canWrite() != true) f.parentFile?.setWritable(true)
|
||||
f.delete()
|
||||
createNew(f)
|
||||
}
|
||||
private var cache = hashMapOf<String, String>()
|
||||
|
||||
init {
|
||||
if(!f.exists()) {
|
||||
if(f.parentFile?.exists() != true) f.parentFile?.mkdirs()
|
||||
if(f.parentFile?.canWrite() != true) f.parentFile?.setWritable(true)
|
||||
if(f.parentFile?.canRead() != true) f.parentFile?.setReadable(true)
|
||||
return f
|
||||
createNew(f)
|
||||
} else if(f.isDirectory) {
|
||||
if(f.parentFile?.canWrite() != true) f.parentFile?.setWritable(true)
|
||||
f.delete()
|
||||
createNew(f)
|
||||
}
|
||||
private fun createNew(f: File){
|
||||
if(f.parentFile?.canWrite() != true) f.parentFile?.setWritable(true)
|
||||
if(f.parentFile?.canRead() != true) f.parentFile?.setReadable(true)
|
||||
}
|
||||
|
||||
private fun createNew(f: File) {
|
||||
f.createNewFile()
|
||||
val o = f.outputStream()
|
||||
this.storeToXML(o, "store")
|
||||
//Log.d("MyPT", "Generate new prop.")
|
||||
Log.d("MyPT", "Generate new prop.")
|
||||
o.close()
|
||||
}
|
||||
|
||||
private fun loadFromXml(`in`: InputStream?): PropertiesTools {
|
||||
this.loadFromXML(`in`)
|
||||
return this
|
||||
}
|
||||
|
||||
private fun setProp(key: String?, value: String?): PropertiesTools {
|
||||
this.setProperty(key, value)
|
||||
return this
|
||||
}
|
||||
|
||||
operator fun get(key: String): String{
|
||||
val i = propfile.inputStream()
|
||||
val re = this.loadFromXml(i).getProperty(key)?:"null"
|
||||
//Log.d("MyPT", "Get prop: $re")
|
||||
i.close()
|
||||
return re
|
||||
return if(cache.containsKey(key)) cache[key]?:"null"
|
||||
else {
|
||||
val i = f.inputStream()
|
||||
val re = this.loadFromXml(i).getProperty(key)?:"null"
|
||||
Log.d("MyPT", "Read $key = $re")
|
||||
i.close()
|
||||
cache[key] = re
|
||||
re
|
||||
}
|
||||
}
|
||||
operator fun set(key: String, value: String){
|
||||
val o = propfile.outputStream()
|
||||
|
||||
operator fun set(key: String, value: String) {
|
||||
cache[key] = value
|
||||
val o = f.outputStream()
|
||||
this.setProp(key, value).storeToXML(o, "store")
|
||||
//Log.d("MyPT", "Set $key = $value")
|
||||
Log.d("MyPT", "Set $key = $value")
|
||||
o.close()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import android.app.AlertDialog
|
||||
import android.content.Context
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.NetworkCapabilities
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import top.fumiama.dmzj.copymanga.R
|
||||
import java.lang.ref.WeakReference
|
||||
@@ -53,6 +54,26 @@ class UITools(that: Context?, w: WeakReference<Activity>? = null) {
|
||||
txtN?.let { info.setNeutralButton(it) { _, _ -> neutral?.let { it() } } }
|
||||
info.show()
|
||||
}
|
||||
fun buildAlertWithView(
|
||||
title: String,
|
||||
view: View,
|
||||
txtOk: String? = null,
|
||||
txtN: String? = null,
|
||||
txtCancel: String? = null,
|
||||
ok: (() -> Unit)? = null,
|
||||
neutral: (() -> Unit)? = null,
|
||||
cancel: (() -> Unit)? = null
|
||||
): AlertDialog {
|
||||
val info = AlertDialog.Builder(zis)
|
||||
info.setIcon(R.drawable.ic_launcher_foreground)
|
||||
info.setTitle(title)
|
||||
|
||||
info.setView(view)
|
||||
txtOk?.let { info.setPositiveButton(it) { _, _ -> ok?.let { it() } } }
|
||||
txtCancel?.let { info.setNegativeButton(it) { _, _ -> cancel?.let { it() } } }
|
||||
txtN?.let { info.setNeutralButton(it) { _, _ -> neutral?.let { it() } } }
|
||||
return info.show()
|
||||
}
|
||||
fun dp2px(dp:Int):Int?{
|
||||
return zis?.resources?.displayMetrics?.density?.let { (dp * it + 0.5).toInt()}
|
||||
}
|
||||
@@ -85,4 +106,14 @@ class UITools(that: Context?, w: WeakReference<Activity>? = null) {
|
||||
val totalWidth = ((zis?.resources?.displayMetrics?.widthPixels?:1080)-marginPx)/numPerRow
|
||||
return listOf(numPerRow, w, totalWidth)
|
||||
}
|
||||
fun toHexStr(byteArray: ByteArray) =
|
||||
with(StringBuilder()) {
|
||||
byteArray.forEach {
|
||||
val hex = it.toInt() and (0xFF)
|
||||
val hexStr = Integer.toHexString(hex)
|
||||
if (hexStr.length == 1) append("0").append(hexStr)
|
||||
else append(hexStr)
|
||||
}
|
||||
toString()
|
||||
}
|
||||
}
|
||||
@@ -61,12 +61,16 @@ class BookFragment:NoBackRefreshFragment(R.layout.fragment_book) {
|
||||
val groups = bookHandler.book?.results?.groups
|
||||
var keys = arrayOf<String>()
|
||||
var gpws = arrayOf<String>()
|
||||
var cnts = intArrayOf()
|
||||
groups?.values?.forEach {
|
||||
keys += it.name
|
||||
gpws += it.path_word
|
||||
cnts += it.count
|
||||
Log.d("MyBF", "Add caption: ${it.name} @ ${it.path_word} of ${it.count}")
|
||||
}
|
||||
bundle.putStringArray("group", gpws)
|
||||
bundle.putStringArray("groupNames", keys)
|
||||
bundle.putIntArray("count", cnts)
|
||||
rootView?.let { Navigation.findNavController(it).navigate(R.id.action_nav_book_to_nav_group, bundle) }
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ import android.view.View
|
||||
import com.google.gson.Gson
|
||||
import top.fumiama.dmzj.copymanga.R
|
||||
import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
|
||||
import top.fumiama.copymanga.json.ChapterStructure
|
||||
import top.fumiama.copymanga.json.VolumeStructure
|
||||
import top.fumiama.copymanga.template.AutoDownloadThread
|
||||
import top.fumiama.copymanga.template.NoBackRefreshFragment
|
||||
@@ -32,7 +33,8 @@ class ComicDlFragment:NoBackRefreshFragment(R.layout.fragment_dlcomic) {
|
||||
}
|
||||
else -> initComicData(
|
||||
arguments?.getString("path"),
|
||||
arguments?.getStringArray("group")
|
||||
arguments?.getStringArray("group"),
|
||||
arguments?.getIntArray("count")
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -44,7 +46,7 @@ class ComicDlFragment:NoBackRefreshFragment(R.layout.fragment_dlcomic) {
|
||||
mainWeakReference?.get()?.menuMain?.let { setMenuInvisible(it) }
|
||||
}*/
|
||||
|
||||
fun start2load(volumes: Array<VolumeStructure>, isFromFile: Boolean = false, groupArray: Array<String>? =null){
|
||||
private fun start2load(volumes: Array<VolumeStructure>, isFromFile: Boolean = false, groupArray: Array<String>? =null){
|
||||
handler = ComicDlHandler(Looper.myLooper()!!,
|
||||
WeakReference(this),
|
||||
volumes,
|
||||
@@ -85,21 +87,42 @@ class ComicDlFragment:NoBackRefreshFragment(R.layout.fragment_dlcomic) {
|
||||
menu.findItem(R.id.action_download)?.isVisible = false
|
||||
}*/
|
||||
|
||||
private fun initComicData(pw: String?, gpws: Array<String>?) {
|
||||
var volumes = arrayOf<VolumeStructure>()
|
||||
val waitHandler = WaitHandler(WeakReference(this))
|
||||
private fun initComicData(pw: String?, gpws: Array<String>?, counts: IntArray?) {
|
||||
var volumes = emptyArray<VolumeStructure>()
|
||||
if (gpws != null) {
|
||||
gpws.forEach { gpw ->
|
||||
gpws.forEachIndexed { i, gpw ->
|
||||
Log.d("MyCDF", "下载:$gpw")
|
||||
CMApi.getApiUrl(R.string.groupInfoApiUrl, pw, gpw)?.let {
|
||||
AutoDownloadThread(it) { result ->
|
||||
//Log.d("MyCDF", "返回:${result?.decodeToString()}")
|
||||
volumes += Gson().fromJson(
|
||||
result?.decodeToString(),
|
||||
VolumeStructure::class.java
|
||||
)
|
||||
}.start()
|
||||
}
|
||||
var offset = 0
|
||||
val re = arrayOfNulls<VolumeStructure>(counts?.get(i)?:1)
|
||||
do {
|
||||
counts?.set(i, counts[i] - 100)
|
||||
CMApi.getApiUrl(R.string.groupInfoApiUrl, pw, gpw, offset)?.let {
|
||||
AutoDownloadThread(it) { result ->
|
||||
//Log.d("MyCDF", "返回:${result?.decodeToString()}")
|
||||
val r = Gson().fromJson(result?.decodeToString(), VolumeStructure::class.java)
|
||||
re[r.results.offset / 100] = r
|
||||
}.start()
|
||||
offset += 100
|
||||
}
|
||||
} while ((counts?.get(i) ?: 0) > 0)
|
||||
Thread {
|
||||
var c = 0
|
||||
while (c++ < 80) {
|
||||
sleep(100)
|
||||
if(re.all { it != null }) break
|
||||
}
|
||||
if(re.size > 1) {
|
||||
val r = re[0]
|
||||
var s = emptyArray<ChapterStructure>()
|
||||
re.forEach {
|
||||
it?.results?.list?.forEach {
|
||||
s += it
|
||||
}
|
||||
}
|
||||
r?.results?.list = s
|
||||
r?.apply { volumes += this }
|
||||
} else re[0]?.apply { volumes += this }
|
||||
}.start()
|
||||
}
|
||||
Thread {
|
||||
var c = 0
|
||||
@@ -109,7 +132,9 @@ class ComicDlFragment:NoBackRefreshFragment(R.layout.fragment_dlcomic) {
|
||||
c++
|
||||
}
|
||||
if (volumes.size == gpws.size) {
|
||||
waitHandler.obtainMessage(0, volumes).sendToTarget()
|
||||
mainWeakReference?.get()?.runOnUiThread {
|
||||
start2load(volumes)
|
||||
}
|
||||
}
|
||||
}.start()
|
||||
}
|
||||
@@ -122,15 +147,6 @@ class ComicDlFragment:NoBackRefreshFragment(R.layout.fragment_dlcomic) {
|
||||
handler?.startLoad()
|
||||
}
|
||||
|
||||
class WaitHandler(private val that: WeakReference<ComicDlFragment>): Handler(Looper.myLooper()!!){
|
||||
override fun handleMessage(msg: Message) {
|
||||
super.handleMessage(msg)
|
||||
when(msg.what){
|
||||
0 -> that.get()?.start2load(msg.obj as Array<VolumeStructure>)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
var json: String? = null
|
||||
}
|
||||
|
||||
139
app/src/main/java/top/fumiama/copymanga/update/Client.kt
Normal file
139
app/src/main/java/top/fumiama/copymanga/update/Client.kt
Normal file
@@ -0,0 +1,139 @@
|
||||
package top.fumiama.copymanga.update
|
||||
|
||||
import android.util.Log
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.io.OutputStream
|
||||
import java.net.Socket
|
||||
|
||||
class Client(private val ip: String, private val port: Int) {
|
||||
//普通数据交互接口
|
||||
private var sc: Socket? = null
|
||||
|
||||
//普通交互流
|
||||
private var dout: OutputStream? = null
|
||||
private var din: InputStream? = null
|
||||
|
||||
//已连接标记
|
||||
private val isConnect get() = sc != null && din != null && dout != null
|
||||
|
||||
/**
|
||||
* 初始化普通交互连接
|
||||
*/
|
||||
fun initConnect(depth: Int = 0): Boolean{
|
||||
if(depth > 3) Log.d("MyC", "connect server failed after $depth tries")
|
||||
else try {
|
||||
sc = Socket(ip, port) //通过socket连接服务器
|
||||
din = sc?.getInputStream() //获取输入流并转换为StreamReader,约定编码格式
|
||||
dout = sc?.getOutputStream() //获取输出流
|
||||
sc?.soTimeout = 2333 //设置连接超时限制
|
||||
return if (isConnect) {
|
||||
Log.d("MyC", "connect server successful")
|
||||
true
|
||||
} else {
|
||||
Log.d("MyC", "connect server failed, now retry...")
|
||||
initConnect(depth + 1)
|
||||
}
|
||||
} catch (e: IOException) { //获取输入输出流是可能报IOException的,所以必须try-catch
|
||||
e.printStackTrace()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送数据至服务器
|
||||
* @param message 要发送至服务器的字符串
|
||||
*/
|
||||
fun sendMessage(message: CharSequence?): Boolean {
|
||||
try {
|
||||
if (isConnect) {
|
||||
if (message != null) { //判断输出流或者消息是否为空,为空的话会产生nullpoint错误
|
||||
dout?.write(message.toString().toByteArray())
|
||||
dout?.flush()
|
||||
Log.d("MyC", "Send msg: $message")
|
||||
return true
|
||||
} else Log.d("MyC", "The message to be sent is empty")
|
||||
Log.d("MyC", "send message succeed")
|
||||
} else Log.d("MyC", "send message failed: no connect")
|
||||
} catch (e: IOException) {
|
||||
Log.d("MyC", "send message failed: crash")
|
||||
e.printStackTrace()
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
var buffer = byteArrayOf()
|
||||
|
||||
fun receiveRawMessage(totalSize: Int = -1, bufferSize: Int = 1048576, setProgress: Boolean = false) : ByteArray {
|
||||
if(totalSize == buffer.size) {
|
||||
val re = buffer
|
||||
buffer = byteArrayOf()
|
||||
return re
|
||||
} else {
|
||||
var re = byteArrayOf()
|
||||
try {
|
||||
if (isConnect) {
|
||||
Log.d("MyC", "开始接收服务端信息")
|
||||
val inMessage = ByteArray(bufferSize) //设置接受缓冲,避免接受数据过长占用过多内存
|
||||
var a: Int
|
||||
do {
|
||||
a = din?.read(inMessage)?:0 //a存储返回消息的长度
|
||||
if(a > 0) {
|
||||
re += inMessage.copyOf(a)
|
||||
Log.d("MyC", "reply length:$a")
|
||||
if(totalSize < 0 && a < bufferSize) break
|
||||
else if(setProgress && totalSize > 0) progress?.notify(100 * re.size / totalSize)
|
||||
} else break
|
||||
} while (totalSize > re.size)
|
||||
} else Log.d("MyC", "no connect to receive message")
|
||||
} catch (e: IOException) {
|
||||
Log.d("MyC", "receive message failed")
|
||||
e.printStackTrace()
|
||||
}
|
||||
if(totalSize > 0 && re.size > totalSize) {
|
||||
Log.d("MyC", "Reduce re size from ${re.size} to $totalSize")
|
||||
buffer += re.copyOfRange(totalSize, re.size)
|
||||
re = re.copyOf(totalSize)
|
||||
} else if(totalSize > 0 && buffer.isNotEmpty()) {
|
||||
Log.d("MyC", "Increase re size.")
|
||||
buffer += re
|
||||
if(buffer.size > totalSize) {
|
||||
re = buffer.copyOf(totalSize)
|
||||
buffer = buffer.copyOfRange(totalSize, buffer.size)
|
||||
} else {
|
||||
re = buffer
|
||||
buffer = byteArrayOf()
|
||||
}
|
||||
} else if(totalSize < 0 && buffer.isNotEmpty()) {
|
||||
re = buffer
|
||||
buffer = byteArrayOf()
|
||||
Log.d("MyC", "clear buffer")
|
||||
}
|
||||
return re
|
||||
}
|
||||
}
|
||||
|
||||
//fun receiveMessage() = receiveRawMessage().decodeToString()
|
||||
|
||||
/**
|
||||
* 关闭连接
|
||||
*/
|
||||
fun closeConnect() = try {
|
||||
din?.close()
|
||||
dout?.close()
|
||||
sc?.close()
|
||||
sc = null
|
||||
din = null
|
||||
dout = null
|
||||
true
|
||||
} catch (e: IOException) {
|
||||
e.printStackTrace()
|
||||
false
|
||||
}
|
||||
|
||||
var progress: Progress? = null
|
||||
|
||||
interface Progress {
|
||||
fun notify(progressPercentage: Int)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package top.fumiama.copymanga.update
|
||||
|
||||
import android.util.Log
|
||||
|
||||
class SimpleKanban(private val client: Client, private val pwd: String) { //must run in thread
|
||||
private val raw: ByteArray?
|
||||
get() {
|
||||
var times = 3
|
||||
var re: ByteArray
|
||||
var firstRecv: ByteArray
|
||||
do {
|
||||
re = byteArrayOf()
|
||||
if(client.initConnect()) {
|
||||
client.sendMessage("${pwd}catquit")
|
||||
client.receiveRawMessage(33) //Welcome to simple kanban server.
|
||||
try {
|
||||
firstRecv = client.receiveRawMessage(4) //le
|
||||
val length = convert2Int(firstRecv)
|
||||
if(firstRecv.size > 4) re += firstRecv.copyOfRange(4, firstRecv.size)
|
||||
re += client.receiveRawMessage(length - re.size, setProgress = true)
|
||||
break
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
client.closeConnect()
|
||||
}
|
||||
} while (times-- > 0)
|
||||
return if(re.isEmpty()) null else re
|
||||
}
|
||||
|
||||
private fun convert2Int(buffer: ByteArray) =
|
||||
(buffer[3].toInt() and 0xff shl 24) or
|
||||
(buffer[2].toInt() and 0xff shl 16) or
|
||||
(buffer[1].toInt() and 0xff shl 8) or
|
||||
(buffer[0].toInt() and 0xff)
|
||||
|
||||
fun fetchRaw(doOnLoadFailure: ()->Unit = {
|
||||
Log.d("MySD", "Fetch dict failed")
|
||||
}, doOnLoadSuccess: (data: ByteArray)->Unit = {
|
||||
Log.d("MySD", "Fetch dict success")
|
||||
}) {
|
||||
raw?.apply {
|
||||
doOnLoadSuccess(this)
|
||||
}?:doOnLoadFailure()
|
||||
}
|
||||
|
||||
operator fun get(version: Int): String =
|
||||
if(client.initConnect()) {
|
||||
client.sendMessage("${pwd}get${version}quit")
|
||||
client.receiveRawMessage(36) //Welcome to simple kanban server. get
|
||||
val r = try {
|
||||
val firstRecv = client.receiveRawMessage(4)
|
||||
if(firstRecv.decodeToString() == "null") "null"
|
||||
else {
|
||||
val length = convert2Int(firstRecv)
|
||||
var re = byteArrayOf()
|
||||
if(firstRecv.size > 4) re += firstRecv.copyOfRange(4, firstRecv.size)
|
||||
re += client.receiveRawMessage(length - re.size)
|
||||
if(re.isNotEmpty()) re.decodeToString() else "null"
|
||||
}
|
||||
} catch (e: Exception){
|
||||
e.printStackTrace()
|
||||
"null"
|
||||
}
|
||||
client.closeConnect()
|
||||
r
|
||||
} else "null"
|
||||
}
|
||||
87
app/src/main/java/top/fumiama/copymanga/update/Update.kt
Normal file
87
app/src/main/java/top/fumiama/copymanga/update/Update.kt
Normal file
@@ -0,0 +1,87 @@
|
||||
package top.fumiama.copymanga.update
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import android.widget.Toast
|
||||
import androidx.core.content.FileProvider
|
||||
import kotlinx.android.synthetic.main.dialog_progress.view.*
|
||||
import top.fumiama.copymanga.tools.PropertiesTools
|
||||
import top.fumiama.copymanga.tools.UITools
|
||||
import top.fumiama.dmzj.copymanga.R
|
||||
import java.io.File
|
||||
import java.security.MessageDigest
|
||||
|
||||
object Update {
|
||||
fun checkUpdate(activity: Activity, p: PropertiesTools, toolsBox: UITools, ignoreSkip: Boolean = false) = activity.apply{
|
||||
val client = Client("copymanga.v6.army", 12315)
|
||||
val progressBar = layoutInflater.inflate(R.layout.dialog_progress, null, false)
|
||||
val progressHandler = object : Client.Progress{
|
||||
override fun notify(progressPercentage: Int) {
|
||||
Log.d("MyUP", "Set progress: $progressPercentage")
|
||||
progressBar.dpp.progress = progressPercentage
|
||||
}
|
||||
}
|
||||
val kanban = SimpleKanban(client, "fumiama")
|
||||
val msg = kanban[packageManager.getPackageInfo(packageName, 0).versionCode]
|
||||
if(msg != "null") {
|
||||
val verNum = msg.substringBefore('\n').toIntOrNull()
|
||||
val skipNum = p["skipVersion"].let { if(it != "null") it.toInt() else 0 }
|
||||
|
||||
Log.d("MyUP", "Ver:$verNum, skip: $skipNum")
|
||||
if(verNum != null) {
|
||||
if(msg.contains("md5:")) {
|
||||
if(skipNum < verNum || ignoreSkip) runOnUiThread {
|
||||
toolsBox.buildInfo("看板", msg.substringAfter('\n').substringBeforeLast('\n'), "下载新版", "跳过该版", "取消", {
|
||||
val info = toolsBox.buildAlertWithView("下载进度", progressBar, "隐藏")
|
||||
client.progress = progressHandler
|
||||
Thread {
|
||||
kanban.fetchRaw({
|
||||
runOnUiThread {
|
||||
Toast.makeText(this, "下载失败", Toast.LENGTH_SHORT).show()
|
||||
client.progress = null
|
||||
}
|
||||
}) {
|
||||
val md5 = msg.substringAfterLast("md5:")
|
||||
if (md5 == toolsBox.toHexStr(
|
||||
MessageDigest.getInstance("MD5").digest(it)
|
||||
)
|
||||
) {
|
||||
runOnUiThread {
|
||||
Toast.makeText(this, "下载成功", Toast.LENGTH_SHORT).show()
|
||||
info.dismiss()
|
||||
}
|
||||
val f = File(externalCacheDir, "new.apk")
|
||||
f.writeBytes(it)
|
||||
install(f, activity)
|
||||
} else runOnUiThread {
|
||||
Toast.makeText(this, "文件损坏", Toast.LENGTH_SHORT).show()
|
||||
info.dismiss()
|
||||
}
|
||||
client.progress = null
|
||||
}
|
||||
}.start()
|
||||
}, { p["skipVersion"] = verNum.toString() })
|
||||
}
|
||||
} else runOnUiThread {
|
||||
toolsBox.buildInfo("看板", msg.substringAfter('\n'), "知道了")
|
||||
}
|
||||
}
|
||||
} else if(ignoreSkip) runOnUiThread {
|
||||
Toast.makeText(this, "无更新", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
private fun install(apkFile: File, activity: Activity) = activity.apply{
|
||||
val intent = Intent(Intent.ACTION_VIEW)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
||||
val contentUri: Uri = FileProvider.getUriForFile(this, "$packageName.fileprovider", apkFile)
|
||||
intent.setDataAndType(contentUri, "application/vnd.android.package-archive")
|
||||
} else intent.setDataAndType(Uri.fromFile(apkFile), "application/vnd.android.package-archive")
|
||||
startActivity(intent)
|
||||
}
|
||||
}
|
||||
42
app/src/main/res/layout/dialog_progress.xml
Normal file
42
app/src/main/res/layout/dialog_progress.xml
Normal file
@@ -0,0 +1,42 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="24dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="24dp"
|
||||
android:background="@drawable/rndbg_white"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_dere"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/dpp"
|
||||
style="?android:attr/progressBarStyleHorizontal"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="32dp"
|
||||
android:layout_marginEnd="32dp"
|
||||
android:progress="0"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
@@ -15,6 +15,7 @@
|
||||
<string name="page_book">图书详情</string>
|
||||
<string name="page_chapter">章节内容</string>
|
||||
<string name="page_group">漫画下载</string>
|
||||
<string name="check_update">检查更新</string>
|
||||
|
||||
<string name="navTextInfo">illust: Hiten(490219)</string>
|
||||
<string name="navTextInfoInputHint">请设定提示文字内容</string>
|
||||
@@ -34,7 +35,7 @@
|
||||
<string name="filterApiUrl">https://api.copymanga.com/api/v3/h5/filterIndex/comic/tags</string>
|
||||
<string name="sortApiUrl">https://api.copymanga.com/api/v3/comics?limit=21&offset=%1$d&ordering=%2$s&theme=%3$s</string>
|
||||
<string name="bookInfoApiUrl">https://api.copymanga.com/api/v3/comic2/%1$s</string>
|
||||
<string name="groupInfoApiUrl">https://api.copymanga.com/api/v3/comic/%1$s/group/%2$s/chapters</string>
|
||||
<string name="groupInfoApiUrl">https://api.copymanga.com/api/v3/comic/%1$s/group/%2$s/chapters?limit=100&offset=%3$d</string>
|
||||
<string name="chapterInfoApiUrl">https://api.copymanga.com/api/v3/comic/%1$s/chapter2/%2$s</string>
|
||||
<string name="chapterTxtUrl">https://nnv3api.dmzj1.com/novel/download/%1$d_%2$d_%3$d.txt</string>
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<paths>
|
||||
<external-files-path name="ef" path="."/>
|
||||
<files-path name="if" path="."/>
|
||||
<external-cache-path name="ec" path="."/>
|
||||
</paths>
|
||||
Reference in New Issue
Block a user