Package module
This commit is contained in:
parent
bb7bfaad26
commit
78420d8758
|
|
@ -7,6 +7,10 @@
|
|||
android:name="android.permission.QUERY_ALL_PACKAGES"
|
||||
tools:ignore="QueryAllPackagesPermission" />
|
||||
|
||||
<uses-permission
|
||||
android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||
android:maxSdkVersion="28" />
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
|
|
|
|||
|
|
@ -1,6 +1,9 @@
|
|||
package org.lsposed.lspatch
|
||||
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.lsposed.patch.LSPatch
|
||||
import org.lsposed.patch.util.Logger
|
||||
|
||||
object Patcher {
|
||||
class Options(
|
||||
|
|
@ -38,7 +41,9 @@ object Patcher {
|
|||
}
|
||||
}
|
||||
|
||||
fun patch(options: Options) {
|
||||
LSPatch(*options.toStringArray()).doCommandLine()
|
||||
suspend fun patch(logger: Logger, options: Options) {
|
||||
withContext(Dispatchers.IO) {
|
||||
LSPatch(logger, *options.toStringArray()).doCommandLine()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,73 @@
|
|||
package org.lsposed.lspatch.ui.component
|
||||
|
||||
import androidx.compose.animation.core.*
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
private val ShimmerColorShades
|
||||
@Composable get() = listOf(
|
||||
MaterialTheme.colorScheme.secondaryContainer.copy(0.9f),
|
||||
MaterialTheme.colorScheme.secondaryContainer.copy(0.2f),
|
||||
MaterialTheme.colorScheme.secondaryContainer.copy(0.9f)
|
||||
)
|
||||
|
||||
class ShimmerScope(val brush: Brush)
|
||||
|
||||
@Composable
|
||||
fun ShimmerAnimation(
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
content: @Composable ShimmerScope.() -> Unit
|
||||
) {
|
||||
val transition = rememberInfiniteTransition()
|
||||
val translateAnim by transition.animateFloat(
|
||||
initialValue = 0f,
|
||||
targetValue = 1000f,
|
||||
animationSpec = infiniteRepeatable(
|
||||
tween(durationMillis = 1200, easing = FastOutSlowInEasing),
|
||||
RepeatMode.Reverse
|
||||
)
|
||||
)
|
||||
|
||||
val brush = Brush.linearGradient(
|
||||
colors = if (enabled) ShimmerColorShades else List(3) { ShimmerColorShades[0] },
|
||||
start = Offset(10f, 10f),
|
||||
end = Offset(translateAnim, translateAnim)
|
||||
)
|
||||
|
||||
Surface(modifier.background(brush)) {
|
||||
content(ShimmerScope(brush))
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun ShimmerPreview() {
|
||||
ShimmerAnimation {
|
||||
Column(modifier = Modifier.padding(16.dp)) {
|
||||
Spacer(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.size(250.dp)
|
||||
.background(brush = brush)
|
||||
)
|
||||
Spacer(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(30.dp)
|
||||
.padding(vertical = 8.dp)
|
||||
.background(brush = brush)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,11 +1,17 @@
|
|||
package org.lsposed.lspatch.ui.page
|
||||
|
||||
import android.os.Environment
|
||||
import android.util.Log
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.animation.animateContentSize
|
||||
import androidx.compose.animation.core.Spring
|
||||
import androidx.compose.animation.core.spring
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Api
|
||||
|
|
@ -18,7 +24,9 @@ import androidx.compose.runtime.livedata.observeAsState
|
|||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
|
|
@ -26,25 +34,32 @@ import org.lsposed.lspatch.Patcher
|
|||
import org.lsposed.lspatch.R
|
||||
import org.lsposed.lspatch.TAG
|
||||
import org.lsposed.lspatch.ui.component.SelectionColumn
|
||||
import org.lsposed.lspatch.ui.component.ShimmerAnimation
|
||||
import org.lsposed.lspatch.ui.component.settings.SettingsCheckBox
|
||||
import org.lsposed.lspatch.ui.component.settings.SettingsItem
|
||||
import org.lsposed.lspatch.ui.util.LocalNavController
|
||||
import org.lsposed.lspatch.ui.util.isScrolledToEnd
|
||||
import org.lsposed.lspatch.ui.util.lastItemIndex
|
||||
import org.lsposed.lspatch.ui.viewmodel.AppInfo
|
||||
import org.lsposed.patch.util.Logger
|
||||
|
||||
enum class PatchState {
|
||||
SELECTING, CONFIGURING, SUBMITTING, PATCHING, FINISHED
|
||||
}
|
||||
|
||||
class NewPatchPageViewModel : ViewModel() {
|
||||
var patchApp by mutableStateOf<AppInfo?>(null)
|
||||
var confirm by mutableStateOf(false)
|
||||
var patchState by mutableStateOf(PatchState.SELECTING)
|
||||
var patchOptions by mutableStateOf<Patcher.Options?>(null)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun NewPatchFab() {
|
||||
val viewModel = viewModel<NewPatchPageViewModel>()
|
||||
if (viewModel.patchApp != null) {
|
||||
if (viewModel.patchState == PatchState.CONFIGURING) {
|
||||
ExtendedFloatingActionButton(
|
||||
text = { Text(stringResource(R.string.patch_start)) },
|
||||
icon = { Icon(Icons.Outlined.AutoFixHigh, null) },
|
||||
onClick = { viewModel.confirm = true }
|
||||
onClick = { viewModel.patchState = PatchState.SUBMITTING }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -53,21 +68,20 @@ fun NewPatchFab() {
|
|||
fun NewPatchPage() {
|
||||
val viewModel = viewModel<NewPatchPageViewModel>()
|
||||
val navController = LocalNavController.current
|
||||
val appInfo by navController.currentBackStackEntry!!.savedStateHandle
|
||||
val patchApp by navController.currentBackStackEntry!!.savedStateHandle
|
||||
.getLiveData<AppInfo>("appInfo").observeAsState()
|
||||
viewModel.patchApp = appInfo
|
||||
if (viewModel.patchState == PatchState.SELECTING && patchApp != null) viewModel.patchState = PatchState.CONFIGURING
|
||||
|
||||
Log.d(TAG, "confirm = ${viewModel.confirm}")
|
||||
|
||||
when {
|
||||
viewModel.patchApp == null -> navController.navigate(PageList.SelectApps.name + "/false")
|
||||
viewModel.patchOptions == null -> PatchOptionsPage(viewModel.patchApp!!, viewModel.confirm)
|
||||
else -> PatchingPage(viewModel.patchOptions!!)
|
||||
Log.d(TAG, "NewPatchPage: ${viewModel.patchState}")
|
||||
when (viewModel.patchState) {
|
||||
PatchState.SELECTING -> navController.navigate(PageList.SelectApps.name + "/false")
|
||||
PatchState.CONFIGURING, PatchState.SUBMITTING -> PatchOptionsPage(patchApp!!)
|
||||
PatchState.PATCHING, PatchState.FINISHED -> DoPatchPage(viewModel.patchOptions!!)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PatchOptionsPage(patchApp: AppInfo, confirm: Boolean) {
|
||||
private fun PatchOptionsPage(patchApp: AppInfo) {
|
||||
val viewModel = viewModel<NewPatchPageViewModel>()
|
||||
var useManager by rememberSaveable { mutableStateOf(true) }
|
||||
var debuggable by rememberSaveable { mutableStateOf(false) }
|
||||
|
|
@ -77,9 +91,11 @@ private fun PatchOptionsPage(patchApp: AppInfo, confirm: Boolean) {
|
|||
val sigBypassLevel by rememberSaveable { mutableStateOf(2) }
|
||||
var overrideVersionCode by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
if (confirm) LaunchedEffect(patchApp) {
|
||||
if (viewModel.patchState == PatchState.SUBMITTING) LaunchedEffect(patchApp) {
|
||||
val downloadDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS).path
|
||||
viewModel.patchOptions = Patcher.Options(
|
||||
apkPaths = arrayOf(patchApp.app.sourceDir), // TODO: Split Apk
|
||||
outputPath = downloadDir,
|
||||
debuggable = debuggable,
|
||||
sigbypassLevel = sigBypassLevel,
|
||||
v1 = v1, v2 = v2, v3 = v3,
|
||||
|
|
@ -88,6 +104,7 @@ private fun PatchOptionsPage(patchApp: AppInfo, confirm: Boolean) {
|
|||
verbose = true,
|
||||
embeddedModules = emptyList() // TODO: Embed modules
|
||||
)
|
||||
viewModel.patchState = PatchState.PATCHING
|
||||
}
|
||||
|
||||
Column(
|
||||
|
|
@ -172,6 +189,89 @@ private fun PatchOptionsPage(patchApp: AppInfo, confirm: Boolean) {
|
|||
}
|
||||
|
||||
@Composable
|
||||
private fun PatchingPage(patcherOptions: Patcher.Options) {
|
||||
|
||||
private fun DoPatchPage(patcherOptions: Patcher.Options) {
|
||||
val viewModel = viewModel<NewPatchPageViewModel>()
|
||||
val navController = LocalNavController.current
|
||||
val logs = remember { mutableStateListOf<Pair<Int, String>>() }
|
||||
val logger = remember {
|
||||
object : Logger() {
|
||||
override fun d(msg: String) {
|
||||
if (verbose) {
|
||||
Log.d(TAG, msg)
|
||||
logs += Log.DEBUG to msg
|
||||
}
|
||||
}
|
||||
|
||||
override fun i(msg: String) {
|
||||
Log.i(TAG, msg)
|
||||
logs += Log.INFO to msg
|
||||
}
|
||||
|
||||
override fun e(msg: String) {
|
||||
Log.e(TAG, msg)
|
||||
logs += Log.ERROR to msg
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(patcherOptions) {
|
||||
Patcher.patch(logger, patcherOptions)
|
||||
viewModel.patchState = PatchState.FINISHED
|
||||
}
|
||||
|
||||
Column(
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.padding(24.dp)
|
||||
.wrapContentHeight()
|
||||
.animateContentSize(spring(stiffness = Spring.StiffnessLow))
|
||||
) {
|
||||
val patching by remember { derivedStateOf { viewModel.patchState == PatchState.PATCHING } }
|
||||
ShimmerAnimation(enabled = patching) {
|
||||
CompositionLocalProvider(
|
||||
LocalTextStyle provides MaterialTheme.typography.bodyMedium.copy(fontFamily = FontFamily.Monospace)
|
||||
) {
|
||||
val scrollState = rememberLazyListState()
|
||||
LazyColumn(
|
||||
state = scrollState,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(max = 300.dp)
|
||||
.clip(RoundedCornerShape(32.dp))
|
||||
.background(brush)
|
||||
.padding(horizontal = 24.dp, vertical = 18.dp)
|
||||
) {
|
||||
items(logs) {
|
||||
when (it.first) {
|
||||
Log.DEBUG -> Text(text = it.second)
|
||||
Log.INFO -> Text(text = it.second)
|
||||
Log.ERROR -> Text(text = it.second, color = MaterialTheme.colorScheme.error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(scrollState.lastItemIndex) {
|
||||
if (!scrollState.isScrolledToEnd) {
|
||||
scrollState.animateScrollToItem(scrollState.lastItemIndex!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!patching) {
|
||||
Row(Modifier.padding(top = 12.dp)) {
|
||||
Button(
|
||||
onClick = { navController.popBackStack() },
|
||||
modifier = Modifier.weight(1f),
|
||||
content = { Text(stringResource(R.string.patch_return)) }
|
||||
)
|
||||
Spacer(Modifier.weight(0.2f))
|
||||
Button(
|
||||
onClick = { /* TODO: Install */ },
|
||||
modifier = Modifier.weight(1f),
|
||||
content = { Text(stringResource(R.string.patch_install)) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,12 @@
|
|||
package org.lsposed.lspatch.ui.util
|
||||
|
||||
import androidx.compose.foundation.lazy.LazyListState
|
||||
|
||||
val LazyListState.lastVisibleItemIndex
|
||||
get() = layoutInfo.visibleItemsInfo.lastOrNull()?.index
|
||||
|
||||
val LazyListState.lastItemIndex
|
||||
get() = layoutInfo.totalItemsCount.let { if (it == 0) null else it }
|
||||
|
||||
val LazyListState.isScrolledToEnd
|
||||
get() = lastVisibleItemIndex == lastItemIndex
|
||||
|
|
@ -26,4 +26,6 @@
|
|||
<string name="patch_override_version_code">Override version code</string>
|
||||
<string name="patch_override_version_code_desc">Override the patched app\'s version code to 1\nThis allows downgrade installation</string>
|
||||
<string name="patch_start">Start Patch</string>
|
||||
<string name="patch_return">Return</string>
|
||||
<string name="patch_install">Install</string>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -23,6 +23,8 @@ import org.apache.commons.io.FileUtils;
|
|||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.lsposed.lspatch.share.PatchConfig;
|
||||
import org.lsposed.patch.util.ApkSignatureHelper;
|
||||
import org.lsposed.patch.util.JavaLogger;
|
||||
import org.lsposed.patch.util.Logger;
|
||||
import org.lsposed.patch.util.ManifestParser;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
|
|
@ -114,15 +116,19 @@ public class LSPatch {
|
|||
|
||||
private final JCommander jCommander;
|
||||
|
||||
public LSPatch(String... args) {
|
||||
private final Logger logger;
|
||||
|
||||
public LSPatch(Logger logger, String... args) {
|
||||
jCommander = JCommander.newBuilder()
|
||||
.addObject(this)
|
||||
.build();
|
||||
jCommander.parse(args);
|
||||
this.logger = logger;
|
||||
logger.verbose = verbose;
|
||||
}
|
||||
|
||||
public static void main(String... args) throws IOException {
|
||||
LSPatch lsPatch = new LSPatch(args);
|
||||
LSPatch lsPatch = new LSPatch(new JavaLogger(), args);
|
||||
try {
|
||||
lsPatch.doCommandLine();
|
||||
} catch (PatchError e) {
|
||||
|
|
@ -157,7 +163,7 @@ public class LSPatch {
|
|||
|
||||
if (outputFile.exists() && !forceOverwrite)
|
||||
throw new PatchError(outputPath + " exists. Use --force to overwrite");
|
||||
System.out.println("Processing " + srcApkFile + " -> " + outputFile);
|
||||
logger.i("Processing " + srcApkFile + " -> " + outputFile);
|
||||
|
||||
patch(srcApkFile, outputFile);
|
||||
}
|
||||
|
|
@ -170,16 +176,15 @@ public class LSPatch {
|
|||
File tmpApk = Files.createTempFile(srcApkFile.getName(), "unsigned").toFile();
|
||||
tmpApk.delete();
|
||||
|
||||
if (verbose)
|
||||
System.out.println("apk path: " + srcApkFile);
|
||||
logger.d("apk path: " + srcApkFile);
|
||||
|
||||
System.out.println("Parsing original apk...");
|
||||
logger.i("Parsing original apk...");
|
||||
|
||||
try (var dstZFile = ZFile.openReadWrite(tmpApk, Z_FILE_OPTIONS);
|
||||
var srcZFile = dstZFile.addNestedZip((ignore) -> ORIGINAL_APK_ASSET_PATH, srcApkFile, false)) {
|
||||
|
||||
// sign apk
|
||||
System.out.println("Register apk signer...");
|
||||
logger.i("Register apk signer...");
|
||||
try {
|
||||
var keyStore = KeyStore.getInstance(KeyStore.getDefaultType());
|
||||
try (var is = getClass().getClassLoader().getResourceAsStream("assets/keystore")) {
|
||||
|
|
@ -205,8 +210,7 @@ public class LSPatch {
|
|||
throw new PatchError("get original signature failed");
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
System.out.println("Original signature\n" + originalSignature);
|
||||
logger.d("Original signature\n" + originalSignature);
|
||||
}
|
||||
|
||||
// copy out manifest file from zlib
|
||||
|
|
@ -222,11 +226,10 @@ public class LSPatch {
|
|||
throw new PatchError("Failed to parse AndroidManifest.xml");
|
||||
appComponentFactory = pair.appComponentFactory == null ? "" : pair.appComponentFactory;
|
||||
|
||||
if (verbose)
|
||||
System.out.println("original appComponentFactory class: " + appComponentFactory);
|
||||
logger.d("original appComponentFactory class: " + appComponentFactory);
|
||||
}
|
||||
|
||||
System.out.println("Patching apk...");
|
||||
logger.i("Patching apk...");
|
||||
// modify manifest
|
||||
try (var is = new ByteArrayInputStream(modifyManifestFile(manifestEntry.open()))) {
|
||||
dstZFile.add(ANDROID_MANIFEST_XML, is);
|
||||
|
|
@ -234,8 +237,7 @@ public class LSPatch {
|
|||
throw new PatchError("Error when modifying manifest", e);
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
System.out.println("Adding native lib..");
|
||||
logger.d("Adding native lib..");
|
||||
|
||||
// copy so and dex files into the unzipped apk
|
||||
// do not put liblspd.so into apk!lib because x86 native bridge causes crash
|
||||
|
|
@ -247,12 +249,10 @@ public class LSPatch {
|
|||
// More exception info
|
||||
throw new PatchError("Error when adding native lib", e);
|
||||
}
|
||||
if (verbose)
|
||||
System.out.println("added " + entryName);
|
||||
logger.d("added " + entryName);
|
||||
}
|
||||
|
||||
if (verbose)
|
||||
System.out.println("Adding dex..");
|
||||
logger.d("Adding dex..");
|
||||
|
||||
try (var is = getClass().getClassLoader().getResourceAsStream("assets/dex/loader.dex")) {
|
||||
dstZFile.add("classes.dex", is);
|
||||
|
|
@ -278,8 +278,7 @@ public class LSPatch {
|
|||
|
||||
Set<String> apkArchs = new HashSet<>();
|
||||
|
||||
if (verbose)
|
||||
System.out.println("Search target apk library arch...");
|
||||
logger.d("Search target apk library arch...");
|
||||
|
||||
for (StoredEntry storedEntry : srcZFile.entries()) {
|
||||
var name = storedEntry.getCentralDirectoryHeader().getName();
|
||||
|
|
@ -291,7 +290,7 @@ public class LSPatch {
|
|||
if (apkArchs.isEmpty()) apkArchs.addAll(ARCHES);
|
||||
apkArchs.removeIf((arch) -> {
|
||||
if (!ARCHES.contains(arch) && !arch.equals("armeabi")) {
|
||||
System.err.println("Warning: unsupported arch " + arch + ". Skipping...");
|
||||
logger.e("Warning: unsupported arch " + arch + ". Skipping...");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
@ -300,8 +299,7 @@ public class LSPatch {
|
|||
embedModules(dstZFile);
|
||||
|
||||
// create zip link
|
||||
if (verbose)
|
||||
System.out.println("Creating nested apk link...");
|
||||
logger.d("Creating nested apk link...");
|
||||
|
||||
for (StoredEntry entry : srcZFile.entries()) {
|
||||
String name = entry.getCentralDirectoryHeader().getName();
|
||||
|
|
@ -314,12 +312,12 @@ public class LSPatch {
|
|||
|
||||
dstZFile.realign();
|
||||
|
||||
System.out.println("Writing apk...");
|
||||
logger.i("Writing apk...");
|
||||
} finally {
|
||||
try {
|
||||
outputFile.delete();
|
||||
FileUtils.moveFile(tmpApk, outputFile);
|
||||
System.out.println("Done. Output APK: " + outputFile.getAbsolutePath());
|
||||
logger.i("Done. Output APK: " + outputFile.getAbsolutePath());
|
||||
} catch (Throwable e) {
|
||||
throw new PatchError("Error writing apk", e);
|
||||
}
|
||||
|
|
@ -327,7 +325,7 @@ public class LSPatch {
|
|||
}
|
||||
|
||||
private void embedModules(ZFile zFile) {
|
||||
System.out.println("Embedding modules...");
|
||||
logger.i("Embedding modules...");
|
||||
for (var module : modules) {
|
||||
File file = new File(module);
|
||||
try (var apk = ZFile.openReadOnly(new File(module));
|
||||
|
|
@ -336,10 +334,10 @@ public class LSPatch {
|
|||
) {
|
||||
var manifest = Objects.requireNonNull(ManifestParser.parseManifestFile(xmlIs));
|
||||
var packageName = manifest.packageName;
|
||||
System.out.println(" - " + packageName);
|
||||
logger.i(" - " + packageName);
|
||||
zFile.add("assets/lspatch/modules/" + packageName + ".bin", fileIs);
|
||||
} catch (NullPointerException | IOException e) {
|
||||
System.err.println(module + " does not exist or is not a valid apk file.");
|
||||
logger.e(module + " does not exist or is not a valid apk file.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
package org.lsposed.patch.util;
|
||||
|
||||
public class JavaLogger extends Logger {
|
||||
|
||||
@Override
|
||||
public void d(String msg) {
|
||||
if (verbose) System.out.println(msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void i(String msg) {
|
||||
System.out.println(msg);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void e(String msg) {
|
||||
System.err.println(msg);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
package org.lsposed.patch.util;
|
||||
|
||||
public abstract class Logger {
|
||||
|
||||
public boolean verbose = false;
|
||||
|
||||
abstract public void d(String msg);
|
||||
|
||||
abstract public void i(String msg);
|
||||
|
||||
abstract public void e(String msg);
|
||||
}
|
||||
Loading…
Reference in New Issue