1
0
mirror of https://github.com/fumiama/copymanga.git synced 2026-06-05 07:20:23 +08:00
This commit is contained in:
index
2020-10-14 14:06:58 +08:00
parent 5731a10521
commit 5c9f45e272
70 changed files with 4262 additions and 54 deletions

View File

@@ -17,6 +17,7 @@
<package name="" alias="true" withSubpackages="true" />
</value>
</option>
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</JetCodeStyleSettings>
<codeStyleSettings language="XML">
<indentOptions>
@@ -130,5 +131,8 @@
</rules>
</arrangement>
</codeStyleSettings>
<codeStyleSettings language="kotlin">
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />
</codeStyleSettings>
</code_scheme>
</component>

5
.idea/codeStyles/codeStyleConfig.xml generated Normal file
View File

@@ -0,0 +1,5 @@
<component name="ProjectCodeStyleConfiguration">
<state>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</state>
</component>

7
.idea/gradle.xml generated
View File

@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GradleMigrationSettings" migrationVersion="1" />
<component name="GradleSettings">
<option name="linkedExternalProjectsSettings">
<GradleProjectSettings>
@@ -7,6 +8,12 @@
<option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleJvm" value="1.8" />
<option name="modules">
<set>
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
<option name="resolveModulePerSourceSet" value="false" />
</GradleProjectSettings>
</option>

25
.idea/jarRepositories.xml generated Normal file
View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RemoteRepositoriesConfiguration">
<remote-repository>
<option name="id" value="central" />
<option name="name" value="Maven Central repository" />
<option name="url" value="https://repo1.maven.org/maven2" />
</remote-repository>
<remote-repository>
<option name="id" value="jboss.community" />
<option name="name" value="JBoss Community repository" />
<option name="url" value="https://repository.jboss.org/nexus/content/repositories/public/" />
</remote-repository>
<remote-repository>
<option name="id" value="BintrayJCenter" />
<option name="name" value="BintrayJCenter" />
<option name="url" value="https://jcenter.bintray.com/" />
</remote-repository>
<remote-repository>
<option name="id" value="Google" />
<option name="name" value="Google" />
<option name="url" value="https://dl.google.com/dl/android/maven2/" />
</remote-repository>
</component>
</project>

2
.idea/vcs.xml generated
View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/dmzj" vcs="Git" />
<mapping directory="$PROJECT_DIR$" vcs="Git" />
</component>
</project>

View File

