1
0
mirror of https://github.com/fumiama/copymanga.git synced 2026-06-05 07:20:23 +08:00

v2.0.beta

This commit is contained in:
fumiama
2021-03-04 22:42:12 +08:00
parent 5bbe768882
commit 84e52e3bf5
180 changed files with 6422 additions and 2062 deletions

View File

@@ -1,9 +1,8 @@
<component name="ProjectDictionaryState">
<dictionary name="rumia">
<words>
<w>cdwn</w>
<w>manga</w>
<w>rmrf</w>
<w>copymanga</w>
<w>fumiama</w>
</words>
</dictionary>
</component>

View File

@@ -21,5 +21,10 @@
<option name="name" value="Google" />
<option name="url" value="https://dl.google.com/dl/android/maven2/" />
</remote-repository>
<remote-repository>
<option name="id" value="maven" />
<option name="name" value="maven" />
<option name="url" value="https://jitpack.io" />
</remote-repository>
</component>
</project>

12
.idea/runConfigurations.xml generated Normal file
View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="RunConfigurationProducerService">
<option name="ignoredProducers">
<set>
<option value="org.jetbrains.plugins.gradle.execution.test.runner.AllInPackageGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestClassGradleConfigurationProducer" />
<option value="org.jetbrains.plugins.gradle.execution.test.runner.TestMethodGradleConfigurationProducer" />
</set>
</option>
</component>
</project>

View File

@@ -1,8 +1,16 @@
# copymanga 拷贝漫画
拷贝漫画的第三方APP优化阅读/下载体验
# 说明
本应用基于官方的`HLML5`手机版本网页端,作者不对其中呈现的任何内容负责
1. 本应用基于官方的`HTML5`手机版本网页端,官方关闭`H5`后,从`2.0`版本开始使用官方`APP``API`作者不对其中呈现的任何内容负责
2. 下载文件为`webp`格式图片,按章节打包为`zip`,可使用本应用或其他漫画阅读应用打开
3. 若想查看下载的漫画是否有错误,可以长按该漫画目录执行查错
4. 下载文件位于`./Android/data/top.fumiama.copymanga/files`目录
# 功能
1. 阅读漫画更改为横屏模式,适于墨水屏阅读
2. 增加下载漫画功能,但是由于不可抗力,下载速度较慢且容易出错,这绝对不是优化的原因,绝对不是
3. 增加阅读下载的漫画的功能,但是无法切换章节,需要手动点选
1. 浏览主页、分类,查看漫画并阅读
2. 下载漫画但是由于不可抗力,下载速度较慢且容易出错,这绝对不是优化的原因,绝对不是
3. 阅读下载的漫画
# 未实现功能
未在上表列出的官方`APP`的其他功能

View File

@@ -4,20 +4,6 @@ apply plugin: 'kotlin-android-extensions'
apply plugin: 'AndResGuard'
android {
compileSdkVersion 30
buildToolsVersion "30.0.2"
defaultConfig {
applicationId "top.fumiama.copymanga"
minSdkVersion 23
targetSdkVersion 30
versionCode 10
versionName '1.3.2'
resConfigs "zh", "zh-rCN"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
signingConfigs {
release {
storeFile file('../../../OneDrive/swc/developer/android_key/open_key')
@@ -27,6 +13,26 @@ android {
v1SigningEnabled true
v2SigningEnabled true
}
/*winrelease {
storeFile file('C:/Users/spayi/OneDrive/swc/developer/android_key/open_key')
storePassword 'fumiama'
keyAlias 'default'
keyPassword 'fumiama'
v1SigningEnabled true
v2SigningEnabled true
}*/
}
compileSdkVersion 30
defaultConfig {
applicationId 'top.fumiama.copymanga'
minSdkVersion 23
targetSdkVersion 30
versionCode 12
versionName '2.0.beta'
resConfigs "zh", "zh-rCN"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
@@ -36,30 +42,60 @@ android {
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.release
}
/*debug{
/*winrelease {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.winrelease
}
debug{
minifyEnabled false
shrinkResources false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
signingConfig signingConfigs.winrelease
}*/
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = '1.8'
}
bundle{
density{
enableSplit = true
}
language{
enableSplit = false
}
}
}
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.2'
//implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'com.google.android.material:material:1.3.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
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'
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.3'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.3'
implementation 'androidx.navigation:navigation-fragment-ktx:2.3.3'
implementation 'androidx.navigation:navigation-ui-ktx:2.3.3'
testImplementation 'junit:junit:4.13.2'
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.afollestad.material-dialogs:input:3.3.0'
implementation 'com.github.yalantis:ucrop:2.2.6'
implementation 'com.to.aboomy:pager2banner:1.0.1'
implementation 'com.github.bumptech.glide:glide:4.12.0'
annotationProcessor 'com.github.bumptech.glide:compiler:4.12.0'
implementation 'com.google.code.gson:gson:2.8.6'
//implementation 'com.liaoinstan.springview:library:1.7.0'
implementation 'com.github.vovaksenov99:OverscrollableScrollView:1.0'
implementation 'com.liaoinstan.springview:library:1.7.0'
implementation 'com.github.zawadz88.materialpopupmenu:material-popup-menu:4.0.1'
}
andResGuard {
@@ -67,14 +103,15 @@ andResGuard {
mappingFile = null
use7zip = true
useSign = true
// 打开这个开关会keep住所有资源的原始路径只混淆资源的名字
// It will keep the origin path of your resources when it's true
keepRoot = false
// 设置这个值会把arsc name列混淆成相同的名字减少string常量池的大小
// If set, name column in arsc those need to proguard will be kept to this value
fixedResName = "arg"
// 打开这个开关会合并所有哈希值相同的资源,但请不要过度依赖这个功能去除去冗余资源
// It will merge the duplicated resources, but don't rely on this feature too much.
// it's always better to remove duplicated resource from repo
mergeDuplicatedRes = true
whiteList = [
// for your icon
// your icon
"R.drawable.icon",
// for fabric
"R.string.com.crashlytics.*",
@@ -92,20 +129,10 @@ andResGuard {
"*.jpg",
"*.jpeg",
"*.gif",
"resources.arsc"
]
sevenzip {
artifact = 'com.tencent.mm:SevenZip:1.2.20'
//path = "/usr/local/bin/7za"
}
/**
* 可选: 如果不设置则会默认覆盖assemble输出的apk
**/
// finalApkBackupPath = "${project.rootDir}/final.apk"
/**
* 可选: 指定v1签名时生成jar文件的摘要算法
* 默认值为“SHA-1”
**/
// digestalg = "SHA-256"
}

View File

@@ -27,10 +27,27 @@
**[] $VALUES;
public *;
}
-keep class com.bumptech.glide.load.data.ParcelFileDescriptorRewinder$InternalRewinder {
-keep class com.bumptech.glide.load.data.ParcelFileDescriptorRewinder.InternalRewinder$** {
*** rewind();
}
-assumenosideeffects class android.util.Log {
public static *** d(...);
public static *** e(...);
public static *** i(...);
public static *** v(...);
public static *** println(...);
public static *** w(...);
public static *** wtf(...);
}
-keep class com.to.aboomy.pager2banner.* {*;}
-keep class androidx.viewpager2.widget.* {*;}
-dontwarn com.yalantis.ucrop**
-keep class com.yalantis.ucrop** { *; }
-keep interface com.yalantis.ucrop** { *; }
##---------------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.
@@ -44,7 +61,7 @@
#-keep class com.google.gson.stream.** { *; }
# Application classes that will be serialized/deserialized over Gson
-keep public class top.fumiama.copymanga.data.* { *; }
-keep class top.fumiama.dmzjxs.json.*{ *; }
# Prevent proguard from stripping interface information from TypeAdapter, TypeAdapterFactory,
# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter)
@@ -59,13 +76,3 @@
}
##---------------End: proguard configuration for Gson ----------
-assumenosideeffects class android.util.Log {
public static *** d(...);
public static *** e(...);
public static *** i(...);
public static *** v(...);
public static *** println(...);
public static *** w(...);
public static *** wtf(...);
}

View File

@@ -1,4 +1,4 @@
package top.fumiama.copymanga
package top.fumiama.dmzj.dmzjxs
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -19,6 +19,6 @@ class ExampleInstrumentedTest {
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("top.fumiama.copymanga", appContext.packageName)
assertEquals("top.fumiama.dmzj.dmzjxs", appContext.packageName)
}
}

View File

@@ -1,23 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="top.fumiama.copymanga">
package="top.fumiama.dmzj.copymanga">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<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"
android:supportsRtl="true"
android:theme="@style/AppTheme" >
<activity android:name=".activity.MainActivity">
android:theme="@style/AppTheme"
android:usesCleartextTraffic="true">
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="top.fumiama.copymanga.fileprovider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />
</provider>
<activity
android:name="top.fumiama.copymanga.MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
<action android:name="top.fumiama.copymanga.SHARE"/>
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<activity android:name=".activity.ViewMangaActivity"/>
<activity android:name=".activity.DlActivity"/>
<activity android:name=".activity.DlListActivity"/>
<activity
android:name="top.fumiama.copymanga.LoginActivity"
android:theme="@style/AppTheme.NoActionBar"/>
<activity
android:name="com.yalantis.ucrop.UCropActivity"
android:screenOrientation="portrait"
android:theme="@style/Theme.AppCompat.Light.NoActionBar"/>
<activity
android:name="top.fumiama.copymanga.ui.vm.ViewMangaActivity"
android:theme="@style/AppTheme.NoActionBar"/>
</application>
</manifest>

View File

@@ -1,45 +0,0 @@
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();

View File

@@ -1,47 +0,0 @@
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);
else if (url.indexOf("/personal") > 0) GM.enterProfile();
}
modify();
invoke.urlChangeListener(modify);
} else {
setTimeout(modify, 1280);
}

View File

@@ -0,0 +1,12 @@
package top.fumiama.copymanga
import android.app.Activity
import android.os.Bundle
import top.fumiama.dmzj.copymanga.R
class LoginActivity:Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_login)
}
}

View File

@@ -0,0 +1,263 @@
package top.fumiama.copymanga
import android.Manifest
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.MediaStore
import android.util.Log
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.TextView
import android.widget.Toast
import androidx.appcompat.app.AlertDialog
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.net.toUri
import androidx.drawerlayout.widget.DrawerLayout
import androidx.navigation.findNavController
import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.navigateUp
import androidx.navigation.ui.setupActionBarWithNavController
import androidx.navigation.ui.setupWithNavController
import com.afollestad.materialdialogs.MaterialDialog
import com.afollestad.materialdialogs.input.input
import com.yalantis.ucrop.UCrop
import kotlinx.android.synthetic.main.activity_main.*
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.ui.download.DownloadFragment
import java.io.File
import java.io.FileInputStream
import java.lang.ref.WeakReference
class MainActivity : AppCompatActivity() {
var isDrawerClosed = true
var menuMain: Menu? = null
private lateinit var appBarConfiguration: AppBarConfiguration
private lateinit var p: PropertiesTools
private lateinit var headPic: File
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
//translucentStatusBar()
coordiv.layoutParams.height = getStatusBarHeight()
val navController = findNavController(R.id.nav_host_fragment)
appBarConfiguration = AppBarConfiguration(
setOf(
R.id.nav_home,
R.id.nav_sort,
R.id.nav_rank,
R.id.nav_sub,
R.id.nav_history,
R.id.nav_download,
R.id.nav_settings
), drawer_layout
)
setupActionBarWithNavController(navController, appBarConfiguration)
nav_view.setupWithNavController(navController)
p = PropertiesTools(File(filesDir, "database.prop"))
headPic = File(getExternalFilesDir(""), "headPic")
mainWeakReference = WeakReference(this)
drawer_layout.addDrawerListener(object : DrawerLayout.DrawerListener {
override fun onDrawerClosed(drawerView: View) {
Log.d("MyMain", "onDrawerClosed")
isDrawerClosed = true
}
override fun onDrawerOpened(drawerView: View) {
Log.d("MyMain", "onDrawerOpened")
isDrawerClosed = false
DownloadFragment.currentDir = getExternalFilesDir("")
}
override fun onDrawerSlide(drawerView: View, slideOffset: Float) {}
override fun onDrawerStateChanged(newState: Int) {}
})
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
// Inflate the menu; this adds items to the action bar if it is present.
menuInflater.inflate(R.menu.main, menu)
menuMain = menu
return true
}
override fun onSupportNavigateUp(): Boolean {
val navController = findNavController(R.id.nav_host_fragment)
checkHeadPicture()
if (headPic.exists()) navhbg.setOnLongClickListener {
if (headPic.exists()) {
val dl = AlertDialog.Builder(this)
dl.setMessage(R.string.clearHeadImgMsg)
dl.setPositiveButton(android.R.string.ok) { _, _ ->
if (headPic.exists()) headPic.delete()
navhbg.setImageResource(R.drawable.illust_57793944_20190427_134853)
}
dl.show()
}
true
}
p["navTextInfo"].let { if (it != "null") navtinfo.text = it }
return navController.navigateUp(appBarConfiguration) || super.onSupportNavigateUp()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == RESULT_OK) when (requestCode) {
UCrop.REQUEST_CROP -> {
val fi = headPic.inputStream()
navhbg.setImageBitmap(BitmapFactory.decodeStream(fi))
fi.close()
}
1 -> {
data?.data?.let {
saveFile(it)
cropImageUri()
}
}
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String?>,
grantResults: IntArray
) {
when (requestCode) {
1 -> {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) pickPicture()
else Toast.makeText(this, R.string.permissionDenied, Toast.LENGTH_SHORT).show()
}
}
}
private fun checkReadPermission(): Boolean {
return if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N && ContextCompat.checkSelfPermission(
this,
Manifest.permission.READ_EXTERNAL_STORAGE
) != PackageManager.PERMISSION_GRANTED
) {
ActivityCompat.requestPermissions(
this,
arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), 1
)
false
} else true
}
private fun pickPicture() {
val i = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
i.type = "image/*"
startActivityForResult(i, 1)
}
private fun saveFile(uri: Uri) {
//val f = File(getExternalFilesDir(""), "headPic")
val fd = contentResolver.openFileDescriptor(uri, "r")
fd?.fileDescriptor?.let {
val fi = FileInputStream(it)
val fo = headPic.outputStream()
fi.copyTo(fo)
fi.close()
fo.close()
}
fd?.close()
}
private fun checkHeadPicture() {
//val hp = File(getExternalFilesDir(""), "headPic")
if (headPic.exists()) navhbg.setImageURI(headPic.toUri())
}
private fun cropImageUri() {
val op = UCrop.Options()
val r = navhbg.width.toFloat() / navhbg.height.toFloat()
Log.d("MyMain", "Img info: (${navhbg.width}, ${navhbg.height})")
Log.d("MyMain", "Result code: ${UCrop.REQUEST_CROP}")
op.setCompressionFormat(Bitmap.CompressFormat.WEBP)
op.setStatusBarColor(resources.getColor(R.color.colorPrimaryDark, theme))
op.setToolbarColor(resources.getColor(R.color.colorPrimary, theme))
op.setActiveControlsWidgetColor(resources.getColor(R.color.colorAccent, theme))
UCrop.of(headPic.toUri(), headPic.toUri())
.withAspectRatio(r, 1F)
.withMaxResultSize(navhbg.width, navhbg.height)
.withOptions(op)
.start(this)
}
/*private fun translucentStatusBar() {
//添加Flag把状态栏设为可绘制模式
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
//如果为全透明模式取消设置Window半透明的Flag
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
//设置状态栏为透明
window.statusBarColor = Color.TRANSPARENT
//设置window的状态栏不可见
window.decorView.systemUiVisibility =
View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
//view不根据系统窗口来调整自己的布局
val mContentView: ViewGroup = window.findViewById(Window.ID_ANDROID_CONTENT) as ViewGroup
val mChildView: View = mContentView.getChildAt(0)
ViewCompat.requestApplyInsets(mChildView)
coordiv.layoutParams.height = getStatusBarHeight()
}*/
private fun getStatusBarHeight() =
resources.getDimensionPixelOffset(
resources.getIdentifier(
"status_bar_height",
"dimen",
"android"
)
)
fun showAbout(item: MenuItem) {
val dl = android.app.AlertDialog.Builder(this)
dl.setMessage(R.string.app_description)
dl.setTitle(R.string.action_info)
dl.setIcon(R.mipmap.ic_launcher)
dl.setPositiveButton(android.R.string.ok) { _, _ -> }
dl.show()
}
fun onNavTInfoClicked(it: View) {
MaterialDialog(this).show {
input(prefill = (it as TextView).text) { _, charSequence ->
it.text = charSequence
p["navTextInfo"] = charSequence.toString()
}
positiveButton(android.R.string.ok)
title(R.string.navTextInfoInputHint)
}
}
fun onNavHBgClicked(v: View) {
if (checkReadPermission()) pickPicture()
}
fun startLoginActivity(v: View){
startActivity(Intent(this, LoginActivity::class.java))
}
companion object{
var mainWeakReference: WeakReference<MainActivity>? = null
}
}

View File

@@ -1,332 +0,0 @@
package top.fumiama.copymanga.activity
import android.animation.ObjectAnimator
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.view.ViewGroup
import android.view.ViewTreeObserver
import android.widget.Toast
import android.widget.ToggleButton
import com.google.gson.Gson
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.data.ComicStructure
import top.fumiama.copymanga.handler.DlHandler
import top.fumiama.copymanga.tool.MangaDlTools
import top.fumiama.copymanga.tool.MangaDlTools.Companion.wmdlt
import top.fumiama.copymanga.tool.ToolsBox
import top.fumiama.copymanga.view.ChapterToggleButton
import top.fumiama.copymanga.view.LazyScrollView
import java.io.File
import java.lang.Thread.sleep
import java.lang.ref.WeakReference
import java.util.zip.ZipFile
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: Array<ChapterToggleButton> = arrayOf()
private val handler = DlHandler(this, Looper.myLooper()!!)
private var btnw = 0
private var cdwnWidth = 0
private var canDl = false
private lateinit var toolsBox: ToolsBox
private lateinit var mangaDlTools: MangaDlTools
var multiSelect = false
@ExperimentalStdlibApi
@SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_dl)
mh?.saveUrlsOnly = true
mangaDlTools = MangaDlTools(this)
handler.sendEmptyMessage(-2) //setLayouts
}
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, "translationX", cdwnWidth.toFloat() * 0.9f, 0f).setDuration(
233
).start()
}
private fun hideDlCard(){
//ObjectAnimator.ofFloat(csdwn, "alpha", 0.9f, 0.3f).setDuration(233).start()
ObjectAnimator.ofFloat(csdwn, "translationX", 0f, cdwnWidth.toFloat() * 0.9f).setDuration(
233
).start()
}
private fun fillChapters() {
mangaDlTools.allocateChapterUrls(checkedChapter)
for (i in tbtnlist) {
if (i.isChecked) mangaDlTools.dlChapterUrl(i.url.toString())
}
}
private fun dlThread(dlMethod: (i: ChapterToggleButton) -> Unit) {
sleep(10000)
for (i in tbtnlist) {
if (i.isChecked) dlMethod(i)
if (!canDl) {
checkedChapter -= dldChapter
dldChapter = 0
break
}
}
if (canDl) {
haveDlStarted = false
canDl = false
}
handler.sendEmptyMessage(8) //set dl card color to blue
}
@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() {
cdwnWidth = csdwn.width
csdwn.viewTreeObserver.removeOnGlobalLayoutListener(this)
}
})
dllazys.onScrollListener = object : LazyScrollView.OnScrollListener {
override fun onBottom() {}
override fun onScroll() { if (csdwn.translationX == 0f) hideDlCard() }
override fun onTop() {}
}
cdwn.setOnClickListener {
if (csdwn.translationX != 0f) showDlCard()
else if (checkedChapter == 0) hideDlCard()
else {
pdwn.progress = 0
if (canDl || checkedChapter == 0) canDl = false
else {
haveDlStarted = true
canDl = true
handler.sendEmptyMessage(9) //set dl card color to red
Toast.makeText(this, "十秒后开始下载...", Toast.LENGTH_SHORT).show()
fillChapters()
Thread { dlThread { downloadChapterPages(it) } }.start()
}
}
}
cdwn.setOnLongClickListener {
handler.sendEmptyMessage(4)
return@setOnLongClickListener true
}
isearch.setOnClickListener { showMultiSelectInfo() }
analyzeStructure()
}
private fun showMultiSelectInfo() {
toolsBox.buildInfo("进入多选模式?", "确定后,长按下载条可选中全部漫画,而不仅限于未下载者;点击已下载漫画可进行选择。",
"确定", null, "取消", { multiSelect = true })
}
private fun analyzeStructure() {
ViewMangaActivity.zipList = arrayOf()
Gson().fromJson(json?.reader(), Array<ComicStructure>::class.java)?.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)
}
}
val mangaHome = File("${getExternalFilesDir("")}/$comicName")
val jsonFile = File(mangaHome, "info.bin")
if(!mangaHome.exists()) mangaHome.mkdirs()
if(!(jsonFile.exists() && intent.getBooleanExtra("callFromDlList", false))) json?.let { jsonFile.writeText(it) }
}
@ExperimentalStdlibApi
private fun downloadChapterPages(i: ChapterToggleButton) {
mangaDlTools.onDownloadedListener =
object : MangaDlTools.OnDownloadedListener {
override fun handleMessage(succeed: Boolean) {
handler.obtainMessage(if (succeed) 1 else -1, i.index, 0)
.sendToTarget()
}
override fun handleMessage(succeed: Boolean, pageNow: Int) {
handler.obtainMessage(
5,
i.index,
pageNow,
succeed
).sendToTarget()
}
override fun handleMessage(pageNow: Int){
handler.obtainMessage(
10,
i.index,
pageNow
).sendToTarget()
}
}
i.hash?.let {
mangaDlTools.dlChapterAndPackIntoZip(
File("${getExternalFilesDir("")}/$comicName/${i.hint}/${i.textOn}.zip"),
it
)
}
}
@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)
tbv.tbtn.index = tbtnlist.size
tbtnlist += tbv.tbtn
tbv.tbtn.url = url
tbtncnt++
val zipPosition = ViewMangaActivity.zipList?.size
ViewMangaActivity.zipList = ViewMangaActivity.zipList?.plus("$title.zip")
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
tbv.tbtn.freezesText = true
}
ltbtn.ltbtn.addView(tbv)
ltbtn.invalidate()
tbv.tbtn.setOnClickListener {
val normalAct = (multiSelect && zipf.exists()) || !zipf.exists()
if (zipf.exists() && !it.tbtn.isChecked) it.tbtn.setBackgroundResource(R.drawable.rndbg_checked)
else if(normalAct) it.tbtn.setBackgroundResource(R.drawable.toggle_button)
if(normalAct){
if (it.tbtn.isChecked) tdwn.text = "$dldChapter/${++checkedChapter}"
else tdwn.text = "$dldChapter/${--checkedChapter}"
}else if(it.tbtn.isChecked){
it.tbtn.isChecked = false
zipPosition?.let { callVM(title, zipf, it) }
}
}
tbv.tbtn.setOnLongClickListener {
if (zipf.exists()) {
toolsBox.buildInfo("确认删除这些章节?",
"该操作将不可撤销",
"确定",
null,
"取消",
{
if (checkedChapter == 0) {
it.tbtn.isChecked = true
tdwn.text = "$dldChapter/${++checkedChapter}"
}
handler.sendEmptyMessage(7)
})
}
true
}
}
fun deleteChapters() {
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 callVM(titleText: String, zipFile: File, zipPosition:Int){
ViewMangaActivity.titleText = titleText
ViewMangaActivity.zipFile = zipFile
//ViewMangaActivity.zipList = zipArrayList
ViewMangaActivity.zipPosition = zipPosition
ViewMangaActivity.cd = zipFile.parentFile
startActivity(Intent(this, ViewMangaActivity::class.java))
}
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"
var json: String? = null
}
}

