mirror of
https://git.chinosk6.cn/chinosk/gkms-local.git
synced 2026-02-06 09:42:28 +00:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c850ad7db | ||
|
|
7bf429336b | ||
|
|
c7e3d4f718 | ||
|
|
b74713be78 | ||
|
|
67945c86dd | ||
|
|
06a96a450e | ||
|
|
6e512d9380 | ||
|
|
f82e73845a | ||
|
|
8ddd6f53bc |
@@ -15,7 +15,7 @@
|
|||||||
|
|
||||||
- [x] 卡片信息、TIPS 等部分的文本 hook (`generic`)
|
- [x] 卡片信息、TIPS 等部分的文本 hook (`generic`)
|
||||||
- [ ] 更多类型的文件替换
|
- [ ] 更多类型的文件替换
|
||||||
- [ ] LSPatch 集成模式无效
|
- [x] LSPatch 集成模式无效
|
||||||
|
|
||||||
... and more
|
... and more
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ android {
|
|||||||
minSdk 29
|
minSdk 29
|
||||||
targetSdk 34
|
targetSdk 34
|
||||||
versionCode 4
|
versionCode 4
|
||||||
versionName "v1.6"
|
versionName "v1.6.3"
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
vectorDrawables {
|
vectorDrawables {
|
||||||
|
|||||||
3
app/lint.xml
Normal file
3
app/lint.xml
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
<lint>
|
||||||
|
<issue id="ExtraTranslation" severity="ignore" />
|
||||||
|
</lint>
|
||||||
@@ -6,6 +6,9 @@
|
|||||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||||
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
|
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES"
|
||||||
tools:ignore="QueryAllPackagesPermission" />
|
tools:ignore="QueryAllPackagesPermission" />
|
||||||
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||||
|
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
|
|||||||
Submodule app/src/main/assets/gakumas-local updated: 52aefc27eb...df448152cf
@@ -474,12 +474,41 @@ namespace GakumasLocal::HookMain {
|
|||||||
PictureBookLiveThumbnailView_SetData_Orig(self, liveData, isUnlocked, isNew, ct, mtd);
|
PictureBookLiveThumbnailView_SetData_Orig(self, liveData, isUnlocked, isNew, ct, mtd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void* PictureBookWindowPresenter_instance = nullptr;
|
||||||
|
std::string PictureBookWindowPresenter_charaId;
|
||||||
|
DEFINE_HOOK(void*, PictureBookWindowPresenter_GetLiveMusics, (void* self, Il2cppString* charaId, void* mtd)) {
|
||||||
|
// Log::DebugFmt("GetLiveMusics: %s", charaId->ToString().c_str());
|
||||||
|
|
||||||
|
if (Config::unlockAllLive) {
|
||||||
|
PictureBookWindowPresenter_instance = self;
|
||||||
|
PictureBookWindowPresenter_charaId = charaId->ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
return PictureBookWindowPresenter_GetLiveMusics_Orig(self, charaId, mtd);
|
||||||
|
}
|
||||||
|
|
||||||
|
DEFINE_HOOK(void, PictureBookLiveSelectScreenModel_ctor, (void* self, void* transitionParam, void* musics, void* mtd)) {
|
||||||
|
// Log::DebugFmt("PictureBookLiveSelectScreenModel_ctor");
|
||||||
|
|
||||||
|
if (Config::unlockAllLive) {
|
||||||
|
static auto GetLiveMusics = Il2cppUtils::GetMethod("Assembly-CSharp.dll", "Campus.OutGame",
|
||||||
|
"PictureBookWindowPresenter", "GetLiveMusics");
|
||||||
|
if (PictureBookWindowPresenter_instance && !PictureBookWindowPresenter_charaId.empty()) {
|
||||||
|
auto fullMusics = GetLiveMusics->Invoke<void*>(PictureBookWindowPresenter_instance,
|
||||||
|
Il2cppString::New(PictureBookWindowPresenter_charaId));
|
||||||
|
return PictureBookLiveSelectScreenModel_ctor_Orig(self, transitionParam, fullMusics, mtd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return PictureBookLiveSelectScreenModel_ctor_Orig(self, transitionParam, musics, mtd);
|
||||||
|
}
|
||||||
|
|
||||||
bool needRestoreHides = false;
|
bool needRestoreHides = false;
|
||||||
DEFINE_HOOK(void*, PictureBookLiveSelectScreenPresenter_MoveLiveScene, (void* self, void* produceLive,
|
DEFINE_HOOK(void*, PictureBookLiveSelectScreenPresenter_MoveLiveScene, (void* self, void* produceLive,
|
||||||
Il2cppString* characterId, Il2cppString* costumeId, Il2cppString* costumeHeadId)) {
|
Il2cppString* characterId, Il2cppString* idolCardId, Il2cppString* costumeId, Il2cppString* costumeHeadId, void* mtd)) {
|
||||||
needRestoreHides = false;
|
needRestoreHides = false;
|
||||||
Log::InfoFmt("MoveLiveScene: characterId: %s, costumeId: %s, costumeHeadId: %s,",
|
Log::InfoFmt("MoveLiveScene: characterId: %s, idolCardId: %s, costumeId: %s, costumeHeadId: %s,",
|
||||||
characterId->ToString().c_str(), costumeId->ToString().c_str(), costumeHeadId->ToString().c_str());
|
characterId->ToString().c_str(), idolCardId->ToString().c_str(), costumeId->ToString().c_str(), costumeHeadId->ToString().c_str());
|
||||||
|
|
||||||
/*
|
/*
|
||||||
characterId: hski, costumeId: hski-cstm-0002, costumeHeadId: costume_head_hski-cstm-0002,
|
characterId: hski, costumeId: hski-cstm-0002, costumeHeadId: costume_head_hski-cstm-0002,
|
||||||
@@ -488,12 +517,13 @@ namespace GakumasLocal::HookMain {
|
|||||||
|
|
||||||
if (Config::dbgMode && Config::enableLiveCustomeDress) {
|
if (Config::dbgMode && Config::enableLiveCustomeDress) {
|
||||||
// 修改 LiveFixedData_GetCharacter 可以更改 Loading 角色和演唱者名字,而不变更实际登台人
|
// 修改 LiveFixedData_GetCharacter 可以更改 Loading 角色和演唱者名字,而不变更实际登台人
|
||||||
return PictureBookLiveSelectScreenPresenter_MoveLiveScene_Orig(self, produceLive, characterId,
|
return PictureBookLiveSelectScreenPresenter_MoveLiveScene_Orig(self, produceLive, characterId, idolCardId,
|
||||||
Config::liveCustomeCostumeId.empty() ? costumeId : Il2cppString::New(Config::liveCustomeCostumeId),
|
Config::liveCustomeCostumeId.empty() ? costumeId : Il2cppString::New(Config::liveCustomeCostumeId),
|
||||||
Config::liveCustomeHeadId.empty() ? costumeHeadId : Il2cppString::New(Config::liveCustomeHeadId));
|
Config::liveCustomeHeadId.empty() ? costumeHeadId : Il2cppString::New(Config::liveCustomeHeadId),
|
||||||
|
mtd);
|
||||||
}
|
}
|
||||||
|
|
||||||
return PictureBookLiveSelectScreenPresenter_MoveLiveScene_Orig(self, produceLive, characterId, costumeId, costumeHeadId);
|
return PictureBookLiveSelectScreenPresenter_MoveLiveScene_Orig(self, produceLive, characterId, idolCardId, costumeId, costumeHeadId, mtd);
|
||||||
}
|
}
|
||||||
|
|
||||||
// std::string lastMusicId;
|
// std::string lastMusicId;
|
||||||
@@ -883,6 +913,12 @@ namespace GakumasLocal::HookMain {
|
|||||||
ADD_HOOK(PictureBookLiveThumbnailView_SetData,
|
ADD_HOOK(PictureBookLiveThumbnailView_SetData,
|
||||||
Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.OutGame.PictureBook",
|
Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.OutGame.PictureBook",
|
||||||
"PictureBookLiveThumbnailView", "SetDataAsync", {"*", "*", "*", "*"}));
|
"PictureBookLiveThumbnailView", "SetDataAsync", {"*", "*", "*", "*"}));
|
||||||
|
ADD_HOOK(PictureBookWindowPresenter_GetLiveMusics,
|
||||||
|
Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.OutGame",
|
||||||
|
"PictureBookWindowPresenter", "GetLiveMusics"));
|
||||||
|
ADD_HOOK(PictureBookLiveSelectScreenModel_ctor,
|
||||||
|
Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.OutGame",
|
||||||
|
"PictureBookLiveSelectScreenModel", ".ctor"));
|
||||||
|
|
||||||
ADD_HOOK(PictureBookLiveSelectScreenPresenter_MoveLiveScene,
|
ADD_HOOK(PictureBookLiveSelectScreenPresenter_MoveLiveScene,
|
||||||
Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.OutGame",
|
Il2cppUtils::GetMethodPointer("Assembly-CSharp.dll", "Campus.OutGame",
|
||||||
|
|||||||
@@ -521,6 +521,17 @@ namespace GakumasLocal::Local {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 匹配升级卡名
|
||||||
|
if (auto plusPos = origText.find_last_not_of('+'); plusPos != std::string::npos) {
|
||||||
|
const auto noPlusText = origText.substr(0, plusPos + 1);
|
||||||
|
|
||||||
|
if (const auto iter = genericText.find(noPlusText); iter != genericText.end()) {
|
||||||
|
size_t plusCount = origText.length() - (plusPos + 1);
|
||||||
|
*newStr = iter->second + std::string(plusCount, '+');
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// fmt 文本
|
// fmt 文本
|
||||||
auto fmtText = StringParser::ParseItems::parse(origText, false);
|
auto fmtText = StringParser::ParseItems::parse(origText, false);
|
||||||
if (fmtText.isValid) {
|
if (fmtText.isValid) {
|
||||||
|
|||||||
@@ -1,19 +1,29 @@
|
|||||||
package io.github.chinosk.gakumas.localify
|
package io.github.chinosk.gakumas.localify
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.content.ContentValues
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.pm.PackageInstaller
|
import android.content.pm.PackageInstaller
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.media.MediaScannerConnection
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
|
import android.provider.MediaStore
|
||||||
import android.provider.OpenableColumns
|
import android.provider.OpenableColumns
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
|
import androidx.activity.result.IntentSenderRequest
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.content.FileProvider
|
import androidx.core.content.FileProvider
|
||||||
import io.github.chinosk.gakumas.localify.mainUtils.IOnShell
|
import io.github.chinosk.gakumas.localify.mainUtils.IOnShell
|
||||||
import io.github.chinosk.gakumas.localify.mainUtils.LSPatchUtils
|
import io.github.chinosk.gakumas.localify.mainUtils.LSPatchUtils
|
||||||
@@ -29,11 +39,13 @@ import kotlinx.coroutines.withContext
|
|||||||
import org.lsposed.patch.LSPatch
|
import org.lsposed.patch.LSPatch
|
||||||
import org.lsposed.patch.util.Logger
|
import org.lsposed.patch.util.Logger
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.io.FileInputStream
|
||||||
import java.io.FileOutputStream
|
import java.io.FileOutputStream
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.io.OutputStream
|
import java.io.OutputStream
|
||||||
import java.nio.file.Files
|
import java.nio.file.Files
|
||||||
import java.nio.file.attribute.PosixFilePermissions
|
import java.nio.file.attribute.PosixFilePermissions
|
||||||
|
import java.util.concurrent.CountDownLatch
|
||||||
|
|
||||||
|
|
||||||
interface PatchCallback {
|
interface PatchCallback {
|
||||||
@@ -99,6 +111,137 @@ class PatchActivity : ComponentActivity() {
|
|||||||
private var reservePatchFiles: Boolean = false
|
private var reservePatchFiles: Boolean = false
|
||||||
var patchCallback: PatchCallback? = null
|
var patchCallback: PatchCallback? = null
|
||||||
|
|
||||||
|
private val writePermissionLauncher = registerForActivityResult(
|
||||||
|
ActivityResultContracts.StartIntentSenderForResult()
|
||||||
|
) { result ->
|
||||||
|
if (result.resultCode != RESULT_OK) {
|
||||||
|
Toast.makeText(this, "Permission Request Failed.", Toast.LENGTH_SHORT).show()
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val writePermissionLauncherQ = registerForActivityResult(
|
||||||
|
ActivityResultContracts.RequestPermission()
|
||||||
|
) { isGranted ->
|
||||||
|
if (!isGranted) {
|
||||||
|
Toast.makeText(this, "Permission Request Failed.", Toast.LENGTH_SHORT).show()
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun checkAndRequestWritePermission() {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
|
/*
|
||||||
|
// 针对 API 级别 30 及以上使用 MediaStore.createWriteRequest
|
||||||
|
val uri = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
|
||||||
|
val intentSender = MediaStore.createWriteRequest(contentResolver, listOf(uri)).intentSender
|
||||||
|
writePermissionLauncher.launch(IntentSenderRequest.Builder(intentSender).build())*/
|
||||||
|
}
|
||||||
|
else if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {
|
||||||
|
if (ContextCompat.checkSelfPermission(this, Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||||
|
!= PackageManager.PERMISSION_GRANTED) {
|
||||||
|
// 请求 WRITE_EXTERNAL_STORAGE 权限
|
||||||
|
writePermissionLauncherQ.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun writeFileToDownloadFolder(
|
||||||
|
sourceFile: File,
|
||||||
|
targetFolder: String,
|
||||||
|
targetFileName: String
|
||||||
|
): Boolean {
|
||||||
|
val downloadDirectory = Environment.DIRECTORY_DOWNLOADS
|
||||||
|
val relativePath = "$downloadDirectory/$targetFolder/"
|
||||||
|
val resolver = contentResolver
|
||||||
|
|
||||||
|
// 检查文件是否已经存在
|
||||||
|
val existingUri = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
|
||||||
|
val query = resolver.query(
|
||||||
|
existingUri,
|
||||||
|
arrayOf(MediaStore.Files.FileColumns._ID),
|
||||||
|
"${MediaStore.Files.FileColumns.RELATIVE_PATH}=? AND ${MediaStore.Files.FileColumns.DISPLAY_NAME}=?",
|
||||||
|
arrayOf(relativePath, targetFileName),
|
||||||
|
null
|
||||||
|
)
|
||||||
|
|
||||||
|
query?.use {
|
||||||
|
if (it.moveToFirst()) {
|
||||||
|
// 如果文件存在,则删除
|
||||||
|
val id = it.getLong(it.getColumnIndexOrThrow(MediaStore.Files.FileColumns._ID))
|
||||||
|
val deleteUri = MediaStore.Files.getContentUri("external", id)
|
||||||
|
resolver.delete(deleteUri, null, null)
|
||||||
|
Log.d(patchTag, "query delete: $deleteUri")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val contentValues = ContentValues().apply {
|
||||||
|
put(MediaStore.Downloads.DISPLAY_NAME, targetFileName)
|
||||||
|
put(MediaStore.Downloads.MIME_TYPE, "application/octet-stream")
|
||||||
|
put(MediaStore.Downloads.RELATIVE_PATH, relativePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
var uri = resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues)
|
||||||
|
Log.d(patchTag, "insert uri: $uri")
|
||||||
|
|
||||||
|
if (uri == null) {
|
||||||
|
val latch = CountDownLatch(1)
|
||||||
|
val downloadDirectory = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
|
||||||
|
val downloadSaveDirectory = File(downloadDirectory, targetFolder)
|
||||||
|
val downloadSaveFile = File(downloadSaveDirectory, targetFileName)
|
||||||
|
MediaScannerConnection.scanFile(this, arrayOf(downloadSaveFile.absolutePath),
|
||||||
|
null
|
||||||
|
) { _, _ ->
|
||||||
|
Log.d(patchTag, "scanFile finished.")
|
||||||
|
latch.countDown()
|
||||||
|
}
|
||||||
|
latch.await()
|
||||||
|
uri = resolver.insert(MediaStore.Downloads.EXTERNAL_CONTENT_URI, contentValues)
|
||||||
|
if (uri == null) {
|
||||||
|
Log.e(patchTag, "uri is still null")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return try {
|
||||||
|
resolver.openOutputStream(uri)?.use { outputStream ->
|
||||||
|
FileInputStream(sourceFile).use { inputStream ->
|
||||||
|
inputStream.copyTo(outputStream)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
contentValues.clear()
|
||||||
|
contentValues.put(MediaStore.Downloads.IS_PENDING, 0)
|
||||||
|
resolver.update(uri, contentValues, null, null)
|
||||||
|
true
|
||||||
|
} catch (e: Exception) {
|
||||||
|
resolver.delete(uri, null, null)
|
||||||
|
e.printStackTrace()
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun deleteFileInDownloadFolder(targetFolder: String, targetFileName: String) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
|
val selection =
|
||||||
|
"${MediaStore.MediaColumns.RELATIVE_PATH} = ? AND ${MediaStore.MediaColumns.DISPLAY_NAME} = ?"
|
||||||
|
val selectionArgs =
|
||||||
|
arrayOf("${Environment.DIRECTORY_DOWNLOADS}/$targetFolder/", targetFileName)
|
||||||
|
|
||||||
|
val uri = MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
|
||||||
|
contentResolver.delete(uri, selection, selectionArgs)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val file = File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "$targetFolder/$targetFileName")
|
||||||
|
if (file.exists()) {
|
||||||
|
if (file.delete()) {
|
||||||
|
// Toast.makeText(this, "文件已删除", Toast.LENGTH_SHORT).show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun handleSelectedFile(uri: Uri) {
|
private fun handleSelectedFile(uri: Uri) {
|
||||||
val fileName = uri.path?.substringAfterLast('/')
|
val fileName = uri.path?.substringAfterLast('/')
|
||||||
if (fileName != null) {
|
if (fileName != null) {
|
||||||
@@ -110,6 +253,7 @@ class PatchActivity : ComponentActivity() {
|
|||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
outputDir = "${filesDir.absolutePath}/output"
|
outputDir = "${filesDir.absolutePath}/output"
|
||||||
// ShizukuApi.init()
|
// ShizukuApi.init()
|
||||||
|
checkAndRequestWritePermission()
|
||||||
|
|
||||||
setContent {
|
setContent {
|
||||||
GakumasLocalifyTheme(dynamicColor = false, darkTheme = false) {
|
GakumasLocalifyTheme(dynamicColor = false, darkTheme = false) {
|
||||||
@@ -414,7 +558,38 @@ class PatchActivity : ComponentActivity() {
|
|||||||
return movedFiles
|
return movedFiles
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun installSplitApks(context: Context, apkFiles: List<File>, reservePatchFiles: Boolean,
|
private fun generateNonce(size: Int): String {
|
||||||
|
val nonceScope = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
|
||||||
|
val scopeSize = nonceScope.length
|
||||||
|
val nonceItem: (Int) -> Char = { nonceScope[(scopeSize * Math.random()).toInt()] }
|
||||||
|
return Array(size, nonceItem).joinToString("")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun saveFilesToDownload(context: PatchActivity, apkFiles: List<File>, targetFolder: String,
|
||||||
|
isMove: Boolean): List<String>? {
|
||||||
|
val ret: MutableList<String> = mutableListOf()
|
||||||
|
apkFiles.forEach { f ->
|
||||||
|
val success = context.writeFileToDownloadFolder(f, targetFolder, f.name)
|
||||||
|
if (success) {
|
||||||
|
ret.add(f.name)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val newName = "${generateNonce(6)}${f.name}"
|
||||||
|
val success2 = context.writeFileToDownloadFolder(f, targetFolder,
|
||||||
|
newName)
|
||||||
|
if (!success2) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
ret.add(newName)
|
||||||
|
}
|
||||||
|
if (isMove) {
|
||||||
|
f.delete()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret
|
||||||
|
}
|
||||||
|
|
||||||
|
suspend fun installSplitApks(context: PatchActivity, apkFiles: List<File>, reservePatchFiles: Boolean,
|
||||||
patchCallback: PatchCallback?): Pair<Int, String?> {
|
patchCallback: PatchCallback?): Pair<Int, String?> {
|
||||||
Log.i(TAG, "Perform install patched apks")
|
Log.i(TAG, "Perform install patched apks")
|
||||||
var status = PackageInstaller.STATUS_FAILURE
|
var status = PackageInstaller.STATUS_FAILURE
|
||||||
@@ -424,13 +599,27 @@ class PatchActivity : ComponentActivity() {
|
|||||||
runCatching {
|
runCatching {
|
||||||
val sdcardPath = Environment.getExternalStorageDirectory().path
|
val sdcardPath = Environment.getExternalStorageDirectory().path
|
||||||
val targetDirectory = File(sdcardPath, "Download/gkms_local_patch")
|
val targetDirectory = File(sdcardPath, "Download/gkms_local_patch")
|
||||||
val savedFiles = saveFileTo(apkFiles, targetDirectory, true, false)
|
// val savedFiles = saveFileTo(apkFiles, targetDirectory, true, false)
|
||||||
patchCallback?.onLog("Patched files: $savedFiles")
|
|
||||||
|
val savedFileNames = saveFilesToDownload(context, apkFiles, "gkms_local_patch", true)
|
||||||
|
if (savedFileNames == null) {
|
||||||
|
status = PackageInstaller.STATUS_FAILURE
|
||||||
|
message = "Save files failed."
|
||||||
|
return@runCatching
|
||||||
|
}
|
||||||
|
|
||||||
|
// patchCallback?.onLog("Patched files: $savedFiles")
|
||||||
|
patchCallback?.onLog("Patched files: $apkFiles")
|
||||||
|
|
||||||
if (!ShizukuApi.isPermissionGranted) {
|
if (!ShizukuApi.isPermissionGranted) {
|
||||||
status = PackageInstaller.STATUS_FAILURE
|
status = PackageInstaller.STATUS_FAILURE
|
||||||
message = "Shizuku Not Ready."
|
message = "Shizuku Not Ready."
|
||||||
if (!reservePatchFiles) savedFiles.forEach { file -> if (file.exists()) file.delete() }
|
// if (!reservePatchFiles) savedFiles.forEach { file -> if (file.exists()) file.delete() }
|
||||||
|
if (!reservePatchFiles) {
|
||||||
|
savedFileNames.forEach { f ->
|
||||||
|
context.deleteFileInDownloadFolder("gkms_local_patch", f)
|
||||||
|
}
|
||||||
|
}
|
||||||
return@runCatching
|
return@runCatching
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -455,16 +644,26 @@ class PatchActivity : ComponentActivity() {
|
|||||||
val action = if (reservePatchFiles) "cp" else "mv"
|
val action = if (reservePatchFiles) "cp" else "mv"
|
||||||
val copyFilesCmd: MutableList<String> = mutableListOf()
|
val copyFilesCmd: MutableList<String> = mutableListOf()
|
||||||
val movedFiles: MutableList<String> = mutableListOf()
|
val movedFiles: MutableList<String> = mutableListOf()
|
||||||
|
savedFileNames.forEach { file ->
|
||||||
|
val movedFileName = "$installDS/${file}"
|
||||||
|
movedFiles.add(movedFileName)
|
||||||
|
val dlSaveFileName = File(targetDirectory, file)
|
||||||
|
copyFilesCmd.add("$action ${dlSaveFileName.absolutePath} $movedFileName")
|
||||||
|
}
|
||||||
|
/*
|
||||||
savedFiles.forEach { file ->
|
savedFiles.forEach { file ->
|
||||||
val movedFileName = "$installDS/${file.name}"
|
val movedFileName = "$installDS/${file.name}"
|
||||||
movedFiles.add(movedFileName)
|
movedFiles.add(movedFileName)
|
||||||
copyFilesCmd.add("$action ${file.absolutePath} $movedFileName")
|
copyFilesCmd.add("$action ${file.absolutePath} $movedFileName")
|
||||||
}
|
}
|
||||||
val moveFileCommand = "mkdir $installDS && " +
|
*/
|
||||||
"chmod 777 $installDS && " +
|
val createDirCommand = "mkdir $installDS"
|
||||||
|
val moveFileCommand = "chmod 777 $installDS && " +
|
||||||
copyFilesCmd.joinToString(" && ")
|
copyFilesCmd.joinToString(" && ")
|
||||||
Log.d(TAG, "moveFileCommand: $moveFileCommand")
|
Log.d(TAG, "moveFileCommand: $moveFileCommand")
|
||||||
|
|
||||||
|
ShizukuShell(mutableListOf(), createDirCommand, ioShell).exec().destroy()
|
||||||
|
|
||||||
val cpFileShell = ShizukuShell(mutableListOf(), moveFileCommand, ioShell)
|
val cpFileShell = ShizukuShell(mutableListOf(), moveFileCommand, ioShell)
|
||||||
cpFileShell.exec()
|
cpFileShell.exec()
|
||||||
cpFileShell.destroy()
|
cpFileShell.destroy()
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ class ShizukuShell(private var mOutput: MutableList<String>, private var mComman
|
|||||||
val isBusy: Boolean
|
val isBusy: Boolean
|
||||||
get() = mOutput.size > 0 && mOutput[mOutput.size - 1] != "aShell: Finish"
|
get() = mOutput.size > 0 && mOutput[mOutput.size - 1] != "aShell: Finish"
|
||||||
|
|
||||||
fun exec() {
|
fun exec(): ShizukuShell {
|
||||||
try {
|
try {
|
||||||
Log.i(shellTag, "Execute: $mCommand")
|
Log.i(shellTag, "Execute: $mCommand")
|
||||||
shellCallback?.onShellLine(mCommand)
|
shellCallback?.onShellLine(mCommand)
|
||||||
@@ -66,6 +66,7 @@ class ShizukuShell(private var mOutput: MutableList<String>, private var mComman
|
|||||||
mProcess!!.waitFor()
|
mProcess!!.waitFor()
|
||||||
} catch (ignored: Exception) {
|
} catch (ignored: Exception) {
|
||||||
}
|
}
|
||||||
|
return this
|
||||||
}
|
}
|
||||||
|
|
||||||
fun destroy() {
|
fun destroy() {
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ import java.io.File
|
|||||||
|
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun InstallDiag(context: Context?, apkFiles: List<File>, patchCallback: PatchCallback?, reservePatchFiles: Boolean,
|
fun InstallDiag(context: PatchActivity?, apkFiles: List<File>, patchCallback: PatchCallback?, reservePatchFiles: Boolean,
|
||||||
onFinish: (Int, String?) -> Unit) {
|
onFinish: (Int, String?) -> Unit) {
|
||||||
// val scope = rememberCoroutineScope()
|
// val scope = rememberCoroutineScope()
|
||||||
// var uninstallFirst by remember { mutableStateOf(ShizukuApi.isPackageInstalledWithoutPatch(patchApp.app.packageName)) }
|
// var uninstallFirst by remember { mutableStateOf(ShizukuApi.isPackageInstalledWithoutPatch(patchApp.app.packageName)) }
|
||||||
|
|||||||
128
app/src/main/res/values-ja/strings.xml
Normal file
128
app/src/main/res/values-ja/strings.xml
Normal file
@@ -0,0 +1,128 @@
|
|||||||
|
<resources>
|
||||||
|
<string name="about">情報</string>
|
||||||
|
<string name="about_about_p1">このプラグインは完全に無料で提供されます。このプラグインで料金を支払ってしまった場合は、販売者に報告をしてください。</string>
|
||||||
|
<string name="about_about_p2">プラグインの QQ グループ: 975854705</string>
|
||||||
|
<string name="about_about_title">このプラグインについて</string>
|
||||||
|
<string name="about_contributors_asset_file">about_contributors_en.json</string>
|
||||||
|
<string name="about_warn_p1">このプラグインは学習とコミュニケーションのみを目的としています。</string>
|
||||||
|
<string name="about_warn_p2">外部プラグインは関連する TOS に違反するため、自己責任でご使用ください。</string>
|
||||||
|
<string name="about_warn_title">警告</string>
|
||||||
|
<string name="advanced_settings">高度な設定</string>
|
||||||
|
<string name="api_addr">APIアドレス (GitHub の最新リリース API)</string>
|
||||||
|
<string name="app_name">Gakumas Localify</string>
|
||||||
|
<string name="average">平均</string>
|
||||||
|
<string name="axisx_x">X 軸.x</string>
|
||||||
|
<string name="axisx_y">X 軸.y</string>
|
||||||
|
<string name="axisy_x">Y 軸.x</string>
|
||||||
|
<string name="axisy_y">Y 軸.y</string>
|
||||||
|
<string name="axisz_x">Z 軸.x</string>
|
||||||
|
<string name="axisz_y">Z 軸.y</string>
|
||||||
|
<string name="basic_settings">基本設定</string>
|
||||||
|
<string name="breast_param">胸のパラメーター</string>
|
||||||
|
<string name="breast_scale">胸の大きさ</string>
|
||||||
|
<string name="camera_settings">カメラ設定</string>
|
||||||
|
<string name="cancel">キャンセル</string>
|
||||||
|
<string name="character_counter_content_description">%1$d の %2$d に入力された文字</string>
|
||||||
|
<string name="character_counter_overflowed_content_description">文字制限が %2$d 文字中、 %1$d 文字を超えています</string>
|
||||||
|
<string name="character_counter_pattern">%1$d/%2$d</string>
|
||||||
|
<string name="check_built_in_resource">内蔵アセットのアップデートを確認</string>
|
||||||
|
<string name="check_resource_from_api">リソースアップデートを API から確認</string>
|
||||||
|
<string name="check_update">確認</string>
|
||||||
|
<string name="clear_text_end_icon_content_description">テキストを消去</string>
|
||||||
|
<string name="close_drawer">ナビゲーションメニューを閉じる</string>
|
||||||
|
<string name="close_sheet">シートを閉じる</string>
|
||||||
|
<string name="contributors">貢献者</string>
|
||||||
|
<string name="damping">ダンプ中</string>
|
||||||
|
<string name="debug_settings">デバッグ設定</string>
|
||||||
|
<string name="default_assets_check_api">https://api.github.com/repos/NatsumeLS/Gakumas-Translation-Data-EN/releases/latest</string>
|
||||||
|
<string name="default_error_message">入力が無効です</string>
|
||||||
|
<string name="default_popup_window_title">ポップアップウィンドウ</string>
|
||||||
|
<string name="del_remote_after_update">キャッシュファイルをアップデート後に削除</string>
|
||||||
|
<string name="delete_plugin_resource">プラグインリソースを削除</string>
|
||||||
|
<string name="download">ダウンロード</string>
|
||||||
|
<string name="downloaded_resource_version">ダウンロードされたバージョン</string>
|
||||||
|
<string name="dropdown_menu">ドロップダウンメニュー</string>
|
||||||
|
<string name="enable_breast_param">胸のパラメーターを有効化</string>
|
||||||
|
<string name="enable_free_camera">フリーカメラを有効化</string>
|
||||||
|
<string name="enable_plugin">プラグイン有効化 (ホットリロードなし)</string>
|
||||||
|
<string name="error_a11y_label">エラー: 無効</string>
|
||||||
|
<string name="error_icon_content_description">エラー</string>
|
||||||
|
<string name="export_text">テキストをエクスポート</string>
|
||||||
|
<string name="exposed_dropdown_menu_content_description">ドロップダウンメニューを表示</string>
|
||||||
|
<string name="force_export_resource">リソースのアップデートを強制する</string>
|
||||||
|
<string name="gakumas_localify">Gakumas Localify</string>
|
||||||
|
<string name="game_patch">ゲームパッチ</string>
|
||||||
|
<string name="graphic_settings">グラフィック設定</string>
|
||||||
|
<string name="hign">高</string>
|
||||||
|
<string name="home">ホーム</string>
|
||||||
|
<string name="home_shizuku_warning">一部の機能が使用できません</string>
|
||||||
|
<string name="icon_content_description">ダイアログアイコン</string>
|
||||||
|
<string name="in_progress">実行中</string>
|
||||||
|
<string name="indeterminate">部分的にチェック済み</string>
|
||||||
|
<string name="install">インストール</string>
|
||||||
|
<string name="installing">インストール中</string>
|
||||||
|
<string name="invalid_zip_file">無効なファイル</string>
|
||||||
|
<string name="invalid_zip_file_warn">このファイルは有効な ZIP 翻訳リソースパックではありません。</string>
|
||||||
|
<string name="isdirty">IsDirty</string>
|
||||||
|
<string name="item_view_role_description">タブ</string>
|
||||||
|
<string name="lazy_init">高速な初期化 (読み込みを遅延)</string>
|
||||||
|
<string name="liveUseCustomeDress">ライブのキャラクターをカスタム</string>
|
||||||
|
<string name="live_costume_head_id">ライブのカスタムヘッド ID (例: costume_head_hski-cstm-0002)</string>
|
||||||
|
<string name="live_custome_dress_id">ライブ衣装のカスタム ID (例: hski-cstm-0002)</string>
|
||||||
|
<string name="low">低</string>
|
||||||
|
<string name="max_high">ウルトラ</string>
|
||||||
|
<string name="middle">中</string>
|
||||||
|
<string name="off">OFF</string>
|
||||||
|
<string name="ok">OK</string>
|
||||||
|
<string name="on">ON</string>
|
||||||
|
<string name="orientation_landscape">横画面</string>
|
||||||
|
<string name="orientation_lock">画面を固定</string>
|
||||||
|
<string name="orientation_orig">オリジナル</string>
|
||||||
|
<string name="orientation_portrait">縦画面</string>
|
||||||
|
<string name="password_toggle_content_description">パスワードを表示</string>
|
||||||
|
<string name="patch_debuggable">デバッグを可能にする</string>
|
||||||
|
<string name="patch_finished">パッチが完了しました。インストールをしますか?</string>
|
||||||
|
<string name="patch_integrated">統合</string>
|
||||||
|
<string name="patch_integrated_desc">"モジュールを埋め込んだ状態なアプリでパッチを当てます。\nパッチを適用したアプリは LSPatch Manager なしで実行できますが、動的に管理はできません。\n統合パッチが適用されたアプリは、LSPatch Manager がインストールされていないデバイスでも使用が可能です。"</string>
|
||||||
|
<string name="patch_local">ローカル</string>
|
||||||
|
<string name="patch_local_desc">"モジュールを埋め込まずにアプリにパッチを当てます。\nXposed スコープは再パッチなしで動的に変更が可能です。\nローカルでのパッチを当てたアプリは、ローカルのデバイスでのみ実行可能です。"</string>
|
||||||
|
<string name="patch_mode">パッチモード</string>
|
||||||
|
<string name="patch_uninstall_confirm">アンインストールをしてもよろしいですか?</string>
|
||||||
|
<string name="patch_uninstall_text">"署名が異なるため、パッチをインストールする前に元となるアプリをアンインストールする必要があります。\n個人データのバックアップを設定済みであることを確認してください。"</string>
|
||||||
|
<string name="pendulum">揺れ</string>
|
||||||
|
<string name="pendulumrange">揺れの範囲</string>
|
||||||
|
<string name="plugin_code">プラグインのコード</string>
|
||||||
|
<string name="project_contribution">プロジェクトの貢献者</string>
|
||||||
|
<string name="range_end">範囲の終了</string>
|
||||||
|
<string name="range_start">範囲の開始</string>
|
||||||
|
<string name="renderscale">RenderScale (0.5/0.59/0.67/0.77/1.0)</string>
|
||||||
|
<string name="replace_font">フォントを置換する</string>
|
||||||
|
<string name="reserve_patched">パッチ済みの APK を予約する</string>
|
||||||
|
<string name="resource_settings">リソース設定</string>
|
||||||
|
<string name="resource_url">リソース URL</string>
|
||||||
|
<string name="rootweight">ルートウェイト</string>
|
||||||
|
<string name="selected">選択済み</string>
|
||||||
|
<string name="setFpsTitle">最大 FPS (0 はオリジナルの設定を使用します)</string>
|
||||||
|
<string name="shizuku_available">Shizuku サービスが有効です</string>
|
||||||
|
<string name="shizuku_unavailable">Shizuku サービスが接続されていません</string>
|
||||||
|
<string name="spring">跳ね</string>
|
||||||
|
<string name="start_game">ゲーム開始 / ホットリロードの設定</string>
|
||||||
|
<string name="stiffness">剛性</string>
|
||||||
|
<string name="support_file_types">"対応ファイル:\n単一または複数選択: apk\n単一選択: apks、xapk、zip"</string>
|
||||||
|
<string name="switch_role">切り替え</string>
|
||||||
|
<string name="tab">タブ</string>
|
||||||
|
<string name="template_percent">%1$d パーセント。</string>
|
||||||
|
<string name="test_mode_live">テストモード - ライブ</string>
|
||||||
|
<string name="text_hook_test_mode">テキストフックテストモード</string>
|
||||||
|
<string name="translation_repository">翻訳のリポジトリ</string>
|
||||||
|
<string name="translation_resource_update">翻訳リソースをアップデート</string>
|
||||||
|
<string name="unlockAllLive">すべてのライブを開放</string>
|
||||||
|
<string name="useCustomeGraphicSettings">カスタムグラフィック設定を使用する</string>
|
||||||
|
<string name="use_remote_zip_resource">リモート ZIP リソースを使用する</string>
|
||||||
|
<string name="usearmcorrection">Arm コレクションを使用する</string>
|
||||||
|
<string name="uselimit_0_1">リミットレンジの倍率 (0 は無制限)</string>
|
||||||
|
<string name="uselimitmultiplier">乗数制限を使用する</string>
|
||||||
|
<string name="usescale">胸の大きさを使用する</string>
|
||||||
|
<string name="very_high">最高</string>
|
||||||
|
<string name="warning">警告</string>
|
||||||
|
</resources>
|
||||||
Reference in New Issue
Block a user