@@ -1,6 +1,7 @@
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'AndResGuard'
android {
compileSdkVersion 30
@@ -12,13 +13,32 @@ android {
targetSdkVersion 30
versionCode 1
versionName "1.0"
resConfigs "zh", "zh-rCN"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
signingConfigs {
release {
storeFile file('../../../OneDrive/swc/developer/android_key/open_key')
storePassword 'fumiama'
keyAlias 'default'
keyPassword 'fumiama'
v1SigningEnabled true
v2SigningEnabled true
}
}
buildTypes {
release {
minifyEnabled false
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
debug{
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
@@ -27,10 +47,64 @@ android {
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.1.0'
implementation 'androidx.appcompat:appcompat:1.1.0'
testImplementation 'junit:junit:4.12'
implementation 'androidx.core:core-ktx:1.3.2'
//implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.2'
implementation 'androidx.cardview:cardview:1.0.0'
implementation 'androidx.viewpager2:viewpager2:1.0.0'
//implementation 'com.google.android.material:material:1.2.1'
testImplementation 'junit:junit:4.13.1'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
implementation 'com.github.bumptech.glide:glide:4.11.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0'
implementation 'com.google.code.gson:gson:2.8.6'
}
andResGuard {
// mappingFile = file("./resource_mapping.txt")
mappingFile = null
use7zip = true
useSign = true
// 打开这个开关会keep住所有资源的原始路径只混淆资源的名字
keepRoot = false
// 设置这个值会把arsc name列混淆成相同的名字减少string常量池的大小
fixedResName = "arg"
// 打开这个开关会合并所有哈希值相同的资源,但请不要过度依赖这个功能去除去冗余资源
mergeDuplicatedRes = true
whiteList = [
// for your icon
"R.drawable.icon",
// for fabric
"R.string.com.crashlytics.*",
// for google-services
"R.string.google_app_id",
"R.string.gcm_defaultSenderId",
"R.string.default_web_client_id",
"R.string.ga_trackingId",
"R.string.firebase_database_url",
"R.string.google_api_key",
"R.string.google_crash_reporting_api_key"
]
compressFilePattern = [
"*.png",
"*.jpg",
"*.jpeg",
"*.gif",
]
sevenzip {
artifact = 'com.tencent.mm:SevenZip:1.2.19'
//path = "/usr/local/bin/7za"
}
/**
* 可选: 如果不设置则会默认覆盖assemble输出的apk
**/
// finalApkBackupPath = "${project.rootDir}/final.apk"
/**
* 可选: 指定v1签名时生成jar文件的摘要算法
* 默认值为“SHA-1”
**/
// digestalg = "SHA-256"
}

View File

@@ -18,4 +18,44 @@
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
#-renamesourcefileattribute SourceFile
-keep public class * implements com.bumptech.glide.module.GlideModule
-keep class * extends com.bumptech.glide.module.AppGlideModule {
<init>(...);
}
-keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
**[] $VALUES;
public *;
}
-keep class com.bumptech.glide.load.data.ParcelFileDescriptorRewinder$InternalRewinder {
*** rewind();
}
##---------------Begin: proguard configuration for Gson ----------
# Gson uses generic type information stored in a class file when working with fields. Proguard
# removes such information by default, so configure it to keep all of it.
-keepattributes Signature
# For using GSON @Expose annotation
-keepattributes *Annotation*
# Gson specific classes
-dontwarn sun.misc.**
#-keep class com.google.gson.stream.** { *; }
# Application classes that will be serialized/deserialized over Gson
-keep public class top.fumiama.copymanga.data.* { *; }
# Prevent proguard from stripping interface information from TypeAdapter, TypeAdapterFactory,
# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter)
-keep class * extends com.google.gson.TypeAdapter
-keep class * implements com.google.gson.TypeAdapterFactory
-keep class * implements com.google.gson.JsonSerializer
-keep class * implements com.google.gson.JsonDeserializer
# Prevent R8 from leaving Data object members always null
-keepclassmembers,allowobfuscation class * {
@com.google.gson.annotations.SerializedName <fields>;
}
##---------------End: proguard configuration for Gson ----------

View File

@@ -1,12 +1,22 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="top.fumiama.copymanga">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:roundIcon="@mipmap/ic_launcher"
android:supportsRtl="true"
android:theme="@style/AppTheme" />
android:theme="@style/AppTheme" >
<activity android:name=".activity.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".activity.ViewMangaActivity"/>
<activity android:name=".activity.DlActivity"/>
</application>
</manifest>

45
app/src/main/assets/h.js Normal file
View File

@@ -0,0 +1,45 @@
javascript:
if (typeof (loaded) == "undefined"){
var loaded = true;
function scanChapters(chapter){
var chapterList = chapter.getElementsByClassName("table-all")[0].getElementsByTagName("a");
var chapterArr = Array();
for(var i = 0; i < chapterList.length; i++){
chapterArr.push(JSON.constructor());
chapterArr[i]["name"] = chapterList[i].title;
chapterArr[i]["url"] = chapterList[i].href;
}
return chapterArr;
}
function modify(){
var url = location.href;
if(url.indexOf("/chapter/")>0){
var imglist = document.getElementsByClassName("container-fluid comicContent")[0].getElementsByTagName("li");
var nextChapter = document.getElementsByClassName("comicContent-next")[0].getElementsByTagName("a")[0].href;
var prevChapter = document.getElementsByClassName("comicContent-prev")[1].getElementsByTagName("a")[0].href;
if(nextChapter == location.href) nextChapter = "null";
if(prevChapter == location.href) prevChapter = "null";
var liststr = document.title.split(" - ")[1] + " " + location.href.substring(location.href.lastIndexOf("/")+1) + "\n" + nextChapter + "\n" + prevChapter;
for(var i = 0; i < imglist.length; i++) liststr += "\n" + imglist[i].getElementsByTagName("img")[0].dataset.src;
GM.loadChapter(liststr);
}else {
var json = Array();
var chapters = document.getElementsByClassName("upLoop")[0].children;
var newObj = null;
for(var i = 0; i < chapters.length; i++) {
if(i % 2) {
newObj["chapters"] = scanChapters(chapters[i]);
json.push(newObj);
newObj = null;
}
else {
newObj = JSON.constructor();
newObj["name"] = chapters[i].innerText;
}
}
GM.setTitle(document.getElementsByTagName("h6")[0].title);
GM.setFab(JSON.stringify(json));
}
}
modify();
}else modify();

46
app/src/main/assets/i.js Normal file
View File

@@ -0,0 +1,46 @@
javascript:
if (typeof (loaded) == "undefined") {
var loaded = true;
var invoke = {
preUrl: "",
pinTitle: function () {
/*document.getElementsByClassName("van-button__content")[2].click();*/
document.getElementsByClassName("indexTitle")[0].style.position = "fixed";
document.getElementsByClassName("indexTitle")[0].style.zIndex = 999;
document.getElementsByClassName("indexTitle")[0].style.width = document.body.clientWidth - 18 + "px";
document.getElementsByClassName("copySwiper")[0].style.marginTop = "56px";
document.getElementsByClassName("indexTitle")[0].style.marginTop = "-56px";
},
notCallGM: function (url) {
if (this.preUrl == url) return false;
else {
this.preUrl = url;
return true;
}
},
clickClass: function (name, index) { document.getElementsByClassName(name)[index].click(); },
clickClassCenter: function (name, index) {
var ev = document.createEvent('HTMLEvents');
ev.clientX = innerWidth / 2;
ev.clientY = innerHeight / 2;
ev.initEvent('click', false, true);
document.getElementsByClassName(name)[index].dispatchEvent(ev);
},
resetPreUrl: function () { this.preUrl = ""; },
loadChapter: function () { this.clickClassCenter("comicContentPopupImageItem", 0); GM.loadComic(location.href); },
urlChangeListener: function (todo) {
setInterval(function () { if (invoke.notCallGM(location.href)) { todo(); } }, 1000);
}
};
function modify() {
var url = location.href;
GM.hideFab();
if (url.endsWith("/index")) invoke.pinTitle();
else if (url.indexOf("/comicContent/") > 0) setTimeout(function () { invoke.loadChapter() }, 1000);
else if (url.indexOf("/details/comic/") > 0) GM.loadComic(url);
}
modify();
invoke.urlChangeListener(modify);
} else {
setTimeout(modify, 1280);
}

View File

@@ -0,0 +1,292 @@
package top.fumiama.copymanga.activity
import android.animation.ObjectAnimator
import android.annotation.SuppressLint
import android.app.Activity
import android.os.Bundle
import android.util.Log
import android.view.View
import android.view.ViewGroup
import android.view.ViewTreeObserver
import android.widget.Toast
import android.widget.ToggleButton
import kotlinx.android.synthetic.main.activity_dl.*
import kotlinx.android.synthetic.main.button_tbutton.view.*
import kotlinx.android.synthetic.main.line_caption.view.*
import kotlinx.android.synthetic.main.line_horizonal.view.*
import kotlinx.android.synthetic.main.widget_downloadbar.*
import kotlinx.android.synthetic.main.widget_titlebar.*
import top.fumiama.copymanga.R
import top.fumiama.copymanga.activity.MainActivity.Companion.mh
import top.fumiama.copymanga.handler.DlHandler
import top.fumiama.copymanga.tool.MangaDlTools
import top.fumiama.copymanga.tool.MangaDlTools.Companion.comicStructure
import top.fumiama.copymanga.tool.MangaDlTools.Companion.wmdlt
import top.fumiama.copymanga.tool.ToolsBox
import top.fumiama.copymanga.view.LazyScrollView
import java.io.File
import java.lang.Thread.sleep
import java.lang.ref.WeakReference
class DlActivity : Activity() {
private var tbtncnt = 0
private var isNewTitle = false
var haveSElectAll = false
var checkedChapter = 0
var dldChapter = 0
var haveDlStarted = false
private var btnNumPerRow = 4
private lateinit var ltbtn: View
var tbtnlist: List<ToggleButton> = arrayListOf()
var tbtnUrlList = arrayListOf<String>()
private val handler = DlHandler(this)
private var btnw = 0
private var cdwnHeight = 0
private var canDl = false
private lateinit var toolsBox: ToolsBox
val mangaDlTools = MangaDlTools()
@ExperimentalStdlibApi
@SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_dl)
mh?.saveUrlsOnly = true
handler.sendEmptyMessage(-2)
}
override fun onDestroy() {
mh?.saveUrlsOnly = false
wmdlt?.get()?.exit = true
super.onDestroy()
}
private fun showDlCard() {
//ObjectAnimator.ofFloat(csdwn, "alpha", 0.3f, 0.9f).setDuration(233).start()
ObjectAnimator.ofFloat(csdwn, "translationY", cdwnHeight.toFloat(), 0f).setDuration(233)
.start()
}
private fun hideDlCard() {
//ObjectAnimator.ofFloat(csdwn, "alpha", 0.9f, 0.3f).setDuration(233).start()
ObjectAnimator.ofFloat(csdwn, "translationY", 0f, cdwnHeight.toFloat()).setDuration(233)
.start()
}
private fun fillChapters() {
mangaDlTools.allocateChapterUrls(checkedChapter)
for (i in tbtnlist.indices) {
if (tbtnlist[i].isChecked) mangaDlTools.dlChapterUrl(tbtnUrlList[i])
}
}
private fun dlThead(dlMethod: (i: ToggleButton) -> Unit) {
sleep(2333)
for (i in tbtnlist.listIterator()) {
if (i.isChecked) dlMethod(i)
if (!canDl) break
}
if (canDl) {
haveDlStarted = false
canDl = false
}
handler.sendEmptyMessage(8)
}
@ExperimentalStdlibApi
@SuppressLint("SetTextI18n")
fun setLayouts() {
ttitle.text = comicName
toolsBox = ToolsBox(WeakReference(this))
val widthData = toolsBox.calcWidthFromDp(8, 64)
btnNumPerRow = widthData[0]
btnw = widthData[1]
csdwn.viewTreeObserver.addOnGlobalLayoutListener(object :
ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
cdwnHeight = csdwn.height
Log.d("MyDl", "Get csdwn height: $cdwnHeight")
csdwn.viewTreeObserver.removeOnGlobalLayoutListener(this)
}
})
dllazys.onScrollListener = object : LazyScrollView.OnScrollListener {
override fun onBottom() {
if (csdwn.translationY > 0f) showDlCard()
}
override fun onScroll() {
if (csdwn.translationY == 0f) hideDlCard()
}
override fun onTop() {
if (csdwn.translationY > 0f) showDlCard()
}
}
cdwn.setOnClickListener {
pdwn.progress = 0
if (canDl || checkedChapter == 0) canDl = false
else {
haveDlStarted = true
canDl = true
handler.sendEmptyMessage(9)
Toast.makeText(this, "准备下载...", Toast.LENGTH_SHORT).show()
fillChapters()
Thread { dlThead { downloadChapterPages(it) } }.start()
}
}
cdwn.setOnLongClickListener {
Thread { handler.sendEmptyMessage(4) }.start()
return@setOnLongClickListener true
}
analyzeStructure()
}
private fun analyzeStructure() {
comicStructure?.let {
for (group in it) {
val tc = layoutInflater.inflate(R.layout.line_caption, ldwn, false)
tc.tcptn.text = group.name
ldwn.addView(
tc,
ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
)
ldwn.addView(
layoutInflater.inflate(R.layout.div_h, ldwn, false),
ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
)
isNewTitle = true
for (chapter in group.chapters) addTbtn(chapter.name, chapter.url, group.name)
}
}
}
@ExperimentalStdlibApi
private fun downloadChapterPages(i: ToggleButton) {
mangaDlTools.onDownloadedListener =
object : MangaDlTools.OnDownloadedListener {
override fun handleMessage(succeed: Boolean) {
handler.obtainMessage(if (succeed) 1 else -1, tbtnlist.indexOf(i), 0)
.sendToTarget()
}
override fun handleMessage(succeed: Boolean, pageNow: Int) {
handler.obtainMessage(
5,
tbtnlist.indexOf(i),
pageNow,
succeed
).sendToTarget()
}
}
mangaDlTools.dlChapterAndPackIntoZip(
File("${getExternalFilesDir("")}/$comicName/${i.hint}/${i.textOn}.zip"),
tbtnUrlList[tbtnlist.indexOf(i)].substringAfterLast("/")
)
}
@SuppressLint("SetTextI18n")
fun addTbtn(title: String, url: String, caption: String) {
if ((tbtncnt % btnNumPerRow == 0) || isNewTitle) {
ltbtn = layoutInflater.inflate(R.layout.line_horizonal, ldwn, false)
ldwn.addView(ltbtn)
tbtncnt = 0
isNewTitle = false
}
val tbv = layoutInflater.inflate(R.layout.button_tbutton, ltbtn.ltbtn, false)
tbtnlist += tbv.tbtn
tbtncnt++
tbtnUrlList.add(url)
tbv.tbtn.textOff = title
tbv.tbtn.textOn = title
tbv.tbtn.text = title
tbv.tbtn.hint = caption
tbv.tbtn.layoutParams.width = btnw
val zipf = File("${getExternalFilesDir("")}/$comicName/$caption/$title.zip")
if (zipf.exists()) {
tbv.tbtn.setBackgroundResource(R.drawable.rndbg_checked)
tbv.tbtn.isChecked = false
}
ltbtn.ltbtn.addView(tbv)
ltbtn.invalidate()
tbv.tbtn.setOnClickListener {
if (zipf.exists() && !it.tbtn.isChecked) it.tbtn.setBackgroundResource(R.drawable.rndbg_checked)
else it.tbtn.setBackgroundResource(R.drawable.toggle_button)
if (it.tbtn.isChecked) tdwn.text = "$dldChapter/${++checkedChapter}"
else tdwn.text = "$dldChapter/${--checkedChapter}"
}
tbv.tbtn.setOnLongClickListener {
if (zipf.exists()) {
toolsBox.buildInfo("确认删除这些章节?",
"该操作将不可撤销",
"确定",
null,
"取消",
{
Thread {
handler.obtainMessage(
7,
tbtnlist.indexOf(it.tbtn),
0,
zipf
).sendToTarget()
}.start()
})
}
true
}
}
fun deleteChapters(zipf: File, index: Int) {
for (i in tbtnlist) {
if (i.isChecked) {
val f =
File("${getExternalFilesDir("")}/$comicName/${i.hint}/${i.textOn}.zip")
if (f.exists()) {
deleteChapter(f, i)
checkedChapter--
}
}
}
handler.sendEmptyMessage(6)
}
private fun deleteChapter(f: File, v: ToggleButton) {
f.delete()
v.setBackgroundResource(R.drawable.toggle_button)
v.isChecked = false
}
@SuppressLint("SetTextI18n")
fun updateProgressBar() {
tdwn.text = "${++dldChapter}/$checkedChapter"
setProgress2(dldChapter * 100 / checkedChapter, 233)
}
fun updateProgressBar(pageNow: Int, size: Int) {
val delta = 100 / checkedChapter
val start = dldChapter * delta
val now = pageNow * delta / size
setProgress2(start + now, 64)
}
fun setProgress2(end: Int, duration: Long) {
ObjectAnimator.ofInt(
pdwn,
"progress",
pdwn.progress,
end
).setDuration(duration).start()
}
companion object {
var comicName = "Null"
}
}