View File

@@ -1,174 +0,0 @@
package top.fumiama.copymanga.activity
import android.app.Activity
import android.app.AlertDialog
import android.app.Dialog
import android.content.Intent
import android.os.Bundle
import android.os.Looper
import android.util.Log
import android.widget.ArrayAdapter
import android.widget.Toast
import kotlinx.android.synthetic.main.activity_dlist.*
import kotlinx.android.synthetic.main.widget_titlebar.*
import top.fumiama.copymanga.R
import top.fumiama.copymanga.handler.DlLHandler
import java.io.File
import java.util.regex.Pattern
import java.util.zip.ZipInputStream
class DlListActivity:Activity() {
private var nullZipDirStr = emptyArray<String>()
private var handler: DlLHandler? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_dlist)
ttitle.text = intent.getStringExtra("title")
handler = DlLHandler(Looper.myLooper()!!, this)
handler?.obtainMessage(3, currentDir)?.sendToTarget() //call scanFile
}
fun scanFile(cd: File?){
val isRoot = cd == getExternalFilesDir("")
val jsonFile = File(cd, "info.bin")
if(isRoot || !jsonFile.exists()) cd?.list()?.sortedArrayWith { o1, o2 ->
if(o1.endsWith(".zip") && o2.endsWith(".zip")) (10000*getFloat(o1) - 10000*getFloat(o2) + 0.5).toInt()
else o1[0] - o2[0]
}?.let {
mylv.adapter = ArrayAdapter(this, android.R.layout.simple_list_item_1, it)
mylv.setOnItemClickListener { _, _, position, _ ->
val chosenFile = File(cd, it[position])
val chosenJson = File(chosenFile, "info.bin")
//Toast.makeText(this, "进入$chosenFile", Toast.LENGTH_SHORT).show()
when {
chosenJson.exists() -> callDownloadActivity(chosenJson)
chosenFile.isDirectory -> {
currentDir = chosenFile
startActivity(
Intent(
this,
DlListActivity::class.java
).putExtra("title", it[position])
)
}
chosenFile.name.endsWith(".zip") -> {
Toast.makeText(this, "加载中...", Toast.LENGTH_SHORT).show()
ViewMangaActivity.zipFile = chosenFile
ViewMangaActivity.titleText = it[position]
ViewMangaActivity.zipPosition = position
ViewMangaActivity.zipList = it as Array<String>
ViewMangaActivity.cd = cd
startActivity(Intent(this, ViewMangaActivity::class.java))
}
}
}
mylv.setOnItemLongClickListener { _, _, position, _ ->
val chosenFile = File(cd, it[position])
AlertDialog.Builder(this)
.setIcon(R.drawable.ic_launcher_foreground).setMessage("在此执行删除/查错?")
.setTitle("提示").setPositiveButton("删除"){ _, _ ->
if(chosenFile.exists()) handler?.obtainMessage(2, chosenFile)?.sendToTarget() //call rmrf
handler?.obtainMessage(3, cd)?.sendToTarget() //call scanFile
}.setNegativeButton(android.R.string.cancel){_, _ ->}
.setNeutralButton("查错"){_, _ -> handler?.obtainMessage(1, chosenFile)?.sendToTarget()} //call checkDir
.show()
true
}
}
}
fun rmrf(f: File) {
if (f.isDirectory) f.listFiles()?.let {
for (i in it)
if (i.isDirectory) rmrf(i)
else i.delete()
}
f.delete()
}
fun checkDir(f: File){
nullZipDirStr = emptyArray()
findNullWebpZipFileInDir(f)
if(nullZipDirStr.isNotEmpty()) showErrorZip(nullZipDirStr.joinToString("\n"))
else Toast.makeText(this, "未发现错误", Toast.LENGTH_SHORT).show()
}
private fun callDownloadActivity(jsonFile: File){
DlActivity.json = jsonFile.readText()
DlActivity.comicName = jsonFile.parentFile?.name?:"Null"
startActivity(
Intent(this, DlActivity::class.java)
.putExtra("callFromDlList", true)
)
}
private fun findNullWebpZipFileInDir(f: File){
if (f.isDirectory) f.listFiles()?.let {
for (i in it)
if (i.isDirectory) findNullWebpZipFileInDir(i)
else if(!checkZip(i)) nullZipDirStr += i.path.substringAfterLast(getExternalFilesDir("").toString())
}
}
private fun checkZip(f: File): Boolean{
return try {
val exist = f.exists()
if (!exist) true
else {
var re = true
val zip = ZipInputStream(f.inputStream().buffered())
var entry = zip.nextEntry
while (entry != null) {
if (!entry.isDirectory){
if(zip.read() == -1 && entry.size == 0L){
re = false
break
}
}
entry = zip.nextEntry
}
zip.closeEntry()
zip.close()
re
}
} catch (e: Exception) {
Toast.makeText(this, "读取${f.name}错误!", Toast.LENGTH_SHORT).show()
true
}
}
private fun showErrorZip(msg: CharSequence) = AlertDialog.Builder(this)
.setIcon(R.drawable.ic_launcher_foreground)
.setTitle("找到以下错误文件,是否删除?")
.setMessage(msg)
.setPositiveButton(android.R.string.ok){_, _ -> deleteErrorZip()}
.setNegativeButton(android.R.string.cancel){_, _ ->}
.show()
private fun deleteErrorZip(){
val exf = getExternalFilesDir("")
for(i in nullZipDirStr){
val f = File(exf, i)
if(f.exists()) f.delete()
}
}
private fun getFloat(oldString: String): Float {
val newString = StringBuffer()
var matcher = Pattern.compile("\\d+.+\\d+").matcher(oldString)
while (matcher.find()) newString.append(matcher.group())
//Log.d("MyDLL1", newString.toString())
if(newString.isEmpty()){
matcher = Pattern.compile("\\d").matcher(oldString)
while (matcher.find()) newString.append(matcher.group())
}
//Log.d("MyDLL2", newString.toString().toFloat().toString())
return if(newString.isEmpty()) 0f else newString.toString().toFloat()
}
companion object{
var currentDir: File? = null
}
}

View File

@@ -1,65 +0,0 @@
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.tool.ToolsBox
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
var toolsBox: ToolsBox? = null
@SuppressLint("JavascriptInterface")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
wm = WeakReference(this)
mh = MainHandler(Looper.myLooper()!!)
toolsBox = ToolsBox(wm as WeakReference<Any>)
toolsBox?.netinfo?.let {
if(it == "无网络" || it == "错误"){
Thread{mh?.sendEmptyMessage(6)}.start()
}else{
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){
DlListActivity.currentDir = getExternalFilesDir("")
startActivity(
Intent(this, (if(mh?.showDlList == true) DlListActivity::class else DlActivity::class).java)
.putExtra("title", "我的下载")
)
}
companion object{
var wm: WeakReference<MainActivity>? = null
var mh: MainHandler? = null
}
}

View File

@@ -1,444 +0,0 @@
package top.fumiama.copymanga.activity
import android.animation.ObjectAnimator
import android.annotation.SuppressLint
import android.app.Activity
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.os.*
import android.util.Log
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.*
import java.util.zip.ZipFile
import java.util.zip.ZipInputStream
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
private var mangaZip = zipFile
val dlZip2View = mangaZip != null
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
//isearch.visibility = View.VISIBLE
Log.d("MyVM", "dlZip2View: $dlZip2View, mangaZip: $mangaZip")
if(dlZip2View && mangaZip?.exists() != true) toolsBox.toastError("已经到头了~")
else {
try {
count = if (dlZip2View) countZipItems() else imgUrls.size
} catch (e: Exception) {
e.printStackTrace()
toolsBox.toastError("分析图片url错误")
}
try {
prepareItems()
if(pn > 0) {
pageNum = pn
pn = -1
}else if(pn == -2){
pageNum = count
pn = -1
}
} 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
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) window.setDecorFitsSystemWindows(false)
}
}
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 getImgBitmap(position: Int): Bitmap? {
if (position >= count || position < 0) return null
else {
val zip = ZipFile(mangaZip)
//if (q == 100)
return BitmapFactory.decodeStream(zip.getInputStream(zip.getEntry("${position}.webp")))
/*else {
val out = ByteArrayOutputStream()
try {
BitmapFactory.decodeStream(zip.getInputStream(zip.getEntry("${position}.jpg")))
} catch (e: Exception) {
e.printStackTrace()
return null
}?.compress(Bitmap.CompressFormat.JPEG, q, out)
return BitmapFactory.decodeStream(ByteArrayInputStream(out.toByteArray()))
}*/
}
}
private fun loadOneImg() {
if(dlZip2View) onei.setImageBitmap(getImgBitmap(currentItem))
else Glide.with(this@ViewMangaActivity)
.load(imgUrls[currentItem])
.placeholder(R.drawable.ic_dl)
.dontAnimate()
.into(onei)
updateSeekBar()
}
private fun setIdPosition(position: Int) {
infoDrawerDelta = position.toFloat()
infcard.translationY = infoDrawerDelta
}
@SuppressLint("SetTextI18n")
private fun prepareItems() {
prepareVP()
prepareInfoBar(count)
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, "下次浏览生效", Toast.LENGTH_SHORT).show()
}
}
private fun prepareIdBtVP() {
idtbvp.isChecked = notUseVP
idtbvp.setOnClickListener {
if (idtbvp.isChecked) p["noAnimation"] = "true"
else p["noAnimation"] = "false"
Toast.makeText(this, "下次浏览生效", 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, "下次浏览生效", Toast.LENGTH_SHORT).show()
}
}
private fun countZipItems(): Int {
var c = 0
try {
val exist = mangaZip?.exists() == true
if (!exist) return 0
else {
Log.d("Myvm", "zipf: $mangaZip")
val zip = ZipInputStream(mangaZip?.inputStream()?.buffered())
var entry = zip.nextEntry
while (entry != null) {
if (!entry.isDirectory) c++
entry = zip.nextEntry
}
zip.closeEntry()
zip.close()
}
} catch (e: Exception) {
toolsBox.toastError("读取zip错误!")
}
return c
}
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
if(dlZip2View) getImgBitmap(pos)?.let {
//Glide.with(this@ViewMangaActivity).load(it).placeholder(R.drawable.bg_comment).into(holder.itemView.onei)
holder.itemView.onei.setImageBitmap(it)
}
else Glide.with(this@ViewMangaActivity).load(imgUrls[pos]).placeholder(R.drawable.ic_dl).dontAnimate().timeout(10000).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(Looper.myLooper()!!) {
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 zipFile: File? = null
get() {
val re = field
if(field != null) field = null
return re
}
var titleText = "Null"
var nextChapterUrl: String? = null
var previousChapterUrl: String? = null
var zipPosition = 0
var zipList: Array<String>? = null
var cd: File? = null
var pn = -1
}
}

View File

@@ -1,97 +0,0 @@
package top.fumiama.copymanga.handler
import android.annotation.SuppressLint
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.widget.Toast
import android.widget.ToggleButton
import kotlinx.android.synthetic.main.widget_downloadbar.*
import top.fumiama.copymanga.R
import top.fumiama.copymanga.activity.DlActivity
import top.fumiama.copymanga.tool.MangaDlTools.Companion.wmdlt
import java.lang.ref.WeakReference
class DlHandler(activity: DlActivity, looper: Looper) : Handler(looper) {
private val da = WeakReference(activity)
private val d
get() = da.get()
private var size = 0
private var refreshSize = true
@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?.get(msg.arg1)?.textOn}失败", Toast.LENGTH_SHORT).show()
d?.updateProgressBar()
}
4 -> {
d?.pdwn?.progress = 0
val selectDownloaded = d?.multiSelect?:false
if (d?.haveSElectAll == true) {
d?.tbtnlist?.forEach { i ->
if(i.freezesText) i.setBackgroundResource(R.drawable.rndbg_checked) else i.setBackgroundResource(R.drawable.toggle_button)
i.isChecked = false
}
d?.haveSElectAll = false
d?.checkedChapter = 0
d?.dldChapter = 0
} else {
d?.let {
val checkBtn = { i: ToggleButton, it: DlActivity ->
i.setBackgroundResource(R.drawable.toggle_button)
i.isChecked = true
it.checkedChapter++
}
for (i in it.tbtnlist) {
if(selectDownloaded) checkBtn(i, it)
else if(!i.freezesText) checkBtn(i, it)
}
}
d?.haveSElectAll = true
}
d?.tdwn?.text = "${d?.dldChapter}/${d?.checkedChapter}"
}
5 -> {
setSize(msg.arg2, msg.arg1)
d?.updateProgressBar(msg.arg2, size)
if (!(msg.obj as Boolean)) {
Toast.makeText(d, "下载${d?.tbtnlist?.get(msg.arg1)?.textOn}的第${msg.arg2}页失败", Toast.LENGTH_SHORT).show()
}else{
val progressTxt = d?.tdwn?.text.toString()
d?.tdwn?.text = "${progressTxt.substringBefore(' ')}${msg.arg2}/${size}"
}
}
6 -> d?.tdwn?.text = "${d?.dldChapter}/${d?.checkedChapter}"
7 -> d?.deleteChapters()
8 -> d?.resources?.getColor(R.color.colorBlue)?.let { d?.cdwn?.setCardBackgroundColor(it) }
9 -> d?.resources?.getColor(R.color.colorRed)?.let { d?.cdwn?.setCardBackgroundColor(it) }
10 -> Toast.makeText(d, "下载${d?.tbtnlist?.get(msg.arg1)?.textOn}的第${msg.arg2}页失败,尝试重新下载...", Toast.LENGTH_SHORT).show()
}
}
private fun setSize(pageNow: Int, tbtnNo: Int){
if(refreshSize || size == 0) {
size = d?.tbtnlist?.get(tbtnNo)?.hash?.let { wmdlt?.get()?.getImgsCountByHash(it) }?:0
refreshSize = false
}else if(pageNow == size) refreshSize = true
}
}

View File

@@ -1,67 +0,0 @@
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.view.View
import kotlinx.android.synthetic.main.activity_main.*
import top.fumiama.copymanga.activity.DlActivity.Companion.json
import top.fumiama.copymanga.activity.MainActivity.Companion.wm
import top.fumiama.copymanga.activity.ViewMangaActivity
import top.fumiama.copymanga.tool.MangaDlTools.Companion.wmdlt
class MainHandler(looper: Looper):Handler(looper) {
var saveUrlsOnly = false
var showDlList = 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()
6 -> setFab2DlList()
}
}
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 showFab() {wm?.get()?.fab?.visibility = View.VISIBLE}
private fun hideFab() {wm?.get()?.fab?.visibility = View.GONE}
private fun setFab(content: String){
//Log.d("MyMH", "Get chapter json: $content")
showDlList = false
json = content
showFab()
}
private fun setFab2DlList(){
showDlList = true
showFab()
}
}

View File

@@ -0,0 +1,11 @@
package top.fumiama.copymanga.json;
import java.util.HashMap;
public class BookInfoStructure extends ReturnBase {
public Results results;
public static class Results extends ResultsBase{
public ComicStructure comic;
public HashMap<String, ThemeStructure> groups;
}
}

View File

@@ -0,0 +1,11 @@
package top.fumiama.copymanga.json;
public class BookListStructure extends ReturnBase {
public Results results;
public static class Results {
public int total;
public ComicStructure[] list;
public int limit;
public int offset;
}
}

View File

@@ -0,0 +1,9 @@
package top.fumiama.copymanga.json;
public class Chapter2Return extends ReturnBase {
public Results results;
public static class Results extends ResultsBase{
public ComicStructure comic;
public ChapterWithContent chapter;
}
}

View File

@@ -0,0 +1,18 @@
package top.fumiama.copymanga.json;
public class ChapterStructure {
public int index;
public String uuid;
public int count;
public int size;
public String name;
public String comic_id;
public String comic_path_word;
public String group_id;
public String group_path_word;
public int type;
public int img_type;
public String datetime_created;
public String prev;
public String next;
}

View File

@@ -0,0 +1,7 @@
package top.fumiama.copymanga.json;
public class ChapterWithContent extends ChapterStructure {
public UUIDUrlPair[] contents;
public int[] words;
public Boolean is_long;
}

View File

@@ -0,0 +1,16 @@
package top.fumiama.copymanga.json;
public class ComicStructure {
public String uuid;
public String name;
public String alias;
public ValueDisplayPair status;
public ThemeStructure[] theme;
public String path_word;
public ThemeStructure[] author;
public int img_type;
public String brief;
public String datetime_updated;
public String cover;
public int popular;
}

View File

@@ -1,6 +1,6 @@
package top.fumiama.copymanga.data;
package top.fumiama.copymanga.json;
public class ComicStructure {
public class ComicStructureOld {
public String name;
public Chapters[] chapters;
public static class Chapters{

View File

@@ -0,0 +1,8 @@
package top.fumiama.copymanga.json;
public class FilterStructure extends ReturnBase {
public Results results;
public static class Results{
public ThemeStructure[] theme;
}
}

View File

@@ -0,0 +1,58 @@
package top.fumiama.copymanga.json;
public class IndexStructure extends ReturnBase {
public Results results;
public static class Results{
public Banners[] banners;
public Topics topics;
public RecComics recComics;
public RankComics rankDayComics;
public RankComics rankWeekComics;
public RankComics rankMonthComics;
public ComicWrap[] hotComics;
public ComicWrap[] newComics;
public FinishComics finishComics;
public static class Banners{
public String cover;
public String brief;
public String out_uuid;
public ComicStructure comic;
}
public static class Topics extends InfoBase{
public List[] list;
public static class List{
public String title;
public SeriesStructure series;
public String journal;
public String cover;
public String period;
public int type;
public String brief;
public String path_word;
public String datetime_created;
}
}
public static class RecComics extends InfoBase{
public List[] list;
public static class List{
public int type;
public ComicStructure comic;
}
}
public static class RankComics extends InfoBase{
public InfoStructure[] list;
}
public static class ComicWrap{
public ComicStructure comic;
}
public static class FinishComics extends InfoBase{
public ComicStructure[] list;
public String path_word;
public String name;
public String type;
}
}
}

View File

@@ -0,0 +1,7 @@
package top.fumiama.copymanga.json;
public class InfoBase {
public int total;
public int limit;
public int offset;
}

View File

@@ -0,0 +1,11 @@
package top.fumiama.copymanga.json;
public class InfoStructure {
public int sort;
public int sort_last;
public int rise_sort;
public int rise_num;
public int date_type;
public int popular;
public ComicStructure comic;
}

View File

@@ -0,0 +1,8 @@
package top.fumiama.copymanga.json;
public class ResultsBase {
public Boolean is_lock;
public Boolean is_login;
public Boolean is_mobile_bind;
public Boolean is_vip;
}

View File

@@ -0,0 +1,6 @@
package top.fumiama.copymanga.json;
public class ReturnBase {
public int code;
public String message;
}

View File

@@ -0,0 +1,5 @@
package top.fumiama.copymanga.json;
public class SeriesStructure extends ThemeStructure {
public String color;
}

View File

@@ -0,0 +1,6 @@
package top.fumiama.copymanga.json;
public class ThemeStructure {
public String name;
public String path_word;
}

View File

@@ -0,0 +1,6 @@
package top.fumiama.copymanga.json;
public class UUIDUrlPair {
public String uuid;
public String url;
}

View File

@@ -0,0 +1,6 @@
package top.fumiama.copymanga.json;
public class ValueDisplayPair {
public int value;
public String display;
}

View File

@@ -0,0 +1,8 @@
package top.fumiama.copymanga.json;
public class VolumeStructure extends ReturnBase {
public Results results;
public static class Results extends InfoBase{
public ChapterStructure[] list;
}
}

View File

@@ -0,0 +1,28 @@
package top.fumiama.copymanga.template
import android.app.Activity
import android.os.Bundle
import android.view.View
import top.fumiama.copymanga.tools.PropertiesTools
import top.fumiama.copymanga.tools.UITools
import java.io.File
import java.lang.ref.WeakReference
open class ActivityTemplate:Activity() {
lateinit var p: PropertiesTools
lateinit var toolsBox: UITools
private val allFullScreen
get() = p["allFullScreen"] == "true"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
p = PropertiesTools(File("$filesDir/settings.properties"))
toolsBox = UITools(WeakReference(this))
}
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
if(allFullScreen) 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
}
}

View File

@@ -0,0 +1,60 @@
package top.fumiama.copymanga.template
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.util.Log
import com.google.gson.Gson
import top.fumiama.dmzj.copymanga.R
import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
import top.fumiama.copymanga.json.ReturnBase
import top.fumiama.copymanga.tools.DownloadTools
import top.fumiama.copymanga.tools.TimeThread
open class AutoDownloadHandler(private val url: String, private val jsonClass: Class<*>, looper: Looper, private val callCheckMsg: Int = -1): Handler(looper) {
private var timeThread: TimeThread? = null
private var checkTimes = 0
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
when(msg.what){
callCheckMsg -> check()
0 -> setLayouts()
}
}
open fun setGsonItem(gsonObj: Any) {}
open fun getGsonItem(): ReturnBase? = null
open fun onError() {}
open fun doWhenFinishDownload() {}
fun startLoad() {
sendEmptyMessage(0)
}
private fun download(){
Thread{
DownloadTools.getHttpContent(url,
mainWeakReference?.get()?.getString(R.string.referUrl)!!,
mainWeakReference?.get()?.getString(R.string.pc_ua)!!
)?.let {
val fi = it.inputStream()
setGsonItem(Gson().fromJson(fi.reader(), jsonClass))
fi.close()
}
}.start()
checkTimes = 0
timeThread = TimeThread(this, callCheckMsg)
timeThread?.canDo = true
timeThread?.start()
}
private fun check(){
val g = getGsonItem()
if(g != null) {
timeThread?.canDo = false
if(g.code == 200) sendEmptyMessage(0)
else onError()
Log.d("MyADH", "[${g.code}]${g.message}")
} else if(checkTimes++ > 10) timeThread?.canDo = false
}
private fun setLayouts() {
if(getGsonItem() == null) download()
else doWhenFinishDownload()
}
}

