1. Dependency check with download redirect support.
2. Config editor upgrade (ref: https://github.com/josdejong/jsoneditor). 3. Upgrade for Android R. 4. Bug fix
This commit is contained in:
parent
feca85bcbe
commit
d014d66f0d
|
@ -0,0 +1,7 @@
|
||||||
|
<component name="ProjectDictionaryState">
|
||||||
|
<dictionary name="Zane">
|
||||||
|
<words>
|
||||||
|
<w>stadew</w>
|
||||||
|
</words>
|
||||||
|
</dictionary>
|
||||||
|
</component>
|
|
@ -5,21 +5,18 @@ apply plugin: 'kotlin-android'
|
||||||
apply plugin: 'kotlin-android-extensions'
|
apply plugin: 'kotlin-android-extensions'
|
||||||
|
|
||||||
android {
|
android {
|
||||||
compileSdkVersion 28
|
compileSdkVersion 30
|
||||||
|
|
||||||
|
|
||||||
defaultConfig {
|
defaultConfig {
|
||||||
applicationId "com.zane.smapiinstaller"
|
applicationId "com.zane.smapiinstaller"
|
||||||
minSdkVersion 19
|
minSdkVersion 19
|
||||||
targetSdkVersion 28
|
targetSdkVersion 30
|
||||||
versionCode 56
|
versionCode 58
|
||||||
versionName "1.6.3"
|
versionName "1.6.5"
|
||||||
|
|
||||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
multiDexEnabled true
|
multiDexEnabled true
|
||||||
ndk {
|
|
||||||
abiFilters 'armeabi-v7a','arm64-v8a'
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
buildTypes {
|
buildTypes {
|
||||||
|
@ -53,8 +50,8 @@ android {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
viewBinding {
|
buildFeatures {
|
||||||
enabled = true
|
viewBinding true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,10 +5,12 @@
|
||||||
package="com.zane.smapiinstaller">
|
package="com.zane.smapiinstaller">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
|
||||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
|
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
|
||||||
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES"/>
|
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES"/>
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
|
||||||
|
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".MainApplication"
|
android:name=".MainApplication"
|
||||||
|
@ -21,6 +23,7 @@
|
||||||
android:configChanges="orientation|keyboard|screenSize"
|
android:configChanges="orientation|keyboard|screenSize"
|
||||||
android:networkSecurityConfig="@xml/network_security_config"
|
android:networkSecurityConfig="@xml/network_security_config"
|
||||||
android:theme="@style/AppTheme"
|
android:theme="@style/AppTheme"
|
||||||
|
android:requestLegacyExternalStorage="true"
|
||||||
tools:ignore="UnusedAttribute">
|
tools:ignore="UnusedAttribute">
|
||||||
<activity
|
<activity
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
<!DOCTYPE HTML>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<!-- when using the mode "code", it's important to specify charset utf-8 -->
|
||||||
|
<meta charset="utf-8">
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no">
|
||||||
|
<link href="jsoneditor.min.css" rel="stylesheet" type="text/css">
|
||||||
|
<script src="jsoneditor.min.js"></script>
|
||||||
|
<style type="text/css">
|
||||||
|
@media screen and (min-width: 520px) {
|
||||||
|
.jsoneditor-search input {
|
||||||
|
width: 120px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media screen and (max-width: 490px) {
|
||||||
|
.jsoneditor-search input {
|
||||||
|
width: 90px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media screen and (max-width: 460px) {
|
||||||
|
.jsoneditor-search input {
|
||||||
|
width: 60px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media screen and (max-width: 430px) {
|
||||||
|
.jsoneditor-search input {
|
||||||
|
width: 30px;
|
||||||
|
}
|
||||||
|
#jsoneditor {
|
||||||
|
min-width: 390px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@media screen and (max-width: 410px) {
|
||||||
|
.jsoneditor-search input {
|
||||||
|
width: 20px;
|
||||||
|
}
|
||||||
|
#jsoneditor {
|
||||||
|
min-width: 370px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="jsoneditor" style="width: 100%"></div>
|
||||||
|
<script>
|
||||||
|
webObject = { getText: function(){ return '{"Array":[1,2,3],"Boolean":true,"Null":null,"Number":123,"Object":{"a":"b","c":"d"},"String":"Hello World"}'; }, getMode: function(){ return 'tree';}, getLanguage: function(){ return 'zh-CN';}, isEditable: function(){ return true;}, getHeight: function(){ return 400;}, };
|
||||||
|
// create the editor
|
||||||
|
const container = document.getElementById("jsoneditor");
|
||||||
|
container.style.height = webObject.getHeight() + "px";
|
||||||
|
const options = {
|
||||||
|
mode: webObject.getMode(),
|
||||||
|
modes: webObject.getMode() == "text-plain" ? [] : ["code","form","tree","text","preview"],
|
||||||
|
language: webObject.getLanguage(),
|
||||||
|
enableTransform: false,
|
||||||
|
onCreateMenu: function (items, node) {
|
||||||
|
return items.filter(function (item) {
|
||||||
|
return item.className !== 'jsoneditor-extract';
|
||||||
|
})
|
||||||
|
}
|
||||||
|
};
|
||||||
|
options.onEditable = function() { return webObject.isEditable();};
|
||||||
|
|
||||||
|
const editor = new JSONEditor(container, options);
|
||||||
|
|
||||||
|
// set json
|
||||||
|
var initialJson;
|
||||||
|
if(webObject.getMode() == "text-plain") {
|
||||||
|
initialJson = webObject.getText();
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
initialJson = JSON.parse(webObject.getText());
|
||||||
|
}
|
||||||
|
editor.setSchema(null);
|
||||||
|
editor.set(initialJson);
|
||||||
|
function getJson()
|
||||||
|
{
|
||||||
|
// get json
|
||||||
|
webObject.setText(JSON.stringfy(editor.get()))
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -0,0 +1,749 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="240"
|
||||||
|
height="144"
|
||||||
|
id="svg4136"
|
||||||
|
version="1.1"
|
||||||
|
inkscape:version="0.91 r13725"
|
||||||
|
sodipodi:docname="jsoneditor-icons.svg">
|
||||||
|
<title
|
||||||
|
id="title6512">JSON Editor Icons</title>
|
||||||
|
<metadata
|
||||||
|
id="metadata4148">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title>JSON Editor Icons</dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<defs
|
||||||
|
id="defs4146" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
pagecolor="#ff63ff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1"
|
||||||
|
objecttolerance="10"
|
||||||
|
gridtolerance="10"
|
||||||
|
guidetolerance="10"
|
||||||
|
inkscape:pageopacity="0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:window-width="1920"
|
||||||
|
inkscape:window-height="1026"
|
||||||
|
id="namedview4144"
|
||||||
|
showgrid="true"
|
||||||
|
inkscape:zoom="4"
|
||||||
|
inkscape:cx="13.229181"
|
||||||
|
inkscape:cy="119.82429"
|
||||||
|
inkscape:window-x="0"
|
||||||
|
inkscape:window-y="0"
|
||||||
|
inkscape:window-maximized="1"
|
||||||
|
inkscape:current-layer="svg4136"
|
||||||
|
showguides="false"
|
||||||
|
borderlayer="false"
|
||||||
|
inkscape:showpageshadow="true"
|
||||||
|
showborder="true">
|
||||||
|
<inkscape:grid
|
||||||
|
type="xygrid"
|
||||||
|
id="grid4640"
|
||||||
|
empspacing="24" />
|
||||||
|
</sodipodi:namedview>
|
||||||
|
<!-- Created with SVG-edit - http://svg-edit.googlecode.com/ -->
|
||||||
|
<rect
|
||||||
|
style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0"
|
||||||
|
id="svg_1"
|
||||||
|
height="16"
|
||||||
|
width="16"
|
||||||
|
y="4"
|
||||||
|
x="4" />
|
||||||
|
<rect
|
||||||
|
id="svg_1-7"
|
||||||
|
height="16"
|
||||||
|
width="16"
|
||||||
|
y="3.999995"
|
||||||
|
x="28.000006"
|
||||||
|
style="fill:#ec3f29;fill-opacity:0.94117647;stroke:none;stroke-width:0" />
|
||||||
|
<rect
|
||||||
|
style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0"
|
||||||
|
x="52.000004"
|
||||||
|
y="3.999995"
|
||||||
|
width="16"
|
||||||
|
height="16"
|
||||||
|
id="rect4165" />
|
||||||
|
<rect
|
||||||
|
id="rect4175"
|
||||||
|
height="16"
|
||||||
|
width="16"
|
||||||
|
y="3.9999852"
|
||||||
|
x="172.00002"
|
||||||
|
style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0" />
|
||||||
|
<rect
|
||||||
|
id="rect4175-3"
|
||||||
|
height="16"
|
||||||
|
width="16"
|
||||||
|
y="3.999995"
|
||||||
|
x="196"
|
||||||
|
style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0" />
|
||||||
|
<g
|
||||||
|
id="g4299"
|
||||||
|
style="stroke:none">
|
||||||
|
<rect
|
||||||
|
x="7.0000048"
|
||||||
|
y="10.999998"
|
||||||
|
width="9.9999924"
|
||||||
|
height="1.9999986"
|
||||||
|
id="svg_1-1"
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0" />
|
||||||
|
<rect
|
||||||
|
x="11.000005"
|
||||||
|
y="7.0000114"
|
||||||
|
width="1.9999955"
|
||||||
|
height="9.9999838"
|
||||||
|
id="svg_1-1-1"
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
id="g4299-3"
|
||||||
|
transform="matrix(0.70710678,-0.70710678,0.70710678,0.70710678,19.029435,12.000001)"
|
||||||
|
style="stroke:none">
|
||||||
|
<rect
|
||||||
|
x="7.0000048"
|
||||||
|
y="10.999998"
|
||||||
|
width="9.9999924"
|
||||||
|
height="1.9999986"
|
||||||
|
id="svg_1-1-0"
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0" />
|
||||||
|
<rect
|
||||||
|
x="11.000005"
|
||||||
|
y="7.0000114"
|
||||||
|
width="1.9999955"
|
||||||
|
height="9.9999838"
|
||||||
|
id="svg_1-1-1-9"
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0" />
|
||||||
|
</g>
|
||||||
|
<rect
|
||||||
|
id="svg_1-7-5"
|
||||||
|
height="6.9999905"
|
||||||
|
width="6.9999909"
|
||||||
|
y="7.0000048"
|
||||||
|
x="55.000004"
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0" />
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:#4c4c4c;stroke-width:2;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
x="58"
|
||||||
|
y="10.00001"
|
||||||
|
width="6.9999909"
|
||||||
|
height="6.9999905"
|
||||||
|
id="rect4354" />
|
||||||
|
<rect
|
||||||
|
id="svg_1-7-5-7"
|
||||||
|
height="6.9999905"
|
||||||
|
width="6.9999909"
|
||||||
|
y="10.000005"
|
||||||
|
x="58.000004"
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:#3c80df;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:0.94117647" />
|
||||||
|
<g
|
||||||
|
id="g4378">
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0"
|
||||||
|
x="198"
|
||||||
|
y="10.999999"
|
||||||
|
width="7.9999909"
|
||||||
|
height="1.9999965"
|
||||||
|
id="svg_1-7-5-3" />
|
||||||
|
<rect
|
||||||
|
id="rect4374"
|
||||||
|
height="1.9999946"
|
||||||
|
width="11.999995"
|
||||||
|
y="7.0000005"
|
||||||
|
x="198"
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0" />
|
||||||
|
<rect
|
||||||
|
id="rect4376"
|
||||||
|
height="1.9999995"
|
||||||
|
width="3.9999928"
|
||||||
|
y="14.999996"
|
||||||
|
x="198"
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0" />
|
||||||
|
</g>
|
||||||
|
<g
|
||||||
|
transform="matrix(1,0,0,-1,-23.999995,23.999995)"
|
||||||
|
id="g4383">
|
||||||
|
<rect
|
||||||
|
id="rect4385"
|
||||||
|
height="1.9999965"
|
||||||
|
width="7.9999909"
|
||||||
|
y="10.999999"
|
||||||
|
x="198"
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0" />
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0"
|
||||||
|
x="198"
|
||||||
|
y="7.0000005"
|
||||||
|
width="11.999995"
|
||||||
|
height="1.9999946"
|
||||||
|
id="rect4387" />
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:#000000;stroke-width:0"
|
||||||
|
x="198"
|
||||||
|
y="14.999996"
|
||||||
|
width="3.9999928"
|
||||||
|
height="1.9999995"
|
||||||
|
id="rect4389" />
|
||||||
|
</g>
|
||||||
|
<rect
|
||||||
|
style="fill:#4c4c4c;fill-opacity:1;stroke:none"
|
||||||
|
id="rect3754-4"
|
||||||
|
width="16"
|
||||||
|
height="16"
|
||||||
|
x="76"
|
||||||
|
y="3.9999199" />
|
||||||
|
<path
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 85.10447,6.0157384 -0.0156,1.4063 c 3.02669,-0.2402 0.33008,3.6507996 2.48438,4.5780996 -2.18694,1.0938 0.49191,4.9069 -2.45313,4.5781 l -0.0156,1.4219 c 5.70828,0.559 1.03264,-5.1005 4.70313,-5.2656 l 0,-1.4063 c -3.61303,-0.027 1.11893,-5.7069996 -4.70313,-5.3124996 z"
|
||||||
|
id="path4351"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cccccccc" />
|
||||||
|
<path
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:0.2;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 82.78125,5.9984384 0.0156,1.4063 c -3.02668,-0.2402 -0.33007,3.6506996 -2.48437,4.5780996 2.18694,1.0938 -0.49192,4.9069 2.45312,4.5781 l 0.0156,1.4219 c -5.70827,0.559 -1.03263,-5.1004 -4.70312,-5.2656 l 0,-1.4063 c 3.61303,-0.027 -1.11894,-5.7070996 4.70312,-5.3124996 z"
|
||||||
|
id="path4351-9"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cccccccc" />
|
||||||
|
<rect
|
||||||
|
style="fill:#4c4c4c;fill-opacity:1;stroke:none"
|
||||||
|
id="rect3754-25"
|
||||||
|
width="16"
|
||||||
|
height="16"
|
||||||
|
x="100"
|
||||||
|
y="3.9999199" />
|
||||||
|
<path
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:none"
|
||||||
|
d="m 103.719,5.6719384 0,12.7187996 3.03125,0 0,-1.5313 -1.34375,0 0,-9.6249996 1.375,0 0,-1.5625 z"
|
||||||
|
id="path2987"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:none"
|
||||||
|
d="m 112.2185,5.6721984 0,12.7187996 -3.03125,0 0,-1.5313 1.34375,0 0,-9.6249996 -1.375,0 0,-1.5625 z"
|
||||||
|
id="path2987-1"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<rect
|
||||||
|
style="fill:#4c4c4c;fill-opacity:1;stroke:none"
|
||||||
|
id="rect3754-73"
|
||||||
|
width="16"
|
||||||
|
height="16"
|
||||||
|
x="124"
|
||||||
|
y="3.9999199" />
|
||||||
|
<path
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:none"
|
||||||
|
d="m 126.2824,17.602938 1.78957,0 1.14143,-2.8641 5.65364,0 1.14856,2.8641 1.76565,0 -4.78687,-11.1610996 -1.91903,0 z"
|
||||||
|
id="path3780"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="ccccccccc" />
|
||||||
|
<path
|
||||||
|
style="fill:#4c4c4c;fill-opacity:1;stroke:none"
|
||||||
|
d="m 129.72704,13.478838 4.60852,0.01 -2.30426,-5.5497996 z"
|
||||||
|
id="path3782"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<rect
|
||||||
|
style="fill:#4c4c4c;fill-opacity:1;stroke:none"
|
||||||
|
id="rect3754-35"
|
||||||
|
width="16"
|
||||||
|
height="16"
|
||||||
|
x="148"
|
||||||
|
y="3.9999199" />
|
||||||
|
<path
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:none"
|
||||||
|
d="m 156.47655,5.8917384 0,2.1797 0.46093,2.3983996 1.82813,0 0.39844,-2.3983996 0,-2.1797 z"
|
||||||
|
id="path5008-2"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="ccccccc" />
|
||||||
|
<path
|
||||||
|
style="fill:#ffffff;fill-opacity:1;stroke:none"
|
||||||
|
d="m 152.51561,5.8906384 0,2.1797 0.46094,2.3983996 1.82812,0 0.39844,-2.3983996 0,-2.1797 z"
|
||||||
|
id="path5008-2-8"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="ccccccc" />
|
||||||
|
<rect
|
||||||
|
id="svg_1-7-2"
|
||||||
|
height="1.9999961"
|
||||||
|
width="11.999996"
|
||||||
|
y="64"
|
||||||
|
x="54"
|
||||||
|
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0" />
|
||||||
|
<rect
|
||||||
|
id="svg_1-7-2-2"
|
||||||
|
height="2.9999905"
|
||||||
|
width="2.9999907"
|
||||||
|
y="52"
|
||||||
|
x="80.000008"
|
||||||
|
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0" />
|
||||||
|
<rect
|
||||||
|
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
|
||||||
|
x="85.000008"
|
||||||
|
y="52"
|
||||||
|
width="2.9999907"
|
||||||
|
height="2.9999905"
|
||||||
|
id="rect4561" />
|
||||||
|
<rect
|
||||||
|
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
|
||||||
|
x="80.000008"
|
||||||
|
y="58"
|
||||||
|
width="2.9999907"
|
||||||
|
height="2.9999905"
|
||||||
|
id="rect4563" />
|
||||||
|
<rect
|
||||||
|
id="rect4565"
|
||||||
|
height="2.9999905"
|
||||||
|
width="2.9999907"
|
||||||
|
y="58"
|
||||||
|
x="85.000008"
|
||||||
|
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0" />
|
||||||
|
<rect
|
||||||
|
id="rect4567"
|
||||||
|
height="2.9999905"
|
||||||
|
width="2.9999907"
|
||||||
|
y="64"
|
||||||
|
x="80.000008"
|
||||||
|
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0" />
|
||||||
|
<rect
|
||||||
|
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
|
||||||
|
x="85.000008"
|
||||||
|
y="64"
|
||||||
|
width="2.9999907"
|
||||||
|
height="2.9999905"
|
||||||
|
id="rect4569" />
|
||||||
|
<circle
|
||||||
|
style="opacity:1;fill:none;fill-opacity:1;stroke:#4c4c4c;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
|
||||||
|
id="path4571"
|
||||||
|
cx="110.06081"
|
||||||
|
cy="57.939209"
|
||||||
|
r="4.7438836" />
|
||||||
|
<rect
|
||||||
|
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
|
||||||
|
x="116.64566"
|
||||||
|
y="-31.79752"
|
||||||
|
width="4.229713"
|
||||||
|
height="6.4053884"
|
||||||
|
id="rect4563-2"
|
||||||
|
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,0,0)" />
|
||||||
|
<path
|
||||||
|
style="fill:#4c4c4c;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="M 125,56 138.77027,56.095 132,64 Z"
|
||||||
|
id="path4613"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cccc" />
|
||||||
|
<path
|
||||||
|
sodipodi:nodetypes="cccc"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path4615"
|
||||||
|
d="M 149,64 162.77027,63.905 156,56 Z"
|
||||||
|
style="fill:#4c4c4c;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||||
|
<rect
|
||||||
|
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
|
||||||
|
x="54"
|
||||||
|
y="53"
|
||||||
|
width="11.999996"
|
||||||
|
height="1.9999961"
|
||||||
|
id="rect4638" />
|
||||||
|
<rect
|
||||||
|
id="svg_1-7-2-24"
|
||||||
|
height="1.9999957"
|
||||||
|
width="12.99999"
|
||||||
|
y="-56"
|
||||||
|
x="53"
|
||||||
|
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
|
||||||
|
transform="matrix(0,1,-1,0,0,0)" />
|
||||||
|
<rect
|
||||||
|
transform="matrix(0,1,-1,0,0,0)"
|
||||||
|
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0"
|
||||||
|
x="53"
|
||||||
|
y="-66"
|
||||||
|
width="12.99999"
|
||||||
|
height="1.9999957"
|
||||||
|
id="rect4657" />
|
||||||
|
<rect
|
||||||
|
id="rect4659"
|
||||||
|
height="0.99999291"
|
||||||
|
width="11.999999"
|
||||||
|
y="57"
|
||||||
|
x="54"
|
||||||
|
style="fill:#4c4c4c;fill-opacity:0.98431373;stroke:none;stroke-width:0" />
|
||||||
|
<rect
|
||||||
|
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
|
||||||
|
x="54"
|
||||||
|
y="88.000122"
|
||||||
|
width="11.999996"
|
||||||
|
height="1.9999961"
|
||||||
|
id="rect4661" />
|
||||||
|
<rect
|
||||||
|
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
|
||||||
|
x="80.000008"
|
||||||
|
y="76.000122"
|
||||||
|
width="2.9999907"
|
||||||
|
height="2.9999905"
|
||||||
|
id="rect4663" />
|
||||||
|
<rect
|
||||||
|
id="rect4665"
|
||||||
|
height="2.9999905"
|
||||||
|
width="2.9999907"
|
||||||
|
y="76.000122"
|
||||||
|
x="85.000008"
|
||||||
|
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1" />
|
||||||
|
<rect
|
||||||
|
id="rect4667"
|
||||||
|
height="2.9999905"
|
||||||
|
width="2.9999907"
|
||||||
|
y="82.000122"
|
||||||
|
x="80.000008"
|
||||||
|
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1" />
|
||||||
|
<rect
|
||||||
|
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
|
||||||
|
x="85.000008"
|
||||||
|
y="82.000122"
|
||||||
|
width="2.9999907"
|
||||||
|
height="2.9999905"
|
||||||
|
id="rect4669" />
|
||||||
|
<rect
|
||||||
|
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
|
||||||
|
x="80.000008"
|
||||||
|
y="88.000122"
|
||||||
|
width="2.9999907"
|
||||||
|
height="2.9999905"
|
||||||
|
id="rect4671" />
|
||||||
|
<rect
|
||||||
|
id="rect4673"
|
||||||
|
height="2.9999905"
|
||||||
|
width="2.9999907"
|
||||||
|
y="88.000122"
|
||||||
|
x="85.000008"
|
||||||
|
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1" />
|
||||||
|
<circle
|
||||||
|
r="4.7438836"
|
||||||
|
cy="81.939331"
|
||||||
|
cx="110.06081"
|
||||||
|
id="circle4675"
|
||||||
|
style="opacity:1;fill:none;fill-opacity:1;stroke:#d3d3d3;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||||
|
<rect
|
||||||
|
transform="matrix(0.70710678,0.70710678,-0.70710678,0.70710678,0,0)"
|
||||||
|
id="rect4677"
|
||||||
|
height="6.4053884"
|
||||||
|
width="4.229713"
|
||||||
|
y="-14.826816"
|
||||||
|
x="133.6163"
|
||||||
|
style="fill:#d3d3d3;fill-opacity:1;stroke:#d3d3d3;stroke-width:0;stroke-opacity:1" />
|
||||||
|
<path
|
||||||
|
sodipodi:nodetypes="cccc"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path4679"
|
||||||
|
d="m 125,80.000005 13.77027,0.09499 L 132,87.999992 Z"
|
||||||
|
style="fill:#d3d3d3;fill-opacity:1;fill-rule:evenodd;stroke:#d3d3d3;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||||
|
<path
|
||||||
|
style="fill:#d3d3d3;fill-opacity:1;fill-rule:evenodd;stroke:#d3d3d3;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="M 149,88.0002 162.77027,87.9052 156,80.0002 Z"
|
||||||
|
id="path4681"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cccc" />
|
||||||
|
<rect
|
||||||
|
id="rect4683"
|
||||||
|
height="1.9999961"
|
||||||
|
width="11.999996"
|
||||||
|
y="77.000122"
|
||||||
|
x="54"
|
||||||
|
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1" />
|
||||||
|
<rect
|
||||||
|
transform="matrix(0,1,-1,0,0,0)"
|
||||||
|
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
|
||||||
|
x="77.000122"
|
||||||
|
y="-56"
|
||||||
|
width="12.99999"
|
||||||
|
height="1.9999957"
|
||||||
|
id="rect4685" />
|
||||||
|
<rect
|
||||||
|
id="rect4687"
|
||||||
|
height="1.9999957"
|
||||||
|
width="12.99999"
|
||||||
|
y="-66"
|
||||||
|
x="77.000122"
|
||||||
|
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
|
||||||
|
transform="matrix(0,1,-1,0,0,0)" />
|
||||||
|
<rect
|
||||||
|
style="fill:#d3d3d3;fill-opacity:1;stroke:none;stroke-width:0;stroke-opacity:1"
|
||||||
|
x="54"
|
||||||
|
y="81.000122"
|
||||||
|
width="11.999999"
|
||||||
|
height="0.99999291"
|
||||||
|
id="rect4689" />
|
||||||
|
<rect
|
||||||
|
id="rect4761-1"
|
||||||
|
height="1.9999945"
|
||||||
|
width="15.99999"
|
||||||
|
y="101"
|
||||||
|
x="76.000008"
|
||||||
|
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
|
||||||
|
<rect
|
||||||
|
id="rect4761-0"
|
||||||
|
height="1.9999945"
|
||||||
|
width="15.99999"
|
||||||
|
y="105"
|
||||||
|
x="76.000008"
|
||||||
|
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
|
||||||
|
<rect
|
||||||
|
id="rect4761-7"
|
||||||
|
height="1.9999945"
|
||||||
|
width="9"
|
||||||
|
y="109"
|
||||||
|
x="76.000008"
|
||||||
|
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
|
||||||
|
<rect
|
||||||
|
id="rect4761-1-1"
|
||||||
|
height="1.9999945"
|
||||||
|
width="12"
|
||||||
|
y="125"
|
||||||
|
x="76.000008"
|
||||||
|
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
|
||||||
|
<rect
|
||||||
|
id="rect4761-1-1-4"
|
||||||
|
height="1.9999945"
|
||||||
|
width="10"
|
||||||
|
y="137"
|
||||||
|
x="76.000008"
|
||||||
|
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
|
||||||
|
<rect
|
||||||
|
id="rect4761-1-1-4-4"
|
||||||
|
height="1.9999945"
|
||||||
|
width="10"
|
||||||
|
y="129"
|
||||||
|
x="82"
|
||||||
|
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
|
||||||
|
<rect
|
||||||
|
id="rect4761-1-1-4-4-3"
|
||||||
|
height="1.9999945"
|
||||||
|
width="9"
|
||||||
|
y="133"
|
||||||
|
x="82"
|
||||||
|
style="fill:#ffffff;fill-opacity:0.8;stroke:none;stroke-width:0" />
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.8;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.66157866;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||||
|
d="m 36.398438,100.0254 c -0.423362,-0.013 -0.846847,0.01 -1.265626,0.062 -1.656562,0.2196 -3.244567,0.9739 -4.507812,2.2266 L 29,100.5991 l -2.324219,7.7129 7.826172,-1.9062 -1.804687,-1.9063 c 1.597702,-1.5308 4.048706,-1.8453 5.984375,-0.7207 1.971162,1.1452 2.881954,3.3975 2.308593,5.5508 -0.573361,2.1533 -2.533865,3.6953 -4.830078,3.6953 l 0,3.0742 c 3.550756,0 6.710442,-2.4113 7.650391,-5.9414 0.939949,-3.5301 -0.618463,-7.2736 -3.710938,-9.0703 -1.159678,-0.6738 -2.431087,-1.0231 -3.701171,-1.0625 z"
|
||||||
|
id="path4138" />
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.8;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.66157866;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||||
|
d="m 59.722656,99.9629 c -1.270084,0.039 -2.541493,0.3887 -3.701172,1.0625 -3.092475,1.7967 -4.650886,5.5402 -3.710937,9.0703 0.939949,3.5301 4.09768,5.9414 7.648437,5.9414 l 0,-3.0742 c -2.296214,0 -4.256717,-1.542 -4.830078,-3.6953 -0.573361,-2.1533 0.337432,-4.4056 2.308594,-5.5508 1.935731,-1.1246 4.38863,-0.8102 5.986326,0.7207 l -1.806638,1.9063 7.828128,1.9062 -2.32422,-7.7129 -1.62696,1.7168 c -1.26338,-1.2531 -2.848917,-2.0088 -4.505855,-2.2285 -0.418778,-0.055 -0.842263,-0.076 -1.265625,-0.062 z"
|
||||||
|
id="path4138-1" />
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.96599996;stroke-miterlimit:4;stroke-dasharray:none"
|
||||||
|
d="m 10.5,100 0,2 -2.4999996,0 L 12,107 l 4,-5 -2.5,0 0,-2 -3,0 z"
|
||||||
|
id="path3055-0-77" />
|
||||||
|
<path
|
||||||
|
style="opacity:0.8;fill:none;stroke:#ffffff;stroke-width:1.96599996;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 4.9850574,108.015 14.0298856,-0.03"
|
||||||
|
id="path5244-5-0-5"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
style="opacity:0.8;fill:none;stroke:#ffffff;stroke-width:1.96599996;stroke-linecap:square;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
d="m 4.9849874,132.015 14.0298866,-0.03"
|
||||||
|
id="path5244-5-0-5-8"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cc" />
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.4;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.66157866;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||||
|
d="m 36.398438,123.9629 c -0.423362,-0.013 -0.846847,0.01 -1.265626,0.062 -1.656562,0.2196 -3.244567,0.9739 -4.507812,2.2266 L 29,124.5366 l -2.324219,7.7129 7.826172,-1.9062 -1.804687,-1.9063 c 1.597702,-1.5308 4.048706,-1.8453 5.984375,-0.7207 1.971162,1.1453 2.881954,3.3975 2.308593,5.5508 -0.573361,2.1533 -2.533864,3.6953 -4.830078,3.6953 l 0,3.0742 c 3.550757,0 6.710442,-2.4093 7.650391,-5.9394 0.939949,-3.5301 -0.618463,-7.2756 -3.710938,-9.0723 -1.159678,-0.6737 -2.431087,-1.0231 -3.701171,-1.0625 z"
|
||||||
|
id="path4138-12" />
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="color:#000000;font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:medium;line-height:normal;font-family:sans-serif;text-indent:0;text-align:start;text-decoration:none;text-decoration-line:none;text-decoration-style:solid;text-decoration-color:#000000;letter-spacing:normal;word-spacing:normal;text-transform:none;direction:ltr;block-progression:tb;writing-mode:lr-tb;baseline-shift:baseline;text-anchor:start;white-space:normal;clip-rule:nonzero;display:inline;overflow:visible;visibility:visible;opacity:0.4;isolation:auto;mix-blend-mode:normal;color-interpolation:sRGB;color-interpolation-filters:linearRGB;solid-color:#000000;solid-opacity:1;fill:#4d4d4d;fill-opacity:1;fill-rule:nonzero;stroke:none;stroke-width:2.66157866;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-dashoffset:0;stroke-opacity:1;color-rendering:auto;image-rendering:auto;shape-rendering:auto;text-rendering:auto;enable-background:accumulate"
|
||||||
|
d="m 59.722656,123.9629 c -1.270084,0.039 -2.541493,0.3888 -3.701172,1.0625 -3.092475,1.7967 -4.650886,5.5422 -3.710937,9.0723 0.939949,3.5301 4.09768,5.9394 7.648437,5.9394 l 0,-3.0742 c -2.296214,0 -4.256717,-1.542 -4.830078,-3.6953 -0.573361,-2.1533 0.337432,-4.4055 2.308594,-5.5508 1.935731,-1.1246 4.38863,-0.8102 5.986326,0.7207 l -1.806638,1.9063 7.828128,1.9062 -2.32422,-7.7129 -1.62696,1.7168 c -1.26338,-1.2531 -2.848917,-2.0088 -4.505855,-2.2285 -0.418778,-0.055 -0.842263,-0.076 -1.265625,-0.062 z"
|
||||||
|
id="path4138-1-3" />
|
||||||
|
<path
|
||||||
|
id="path6191"
|
||||||
|
d="m 10.5,116 0,-2 -2.4999996,0 L 12,109 l 4,5 -2.5,0 0,2 -3,0 z"
|
||||||
|
style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.96599996;stroke-miterlimit:4;stroke-dasharray:none"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.96599996;stroke-miterlimit:4;stroke-dasharray:none"
|
||||||
|
d="m 10.5,129 0,-2 -2.4999996,0 L 12,122 l 4,5 -2.5,0 0,2 -3,0 z"
|
||||||
|
id="path6193" />
|
||||||
|
<path
|
||||||
|
id="path6195"
|
||||||
|
d="m 10.5,135 0,2 -2.4999996,0 L 12,142 l 4,-5 -2.5,0 0,-2 -3,0 z"
|
||||||
|
style="opacity:0.8;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.96599996;stroke-miterlimit:4;stroke-dasharray:none"
|
||||||
|
inkscape:connector-curvature="0" />
|
||||||
|
<path
|
||||||
|
sodipodi:type="star"
|
||||||
|
style="fill:#4d4d4d;fill-opacity:0.90196078;stroke:#d3d3d3;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
|
||||||
|
id="path4500"
|
||||||
|
sodipodi:sides="3"
|
||||||
|
sodipodi:cx="11.55581"
|
||||||
|
sodipodi:cy="60.073242"
|
||||||
|
sodipodi:r1="5.1116104"
|
||||||
|
sodipodi:r2="2.5558052"
|
||||||
|
sodipodi:arg1="0"
|
||||||
|
sodipodi:arg2="1.0471976"
|
||||||
|
inkscape:flatsided="false"
|
||||||
|
inkscape:rounded="0"
|
||||||
|
inkscape:randomized="0"
|
||||||
|
d="m 16.66742,60.073242 -3.833708,2.213392 -3.8337072,2.213393 0,-4.426785 0,-4.426784 3.8337082,2.213392 z"
|
||||||
|
inkscape:transform-center-x="-1.2779026" />
|
||||||
|
<path
|
||||||
|
inkscape:transform-center-x="1.277902"
|
||||||
|
d="m -31.500004,60.073242 -3.833708,2.213392 -3.833707,2.213393 0,-4.426785 0,-4.426784 3.833707,2.213392 z"
|
||||||
|
inkscape:randomized="0"
|
||||||
|
inkscape:rounded="0"
|
||||||
|
inkscape:flatsided="false"
|
||||||
|
sodipodi:arg2="1.0471976"
|
||||||
|
sodipodi:arg1="0"
|
||||||
|
sodipodi:r2="2.5558052"
|
||||||
|
sodipodi:r1="5.1116104"
|
||||||
|
sodipodi:cy="60.073242"
|
||||||
|
sodipodi:cx="-36.611614"
|
||||||
|
sodipodi:sides="3"
|
||||||
|
id="path4502"
|
||||||
|
style="fill:#4d4d4d;fill-opacity:0.90196078;stroke:#d3d3d3;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
|
||||||
|
sodipodi:type="star"
|
||||||
|
transform="scale(-1,1)" />
|
||||||
|
<path
|
||||||
|
d="m 16.66742,60.073212 -3.833708,2.213392 -3.8337072,2.213392 0,-4.426784 0,-4.426785 3.8337082,2.213392 z"
|
||||||
|
inkscape:randomized="0"
|
||||||
|
inkscape:rounded="0"
|
||||||
|
inkscape:flatsided="false"
|
||||||
|
sodipodi:arg2="1.0471976"
|
||||||
|
sodipodi:arg1="0"
|
||||||
|
sodipodi:r2="2.5558052"
|
||||||
|
sodipodi:r1="5.1116104"
|
||||||
|
sodipodi:cy="60.073212"
|
||||||
|
sodipodi:cx="11.55581"
|
||||||
|
sodipodi:sides="3"
|
||||||
|
id="path4504"
|
||||||
|
style="fill:#4d4d4d;fill-opacity:0.90196078;stroke:#d3d3d3;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
|
||||||
|
sodipodi:type="star"
|
||||||
|
transform="matrix(0,1,-1,0,72.0074,71.7877)"
|
||||||
|
inkscape:transform-center-y="1.2779029" />
|
||||||
|
<path
|
||||||
|
inkscape:transform-center-y="-1.2779026"
|
||||||
|
transform="matrix(0,-1,-1,0,96,96)"
|
||||||
|
sodipodi:type="star"
|
||||||
|
style="fill:#4d4d4d;fill-opacity:0.90196078;stroke:#d3d3d3;stroke-width:0;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none"
|
||||||
|
id="path4506"
|
||||||
|
sodipodi:sides="3"
|
||||||
|
sodipodi:cx="11.55581"
|
||||||
|
sodipodi:cy="60.073212"
|
||||||
|
sodipodi:r1="5.1116104"
|
||||||
|
sodipodi:r2="2.5558052"
|
||||||
|
sodipodi:arg1="0"
|
||||||
|
sodipodi:arg2="1.0471976"
|
||||||
|
inkscape:flatsided="false"
|
||||||
|
inkscape:rounded="0"
|
||||||
|
inkscape:randomized="0"
|
||||||
|
d="m 16.66742,60.073212 -3.833708,2.213392 -3.8337072,2.213392 0,-4.426784 0,-4.426785 3.8337082,2.213392 z" />
|
||||||
|
<path
|
||||||
|
sodipodi:nodetypes="cccc"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path4615-5"
|
||||||
|
d="m 171.82574,65.174193 16.34854,0 -8.17427,-13.348454 z"
|
||||||
|
style="fill:#fbb917;fill-opacity:1;fill-rule:evenodd;stroke:#fbb917;stroke-width:1.65161395;stroke-linecap:butt;stroke-linejoin:round;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1" />
|
||||||
|
<path
|
||||||
|
style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="m 179,55 0,6 2,0 0,-6"
|
||||||
|
id="path4300"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cccc" />
|
||||||
|
<path
|
||||||
|
style="opacity:1;fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="m 179,62 0,2 2,0 0,-2"
|
||||||
|
id="path4300-6"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cccc" />
|
||||||
|
<path
|
||||||
|
style="fill:#ffffff;fill-opacity:0.8;fill-rule:evenodd;stroke:#ffffff;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:round;stroke-opacity:0.8"
|
||||||
|
d="M 99.994369,113.0221 102,114.98353 l 7,-6.9558 3,0.97227 2,-1 1,-2 0,-3 -3,3 -3,-3 3,-3 -3,0 -2,1 -1,2 0.99437,3.0221 z"
|
||||||
|
id="path4268"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="ccccccccccccccc" />
|
||||||
|
<rect
|
||||||
|
id="rect4175-3-5"
|
||||||
|
height="16"
|
||||||
|
width="16"
|
||||||
|
y="4"
|
||||||
|
x="220"
|
||||||
|
style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0" />
|
||||||
|
<path
|
||||||
|
style="fill:#ffffff;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="m 234,6 0,2 -5,5 0,5 -2,0 0,-5 -5,-5 0,-2"
|
||||||
|
id="path3546"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cccccccc" />
|
||||||
|
<g
|
||||||
|
transform="matrix(1.3333328,0,0,-1.5999992,-139.9999,127.19999)"
|
||||||
|
id="g4383-6">
|
||||||
|
<rect
|
||||||
|
id="rect4385-2"
|
||||||
|
height="1.2499905"
|
||||||
|
width="5.9999924"
|
||||||
|
y="12.625005"
|
||||||
|
x="198.00002"
|
||||||
|
style="fill:#ffffff;fill-opacity:0.8;stroke:#000000;stroke-width:0" />
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffff;fill-opacity:0.8;stroke:#000000;stroke-width:0"
|
||||||
|
x="198.00002"
|
||||||
|
y="15.125007"
|
||||||
|
width="7.4999928"
|
||||||
|
height="1.2499949"
|
||||||
|
id="rect4387-9" />
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffff;fill-opacity:0.8;stroke:#000000;stroke-width:0"
|
||||||
|
x="198.00002"
|
||||||
|
y="7.6250024"
|
||||||
|
width="2.9999909"
|
||||||
|
height="1.2499905"
|
||||||
|
id="rect4389-1-0" />
|
||||||
|
<rect
|
||||||
|
style="fill:#ffffff;fill-opacity:0.8;stroke:#000000;stroke-width:0"
|
||||||
|
x="198.00002"
|
||||||
|
y="10.125004"
|
||||||
|
width="4.4999919"
|
||||||
|
height="1.2499905"
|
||||||
|
id="rect4389-1-9" />
|
||||||
|
<path
|
||||||
|
style="fill:#ffffff;fill-opacity:0.8;fill-rule:evenodd;stroke:none;stroke-width:0.68465352px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="m 207.00001,16.375004 0,-5.625005 -2.25,0 3,-3.1250014 3,3.1250014 -2.25,0 0,5.625005 -1.5,0"
|
||||||
|
id="path4402"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cccccccc" />
|
||||||
|
</g>
|
||||||
|
<path
|
||||||
|
style="fill:#ffffff;fill-opacity:0.8;fill-rule:evenodd;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||||
|
d="m 164,100 0,3 -6,6 0,7 -4,0 0,-7 -6,-6 0,-3"
|
||||||
|
id="path3546-2-2"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
sodipodi:nodetypes="cccccccc" />
|
||||||
|
<rect
|
||||||
|
style="fill:#4c4c4c;fill-opacity:1;stroke:none;stroke-width:0"
|
||||||
|
id="svg_1-3"
|
||||||
|
height="16"
|
||||||
|
width="16"
|
||||||
|
y="28"
|
||||||
|
x="4" />
|
||||||
|
<path
|
||||||
|
sodipodi:nodetypes="ccccccccc"
|
||||||
|
inkscape:connector-curvature="0"
|
||||||
|
id="path4402-5-7"
|
||||||
|
d="m 15,41 0,-7 -4,0 0,3 -5,-4 5,-4 0,3 6,0 0,9"
|
||||||
|
style="fill:#ffffff;fill-opacity:1;fill-rule:evenodd;stroke:none;stroke-width:0.68465352px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||||
|
</svg>
|
After Width: | Height: | Size: 31 KiB |
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -4,8 +4,10 @@ import android.Manifest;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
|
import android.provider.Settings;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
@ -32,6 +34,7 @@ import com.zane.smapiinstaller.logic.GameLauncher;
|
||||||
import com.zane.smapiinstaller.logic.ModAssetsManager;
|
import com.zane.smapiinstaller.logic.ModAssetsManager;
|
||||||
import com.zane.smapiinstaller.utils.ConfigUtils;
|
import com.zane.smapiinstaller.utils.ConfigUtils;
|
||||||
import com.zane.smapiinstaller.utils.DialogUtils;
|
import com.zane.smapiinstaller.utils.DialogUtils;
|
||||||
|
import com.zane.smapiinstaller.utils.FileUtils;
|
||||||
import com.zane.smapiinstaller.utils.JsonCallback;
|
import com.zane.smapiinstaller.utils.JsonCallback;
|
||||||
import com.zane.smapiinstaller.utils.JsonUtil;
|
import com.zane.smapiinstaller.utils.JsonUtil;
|
||||||
import com.zane.smapiinstaller.utils.TranslateUtil;
|
import com.zane.smapiinstaller.utils.TranslateUtil;
|
||||||
|
@ -64,6 +67,27 @@ public class MainActivity extends AppCompatActivity {
|
||||||
private ActivityMainBinding binding;
|
private ActivityMainBinding binding;
|
||||||
|
|
||||||
private void requestPermissions() {
|
private void requestPermissions() {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||||
|
// 先判断有没有权限
|
||||||
|
if (!Environment.isExternalStorageManager()) {
|
||||||
|
DialogUtils.showConfirmDialog(MainActivity.instance, R.string.confirm, R.string.request_all_files_access_permission, ((dialog, dialogAction) -> {
|
||||||
|
if (dialogAction == DialogAction.POSITIVE) {
|
||||||
|
ActivityResultHandler.registerListener(ActivityResultHandler.REQUEST_CODE_ALL_FILES_ACCESS_PERMISSION, (resultCode, data) -> {
|
||||||
|
if (!Environment.isExternalStorageManager()) {
|
||||||
|
this.finish();
|
||||||
|
} else {
|
||||||
|
requestPermissions();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
startActivityForResult(new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION), ActivityResultHandler.REQUEST_CODE_ALL_FILES_ACCESS_PERMISSION);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.finish();
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE)
|
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE)
|
||||||
!= PackageManager.PERMISSION_GRANTED) {
|
!= PackageManager.PERMISSION_GRANTED) {
|
||||||
ActivityCompat.requestPermissions(MainActivity.this,
|
ActivityCompat.requestPermissions(MainActivity.this,
|
||||||
|
@ -235,7 +259,7 @@ public class MainActivity extends AppCompatActivity {
|
||||||
DialogUtils.showInputDialog(binding.appBarMain.toolbar, R.string.input, R.string.input_mods_path, Constants.MOD_PATH, Constants.MOD_PATH, (dialog, input) -> {
|
DialogUtils.showInputDialog(binding.appBarMain.toolbar, R.string.input, R.string.input_mods_path, Constants.MOD_PATH, Constants.MOD_PATH, (dialog, input) -> {
|
||||||
if (StringUtils.isNoneBlank(input)) {
|
if (StringUtils.isNoneBlank(input)) {
|
||||||
String pathString = input.toString();
|
String pathString = input.toString();
|
||||||
File file = new File(Environment.getExternalStorageDirectory(), pathString);
|
File file = new File(FileUtils.getStadewValleyBasePath(), pathString);
|
||||||
if (file.exists() && file.isDirectory()) {
|
if (file.exists() && file.isDirectory()) {
|
||||||
Constants.MOD_PATH = pathString;
|
Constants.MOD_PATH = pathString;
|
||||||
config.setModsPath(pathString);
|
config.setModsPath(pathString);
|
||||||
|
|
|
@ -25,6 +25,11 @@ import lombok.RequiredArgsConstructor;
|
||||||
@RequiredArgsConstructor
|
@RequiredArgsConstructor
|
||||||
public class ModUpdateCheckRequestDto {
|
public class ModUpdateCheckRequestDto {
|
||||||
|
|
||||||
|
public ModUpdateCheckRequestDto(List<ModInfo> mods, SemanticVersion gameVersion) {
|
||||||
|
this.mods = mods;
|
||||||
|
this.gameVersion = gameVersion;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 待检查MOD列表
|
* 待检查MOD列表
|
||||||
*/
|
*/
|
||||||
|
@ -37,7 +42,6 @@ public class ModUpdateCheckRequestDto {
|
||||||
/**
|
/**
|
||||||
* 游戏版本
|
* 游戏版本
|
||||||
*/
|
*/
|
||||||
@NonNull
|
|
||||||
private SemanticVersion gameVersion;
|
private SemanticVersion gameVersion;
|
||||||
/**
|
/**
|
||||||
* 平台版本
|
* 平台版本
|
||||||
|
|
|
@ -2,7 +2,9 @@ package com.zane.smapiinstaller.dto;
|
||||||
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
import lombok.Data;
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Zane
|
* @author Zane
|
||||||
|
@ -12,9 +14,22 @@ public class ModUpdateCheckResponseDto {
|
||||||
private String id;
|
private String id;
|
||||||
private UpdateInfo suggestedUpdate;
|
private UpdateInfo suggestedUpdate;
|
||||||
private List<String> errors;
|
private List<String> errors;
|
||||||
|
private Metadata metadata;
|
||||||
@Data
|
@Data
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
public static class UpdateInfo {
|
public static class UpdateInfo {
|
||||||
private String version;
|
private String version;
|
||||||
private String url;
|
private String url;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public static class Metadata {
|
||||||
|
private Main main;
|
||||||
|
@Data
|
||||||
|
public static class Main {
|
||||||
|
private String version;
|
||||||
|
private String url;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
package com.zane.smapiinstaller.dto;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class Tuple2<U, V> {
|
||||||
|
private U first;
|
||||||
|
private V second;
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
package com.zane.smapiinstaller.dto;
|
||||||
|
|
||||||
|
import android.webkit.JavascriptInterface;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Getter;
|
||||||
|
import lombok.Setter;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Zane
|
||||||
|
*/
|
||||||
|
@AllArgsConstructor
|
||||||
|
public class WebViewObject {
|
||||||
|
private String text;
|
||||||
|
private String mode;
|
||||||
|
private String language;
|
||||||
|
private boolean editable;
|
||||||
|
private int height;
|
||||||
|
|
||||||
|
@JavascriptInterface
|
||||||
|
public String getText() {
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JavascriptInterface
|
||||||
|
public void setText(String text) {
|
||||||
|
this.text = text;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JavascriptInterface
|
||||||
|
public boolean isEditable() {
|
||||||
|
return editable;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JavascriptInterface
|
||||||
|
public int getHeight() {
|
||||||
|
return height;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JavascriptInterface
|
||||||
|
public String getMode() {
|
||||||
|
return mode;
|
||||||
|
}
|
||||||
|
|
||||||
|
@JavascriptInterface
|
||||||
|
public String getLanguage() {
|
||||||
|
return language;
|
||||||
|
}
|
||||||
|
}
|
|
@ -10,7 +10,8 @@ import java.util.function.BiConsumer;
|
||||||
* @author Zane
|
* @author Zane
|
||||||
*/
|
*/
|
||||||
public class ActivityResultHandler {
|
public class ActivityResultHandler {
|
||||||
public static int REQUEST_CODE_APP_INSTALL = 1001;
|
public static final int REQUEST_CODE_APP_INSTALL = 1001;
|
||||||
|
public static final int REQUEST_CODE_ALL_FILES_ACCESS_PERMISSION = 1002;
|
||||||
|
|
||||||
public static ConcurrentHashMap<Integer, BiConsumer<Integer, Intent>> listenerMap = new ConcurrentHashMap<>();
|
public static ConcurrentHashMap<Integer, BiConsumer<Integer, Intent>> listenerMap = new ConcurrentHashMap<>();
|
||||||
|
|
||||||
|
|
|
@ -7,17 +7,14 @@ import android.content.pm.PackageInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Environment;
|
|
||||||
import android.provider.Settings;
|
import android.provider.Settings;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import com.android.apksig.ApkSigner;
|
import com.android.apksig.ApkSigner;
|
||||||
import com.android.apksig.ApkVerifier;
|
import com.android.apksig.ApkVerifier;
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
import com.google.common.base.Predicate;
|
|
||||||
import com.google.common.base.Stopwatch;
|
import com.google.common.base.Stopwatch;
|
||||||
import com.google.common.collect.Iterables;
|
import com.google.common.collect.Iterables;
|
||||||
import com.google.common.hash.Hashing;
|
|
||||||
import com.google.common.io.Files;
|
import com.google.common.io.Files;
|
||||||
import com.zane.smapiinstaller.BuildConfig;
|
import com.zane.smapiinstaller.BuildConfig;
|
||||||
import com.zane.smapiinstaller.MainActivity;
|
import com.zane.smapiinstaller.MainActivity;
|
||||||
|
@ -25,6 +22,7 @@ import com.zane.smapiinstaller.R;
|
||||||
import com.zane.smapiinstaller.constant.Constants;
|
import com.zane.smapiinstaller.constant.Constants;
|
||||||
import com.zane.smapiinstaller.constant.DialogAction;
|
import com.zane.smapiinstaller.constant.DialogAction;
|
||||||
import com.zane.smapiinstaller.constant.ManifestPatchConstants;
|
import com.zane.smapiinstaller.constant.ManifestPatchConstants;
|
||||||
|
import com.zane.smapiinstaller.dto.Tuple2;
|
||||||
import com.zane.smapiinstaller.entity.ApkFilesManifest;
|
import com.zane.smapiinstaller.entity.ApkFilesManifest;
|
||||||
import com.zane.smapiinstaller.entity.ManifestEntry;
|
import com.zane.smapiinstaller.entity.ManifestEntry;
|
||||||
import com.zane.smapiinstaller.utils.DialogUtils;
|
import com.zane.smapiinstaller.utils.DialogUtils;
|
||||||
|
@ -47,18 +45,17 @@ import java.security.cert.X509Certificate;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicInteger;
|
import java.util.concurrent.atomic.AtomicInteger;
|
||||||
import java.util.concurrent.atomic.AtomicLong;
|
import java.util.concurrent.atomic.AtomicLong;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
import java.util.zip.Deflater;
|
import java.util.zip.Deflater;
|
||||||
|
|
||||||
import androidx.core.content.FileProvider;
|
import androidx.core.content.FileProvider;
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.function.Consumer;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import pxb.android.axml.NodeVisitor;
|
import pxb.android.axml.NodeVisitor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -108,46 +105,44 @@ public class ApkPatcher {
|
||||||
String sourceDir = packageInfo.applicationInfo.publicSourceDir;
|
String sourceDir = packageInfo.applicationInfo.publicSourceDir;
|
||||||
File apkFile = new File(sourceDir);
|
File apkFile = new File(sourceDir);
|
||||||
|
|
||||||
File externalFilesDir = Environment.getExternalStorageDirectory();
|
String stadewValleyBasePath = FileUtils.getStadewValleyBasePath();
|
||||||
if (externalFilesDir != null) {
|
File dest = new File(stadewValleyBasePath + "/SMAPI Installer/");
|
||||||
File dest = new File(externalFilesDir.getAbsolutePath() + "/SMAPI Installer/");
|
if (!dest.exists()) {
|
||||||
if (!dest.exists()) {
|
if (!dest.mkdir()) {
|
||||||
if (!dest.mkdir()) {
|
errorMessage.set(String.format(context.getString(R.string.error_failed_to_create_file), dest.getAbsolutePath()));
|
||||||
errorMessage.set(String.format(context.getString(R.string.error_failed_to_create_file), dest.getAbsolutePath()));
|
return null;
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
File distFile = new File(dest, apkFile.getName());
|
|
||||||
if (advancedStage == 0) {
|
|
||||||
AtomicInteger count = new AtomicInteger();
|
|
||||||
ZipUtil.unpack(apkFile, new File(externalFilesDir.getAbsolutePath() + "/StardewValley/"), name -> {
|
|
||||||
if (name.startsWith("assets/")) {
|
|
||||||
int progress = count.incrementAndGet();
|
|
||||||
if (progress % 30 == 0) {
|
|
||||||
emitProgress(progress / 30);
|
|
||||||
}
|
|
||||||
return name.replaceFirst("assets/", "");
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
});
|
|
||||||
return distFile.getAbsolutePath();
|
|
||||||
} else if (advancedStage == 1) {
|
|
||||||
File contentFolder = new File(externalFilesDir.getAbsolutePath() + "/StardewValley/Content");
|
|
||||||
if (contentFolder.exists()) {
|
|
||||||
if (!contentFolder.isDirectory()) {
|
|
||||||
errorMessage.set(context.getString(R.string.error_directory_exists_with_same_filename, contentFolder.getAbsolutePath()));
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
extract(0);
|
|
||||||
}
|
|
||||||
ZipUtils.removeEntries(sourceDir, "assets/Content", distFile.getAbsolutePath(), (progress) -> emitProgress((int) (progress * 0.05)));
|
|
||||||
} else {
|
|
||||||
Files.copy(apkFile, distFile);
|
|
||||||
}
|
|
||||||
emitProgress(5);
|
|
||||||
return distFile.getAbsolutePath();
|
|
||||||
}
|
}
|
||||||
|
File distFile = new File(dest, apkFile.getName());
|
||||||
|
if (advancedStage == 0) {
|
||||||
|
AtomicInteger count = new AtomicInteger();
|
||||||
|
ZipUtil.unpack(apkFile, new File(stadewValleyBasePath + "/StardewValley/"), name -> {
|
||||||
|
if (name.startsWith("assets/")) {
|
||||||
|
int progress = count.incrementAndGet();
|
||||||
|
if (progress % 30 == 0) {
|
||||||
|
emitProgress(progress / 30);
|
||||||
|
}
|
||||||
|
return name.replaceFirst("assets/", "");
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
|
return distFile.getAbsolutePath();
|
||||||
|
} else if (advancedStage == 1) {
|
||||||
|
File contentFolder = new File(stadewValleyBasePath + "/StardewValley/Content");
|
||||||
|
if (contentFolder.exists()) {
|
||||||
|
if (!contentFolder.isDirectory()) {
|
||||||
|
errorMessage.set(context.getString(R.string.error_directory_exists_with_same_filename, contentFolder.getAbsolutePath()));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
extract(0);
|
||||||
|
}
|
||||||
|
ZipUtils.removeEntries(sourceDir, "assets/Content", distFile.getAbsolutePath(), (progress) -> emitProgress((int) (progress * 0.05)));
|
||||||
|
} else {
|
||||||
|
Files.copy(apkFile, distFile);
|
||||||
|
}
|
||||||
|
emitProgress(5);
|
||||||
|
return distFile.getAbsolutePath();
|
||||||
} catch (PackageManager.NameNotFoundException ignored) {
|
} catch (PackageManager.NameNotFoundException ignored) {
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
Log.e(TAG, "Extract error", e);
|
Log.e(TAG, "Extract error", e);
|
||||||
|
@ -250,9 +245,9 @@ public class ApkPatcher {
|
||||||
AtomicReference<String> packageName = new AtomicReference<>();
|
AtomicReference<String> packageName = new AtomicReference<>();
|
||||||
AtomicReference<String> versionName = new AtomicReference<>();
|
AtomicReference<String> versionName = new AtomicReference<>();
|
||||||
AtomicLong versionCode = new AtomicLong();
|
AtomicLong versionCode = new AtomicLong();
|
||||||
Predicate<ManifestTagVisitor.AttrArgs> processLogic = (attr) -> {
|
Function<ManifestTagVisitor.AttrArgs, List<ManifestTagVisitor.AttrArgs>> attrProcessLogic = (attr) -> {
|
||||||
if (attr == null) {
|
if (attr == null) {
|
||||||
return true;
|
return null;
|
||||||
}
|
}
|
||||||
if (attr.type == NodeVisitor.TYPE_STRING) {
|
if (attr.type == NodeVisitor.TYPE_STRING) {
|
||||||
String strObj = (String) attr.obj;
|
String strObj = (String) attr.obj;
|
||||||
|
@ -276,6 +271,7 @@ public class ApkPatcher {
|
||||||
attr.obj = Constants.PATCHED_APP_NAME;
|
attr.obj = Constants.PATCHED_APP_NAME;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// return Collections.singletonList(new ManifestTagVisitor.AttrArgs(attr.ns, "requestLegacyExternalStorage", -1, NodeVisitor.TYPE_INT_BOOLEAN, true));
|
||||||
break;
|
break;
|
||||||
case "authorities":
|
case "authorities":
|
||||||
if (strObj.contains(packageName.get())) {
|
if (strObj.contains(packageName.get())) {
|
||||||
|
@ -296,10 +292,22 @@ public class ApkPatcher {
|
||||||
versionCode.set((int) attr.obj);
|
versionCode.set((int) attr.obj);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return null;
|
||||||
};
|
};
|
||||||
|
AtomicReference<Boolean> permissionAppended = new AtomicReference<>(true);
|
||||||
|
Function<ManifestTagVisitor.ChildArgs, List<ManifestTagVisitor.ChildArgs>> childProcessLogic = (child -> {
|
||||||
|
if (!permissionAppended.get() && StringUtils.equals(child.name, "uses-permission")) {
|
||||||
|
permissionAppended.set(true);
|
||||||
|
return Collections.singletonList(new ManifestTagVisitor.ChildArgs(
|
||||||
|
child.ns, child.name, Collections.singletonList(
|
||||||
|
new ManifestTagVisitor.AttrArgs(
|
||||||
|
"http://schemas.android.com/apk/res/android", "name", -1,
|
||||||
|
NodeVisitor.TYPE_STRING, "android.permission.MANAGE_EXTERNAL_STORAGE"))));
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
});
|
||||||
try {
|
try {
|
||||||
byte[] modifyManifest = CommonLogic.modifyManifest(bytes, processLogic);
|
byte[] modifyManifest = CommonLogic.modifyManifest(bytes, attrProcessLogic, childProcessLogic);
|
||||||
if (StringUtils.endsWith(versionName.get(), ManifestPatchConstants.PATTERN_VERSION_AMAZON)) {
|
if (StringUtils.endsWith(versionName.get(), ManifestPatchConstants.PATTERN_VERSION_AMAZON)) {
|
||||||
packageName.set(ManifestPatchConstants.APP_PACKAGE_NAME + ManifestPatchConstants.PATTERN_VERSION_AMAZON);
|
packageName.set(ManifestPatchConstants.APP_PACKAGE_NAME + ManifestPatchConstants.PATTERN_VERSION_AMAZON);
|
||||||
}
|
}
|
||||||
|
@ -335,56 +343,54 @@ public class ApkPatcher {
|
||||||
*/
|
*/
|
||||||
public String sign(String apkPath) {
|
public String sign(String apkPath) {
|
||||||
try {
|
try {
|
||||||
File externalFilesDir = Environment.getExternalStorageDirectory();
|
String stadewValleyBasePath = FileUtils.getStadewValleyBasePath();
|
||||||
emitProgress(47);
|
emitProgress(47);
|
||||||
if (externalFilesDir != null) {
|
String signApkPath = stadewValleyBasePath + "/SMAPI Installer/base_signed.apk";
|
||||||
String signApkPath = externalFilesDir.getAbsolutePath() + "/SMAPI Installer/base_signed.apk";
|
KeyStore ks = new KeyStoreFileManager.JksKeyStore();
|
||||||
KeyStore ks = new KeyStoreFileManager.JksKeyStore();
|
try (InputStream fis = context.getAssets().open("debug.keystore.dat")) {
|
||||||
try (InputStream fis = context.getAssets().open("debug.keystore.dat")) {
|
ks.load(fis, PASSWORD.toCharArray());
|
||||||
ks.load(fis, PASSWORD.toCharArray());
|
|
||||||
}
|
|
||||||
String alias = ks.aliases().nextElement();
|
|
||||||
X509Certificate publicKey = (X509Certificate) ks.getCertificate(alias);
|
|
||||||
PrivateKey privateKey = (PrivateKey) ks.getKey(alias, "android".toCharArray());
|
|
||||||
ApkSigner.SignerConfig signerConfig = new ApkSigner.SignerConfig.Builder("debug", privateKey, Collections.singletonList(publicKey)).build();
|
|
||||||
emitProgress(49);
|
|
||||||
File outputFile = new File(signApkPath);
|
|
||||||
ApkSigner signer = new ApkSigner.Builder(Collections.singletonList(signerConfig))
|
|
||||||
.setInputApk(new File(apkPath))
|
|
||||||
.setOutputApk(outputFile)
|
|
||||||
.setV1SigningEnabled(true)
|
|
||||||
.setV2SigningEnabled(true).build();
|
|
||||||
long zipOpElapsed = stopwatch.elapsed(TimeUnit.MILLISECONDS);
|
|
||||||
stopwatch.reset();
|
|
||||||
Thread thread = new Thread(() -> {
|
|
||||||
stopwatch.start();
|
|
||||||
while (true) {
|
|
||||||
try {
|
|
||||||
Thread.sleep(20);
|
|
||||||
long elapsed = stopwatch.elapsed(TimeUnit.MILLISECONDS);
|
|
||||||
double progress = elapsed * 0.98 / zipOpElapsed;
|
|
||||||
if (progress < 1.0) {
|
|
||||||
emitProgress((int) (49 + 45 * progress));
|
|
||||||
}
|
|
||||||
} catch (InterruptedException ignored) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
thread.start();
|
|
||||||
signer.sign();
|
|
||||||
FileUtils.forceDelete(new File(apkPath));
|
|
||||||
ApkVerifier.Result result = new ApkVerifier.Builder(outputFile).build().verify();
|
|
||||||
if (thread.isAlive() && !thread.isInterrupted()) {
|
|
||||||
thread.interrupt();
|
|
||||||
}
|
|
||||||
if (result.containsErrors()) {
|
|
||||||
errorMessage.set(result.getErrors().stream().map(ApkVerifier.IssueWithParams::toString).collect(Collectors.joining(",")));
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
emitProgress(95);
|
|
||||||
return signApkPath;
|
|
||||||
}
|
}
|
||||||
|
String alias = ks.aliases().nextElement();
|
||||||
|
X509Certificate publicKey = (X509Certificate) ks.getCertificate(alias);
|
||||||
|
PrivateKey privateKey = (PrivateKey) ks.getKey(alias, "android".toCharArray());
|
||||||
|
ApkSigner.SignerConfig signerConfig = new ApkSigner.SignerConfig.Builder("debug", privateKey, Collections.singletonList(publicKey)).build();
|
||||||
|
emitProgress(49);
|
||||||
|
File outputFile = new File(signApkPath);
|
||||||
|
ApkSigner signer = new ApkSigner.Builder(Collections.singletonList(signerConfig))
|
||||||
|
.setInputApk(new File(apkPath))
|
||||||
|
.setOutputApk(outputFile)
|
||||||
|
.setV1SigningEnabled(true)
|
||||||
|
.setV2SigningEnabled(true).build();
|
||||||
|
long zipOpElapsed = stopwatch.elapsed(TimeUnit.MILLISECONDS);
|
||||||
|
stopwatch.reset();
|
||||||
|
Thread thread = new Thread(() -> {
|
||||||
|
stopwatch.start();
|
||||||
|
while (true) {
|
||||||
|
try {
|
||||||
|
Thread.sleep(20);
|
||||||
|
long elapsed = stopwatch.elapsed(TimeUnit.MILLISECONDS);
|
||||||
|
double progress = elapsed * 0.98 / zipOpElapsed;
|
||||||
|
if (progress < 1.0) {
|
||||||
|
emitProgress((int) (49 + 45 * progress));
|
||||||
|
}
|
||||||
|
} catch (InterruptedException ignored) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
thread.start();
|
||||||
|
signer.sign();
|
||||||
|
FileUtils.forceDelete(new File(apkPath));
|
||||||
|
ApkVerifier.Result result = new ApkVerifier.Builder(outputFile).build().verify();
|
||||||
|
if (thread.isAlive() && !thread.isInterrupted()) {
|
||||||
|
thread.interrupt();
|
||||||
|
}
|
||||||
|
if (result.containsErrors()) {
|
||||||
|
errorMessage.set(result.getErrors().stream().map(ApkVerifier.IssueWithParams::toString).collect(Collectors.joining(",")));
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
emitProgress(95);
|
||||||
|
return signApkPath;
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
Log.e(TAG, "Sign error", e);
|
Log.e(TAG, "Sign error", e);
|
||||||
errorMessage.set(e.getLocalizedMessage());
|
errorMessage.set(e.getLocalizedMessage());
|
||||||
|
|
|
@ -11,7 +11,6 @@ import android.content.pm.PackageInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Environment;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.animation.Animation;
|
import android.view.animation.Animation;
|
||||||
|
@ -20,7 +19,6 @@ import android.widget.ImageView;
|
||||||
|
|
||||||
import com.afollestad.materialdialogs.MaterialDialog;
|
import com.afollestad.materialdialogs.MaterialDialog;
|
||||||
import com.fasterxml.jackson.core.type.TypeReference;
|
import com.fasterxml.jackson.core.type.TypeReference;
|
||||||
import com.google.common.base.Predicate;
|
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
import com.google.common.io.ByteStreams;
|
import com.google.common.io.ByteStreams;
|
||||||
import com.lmntrx.android.library.livin.missme.ProgressDialog;
|
import com.lmntrx.android.library.livin.missme.ProgressDialog;
|
||||||
|
@ -28,6 +26,7 @@ import com.microsoft.appcenter.crashes.Crashes;
|
||||||
import com.zane.smapiinstaller.MainApplication;
|
import com.zane.smapiinstaller.MainApplication;
|
||||||
import com.zane.smapiinstaller.R;
|
import com.zane.smapiinstaller.R;
|
||||||
import com.zane.smapiinstaller.constant.DialogAction;
|
import com.zane.smapiinstaller.constant.DialogAction;
|
||||||
|
import com.zane.smapiinstaller.dto.Tuple2;
|
||||||
import com.zane.smapiinstaller.entity.ApkFilesManifest;
|
import com.zane.smapiinstaller.entity.ApkFilesManifest;
|
||||||
import com.zane.smapiinstaller.entity.ManifestEntry;
|
import com.zane.smapiinstaller.entity.ManifestEntry;
|
||||||
import com.zane.smapiinstaller.utils.DialogUtils;
|
import com.zane.smapiinstaller.utils.DialogUtils;
|
||||||
|
@ -43,9 +42,10 @@ import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
import pxb.android.axml.AxmlReader;
|
import pxb.android.axml.AxmlReader;
|
||||||
import pxb.android.axml.AxmlVisitor;
|
import pxb.android.axml.AxmlVisitor;
|
||||||
import pxb.android.axml.AxmlWriter;
|
import pxb.android.axml.AxmlWriter;
|
||||||
|
@ -196,7 +196,7 @@ public class CommonLogic {
|
||||||
if (manifestEntries == null) {
|
if (manifestEntries == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
File basePath = new File(Environment.getExternalStorageDirectory() + "/StardewValley/");
|
File basePath = new File(FileUtils.getStadewValleyBasePath() + "/StardewValley/");
|
||||||
if (!basePath.exists()) {
|
if (!basePath.exists()) {
|
||||||
if (!basePath.mkdir()) {
|
if (!basePath.mkdir()) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -244,18 +244,18 @@ public class CommonLogic {
|
||||||
* 修改AndroidManifest.xml文件
|
* 修改AndroidManifest.xml文件
|
||||||
*
|
*
|
||||||
* @param bytes AndroidManifest.xml文件字符数组
|
* @param bytes AndroidManifest.xml文件字符数组
|
||||||
* @param processLogic 处理逻辑
|
* @param attrProcessLogic 处理逻辑
|
||||||
* @return 修改后的AndroidManifest.xml文件字符数组
|
* @return 修改后的AndroidManifest.xml文件字符数组
|
||||||
* @throws IOException 异常
|
* @throws IOException 异常
|
||||||
*/
|
*/
|
||||||
public static byte[] modifyManifest(byte[] bytes, Predicate<ManifestTagVisitor.AttrArgs> processLogic) throws IOException {
|
public static byte[] modifyManifest(byte[] bytes, Function<ManifestTagVisitor.AttrArgs, List<ManifestTagVisitor.AttrArgs>> attrProcessLogic, Function<ManifestTagVisitor.ChildArgs, List<ManifestTagVisitor.ChildArgs>> childProcessLogic) throws IOException {
|
||||||
AxmlReader reader = new AxmlReader(bytes);
|
AxmlReader reader = new AxmlReader(bytes);
|
||||||
AxmlWriter writer = new AxmlWriter();
|
AxmlWriter writer = new AxmlWriter();
|
||||||
reader.accept(new AxmlVisitor(writer) {
|
reader.accept(new AxmlVisitor(writer) {
|
||||||
@Override
|
@Override
|
||||||
public NodeVisitor child(String ns, String name) {
|
public NodeVisitor child(String ns, String name) {
|
||||||
NodeVisitor child = super.child(ns, name);
|
NodeVisitor child = super.child(ns, name);
|
||||||
return new ManifestTagVisitor(child, processLogic);
|
return new ManifestTagVisitor(child, attrProcessLogic, childProcessLogic);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return writer.toByteArray();
|
return writer.toByteArray();
|
||||||
|
|
|
@ -1,7 +1,5 @@
|
||||||
package com.zane.smapiinstaller.logic;
|
package com.zane.smapiinstaller.logic;
|
||||||
|
|
||||||
import android.os.Environment;
|
|
||||||
|
|
||||||
import com.zane.smapiinstaller.constant.Constants;
|
import com.zane.smapiinstaller.constant.Constants;
|
||||||
import com.zane.smapiinstaller.entity.FrameworkConfig;
|
import com.zane.smapiinstaller.entity.FrameworkConfig;
|
||||||
import com.zane.smapiinstaller.utils.FileUtils;
|
import com.zane.smapiinstaller.utils.FileUtils;
|
||||||
|
@ -16,7 +14,7 @@ public class ConfigManager {
|
||||||
private FrameworkConfig config;
|
private FrameworkConfig config;
|
||||||
|
|
||||||
public ConfigManager() {
|
public ConfigManager() {
|
||||||
File configFile = new File(Environment.getExternalStorageDirectory(), Constants.CONFIG_PATH);
|
File configFile = new File(FileUtils.getStadewValleyBasePath(), Constants.CONFIG_PATH);
|
||||||
if(configFile.exists()) {
|
if(configFile.exists()) {
|
||||||
config = FileUtils.getFileJson(configFile, FrameworkConfig.class);
|
config = FileUtils.getFileJson(configFile, FrameworkConfig.class);
|
||||||
}
|
}
|
||||||
|
@ -31,7 +29,7 @@ public class ConfigManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
public void flushConfig() {
|
public void flushConfig() {
|
||||||
File configFile = new File(Environment.getExternalStorageDirectory(), Constants.CONFIG_PATH);
|
File configFile = new File(FileUtils.getStadewValleyBasePath(), Constants.CONFIG_PATH);
|
||||||
FileUtils.writeFileJson(configFile, config);
|
FileUtils.writeFileJson(configFile, config);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,10 @@
|
||||||
package com.zane.smapiinstaller.logic;
|
package com.zane.smapiinstaller.logic;
|
||||||
|
|
||||||
import com.google.common.base.Predicate;
|
import java.util.List;
|
||||||
|
import java.util.function.Function;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
import pxb.android.axml.NodeVisitor;
|
import pxb.android.axml.NodeVisitor;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -9,38 +12,56 @@ import pxb.android.axml.NodeVisitor;
|
||||||
*/
|
*/
|
||||||
class ManifestTagVisitor extends NodeVisitor {
|
class ManifestTagVisitor extends NodeVisitor {
|
||||||
|
|
||||||
private final Predicate<AttrArgs> attrProcessLogic;
|
private final Function<AttrArgs, List<AttrArgs>> attrProcessLogic;
|
||||||
|
private final Function<ChildArgs, List<ChildArgs>> childProcessLogic;
|
||||||
|
|
||||||
public ManifestTagVisitor(NodeVisitor nv, Predicate<AttrArgs> attrProcessLogic) {
|
public ManifestTagVisitor(NodeVisitor nv, Function<AttrArgs, List<AttrArgs>> attrProcessLogic, Function<ChildArgs, List<ChildArgs>> childProcessLogic) {
|
||||||
super(nv);
|
super(nv);
|
||||||
this.attrProcessLogic = attrProcessLogic;
|
this.attrProcessLogic = attrProcessLogic;
|
||||||
|
this.childProcessLogic = childProcessLogic;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void attr(String ns, String name, int resourceId, int type, Object obj) {
|
public void attr(String ns, String name, int resourceId, int type, Object obj) {
|
||||||
AttrArgs attrArgs = new AttrArgs(ns, name, resourceId, type, obj);
|
AttrArgs attrArgs = new AttrArgs(ns, name, resourceId, type, obj);
|
||||||
attrProcessLogic.apply(attrArgs);
|
List<AttrArgs> appendAttrs = attrProcessLogic.apply(attrArgs);
|
||||||
super.attr(attrArgs.ns, attrArgs.name, attrArgs.resourceId, attrArgs.type, attrArgs.obj);
|
super.attr(attrArgs.ns, attrArgs.name, attrArgs.resourceId, attrArgs.type, attrArgs.obj);
|
||||||
|
if(appendAttrs != null) {
|
||||||
|
for (AttrArgs attr: appendAttrs) {
|
||||||
|
super.attr(attr.ns, attr.name, attr.resourceId, attr.type, attr.obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public NodeVisitor child(String ns, String name) {
|
public NodeVisitor child(String ns, String name) {
|
||||||
return new ManifestTagVisitor(super.child(ns, name), attrProcessLogic);
|
ChildArgs childArgs = new ChildArgs(ns, name, null);
|
||||||
|
List<ChildArgs> appendChild = childProcessLogic.apply(childArgs);
|
||||||
|
NodeVisitor child = super.child(childArgs.ns, childArgs.name);
|
||||||
|
if(appendChild != null) {
|
||||||
|
for (ChildArgs c: appendChild) {
|
||||||
|
NodeVisitor visitor = super.child(c.ns, c.name);
|
||||||
|
if(c.attrArgs != null) {
|
||||||
|
for (AttrArgs attr: c.attrArgs) {
|
||||||
|
visitor.attr(attr.ns, attr.name, attr.resourceId, attr.type, attr.obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new ManifestTagVisitor(child, attrProcessLogic, childProcessLogic);
|
||||||
}
|
}
|
||||||
|
@AllArgsConstructor
|
||||||
|
public static class ChildArgs {
|
||||||
|
String ns;
|
||||||
|
String name;
|
||||||
|
List<AttrArgs> attrArgs;
|
||||||
|
}
|
||||||
|
@AllArgsConstructor
|
||||||
public static class AttrArgs {
|
public static class AttrArgs {
|
||||||
String ns;
|
String ns;
|
||||||
String name;
|
String name;
|
||||||
int resourceId;
|
int resourceId;
|
||||||
int type;
|
int type;
|
||||||
Object obj;
|
Object obj;
|
||||||
|
|
||||||
AttrArgs(String ns, String name, int resourceId, int type, Object obj) {
|
|
||||||
this.ns = ns;
|
|
||||||
this.name = name;
|
|
||||||
this.resourceId = resourceId;
|
|
||||||
this.type = type;
|
|
||||||
this.obj = obj;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,6 @@ package com.zane.smapiinstaller.logic;
|
||||||
|
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.pm.PackageInfo;
|
import android.content.pm.PackageInfo;
|
||||||
import android.os.Environment;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
|
@ -17,16 +16,18 @@ import com.google.common.collect.Queues;
|
||||||
import com.lzy.okgo.OkGo;
|
import com.lzy.okgo.OkGo;
|
||||||
import com.lzy.okgo.model.Response;
|
import com.lzy.okgo.model.Response;
|
||||||
import com.microsoft.appcenter.crashes.Crashes;
|
import com.microsoft.appcenter.crashes.Crashes;
|
||||||
|
import com.zane.smapiinstaller.MobileNavigationDirections;
|
||||||
import com.zane.smapiinstaller.R;
|
import com.zane.smapiinstaller.R;
|
||||||
import com.zane.smapiinstaller.constant.Constants;
|
import com.zane.smapiinstaller.constant.Constants;
|
||||||
import com.zane.smapiinstaller.constant.DialogAction;
|
import com.zane.smapiinstaller.constant.DialogAction;
|
||||||
import com.zane.smapiinstaller.dto.ModUpdateCheckRequestDto;
|
import com.zane.smapiinstaller.dto.ModUpdateCheckRequestDto;
|
||||||
import com.zane.smapiinstaller.dto.ModUpdateCheckResponseDto;
|
import com.zane.smapiinstaller.dto.ModUpdateCheckResponseDto;
|
||||||
|
import com.zane.smapiinstaller.dto.Tuple2;
|
||||||
import com.zane.smapiinstaller.entity.ModManifestEntry;
|
import com.zane.smapiinstaller.entity.ModManifestEntry;
|
||||||
import com.zane.smapiinstaller.utils.DialogUtils;
|
import com.zane.smapiinstaller.utils.DialogUtils;
|
||||||
import com.zane.smapiinstaller.utils.FileUtils;
|
import com.zane.smapiinstaller.utils.FileUtils;
|
||||||
import com.zane.smapiinstaller.utils.JsonUtil;
|
|
||||||
import com.zane.smapiinstaller.utils.JsonCallback;
|
import com.zane.smapiinstaller.utils.JsonCallback;
|
||||||
|
import com.zane.smapiinstaller.utils.JsonUtil;
|
||||||
import com.zane.smapiinstaller.utils.VersionUtil;
|
import com.zane.smapiinstaller.utils.VersionUtil;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
@ -35,17 +36,22 @@ import org.zeroturnaround.zip.ZipUtil;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Objects;
|
||||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
|
|
||||||
import java.util.Objects;
|
|
||||||
import java.util.function.Consumer;
|
import java.util.function.Consumer;
|
||||||
import java.util.function.Predicate;
|
import java.util.function.Predicate;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
|
import androidx.navigation.NavController;
|
||||||
|
import androidx.navigation.Navigation;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mod资源管理器
|
* Mod资源管理器
|
||||||
|
*
|
||||||
* @author Zane
|
* @author Zane
|
||||||
*/
|
*/
|
||||||
public class ModAssetsManager {
|
public class ModAssetsManager {
|
||||||
|
@ -68,7 +74,7 @@ public class ModAssetsManager {
|
||||||
*/
|
*/
|
||||||
public static ModManifestEntry findFirstModIf(Predicate<ModManifestEntry> filter) {
|
public static ModManifestEntry findFirstModIf(Predicate<ModManifestEntry> filter) {
|
||||||
ConcurrentLinkedQueue<File> files = Queues.newConcurrentLinkedQueue();
|
ConcurrentLinkedQueue<File> files = Queues.newConcurrentLinkedQueue();
|
||||||
files.add(new File(Environment.getExternalStorageDirectory(), Constants.MOD_PATH));
|
files.add(new File(FileUtils.getStadewValleyBasePath(), Constants.MOD_PATH));
|
||||||
do {
|
do {
|
||||||
File currentFile = files.poll();
|
File currentFile = files.poll();
|
||||||
if (currentFile != null && currentFile.exists()) {
|
if (currentFile != null && currentFile.exists()) {
|
||||||
|
@ -115,7 +121,7 @@ public class ModAssetsManager {
|
||||||
*/
|
*/
|
||||||
public static List<ModManifestEntry> findAllInstalledMods(boolean ignoreDisabledMod) {
|
public static List<ModManifestEntry> findAllInstalledMods(boolean ignoreDisabledMod) {
|
||||||
ConcurrentLinkedQueue<File> files = Queues.newConcurrentLinkedQueue();
|
ConcurrentLinkedQueue<File> files = Queues.newConcurrentLinkedQueue();
|
||||||
files.add(new File(Environment.getExternalStorageDirectory(), Constants.MOD_PATH));
|
files.add(new File(FileUtils.getStadewValleyBasePath(), Constants.MOD_PATH));
|
||||||
List<ModManifestEntry> mods = new ArrayList<>(30);
|
List<ModManifestEntry> mods = new ArrayList<>(30);
|
||||||
do {
|
do {
|
||||||
File currentFile = files.poll();
|
File currentFile = files.poll();
|
||||||
|
@ -162,7 +168,7 @@ public class ModAssetsManager {
|
||||||
if (modManifestEntries == null) {
|
if (modManifestEntries == null) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
File modFolder = new File(Environment.getExternalStorageDirectory(), Constants.MOD_PATH);
|
File modFolder = new File(FileUtils.getStadewValleyBasePath(), Constants.MOD_PATH);
|
||||||
ImmutableListMultimap<String, ModManifestEntry> installedModMap = Multimaps.index(findAllInstalledMods(), ModManifestEntry::getUniqueID);
|
ImmutableListMultimap<String, ModManifestEntry> installedModMap = Multimaps.index(findAllInstalledMods(), ModManifestEntry::getUniqueID);
|
||||||
for (ModManifestEntry mod : modManifestEntries) {
|
for (ModManifestEntry mod : modManifestEntries) {
|
||||||
if (installedModMap.containsKey(mod.getUniqueID()) || installedModMap.containsKey(mod.getUniqueID().replace("ZaneYork.CustomLocalization", "SMAPI.CustomLocalization"))) {
|
if (installedModMap.containsKey(mod.getUniqueID()) || installedModMap.containsKey(mod.getUniqueID().replace("ZaneYork.CustomLocalization", "SMAPI.CustomLocalization"))) {
|
||||||
|
@ -253,18 +259,28 @@ public class ModAssetsManager {
|
||||||
* @param returnCallback 回调函数
|
* @param returnCallback 回调函数
|
||||||
*/
|
*/
|
||||||
private void checkUnsatisfiedDependencies(ImmutableListMultimap<String, ModManifestEntry> installedModMap, Consumer<Boolean> returnCallback) {
|
private void checkUnsatisfiedDependencies(ImmutableListMultimap<String, ModManifestEntry> installedModMap, Consumer<Boolean> returnCallback) {
|
||||||
List<String> dependencyErrors = installedModMap.values().stream()
|
List<Tuple2<String, List<String>>> dependencyErrors = installedModMap.values().stream()
|
||||||
.map(mod -> checkModDependencyError(mod, installedModMap))
|
.map(mod -> checkModDependencyError(mod, installedModMap))
|
||||||
.filter(Objects::nonNull)
|
.filter(Objects::nonNull)
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
if (dependencyErrors.size() > 0) {
|
if (dependencyErrors.size() > 0) {
|
||||||
DialogUtils.showConfirmDialog(root, R.string.error,
|
DialogUtils.showConfirmDialog(root, R.string.error,
|
||||||
Joiner.on(";").join(dependencyErrors),
|
dependencyErrors.stream().map(Tuple2::getFirst).collect(Collectors.joining(";")),
|
||||||
R.string.continue_text, R.string.abort,
|
R.string.continue_text, R.string.menu_download,
|
||||||
((dialog, which) -> {
|
((dialog, which) -> {
|
||||||
if (which == DialogAction.POSITIVE) {
|
if (which == DialogAction.POSITIVE) {
|
||||||
returnCallback.accept(true);
|
returnCallback.accept(true);
|
||||||
} else {
|
} else {
|
||||||
|
List<ModUpdateCheckRequestDto.ModInfo> list = dependencyErrors.stream()
|
||||||
|
.map(Tuple2::getSecond).flatMap(Collection::stream).distinct()
|
||||||
|
.map(item -> {
|
||||||
|
ModUpdateCheckRequestDto.ModInfo modInfo = new ModUpdateCheckRequestDto.ModInfo();
|
||||||
|
modInfo.setId(item);
|
||||||
|
return modInfo;
|
||||||
|
})
|
||||||
|
.distinct()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
redirectModDownload(list);
|
||||||
returnCallback.accept(false);
|
returnCallback.accept(false);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
@ -280,17 +296,26 @@ public class ModAssetsManager {
|
||||||
* @param returnCallback 回调函数
|
* @param returnCallback 回调函数
|
||||||
*/
|
*/
|
||||||
private void checkContentpacks(ImmutableListMultimap<String, ModManifestEntry> installedModMap, Consumer<Boolean> returnCallback) {
|
private void checkContentpacks(ImmutableListMultimap<String, ModManifestEntry> installedModMap, Consumer<Boolean> returnCallback) {
|
||||||
List<String> dependencyErrors = installedModMap.values().stream()
|
List<Tuple2<String, String>> dependencyErrors = installedModMap.values().stream()
|
||||||
.map(mod -> checkContentPackDependencyError(mod, installedModMap))
|
.map(mod -> checkContentPackDependencyError(mod, installedModMap))
|
||||||
.filter(Objects::nonNull).collect(Collectors.toList());
|
.filter(Objects::nonNull).collect(Collectors.toList());
|
||||||
if (!dependencyErrors.isEmpty()) {
|
if (!dependencyErrors.isEmpty()) {
|
||||||
DialogUtils.showConfirmDialog(root, R.string.error,
|
DialogUtils.showConfirmDialog(root, R.string.error,
|
||||||
Joiner.on(";").join(dependencyErrors),
|
dependencyErrors.stream().map(Tuple2::getFirst).collect(Collectors.joining(";")),
|
||||||
R.string.continue_text, R.string.abort,
|
R.string.continue_text, R.string.menu_download,
|
||||||
((dialog, which) -> {
|
((dialog, which) -> {
|
||||||
if (which == DialogAction.POSITIVE) {
|
if (which == DialogAction.POSITIVE) {
|
||||||
returnCallback.accept(true);
|
returnCallback.accept(true);
|
||||||
} else {
|
} else {
|
||||||
|
List<ModUpdateCheckRequestDto.ModInfo> list = dependencyErrors.stream()
|
||||||
|
.map(item -> {
|
||||||
|
ModUpdateCheckRequestDto.ModInfo modInfo = new ModUpdateCheckRequestDto.ModInfo();
|
||||||
|
modInfo.setId(item.getSecond());
|
||||||
|
return modInfo;
|
||||||
|
})
|
||||||
|
.distinct()
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
redirectModDownload(list);
|
||||||
returnCallback.accept(false);
|
returnCallback.accept(false);
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
@ -299,6 +324,45 @@ public class ModAssetsManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void redirectModDownload(List<ModUpdateCheckRequestDto.ModInfo> list) {
|
||||||
|
ModUpdateCheckRequestDto requestDto = new ModUpdateCheckRequestDto(list);
|
||||||
|
try {
|
||||||
|
requestDto.setIncludeExtendedMetadata(true);
|
||||||
|
OkGo.<List<ModUpdateCheckResponseDto>>post(Constants.UPDATE_CHECK_SERVICE_URL)
|
||||||
|
.upJson(JsonUtil.toJson(requestDto))
|
||||||
|
.execute(new JsonCallback<List<ModUpdateCheckResponseDto>>(new TypeReference<List<ModUpdateCheckResponseDto>>() {
|
||||||
|
}) {
|
||||||
|
@Override
|
||||||
|
public void onError(Response<List<ModUpdateCheckResponseDto>> response) {
|
||||||
|
super.onError(response);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Response<List<ModUpdateCheckResponseDto>> response) {
|
||||||
|
List<ModUpdateCheckResponseDto> checkResponseDtos = response.body();
|
||||||
|
if (checkResponseDtos != null) {
|
||||||
|
List<ModUpdateCheckResponseDto> list = checkResponseDtos.stream()
|
||||||
|
.filter(dto -> dto.getMetadata() != null && dto.getMetadata().getMain() != null && StringUtils.isNoneBlank(dto.getMetadata().getMain().getUrl()))
|
||||||
|
.collect(Collectors.toList());
|
||||||
|
try {
|
||||||
|
if (list.size() > 0) {
|
||||||
|
for (ModUpdateCheckResponseDto dto : list) {
|
||||||
|
dto.setSuggestedUpdate(new ModUpdateCheckResponseDto.UpdateInfo(dto.getMetadata().getMain().getVersion(), dto.getMetadata().getMain().getUrl()));
|
||||||
|
}
|
||||||
|
NavController controller = Navigation.findNavController(CommonLogic.getActivityFromView(root), R.id.nav_host_fragment);
|
||||||
|
controller.navigate(MobileNavigationDirections.actionNavAnyToModUpdateFragment(JsonUtil.toJson(list)));
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
Crashes.trackError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (Exception e) {
|
||||||
|
Crashes.trackError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void checkModUpdate(Consumer<List<ModUpdateCheckResponseDto>> callback) {
|
public void checkModUpdate(Consumer<List<ModUpdateCheckResponseDto>> callback) {
|
||||||
if (checkUpdating.get()) {
|
if (checkUpdating.get()) {
|
||||||
return;
|
return;
|
||||||
|
@ -344,13 +408,15 @@ public class ModAssetsManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private String checkModDependencyError(ModManifestEntry mod, ImmutableListMultimap<String, ModManifestEntry> installedModMap) {
|
private Tuple2<String, List<String>> checkModDependencyError(ModManifestEntry mod, ImmutableListMultimap<String, ModManifestEntry> installedModMap) {
|
||||||
if (mod.getDependencies() != null) {
|
if (mod.getDependencies() != null) {
|
||||||
List<ModManifestEntry> unsatisfiedDependencies = mod.getDependencies().stream()
|
List<ModManifestEntry> unsatisfiedDependencies = mod.getDependencies().stream()
|
||||||
.filter(dependency -> isDependencyIsExist(dependency, installedModMap))
|
.filter(dependency -> isDependencyIsExist(dependency, installedModMap))
|
||||||
.collect(Collectors.toList());
|
.collect(Collectors.toList());
|
||||||
if (unsatisfiedDependencies.size() > 0) {
|
if (unsatisfiedDependencies.size() > 0) {
|
||||||
return root.getContext().getString(R.string.error_depends_on_mod, mod.getUniqueID(), Joiner.on(",").join(Lists.transform(unsatisfiedDependencies, ModManifestEntry::getUniqueID)));
|
return new Tuple2<>(
|
||||||
|
root.getContext().getString(R.string.error_depends_on_mod, mod.getUniqueID(), Joiner.on(",").join(Lists.transform(unsatisfiedDependencies, ModManifestEntry::getUniqueID))),
|
||||||
|
unsatisfiedDependencies.stream().map(ModManifestEntry::getUniqueID).collect(Collectors.toList()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@ -386,7 +452,7 @@ public class ModAssetsManager {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private String checkContentPackDependencyError(ModManifestEntry mod, ImmutableListMultimap<String, ModManifestEntry> installedModMap) {
|
private Tuple2<String, String> checkContentPackDependencyError(ModManifestEntry mod, ImmutableListMultimap<String, ModManifestEntry> installedModMap) {
|
||||||
ModManifestEntry dependency = mod.getContentPackFor();
|
ModManifestEntry dependency = mod.getContentPackFor();
|
||||||
if (dependency != null) {
|
if (dependency != null) {
|
||||||
if (dependency.getIsRequired() != null && !dependency.getIsRequired()) {
|
if (dependency.getIsRequired() != null && !dependency.getIsRequired()) {
|
||||||
|
@ -403,7 +469,7 @@ public class ModAssetsManager {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (entries.size() != 1) {
|
if (entries.size() != 1) {
|
||||||
return root.getContext().getString(R.string.error_depends_on_mod, mod.getUniqueID(), dependency.getUniqueID());
|
return new Tuple2<>(root.getContext().getString(R.string.error_depends_on_mod, mod.getUniqueID(), dependency.getUniqueID()), dependency.getUniqueID());
|
||||||
}
|
}
|
||||||
String version = entries.get(0).getVersion();
|
String version = entries.get(0).getVersion();
|
||||||
if (!StringUtils.isBlank(version)) {
|
if (!StringUtils.isBlank(version)) {
|
||||||
|
@ -411,7 +477,7 @@ public class ModAssetsManager {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (VersionUtil.compareVersion(version, dependency.getMinimumVersion()) < 0) {
|
if (VersionUtil.compareVersion(version, dependency.getMinimumVersion()) < 0) {
|
||||||
return root.getContext().getString(R.string.error_depends_on_mod_version, mod.getUniqueID(), dependency.getUniqueID(), dependency.getMinimumVersion());
|
return new Tuple2<>(root.getContext().getString(R.string.error_depends_on_mod_version, mod.getUniqueID(), dependency.getUniqueID(), dependency.getMinimumVersion()), dependency.getUniqueID());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -7,12 +7,19 @@ import android.os.Bundle;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.webkit.WebChromeClient;
|
||||||
|
import android.webkit.WebResourceError;
|
||||||
|
import android.webkit.WebResourceRequest;
|
||||||
|
import android.webkit.WebView;
|
||||||
|
import android.webkit.WebViewClient;
|
||||||
|
|
||||||
|
import com.hjq.language.LanguagesManager;
|
||||||
import com.zane.smapiinstaller.BuildConfig;
|
import com.zane.smapiinstaller.BuildConfig;
|
||||||
import com.zane.smapiinstaller.R;
|
import com.zane.smapiinstaller.R;
|
||||||
import com.zane.smapiinstaller.constant.Constants;
|
import com.zane.smapiinstaller.constant.Constants;
|
||||||
import com.zane.smapiinstaller.constant.DialogAction;
|
import com.zane.smapiinstaller.constant.DialogAction;
|
||||||
import com.zane.smapiinstaller.databinding.FragmentConfigEditBinding;
|
import com.zane.smapiinstaller.databinding.FragmentConfigEditBinding;
|
||||||
|
import com.zane.smapiinstaller.dto.WebViewObject;
|
||||||
import com.zane.smapiinstaller.logic.CommonLogic;
|
import com.zane.smapiinstaller.logic.CommonLogic;
|
||||||
import com.zane.smapiinstaller.utils.DialogUtils;
|
import com.zane.smapiinstaller.utils.DialogUtils;
|
||||||
import com.zane.smapiinstaller.utils.FileUtils;
|
import com.zane.smapiinstaller.utils.FileUtils;
|
||||||
|
@ -21,6 +28,7 @@ import com.zane.smapiinstaller.utils.JsonUtil;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.OutputStreamWriter;
|
import java.io.OutputStreamWriter;
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
import androidx.annotation.NonNull;
|
import androidx.annotation.NonNull;
|
||||||
import androidx.core.content.FileProvider;
|
import androidx.core.content.FileProvider;
|
||||||
|
@ -35,30 +43,72 @@ public class ConfigEditFragment extends Fragment {
|
||||||
private String configPath;
|
private String configPath;
|
||||||
|
|
||||||
private FragmentConfigEditBinding binding;
|
private FragmentConfigEditBinding binding;
|
||||||
|
private WebViewObject webObject = null;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(@NonNull LayoutInflater inflater,
|
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||||
ViewGroup container, Bundle savedInstanceState) {
|
ViewGroup container, Bundle savedInstanceState) {
|
||||||
binding = FragmentConfigEditBinding.inflate(inflater, container, false);
|
binding = FragmentConfigEditBinding.inflate(inflater, container, false);
|
||||||
|
initView();
|
||||||
|
binding.buttonConfigCancel.setOnClickListener(v -> onConfigCancel());
|
||||||
|
binding.buttonConfigSave.setOnClickListener(v -> onConfigSave());
|
||||||
|
binding.buttonLogParser.setOnClickListener(v -> onLogParser());
|
||||||
|
return binding.getRoot();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initView() {
|
||||||
CommonLogic.doOnNonNull(this.getArguments(), arguments -> {
|
CommonLogic.doOnNonNull(this.getArguments(), arguments -> {
|
||||||
ConfigEditFragmentArgs args = ConfigEditFragmentArgs.fromBundle(arguments);
|
ConfigEditFragmentArgs args = ConfigEditFragmentArgs.fromBundle(arguments);
|
||||||
editable = args.getEditable();
|
editable = args.getEditable();
|
||||||
if (!editable) {
|
if (!editable) {
|
||||||
binding.editTextConfigEdit.setKeyListener(null);
|
|
||||||
binding.buttonConfigSave.setVisibility(View.INVISIBLE);
|
binding.buttonConfigSave.setVisibility(View.INVISIBLE);
|
||||||
binding.buttonConfigCancel.setVisibility(View.INVISIBLE);
|
binding.buttonConfigCancel.setVisibility(View.INVISIBLE);
|
||||||
binding.buttonLogParser.setVisibility(View.VISIBLE);
|
binding.buttonLogParser.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
|
binding.editTextConfigWebview.getSettings().setJavaScriptEnabled(true);
|
||||||
|
binding.editTextConfigWebview.setWebChromeClient(new WebChromeClient());
|
||||||
|
binding.editTextConfigWebview.setWebViewClient(new WebViewClient());
|
||||||
configPath = args.getConfigPath();
|
configPath = args.getConfigPath();
|
||||||
File file = new File(configPath);
|
File file = new File(configPath);
|
||||||
if (file.exists() && file.length() < Constants.TEXT_FILE_OPEN_SIZE_LIMIT) {
|
if (file.exists() && file.length() < Constants.TEXT_FILE_OPEN_SIZE_LIMIT) {
|
||||||
String fileText = FileUtils.getFileText(file);
|
String fileText = FileUtils.getFileText(file);
|
||||||
if (fileText != null) {
|
if (fileText != null) {
|
||||||
binding.editTextConfigEdit.setText(fileText);
|
binding.scrollView.post(() -> {
|
||||||
|
CommonLogic.doOnNonNull(this.getContext(), (context -> {
|
||||||
|
int height = (int) (binding.scrollView.getMeasuredHeight() / context.getResources().getDisplayMetrics().density * 0.95);
|
||||||
|
String lang = LanguagesManager.getAppLanguage(context).getCountry();
|
||||||
|
switch (lang){
|
||||||
|
case "zh":
|
||||||
|
lang = "zh-CN";
|
||||||
|
break;
|
||||||
|
case "fr":
|
||||||
|
lang = "fr-FR";
|
||||||
|
break;
|
||||||
|
case "pt":
|
||||||
|
lang = "pt-BR";
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (editable) {
|
||||||
|
webObject = new WebViewObject(fileText, "code", lang, true, height);
|
||||||
|
binding.editTextConfigWebview.addJavascriptInterface(webObject, "webObject");
|
||||||
|
} else {
|
||||||
|
webObject = new WebViewObject(fileText, "text-plain", lang, false, height);
|
||||||
|
binding.editTextConfigWebview.addJavascriptInterface(webObject, "webObject");
|
||||||
|
}
|
||||||
|
String assetText = FileUtils.getAssetText(context, "jsoneditor/editor.html");
|
||||||
|
if (assetText != null) {
|
||||||
|
binding.editTextConfigWebview.loadDataWithBaseURL(
|
||||||
|
"file:///android_asset/jsoneditor/",
|
||||||
|
assetText,
|
||||||
|
"text/html",
|
||||||
|
"utf-8", "");
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
binding.editTextConfigEdit.setText("");
|
|
||||||
binding.editTextConfigEdit.setKeyListener(null);
|
|
||||||
DialogUtils.showConfirmDialog(binding.getRoot(), R.string.error, this.getString(R.string.text_too_large), R.string.open_with, R.string.cancel, ((dialog, which) -> {
|
DialogUtils.showConfirmDialog(binding.getRoot(), R.string.error, this.getString(R.string.text_too_large), R.string.open_with, R.string.cancel, ((dialog, which) -> {
|
||||||
if (which == DialogAction.POSITIVE) {
|
if (which == DialogAction.POSITIVE) {
|
||||||
Intent intent = new Intent("android.intent.action.VIEW");
|
Intent intent = new Intent("android.intent.action.VIEW");
|
||||||
|
@ -79,9 +129,6 @@ public class ConfigEditFragment extends Fragment {
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
binding.buttonConfigCancel.setOnClickListener(v -> onConfigCancel());
|
|
||||||
binding.buttonConfigSave.setOnClickListener(v -> onConfigSave());
|
|
||||||
return binding.getRoot();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -92,11 +139,14 @@ public class ConfigEditFragment extends Fragment {
|
||||||
|
|
||||||
private void onConfigSave() {
|
private void onConfigSave() {
|
||||||
try {
|
try {
|
||||||
JsonUtil.checkJson(binding.editTextConfigEdit.getText().toString());
|
if(webObject != null) {
|
||||||
FileOutputStream outputStream = new FileOutputStream(configPath);
|
binding.editTextConfigWebview.loadUrl("javascript:getJson()");
|
||||||
try (OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream)) {
|
JsonUtil.checkJson(webObject.getText());
|
||||||
outputStreamWriter.write(binding.editTextConfigEdit.getText().toString());
|
FileOutputStream outputStream = new FileOutputStream(configPath);
|
||||||
outputStreamWriter.flush();
|
try (OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream)) {
|
||||||
|
outputStreamWriter.write(webObject.getText());
|
||||||
|
outputStreamWriter.flush();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
DialogUtils.showAlertDialog(getView(), R.string.error, e.getLocalizedMessage());
|
DialogUtils.showAlertDialog(getView(), R.string.error, e.getLocalizedMessage());
|
||||||
|
|
|
@ -39,8 +39,13 @@ public class ConfigFragment extends Fragment {
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
binding.viewModList.addItemDecoration(new DividerItemDecoration(binding.viewModList.getContext(), DividerItemDecoration.VERTICAL));
|
binding.viewModList.addItemDecoration(new DividerItemDecoration(binding.viewModList.getContext(), DividerItemDecoration.VERTICAL));
|
||||||
binding.buttonSearch.addTextChangedListener((TextChangedWatcher) (s, start, before, count) -> configViewModel.filter(s));
|
binding.buttonSearch.addTextChangedListener(new TextChangedWatcher() {
|
||||||
binding.buttonSortBy.setOnClickListener(v -> onSortByClick());
|
@Override
|
||||||
|
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||||
|
configViewModel.filter(s);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
binding.buttonSortBy.setOnClickListener(this::onSortByClick);
|
||||||
return binding.getRoot();
|
return binding.getRoot();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,7 +55,7 @@ public class ConfigFragment extends Fragment {
|
||||||
binding = null;
|
binding = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
void onSortByClick() {
|
public void onSortByClick(View v) {
|
||||||
int index = 0;
|
int index = 0;
|
||||||
switch (configViewModel.getSortBy()) {
|
switch (configViewModel.getSortBy()) {
|
||||||
case "Name asc":
|
case "Name asc":
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
package com.zane.smapiinstaller.ui.help;
|
package com.zane.smapiinstaller.ui.help;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Environment;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
@ -12,6 +11,7 @@ import com.zane.smapiinstaller.databinding.FragmentHelpBinding;
|
||||||
import com.zane.smapiinstaller.entity.HelpItemList;
|
import com.zane.smapiinstaller.entity.HelpItemList;
|
||||||
import com.zane.smapiinstaller.logic.CommonLogic;
|
import com.zane.smapiinstaller.logic.CommonLogic;
|
||||||
import com.zane.smapiinstaller.logic.UpdatableListManager;
|
import com.zane.smapiinstaller.logic.UpdatableListManager;
|
||||||
|
import com.zane.smapiinstaller.utils.FileUtils;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
|
@ -65,7 +65,7 @@ public class HelpFragment extends Fragment {
|
||||||
private void showLog() {
|
private void showLog() {
|
||||||
CommonLogic.doOnNonNull(this.getView(), view -> {
|
CommonLogic.doOnNonNull(this.getView(), view -> {
|
||||||
NavController controller = Navigation.findNavController(view);
|
NavController controller = Navigation.findNavController(view);
|
||||||
File logFile = new File(Environment.getExternalStorageDirectory(), Constants.LOG_PATH);
|
File logFile = new File(FileUtils.getStadewValleyBasePath(), Constants.LOG_PATH);
|
||||||
if (logFile.exists()) {
|
if (logFile.exists()) {
|
||||||
MobileNavigationDirections.ActionNavAnyToConfigEditFragment action = HelpFragmentDirections.actionNavAnyToConfigEditFragment(logFile.getAbsolutePath());
|
MobileNavigationDirections.ActionNavAnyToConfigEditFragment action = HelpFragmentDirections.actionNavAnyToConfigEditFragment(logFile.getAbsolutePath());
|
||||||
action.setEditable(false);
|
action.setEditable(false);
|
||||||
|
|
|
@ -3,7 +3,6 @@ package com.zane.smapiinstaller.ui.install;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Environment;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
@ -21,6 +20,7 @@ import com.zane.smapiinstaller.logic.ModAssetsManager;
|
||||||
import com.zane.smapiinstaller.ui.main.MainTabsFragmentDirections;
|
import com.zane.smapiinstaller.ui.main.MainTabsFragmentDirections;
|
||||||
import com.zane.smapiinstaller.utils.ConfigUtils;
|
import com.zane.smapiinstaller.utils.ConfigUtils;
|
||||||
import com.zane.smapiinstaller.utils.DialogUtils;
|
import com.zane.smapiinstaller.utils.DialogUtils;
|
||||||
|
import com.zane.smapiinstaller.utils.FileUtils;
|
||||||
|
|
||||||
import org.apache.commons.lang3.RegExUtils;
|
import org.apache.commons.lang3.RegExUtils;
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
@ -55,7 +55,7 @@ public class InstallFragment extends Fragment {
|
||||||
binding.layoutAdvInstall.setVisibility(View.VISIBLE);
|
binding.layoutAdvInstall.setVisibility(View.VISIBLE);
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
String firstLine = Files.asCharSource(new File(Environment.getExternalStorageDirectory(), Constants.LOG_PATH), StandardCharsets.UTF_8).readFirstLine();
|
String firstLine = Files.asCharSource(new File(FileUtils.getStadewValleyBasePath(), Constants.LOG_PATH), StandardCharsets.UTF_8).readFirstLine();
|
||||||
if (StringUtils.isNoneBlank(firstLine)) {
|
if (StringUtils.isNoneBlank(firstLine)) {
|
||||||
String versionString = RegExUtils.removePattern(firstLine, "\\[.+\\]\\s+");
|
String versionString = RegExUtils.removePattern(firstLine, "\\[.+\\]\\s+");
|
||||||
versionString = RegExUtils.removePattern(versionString, "\\s+with.+");
|
versionString = RegExUtils.removePattern(versionString, "\\s+with.+");
|
||||||
|
|
|
@ -70,6 +70,13 @@ public class ModUpdateAdapter extends RecyclerView.Adapter<ModUpdateAdapter.View
|
||||||
activity.getString(R.string.mod_version_update_text, modManifestEntry.getVersion(), updateInfo.getSuggestedUpdate().getVersion())
|
activity.getString(R.string.mod_version_update_text, modManifestEntry.getVersion(), updateInfo.getSuggestedUpdate().getVersion())
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
binding.textViewModName.setText(this.updateInfo.getId());
|
||||||
|
CommonLogic.doOnNonNull(CommonLogic.getActivityFromView(binding.textViewModName),
|
||||||
|
activity -> binding.textViewModVersion.setText(
|
||||||
|
activity.getString(R.string.mod_version_update_text, updateInfo.getSuggestedUpdate().getVersion(), updateInfo.getSuggestedUpdate().getVersion())
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ViewHolder(View view) {
|
public ViewHolder(View view) {
|
||||||
|
|
|
@ -279,7 +279,7 @@ public class FileUtils extends org.zeroturnaround.zip.commons.FileUtils {
|
||||||
* @return 移除前缀后的路径
|
* @return 移除前缀后的路径
|
||||||
*/
|
*/
|
||||||
public static String toPrettyPath(String path) {
|
public static String toPrettyPath(String path) {
|
||||||
return StringUtils.removeStart(path, Environment.getExternalStorageDirectory().getAbsolutePath());
|
return StringUtils.removeStart(path, FileUtils.getStadewValleyBasePath());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -310,4 +310,8 @@ public class FileUtils extends org.zeroturnaround.zip.commons.FileUtils {
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getStadewValleyBasePath() {
|
||||||
|
return Environment.getExternalStorageDirectory().getAbsolutePath();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,7 @@ import android.text.Editable;
|
||||||
/**
|
/**
|
||||||
* @author Zane
|
* @author Zane
|
||||||
*/
|
*/
|
||||||
@FunctionalInterface
|
public abstract class TextChangedWatcher implements android.text.TextWatcher {
|
||||||
public interface TextChangedWatcher extends android.text.TextWatcher {
|
|
||||||
/**
|
/**
|
||||||
* Do nothing
|
* Do nothing
|
||||||
* @param s origin string
|
* @param s origin string
|
||||||
|
@ -15,7 +14,7 @@ public interface TextChangedWatcher extends android.text.TextWatcher {
|
||||||
* @param after modified string
|
* @param after modified string
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
default void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Text changed event
|
* Text changed event
|
||||||
|
@ -24,12 +23,12 @@ public interface TextChangedWatcher extends android.text.TextWatcher {
|
||||||
* @param count modify count
|
* @param count modify count
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
void onTextChanged(CharSequence s, int start, int before, int count);
|
public abstract void onTextChanged(CharSequence s, int start, int before, int count);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Do nothing
|
* Do nothing
|
||||||
* @param s target view
|
* @param s target view
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
default void afterTextChanged(Editable s) {}
|
public void afterTextChanged(Editable s) {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent">
|
android:layout_height="match_parent">
|
||||||
<ScrollView
|
<ScrollView
|
||||||
|
android:id="@+id/scrollView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_marginBottom="60dp"
|
android:layout_marginBottom="60dp"
|
||||||
|
@ -11,12 +12,10 @@
|
||||||
app:layout_constraintEnd_toStartOf="@id/guideline_v2"
|
app:layout_constraintEnd_toStartOf="@id/guideline_v2"
|
||||||
app:layout_constraintTop_toBottomOf="@id/guideline_h1"
|
app:layout_constraintTop_toBottomOf="@id/guideline_h1"
|
||||||
app:layout_constraintBottom_toTopOf="@id/guideline_h2">
|
app:layout_constraintBottom_toTopOf="@id/guideline_h2">
|
||||||
<EditText
|
<WebView
|
||||||
android:id="@+id/edit_text_config_edit"
|
android:id="@+id/edit_text_config_webview"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"/>
|
||||||
android:inputType="textMultiLine"
|
|
||||||
android:gravity="top|start"/>
|
|
||||||
</ScrollView>
|
</ScrollView>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
|
|
|
@ -84,4 +84,5 @@
|
||||||
<string name="request_unknown_source_permission">需要开启未知源权限以安装Mod框架,是否前往设置开启?</string>
|
<string name="request_unknown_source_permission">需要开启未知源权限以安装Mod框架,是否前往设置开启?</string>
|
||||||
<string name="settings_rewrite_missing">重寫增強</string>
|
<string name="settings_rewrite_missing">重寫增強</string>
|
||||||
<string name="settings_set_app_name">設定應用名稱</string>
|
<string name="settings_set_app_name">設定應用名稱</string>
|
||||||
|
<string name="request_all_files_access_permission">安卓11及以後版本需要授權文件管理權限以完成框架安裝,是否繼續?</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -84,4 +84,5 @@
|
||||||
<string name="request_unknown_source_permission">需要开启未知源权限以安装Mod框架,是否前往设置开启?</string>
|
<string name="request_unknown_source_permission">需要开启未知源权限以安装Mod框架,是否前往设置开启?</string>
|
||||||
<string name="settings_rewrite_missing">重写增强</string>
|
<string name="settings_rewrite_missing">重写增强</string>
|
||||||
<string name="settings_set_app_name">设置应用名称</string>
|
<string name="settings_set_app_name">设置应用名称</string>
|
||||||
|
<string name="request_all_files_access_permission">安卓11及以后版本需要授权文件管理权限以完成框架安装,是否继续?</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -88,4 +88,5 @@
|
||||||
<string name="request_unknown_source_permission">Needs to switch unknown source permission on to install mod framework, open settings?</string>
|
<string name="request_unknown_source_permission">Needs to switch unknown source permission on to install mod framework, open settings?</string>
|
||||||
<string name="settings_rewrite_missing">Rewrite Missing</string>
|
<string name="settings_rewrite_missing">Rewrite Missing</string>
|
||||||
<string name="settings_set_app_name">Patched App Name</string>
|
<string name="settings_set_app_name">Patched App Name</string>
|
||||||
|
<string name="request_all_files_access_permission">Need all files access permission on Android Q or above to install mod framework, continue?</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -8,7 +8,7 @@ buildscript {
|
||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:4.0.1'
|
classpath 'com.android.tools.build:gradle:4.0.2'
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.3.72"
|
||||||
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.3.0"
|
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.3.0"
|
||||||
classpath 'org.greenrobot:greendao-gradle-plugin:3.3.0'
|
classpath 'org.greenrobot:greendao-gradle-plugin:3.3.0'
|
||||||
|
|
Loading…
Reference in New Issue