View File

@@ -0,0 +1,53 @@
package top.fumiama.copymanga.activity
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Intent
import android.os.Bundle
import android.os.Looper
import android.view.View
import android.webkit.WebView
import kotlinx.android.synthetic.main.activity_main.*
import top.fumiama.copymanga.R
import top.fumiama.copymanga.handler.MainHandler
import top.fumiama.copymanga.view.JSWebView
import top.fumiama.copymanga.web.JS
import top.fumiama.copymanga.web.JSHidden
import top.fumiama.copymanga.web.WebChromeClient
import java.lang.ref.WeakReference
class MainActivity: Activity() {
var wh: JSWebView? = null
@SuppressLint("JavascriptInterface")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
wm = WeakReference(this)
mh = Looper.myLooper()?.let { MainHandler(it) }
WebView.setWebContentsDebuggingEnabled(true)
w.setWebViewClient("i.js")
w.webChromeClient = WebChromeClient()
w.loadJSInterface(JS())
w.loadUrl(getString(R.string.web_home))
wh = JSWebView(this, getString(R.string.pc_ua))
wh?.setWebViewClient("h.js")
wh?.loadJSInterface(JSHidden())
}
override fun onBackPressed() {
if(w.canGoBack()) w.goBack()
else super.onBackPressed()
}
fun onFabClicked(v: View){
startActivity(Intent(this, DlActivity::class.java))
}
companion object{
var wm: WeakReference<MainActivity>? = null
var mh: MainHandler? = null
}
}

View File

@@ -0,0 +1,372 @@
package top.fumiama.copymanga.activity
import android.animation.ObjectAnimator
import android.annotation.SuppressLint
import android.app.Activity
import android.os.Bundle
import android.os.Handler
import android.os.Message
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.SeekBar
import android.widget.Toast
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
import com.bumptech.glide.Glide
import kotlinx.android.synthetic.main.activity_main.*
import kotlinx.android.synthetic.main.activity_viewmanga.*
import kotlinx.android.synthetic.main.page_imgview.*
import kotlinx.android.synthetic.main.page_imgview.view.*
import kotlinx.android.synthetic.main.widget_infodrawer.*
import kotlinx.android.synthetic.main.widget_infodrawer.view.*
import kotlinx.android.synthetic.main.widget_titlebar.*
import kotlinx.android.synthetic.main.widget_viewmangainfo.*
import top.fumiama.copymanga.R
import top.fumiama.copymanga.activity.MainActivity.Companion.wm
import top.fumiama.copymanga.handler.TimeThread
import top.fumiama.copymanga.tool.PropertiesTools
import top.fumiama.copymanga.tool.ToolsBox
import java.io.File
import java.lang.ref.WeakReference
import java.text.SimpleDateFormat
import java.util.*
class ViewMangaActivity : Activity() {
var count = 0
lateinit var handler: Handler
lateinit var tt: TimeThread
var clicked = false
private var isInSeek = false
private var useFullScreen = false
var r2l = true
private var currentItem = 0
private var notUseVP = true
private var q = 90
var infoDrawerDelta = 0f
lateinit var toolsBox: ToolsBox
private lateinit var p: PropertiesTools
var pageNum = 1
get() {
field = getPageNumber()
return field
}
set(value) {
setPageNumber(value)
if (notUseVP) {
//currentItem += delta
try {
loadOneImg()
} catch (e: java.lang.Exception) {
e.printStackTrace()
toolsBox.toastError("页数${currentItem}不合法")
}
}// else vp.currentItem += delta
field = getPageNumber()
}
@ExperimentalStdlibApi
@SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_viewmanga)
toolsBox = ToolsBox(WeakReference(this))
va = WeakReference(this)
p = PropertiesTools(File("$filesDir/settings.properties"))
useFullScreen = p["useFullScreen"] != "true"
r2l = p["r2l"] == "true"
//toolsBox = ToolsBox(WeakReference(this))
notUseVP = p["noAnimation"] == "true"
handler = MyHandler(infcard, toolsBox)
if (p["quality"] == "null") p["quality"] = "90"
else q = p["quality"].toInt()
tt = TimeThread(handler, 22)
tt.canDo = true
tt.start()
ttitle.text = titleText
try {
count = imgUrls.size
} catch (e: Exception) {
e.printStackTrace()
toolsBox.toastError("分析图片url错误")
}
try {
prepareItems(count)
} catch (e: Exception) {
e.printStackTrace()
toolsBox.toastError("准备控件错误")
}
}
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
if (useFullScreen) window.decorView.systemUiVisibility =
View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_FULLSCREEN or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
}
private fun getPageNumber(): Int {
return if (r2l && !notUseVP) count - vp.currentItem
else (if (notUseVP) currentItem else vp.currentItem) + 1
}
private fun setPageNumber(num: Int) {
if (r2l && !notUseVP) vp.currentItem = count - num
else if (notUseVP) currentItem = num - 1 else vp.currentItem = num - 1
}
private fun loadOneImg() {
Glide.with(this@ViewMangaActivity.applicationContext).load(
imgUrls[currentItem]
).thumbnail(
Glide.with(this@ViewMangaActivity.applicationContext).load(R.drawable.bg_comment)
).into(onei)
updateSeekBar()
}
private fun setIdPosition(position: Int) {
infoDrawerDelta = position.toFloat()
infcard.translationY = infoDrawerDelta
}
@SuppressLint("SetTextI18n")
private fun prepareItems(size: Int) {
prepareVP()
prepareInfoBar(size)
if (notUseVP) loadOneImg() else prepareIdBtVH()
toolsBox.dp2px(67)?.let { setIdPosition(it) }
prepareIdBtFullScreen()
prepareIdBtVP()
prepareIdBtLR()
}
private fun prepareIdBtLR() {
idtblr.isChecked = r2l
idtblr.setOnClickListener {
if (idtblr.isChecked) p["r2l"] = "true"
else p["r2l"] = "false"
Toast.makeText(this.applicationContext, "下次浏览生效", Toast.LENGTH_SHORT).show()
}
}
private fun prepareIdBtVP() {
idtbvp.setOnClickListener {
if (idtbvp.isChecked) p["noAnimation"] = "true"
else p["noAnimation"] = "false"
Toast.makeText(this.applicationContext, "下次浏览生效", Toast.LENGTH_SHORT).show()
}
}
private fun prepareVP() {
if (notUseVP) {
vp.visibility = View.INVISIBLE
vone.visibility = View.VISIBLE
} else {
vp.visibility = View.VISIBLE
vone.visibility = View.INVISIBLE
vp.adapter = ViewData(vp).RecyclerViewAdapter()
vp.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
updateSeekBar()
super.onPageSelected(position)
}
})
if (r2l) vp.currentItem = count - 1
}
}
private fun updateSeekBar() {
if (!isInSeek) hideObjs()
updateSeekText()
updateSeekProgress()
sendProgress()
}
@SuppressLint("SetTextI18n")
private fun prepareInfoBar(size: Int) {
oneinfo.alpha = 0F
infseek.visibility = View.INVISIBLE
isearch.visibility = View.INVISIBLE
inftxtprogress.text = "$pageNum/$size"
infseek.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(p0: SeekBar?, p1: Int, isHuman: Boolean) {
if (isHuman) {
if (p1 >= (pageNum + 1) * 100 / size) scrollForward()
else if (p1 < (pageNum - 1) * 100 / size) scrollBack()
}
}
override fun onStartTrackingTouch(p0: SeekBar?) {
isInSeek = true
}
override fun onStopTrackingTouch(p0: SeekBar?) {
isInSeek = false
}
})
isearch.setOnClickListener {
handler.sendEmptyMessage(3)
}
}
private fun prepareIdBtVH() {
idtbvh.isChecked =
p["vertical"] == "true"
if (idtbvh.isChecked) vp.orientation = ViewPager2.ORIENTATION_VERTICAL
idtbvh.setOnClickListener {
if (idtbvh.isChecked) {
vp.orientation = ViewPager2.ORIENTATION_VERTICAL
p["vertical"] = "true"
} else {
vp.orientation = ViewPager2.ORIENTATION_HORIZONTAL
p["vertical"] = "false"
}
}
}
private fun prepareIdBtFullScreen() {
idtbfullscreen.isChecked = !useFullScreen
idtbfullscreen.setOnClickListener {
if (idtbfullscreen.isChecked) p["useFullScreen"] =
"true"
else p["useFullScreen"] = "false"
Toast.makeText(this.applicationContext, "下次浏览生效", Toast.LENGTH_SHORT).show()
}
}
fun scrollBack() {
pageNum--
}
fun scrollForward() {
pageNum++
}
private fun sendProgress() {
}
@SuppressLint("SetTextI18n")
private fun updateSeekText() {
inftxtprogress.text = "$pageNum/$count"
}
private fun updateSeekProgress() {
infseek.progress = pageNum * 100 / count
}
override fun onBackPressed() {
tt.canDo = false
wm?.get()?.w?.goBack()
super.onBackPressed()
}
override fun onDestroy() {
tt.canDo = false
super.onDestroy()
}
inner class ViewData(itemView: View) : RecyclerView.ViewHolder(itemView) {
inner class RecyclerViewAdapter :
RecyclerView.Adapter<ViewData>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewData {
return ViewData(
LayoutInflater.from(parent.context)
.inflate(R.layout.page_imgview, parent, false)
)
}
@SuppressLint("ClickableViewAccessibility", "SetTextI18n")
override fun onBindViewHolder(holder: ViewData, position: Int) {
val pos = if (r2l) count - position - 1 else position
Glide.with(this@ViewMangaActivity.applicationContext).load(
imgUrls[pos]
).thumbnail(
Glide.with(this@ViewMangaActivity.applicationContext)
.load(R.drawable.bg_comment)
).into(holder.itemView.onei)
}
override fun getItemCount(): Int {
return count
}
}
}
fun showObjs() {
infseek.visibility = View.VISIBLE
isearch.visibility = View.VISIBLE
ObjectAnimator.ofFloat(
oneinfo,
"alpha",
oneinfo.alpha,
1F
).setDuration(233).start()
clicked = true
}
fun hideObjs() {
ObjectAnimator.ofFloat(
oneinfo,
"alpha",
oneinfo.alpha,
0F
).setDuration(233).start()
clicked = false
infseek.postDelayed({
infseek.visibility = View.INVISIBLE
isearch.visibility = View.INVISIBLE
}, 300)
handler.sendEmptyMessage(1)
}
class MyHandler(
private val infcard: View,
private val toolsBox: ToolsBox
) : Handler() {
private var infcShowed = false
private var delta = -1f
get() {
if (field < 0) field = va?.get()?.infoDrawerDelta ?: 0f
return field
}
@SuppressLint("SimpleDateFormat", "SetTextI18n")
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
when (msg.what) {
1 -> if (infcShowed) {
hideInfCard(); infcShowed = false
}
2 -> if (!infcShowed) {
showInfCard(); infcShowed = true
}
3 -> infcShowed = if (infcShowed) {
hideInfCard(); false
} else {
showInfCard(); true
}
22 -> toolsBox.zis?.idtime?.text =
SimpleDateFormat("HH:mm").format(Date()) + toolsBox.week + toolsBox.netinfo
}
}
private fun showInfCard() {
ObjectAnimator.ofFloat(infcard.idc, "alpha", 0.3F, 0.8F).setDuration(233).start()
ObjectAnimator.ofFloat(infcard, "translationY", delta, 0F).setDuration(233).start()
}
private fun hideInfCard() {
ObjectAnimator.ofFloat(infcard.idc, "alpha", 0.8F, 0.3F).setDuration(233).start()
ObjectAnimator.ofFloat(infcard, "translationY", 0F, delta).setDuration(233).start()
}
}
companion object {
var va: WeakReference<ViewMangaActivity>? = null
var imgUrls = arrayOf<String>()
var titleText = "Null"
var nextChapterUrl: String? = null
var previousChapterUrl: String? = null
}
}