View File

@@ -0,0 +1,20 @@
package top.fumiama.copymanga.template
import top.fumiama.dmzj.copymanga.R
import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
import top.fumiama.copymanga.tools.DownloadTools
class AutoDownloadThread(private val url: String, private val whenFinish: (result: ByteArray?)->Unit): Thread() {
override fun run() {
super.run()
var re: ByteArray? = null
var c = 0
while (re == null && c++ < 3){
re = DownloadTools.getHttpContent(url,
mainWeakReference?.get()?.getString(R.string.referUrl)!!,
mainWeakReference?.get()?.getString(R.string.pc_ua)!!
)
}
whenFinish(re)
}
}

View File

@@ -0,0 +1,109 @@
package top.fumiama.copymanga.template
import android.annotation.SuppressLint
import android.net.Uri
import android.util.Log
import android.view.View
import androidx.fragment.app.Fragment
import com.bumptech.glide.Glide
import com.bumptech.glide.load.model.GlideUrl
import kotlinx.android.synthetic.main.card_book.view.*
import kotlinx.android.synthetic.main.line_horizonal_empty.view.*
import kotlinx.android.synthetic.main.line_lazybooklines.*
import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
import top.fumiama.copymanga.tools.CMApi
import top.fumiama.dmzj.copymanga.R
import java.io.File
import java.lang.ref.WeakReference
class CardList(
fragment: WeakReference<Fragment>,
private val cardWidth: Int,
private val cardHeight: Int,
private val cardPerRow: Int
) {
private val that = fragment.get()
private var rows:Array<View?> = arrayOfNulls(20)
private var index = 0
private var count = 0
var initClickListeners: InitClickListeners? = null
fun reset(){
rows = arrayOfNulls(20)
index = 0
count = 0
}
private fun manageRow(){
if(count++ % cardPerRow == 0) inflateRow()
Log.d("MyCL", "index: $index, cardPR: $cardPerRow")
}
private fun inflateRow(){
that?.layoutInflater?.inflate(R.layout.line_horizonal_empty, that.mydll, false)?.let {
it.layoutParams.height = cardHeight + 16
mainWeakReference?.get()?.runOnUiThread {
that.mydll.addView(it)
}
recycleOneRow(it)
}
}
private fun recycleOneRow(v:View?){
if(index < 20) rows[index] = v
else{
mainWeakReference?.get()?.runOnUiThread {
that?.mydll?.removeView(rows[index % 20])
//zis.mys?.scrollY = zis.mys?.scrollY?.minus(cardHeight + 16)?:0
}
rows[index % 20] = v
}
index++
}
@ExperimentalStdlibApi
fun addCard(name: String, append: String? = null, head: String? = null, path: String? = null, chapterUUID: String? = null, pn: Int? = null, isFinish: Boolean = false){
manageRow()
that?.layoutInflater?.inflate(R.layout.card_book, that.mydll.ltbtn, false)?.let {
val card = it.cic
card.name = name
card.append = append
card.headImageUrl = head
card.path = path
card.index = index - 1
card.chapterUUID = chapterUUID
card.pageNumber = pn
card.isFinish = isFinish
mainWeakReference?.get()?.runOnUiThread{
addCard(it)
}
}
}
@SuppressLint("SetTextI18n")
@ExperimentalStdlibApi
fun addCard(cardFrame: View) {
val card = cardFrame.cic
val name = card.name + (card.append?:"")
val head = card.headImageUrl
val file = File(that?.context?.getExternalFilesDir(""), card.name)
cardFrame.let {
it.tic.text = name
if(!file.exists()){
that?.context?.let { context ->
Glide.with(context).load(GlideUrl(head, CMApi.myGlideHeaders)).into(it.imic)
}
}else {
val img = File(file, "head.jpg")
if(img.exists()) it.imic.setImageURI(Uri.fromFile(img))
}
if(card.isFinish) it.sgnic.visibility = View.VISIBLE
initClickListeners?.prepareListeners(card, card.name, card.path, card.chapterUUID, card.pageNumber)
rows[card.index % 20]?.ltbtn?.addView(it)
it.layoutParams.height = cardHeight
it.layoutParams.width = cardWidth
}
}
interface InitClickListeners{
fun prepareListeners(v: View, name: String, path: String?, chapterUUID: String?, pn: Int?)
}
}

View File

@@ -0,0 +1,60 @@
package top.fumiama.copymanga.template
import android.os.Bundle
import android.util.JsonReader
import android.view.View
import androidx.fragment.app.Fragment
import androidx.navigation.Navigation
import com.google.gson.Gson
import top.fumiama.dmzj.copymanga.R
import top.fumiama.copymanga.json.BookListStructure
import top.fumiama.copymanga.tools.DownloadTools
import java.io.File
import java.lang.ref.WeakReference
@ExperimentalStdlibApi
open class InfoCardLoader(inflateRes:Int, private val navId:Int): MangaPagesFragmentTemplate(inflateRes) {
private val subUrl get() = getApiUrl()
init {
pageHandler = object : PageHandler {
override fun addPage(){
AutoDownloadThread(subUrl){
if(isRefresh){
page = 0
isRefresh = false
}
val bookList = Gson().fromJson(it?.decodeToString(), BookListStructure::class.java)
bookList?.let {
if(it.code == 200) it.results.list?.forEach{ book ->
cardList.addCard(book.name, null, book.cover, book.path_word, null, null, false)
}
}
page++
onLoadFinish()
}.start()
}
override fun initCardList(weakReference: WeakReference<Fragment>) {
cardList = CardList(weakReference, cardWidth, cardHeight, cardPerRow)
cardList.initClickListeners = object : CardList.InitClickListeners {
override fun prepareListeners(v: View, name: String, path: String?, chapterUUID: String?, pn: Int?) {
v.setOnClickListener {
val bundle = Bundle()
bundle.putString("path", path)
rootView?.let { Navigation.findNavController(it).navigate(navId, bundle) }
}
}
}
}
override fun setListeners() { this@InfoCardLoader.setListeners() }
}
}
open fun getApiUrl(): String{
return ""
}
open fun setListeners(){}
open fun onLoadFinish(){}
}

View File

@@ -0,0 +1,67 @@
package top.fumiama.copymanga.template
import android.annotation.SuppressLint
import android.os.Bundle
import android.util.JsonReader
import android.util.Log
import android.view.View
import androidx.fragment.app.Fragment
import kotlinx.android.synthetic.main.line_header.view.*
import kotlinx.android.synthetic.main.line_lazybooklines.*
import top.fumiama.copymanga.template.handler.MPATHandler
import top.fumiama.copymanga.tools.UITools
import java.lang.Thread.sleep
import java.lang.ref.WeakReference
open class MangaPagesFragmentTemplate(inflateRes:Int, val isLazy: Boolean = true, val forceLoad: Boolean = false) : NoBackRefreshFragment(inflateRes) {
var cardPerRow = 3
var cardWidth = 0
var cardHeight = 0
lateinit var cardList: CardList
var mh: MPATHandler? = null
var row: View? = null
var isEnd = false
var jsonReaderNow: JsonReader? = null
var page = 0
var isRefresh = false
@SuppressLint("ClickableViewAccessibility")
@ExperimentalStdlibApi
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if(isFirstInflate) {
mh = MPATHandler(WeakReference(this))
Thread {
sleep(600)
mh?.sendEmptyMessage(0)
}.start()
}
}
fun setLayouts() {
val toolsBox = this.context?.let { UITools(it) }
val widthData = toolsBox?.calcWidthFromDp(8, 135)
cardPerRow = widthData?.get(0) ?: 3
cardWidth = widthData?.get(2) ?: 128
cardHeight = (cardWidth / 0.75 + 0.5).toInt()
mysp.footerView.lht.text = "加载"
mysp.headerView.lht.text = "刷新"
Log.d("MyMPAT", "Card per row: $cardPerRow")
Log.d("MyMPAT", "Card width: $cardWidth")
pageHandler?.initCardList(WeakReference(this))
Thread { mh?.sendEmptyMessage(1) }.start()
pageHandler?.setListeners()
//mypl.visibility = View.GONE
}
var pageHandler: PageHandler? = null
interface PageHandler {
fun addPage()
fun initCardList(weakReference: WeakReference<Fragment>)
fun setListeners()
}
}

View File

@@ -0,0 +1,23 @@
package top.fumiama.copymanga.template
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
open class NoBackRefreshFragment(private val layoutToLoad: Int):Fragment() {
var rootView: View? = null
var isFirstInflate = true
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
if(rootView == null){
isFirstInflate = true
rootView = inflater.inflate(layoutToLoad, container, false)
} else isFirstInflate = false
return rootView
}
}

View File

@@ -0,0 +1,13 @@
package top.fumiama.copymanga.template
import android.os.Bundle
import kotlinx.android.synthetic.main.widget_titlebar.*
open class TitleActivityTemplate:ActivityTemplate() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
ilogo.setOnClickListener {
onBackPressed()
}
}
}

View File

@@ -0,0 +1,83 @@
package top.fumiama.copymanga.template.handler
import android.content.Context
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.os.Handler
import android.os.Message
import android.view.View
import android.widget.Toast
import com.liaoinstan.springview.widget.SpringView
import top.fumiama.dmzj.copymanga.R
import kotlinx.android.synthetic.main.line_lazybooklines.*
import top.fumiama.copymanga.template.MangaPagesFragmentTemplate
import java.lang.ref.WeakReference
class MPATHandler(private val w: WeakReference<MangaPagesFragmentTemplate>) : Handler() {
private val wa get() = w.get()
private val netinfo: String
get() {
val cm: ConnectivityManager =
wa?.context?.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
return cm.getNetworkCapabilities(cm.activeNetwork)?.let {
when {
it.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> return@let wa?.context?.getString(
R.string.TRANSPORT_WIFI)
it.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> return@let wa?.context?.getString(
R.string.TRANSPORT_CELLULAR)
it.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH) -> return@let wa?.context?.getString(
R.string.TRANSPORT_BLUETOOTH)
it.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> return@let wa?.context?.getString(
R.string.TRANSPORT_ETHERNET)
it.hasTransport(NetworkCapabilities.TRANSPORT_LOWPAN) -> return@let wa?.context?.getString(
R.string.TRANSPORT_LOWPAN)
it.hasTransport(NetworkCapabilities.TRANSPORT_VPN) -> return@let "VPN"
else -> return@let wa?.context?.getString(R.string.TRANSPORT_NULL)
}
} ?: "错误"
}
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
if (wa?.forceLoad == true || netinfo != "无网络" && netinfo != "错误") {
when (msg.what) {
0 -> wa?.setLayouts()
1 -> managePage()
2 -> addPageHandler()
3 -> {
wa?.pageHandler?.addPage()
//wa?.myp?.visibility = View.GONE
wa?.mysp?.onFinishFreshAndLoad()
//wa?.mys?.fullScroll(ScrollView.FOCUS_UP)
}
4 ->{
wa?.mydll?.removeAllViews()
wa?.isEnd = false
wa?.jsonReaderNow = null
wa?.page = 0
wa?.cardList?.reset()
addPageHandler()
wa?.mysp?.onFinishFreshAndLoad()
wa?.mypl?.visibility = View.VISIBLE
}
}
} else Toast.makeText(wa?.context, "${netinfo}链接!", Toast.LENGTH_SHORT).show()
}
private fun managePage() {
addPageHandler()
if (wa?.isLazy == true) wa?.mysp?.setListener(object :SpringView.OnFreshListener{
override fun onLoadmore() {
Thread { this@MPATHandler.sendEmptyMessage(2) }.start()
}
override fun onRefresh() {
Thread { this@MPATHandler.sendEmptyMessage(4) }.start()
}
})
}
private fun addPageHandler() {
//wa?.myp?.visibility = View.VISIBLE
Thread { this.sendEmptyMessage(3) }.start()
}
}

View File

@@ -1,37 +0,0 @@
package top.fumiama.copymanga.tool
import android.util.Log
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, ua: 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) }
ua?.let { connection.setRequestProperty("User-agent", 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

@@ -1,94 +0,0 @@
package top.fumiama.copymanga.tool
import top.fumiama.copymanga.R
import top.fumiama.copymanga.activity.DlActivity
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.Thread.sleep
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(activity: DlActivity) {
var exit = false
private val da = WeakReference(activity)
private val d = da.get()
private val p = PropertiesTools(File("${d?.filesDir}/chapters.hash"))
private var imgUrlsList: Array<Array<String>?>? = null
private var chaptersCount = 0
private val newWebViewHidden: JSWebView?
get() {
val re = d?.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"))
var tryTimes = 3
var s = false
while (!s && tryTimes-- > 0){
s = dl.getHttpContent(it[i], d?.getString(R.string.web_home_www), d?.getString(R.string.pc_ua))?.let { zip.write(it); true } ?: false
if (!s) {
onDownloadedListener?.handleMessage(i + 1)
sleep(2000)
}
}
if(!s && tryTimes <= 0) succeed = false
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)
fun handleMessage(pageNow: Int)
}
companion object {
var wmdlt: WeakReference<MangaDlTools>? = null
}
}

View File

@@ -1,76 +0,0 @@
package top.fumiama.copymanga.tool
import android.content.Intent
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.io.File
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){
if (v?.clicked == false) {
if (if(goNext)judgeNext() else judgePrevious()) {
if(goNext) {
v.scrollForward()
isEndR = false
} else {
v.scrollBack()
isEndL = false
}
} else {
val chapterUrl = if(goNext) ViewMangaActivity.nextChapterUrl else ViewMangaActivity.previousChapterUrl
if (chapterUrl != null) {
if (if(goNext)isEndR else isEndL) {
if(!goNext) ViewMangaActivity.pn = -2
wm?.get()?.w?.loadUrl("javascript:invoke.clickClass(\"comicControlBottomTopClick\",${if(goNext)1 else 0});")
v.tt.canDo = false
v.finish()
} else doubleTapToast(goNext)
} else {
val newZipPosition = ViewMangaActivity.zipPosition + (if(goNext) 1 else -1)
if(v.dlZip2View && newZipPosition >= 0 && newZipPosition < ViewMangaActivity.zipList?.size?:0){
if (if(goNext)isEndR else isEndL){
if(!goNext) ViewMangaActivity.pn = -2
ViewMangaActivity.zipPosition = newZipPosition
ViewMangaActivity.titleText = ViewMangaActivity.zipList?.get(newZipPosition) ?: "null"
ViewMangaActivity.zipFile = File(ViewMangaActivity.cd, ViewMangaActivity.titleText)
v.startActivity(Intent(v, ViewMangaActivity::class.java))
v.tt.canDo = false
v.finish()
}else doubleTapToast(goNext)
}
else Toast.makeText(
v.applicationContext,
"已经到头了~",
Toast.LENGTH_SHORT
).show()
}
}
} else v?.hideObjs()
}
fun manageInfo(){
if (v?.clicked == false) v.showObjs() else v?.hideObjs()
}
private fun doubleTapToast(goNext: Boolean){
val hint = if(goNext) "" else ""
Toast.makeText(
v?.applicationContext,
"再次按下加载${hint}一章",
Toast.LENGTH_SHORT
).show()
if(goNext) isEndR = true
else isEndL = true
}
}

View File

@@ -0,0 +1,18 @@
package top.fumiama.copymanga.tools
import com.bumptech.glide.load.model.LazyHeaders
import top.fumiama.dmzj.copymanga.R
import top.fumiama.copymanga.MainActivity
import top.fumiama.copymanga.json.Chapter2Return
import java.io.File
object CMApi {
var myGlideHeaders: LazyHeaders? = null
get() {
if(field === null) field = LazyHeaders.Builder().addHeader("referer", MainActivity.mainWeakReference?.get()?.getString(R.string.referUrl)!!).addHeader("User-Agent", MainActivity.mainWeakReference?.get()?.getString(R.string.pc_ua)!!).build()
return field
}
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) }
}

View File

@@ -0,0 +1,119 @@
package top.fumiama.copymanga.tools
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
object DownloadTools {
fun getHttpContent(Url: String, refer: String? = null, ua: 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) }
ua?.let { connection.setRequestProperty("User-agent", 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
}
}
fun downloadUsingUrlRet(Url: String?, f: File): Boolean {
Log.d("Mydl", "Ret Get Url: $Url, File: $f")
val task = FutureTask(Callable {
try {
val connection = URL(Url).openConnection() as HttpURLConnection
connection.requestMethod = "GET"
connection.connectTimeout = 10000
connection.readTimeout = 10000
if (f.exists()) f.delete()
else f.parentFile?.mkdirs()
f.parentFile?.let {
if (!it.canRead()) it.setReadable(true)
if (!it.canWrite()) it.setWritable(true)
}
connection.inputStream.buffered().copyTo(f.outputStream())
connection.disconnect()
return@Callable true
} catch (ex: Exception) {
ex.printStackTrace()
return@Callable false
}
})
Thread(task).start()
return try {
task.get()
} catch (ex: Exception) {
ex.printStackTrace()
false
}
}
fun downloadUsingUrl(Url: String?, f: File, refer: String? = null) {
Log.d("Mydl", "Get Url: $Url, File: $f")
Thread(Runnable {
try {
val connection = URL(Url).openConnection() as HttpURLConnection
connection.requestMethod = "GET"
connection.connectTimeout = 10000
connection.readTimeout = 10000
refer?.let { connection.setRequestProperty("referer", it) }
if (f.exists()) f.delete()
else f.parentFile?.mkdirs()
f.parentFile?.let {
if (!it.canRead()) it.setReadable(true)
if (!it.canWrite()) it.setWritable(true)
}
connection.inputStream.buffered().copyTo(f.outputStream())
connection.disconnect()
} catch (ex: Exception) {
ex.printStackTrace()
}
}).start()
}
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,52 @@
package top.fumiama.copymanga.tools
import android.content.Context
import android.graphics.Bitmap
import android.renderscript.Allocation
import android.renderscript.Element
import android.renderscript.RenderScript
import android.renderscript.ScriptIntrinsicBlur
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool
import com.bumptech.glide.load.resource.bitmap.CenterCrop
class GlideBlurTransformation(private val context: Context) : CenterCrop() {
override fun transform(
pool: BitmapPool,
toTransform: Bitmap,
outWidth: Int,
outHeight: Int
): Bitmap {
val bitmap = super.transform(pool, toTransform, outWidth, outHeight)
return blurBitmap(context, bitmap, 25f, outWidth / 2, outHeight / 2)
}
private fun blurBitmap(
context: Context?,
image: Bitmap?,
blurRadius: Float,
outWidth: Int,
outHeight: Int
): Bitmap {
// 将缩小后的图片做为预渲染的图片
val inputBitmap = Bitmap.createScaledBitmap(image!!, outWidth, outHeight, false)
// 创建一张渲染后的输出图片
val outputBitmap = Bitmap.createBitmap(inputBitmap)
// 创建RenderScript内核对象
val rs = RenderScript.create(context)
// 创建一个模糊效果的RenderScript的工具对象
val blurScript = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs))
// 由于RenderScript并没有使用VM来分配内存,所以需要使用Allocation类来创建和分配内存空间
// 创建Allocation对象的时候其实内存是空的,需要使用copyTo()将数据填充进去
val tmpIn = Allocation.createFromBitmap(rs, inputBitmap)
val tmpOut = Allocation.createFromBitmap(rs, outputBitmap)
// 设置渲染的模糊程度, 25f是最大模糊度
blurScript.setRadius(blurRadius)
// 设置blurScript对象的输入内存
blurScript.setInput(tmpIn)
// 将输出数据保存到输出内存中
blurScript.forEach(tmpOut)
// 将数据填充到Allocation中
tmpOut.copyTo(outputBitmap)
return outputBitmap
}
}

