434 lines
17 KiB
Kotlin
434 lines
17 KiB
Kotlin
package com.coldmint.rust.pro
|
|
|
|
import android.content.Intent
|
|
import android.os.Bundle
|
|
import com.coldmint.rust.pro.base.BaseActivity
|
|
import com.coldmint.rust.core.dataBean.ModConfigurationData
|
|
import android.os.Handler
|
|
import com.coldmint.rust.pro.tool.AppSettings
|
|
import com.coldmint.rust.pro.tool.GlobalMethod
|
|
import android.os.Looper
|
|
import android.view.KeyEvent
|
|
import android.view.LayoutInflater
|
|
import android.view.MenuItem
|
|
import android.view.View
|
|
import android.widget.*
|
|
import androidx.core.view.isVisible
|
|
import com.coldmint.rust.core.*
|
|
import com.coldmint.rust.core.interfaces.CompressionInterceptor
|
|
import com.coldmint.rust.core.tool.LineParser
|
|
import com.coldmint.rust.core.interfaces.CompressionListener
|
|
import com.coldmint.rust.core.interfaces.LineParserEvent
|
|
import com.coldmint.rust.core.tool.FileFinder2
|
|
import com.coldmint.rust.core.tool.FileOperator
|
|
import com.coldmint.rust.pro.databinding.ActivityPackBinding
|
|
import com.coldmint.rust.pro.tool.EventRecord
|
|
import java.io.File
|
|
import java.lang.StringBuilder
|
|
import java.text.SimpleDateFormat
|
|
import java.util.concurrent.ExecutorService
|
|
import java.util.concurrent.Executors
|
|
|
|
/**
|
|
* @author Cold Mint
|
|
*/
|
|
class PackActivity : BaseActivity<ActivityPackBinding>() {
|
|
private val executorService: ExecutorService by lazy {
|
|
Executors.newSingleThreadExecutor()
|
|
}
|
|
private lateinit var item: Array<String>
|
|
private lateinit var modFolder: File
|
|
private var modName: String? = null
|
|
private var outputFolder: String? = null
|
|
private var outputFile: File? = null
|
|
private var configurationManager: ModConfigurationManager? = null
|
|
private var configurationData: ModConfigurationData? = null
|
|
private var needRecyclingFile = false
|
|
private var recyclePath: String? = null
|
|
private var newFileBuilder: StringBuilder? = null
|
|
private var sourceFileNum = 0
|
|
|
|
//是否需要返回上级界面结果?
|
|
private var needReturn: Boolean = false
|
|
|
|
override fun whenCreateActivity(savedInstanceState: Bundle?, canUseView: Boolean) {
|
|
if (canUseView) {
|
|
title = getText(R.string.packmod)
|
|
setReturnButton()
|
|
initData()
|
|
initAction()
|
|
}
|
|
}
|
|
|
|
|
|
private fun initData() {
|
|
val intent = intent
|
|
val bundle = intent.getBundleExtra("data")
|
|
if (bundle == null) {
|
|
showError("参数不合法")
|
|
} else {
|
|
needReturn = bundle.getBoolean("needReturn", false)
|
|
if (needReturn) {
|
|
viewBinding.formattingCode.isChecked = true
|
|
viewBinding.removeEmptyLines.isChecked = true
|
|
viewBinding.removeAnnotation.isChecked = true
|
|
viewBinding.removeEmptyFileAndFolder.isChecked = true
|
|
viewBinding.deleteSourceFile.isVisible = false
|
|
}
|
|
val modPath = bundle.getString("modPath")
|
|
modFolder = File(modPath)
|
|
val modClass = ModClass(modFolder)
|
|
modName = modClass.modName
|
|
item = resources.getStringArray(R.array.update_type_entries)
|
|
needRecyclingFile =
|
|
AppSettings.getValue(AppSettings.Setting.EnableRecoveryStation, true)
|
|
if (needRecyclingFile) {
|
|
val recoveryStationFolder = StringBuilder(
|
|
AppSettings.getValue(
|
|
AppSettings.Setting.RecoveryStationFolder,
|
|
this@PackActivity.filesDir.absolutePath + "/backup/"
|
|
)
|
|
)
|
|
if (!recoveryStationFolder.toString().endsWith("/")) {
|
|
recoveryStationFolder.append('/')
|
|
}
|
|
recoveryStationFolder.append(modName)
|
|
recyclePath = recoveryStationFolder.toString()
|
|
newFileBuilder = StringBuilder()
|
|
viewBinding.deleteSourceFile.setText(R.string.recycle_source_file)
|
|
}
|
|
configurationManager = modClass.modConfigurationManager
|
|
val manager = configurationManager
|
|
val data = manager?.readData()
|
|
if (data == null) {
|
|
configurationData = null
|
|
} else {
|
|
configurationData = data
|
|
loadInfoToView(data)
|
|
}
|
|
outputFolder = AppSettings.getValue(
|
|
AppSettings.Setting.PackDirectory,
|
|
AppSettings.dataRootDirectory + "/bin/"
|
|
)
|
|
val folder = outputFolder
|
|
val endValue = "/"
|
|
if (folder != null && !folder.endsWith(endValue)) {
|
|
outputFolder += endValue
|
|
}
|
|
val outputFolderObject = File(folder)
|
|
if (!outputFolderObject.exists()) {
|
|
outputFolderObject.mkdirs()
|
|
}
|
|
outputFile = File("$outputFolder$modName.rwmod")
|
|
}
|
|
|
|
}
|
|
|
|
/**
|
|
* 加载配置信息到视图
|
|
*
|
|
* @param data 数据
|
|
*/
|
|
private fun loadInfoToView(data: ModConfigurationData) {
|
|
viewBinding.filteringRules.setText(data.sourceFileFilteringRule)
|
|
viewBinding.garbageFileFilteringRule.setText(data.garbageFileFilteringRule)
|
|
viewBinding.updateLink.setText(data.updateLink)
|
|
viewBinding.updateTitle.setText(data.updateTitle)
|
|
val updateType = data.updateType
|
|
if (updateType == ModConfigurationManager.qqGroupType) {
|
|
viewBinding.updateSpinner.setSelection(1)
|
|
}
|
|
}
|
|
|
|
private fun initAction() {
|
|
viewBinding.packButton.setOnClickListener {
|
|
val type = viewBinding.packButton.text.toString()
|
|
if (type == getString(R.string.packmod)) {
|
|
sourceFileNum = 0
|
|
viewBinding.packCard.isVisible = true
|
|
viewBinding.packButton.setText(R.string.packing)
|
|
viewBinding.packingTitle.setText(R.string.packmod)
|
|
if (saveConfigurationData()) {
|
|
packMod()
|
|
} else {
|
|
resetButton(false)
|
|
}
|
|
} else if (type == getString(R.string.share_mod)) {
|
|
FileOperator.shareFile(this@PackActivity, outputFile)
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 重置按钮状态
|
|
*
|
|
* @param result 是否解压成功
|
|
*/
|
|
private fun resetButton(result: Boolean) {
|
|
if (result) {
|
|
viewBinding.packButton.setIconResource(R.drawable.ic_outline_share_24)
|
|
viewBinding.packButton.setText(R.string.share_mod)
|
|
if (needRecyclingFile && viewBinding.deleteSourceFile.isChecked) {
|
|
val tip = String.format(getString(R.string.recovery_prompt), modName)
|
|
viewBinding.packingState.text = tip
|
|
} else {
|
|
viewBinding.packingTitle.setText(R.string.packmod)
|
|
viewBinding.packingState.setText(R.string.pack_file_success)
|
|
viewBinding.packCard.postDelayed({
|
|
viewBinding.packCard.isVisible = false
|
|
if (needReturn) {
|
|
val result = Intent()
|
|
if (outputFile == null) {
|
|
setResult(RESULT_CANCELED, result)
|
|
} else {
|
|
result.putExtra("path", outputFile?.absolutePath)
|
|
result.putExtra("num", sourceFileNum)
|
|
}
|
|
setResult(RESULT_OK, result)
|
|
finish()
|
|
}
|
|
}, 300)
|
|
}
|
|
} else {
|
|
viewBinding.packButton.setText(R.string.packmod)
|
|
viewBinding.packCard.isVisible = false
|
|
}
|
|
}
|
|
|
|
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
|
when (item.itemId) {
|
|
android.R.id.home -> {
|
|
if (saveConfigurationData()) {
|
|
finish()
|
|
}
|
|
return true
|
|
}
|
|
}
|
|
return super.onOptionsItemSelected(item)
|
|
}
|
|
|
|
override fun onKeyDown(keyCode: Int, event: KeyEvent): Boolean {
|
|
if (keyCode == KeyEvent.KEYCODE_BACK && event.action == KeyEvent.ACTION_DOWN) {
|
|
if (saveConfigurationData()) {
|
|
finish()
|
|
}
|
|
return true
|
|
}
|
|
return super.onKeyDown(keyCode, event)
|
|
}
|
|
|
|
/**
|
|
* 如果需要删除文件
|
|
* 启用回收站则移动至回收站
|
|
*
|
|
* @param file 文件对象
|
|
* @return 是否删除成功
|
|
*/
|
|
private fun deleteFileIfNeed(file: File): Boolean {
|
|
return if (needRecyclingFile) {
|
|
newFileBuilder!!.delete(0, newFileBuilder!!.length)
|
|
newFileBuilder!!.append(recyclePath)
|
|
newFileBuilder!!.append(
|
|
FileOperator.getRelativePath(
|
|
file,
|
|
modFolder
|
|
)
|
|
)
|
|
if (file.isDirectory) {
|
|
file.delete()
|
|
} else {
|
|
val newFile = File(newFileBuilder.toString())
|
|
FileOperator.removeFile(file, newFile)
|
|
}
|
|
} else {
|
|
file.delete()
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 打包模组
|
|
*
|
|
* @return 打包状态
|
|
*/
|
|
private fun packMod() {
|
|
val handler = Handler(Looper.getMainLooper())
|
|
executorService.execute {
|
|
val deleteFile = viewBinding.deleteSourceFile.isChecked
|
|
val removeEmptyFileAndFolderValue = viewBinding.removeEmptyFileAndFolder.isChecked
|
|
val removeEmptyLinesValue = viewBinding.removeEmptyLines.isChecked
|
|
val removeAnnotationValue = viewBinding.removeAnnotation.isChecked
|
|
val formattingCodeValue = viewBinding.formattingCode.isChecked
|
|
val targetFile = modFolder
|
|
val outPutPath = outputFile ?: return@execute
|
|
|
|
//如果需要回收文件,并且开启了文件删除
|
|
if (needRecyclingFile && deleteFile) {
|
|
val folder = File(recyclePath)
|
|
if (!folder.exists()) {
|
|
folder.mkdirs()
|
|
}
|
|
}
|
|
var temSourceFileRule = viewBinding.filteringRules.text.toString()
|
|
if (temSourceFileRule.isEmpty()) {
|
|
temSourceFileRule = ".+\\.ini|.+\\.template"
|
|
}
|
|
val sourceFileRule = temSourceFileRule
|
|
val uselessFileRule = viewBinding.garbageFileFilteringRule.text.toString()
|
|
val codeBuilder = StringBuilder()
|
|
if (outputFile == null) {
|
|
return@execute
|
|
}
|
|
val compressionManager = CompressionManager.instance
|
|
val fileFinder = FileFinder2(targetFile)
|
|
//fileFinder.isDuplicateCheckFolder = true
|
|
compressionManager.fileFinder = fileFinder
|
|
val packing = getString(R.string.dialog_packing)
|
|
val lineParser = LineParser("")
|
|
lineParser.needTrim = true
|
|
|
|
//若移除空文件夹,则不扫描空文件夹
|
|
fileFinder.isDetectingEmptyFolder = !removeEmptyFileAndFolderValue
|
|
compressionManager.compression(targetFile, outPutPath, object : CompressionListener {
|
|
override fun whenCompressionFile(file: File): Boolean {
|
|
if (deleteFile) {
|
|
deleteFileIfNeed(file)
|
|
}
|
|
val tip = String.format(packing, file.name)
|
|
handler.post { viewBinding.packingState.text = tip }
|
|
return true
|
|
}
|
|
|
|
override fun whenCompressionFolder(folder: File): Boolean {
|
|
if (deleteFile && folder.list().isEmpty()) {
|
|
deleteFileIfNeed(folder)
|
|
}
|
|
val tip = String.format(packing, folder.name)
|
|
handler.post { viewBinding.packingState.text = tip }
|
|
return true
|
|
}
|
|
|
|
override fun whenCompressionComplete(result: Boolean) {
|
|
handler.post {
|
|
EventRecord.packMod(targetFile.name)
|
|
resetButton(result)
|
|
}
|
|
}
|
|
}, object : CompressionInterceptor {
|
|
override val sourceFileRule: String
|
|
get() = sourceFileRule
|
|
override val uselessFileRule: String
|
|
get() = uselessFileRule
|
|
|
|
override fun getSourceCode(file: File?): String? {
|
|
var code = FileOperator.readFile(file) ?: return null
|
|
if (code.isBlank()) {
|
|
return if (removeEmptyFileAndFolderValue) {
|
|
null
|
|
} else {
|
|
code
|
|
}
|
|
}
|
|
sourceFileNum++
|
|
//若移除注释或移除空行,其中任意一项为开启则分析代码
|
|
if (removeEmptyLinesValue || removeAnnotationValue) {
|
|
lineParser.text = code
|
|
codeBuilder.delete(0, codeBuilder.length)
|
|
lineParser.analyse(object : LineParserEvent {
|
|
override fun processingData(
|
|
lineNum: Int,
|
|
lineData: String,
|
|
isEnd: Boolean
|
|
): Boolean {
|
|
if (lineData.isEmpty()) {
|
|
if (!removeEmptyLinesValue) {
|
|
codeBuilder.append(lineData)
|
|
}
|
|
} else {
|
|
if (codeBuilder.isNotEmpty()) {
|
|
codeBuilder.append('\n')
|
|
}
|
|
if (lineData.startsWith("#")) {
|
|
if (!removeAnnotationValue) {
|
|
codeBuilder.append(lineData)
|
|
}
|
|
} else {
|
|
codeBuilder.append(lineData)
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
})
|
|
code = codeBuilder.toString()
|
|
}
|
|
if (formattingCodeValue) {
|
|
code = CodeTranslate.format(code)
|
|
}
|
|
return code
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 保存配置信息
|
|
*
|
|
* @return 是否保存成功
|
|
*/
|
|
private fun saveConfigurationData(): Boolean {
|
|
val deleteFile = viewBinding.deleteSourceFile.isChecked
|
|
if (deleteFile) {
|
|
//若删除了文件则保存失败,判断是否启用删除文件选择器退出
|
|
return true
|
|
}
|
|
val updateTypeItem = item[viewBinding.updateSpinner.selectedItemPosition]
|
|
val webLink = getString(R.string.web_link)
|
|
val qqLink = getString(R.string.qq_group)
|
|
val upTitle = viewBinding.updateTitle.text.toString()
|
|
val upLink = viewBinding.updateLink.text.toString()
|
|
var updateLinkType = ModConfigurationManager.webLinkType
|
|
if (!upTitle.isEmpty()) {
|
|
if (updateTypeItem == qqLink) {
|
|
if (!upLink.matches(Regex("^\\d{8,10}"))) {
|
|
setErrorAndInput(viewBinding.updateLink, getString(R.string.qq_group_error))
|
|
return false
|
|
}
|
|
updateLinkType = ModConfigurationManager.qqGroupType
|
|
} else {
|
|
if (!upLink.matches(Regex("^http://([\\w-]+\\.)+[\\w-]+(/[\\w-./?%&=]*)?$|^https://([\\w-]+\\.)+[\\w-]+(/[\\w-./?%&=]*)?$"))) {
|
|
setErrorAndInput(viewBinding.updateLink, getString(R.string.web_link_error))
|
|
return false
|
|
}
|
|
}
|
|
}
|
|
val buildTime = SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(System.currentTimeMillis())
|
|
var modConfigurationData = configurationData
|
|
if (modConfigurationData == null) {
|
|
modConfigurationData = ModConfigurationData(
|
|
buildDate = buildTime,
|
|
sourceFileFilteringRule = viewBinding.filteringRules.text.toString(),
|
|
garbageFileFilteringRule = viewBinding.garbageFileFilteringRule.text.toString(),
|
|
updateLink = upLink,
|
|
updateTitle = upTitle,
|
|
updateType = updateLinkType
|
|
)
|
|
} else {
|
|
modConfigurationData.buildDate = buildTime
|
|
modConfigurationData.sourceFileFilteringRule =
|
|
viewBinding.filteringRules.text.toString()
|
|
modConfigurationData.garbageFileFilteringRule =
|
|
viewBinding.garbageFileFilteringRule.text.toString()
|
|
modConfigurationData.updateLink = upLink
|
|
modConfigurationData.updateTitle = upTitle
|
|
modConfigurationData.updateType = updateLinkType
|
|
}
|
|
return configurationManager!!.saveData(modConfigurationData)
|
|
}
|
|
|
|
|
|
override fun getViewBindingObject(layoutInflater: LayoutInflater): ActivityPackBinding {
|
|
return ActivityPackBinding.inflate(layoutInflater)
|
|
}
|
|
|
|
|
|
} |