View File

@@ -0,0 +1,10 @@
package top.fumiama.copymanga.data;
public class ComicStructure {
public String name;
public Chapters[] chapters;
public static class Chapters{
public String name;
public String url;
}
}

View File

@@ -0,0 +1,90 @@
package top.fumiama.copymanga.handler
import android.annotation.SuppressLint
import android.os.Handler
import android.os.Message
import android.widget.Toast
import kotlinx.android.synthetic.main.widget_downloadbar.*
import top.fumiama.copymanga.R
import top.fumiama.copymanga.activity.DlActivity
import top.fumiama.copymanga.activity.ViewMangaActivity.Companion.imgUrls
import top.fumiama.copymanga.tool.MangaDlTools.Companion.wmdlt
import java.io.File
import java.lang.ref.WeakReference
class DlHandler(activity: DlActivity) : Handler() {
private val da = WeakReference(activity)
private val d = da.get()
@ExperimentalStdlibApi
@SuppressLint("SetTextI18n")
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
when (msg.what) {
-2 -> d?.setLayouts()
1 -> {
d?.tbtnlist?.get(msg.arg1)?.setBackgroundResource(R.drawable.rndbg_checked)
d?.tbtnlist?.get(msg.arg1)?.isChecked = false
d?.updateProgressBar()
if (d?.haveDlStarted == false) {
d.dldChapter = 0
d.checkedChapter = 0
this.postDelayed({
d.setProgress2(0, 233)
d.tdwn?.text = "0/0"
}, 400)
}
}
-1 -> {
d?.tbtnlist?.get(msg.arg1)?.setBackgroundResource(R.drawable.rndbg_error)
d!!.dldChapter--
Toast.makeText(
d,
"下载${d.tbtnlist[msg.arg1].textOn}失败",
Toast.LENGTH_SHORT
).show()
d.updateProgressBar()
}
4 -> {
d?.pdwn?.progress = 0
if (d?.haveSElectAll == true) {
for (i in d.tbtnlist.listIterator()) {
i.setBackgroundResource(R.drawable.toggle_button)
i.isChecked = false
}
d.haveSElectAll = false
d.checkedChapter = 0
d.dldChapter = 0
} else {
d?.let {
for (i in it.tbtnlist.listIterator()) {
i.setBackgroundResource(R.drawable.toggle_button)
i.isChecked = true
it.checkedChapter++
}
}
d?.haveSElectAll = true
}
d?.tdwn?.text = "${d?.dldChapter}/${d?.checkedChapter}"
}
5 -> {
d?.updateProgressBar(
msg.arg2,
wmdlt?.get()
?.getImgsCountByHash(d.tbtnUrlList[msg.arg1].substringAfterLast("/")) ?: 0
)
if (!(msg.obj as Boolean)) {
Toast.makeText(
d,
"下载${d?.tbtnlist?.get(msg.arg1)?.textOn}的第${msg.arg2}页失败",
Toast.LENGTH_SHORT
).show()
}
}
6 -> d?.tdwn?.text = "${d?.dldChapter}/${d?.checkedChapter}"
7 -> d?.deleteChapters(msg.obj as File, msg.arg1)
8 -> d?.cdwn?.setCardBackgroundColor(d.resources.getColor(R.color.colorBlue))
9 -> d?.cdwn?.setCardBackgroundColor(d.resources.getColor(R.color.colorRed))
}
}
}

View File