View File

@@ -0,0 +1,106 @@
package top.fumiama.copymanga.tools
import android.util.Log
import com.google.gson.Gson
import top.fumiama.dmzj.copymanga.R
import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
import top.fumiama.copymanga.json.Chapter2Return
import top.fumiama.copymanga.template.AutoDownloadThread
import top.fumiama.copymanga.tools.DownloadTools.getHttpContent
import java.io.File
import java.lang.Thread.sleep
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 var comicFileRelative: String? = null
var size = 0
var complete = false
fun downloadChapterInVol(url: CharSequence, chapterName: CharSequence, group: CharSequence, index: Int){
comicFileRelative = "$group/$chapterName.zip"
complete = false
getChapterInfo(url, index)
while (!complete) sleep(1000)
}
private fun getChapterInfo(chapter2Return: Chapter2Return, index: Int) {
if(index >= 0){
comicFileRelative?.let {
dlChapterAndPackIntoZip(
File(
mainWeakReference?.get()?.getExternalFilesDir(""),
"${chapter2Return.results.comic.name}/$it"
),
getMangaUrls(chapter2Return)
)
}
}
}
private fun getMangaUrls(chapter2Return: Chapter2Return): Array<String>{
var re: Array<String> = arrayOf()
val hm: HashMap<Int, String> = hashMapOf()
val chapter = chapter2Return.results.chapter
for(i in 0 until chapter.size) {
hm[chapter.words[i]] = chapter.contents[i].url
}
for(i in 0 until chapter.size){
re += hm[i]?:""
}
size = re.size
return re
}
private fun getChapterInfo(url: CharSequence, index: Int){
Log.d("MyMDT", "下载:$url, index$index")
AutoDownloadThread(url.toString()){
Gson().fromJson(it?.decodeToString(), Chapter2Return::class.java)?.let {
getChapterInfo(it, index)
}
}.start()
}
private fun dlChapterAndPackIntoZip(zipf: File, urls: Array<String>) {
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 urls.indices) {
zip.putNextEntry(ZipEntry("$i.webp"))
var tryTimes = 3
var s = false
while (!s && tryTimes-- > 0) {
s = getHttpContent(
urls[i],
mainWeakReference?.get()?.getString(R.string.referUrl),
mainWeakReference?.get()?.getString(R.string.pc_ua)
)?.let { zip.write(it); true } ?: false
if (!s) {
onDownloadedListener?.handleMessage(i + 1)
sleep(2000)
}
}
if (!s && tryTimes <= 0) succeed = false
onDownloadedListener?.handleMessage(s, i + 1)
zip.flush()
if (exit) break
}
zip.close()
onDownloadedListener?.handleMessage(succeed)
complete = true
}
var onDownloadedListener: OnDownloadedListener? = null
interface OnDownloadedListener {
fun handleMessage(succeed: Boolean)
fun handleMessage(succeed: Boolean, pageNow: Int)
fun handleMessage(pageNow: Int)
}
}

View File

@@ -1,32 +1,31 @@
package top.fumiama.copymanga.tool
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()
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)
createNew(f)
}else if(f.isDirectory) {
if(f.parentFile?.canWrite() != true) f.parentFile?.setWritable(true)
f.delete()
createNew(f)
if(f.parentFile?.canRead() != true) f.parentFile?.setReadable(true)
return 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.")
//Log.d("MyPT", "Generate new prop.")
o.close()
}
private fun loadFromXml(`in`: InputStream?): PropertiesTools {
@@ -40,14 +39,14 @@ class PropertiesTools(private val f: File):Properties() {
operator fun get(key: String): String{
val i = propfile.inputStream()
val re = this.loadFromXml(i).getProperty(key)?:"null"
Log.d("MyPT", "Get $key = $re")
//Log.d("MyPT", "Get prop: $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")
//Log.d("MyPT", "Set $key = $value")
o.close()
}
}
}

View File

@@ -1,4 +1,4 @@
package top.fumiama.copymanga.handler
package top.fumiama.copymanga.tools
import android.os.Handler

View File

@@ -1,34 +1,19 @@
package top.fumiama.copymanga.tool
package top.fumiama.copymanga.tools
import android.app.Activity
import android.app.AlertDialog
import android.content.Context
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import android.util.Log
import android.widget.Toast
import top.fumiama.copymanga.R
import top.fumiama.dmzj.copymanga.R
import java.lang.ref.WeakReference
import java.util.*
import java.util.regex.Pattern
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 -> ""
}
}
class UITools(that: Context?, w: WeakReference<Activity>? = null) {
private val zis = that
private val weak = w
constructor(w: WeakReference<Activity>): this(w.get()?.applicationContext, w)
val netinfo: String
get() {
val cm: ConnectivityManager =
@@ -37,6 +22,7 @@ class ToolsBox(w: WeakReference<Any>) {
when {
it.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> return@let "WIFI"
it.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> return@let "移动数据"
it.hasTransport(NetworkCapabilities.TRANSPORT_WIFI_AWARE) -> return@let "WIFI_AWARE"
it.hasTransport(NetworkCapabilities.TRANSPORT_BLUETOOTH) -> return@let "蓝牙"
it.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> return@let "以太网"
it.hasTransport(NetworkCapabilities.TRANSPORT_LOWPAN) -> return@let "LOWPAN"
@@ -45,8 +31,8 @@ class ToolsBox(w: WeakReference<Any>) {
} ?: "错误"
}
fun toastError(s: String, willFinish: Boolean = true) {
Toast.makeText(zis?.applicationContext, s, Toast.LENGTH_SHORT).show()
if (willFinish) zis?.finish()
Toast.makeText(zis, s, Toast.LENGTH_SHORT).show()
if (willFinish) weak?.get()?.finish()
}
fun buildInfo(
title: String,
@@ -70,9 +56,18 @@ class ToolsBox(w: WeakReference<Any>) {
fun dp2px(dp:Int):Int?{
return zis?.resources?.displayMetrics?.density?.let { (dp * it + 0.5).toInt()}
}
private fun px2dp(px:Int):Int?{
fun px2dp(px:Int):Int?{
return zis?.resources?.displayMetrics?.density?.let { (px.toDouble() / it + 0.5).toInt()}
}
fun calcWidthFromDp(marginLeftDp:Int, widthDp:Int):List<Int>{
val margin = marginLeftDp.toDouble()
val marginPx = dp2px(marginLeftDp)?:16
val screenWidth = zis?.resources?.displayMetrics?.widthPixels?:1080
val numPerRow = ((px2dp(screenWidth)?:400).toDouble() / (widthDp + 2 * margin) + 0.5).toInt()
val w = (screenWidth - marginPx*numPerRow*2)/numPerRow
val totalWidth = screenWidth/numPerRow
return listOf(numPerRow, w, totalWidth)
}
private fun root(a:Double, b:Double, c:Double):List<Double>?{
val d = b*b - 4.0 * a * c
if(d < 0) return null
@@ -81,7 +76,7 @@ class ToolsBox(w: WeakReference<Any>) {
val x2 = (-b - sd)/(2.0 * a)
return listOf(x1, x2)
}
fun calcWidthFromDp(marginLeftDp:Int, widthDp:Int):List<Int>{
fun calcWidthFromDpRoot(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())
@@ -90,4 +85,4 @@ class ToolsBox(w: WeakReference<Any>) {
val totalWidth = ((zis?.resources?.displayMetrics?.widthPixels?:1080)-marginPx)/numPerRow
return listOf(numPerRow, w, totalWidth)
}
}
}

View File

@@ -0,0 +1,72 @@
package top.fumiama.copymanga.ui.book
import android.os.Bundle
import android.util.Log
import android.view.Menu
import android.view.View
import androidx.navigation.Navigation
import top.fumiama.dmzj.copymanga.R
import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
import top.fumiama.copymanga.template.NoBackRefreshFragment
import java.lang.Thread.sleep
import java.lang.ref.WeakReference
class BookFragment:NoBackRefreshFragment(R.layout.fragment_book) {
private lateinit var bookHandler: BookHandler
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
if(isFirstInflate) {
bookHandler = BookHandler(WeakReference(this), arguments?.getString("path")?:"null")
Thread{
sleep(600)
bookHandler.startLoad()
}.start()
}
else bookHandler.fbibinfo?.layoutParams?.height = (bookHandler.fbibinfo?.width?:0 * 4.0 / 9.0 + 0.5).toInt()
}
override fun onResume() {
super.onResume()
mainWeakReference?.get()?.menuMain?.let { setMenuVisible(it) }
}
override fun onDestroy() {
super.onDestroy()
mainWeakReference?.get()?.menuMain?.let { setMenuInvisible(it) }
}
private fun setMenuInvisible(menu: Menu){
menu.findItem(R.id.action_download)?.isVisible = false
}
private fun setMenuVisible(menu: Menu) {
Log.d("MyBF", "显示下载按钮")
val dl = menu.findItem(R.id.action_download)
dl?.isVisible = true
dl?.setIcon(R.drawable.ic_menu_download)
dl?.setOnMenuItemClickListener {
if(bookHandler.complete && it.itemId == R.id.action_download){
navigate2dl()
true
}
else it.itemId == R.id.action_download
}
}
private fun navigate2dl(){
val bundle = Bundle()
bundle.putString("path", arguments?.getString("path")?:"null")
bundle.putString("name", bookHandler.book?.results?.comic?.name)
val groups = bookHandler.book?.results?.groups
var keys = arrayOf<String>()
var gpws = arrayOf<String>()
groups?.values?.forEach {
keys += it.name
gpws += it.path_word
}
bundle.putStringArray("group", gpws)
bundle.putStringArray("groupNames", keys)
rootView?.let { Navigation.findNavController(it).navigate(R.id.action_nav_book_to_nav_group, bundle) }
}
}

View File

@@ -0,0 +1,192 @@
package top.fumiama.copymanga.ui.book
import android.os.Bundle
import android.os.Looper
import android.os.Message
import android.util.Log
import android.view.Menu
import android.view.View
import android.view.ViewTreeObserver
import android.widget.TextView
import android.widget.Toast
import androidx.navigation.Navigation
import com.bumptech.glide.Glide
import com.bumptech.glide.load.model.GlideUrl
import com.bumptech.glide.load.model.LazyHeaders
import com.bumptech.glide.request.RequestOptions
import kotlinx.android.synthetic.main.app_bar_main.*
import kotlinx.android.synthetic.main.card_book.*
import kotlinx.android.synthetic.main.fragment_book.*
import kotlinx.android.synthetic.main.line_2chapters.view.*
import kotlinx.android.synthetic.main.line_bookinfo.*
import kotlinx.android.synthetic.main.line_bookinfo_text.*
import kotlinx.android.synthetic.main.line_chapter.view.*
import top.fumiama.dmzj.copymanga.R
import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
import top.fumiama.copymanga.json.BookInfoStructure
import top.fumiama.copymanga.json.ReturnBase
import top.fumiama.copymanga.json.ThemeStructure
import top.fumiama.copymanga.template.AutoDownloadHandler
import top.fumiama.copymanga.tools.CMApi
import top.fumiama.copymanga.tools.GlideBlurTransformation
import java.lang.ref.WeakReference
class BookHandler(that: WeakReference<BookFragment>, path: String)
:AutoDownloadHandler(
that.get()?.getString(R.string.bookInfoApiUrl)?.let { String.format(it, path) } ?: "",
BookInfoStructure::class.java,
Looper.myLooper()!!){
private val that = that.get()
private var hasToastedError = false
get(){
val re = field
field = true
return re
}
var book: BookInfoStructure? = null
var fbibinfo:View? = null
var complete = false
private val divider get() = that?.layoutInflater?.inflate(R.layout.div_h, that.fbl, false)
private var fbtinfo: View? = null
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
when(msg.what){
//0 -> setLayouts()
1 -> setCover()
2 -> setTexts()
3 -> fbibinfo?.let { setInfoHeight(it) }
//4 -> setThemes()
5 -> setOverScale()
6 -> endSetLayouts()
}
}
override fun onError() {
super.onError()
if(!hasToastedError) {
Toast.makeText(that?.context, R.string.null_book, Toast.LENGTH_SHORT).show()
that?.rootView?.let { it1 ->
Navigation.findNavController(it1).navigateUp()
}
}
}
override fun setGsonItem(gsonObj: Any) {
super.setGsonItem(gsonObj)
book = gsonObj as BookInfoStructure
}
override fun getGsonItem() = book
override fun doWhenFinishDownload() {
super.doWhenFinishDownload()
inflateComponents()
Thread{ for (i in 1..6) sendEmptyMessage(i) }.start()
}
private fun endSetLayouts(){
that?.fbloading?.visibility = View.GONE
complete = true
Log.d("MyBH", "Set complete: true")
}
private fun inflateComponents(){
fbibinfo = that?.layoutInflater?.inflate(R.layout.line_bookinfo, that.fbl, false)
fbtinfo = that?.layoutInflater?.inflate(R.layout.line_text_info, that.fbl, false)
}
private fun setOverScale(){
that?.fbov?.setScaleView(that.lbibg)
}
private fun setCover(){
that?.let {
it.fbl.addView(fbibinfo)
val load = Glide.with(it).load(
GlideUrl(book?.results?.comic?.cover, CMApi.myGlideHeaders)
).timeout(10000)
load.into(it.imic)
it.context?.let { it1 -> GlideBlurTransformation(it1) }
?.let { it2 -> RequestOptions.bitmapTransform(it2) }
?.let { it3 -> load.apply(it3).into(it.lbibg) }
it.imf.visibility = View.GONE
}
that?.fbl?.addView(divider)
}
private fun getThemeSeq(authors: Array<ThemeStructure>): CharSequence{
var re = ""
for(author in authors) re += author.name + ' '
return re
}
private fun setTexts(){
//that?.tic?.text = book?.name
that?.tic?.visibility = View.GONE
mainWeakReference?.get()?.toolbar?.title = book?.results?.comic?.name
that?.btauth?.text = book?.results?.comic?.author?.let { getThemeSeq(it) }
that?.bttag?.text = book?.results?.comic?.theme?.let { getThemeSeq(it) }
that?.bthit?.text = that?.getString(R.string.text_format_hit)?.let { String.format(
it,
book?.results?.comic?.popular
) }?:""
that?.btsub?.text = that?.getString(R.string.text_format_stat)?.let { String.format(
it,
book?.results?.comic?.status?.display
) }?:""
that?.bttime?.text = book?.results?.comic?.datetime_updated
(fbtinfo as TextView).text = book?.results?.comic?.brief
that?.fbl?.addView(fbtinfo)
that?.fbl?.addView(divider)
}
private fun setInfoHeight(v: View){
v.viewTreeObserver.addOnGlobalLayoutListener {
Log.d("MyMy", "Width: ${v.width}")
val newH = (v.width * 4.0 / 9.0 + 0.5).toInt()
v.layoutParams.height = newH
v.invalidate()
}
}
private fun setThemes(){
book?.results?.groups?.let {
val keyIterator = it.keys.iterator()
for(i in 0 until it.size){
if(i % 2 == 0){
that?.fbl?.addView(if(i < it.size - 1){
val line = that.layoutInflater.inflate(R.layout.line_2chapters, that.fbl, false)
val leftKey = keyIterator.next()
line?.l2cl?.lct?.text = it[leftKey]?.name
line?.l2cl?.setOnClickListener { _->
loadVolume(it[leftKey]?.path_word?:"null")
}
val rightKey = keyIterator.next()
line?.l2cr?.lct?.text = it[rightKey]?.name
line?.l2cr?.setOnClickListener { _->
loadVolume(it[rightKey]?.path_word?:"null")
}
line
}else{
//Log.d("MyBH", "Add chapter: ${vol[i].volume_name}")
val line = that.layoutInflater.inflate(R.layout.line_chapter, that.fbl, false)
val key = keyIterator.next()
line?.lct?.text = it[key]?.name
line?.lcc?.setOnClickListener { _->
loadVolume(it[key]?.path_word?:"null")
}
line
})
}
}
}
}
private fun loadVolume(gpw: String){
Log.d("MyBH", "start to load chapter")
val bundle = Bundle()
bundle.putString("group", gpw)
book?.results?.comic?.path_word?.let { bundle.putString("path", it) }
that?.rootView?.let { Navigation.findNavController(it).navigate(R.id.action_nav_book_to_nav_chapter, bundle) }
}
}

View File

@@ -0,0 +1,22 @@
package top.fumiama.copymanga.ui.chapter
import android.os.Bundle
import android.view.View
import top.fumiama.dmzj.copymanga.R
import top.fumiama.copymanga.template.NoBackRefreshFragment
import java.lang.Thread.sleep
import java.lang.ref.WeakReference
class ChapterFragment:NoBackRefreshFragment(R.layout.fragment_chapters) {
var handler: ChapterHandler? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
if(isFirstInflate){
handler = arguments?.let { ChapterHandler(WeakReference(this), it.getString("path")?:"", it.getString("group")?:"") }
Thread{
sleep(600)
handler?.startLoad()
}.start()
}
}
}

View File

@@ -0,0 +1,81 @@
package top.fumiama.copymanga.ui.chapter
import android.os.Looper
import android.os.Message
import android.view.View
import android.widget.Toast
import androidx.navigation.Navigation
import kotlinx.android.synthetic.main.fragment_book.*
import kotlinx.android.synthetic.main.fragment_chapters.*
import kotlinx.android.synthetic.main.line_2chapters.view.*
import kotlinx.android.synthetic.main.line_chapter.view.*
import top.fumiama.dmzj.copymanga.R
import top.fumiama.copymanga.json.ChapterStructure
import top.fumiama.copymanga.json.VolumeStructure
import top.fumiama.copymanga.template.AutoDownloadHandler
import java.lang.ref.WeakReference
class ChapterHandler(that: WeakReference<ChapterFragment>, pw: String, gpw: String):AutoDownloadHandler(
that.get()?.getString(R.string.groupInfoApiUrl)?.let { String.format(it, pw, gpw) } ?: "",
VolumeStructure::class.java,
Looper.myLooper()!!
) {
private val that = that.get()
var hasToastedError = false
get(){
val re = field
field = true
return re
}
private var chapters: VolumeStructure? = null
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
when(msg.what){
//0 -> setLayouts()
1 -> inflateChapters()
}
}
override fun getGsonItem() = chapters
override fun setGsonItem(gsonObj: Any) {
super.setGsonItem(gsonObj)
chapters = gsonObj as VolumeStructure
}
override fun onError() {
super.onError()
if(!hasToastedError) {
Toast.makeText(that?.context, R.string.null_book, Toast.LENGTH_SHORT).show()
that?.rootView?.let { it1 ->
Navigation.findNavController(it1).navigateUp()
}
}
}
override fun doWhenFinishDownload() {
super.doWhenFinishDownload()
Thread{ sendEmptyMessage(1) }.start()
}
private fun inflateChapters(){
that?.fcloading?.visibility = View.GONE
}
private fun addLine(size: Int, name:String, onClick:(()->Unit)? = null){
val line =
that?.let { it.layoutInflater.inflate(R.layout.line_chapter, it.fbl, false) }
line?.lct?.text = name
onClick?.let {action->
line?.lcc?.setOnClickListener {action()}
}
that?.fcl?.addView(line)
}
private fun loadChapter(chapter: ChapterStructure){
/*val bundle = Bundle()
bundle.putInt("id", id)
bundle.putInt("volume", volId)
bundle.putInt("chapter", cid)
that?.rootView?.let { Navigation.findNavController(it).navigate(R.id.action_nav_chapter_to_nav_reader, bundle) }
*/}
}

View File

@@ -0,0 +1,137 @@
package top.fumiama.copymanga.ui.comicdl
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.Message
import android.util.Log
import android.view.Menu
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.VolumeStructure
import top.fumiama.copymanga.template.AutoDownloadThread
import top.fumiama.copymanga.template.NoBackRefreshFragment
import top.fumiama.copymanga.tools.CMApi
import java.io.File
import java.lang.Thread.sleep
import java.lang.ref.WeakReference
class ComicDlFragment:NoBackRefreshFragment(R.layout.fragment_dlcomic) {
var handler: ComicDlHandler? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
if(isFirstInflate){
when {
arguments?.getBoolean("callFromOldDL", false) == true -> initOldComicData()
arguments?.getBoolean("loadJson", false) == true -> context?.getExternalFilesDir("")?.let { home ->
arguments?.getString("name")?.let {
start2load(loadFromJson(), true, loadGroupsFromFile(File(home, "$it/grps.json")))
}
}
else -> initComicData(
arguments?.getString("path"),
arguments?.getStringArray("group")
)
}
}
mainWeakReference?.get()?.menuMain?.let { setMenuVisible(it) }
}
/*override fun onDestroy() {
super.onDestroy()
mainWeakReference?.get()?.menuMain?.let { setMenuInvisible(it) }
}*/
fun start2load(volumes: Array<VolumeStructure>, isFromFile: Boolean = false, groupArray: Array<String>? =null){
handler = ComicDlHandler(Looper.myLooper()!!,
WeakReference(this),
volumes,
arguments?.getString("name")?:"null",
if(isFromFile) groupArray else arguments?.getStringArray("groupNames"))
if(!isFromFile) Thread{
context?.getExternalFilesDir("")?.let { home ->
arguments?.getString("name")?.let { name ->
val mangaFolder = File(home, name)
if(!mangaFolder.exists()) mangaFolder.mkdirs()
File(mangaFolder, "info.json").writeText(Gson().toJson(volumes))
arguments?.getStringArray("groupNames")?.let {
File(mangaFolder, "grps.json").writeText(Gson().toJson(it))
}
}
}
}.start()
handler?.startLoad()
}
private fun loadFromJson() = Gson().fromJson(json, Array<VolumeStructure>::class.java)
private fun loadGroupsFromFile(file: File) = Gson().fromJson(file.reader(), Array<String>::class.java)
private fun setMenuVisible(menu: Menu) {
val dl = menu.findItem(R.id.action_download)
dl?.isVisible = true
dl?.setIcon(R.drawable.ic_menu_sort)
dl?.setOnMenuItemClickListener {
if(handler?.complete == true && it.itemId == R.id.action_download){
handler?.showMultiSelectInfo()
true
}
else it.itemId == R.id.action_download
}
}
/*private fun setMenuInvisible(menu: Menu){
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))
if (gpws != null) {
gpws.forEach { 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()
}
}
Thread {
var c = 0
while (c < 80 && volumes.size != gpws.size) {
sleep(100)
Log.d("MyCDF", "已有:${volumes.size} 共:${gpws.size}")
c++
}
if (volumes.size == gpws.size) {
waitHandler.obtainMessage(0, volumes).sendToTarget()
}
}.start()
}
}
private fun initOldComicData() {
handler = ComicDlHandler(Looper.myLooper()!!,
WeakReference(this),
arguments?.getString("name")?:"null")
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
}
}

View File

@@ -0,0 +1,451 @@
package top.fumiama.copymanga.ui.comicdl
import android.animation.ObjectAnimator
import android.annotation.SuppressLint
import android.app.Dialog
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 android.view.ViewGroup
import android.view.ViewTreeObserver
import android.widget.Toast
import com.google.gson.Gson
import kotlinx.android.synthetic.main.fragment_book.*
import kotlinx.android.synthetic.main.fragment_chapters.*
import kotlinx.android.synthetic.main.line_chapter.view.*
import kotlinx.android.synthetic.main.widget_downloadbar.*
import kotlinx.android.synthetic.main.fragment_dlcomic.*
import kotlinx.android.synthetic.main.line_horizonal_empty.view.*
import kotlinx.android.synthetic.main.button_tbutton.*
import kotlinx.android.synthetic.main.button_tbutton.view.*
import kotlinx.android.synthetic.main.line_caption.view.*
import top.fumiama.dmzj.copymanga.R
import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
import top.fumiama.copymanga.json.ComicStructureOld
import top.fumiama.copymanga.json.VolumeStructure
import top.fumiama.copymanga.tools.CMApi
import top.fumiama.copymanga.tools.MangaDlTools
import top.fumiama.copymanga.tools.PropertiesTools
import top.fumiama.copymanga.tools.UITools
import top.fumiama.copymanga.ui.comicdl.ComicDlFragment.Companion.json
import top.fumiama.copymanga.ui.vm.ViewMangaActivity
import top.fumiama.copymanga.views.ChapterToggleButton
import top.fumiama.copymanga.views.LazyScrollView
import java.io.File
import java.lang.ref.WeakReference
class ComicDlHandler(looper: Looper, that: WeakReference<ComicDlFragment>, private val vols: Array<VolumeStructure>, private val comicName: String, private val groupNames: Array<String>?):Handler(looper) {
constructor(looper: Looper, that: WeakReference<ComicDlFragment>, comicName: String) : this(looper, that, arrayOf(), comicName, null) {
isOld = true
}
private var isOld = false
var complete = false
private val that = that.get()
private val toolsBox = UITools(that.get()?.context)
private val p = PropertiesTools(File("${that.get()?.context?.filesDir}/settings.properties"))
private var btnNumPerRow = 4
private var btnw = 0
private var cdwnWidth = 0
private var dl: Dialog? = null
private var hasToastedError = false
get(){
val re = field
field = true
return re
}
private var haveSElectAll = false
private var checkedChapter = 0
private var dldChapter = 0
private var haveDlStarted = false
private var canDl = false
private var tbtnlist: Array<ChapterToggleButton> = arrayOf()
private var tbtncnt = 0
private var isNewTitle = false
private val mangaDlTools = MangaDlTools()
private var multiSelect = false
private var size = 0
private var refreshSize = true
private var ltbtn: View? = null
@SuppressLint("SetTextI18n")
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
when(msg.what){
0 -> dl?.hide()
1 -> {
tbtnlist[msg.arg1].setBackgroundResource(R.drawable.rndbg_checked)
tbtnlist[msg.arg1].isChecked = false
updateProgressBar()
}
-1 -> {
tbtnlist.get(msg.arg1).setBackgroundResource(R.drawable.rndbg_error)
dldChapter--
Toast.makeText(
that?.context,
"下载${tbtnlist[msg.arg1].chapterName}失败",
Toast.LENGTH_SHORT
).show()
updateProgressBar()
}
//2 -> scanHiddenChapters()
//3 ->
4 -> {
that?.pdwn?.progress = 0
if (haveSElectAll) {
for (i in tbtnlist) {
if (!isChapterExists(i.chapterName, i.caption ?: "null")) {
i.setBackgroundResource(R.drawable.toggle_button)
i.isChecked = false
} else if(multiSelect) {
i.setBackgroundResource(R.drawable.rndbg_checked)
i.isChecked = false
}
}
haveSElectAll = false
checkedChapter = 0
dldChapter = 0
} else {
for (i in tbtnlist) {
if (multiSelect || !i.isChecked && !isChapterExists(i.chapterName, i.caption ?: "null")) {
i.setBackgroundResource(R.drawable.toggle_button)
i.isChecked = true
checkedChapter++
}
}
haveSElectAll = true
}
that?.tdwn?.text = "${dldChapter}/${checkedChapter}"
}
5 -> {
setSize(msg.arg2)
updateProgressBar(msg.arg2, size)
if (!(msg.obj as Boolean)) {
Toast.makeText(that?.context, "下载${tbtnlist.get(msg.arg1).chapterName}的第${msg.arg2}页失败", Toast.LENGTH_SHORT).show()
}else{
val progressTxt = that?.tdwn?.text.toString()
that?.tdwn?.text = "${progressTxt.substringBefore(' ')}${msg.arg2}/${size}"
}
}
6 -> that?.tdwn?.text = "${dldChapter}/${checkedChapter}"
7 -> deleteChapters(msg.obj as File, msg.arg1)
8 -> that?.cdwn?.setCardBackgroundColor(that.resources.getColor(R.color.colorBlue))
9 -> that?.cdwn?.setCardBackgroundColor(that.resources.getColor(R.color.colorGreen))
10 -> addTbtn(msg.obj as Array<String>)
11 -> addCaption(msg.obj as String)
12 -> addDiv()
13 -> that?.let { Toast.makeText(it.context, "下载${tbtnlist[msg.arg1].textOn}的第${msg.arg2}页失败,尝试重新下载...", Toast.LENGTH_SHORT).show() }
}
}
fun startLoad(){
setComponents()
if(isOld) analyzeOldStructure()
else Thread{
ViewMangaActivity.urlArray = arrayOf()
ViewMangaActivity.fileArray = arrayOf()
vols.forEachIndexed { i, vol ->
val caption = groupNames?.get(i)?:vol.results.list[0].group_path_word
Log.d("MyCDH", "caption: $caption, group name: ${groupNames?.get(i)}")
obtainMessage(11, caption).sendToTarget() //addCaption
vol.results.list.forEach { chapter ->
var data = arrayOf<String>()
data += chapter.name
data += chapter.uuid
data += caption
data += CMApi.getApiUrl(R.string.chapterInfoApiUrl, chapter.comic_path_word, chapter.uuid)?:""
obtainMessage(10, data).sendToTarget()
}
sendEmptyMessage(12) //addDiv
}
complete = true
}.start()
}
private fun addDiv(){
that?.ldwn?.addView(
that.layoutInflater.inflate(R.layout.div_h, that.ldwn, false),
ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
)
}
private fun addCaption(title: String){
val tc = that?.layoutInflater?.inflate(R.layout.line_caption, that.ldwn, false)
tc?.tcptn?.text = title
that?.ldwn?.addView(
tc,
ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
)
addDiv()
isNewTitle = true
}
private fun deleteChapter(f: File, v: ChapterToggleButton) {
f.delete()
v.setBackgroundResource(R.drawable.toggle_button)
v.isChecked = false
}
private fun deleteChapters(zipf: File, index: Int) {
if (multiSelect) {
for (i in tbtnlist) {
if (i.isChecked) {
val f = CMApi.getZipFile(that?.context?.getExternalFilesDir(""), comicName, i.caption?:"null", i.chapterName)
if (f.exists()) {
deleteChapter(f, i)
checkedChapter--
}
}
}
multiSelect = false
sendEmptyMessage(6)
} else deleteChapter(zipf, tbtnlist[index])
}
private fun isChapterExists(chapter: CharSequence, caption: CharSequence) =
File(that?.context?.getExternalFilesDir(""),"$comicName/$caption/$chapter.zip").exists()
@SuppressLint("SetTextI18n")
private fun updateProgressBar() {
that?.tdwn?.text = "${++dldChapter}/$checkedChapter"
setProgress2(dldChapter * 100 / checkedChapter, 233)
}
private fun updateProgressBar(pageNow: Int, size: Int) {
val delta = 100 / checkedChapter
val start = dldChapter * delta
val now = pageNow * delta / size
setProgress2(start + now, 64)
}
private fun setProgress2(end: Int, duration: Long) {
ObjectAnimator.ofInt(
that?.pdwn,
"progress",
that?.pdwn?.progress?:0,
end
).setDuration(duration).start()
}
private fun setSize(pageNow: Int){
if(refreshSize || size == 0) {
size = mangaDlTools.size
refreshSize = false
}else if(pageNow == size) refreshSize = true
}
private fun setComponents() {
val widthData = toolsBox.calcWidthFromDpRoot(8, 64)
btnNumPerRow = widthData[0]
btnw = widthData[1]
dl = mainWeakReference?.get()?.let { Dialog(it) }
dl?.setContentView(R.layout.dialog_unzipping)
that?.dlsdwn?.viewTreeObserver?.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener{
override fun onGlobalLayout() {
cdwnWidth = that.dlsdwn.width
Log.d("MyDl", "Get dlsdwn height: $cdwnWidth")
that.dlsdwn.viewTreeObserver.removeOnGlobalLayoutListener(this)
}
})
that?.dllazys?.onScrollListener = object : LazyScrollView.OnScrollListener{
override fun onBottom() { //if(dlsdwn.translationX > 0f) showDlCard()
}
override fun onScroll() { if(that?.dlsdwn?.translationX == 0f) hideDlCard() }
override fun onTop() { //if(dlsdwn.translationX > 0f) showDlCard()
}
}
that?.cdwn?.setOnClickListener {
if(that.dlsdwn.translationX != 0f) showDlCard()
else if(checkedChapter == 0) hideDlCard()
else{
that.pdwn.progress = 0
if (canDl || checkedChapter == 0) canDl = false
else {
haveDlStarted = true
canDl = true
Thread{
sendEmptyMessage(9) //set dl card color to green
downloadMangas()
sendEmptyMessage(8) //set dl card color to blue
if (!haveDlStarted) {
dldChapter = 0
checkedChapter = 0
this.postDelayed({
setProgress2(0, 233)
that.tdwn?.text = "0/0"
}, 400)
}
}.start()
}
}
}
that?.cdwn?.setOnLongClickListener {
Thread { sendEmptyMessage(4) }.start()
return@setOnLongClickListener true
}
}
fun showMultiSelectInfo() {
toolsBox.buildInfo("进入多选模式?", "之后可以对已下载漫画进行批量删除/重新下载",
"确定", null, "取消", { multiSelect = true })
}
private fun downloadMangas(){
for (i in tbtnlist) {
if (i.isChecked) downloadChapterPages(i)
if (!canDl) {
checkedChapter -= dldChapter
dldChapter = 0
break
}
}
if (canDl) {
haveDlStarted = false
canDl = false
}
}
private fun downloadChapterPages(i: ChapterToggleButton) {
mangaDlTools.onDownloadedListener =
object : MangaDlTools.OnDownloadedListener {
override fun handleMessage(succeed: Boolean) {
this@ComicDlHandler.obtainMessage(if (succeed) 1 else -1, i.index, 0)
.sendToTarget()
}
override fun handleMessage(succeed: Boolean, pageNow: Int) {
this@ComicDlHandler.obtainMessage(
5,
i.index,
pageNow,
succeed
).sendToTarget()
}
override fun handleMessage(pageNow: Int){
this@ComicDlHandler.obtainMessage(13, i.index, pageNow).sendToTarget()
}
}
i.url?.let {
mangaDlTools.downloadChapterInVol(
it,
i.chapterName,
i.caption?:"null",
i.index
)
}
}
private fun showDlCard(){
//ObjectAnimator.ofFloat(dlsdwn, "alpha", 0.3f, 0.9f).setDuration(233).start()
ObjectAnimator.ofFloat(that?.dlsdwn, "translationX", cdwnWidth.toFloat() * 0.9f, 0f).setDuration(233).start()
}
private fun hideDlCard(){
//ObjectAnimator.ofFloat(dlsdwn, "alpha", 0.9f, 0.3f).setDuration(233).start()
ObjectAnimator.ofFloat(that?.dlsdwn, "translationX", 0f, cdwnWidth.toFloat() * 0.9f).setDuration(233).start()
}
private fun addTbtn(data: Array<String>){
addTbtn(data[0], data[1], data[2], data[3])
ViewMangaActivity.urlArray += data[3]
}
@SuppressLint("SetTextI18n")
private fun addTbtn(title: String, uuid: String, caption: String, url: String) {
if ((tbtncnt % btnNumPerRow == 0) || isNewTitle) {
ltbtn = that?.layoutInflater?.inflate(R.layout.line_horizonal_empty, that.ldwn, false)
that?.ldwn?.addView(ltbtn)
tbtncnt = 0
isNewTitle = false
}
that?.layoutInflater?.inflate(R.layout.button_tbutton, ltbtn?.ltbtn, false)?.let { tbv ->
tbv.tbtn.index = tbtnlist.size
tbtnlist += tbv.tbtn
tbtncnt++
tbv.tbtn.uuid = uuid
tbv.tbtn.chapterName = title
tbv.tbtn.url = url
//tbv.tbtn.hint = caption
tbv.tbtn.caption = caption
tbv.tbtn.layoutParams.width = btnw
val zipf = CMApi.getZipFile(that.context?.getExternalFilesDir(""), comicName, caption, title)
Log.d("MyCD", "Get zipf: $zipf")
ViewMangaActivity.fileArray += zipf
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() && !multiSelect) {
it.tbtn.setBackgroundResource(R.drawable.rndbg_checked)
it.tbtn.isChecked = false
ViewMangaActivity.zipFile = zipf
ViewMangaActivity.dlhandler = this
ViewMangaActivity.position = it.tbtn.index
dl?.show()
that.startActivity(Intent(that.context, ViewMangaActivity::class.java)
.putExtra("callFrom", "zipFirst")
)
} else {
it.tbtn.setBackgroundResource(R.drawable.toggle_button)
if (it.tbtn.isChecked) that.tdwn?.text = "$dldChapter/${++checkedChapter}"
else that.tdwn.text = "$dldChapter/${--checkedChapter}"
}
}
tbv.tbtn.setOnLongClickListener {
if (zipf.exists()) {
toolsBox.buildInfo("确认删除${if (multiSelect) "这些" else "本"}章节?",
"该操作将不可撤销",
"确定",
null,
"取消",
{
Thread {
obtainMessage(7, it.tbtn.index, 0, zipf).sendToTarget()
}.start()
})
}else{
toolsBox.buildInfo("直接观看", "不下载而进行观看", "确定",
null, "取消", {
ViewMangaActivity.zipFile = null
ViewMangaActivity.dlhandler = this
ViewMangaActivity.position = it.tbtn.index
dl?.show()
that.startActivity(Intent(that.context, ViewMangaActivity::class.java))
}, null, null
)
}
true
}
}
}
private fun analyzeOldStructure() = Thread{
Gson().fromJson(json?.reader(), Array<ComicStructureOld>::class.java)?.let {
for (group in it) {
that?.layoutInflater?.inflate(R.layout.line_caption, that.ldwn, false)?.let { tc ->
tc.tcptn.text = group.name
that.ldwn.addView(
tc,
ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
)
that.ldwn.addView(
that.layoutInflater.inflate(R.layout.div_h, that.ldwn, false),
ViewGroup.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT
)
)
isNewTitle = true
for (chapter in group.chapters) {
val newUrl = CMApi.getApiUrl(R.string.chapterInfoApiUrl, chapter.url.substringAfter("/comic/").substringBefore('/'), chapter.url.substringAfterLast('/'))?:""
Log.d("MyCD", "Generate new url: $newUrl")
obtainMessage(10, arrayOf(chapter.name, "", group.name, newUrl)).sendToTarget()
}
}
}
}
}.start()
}

View File

@@ -1,15 +1,13 @@
package top.fumiama.copymanga.handler
package top.fumiama.copymanga.ui.download
import android.os.Handler
import android.os.Looper
import android.os.Message
import top.fumiama.copymanga.activity.DlListActivity
import java.io.File
import java.lang.ref.WeakReference
class DlLHandler(looper: Looper, activity: DlListActivity): Handler(looper) {
private val dll = WeakReference(activity)
class DlLHandler(looper: Looper, dl: DownloadFragment): Handler(looper) {
private val dll = WeakReference(dl)
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
when(msg.what){

View File

@@ -0,0 +1,201 @@
package top.fumiama.copymanga.ui.download
import android.app.AlertDialog
import android.content.Intent
import android.os.Bundle
import android.os.Looper
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.Toast
import androidx.navigation.Navigation
import kotlinx.android.synthetic.main.app_bar_main.*
import kotlinx.android.synthetic.main.fragment_download.*
import top.fumiama.dmzj.copymanga.R
import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
import top.fumiama.copymanga.template.NoBackRefreshFragment
import top.fumiama.copymanga.ui.comicdl.ComicDlFragment
import top.fumiama.copymanga.ui.vm.ViewMangaActivity
import java.io.File
import java.util.regex.Pattern
import java.util.zip.ZipInputStream
class DownloadFragment: NoBackRefreshFragment(R.layout.fragment_download) {
private var nullZipDirStr = emptyArray<String>()
private var handler: DlLHandler? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
if(isFirstInflate) {
arguments?.getString("title")?.let {
mainWeakReference?.get()?.toolbar?.title = it
}
handler = DlLHandler(Looper.myLooper()!!, this)
handler?.obtainMessage(3, currentDir)?.sendToTarget() //call scanFile
}
}
fun scanFile(cd: File?){
val isRoot = cd == context?.getExternalFilesDir("")
val jsonFile = File(cd, "info.bin")
if(isRoot || !jsonFile.exists()) cd?.list()?.sortedArrayWith { o1, o2 ->
if(o1.endsWith(".zip") && o2.endsWith(".zip")) (10000*getFloat(o1) - 10000*getFloat(o2) + 0.5).toInt()
else o1[0] - o2[0]
}?.let {
mylv.apply {
context.let { c ->
adapter = ArrayAdapter(c, android.R.layout.simple_list_item_1, it)
setOnItemClickListener { _, _, position, _ ->
val chosenFile = File(cd, it[position])
val chosenJson = File(chosenFile, "info.bin")
val newJson = File(chosenFile, "info.json")
//Toast.makeText(this, "进入$chosenFile", Toast.LENGTH_SHORT).show()
when {
chosenJson.exists() -> callDownloadFragment(chosenJson)
newJson.exists() -> callDownloadFragment(newJson, true)
chosenFile.isDirectory -> {
currentDir = chosenFile
callSelf(it[position])
}
chosenFile.name.endsWith(".zip") -> {
Toast.makeText(context, "加载中...", Toast.LENGTH_SHORT).show()
ViewMangaActivity.zipFile = chosenFile
ViewMangaActivity.comicName = it[position]
ViewMangaActivity.position = position
ViewMangaActivity.fileArray = it.map { File(cd, it) }.toTypedArray()
ViewMangaActivity.urlArray = Array(it.size) {return@Array ""}
startActivity(Intent(context, ViewMangaActivity::class.java))
}
}
}
setOnItemLongClickListener { _, _, position, _ ->
val chosenFile = File(cd, it[position])
AlertDialog.Builder(context)
.setIcon(R.drawable.ic_launcher_foreground).setMessage("在此执行删除/查错?")
.setTitle("提示").setPositiveButton("删除") { _, _ ->
if (chosenFile.exists()) handler?.obtainMessage(2, chosenFile)
?.sendToTarget() //call rmrf
handler?.obtainMessage(3, cd)?.sendToTarget() //call scanFile
}.setNegativeButton(android.R.string.cancel) { _, _ -> }
.setNeutralButton("查错") { _, _ ->
handler?.obtainMessage(1, chosenFile)?.sendToTarget()
} //call checkDir
.show()
true
}
}
}
}
}
fun rmrf(f: File) {
if (f.isDirectory) f.listFiles()?.let {
for (i in it)
if (i.isDirectory) rmrf(i)
else i.delete()
}
f.delete()
}
fun checkDir(f: File){
nullZipDirStr = emptyArray()
findNullWebpZipFileInDir(f)
if(nullZipDirStr.isNotEmpty()) showErrorZip(nullZipDirStr.joinToString("\n"))
else Toast.makeText(context, "未发现错误", Toast.LENGTH_SHORT).show()
}
private fun callDownloadFragment(jsonFile: File, isNew: Boolean = false){
val bundle = Bundle()
Log.d("MyDF", "Call dl and is new: $isNew")
bundle.putBoolean(if(isNew) "loadJson" else "callFromOldDL", true)
bundle.putString("name", jsonFile.parentFile?.name?:"Null")
ComicDlFragment.json = jsonFile.readText()
Log.d("MyDF", "root view: $rootView")
rootView?.let {
Log.d("MyDF", "action_nav_download_to_nav_group")
Navigation.findNavController(it).navigate(R.id.action_nav_download_to_nav_group, bundle)
}
}
private fun callSelf(title: String){
val bundle = Bundle()
bundle.putString("title", title)
Log.d("MyDF", "Call self to $title")
Log.d("MyDF", "root view: $rootView")
rootView?.let {
Log.d("MyDF", "action_nav_download_self")
Navigation.findNavController(it).navigate(R.id.action_nav_download_self, bundle)
}
}
private fun findNullWebpZipFileInDir(f: File){
if (f.isDirectory) f.listFiles()?.let {
for (i in it)
if (i.isDirectory) findNullWebpZipFileInDir(i)
else if(!checkZip(i)) nullZipDirStr += i.path.substringAfterLast(context?.getExternalFilesDir("").toString())
}
}
private fun checkZip(f: File): Boolean{
return try {
val exist = f.exists()
if (!exist) true
else {
var re = true
val zip = ZipInputStream(f.inputStream().buffered())
var entry = zip.nextEntry
while (entry != null) {
if (!entry.isDirectory){
if(zip.read() == -1 && entry.size == 0L){
re = false
break
}
}
entry = zip.nextEntry
}
zip.closeEntry()
zip.close()
re
}
} catch (e: Exception) {
Toast.makeText(context, "读取${f.name}错误!", Toast.LENGTH_SHORT).show()
true
}
}
private fun showErrorZip(msg: CharSequence) = AlertDialog.Builder(context)
.setIcon(R.drawable.ic_launcher_foreground)
.setTitle("找到以下错误文件,是否删除?")
.setMessage(msg)
.setPositiveButton(android.R.string.ok){_, _ -> deleteErrorZip()}
.setNegativeButton(android.R.string.cancel){_, _ ->}
.show()
private fun deleteErrorZip(){
val exf = context?.getExternalFilesDir("")
for(i in nullZipDirStr){
val f = File(exf, i)
if(f.exists()) f.delete()
}
}
private fun getFloat(oldString: String): Float {
val newString = StringBuffer()
var matcher = Pattern.compile("\\d+.+\\d+").matcher(oldString)
while (matcher.find()) newString.append(matcher.group())
//Log.d("MyDLL1", newString.toString())
if(newString.isEmpty()){
matcher = Pattern.compile("\\d").matcher(oldString)
while (matcher.find()) newString.append(matcher.group())
}
//Log.d("MyDLL2", newString.toString().toFloat().toString())
return if(newString.isEmpty()) 0f else newString.toString().toFloat()
}
companion object{
var currentDir: File? = null
}
}

View File

@@ -0,0 +1,19 @@
package top.fumiama.copymanga.ui.history
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import kotlinx.android.synthetic.main.fragment_history.*
import top.fumiama.dmzj.copymanga.R
class HistoryFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_history, container, false)
}
}

View File

@@ -0,0 +1,74 @@
package top.fumiama.copymanga.ui.home
import android.os.Bundle
import android.view.View
import android.view.ViewGroup
import androidx.navigation.Navigation
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import com.bumptech.glide.load.model.GlideUrl
import com.bumptech.glide.load.model.LazyHeaders
import kotlinx.android.synthetic.main.fragment_home.*
import kotlinx.android.synthetic.main.viewpage_horizonal.view.*
import top.fumiama.dmzj.copymanga.R
import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
import top.fumiama.copymanga.template.NoBackRefreshFragment
import top.fumiama.copymanga.tools.CMApi
import java.lang.Thread.sleep
import java.lang.ref.WeakReference
class HomeFragment : NoBackRefreshFragment(R.layout.fragment_home) {
lateinit var homeHandler: HomeHandler
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
if(isFirstInflate){
val theme = resources.newTheme()
swiperefresh.setColorSchemeColors(
resources.getColor(R.color.colorAccent, theme),
resources.getColor(R.color.colorBlue2, theme),
resources.getColor(R.color.colorGreen, theme))
Thread{
homeHandler.obtainMessage(-1, true).sendToTarget()
while(mainWeakReference?.get()?.isDrawerClosed != true) sleep(233)
//homeHandler.sendEmptyMessage(6) //removeAllViews
homeHandler.fhib = null
sleep(600)
homeHandler.startLoad()
}.start()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
homeHandler = HomeHandler(WeakReference(this))
}
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.inflate(R.layout.viewpage_horizonal, parent, false))
}
override fun onBindViewHolder(holder: ViewData, position: Int) {
val thisBanner = homeHandler.index?.results?.banners?.get(position)
thisBanner?.cover?.let {
//Log.d("MyHomeFVP", "Load img: $it")
Glide.with(this@HomeFragment).load(
GlideUrl(it, CMApi.myGlideHeaders)
).timeout(10000).into(holder.itemView.vpi)
}
holder.itemView.vpt.text = thisBanner?.brief
holder.itemView.vpc.setOnClickListener {
val bundle = Bundle()
homeHandler.index?.results?.banners?.get(position)?.comic?.path_word?.let { it1 -> bundle.putString("path", it1) }
rootView?.let { it1 -> Navigation.findNavController(it1).navigate(R.id.action_nav_home_to_nav_book, bundle) }
}
}
override fun getItemCount(): Int = homeHandler.index?.results?.banners?.size?:0
}
}
}

View File

@@ -0,0 +1,326 @@
package top.fumiama.copymanga.ui.home
import android.animation.ObjectAnimator
import android.graphics.Color
import android.os.Bundle
import android.os.Looper
import android.os.Message
import android.util.Log
import android.view.View
import android.view.ViewTreeObserver
import android.widget.Toast
import androidx.cardview.widget.CardView
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.navigation.Navigation
import com.bumptech.glide.Glide
import com.bumptech.glide.load.model.GlideUrl
import com.to.aboomy.pager2banner.Banner
import com.to.aboomy.pager2banner.IndicatorView
import com.to.aboomy.pager2banner.ScaleInTransformer
import kotlinx.android.synthetic.main.card_book.view.*
import kotlinx.android.synthetic.main.fragment_home.*
import kotlinx.android.synthetic.main.line_1bookline.view.*
import top.fumiama.dmzj.copymanga.R
import top.fumiama.copymanga.json.ComicStructure
import top.fumiama.copymanga.json.IndexStructure
import top.fumiama.copymanga.template.AutoDownloadHandler
import top.fumiama.copymanga.tools.CMApi
import top.fumiama.copymanga.tools.UITools
import java.lang.Thread.sleep
import java.lang.ref.WeakReference
class HomeHandler(that: WeakReference<HomeFragment>) :AutoDownloadHandler(
that.get()?.getString(R.string.mainPageApiUrl) ?: "",
IndexStructure::class.java,
Looper.myLooper()!!,
9
) {
private val homeF = that.get()
var index: IndexStructure? = null
var fhib: View? = null
get() {
Log.d("MyHH", "Get fhib.")
if(field == null){
field = homeF?.layoutInflater?.inflate(R.layout.viewpage_banner, homeF.fhl, false)
Thread{homeF?.homeHandler?.sendEmptyMessage(3)}.start()
}
return field
}
var indexLines = arrayOf<View>()
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
when (msg.what) {
-1 -> {
homeF?.swiperefresh?.isEnabled = msg.obj as Boolean
homeF?.swiperefresh?.isRefreshing = msg.obj as Boolean
}
//0 -> setLayouts()
1 -> inflateCardLines()
3 -> setBanner(fhib as Banner)
5 -> setBannerInfo(msg.obj as Banner)
6 -> {
homeF?.fhl?.let {
ObjectAnimator.ofFloat(it, "alpha", 1f, 0f).setDuration(233).start()
it.postDelayed({
it.removeAllViews()
ObjectAnimator.ofFloat(it, "alpha", 0f, 1f).setDuration(233).start()
}, 233)
}
}
7 -> inflateBanner()
8 -> homeF?.fhl?.addView(indexLines[msg.arg1])
//9 -> checkIndex()
}
}
override fun getGsonItem() = index
override fun setGsonItem(gsonObj: Any) {
super.setGsonItem(gsonObj)
index = gsonObj as IndexStructure
}
override fun onError() {
super.onError()
Toast.makeText(homeF?.context, R.string.web_error, Toast.LENGTH_SHORT).show()
}
override fun doWhenFinishDownload() {
super.doWhenFinishDownload()
try {
Thread {
sendEmptyMessage(7) //inflateBanner
sendEmptyMessage(1) //inflateCardLines
}.start()
} catch (e: Exception) {
Toast.makeText(homeF?.context, R.string.load_home_error, Toast.LENGTH_SHORT).show()
}
}
private fun inflateBanner() = homeF?.fhl?.addView(fhib)
private fun inflateTopics(){
index?.results?.topics?.list?.let {
var comics = arrayOf<ComicStructure>()
for((i, topic) in it.withIndex()){
if(i > 2) break
val newComic = ComicStructure()
newComic.name = topic.title
newComic.cover = topic.cover
newComic.path_word = topic.path_word
comics += newComic
}
if(comics.size == 3) allocateLine(homeF?.getString(R.string.topics_series)?:"", R.drawable.img_hot_serial, comics)
}
}
private fun inflateRec(){
index?.results?.recComics?.list?.let {
var comics = arrayOf<ComicStructure>()
for((i, rec) in it.withIndex()){
if(i > 2) break
comics += rec.comic
}
if(comics.size == 3) allocateLine(homeF?.getString(R.string.manga_rec)?:"", R.drawable.img_master_work, comics)
}
}
private fun inflateRank(){
var comics = arrayOf<ComicStructure>()
index?.results?.rankDayComics?.list?.let {
for((i, book) in it.withIndex()){
if(i > 2) break
comics += book.comic
}
}
index?.results?.rankWeekComics?.list?.let {
for((i, book) in it.withIndex()){
if(i > 2) break
comics += book.comic
}
}
index?.results?.rankMonthComics?.list?.let {
for((i, book) in it.withIndex()){
if(i > 2) break
comics += book.comic
}
}
if(comics.size == 9) allocateLine(homeF?.getString(R.string.rank_list)?:"", R.drawable.img_novel_bill, comics)
}
private fun inflateHot(){
index?.results?.hotComics?.let {
var comics = arrayOf<ComicStructure>()
for((i, rec) in it.withIndex()){
if(i > 8) break
comics += rec.comic
}
if(comics.size == 9) allocateLine(homeF?.getString(R.string.hot_list)?:"", R.drawable.img_hot, comics)
}
}
private fun inflateNew(){
index?.results?.newComics?.let {
var comics = arrayOf<ComicStructure>()
for((i, rec) in it.withIndex()){
if(i > 8) break
comics += rec.comic
}
if(comics.size == 9) allocateLine(homeF?.getString(R.string.new_list)?:"", R.drawable.img_latest_pub, comics)
}
}
private fun inflateFinish(){
index?.results?.finishComics?.list?.let {
var comics = arrayOf<ComicStructure>()
for((i, rec) in it.withIndex()){
if(i > 5) break
comics += rec
}
if(comics.size == 6) allocateLine(homeF?.getString(R.string.complete)?:"", R.drawable.img_novel_eye, comics, true)
}
}
private fun inflateCardLines() {
inflateRec()
inflateTopics()
inflateHot()
inflateNew()
inflateFinish()
inflateRank()
Thread{
for(i in indexLines.indices) obtainMessage(8, i, 0).sendToTarget()
obtainMessage(-1, false).sendToTarget() //closeLoad
}.start()
}
private fun setBanner(v: Banner): Banner {
v.viewTreeObserver.addOnGlobalLayoutListener(object :
ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
//Log.d("MyMy", "Width: ${v.width}")
v.layoutParams.height = (v.width / 1.875 + 0.5).toInt()
v.invalidate()
v.viewTreeObserver.removeOnGlobalLayoutListener(this)
}
})
Thread{this.obtainMessage(5, v).sendToTarget()}.start() //setBannerInfo
return v
}
private fun setBannerInfo(v: Banner){
homeF?.context?.let { UITools(it) }?.let {
v
.addPageTransformer(ScaleInTransformer())
.setPageMargin(it.dp2px(20) ?: 0, it.dp2px(10) ?: 0)
.setIndicator(
IndicatorView(homeF.context)
.setIndicatorColor(Color.DKGRAY)
.setIndicatorSelectorColor(Color.WHITE)
.setIndicatorStyle(IndicatorView.IndicatorStyle.INDICATOR_BEZIER)
).adapter = homeF.ViewData(v).RecyclerViewAdapter()
}
v.invalidate()
homeF?.fhov?.swipeRefreshLayout = homeF?.swiperefresh
homeF?.swiperefresh?.setOnRefreshListener {
Log.d("MyHFH", "Refresh items.")
//index = null
//Thread{this@HomeHandler.obtainMessage(-1, true).sendToTarget()}.start() //startLoad
Thread{
index = null
//fhib = null
indexLines = arrayOf()
this@HomeHandler.sendEmptyMessage(6) //removeAllViews
sleep(300)
this@HomeHandler.sendEmptyMessage(0) //setLayouts
}.start()
}
}
private fun allocateLine(title: String, iconResId: Int, comics: Array<ComicStructure>, finish: Boolean = false): Int{
val p = indexLines.size
val c = comics.size / 3
homeF?.layoutInflater?.inflate(
when(c){
1 -> R.layout.line_1bookline
2 -> R.layout.line_2bookline
3 -> R.layout.line_3bookline
else -> return -1
}, homeF.fhl, false)?.let {
scanCards(it, comics, finish)
it.rttitle.text = title
it.ir.setImageResource(iconResId)
setLineHeight(it, c)
indexLines += it
}
return p
}
/*private fun setLines(v: View, position: Int) {
lines?.let {
v.rttitle.text = it[position].title
v.ir.setImageResource(
when (position) {
1 -> R.drawable.img_novel_refresh
2 -> R.drawable.img_novel_more
3 -> R.drawable.img_novel_play
4 -> R.drawable.img_novel_eye
else -> R.drawable.img_novel_refresh
}
)
Thread{this.obtainMessage(4, position, 0, v).sendToTarget()}.start() //scanCards
setLineHeight(v, if (position == 4) 2 else 1)
if(position == 4) {
Thread{ this.obtainMessage(-1, false).sendToTarget() }.start() //closeLoad
}
if(position == 1) v.setOnClickListener {
Navigation.findNavController(it).navigate(R.id.action_nav_home_to_nav_latest)
}else v.rimore.visibility = View.GONE
}
}*/
private fun scanCards(v: View, comics: Array<ComicStructure>, finish: Boolean = false){
var id = v.rc1.id
var card = v.findViewById<ConstraintLayout>(id)
for (data in comics){
setCards(
card.cic,
data.path_word,
data.name,
data.cover,
finish
)
card = v.findViewById(++id)
}
}
private fun setCards(cv: CardView, pw: String, name: String, img: String, isFinal: Boolean = false) {
cv.tic.text = name
homeF?.let {
Glide.with(it).load(GlideUrl(img, CMApi.myGlideHeaders)).timeout(10000).into(cv.imic)
}
if (isFinal) cv.sgnic.visibility = View.VISIBLE
cv.setOnClickListener {
val bundle = Bundle()
bundle.putString("path", pw)
homeF?.rootView?.let { Navigation.findNavController(it).navigate(R.id.action_nav_home_to_nav_book, bundle) }
}
}
private fun setLineHeight(v: View, cardCount: Int) {
v.viewTreeObserver.addOnGlobalLayoutListener(object :
ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
homeF?.context?.let { UITools(it) }?.let {
val spaceTitle = it.dp2px(49)!!
val cardSpace = it.dp2px(16)!!
v.layoutParams.height =
((v.width - cardSpace * 3) * cardCount * 4.0 / 9.0 + spaceTitle + cardSpace * cardCount + 0.5).toInt()
v.invalidate()
v.viewTreeObserver.removeOnGlobalLayoutListener(this)
}
//Log.d("MyVTOL", "Set card line: (${v.width}, ${v.height})")
}
})
}
}