@@ -0,0 +1,64 @@
package top.fumiama.copymanga.handler
import android.animation.ObjectAnimator
import android.content.Intent
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.util.Log
import android.view.View
import com.google.gson.Gson
import kotlinx.android.synthetic.main.activity_main.*
import top.fumiama.copymanga.activity.DlActivity
import top.fumiama.copymanga.activity.MainActivity.Companion.wm
import top.fumiama.copymanga.activity.ViewMangaActivity
import top.fumiama.copymanga.data.ComicStructure
import top.fumiama.copymanga.tool.MangaDlTools
import top.fumiama.copymanga.tool.MangaDlTools.Companion.comicStructure
import top.fumiama.copymanga.tool.MangaDlTools.Companion.wmdlt
class MainHandler(looper: Looper):Handler(looper) {
var saveUrlsOnly = false
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
when(msg.what){
1 -> loadUrlInHiddenWebView(msg.obj as String)
2 -> callViewManga(msg.obj as String)
3 -> updateLoadProgress(msg.arg1)
4 -> setFab(msg.obj as String)
5 -> hideFab()
}
}
private fun loadUrlInHiddenWebView(url: String){wm?.get()?.wh?.loadUrl(url)}
private fun callViewManga(content: String){
val listChapter = content.split("\n")
if(!saveUrlsOnly) {
ViewMangaActivity.titleText = listChapter[0].substringBeforeLast(" ")
ViewMangaActivity.nextChapterUrl = listChapter[1].let { if(it == "null") null else it }
ViewMangaActivity.previousChapterUrl = listChapter[2].let { if(it == "null") null else it }
ViewMangaActivity.imgUrls = arrayOf()
for(i in 3 until listChapter.size) ViewMangaActivity.imgUrls += listChapter[i]
wm?.get()?.let { it.startActivity(Intent(it, ViewMangaActivity::class.java)) }
} else{
var imgs = arrayOf<String>()
for(i in 3 until listChapter.size) imgs += listChapter[i]
wmdlt?.get()?.setChapterImgs(listChapter[0].substringAfterLast(" "), imgs)
}
}
private fun updateLoadProgress(progress: Int){
wm?.get()?.let{
if(it.pw.progress == 100 && progress < 100) {
it.pw.progress = 0
it.pw.visibility = View.VISIBLE
}
ObjectAnimator.ofInt(it.pw, "progress", it.pw.progress, progress).setDuration(233).start()
if(progress == 100) it.pw.postDelayed({it.pw.visibility = View.GONE}, 500)
}
}
private fun setFab(content: String){
//Log.d("MyMH", "Get chapter json: $content")
comicStructure = Gson().fromJson(content.reader(), Array<ComicStructure>::class.java)
wm?.get()?.fab?.visibility = View.VISIBLE
}
private fun hideFab() {wm?.get()?.fab?.visibility = View.GONE}
}

View File

@@ -0,0 +1,17 @@
package top.fumiama.copymanga.handler
import android.os.Handler
class TimeThread(private val handler: Handler, private val msg: Int) : Thread() {
var canDo = false
override fun run() {
while (canDo) {
try {
handler.sendEmptyMessage(msg)
sleep(3000)
} catch (e: InterruptedException) {
e.printStackTrace()
}
}
}
}

View File

@@ -0,0 +1,37 @@
package top.fumiama.copymanga.tool
import android.util.Log
import java.io.File
import java.net.HttpURLConnection
import java.net.URL
import java.util.concurrent.Callable
import java.util.concurrent.FutureTask
class DownloadTools {
fun getHttpContent(Url: String, refer: String? = null): ByteArray? {
Log.d("Mydl", "getHttp: $Url")
var ret: ByteArray? = null
val task = FutureTask(Callable {
try {
val connection = URL(Url).openConnection() as HttpURLConnection
connection.requestMethod = "GET"
connection.connectTimeout = 10000
connection.readTimeout = 10000
refer?.let { connection.setRequestProperty("referer", it) }
ret = connection.inputStream.readBytes()
connection.disconnect()
} catch (ex: Exception) {
ex.printStackTrace()
}
return@Callable ret
})
Thread(task).start()
return try {
task.get()
} catch (ex: Exception) {
ex.printStackTrace()
null
}
}
}

View File

@@ -0,0 +1,83 @@
package top.fumiama.copymanga.tool
import top.fumiama.copymanga.R
import top.fumiama.copymanga.activity.MainActivity.Companion.wm
import top.fumiama.copymanga.data.ComicStructure
import top.fumiama.copymanga.view.JSWebView
import top.fumiama.copymanga.web.JSHidden
import java.io.File
import java.lang.ref.WeakReference
import java.util.zip.CRC32
import java.util.zip.CheckedOutputStream
import java.util.zip.ZipEntry
import java.util.zip.ZipOutputStream
class MangaDlTools {
var exit = false
private val p = PropertiesTools(File("${wm?.get()?.filesDir}/chapters.hash"))
private var imgUrlsList: Array<Array<String>?>? = null
private var chaptersCount = 0
private val newWebViewHidden: JSWebView?
get() {
val re = wm?.get()?.let { JSWebView(it, it.getString(R.string.pc_ua)) }
re?.setWebViewClient("h.js")
re?.loadJSInterface(JSHidden())
return re
}
init {
wmdlt = WeakReference(this)
}
fun getImgsCountByHash(hash: String): Int?{
return imgUrlsList?.get(p[hash].toInt())?.size
}
fun allocateChapterUrls(count: Int){
imgUrlsList = arrayOfNulls(count)
chaptersCount = 0
}
fun dlChapterUrl(url: String){
p[url.substringAfterLast("/")] = (chaptersCount++).toString()
newWebViewHidden?.loadUrl(url)
}
fun setChapterImgs(hash: String, imgUrls: Array<String>){
imgUrlsList?.set(p[hash].toInt(), imgUrls)
}
fun dlChapterAndPackIntoZip(zipf: File, hash: String){
imgUrlsList?.get(p[hash].toInt())?.let {
val dl = DownloadTools()
zipf.parentFile?.let { if (!it.exists()) it.mkdirs() }
if (zipf.exists()) zipf.delete()
zipf.createNewFile()
val zip = ZipOutputStream(CheckedOutputStream(zipf.outputStream(), CRC32()))
zip.setLevel(9)
var succeed = true
for (i in it.indices) {
zip.putNextEntry(ZipEntry("$i.webp"))
val s = dl.getHttpContent(it[i])?.let { zip.write(it); true } ?: false
if (!s) succeed = s
onDownloadedListener?.handleMessage(s, i + 1)
zip.flush()
if (exit) break
}
zip.close()
onDownloadedListener?.handleMessage(succeed)
}
}
var onDownloadedListener: OnDownloadedListener? = null
interface OnDownloadedListener {
fun handleMessage(succeed: Boolean)
fun handleMessage(succeed: Boolean, pageNow: Int)
}
companion object {
var wmdlt: WeakReference<MangaDlTools>? = null
var comicStructure: Array<ComicStructure>? = null
}
}

View File

@@ -0,0 +1,60 @@
package top.fumiama.copymanga.tool
import android.widget.Toast
import kotlinx.android.synthetic.main.activity_main.*
import top.fumiama.copymanga.activity.MainActivity.Companion.wm
import top.fumiama.copymanga.activity.ViewMangaActivity
import java.lang.ref.WeakReference
class PagesManager(w: WeakReference<ViewMangaActivity>) {
val v = w.get()
private var isEndL = false
private var isEndR = false
@ExperimentalStdlibApi
fun toPreviousPage(){
toPage(v?.r2l==true)
}
@ExperimentalStdlibApi
fun toNextPage(){
toPage(v?.r2l!=true)
}
private fun judgePrevious() = v?.pageNum?:0 > 1
private fun judgeNext() = v?.pageNum?:0 < v?.count?:0
@ExperimentalStdlibApi
private fun toPage(goNext:Boolean){
val chapterUrl = if(goNext) ViewMangaActivity.nextChapterUrl else ViewMangaActivity.previousChapterUrl
val hint = if(goNext) "" else ""
if (v?.clicked == false) {
if (if(goNext)judgeNext() else judgePrevious()) {
if(goNext) {
v.scrollForward()
isEndR = false
} else {
v.scrollBack()
isEndL = false
}
} else if (chapterUrl != null) {
if (if(goNext)isEndR else isEndL) {
wm?.get()?.w?.loadUrl("javascript:invoke.clickClass(\"comicControlBottomTopClick\",${if(goNext)1 else 0});")
v.tt.canDo = false
v.finish()
} else {
Toast.makeText(
v.applicationContext,
"再次按下加载${hint}一章",
Toast.LENGTH_SHORT
).show()
if(goNext) isEndR = true
else isEndL = true
}
} else Toast.makeText(
v.applicationContext,
"已经到头了~",
Toast.LENGTH_SHORT
).show()
} else v?.hideObjs()
}
fun manageInfo(){
if (v?.clicked == false) v.showObjs() else v?.hideObjs()
}
}

View File

@@ -0,0 +1,53 @@
package top.fumiama.copymanga.tool
//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)
}
if(f.parentFile?.canWrite() != true) f.parentFile?.setWritable(true)
if(f.parentFile?.canRead() != true) f.parentFile?.setReadable(true)
return f
}
private fun createNew(f: File){
f.createNewFile()
val o = f.outputStream()
this.storeToXML(o, "store")
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 $key = $re")
i.close()
return re
}
operator fun set(key: String, value: String){
val o = propfile.outputStream()
this.setProp(key, value).storeToXML(o, "store")
Log.d("MyPT", "Set $key = $value")
o.close()
}
}

View File