View File

@@ -0,0 +1,10 @@
package top.fumiama.copymanga.ui.latest
import androidx.fragment.app.Fragment
import top.fumiama.dmzj.copymanga.R
import top.fumiama.copymanga.template.InfoCardLoader
@ExperimentalStdlibApi
class LatestFragment: Fragment()/*InfoCardLoader(R.layout.line_lazybooklines, R.id.action_nav_latest_to_nav_book, "name", "cover", "id") {
override fun getApiUrl() = getString(R.string.recentUpdateApiUrl).let { String.format(it, page) }
}*/

View File

@@ -0,0 +1,94 @@
package top.fumiama.copymanga.ui.rank
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import com.github.zawadz88.materialpopupmenu.popupMenu
import com.google.gson.Gson
import kotlinx.android.synthetic.main.anchor_popular.view.*
import kotlinx.android.synthetic.main.line_rank.*
import top.fumiama.dmzj.copymanga.R
import top.fumiama.copymanga.json.FilterStructure
import top.fumiama.copymanga.template.InfoCardLoader
import java.lang.Thread.sleep
@ExperimentalStdlibApi
class RankFragment: Fragment()/*: InfoCardLoader(R.layout.fragment_rank, R.id.action_nav_rank_to_nav_book, "name", "cover", "id") {
private var type = 0
private var pop_sub = 0
private var filter: Array<FilterStructure>? = null
get() {
if (field == null) {
context?.assets?.open(getString(R.string.assets_filter))?.let {
field = Gson().fromJson(it.reader(), Array<FilterStructure>::class.java)
it.close()
}
}
return field
}
override fun getApiUrl() =
getString(R.string.rankApiUrl).let { String.format(it, pop_sub, type, page) }
override fun setListeners() {
super.setListeners()
setPop()
setClasses()
}
private fun setPop(){
line_rank_pop.apt.setText(if(pop_sub == 1) R.string.menu_pop_sub else R.string.menu_pop_pop)
line_rank_pop.setOnClickListener {
val popupMenu = popupMenu {
style = R.style.Widget_MPM_Menu_Dark_CustomBackground
section {
item {
labelRes = if(pop_sub == 0) R.string.menu_pop_sub else R.string.menu_pop_pop
labelColor = it.apt.currentTextColor
iconDrawable =
this@RankFragment.context?.let { it1 -> ContextCompat.getDrawable(it1, R.drawable.ic_refresh) } //optional
iconColor = it.apt.currentTextColor
callback = { //optional
if(pop_sub == 0){
pop_sub = 1
it.apt.setText(R.string.menu_pop_sub)
}else{
pop_sub = 0
it.apt.setText(R.string.menu_pop_pop)
}
Thread{
sleep(400)
mh?.sendEmptyMessage(4)
}.start()
}
}
}
}
this.context?.let { it1 -> popupMenu.show(it1, it) }
}
}
private fun setClasses(){
val items = filter?.get(0)?.items
line_rank_class.apt.text = items?.get(0)?.tag_name?:getString(R.string.text_null)
line_rank_class.setOnClickListener {
val popupMenu = popupMenu {
style = R.style.Widget_MPM_Menu_Dark_CustomBackground
if(items != null) section {
for(i in items.indices) item {
label = items[i]?.tag_name
labelColor = it.apt.currentTextColor
callback = { //optional
it.apt.text = label
type = items[i]?.tag_id?:0
Thread{
sleep(400)
mh?.sendEmptyMessage(4)
}.start()
}
}
}
}
this.context?.let { it1 -> popupMenu.show(it1, it) }
}
}
}*/

View File

@@ -0,0 +1,12 @@
package top.fumiama.copymanga.ui.settings
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import top.fumiama.dmzj.copymanga.R
import top.fumiama.copymanga.template.NoBackRefreshFragment
class SettingsFragment:NoBackRefreshFragment(R.layout.fragment_settings) {
}

View File

@@ -0,0 +1,126 @@
package top.fumiama.copymanga.ui.sort
import android.animation.ObjectAnimator
import android.view.View
import com.github.zawadz88.materialpopupmenu.popupMenu
import com.google.gson.Gson
import kotlinx.android.synthetic.main.anchor_popular.view.*
import kotlinx.android.synthetic.main.line_lazybooklines.*
import kotlinx.android.synthetic.main.line_sort.*
import top.fumiama.dmzj.copymanga.R
import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
import top.fumiama.copymanga.json.FilterStructure
import top.fumiama.copymanga.template.AutoDownloadThread
import top.fumiama.copymanga.template.InfoCardLoader
import java.lang.Thread.sleep
@ExperimentalStdlibApi
class SortFragment : InfoCardLoader(R.layout.fragment_sort, R.id.action_nav_sort_to_nav_book) {
private val sortWay = listOf("datetime_updated", "-datetime_updated", "popular", "-popular")
private var theme = -1
private var sortValue = 0
private var filter: FilterStructure? = null
override fun getApiUrl() =
getString(R.string.sortApiUrl).let {
String.format(
it,
page * 21,
sortWay[sortValue],
if(theme >= 0) (filter?.results?.theme?.get(theme)?.path_word ?: "") else ""
)
}
override fun setListeners() {
super.setListeners()
setUpdate()
setHot()
AutoDownloadThread(getString(R.string.filterApiUrl)) {
it?.let {
filter = Gson().fromJson(it.inputStream().reader(), FilterStructure::class.java)
mainWeakReference?.get()?.runOnUiThread{
setClasses()
}
}
}.start()
}
override fun onLoadFinish() {
super.onLoadFinish()
mainWeakReference?.get()?.runOnUiThread {
mypl.visibility = View.GONE
}
}
private fun setUpdate(){
line_sort_time.apt.setText(R.string.menu_update_time)
line_sort_time.setOnClickListener {
sortValue = if(it.apim.rotation == 0f) {
ObjectAnimator.ofFloat(it.apim, "rotation", 0f, 180f).setDuration(233).start()
1
}else{
ObjectAnimator.ofFloat(it.apim, "rotation", 180f, 0f).setDuration(233).start()
0
}
Thread{
sleep(400)
mh?.sendEmptyMessage(4)
}.start()
}
}
private fun setClasses(){
filter?.results?.theme?.let { items ->
line_sort_class.apt.text = "全部"
line_sort_class.setOnClickListener {
val popupMenu = popupMenu {
style = R.style.Widget_MPM_Menu_Dark_CustomBackground
section {
item {
label = "全部"
labelColor = it.apt.currentTextColor
callback = {
theme = -1
it.apt.text = "全部"
Thread{
sleep(400)
mh?.sendEmptyMessage(4)
}.start()
}
}
for(i in items.indices) item {
label = items[i].name
labelColor = it.apt.currentTextColor
callback = { //optional
it.apt.text = label
theme = i
Thread{
sleep(400)
mh?.sendEmptyMessage(4)
}.start()
}
}
}
}
this.context?.let { it1 -> popupMenu.show(it1, it) }
}
}
}
private fun setHot() {
line_sort_hot.apt.setText(R.string.menu_hot)
line_sort_hot.setOnClickListener {
sortValue = if (it.apim.rotation == 0f) {
ObjectAnimator.ofFloat(it.apim, "rotation", 0f, 180f).setDuration(233).start()
1
} else {
ObjectAnimator.ofFloat(it.apim, "rotation", 180f, 0f).setDuration(233).start()
0
}
Thread {
sleep(400)
mh?.sendEmptyMessage(4)
}.start()
}
}
}

View File

@@ -0,0 +1,19 @@
package top.fumiama.copymanga.ui.sub
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import kotlinx.android.synthetic.main.fragment_sub.*
import top.fumiama.dmzj.copymanga.R
class SubFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_sub, container, false)
}
}

View File

@@ -0,0 +1,76 @@
package top.fumiama.copymanga.ui.vm
import android.content.Intent
import android.widget.Toast
import top.fumiama.copymanga.ui.vm.ViewMangaActivity.Companion.fileArray
import top.fumiama.copymanga.ui.vm.ViewMangaActivity.Companion.position
import top.fumiama.copymanga.ui.vm.ViewMangaActivity.Companion.urlArray
import top.fumiama.copymanga.ui.vm.ViewMangaActivity.Companion.zipFile
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
fun toPage(goNext:Boolean){
if (v?.clicked == false) {
if (if(goNext)judgeNext() else judgePrevious()) {
if(goNext) {
v.scrollForward()
isEndR = false
} else {
v.scrollBack()
isEndL = false
}
} else {
val chapterPosition = position + if(goNext) 1 else -1
urlArray.let {
if(chapterPosition >= 0 && chapterPosition < it.size) it[chapterPosition].let {
if (if(goNext)isEndR else isEndL) {
val f = fileArray[chapterPosition]
val intent = Intent(v, ViewMangaActivity::class.java)
//if(v.zipFirst) intent.putExtra("callFrom", "zipFirst")
if(!goNext){
ViewMangaActivity.pn = -2
intent.putExtra("function", "log")
}
zipFile = if (f.exists()) f else null
position = chapterPosition
v.tt.canDo = false
//ViewMangaActivity.dlhandler = null
v.startActivity(intent)
v.finish()
} else {
val hint = if(goNext) '下' 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,209 @@
package top.fumiama.copymanga.ui.vm
import android.animation.ObjectAnimator
import android.annotation.SuppressLint
import android.app.Dialog
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_viewmanga.*
import kotlinx.android.synthetic.main.widget_infodrawer.*
import kotlinx.android.synthetic.main.widget_infodrawer.view.*
import top.fumiama.dmzj.copymanga.R
import top.fumiama.copymanga.json.Chapter2Return
import top.fumiama.copymanga.json.ChapterWithContent
import top.fumiama.copymanga.json.ComicStructure
import top.fumiama.copymanga.template.AutoDownloadHandler
import top.fumiama.copymanga.tools.PropertiesTools
import top.fumiama.copymanga.ui.vm.ViewMangaActivity.Companion.comicName
import top.fumiama.copymanga.ui.vm.ViewMangaActivity.Companion.pn
import top.fumiama.copymanga.views.ScaleImageView
import java.io.File
import java.lang.Exception
import java.lang.Thread.sleep
import java.lang.ref.WeakReference
import java.text.SimpleDateFormat
import java.util.*
import java.util.zip.ZipInputStream
class VMHandler(activity: ViewMangaActivity, url: String) : AutoDownloadHandler(
url, Chapter2Return::class.java, Looper.myLooper()!!
) {
var manga: Chapter2Return? = null
private val wv = WeakReference(activity)
private val infcard = wv.get()?.infcard
private var infcShowed = false
val dl = wv.get()?.let {
val re = Dialog(it)
re.setContentView(R.layout.dialog_unzipping)
re
}
private var delta = -1f
get() {
if (field < 0) field = wv.get()?.infoDrawerDelta ?: 0f
return field
}
private val week: String
get() {
val cal = Calendar.getInstance()
return when (cal[Calendar.DAY_OF_WEEK]) {
1 -> "周日"
2 -> "周一"
3 -> "周二"
4 -> "周三"
5 -> "周四"
6 -> "周五"
7 -> "周六"
else -> ""
}
}
var progressLog: PropertiesTools? = null
@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
}
4 -> {
val simg = msg.obj as ScaleImageView
wv.get()?.loadImgOn(simg, msg.arg1)
simg.setHeight2FitImgWidth()
if(msg.arg2 == 1) sendEmptyMessage(8)
}
5 -> wv.get()?.clearImgOn(msg.obj as ScaleImageView)
6 -> wv.get()?.prepareLastPage(msg.arg1, msg.arg2)
7 -> dl?.show()
8 -> Thread{
sleep(233)
sendEmptyMessage(13)
}.start()
9 -> loadThread(msg.arg1)
10 -> loadThread()
11 -> loadImgsIntoLine(msg.arg1)
12 -> loadImgsIntoLine()
13 -> {
dl?.hide()
wv.get()?.restorePN()
}
14 -> {
val item = (pn - 1) / (wv.get()?.verticalLoadMaxCount?:40) * (wv.get()?.verticalLoadMaxCount?:40)
loadThread(item)
Log.d("MyVMH", "Load page from $item")
}
22 -> wv.get()?.idtime?.text = SimpleDateFormat("HH:mm").format(Date()) + week + wv.get()?.toolsBox?.netinfo
}
}
override fun getGsonItem() = manga
override fun setGsonItem(gsonObj: Any) {
super.setGsonItem(gsonObj)
manga = gsonObj as Chapter2Return
}
override fun onError() {
super.onError()
wv.get()?.toolsBox?.toastError("下载章节信息失败")
}
@ExperimentalStdlibApi
override fun doWhenFinishDownload() {
super.doWhenFinishDownload()
prepareManga()
}
@ExperimentalStdlibApi
fun loadFromFile(file: File): Boolean {
return try {
val jsonFile = File(file.parentFile, "${file.nameWithoutExtension}.json")
if(jsonFile.exists()) manga = Gson().fromJson(jsonFile.reader(), Chapter2Return::class.java)
else{
manga = Chapter2Return()
manga?.let {
it.results = Chapter2Return.Results()
it.results.comic = ComicStructure()
it.results.comic.name = file.parentFile?.name
it.results.chapter = ChapterWithContent()
it.results.chapter.name = file.nameWithoutExtension
it.results.chapter.size = countZipEntries(file)
}
}
prepareManga()
true
}catch (e: Exception){
e.printStackTrace()
//wv.get()?.toolsBox?.toastError("读取本地章节信息失败")
false
}
}
private fun countZipEntries(file: File): Int{
var count = 0
try {
val zip = ZipInputStream(file.inputStream().buffered())
var entry = zip.nextEntry
while (entry != null) {
if (!entry.isDirectory) count++
entry = zip.nextEntry
}
zip.closeEntry()
zip.close()
} catch (e: Exception) {
wv.get()?.toolsBox?.toastError("统计zip图片数错误!")
}
return count
}
@ExperimentalStdlibApi
private fun prepareManga(){
comicName = manga?.results?.comic?.name
progressLog = PropertiesTools(File("${wv.get()?.filesDir}/progress/${manga?.results?.comic?.name}"))
wv.get()?.count = manga?.results?.chapter?.size?:0
wv.get()?.initManga()
wv.get()?.vprog?.visibility = View.GONE
}
private fun loadImgsIntoLine(item: Int = (wv.get()?.currentItem?:0), maxCount: Int = (wv.get()?.verticalLoadMaxCount?:40)){
Log.d("MyVMH", "Fun: loadImgsIntoLine($item)")
val count = wv.get()?.count?.minus(1)?:0
val notFull = item + maxCount > count
val loadCount = (if(notFull) count - item else maxCount) - 1
Log.d("MyVMH", "loadCount: $loadCount")
if(loadCount >= 0) for(i in 0..loadCount) obtainMessage(4,item + i, if(i == loadCount - 1)1 else 0, wv.get()?.scrollImages?.get(i)).sendToTarget()
else sendEmptyMessage(8)
if(notFull) obtainMessage(6, loadCount + 1, maxCount).sendToTarget()
}
private fun loadThread() = Thread{
sendEmptyMessage(7)
//sleep(233)
sendEmptyMessage(12)
}.start()
private fun loadThread(item: Int) = Thread{
sendEmptyMessage(7)
//sleep(233)
Log.d("MyVMH", "loadImgsIntoLine($item)")
obtainMessage(11, item, 0).sendToTarget()
}.start()
private fun showInfCard() {
Log.d("MyVMH", "Read info drawer delta: $delta")
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()
}
}

View File

@@ -0,0 +1,474 @@
package top.fumiama.copymanga.ui.vm
import android.animation.ObjectAnimator
import android.annotation.SuppressLint
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.os.Bundle
import android.os.Handler
import android.util.Log
import android.view.*
import android.widget.SeekBar
import android.widget.Toast
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
import com.bumptech.glide.Glide
import com.bumptech.glide.load.model.GlideUrl
import com.liaoinstan.springview.widget.SpringView
import kotlinx.android.synthetic.main.activity_viewmanga.*
import kotlinx.android.synthetic.main.line_header.view.*
import kotlinx.android.synthetic.main.page_imgview.*
import kotlinx.android.synthetic.main.page_imgview.view.*
import kotlinx.android.synthetic.main.page_scrollimgview.*
import kotlinx.android.synthetic.main.page_scrollimgview.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_titlebar.view.*
import kotlinx.android.synthetic.main.widget_viewmangainfo.*
import top.fumiama.dmzj.copymanga.R
import top.fumiama.copymanga.template.TitleActivityTemplate
import top.fumiama.copymanga.tools.CMApi
import top.fumiama.copymanga.tools.DownloadTools
import top.fumiama.copymanga.tools.TimeThread
import top.fumiama.copymanga.views.ScaleImageView
import java.io.ByteArrayInputStream
import java.io.ByteArrayOutputStream
import java.io.File
import java.lang.ref.WeakReference
import java.util.*
import java.util.zip.ZipFile
class ViewMangaActivity : TitleActivityTemplate() {
var count = 0
private lateinit var handler: VMHandler
lateinit var tt: TimeThread
var clicked = false
private var isInSeek = false
private var isInScroll = true
//private var progressLog: PropertiesTools? = null
var scrollImages = arrayOf<ScaleImageView>()
//var zipFirst = false
private var useFullScreen = false
var r2l = true
var currentItem = 0
var verticalLoadMaxCount = 40
private var notUseVP = true
private var isVertical = false
private var q = 90
private val size get() = if(count / verticalLoadMaxCount > currentItem / verticalLoadMaxCount) verticalLoadMaxCount else count % verticalLoadMaxCount
var infoDrawerDelta = 0f
var pageNum: Int
get() = getPageNumber()
set(value) = setPageNumber(value)
//var pn = 0
private val isPnValid: Boolean get(){
if(pn == -2) pn = count
return intent.getStringExtra("function") == "log" && pn > 0
}
@ExperimentalStdlibApi
@SuppressLint("SetTextI18n")
override fun onCreate(savedInstanceState: Bundle?) {
setContentView(R.layout.activity_viewmanga)
super.onCreate(savedInstanceState)
va = WeakReference(this)
//progressLog = PropertiesTools(File("$filesDir/progress/${chapter2Return?.results?.chapter?.comic_id}"))
//dlZip2View = intent.getStringExtra("callFrom") == "Dl" || p["dlZip2View"] == "true"
//zipFirst = intent.getStringExtra("callFrom") == "zipFirst"
useFullScreen = p["useFullScreen"] != "true"
r2l = p["r2l"] == "true"
isVertical = p["vertical"] == "true"
notUseVP = p["noVP"] == "true" || isVertical
//url = intent.getStringExtra("url")
handler = VMHandler(this, if(urlArray.isNotEmpty()) urlArray[position] else "")
if (p["quality"] != "null") q = p["quality"].toInt()
if (p["verticalMax"] != "null") verticalLoadMaxCount = p["verticalMax"].toInt()
tt = TimeThread(handler, 22)
tt.canDo = true
tt.start()
Log.d("MyVM", "Now ZipFile is $zipFile")
try {
if (zipFile != null && zipFile?.exists() == true) {
if (!handler.loadFromFile(zipFile!!)) prepareImgFromWeb()
} else prepareImgFromWeb()
} catch (e: Exception) {
e.printStackTrace()
toolsBox.toastError("加载漫画错误")
}
}
override fun onWindowFocusChanged(hasFocus: Boolean) {
super.onWindowFocusChanged(hasFocus)
if(useFullScreen) {
if (android.os.Build.VERSION.SDK_INT < android.os.Build.VERSION_CODES.R)
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
else {
window.setDecorFitsSystemWindows(false)
window.insetsController?.hide(WindowInsets.Type.statusBars())
//window.insetsController?.hide(WindowInsets.Type.navigationBars())
}
}
}
fun restorePN(){
if (isPnValid) {
isInScroll = false
pageNum = pn
pn = -1
}
sendProgress()
}
@ExperimentalStdlibApi
fun initManga(){
prepareItems(count)
if (!isVertical) restorePN()
}
@ExperimentalStdlibApi
private fun prepareImgFromWeb() {
handler.startLoad()
}
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) {
if(isVertical){
currentItem = num - 1
val delta = currentItem % verticalLoadMaxCount
Log.d("MyVM", "Height: ${psivl.height}, scrollY: ${psivs.scrollY}")
if (!isInScroll || isInSeek) psivs.scrollY = psivl.height / size * delta
updateSeekBar()
}
else {
currentItem = num - 1
try {
loadOneImg()
} catch (e: Exception) {
e.printStackTrace()
toolsBox.toastError("页数${currentItem}不合法")
}
}
} else vp.currentItem = num - 1
}
fun clearImgOn(imgView: ScaleImageView){
imgView.visibility = View.GONE
}
private fun getTempFile(position: Int) = File(cacheDir, "$position")
private fun getImgUrl(position: Int) = handler.manga?.results?.chapter?.let {
it.contents[it.words.indexOf(position)].url
}
fun loadImgOn(imgView: ScaleImageView, position: Int){
if (zipFile?.exists() == true) imgView.setImageBitmap(getImgBitmap(position))
else if(isVertical) {
val f = getTempFile(position)
if(DownloadTools.downloadUsingUrlRet(getImgUrl(position), f))
imgView.setImageBitmap(BitmapFactory.decodeFile(f.path))
else Toast.makeText(this, "下载第${position}页失败", Toast.LENGTH_SHORT).show()
}
else Glide.with(this)
.load(GlideUrl(getImgUrl(position), CMApi.myGlideHeaders))
.timeout(10000)
.into(imgView)
imgView.visibility = View.VISIBLE
}
private fun loadOneImg() {
loadImgOn(onei, currentItem)
updateSeekBar()
}
private fun initImgList(){
for (i in 0..39) {
val newImg = ScaleImageView(this)
scrollImages += newImg
psivl.addView(newImg)
}
}
fun prepareLastPage(loadCount: Int, maxCount: Int){
for (i in loadCount until maxCount) handler.obtainMessage(5, scrollImages[i]).sendToTarget()
handler.dl?.hide()
}
private fun getImgBitmap(position: Int): Bitmap? {
Log.d("MyVM", "Get bitmap @$position, count is $count")
if (position >= count || position < 0) return null
else {
val zip = ZipFile(zipFile)
if (q == 100) return BitmapFactory.decodeStream(zip.getInputStream(zip.getEntry("${position}.jpg")))
else {
val out = ByteArrayOutputStream()
try {
BitmapFactory.decodeStream(zip.getInputStream(zip.getEntry("${position}.webp")))
} catch (e: Exception) {
e.printStackTrace()
return null
}?.compress(Bitmap.CompressFormat.JPEG, q, out)
return BitmapFactory.decodeStream(ByteArrayInputStream(out.toByteArray()))
}
}
}
private fun setIdPosition(position: Int) {
infoDrawerDelta = position.toFloat()
infcard.translationY = infoDrawerDelta
Log.d("MyVM", "Set info drawer delta to $infoDrawerDelta")
}
@ExperimentalStdlibApi
@SuppressLint("SetTextI18n")
private fun prepareItems(size: Int) {
ttitle.text = handler.manga?.results?.chapter?.name
prepareVP()
prepareInfoBar(size)
if (notUseVP && !isVertical) loadOneImg()
prepareIdBtVH()
toolsBox.dp2px(67)?.let { setIdPosition(it) }
prepareIdBtFullScreen()
prepareIdBtVP()
prepareIdBtLR()
handler.progressLog?.let {
//it["uuid"] = handler.manga?.results?.comic?.uuid
it["name"] = inftitle.ttitle.text
}
}
private fun sendProgress() {
handler.progressLog?.let {
//it["chapterId"] = hm.chapterId.toString()
it["page"] = pageNum.toString()
//it["name"] = inftitle.ttitle.text
}
}
private fun prepareIdBtLR() {
idtblr.isChecked = r2l
idtblr.setOnClickListener {
if (idtblr.isChecked) p["r2l"] = "true"
else p["r2l"] = "false"
Toast.makeText(this, "下次浏览生效", Toast.LENGTH_SHORT).show()
}
}
private fun prepareIdBtVP() {
idtbvp.isChecked = notUseVP
idtbvp.setOnClickListener {
if (idtbvp.isChecked) p["noVP"] = "true"
else p["noVP"] = "false"
Toast.makeText(this, "下次浏览生效", Toast.LENGTH_SHORT).show()
}
}
private fun prepareVP() {
if (notUseVP) {
vp.visibility = View.GONE
if(!isVertical) vone.visibility = View.VISIBLE
} else {
vp.visibility = View.VISIBLE
vone.visibility = View.GONE
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.GONE
isearch.visibility = View.GONE
inftitle.ttitle.text = handler.manga?.results?.chapter?.name
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)
}
}
@ExperimentalStdlibApi
private fun prepareIdBtVH() {
idtbvh.isChecked = isVertical
if (isVertical) {
val vsps = vsp as SpringView
vsps.footerView.lht.text = "更多"
vsps.headerView.lht.text = "更多"
val pm = PagesManager(WeakReference(this))
vsps.setListener(object :SpringView.OnFreshListener{
override fun onLoadmore() {
//scrollForward()
pm.toPage(true)
vsps.onFinishFreshAndLoad()
}
override fun onRefresh() {
//scrollBack()
pm.toPage(false)
vsps.onFinishFreshAndLoad()
}
})
vp.visibility = View.GONE
vsp.visibility = View.VISIBLE
initImgList()
handler.sendEmptyMessage(if(isPnValid)14 else 10)
psivs.setOnScrollChangeListener { _, _, scrollY, _, _ ->
isInScroll = true
if(!isInSeek){
val newCurrent = (scrollY.toFloat() * size.toFloat() / psivl.height.toFloat() + 0.5).toInt()
pageNum += newCurrent - currentItem % verticalLoadMaxCount
}
}
}
idtbvh.setOnClickListener {
p["vertical"] = if (idtbvh.isChecked) "true" else "false"
Toast.makeText(this, "下次浏览生效", Toast.LENGTH_SHORT).show()
}
}
private fun prepareIdBtFullScreen() {
idtbfullscreen.isChecked = !useFullScreen
idtbfullscreen.setOnClickListener {
p["useFullScreen"] = if (idtbfullscreen.isChecked) "true" else "false"
Toast.makeText(this, "下次浏览生效", Toast.LENGTH_SHORT).show()
}
}
fun scrollBack() {
isInScroll = false
if(isVertical && (pageNum-1) % verticalLoadMaxCount == 0){
Log.d("MyVM", "Do scroll back, isVertical: $isVertical, pageNum: $pageNum")
handler.obtainMessage(9, currentItem - verticalLoadMaxCount, 0).sendToTarget() //loadImgsIntoLine(currentItem - verticalLoadMaxCount)
psivl.postDelayed({ pageNum-- }, 233)
}else pageNum--
}
fun scrollForward() {
isInScroll = false
pageNum++
if(isVertical && (pageNum-1) % verticalLoadMaxCount == 0) handler.sendEmptyMessage(10)
}
@SuppressLint("SetTextI18n")
private fun updateSeekText() {
inftxtprogress.text = "$pageNum/$count"
}
private fun updateSeekProgress() {
infseek.progress = pageNum * 100 / count
}
override fun onDestroy() {
dlhandler?.sendEmptyMessage(0)
tt.canDo = false
dlhandler = null
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
if (zipFile?.exists() == true) getImgBitmap(pos)?.let {
Glide.with(this@ViewMangaActivity).load(it)
//.thumbnail(Glide.with(this@ViewMangaActivity).load(R.drawable.load))
.into(holder.itemView.onei)
//holder.itemView.onei.setImageBitmap(it)
}
else Glide.with(this@ViewMangaActivity).load(
GlideUrl(getImgUrl(pos), CMApi.myGlideHeaders))
.timeout(10000)
//.thumbnail(Glide.with(this@ViewMangaActivity).load(R.drawable.load))
.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.GONE
isearch.visibility = View.GONE
}, 300)
handler.sendEmptyMessage(1)
}
companion object {
var comicName: String? = null
var urlArray = arrayOf<String>()
var fileArray = arrayOf<File>()
var position = 0
var zipFile: File? = null
var dlhandler: Handler? = null
var va: WeakReference<ViewMangaActivity>? = null
var pn = 0
}
}