@@ -0,0 +1,91 @@
package top.fumiama.copymanga.tool
import android.app.Activity
import android.app.AlertDialog
import android.content.Context
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.widget.Toast
import top.fumiama.copymanga.R
import java.lang.ref.WeakReference
import java.util.*
import kotlin.math.sqrt
class ToolsBox(w: WeakReference<Any>) {
val zis = (w as WeakReference<Activity>).get()
val week: String
get() {
val cal = Calendar.getInstance()
return when (cal[Calendar.DAY_OF_WEEK]) {
1 -> "周日"
2 -> "周一"
3 -> "周二"
4 -> "周三"
5 -> "周四"
6 -> "周五"
7 -> "周六"
else -> ""
}
}
val netinfo: String
get() {
val cm: ConnectivityManager =
zis?.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
return cm.getNetworkCapabilities(cm.activeNetwork)?.let {
when {
it.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> return@let "WIFI"
it.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> return@let "移动数据"
it.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH) -> return@let "蓝牙"
it.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> return@let "以太网"
it.hasTransport(NetworkCapabilities.TRANSPORT_LOWPAN) -> return@let "LOWPAN"
else -> return@let "无网络"
}
} ?: "错误"
}
fun toastError(s: String, willFinish: Boolean = true) {
Toast.makeText(zis?.applicationContext, s, Toast.LENGTH_SHORT).show()
if (willFinish) zis?.finish()
}
fun buildInfo(
title: String,
msg: String,
txtOk: String? = null,
txtN: String? = null,
txtCancel: String? = null,
ok: (() -> Unit)? = null,
neutral: (() -> Unit)? = null,
cancel: (() -> Unit)? = null
) {
val info = AlertDialog.Builder(zis)
info.setIcon(R.drawable.ic_launcher_foreground)
info.setTitle(title)
info.setMessage(msg)
txtOk?.let { info.setPositiveButton(it) { _, _ -> ok?.let { it() } } }
txtCancel?.let { info.setNegativeButton(it) { _, _ -> cancel?.let { it() } } }
txtN?.let { info.setNeutralButton(it) { _, _ -> neutral?.let { it() } } }
info.show()
}
fun dp2px(dp:Int):Int?{
return zis?.resources?.displayMetrics?.density?.let { (dp * it + 0.5).toInt()}
}
private fun px2dp(px:Int):Int?{
return zis?.resources?.displayMetrics?.density?.let { (px.toDouble() / it + 0.5).toInt()}
}
private fun root(a:Double, b:Double, c:Double):List<Double>?{
val d = b*b - 4.0 * a * c
if(d < 0) return null
val sd = sqrt(d)
val x1 = (-b + sd)/(2.0 * a)
val x2 = (-b - sd)/(2.0 * a)
return listOf(x1, x2)
}
fun calcWidthFromDp(marginLeftDp:Int, widthDp:Int):List<Int>{
val margin = marginLeftDp.toDouble()
val marginPx = dp2px(marginLeftDp)?:16
val root = root(margin, widthDp.toDouble(), -((px2dp(zis?.resources?.displayMetrics?.widthPixels?:1080))?:400).toDouble())
val numPerRow = root?.let { (it[0]+0.5).toInt()}?:3
val w = ((zis?.resources?.displayMetrics?.widthPixels?:1080)-marginPx*(numPerRow+1))/numPerRow
val totalWidth = ((zis?.resources?.displayMetrics?.widthPixels?:1080)-marginPx)/numPerRow
return listOf(numPerRow, w, totalWidth)
}
}

View File

@@ -0,0 +1,24 @@
package top.fumiama.copymanga.view
import android.annotation.SuppressLint
import android.content.Context
import android.util.AttributeSet
import android.util.Log
import android.webkit.WebView
import top.fumiama.copymanga.web.WebViewClient
import kotlin.reflect.KClass
@SuppressLint("JavascriptInterface")
class JSWebView : WebView {
constructor(context: Context): super(context)
constructor(context: Context, attributeSet: AttributeSet): super(context, attributeSet)
constructor(context: Context, attributeSet: AttributeSet, defSA: Int): super(context, attributeSet, defSA)
constructor(context: Context, UA: String) : super(context) { settings.userAgentString = UA }
init {
settings.javaScriptEnabled = true
settings.domStorageEnabled = true
Log.d("MyJSW", "UA is: ${settings.userAgentString}")
}
fun setWebViewClient(jsFileName: String){webViewClient = WebViewClient(context, jsFileName)}
fun loadJSInterface(obj: Any){addJavascriptInterface(obj, "GM")}
}

View File

@@ -0,0 +1,48 @@
package top.fumiama.copymanga.view
import android.annotation.SuppressLint
import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import android.widget.ScrollView
@SuppressLint("ClickableViewAccessibility")
class LazyScrollView : ScrollView {
private val view: View?
get() = getChildAt(0)
constructor(context: Context?) : super(context)
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context?, attrs: AttributeSet?, defStyle: Int) : super(context, attrs, defStyle)
init {
setOnTouchListener { _, event ->
when (event.action) {
MotionEvent.ACTION_UP -> this.postDelayed({
if (view != null && onScrollListener != null) {
if (onScrollListener != null) {
//Log.d("MyS", "view?.measuredHeight: ${view?.measuredHeight}, scrollY: $scrollY, height: $height")
when {
view?.measuredHeight?:0 <= scrollY + height -> onScrollListener?.onBottom()
scrollY == 0 -> onScrollListener?.onTop()
else -> onScrollListener?.onScroll()
}
}
}
}, 233)
}
false
}
}
/**
* 定义接口
* @author admin
*/
interface OnScrollListener {
fun onBottom()
fun onTop()
fun onScroll()
}
var onScrollListener: OnScrollListener? = null
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,25 @@
package top.fumiama.copymanga.web
import android.util.Log
import android.webkit.JavascriptInterface
import top.fumiama.copymanga.R
import top.fumiama.copymanga.activity.MainActivity.Companion.mh
import top.fumiama.copymanga.activity.MainActivity.Companion.wm
import top.fumiama.copymanga.activity.ViewMangaActivity
class JS {
@JavascriptInterface
fun loadComic(url: String){
val u = when {
url.contains("/details/comic/") -> "${wm?.get()?.getString(R.string.web_comic_detail_pc)}${url.substringAfter("comic")}"
url.contains("/comicContent/") -> "${wm?.get()?.getString(R.string.web_comic_detail_pc)}/${url.substringAfter("comicContent/").substringBefore("/")}/chapter/${url.substringAfterLast("/")}"
else -> ""
}
Log.d("MyJS", "Load comic: $u")
Thread{mh?.obtainMessage(1, u)?.sendToTarget()}.start()
}
@JavascriptInterface
fun hideFab(){
Thread{mh?.sendEmptyMessage(5)}.start()
}
}

View File

@@ -0,0 +1,22 @@
package top.fumiama.copymanga.web
import android.util.Log
import android.webkit.JavascriptInterface
import top.fumiama.copymanga.activity.DlActivity
import top.fumiama.copymanga.activity.MainActivity.Companion.mh
class JSHidden {
@JavascriptInterface
fun loadChapter(listString: String){
Thread{mh?.obtainMessage(2, listString)?.sendToTarget()}.start()
}
@JavascriptInterface
fun setTitle(title:String){
Log.d("MyJSH", "Set title: $title")
DlActivity.comicName = title
}
@JavascriptInterface
fun setFab(content: String){
Thread{mh?.obtainMessage(4, content)?.sendToTarget()}.start()
}
}

View File

@@ -0,0 +1,46 @@
package top.fumiama.copymanga.web
import android.webkit.JsPromptResult
import android.webkit.JsResult
import android.webkit.WebChromeClient
import android.webkit.WebView
import top.fumiama.copymanga.activity.MainActivity.Companion.mh
class WebChromeClient:WebChromeClient() {
override fun onProgressChanged(view: WebView?, newProgress: Int) {
super.onProgressChanged(view, newProgress)
//Log.d("MyWCC", "W progress: $newProgress")
Thread{mh?.obtainMessage(3, newProgress, 0)?.sendToTarget()}.start()
}
override fun onJsAlert(
view: WebView?,
url: String?,
message: String?,
result: JsResult?
): Boolean {
result?.confirm()
return true
}
override fun onJsPrompt(
view: WebView?,
url: String?,
message: String?,
defaultValue: String?,
result: JsPromptResult?
): Boolean {
result?.confirm()
return true
}
override fun onJsConfirm(
view: WebView?,
url: String?,
message: String?,
result: JsResult?
): Boolean {
result?.confirm()
return true
}
}

View File

@@ -0,0 +1,30 @@
package top.fumiama.copymanga.web
import android.content.Context
import android.graphics.Bitmap
import android.util.Log
import android.webkit.WebView
import android.webkit.WebViewClient
import android.widget.Toast
import top.fumiama.copymanga.R
class WebViewClient(private val context: Context, jsFileName: String):WebViewClient() {
private val js = context.assets.open(jsFileName).readBytes().decodeToString()
override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
super.onPageStarted(view, url, favicon)
Log.d("MyWC", "Load URL: $url")
url?.let {
if(!it.startsWith(context.getString(R.string.web_home)) && !it.startsWith(context.getString(R.string.web_home_www))){
view?.goBack()
Toast.makeText(context, R.string.blocked_ad, Toast.LENGTH_SHORT).show()
}
}
}
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
url?.let {
view?.loadUrl(js)
}
}
}

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="#000000"
android:pathData="M565 780l51-55a32 32 0 0 1 45-1 33 33 0 0 1 1 46l-97 104a37 37 0 0 1-53 1l-108-104a33 33 0 0 1-1-46 32 32 0 0 1 45-1L501 774V512c0-18 14-32 32-32s32 15 32 32v268zM512 139c123 0 228 87 259 207C864 347 939 426 939 523c0 98-76 177-170 177-18 0-32-14-32-32 0-18 14-32 32-32 58 0 106-50 106-112 0-62-48-112-106-112-6 0-12 1-17 1-18 3-34-9-37-27C698 281 613 203 512 203c-74 0-141 42-177 108a32 32 0 0 1-30 17 140 140 0 0 0-10-0c-80 0-146 69-146 154 0 85 65 154 146 154 18 0 32 14 32 32 0 18-14 32-32 32C179 700 85 602 85 481c0-118 90-215 203-218C338 186 421 139 512 139z"/>
</vector>

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="#000000"
android:pathData="M896 864a32 32 0 0 1 0 64H128a32 32 0 0 1 0-64z m-60-734l15 15c46 46 46 120 0 166L419 743a160 160 0 0 1-78 43l-152 34c-24 5-45-17-38-40l43-147a160 160 0 0 1 40-68l435-435c46-46 120-46 166 0zM636 254L280 611a96 96 0 0 0-24 41l-28 95 99-22a96 96 0 0 0 47-26L727 345l-91-91z m79-79l-33 33 91 91 33-33a53 53 0 0 0 0-75l-15-15a53 53 0 0 0-75 0z"/>
</vector>

View File

@@ -5,7 +5,7 @@
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:fillColor="@color/colorPrimary"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="64dp"
android:height="64dp"
android:viewportWidth="916"
android:viewportHeight="916">
<path
android:fillColor="@color/colorBlue2"
android:pathData="M477 229c89 3 160 74 181 148 10 34 11 92-6 121-4-1-4-2-6-4 1-13 2-25 3-38-11-67-32-111-77-142-11-6-23-13-34-19-13-5-29-6-39-14-9-17-17-34-26-51 1 0 3-1 4-1zm-21 7h4c6 13 19 21 21 39-12 2-31 4-41 0 5-13 11-26 16-39zm-63 52c2 0 3 1 5 1v4c-13 7-27 13-40 20-34 23-62 61-75 105-7 24-8 57-3 84 2 10 3 20 5 30-3 11-24 34-33 39-1 0-3-1-4-1-5-20-12-41-16-64-15-90 45-161 91-192 21-14 45-15 70-26zm67 0c9 0 18 1 27 1 10 7 27 68 22 86-16 1-27 7-36 14-19 0-40-8-58-14 0-16 13-80 21-85 8-1 16-1 24-2zm-137 85c27-1 54 3 74 11 0 53 0 52-35 71-11-8-58-57-51-75l5-5c2-1 5-1 7-2zm258 0c10 0 20 1 30 1 2 2 3 4 5 6 0 18-35 64-47 70-3 0-7 1-10 1-11-8-21-16-32-24-4-8-7-36-2-44 19-3 37-7 56-10zm-166 14c13 0 25 6 36 11 0 1-1 3-1 4-15 7-29 15-44 22 1-9 3-31 9-37zm91 1c2 0 3 1 5 1v23c-1 0-1 1-2 1-6-2-17-7-20-12-2-4 0-6 2-9 5-1 10-3 15-4zm-45 26c67 0 69 97 5 104-30 3-68-31-53-70 8-21 27-25 48-34zm1 29c-9 6-13 9-20 17-1 18 7 23 19 28 14-1 23-10 24-24-7-11-10-16-23-21zm63 1c3 0 5 1 8 1 7 5 15 11 22 16v4c-7 7-16 19-27 22-1 0-1-1-2-1 0-14-1-28-1-42zm-137 8c2 0 4 1 6 1 1 1 2 1 3 2 0 9 1 18 1 27h-4c-8-5-15-10-23-15 1-2 1-5 2-7 5-3 10-5 15-8zm175 19c13 2 66 52 68 64-6 10-19 36-29 41-15 4-71-16-81-23 0-60 19-51 42-82zm-209 2c13 1 37 16 44 25 6 8 11 44 7 54-3 3-71 28-79 25-4-2-28-41-26-49 3-17 42-42 54-55zm155 42l3 3c-2 9-5 17-7 26-10 0-24-4-29-10 0-1 1-3 1-4 13-2 23-9 32-15zm-93 1c11 1 20 10 25 18-1 2-1 3-2 5-6 2-12 3-18 5-1-1-1-2-2-3-1-8-2-17-3-25zm40 25c14 1 37 11 48 18-1 28-22 67-38 82-15-1-48-67-43-82 7-9 25-10 33-18zm183 3c10 3 21 23 26 31-1 3 0 1-2 3h-45v-1l21-33zm-352 2h6c5 8 18 24 16 35-12 1-35 7-45 0 5-15 15-23 23-35zm323 43c20 0 37 1 50 4v6l-46 46c-42 29-114 56-181 33-17-6-32-15-46-24-16-11-45-26-50-47l1-1c4 1 3 0 5 2 9 7 19 14 28 21 14 6 29 11 43 17 82 25 153-22 196-57z" />
</vector>

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape android:shape="rectangle" >
<!-- 填充的颜色 -->
<solid android:color="@color/colorPrimaryDark" />
<!-- 设置按钮的四个角为弧形 -->
<!-- android:radius 弧形的半径 -->
<corners android:radius="16dip" />
<!-- paddingButton里面的文字与Button边界的间隔 -->
<padding android:bottom="1dp" android:left="1dp" android:right="1dp" android:top="1dp" />
</shape>
</item>
<item android:state_pressed="false">
<shape android:shape="rectangle" >
<!-- 填充的颜色 -->
<solid android:color="@color/colorPrimary" />
<!-- 设置按钮的四个角为弧形 -->
<!-- android:radius 弧形的半径 -->
<corners android:radius="16dip" />
<!-- stroke 设置边框显示 -->
<stroke
android:dashGap="0dp"
android:width="1dp"
android:color="@color/colorAccent" />
<!-- paddingButton里面的文字与Button边界的间隔 -->
<padding android:bottom="1dp" android:left="1dp" android:right="1dp" android:top="1dp" />
</shape>
</item>
</selector>

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape android:shape="rectangle" >
<!-- 填充的颜色 -->
<solid android:color="@color/colorPrimaryDark" />
<!-- 设置按钮的四个角为弧形 -->
<!-- android:radius 弧形的半径 -->
<corners android:radius="16dip" />
<!-- paddingButton里面的文字与Button边界的间隔 -->
<padding android:bottom="1dp" android:left="1dp" android:right="1dp" android:top="1dp" />
</shape>
</item>
<item android:state_pressed="false">
<shape android:shape="rectangle" >
<!-- 填充的颜色 -->
<solid android:color="@color/colorGreen" />
<!-- 设置按钮的四个角为弧形 -->
<!-- android:radius 弧形的半径 -->
<corners android:radius="16dip" />
<!-- stroke 设置边框显示 -->
<stroke
android:dashGap="0dp"
android:width="1dp"
android:color="@color/colorAccent" />
<!-- paddingButton里面的文字与Button边界的间隔 -->
<padding android:bottom="1dp" android:left="1dp" android:right="1dp" android:top="1dp" />
</shape>
</item>
</selector>

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape android:shape="rectangle" >
<!-- 填充的颜色 -->
<solid android:color="@color/colorPrimaryDark" />
<!-- 设置按钮的四个角为弧形 -->
<!-- android:radius 弧形的半径 -->
<corners android:radius="16dip" />
<!-- paddingButton里面的文字与Button边界的间隔 -->
<padding android:bottom="1dp" android:left="1dp" android:right="1dp" android:top="1dp" />
</shape>
</item>
<item android:state_pressed="false">
<shape android:shape="rectangle" >
<!-- 填充的颜色 -->
<solid android:color="@color/colorAccent" />
<!-- 设置按钮的四个角为弧形 -->
<!-- android:radius 弧形的半径 -->
<corners android:radius="16dip" />
<!-- stroke 设置边框显示 -->
<stroke
android:dashGap="0dp"
android:width="1dp"
android:color="@color/colorAccent" />
<!-- paddingButton里面的文字与Button边界的间隔 -->
<padding android:bottom="1dp" android:left="1dp" android:right="1dp" android:top="1dp" />
</shape>
</item>
</selector>

View File