View File

@@ -1,24 +0,0 @@
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

@@ -1,4 +1,4 @@
package top.fumiama.copymanga.view
package top.fumiama.copymanga.views
import android.content.Context
import android.util.AttributeSet
@@ -10,9 +10,9 @@ class ChapterToggleButton: ToggleButton {
constructor(context: Context?): super(context, null)
var url: CharSequence? = null
val hash get() = url?.toString()?.substringAfterLast('/')
var caption: CharSequence? = null
var index: Int = 0
var uuid: CharSequence? = null
var chapterName: CharSequence = "null"
set(value) {
textOn = value

View File

@@ -1,20 +1,20 @@
package top.fumiama.copymanga.view
package top.fumiama.copymanga.views
import android.annotation.SuppressLint
import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import android.widget.ScrollView
import androidx.core.widget.NestedScrollView
@SuppressLint("ClickableViewAccessibility")
class LazyScrollView : ScrollView {
class LazyScrollView : NestedScrollView {
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)
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 ->

View File

@@ -0,0 +1,22 @@
package top.fumiama.copymanga.views
import android.content.Context
import android.util.AttributeSet
import androidx.cardview.widget.CardView
import java.io.File
class MangaCardView:CardView {
constructor(context: Context): super(context)
constructor(context: Context, attrs: AttributeSet?): super (context, attrs)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int): super(context, attrs, defStyleAttr)
var name = ""
var append: String? = null
var headImageUrl: String? = null
//var uuid: String? = null
var path: String? = null
var isFinish = false
var index = 0
var chapterUUID: String? = null
var pageNumber: Int? = null
}

View File

@@ -0,0 +1,21 @@
package top.fumiama.copymanga.views
import android.content.Context
import android.util.AttributeSet
import com.akscorp.overscrollablescrollview.OverscrollableNestedScrollView
import kotlinx.android.synthetic.main.app_bar_main.*
import top.fumiama.copymanga.MainActivity.Companion.mainWeakReference
open class OverScrollView :OverscrollableNestedScrollView{
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle)
override fun isAchieveTop(): Boolean {
val re = super.isAchieveTop()
if(re) mainWeakReference?.get()?.appbar?.setExpanded(true)
return re
}
}

View File

@@ -1,4 +1,4 @@
package top.fumiama.copymanga.view
package top.fumiama.copymanga.views
import android.animation.ValueAnimator
import android.animation.ValueAnimator.AnimatorUpdateListener
@@ -14,8 +14,8 @@ import android.view.GestureDetector
import android.view.GestureDetector.SimpleOnGestureListener
import android.view.MotionEvent
import android.widget.ImageView
import top.fumiama.copymanga.activity.ViewMangaActivity
import top.fumiama.copymanga.tool.PagesManager
import top.fumiama.copymanga.ui.vm.PagesManager
import top.fumiama.copymanga.ui.vm.ViewMangaActivity
import java.lang.ref.WeakReference
import java.util.*
import kotlin.math.sqrt
@@ -94,17 +94,17 @@ class ScaleImageView : ImageView {
* 外部变换矩阵记录了图片手势操作的最终结果,是相对于图片fit center状态的变换.
* 默认值为单位矩阵,此时图片为fit center状态.
*
@param matrix 用于填充结果的对象
@return 如果传了matrix参数则将matrix填充后返回,否则new一个填充返回
@param matrix 用于填充结果的对象
@return 如果传了matrix参数则将matrix填充后返回,否则new一个填充返回
fun getOuterMatrix(matrix: Matrix?): Matrix {
var matrix = matrix
if (matrix == null) {
matrix = Matrix(mOuterMatrix)
} else {
matrix.set(mOuterMatrix)
}
return matrix
var matrix = matrix
if (matrix == null) {
matrix = Matrix(mOuterMatrix)
} else {
matrix.set(mOuterMatrix)
}
return matrix
}*/
/**
@@ -140,21 +140,23 @@ class ScaleImageView : ImageView {
}
fun setHeight2FitImgWidth(){
matrix.reset()
val imgX = drawable.intrinsicWidth.toFloat()
val imgY = drawable.intrinsicHeight.toFloat()
//Log.d("MySIV", "ix: $imgX, iy: $imgY, w: $width, h: $height")
//原图大小
val tempSrc = rectFTake(0f, 0f, imgX, imgY)
layoutParams.height = (imgY / imgX * width + 0.5).toInt()
invalidate()
//控件大小
val tempDst = rectFTake(0f, 0f, width.toFloat(), height.toFloat())
//计算fit center矩阵
matrix.setRectToRect(tempSrc, tempDst, Matrix.ScaleToFit.CENTER)
//释放临时对象
rectFGiven(tempDst)
rectFGiven(tempSrc)
if(matrix != null && drawable != null && layoutParams != null){
matrix.reset()
val imgX = drawable.intrinsicWidth.toFloat()
val imgY = drawable.intrinsicHeight.toFloat()
//Log.d("MySIV", "ix: $imgX, iy: $imgY, w: $width, h: $height")
//原图大小
val tempSrc = rectFTake(0f, 0f, imgX, imgY)
layoutParams.height = (imgY / imgX * width + 0.5).toInt()
invalidate()
//控件大小
val tempDst = rectFTake(0f, 0f, width.toFloat(), height.toFloat())
//计算fit center矩阵
matrix.setRectToRect(tempSrc, tempDst, Matrix.ScaleToFit.CENTER)
//释放临时对象
rectFGiven(tempDst)
rectFGiven(tempSrc)
}
}
/**
@@ -210,11 +212,11 @@ class ScaleImageView : ImageView {
* @return 返回当前的mask对象副本,如果当前没有设置mask则返回null
val mask: RectF?
get() = if (mMask != null) {
RectF(mMask)
} else {
null
}*/
get() = if (mMask != null) {
RectF(mMask)
} else {
null
}*/
/**
* 与ViewPager结合的时候使用
@@ -262,29 +264,29 @@ class ScaleImageView : ImageView {
* 调用此方法会停止正在进行中的手势以及手势动画.
* 当duration为0时,outerMatrix值会被立即设置而不会启动动画.
*
@param endMatrix 动画目标矩阵
@param duration 动画持续时间
@param endMatrix 动画目标矩阵
@param duration 动画持续时间
*
@see .getOuterMatrix
@see .getOuterMatrix
fun outerMatrixTo(endMatrix: Matrix?, duration: Long) {
if (endMatrix == null) {
return
}
//将手势设置为PINCH_MODE_FREE将停止后续手势的触发
pinchMode = PINCH_MODE_FREE
//停止所有正在进行的动画
cancelAllAnimator()
//如果时间不合法立即执行结果
if (duration <= 0) {
mOuterMatrix.set(endMatrix)
dispatchOuterMatrixChanged()
invalidate()
} else {
//创建矩阵变化动画
mScaleAnimator = ScaleAnimator(mOuterMatrix, endMatrix, duration)
mScaleAnimator!!.start()
}
if (endMatrix == null) {
return
}
//将手势设置为PINCH_MODE_FREE将停止后续手势的触发
pinchMode = PINCH_MODE_FREE
//停止所有正在进行的动画
cancelAllAnimator()
//如果时间不合法立即执行结果
if (duration <= 0) {
mOuterMatrix.set(endMatrix)
dispatchOuterMatrixChanged()
invalidate()
} else {
//创建矩阵变化动画
mScaleAnimator = ScaleAnimator(mOuterMatrix, endMatrix, duration)
mScaleAnimator!!.start()
}
}*/
/**
@@ -294,32 +296,32 @@ class ScaleImageView : ImageView {
* 当前mask为null时,则不执行动画立即设置为目标mask.
* 当duration为0时,立即将当前mask设置为目标mask,不会执行动画.
*
@param mask 动画目标mask
@param duration 动画持续时间
@param mask 动画目标mask
@param duration 动画持续时间
*
@see .getMask
@see .getMask
fun zoomMaskTo(mask: RectF?, duration: Long) {
if (mask == null) {
return
}
//停止mask动画
if (mMaskAnimator != null) {
mMaskAnimator!!.cancel()
mMaskAnimator = null
}
//如果duration为0或者之前没有设置过mask,不执行动画,立即设置
if (duration <= 0 || mMask == null) {
if (mMask == null) {
mMask = RectF()
}
mMask!!.set(mask)
invalidate()
} else {
//执行mask动画
mMaskAnimator = MaskAnimator(mMask!!, mask, duration)
mMaskAnimator!!.start()
}
if (mask == null) {
return
}
//停止mask动画
if (mMaskAnimator != null) {
mMaskAnimator!!.cancel()
mMaskAnimator = null
}
//如果duration为0或者之前没有设置过mask,不执行动画,立即设置
if (duration <= 0 || mMask == null) {
if (mMask == null) {
mMask = RectF()
}
mMask!!.set(mask)
invalidate()
} else {
//执行mask动画
mMaskAnimator = MaskAnimator(mMask!!, mask, duration)
mMaskAnimator!!.start()
}
}*/
/**
@@ -329,24 +331,24 @@ class ScaleImageView : ImageView {
* 但不清空drawable,以及事件绑定相关数据.
fun reset() {
//重置位置到fit
mOuterMatrix.reset()
dispatchOuterMatrixChanged()
//清空mask
mMask = null
//停止所有手势
pinchMode = PINCH_MODE_FREE
mLastMovePoint[0f] = 0f
mScaleCenter[0f] = 0f
mScaleBase = 0f
//停止所有动画
if (mMaskAnimator != null) {
mMaskAnimator!!.cancel()
mMaskAnimator = null
}
cancelAllAnimator()
//重绘
invalidate()
//重置位置到fit
mOuterMatrix.reset()
dispatchOuterMatrixChanged()
//清空mask
mMask = null
//停止所有手势
pinchMode = PINCH_MODE_FREE
mLastMovePoint[0f] = 0f
mScaleCenter[0f] = 0f
mScaleBase = 0f
//停止所有动画
if (mMaskAnimator != null) {
mMaskAnimator!!.cancel()
mMaskAnimator = null
}
cancelAllAnimator()
//重绘
invalidate()
}*/
////////////////////////////////对外广播事件////////////////////////////////
/**
@@ -399,64 +401,64 @@ class ScaleImageView : ImageView {
/**
* 添加外部矩阵变化监听
*
@param listener
@param listener
fun addOuterMatrixChangedListener(listener: OuterMatrixChangedListener?) {
if (listener == null) {
return
}
//如果监听列表没有被修改锁定直接将监听添加到监听列表
if (mDispatchOuterMatrixChangedLock == 0) {
if (mOuterMatrixChangedListeners == null) {
mOuterMatrixChangedListeners =
ArrayList()
}
mOuterMatrixChangedListeners!!.add(listener)
} else {
//如果监听列表修改被锁定,那么尝试在监听列表副本上添加
//监听列表副本将会在锁定被解除时替换到监听列表里
if (mOuterMatrixChangedListenersCopy == null) {
mOuterMatrixChangedListenersCopy = if (mOuterMatrixChangedListeners != null) {
ArrayList(
mOuterMatrixChangedListeners!!
)
} else {
ArrayList()
}
}
mOuterMatrixChangedListenersCopy!!.add(listener)
}
if (listener == null) {
return
}
//如果监听列表没有被修改锁定直接将监听添加到监听列表
if (mDispatchOuterMatrixChangedLock == 0) {
if (mOuterMatrixChangedListeners == null) {
mOuterMatrixChangedListeners =
ArrayList()
}
mOuterMatrixChangedListeners!!.add(listener)
} else {
//如果监听列表修改被锁定,那么尝试在监听列表副本上添加
//监听列表副本将会在锁定被解除时替换到监听列表里
if (mOuterMatrixChangedListenersCopy == null) {
mOuterMatrixChangedListenersCopy = if (mOuterMatrixChangedListeners != null) {
ArrayList(
mOuterMatrixChangedListeners!!
)
} else {
ArrayList()
}
}
mOuterMatrixChangedListenersCopy!!.add(listener)
}
}*/
/**
* 删除外部矩阵变化监听
*
@param listener
@param listener
fun removeOuterMatrixChangedListener(listener: OuterMatrixChangedListener?) {
if (listener == null) {
return
}
//如果监听列表没有被修改锁定直接在监听列表数据结构上修改
if (mDispatchOuterMatrixChangedLock == 0) {
if (mOuterMatrixChangedListeners != null) {
mOuterMatrixChangedListeners!!.remove(listener)
}
} else {
//如果监听列表被修改锁定,那么就在其副本上修改
//其副本将会在锁定解除时替换回监听列表
if (mOuterMatrixChangedListenersCopy == null) {
if (mOuterMatrixChangedListeners != null) {
mOuterMatrixChangedListenersCopy =
ArrayList(
mOuterMatrixChangedListeners!!
)
}
}
if (mOuterMatrixChangedListenersCopy != null) {
mOuterMatrixChangedListenersCopy!!.remove(listener)
}
}
if (listener == null) {
return
}
//如果监听列表没有被修改锁定直接在监听列表数据结构上修改
if (mDispatchOuterMatrixChangedLock == 0) {
if (mOuterMatrixChangedListeners != null) {
mOuterMatrixChangedListeners!!.remove(listener)
}
} else {
//如果监听列表被修改锁定,那么就在其副本上修改
//其副本将会在锁定解除时替换回监听列表
if (mOuterMatrixChangedListenersCopy == null) {
if (mOuterMatrixChangedListeners != null) {
mOuterMatrixChangedListenersCopy =
ArrayList(
mOuterMatrixChangedListeners!!
)
}
}
if (mOuterMatrixChangedListenersCopy != null) {
mOuterMatrixChangedListenersCopy!!.remove(listener)
}
}
}*/
/**
@@ -583,10 +585,10 @@ class ScaleImageView : ImageView {
* 记录两个手指的中点,作为和mScaleCenter绑定的点.
* 这个绑定可以保证mScaleCenter无论如何都会跟随这个中点.
*
@see .mScaleCenter
@see .mScaleCenter
*
@see .scale
@see .scaleEnd
@see .scale
@see .scaleEnd
*/
private val mLastMovePoint = PointF()
@@ -674,7 +676,6 @@ class ScaleImageView : ImageView {
}
var v :WeakReference<ViewMangaActivity>? = null
var pm:PagesManager? = null
override fun onSingleTapConfirmed(event: MotionEvent): Boolean {
if(v == null) {
v = ViewMangaActivity.va
@@ -1234,8 +1235,8 @@ class ScaleImageView : ImageView {
*
* 参数单位为 像素/
*
@param vectorX 速度向量
@param vectorY 速度向量
@param vectorX 速度向量
@param vectorY 速度向量
*/
init {
setFloatValues(0f, 1f)
@@ -1296,8 +1297,8 @@ class ScaleImageView : ImageView {
*
* 从一个矩阵变换到另外一个矩阵
*
@param start 开始矩阵
@param end 结束矩阵
@param start 开始矩阵
@param end 结束矩阵
*/
init {
setFloatValues(0f, 1f)
@@ -1387,7 +1388,7 @@ class ScaleImageView : ImageView {
/**
* 创建一个对象池
*
@param size 对象池最大容量
@param size 对象池最大容量
*/
init {
mQueue = LinkedList()
@@ -1485,11 +1486,11 @@ class ScaleImageView : ImageView {
* 获取某个矩形的副本
fun rectFTake(rectF: RectF?): RectF {
val result = mRectFPool.take()!!
if (rectF != null) {
result.set(rectF)
}
return result
val result = mRectFPool.take()!!
if (rectF != null) {
result.set(rectF)
}
return result
}*/
/**
@@ -1630,5 +1631,7 @@ class ScaleImageView : ImageView {
* @see .getPinchMode
*/
const val PINCH_MODE_SCALE = 2
var pm:PagesManager? = null
}
}

View File

@@ -0,0 +1,26 @@
package top.fumiama.copymanga.views
import android.content.Context
import android.util.AttributeSet
import androidx.core.widget.NestedScrollView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
class ScrollRefreshView : NestedScrollView {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(
context,
attrs,
defStyle
)
var swipeRefreshLayout: SwipeRefreshLayout? = null
override fun onScrollChanged(l: Int, t: Int, oldl: Int, oldt: Int) {
super.onScrollChanged(l, t, oldl, oldt)
//Log.d("MyOSV", "$l, $t, $oldl, $oldt")
swipeRefreshLayout?.isEnabled = t == 0
}
}

View File

@@ -1,29 +0,0 @@
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()
}
@JavascriptInterface
fun enterProfile(){
Thread{mh?.sendEmptyMessage(6)}.start()
}
}

View File

@@ -1,22 +0,0 @@
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

@@ -1,46 +0,0 @@
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

@@ -1,30 +0,0 @@
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)
}
}
}

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="@android:integer/config_mediumAnimTime"
android:fromXDelta="100%"
android:interpolator="@android:anim/accelerate_interpolator"
android:toXDelta="0" />
</set>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="@android:integer/config_mediumAnimTime"
android:fromXDelta="0"
android:interpolator="@android:anim/accelerate_interpolator"
android:toXDelta="100%" />
</set>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="@android:integer/config_mediumAnimTime"
android:fromXDelta="0"
android:interpolator="@android:anim/accelerate_interpolator"
android:toXDelta="-100%"/>
</set>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="@android:integer/config_mediumAnimTime"
android:fromXDelta="-100%"
android:interpolator="@android:anim/accelerate_interpolator"
android:toXDelta="0"/>
</set>

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,11 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="rectangle">
<corners android:radius="8dp" />
<solid android:color="@android:color/transparent"/>
<stroke android:color="#ffffff"
android:width="2dp"/>
</shape>
</item>
</selector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="?attr/colorOnSurface"
android:pathData="M890 331c-13-12-33-12-45 0L517 662 185 333c-13-12-33-12-45 0-12 13-12 33 0 45l353 350c1 1 1 1 2 1 0 0 0 0 0 0a32 32 0 0 0 23 9c8 0 16-3 23-9l350-353c12-13 12-33-0-45z" />
</vector>

View File

@@ -5,6 +5,6 @@
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="#000000"
android:fillColor="?attr/colorOnSurface"
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>

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="?attr/colorOnSurface"
android:pathData="M20,4L4,4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2zM20,8l-8,5 -8,-5L4,6l8,5 8,-5v2z"/>
</vector>

View File

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

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="?attr/colorOnSurface"
android:pathData="M299 587a75 75 0 1 1 0-149 75 75 0 0 1 0 149z m213 0a75 75 0 1 1 0-149 75 75 0 0 1 0 149z m213 0a75 75 0 1 1 0-149 75 75 0 0 1 0 149z"/>
</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="?attr/colorOnSurface"
android:pathData="M821 256a32 32 0 0 1 64 0v565c0 65-53 117-117 117H256c-65 0-117-53-117-117V203c0-65 53-117 117-117h597a32 32 0 0 1 0 64H256a53 53 0 0 0-53 53v619a53 53 0 0 0 53 53h512a53 53 0 0 0 53-53V256zM341 437a32 32 0 0 1 0-64h341a32 32 0 0 1 0 64H341z m0 171a32 32 0 0 1 0-64h213a32 32 0 0 1 0 64H341z"/>
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="?attr/colorOnSurface"
android:pathData="M648 850a32 32 0 1 1-41-49C758 677 832 566 832 471 832 293 689 149 512 149c-177 0-320 144-320 322 0 115 108 253 329 409a32 32 0 0 1-37 52C248 765 128 613 128 471 128 258 300 85 512 85s384 173 384 386c0 118-84 244-248 379zM512 619c-82 0-149-67-149-149s67-149 149-149 149 67 149 149-67 149-149 149z m0-64a85 85 0 1 0 0-171 85 85 0 0 0 0 171z" />
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="?attr/colorOnSurface"
android:pathData="M18,8h-1L17,6c0,-2.76 -2.24,-5 -5,-5S7,3.24 7,6v2L6,8c-1.1,0 -2,0.9 -2,2v10c0,1.1 0.9,2 2,2h12c1.1,0 2,-0.9 2,-2L20,10c0,-1.1 -0.9,-2 -2,-2zM12,17c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2zM15.1,8L8.9,8L8.9,6c0,-1.71 1.39,-3.1 3.1,-3.1 1.71,0 3.1,1.39 3.1,3.1v2z"/>
</vector>

Some files were not shown because too many files have changed in this diff Show More