@@ -0,0 +1,13 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval"
android:useLevel="false">
<solid android:color="@color/colorAccent" />
<size
android:width="15dp"
android:height="15dp" />
<padding
android:bottom="1dp"
android:left="2dp"
android:right="2dp"
android:top="1dp" />
</shape>

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape android:shape="rectangle" >
<!-- 填充的颜色 -->
<solid android:color="@color/colorPrimaryDark" />
<!-- 设置按钮的四个角为弧形 -->
<!-- android:radius 弧形的半径 -->
<corners android:radius="16dip" />
<!-- paddingButton里面的文字与Button边界的间隔 -->
<padding android:bottom="1dp" android:left="1dp" android:right="1dp" android:top="1dp" />
</shape>
</item>
<item android:state_pressed="false">
<shape android:shape="rectangle" >
<!-- 填充的颜色 -->
<solid android:color="#FFFFFF" />
<!-- 设置按钮的四个角为弧形 -->
<!-- android:radius 弧形的半径 -->
<corners android:radius="16dip" />
<!-- stroke 设置边框显示 -->
<stroke
android:dashGap="0dp"
android:width="1dp"
android:color="@color/colorAccent" />
<!-- paddingButton里面的文字与Button边界的间隔 -->
<padding android:bottom="1dp" android:left="1dp" android:right="1dp" android:top="1dp" />
</shape>
</item>
</selector>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
 <!--选中样式 -->
 <item android:drawable="@drawable/rndbg" android:state_checked="true"/>
 <!--未选中样式 -->
 <item android:drawable="@drawable/rndbg_white" android:state_checked="false"/>
</selector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 B

View File

@@ -1,30 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

View File

@@ -0,0 +1,49 @@
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FCFCFF">
<include
android:id="@+id/dtitle"
layout="@layout/widget_titlebar"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<top.fumiama.copymanga.view.LazyScrollView
android:id="@+id/dllazys"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/dtitle">
<LinearLayout
android:id="@+id/ldwn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="?android:attr/listDivider"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</LinearLayout>
</top.fumiama.copymanga.view.LazyScrollView>
<include
layout="@layout/widget_downloadbar"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,35 @@
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent">
<top.fumiama.copymanga.view.JSWebView
android:id="@+id/w"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ProgressBar
android:id="@+id/pw"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="0dp"
android:layout_height="2dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/fab"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_marginEnd="24dp"
android:layout_marginBottom="24dp"
android:background="@drawable/rndbg_round"
android:foreground="@drawable/ic_dl"
android:foregroundGravity="center"
android:onClick="onFabClicked"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="@+id/w" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,59 @@
<?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:id="@+id/vcp"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include
android:id="@+id/vone"
layout="@layout/page_imgview"
android:layout_width="0dp"
android:layout_height="0dp"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/vp"
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="horizontal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/onec"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:alpha="0"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<include
android:id="@+id/infcard"
layout="@layout/widget_infodrawer"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<include
android:id="@+id/oneinfo"
layout="@layout/widget_viewmangainfo"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<ToggleButton xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/tbtn"
android:layout_width="64dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:alpha="0.7"
android:background="@drawable/toggle_button"
android:padding="2dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<View xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:id="@+id/divider"
android:layout_height="1dp"
android:background="?android:attr/listDivider" />

View File

@@ -0,0 +1,16 @@
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tcptn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.06"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/ltbtn"
android:layout_width="match_parent"
android:layout_height="64dp"
android:gravity="center_vertical"
android:orientation="horizontal" />

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/onecons"
android:layout_width="match_parent"
android:layout_height="match_parent">
<top.fumiama.copymanga.view.ScaleImageView
android:id="@+id/onei"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,67 @@
<?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"
android:id="@+id/csdwn"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:alpha="0.9">
<androidx.cardview.widget.CardView
android:id="@+id/cdwn"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:layout_marginBottom="16dp"
android:clickable="true"
android:focusable="true"
android:foreground="?android:attr/selectableItemBackground"
app:cardBackgroundColor="@color/colorBlue"
app:cardCornerRadius="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ProgressBar
android:id="@+id/pdwn"
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_toTopOf="@+id/tdwn"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView" />
<TextView
android:id="@+id/tdwn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:text="0/0"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/pdwn" />
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="单击此处下载/暂停 长按此处全选/清空 长按某话删除"
app:layout_constraintBottom_toTopOf="@+id/pdwn"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,95 @@
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.cardview.widget.CardView
android:id="@+id/idc"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:alpha="0.3"
android:background="#FCFCFF"
app:cardCornerRadius="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/idtime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:text="加载中..."
android:textColor="#000000"
app:layout_constraintBottom_toTopOf="@+id/idtbvh"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.0" />
<ToggleButton
android:id="@+id/idtbfullscreen"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:background="@drawable/toggle_button"
android:textOff="全屏开"
android:textOn="全屏关"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/idtbvh"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/idtime"
app:layout_constraintVertical_bias="0.0" />
<ToggleButton
android:id="@+id/idtbvh"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_marginEnd="8dp"
android:background="@drawable/toggle_button"
android:textOff="横向"
android:textOn="竖向"
app:layout_constraintEnd_toStartOf="@id/idtbvp"
app:layout_constraintStart_toEndOf="@+id/idtbfullscreen"
app:layout_constraintTop_toTopOf="@+id/idtbfullscreen" />
<ToggleButton
android:id="@+id/idtbvp"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_marginEnd="8dp"
android:background="@drawable/toggle_button"
android:textOff="动画开"
android:textOn="动画关"
app:layout_constraintEnd_toStartOf="@+id/idtblr"
app:layout_constraintStart_toEndOf="@id/idtbvh"
app:layout_constraintTop_toTopOf="@+id/idtbfullscreen" />
<ToggleButton
android:id="@+id/idtblr"
android:layout_width="0dp"
android:layout_height="48dp"
android:layout_marginEnd="8dp"
android:background="@drawable/toggle_button"
android:textOff="←前 后→"
android:textOn="←后 前→"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/idtbvp"
app:layout_constraintTop_toTopOf="@+id/idtbfullscreen" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,57 @@
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.cardview.widget.CardView
android:layout_width="match_parent"
android:layout_height="48dp"
android:foreground="?android:attr/selectableItemBackground"
app:cardBackgroundColor="#FFFFFF"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:foreground="@drawable/line_colorful"
android:foregroundGravity="fill_horizontal|bottom|center">
<TextView
android:id="@+id/ttitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:gravity="start"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="@+id/ilogo"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/isearch"
android:layout_width="28dp"
android:layout_height="28dp"
android:layout_marginEnd="8dp"
android:src="@drawable/ic_edit"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/ilogo"
android:layout_width="48dp"
android:layout_height="64dp"
android:scaleType="centerCrop"
android:src="@drawable/ic_launcher_foreground"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.cardview.widget.CardView>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,50 @@
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include
android:id="@+id/inftitle"
layout="@layout/widget_titlebar"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="0dp"
android:layout_height="24dp"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
android:alpha="0.8"
android:background="@drawable/rndbg_white"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/inftitle">
<SeekBar
android:id="@+id/infseek"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/inftxtprogress"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/inftxtprogress"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:text="0/0"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/infseek"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -1,5 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@drawable/ic_launcher_background" />
<foreground android:drawable="@drawable/ic_launcher_foreground" />
</adaptive-icon>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

After

Width:  |  Height:  |  Size: 9.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.6 KiB

After

Width:  |  Height:  |  Size: 5.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.8 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -1,6 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#6200EE</color>
<color name="colorPrimaryDark">#3700B3</color>
<color name="colorAccent">#03DAC5</color>
<color name="colorPrimary">#FFCC7F</color>
<color name="colorPrimaryDark">#8F8F8F</color>
<color name="colorAccent">#F6837A</color>
<color name="colorRed">#FFCFCB</color>
<color name="colorGreen">#C9E6A4</color>
<color name="colorBlue">#f2faff</color>
<color name="colorBlue1">#00beff</color>
<color name="colorBlue2">#81caf8</color>
<color name="colorBlue3">#c7eaff</color>
</resources>

View File

@@ -1,3 +1,10 @@
<resources>
<string name="app_name">拷贝漫画</string>
<string name="web_home">https://copymanga.net</string>
<string name="web_home_www">https://www.copymanga.net</string>
<string name="web_comic_detail_pc">https://www.copymanga.net/comic</string>
<string name="pc_ua">Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36 Edg/86.0.622.38</string>
<string name="blocked_ad">已屏蔽其他网站页面</string>
</resources>

View File

@@ -1,10 +1,9 @@
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<style name="AppTheme" parent="android:Theme.DeviceDefault.Light.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="android:colorAccent">@color/colorAccent</item>
<item name="android:statusBarColor">@android:color/white</item>
<item name="android:windowLightStatusBar">true</item>
</style>
</resources>

View File

@@ -8,6 +8,7 @@ buildscript {
dependencies {
classpath "com.android.tools.build:gradle:4.0.2"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath 'com.tencent.mm:AndResGuard-gradle-plugin:1.2.19'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files

View File

@@ -18,4 +18,5 @@ android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official
kotlin.code.style=official
android.enableR8.fullMode=true