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'
|
||||
|
||||
android {
|
||||
compileSdkVersion 28
|
||||
compileSdkVersion 30
|
||||
|
||||
|
||||
defaultConfig {
|
||||
applicationId "com.zane.smapiinstaller"
|
||||
minSdkVersion 19
|
||||
targetSdkVersion 28
|
||||
versionCode 56
|
||||
versionName "1.6.3"
|
||||
targetSdkVersion 30
|
||||
versionCode 58
|
||||
versionName "1.6.5"
|
||||
|
||||
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||
multiDexEnabled true
|
||||
ndk {
|
||||
abiFilters 'armeabi-v7a','arm64-v8a'
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
|
@ -53,8 +50,8 @@ android {
|
|||
}
|
||||
}
|
||||
|
||||
viewBinding {
|
||||
enabled = true
|
||||
buildFeatures {
|
||||
viewBinding true
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -5,10 +5,12 @@
|
|||
package="com.zane.smapiinstaller">
|
||||
|
||||
<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_DELETE_PACKAGES"/>
|
||||
<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
|
||||
android:name=".MainApplication"
|
||||
|
@ -21,6 +23,7 @@
|
|||
android:configChanges="orientation|keyboard|screenSize"
|
||||
android:networkSecurityConfig="@xml/network_security_config"
|
||||
android:theme="@style/AppTheme"
|
||||
android:requestLegacyExternalStorage="true"
|
||||
tools:ignore="UnusedAttribute">
|
||||
<activity
|
||||
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.Intent;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.provider.Settings;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
|
@ -32,6 +34,7 @@ import com.zane.smapiinstaller.logic.GameLauncher;
|
|||
import com.zane.smapiinstaller.logic.ModAssetsManager;
|
||||
import com.zane.smapiinstaller.utils.ConfigUtils;
|
||||
import com.zane.smapiinstaller.utils.DialogUtils;
|
||||
import com.zane.smapiinstaller.utils.FileUtils;
|
||||
import com.zane.smapiinstaller.utils.JsonCallback;
|
||||
import com.zane.smapiinstaller.utils.JsonUtil;
|
||||
import com.zane.smapiinstaller.utils.TranslateUtil;
|
||||
|
@ -64,6 +67,27 @@ public class MainActivity extends AppCompatActivity {
|
|||
private ActivityMainBinding binding;
|
||||
|
||||
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)
|
||||
!= PackageManager.PERMISSION_GRANTED) {
|
||||
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) -> {
|
||||
if (StringUtils.isNoneBlank(input)) {
|
||||
String pathString = input.toString();
|
||||
File file = new File(Environment.getExternalStorageDirectory(), pathString);
|
||||
File file = new File(FileUtils.getStadewValleyBasePath(), pathString);
|
||||
if (file.exists() && file.isDirectory()) {
|
||||
Constants.MOD_PATH = pathString;
|
||||
config.setModsPath(pathString);
|
||||
|
|
|
@ -25,6 +25,11 @@ import lombok.RequiredArgsConstructor;
|
|||
@RequiredArgsConstructor
|
||||
public class ModUpdateCheckRequestDto {
|
||||
|
||||
public ModUpdateCheckRequestDto(List<ModInfo> mods, SemanticVersion gameVersion) {
|
||||
this.mods = mods;
|
||||
this.gameVersion = gameVersion;
|
||||
}
|
||||
|
||||
/**
|
||||
* 待检查MOD列表
|
||||
*/
|
||||
|
@ -37,7 +42,6 @@ public class ModUpdateCheckRequestDto {
|
|||
/**
|
||||
* 游戏版本
|
||||
*/
|
||||
@NonNull
|
||||
private SemanticVersion gameVersion;
|
||||
/**
|
||||
* 平台版本
|
||||
|
|
|
@ -2,7 +2,9 @@ package com.zane.smapiinstaller.dto;
|
|||
|
||||
import java.util.List;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
/**
|
||||
* @author Zane
|
||||
|
@ -12,9 +14,22 @@ public class ModUpdateCheckResponseDto {
|
|||
private String id;
|
||||
private UpdateInfo suggestedUpdate;
|
||||
private List<String> errors;
|
||||
private Metadata metadata;
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public static class UpdateInfo {
|
||||
private String version;
|
||||
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
|
||||
*/
|
||||
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<>();
|
||||
|
||||
|
|
|
@ -7,17 +7,14 @@ import android.content.pm.PackageInfo;
|
|||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.provider.Settings;
|
||||
import android.util.Log;
|
||||
|
||||
import com.android.apksig.ApkSigner;
|
||||
import com.android.apksig.ApkVerifier;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.base.Stopwatch;
|
||||
import com.google.common.collect.Iterables;
|
||||
import com.google.common.hash.Hashing;
|
||||
import com.google.common.io.Files;
|
||||
import com.zane.smapiinstaller.BuildConfig;
|
||||
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.DialogAction;
|
||||
import com.zane.smapiinstaller.constant.ManifestPatchConstants;
|
||||
import com.zane.smapiinstaller.dto.Tuple2;
|
||||
import com.zane.smapiinstaller.entity.ApkFilesManifest;
|
||||
import com.zane.smapiinstaller.entity.ManifestEntry;
|
||||
import com.zane.smapiinstaller.utils.DialogUtils;
|
||||
|
@ -47,18 +45,17 @@ import java.security.cert.X509Certificate;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
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 androidx.core.content.FileProvider;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import pxb.android.axml.NodeVisitor;
|
||||
|
||||
/**
|
||||
|
@ -108,46 +105,44 @@ public class ApkPatcher {
|
|||
String sourceDir = packageInfo.applicationInfo.publicSourceDir;
|
||||
File apkFile = new File(sourceDir);
|
||||
|
||||
File externalFilesDir = Environment.getExternalStorageDirectory();
|
||||
if (externalFilesDir != null) {
|
||||
File dest = new File(externalFilesDir.getAbsolutePath() + "/SMAPI Installer/");
|
||||
if (!dest.exists()) {
|
||||
if (!dest.mkdir()) {
|
||||
errorMessage.set(String.format(context.getString(R.string.error_failed_to_create_file), dest.getAbsolutePath()));
|
||||
return null;
|
||||
}
|
||||
String stadewValleyBasePath = FileUtils.getStadewValleyBasePath();
|
||||
File dest = new File(stadewValleyBasePath + "/SMAPI Installer/");
|
||||
if (!dest.exists()) {
|
||||
if (!dest.mkdir()) {
|
||||
errorMessage.set(String.format(context.getString(R.string.error_failed_to_create_file), dest.getAbsolutePath()));
|
||||
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 (IOException e) {
|
||||
Log.e(TAG, "Extract error", e);
|
||||
|
@ -250,9 +245,9 @@ public class ApkPatcher {
|
|||
AtomicReference<String> packageName = new AtomicReference<>();
|
||||
AtomicReference<String> versionName = new AtomicReference<>();
|
||||
AtomicLong versionCode = new AtomicLong();
|
||||
Predicate<ManifestTagVisitor.AttrArgs> processLogic = (attr) -> {
|
||||
Function<ManifestTagVisitor.AttrArgs, List<ManifestTagVisitor.AttrArgs>> attrProcessLogic = (attr) -> {
|
||||
if (attr == null) {
|
||||
return true;
|
||||
return null;
|
||||
}
|
||||
if (attr.type == NodeVisitor.TYPE_STRING) {
|
||||
String strObj = (String) attr.obj;
|
||||
|
@ -276,6 +271,7 @@ public class ApkPatcher {
|
|||
attr.obj = Constants.PATCHED_APP_NAME;
|
||||
}
|
||||
}
|
||||
// return Collections.singletonList(new ManifestTagVisitor.AttrArgs(attr.ns, "requestLegacyExternalStorage", -1, NodeVisitor.TYPE_INT_BOOLEAN, true));
|
||||
break;
|
||||
case "authorities":
|
||||
if (strObj.contains(packageName.get())) {
|
||||
|
@ -296,10 +292,22 @@ public class ApkPatcher {
|
|||
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 {
|
||||
byte[] modifyManifest = CommonLogic.modifyManifest(bytes, processLogic);
|
||||
byte[] modifyManifest = CommonLogic.modifyManifest(bytes, attrProcessLogic, childProcessLogic);
|
||||
if (StringUtils.endsWith(versionName.get(), 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) {
|
||||
try {
|
||||
File externalFilesDir = Environment.getExternalStorageDirectory();
|
||||
String stadewValleyBasePath = FileUtils.getStadewValleyBasePath();
|
||||
emitProgress(47);
|
||||
if (externalFilesDir != null) {
|
||||
String signApkPath = externalFilesDir.getAbsolutePath() + "/SMAPI Installer/base_signed.apk";
|
||||
KeyStore ks = new KeyStoreFileManager.JksKeyStore();
|
||||
try (InputStream fis = context.getAssets().open("debug.keystore.dat")) {
|
||||
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 signApkPath = stadewValleyBasePath + "/SMAPI Installer/base_signed.apk";
|
||||
KeyStore ks = new KeyStoreFileManager.JksKeyStore();
|
||||
try (InputStream fis = context.getAssets().open("debug.keystore.dat")) {
|
||||
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;
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG, "Sign error", e);
|
||||
errorMessage.set(e.getLocalizedMessage());
|
||||
|
|
|
@ -11,7 +11,6 @@ import android.content.pm.PackageInfo;
|
|||
import android.content.pm.PackageManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.animation.Animation;
|
||||
|
@ -20,7 +19,6 @@ import android.widget.ImageView;
|
|||
|
||||
import com.afollestad.materialdialogs.MaterialDialog;
|
||||
import com.fasterxml.jackson.core.type.TypeReference;
|
||||
import com.google.common.base.Predicate;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.io.ByteStreams;
|
||||
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.R;
|
||||
import com.zane.smapiinstaller.constant.DialogAction;
|
||||
import com.zane.smapiinstaller.dto.Tuple2;
|
||||
import com.zane.smapiinstaller.entity.ApkFilesManifest;
|
||||
import com.zane.smapiinstaller.entity.ManifestEntry;
|
||||
import com.zane.smapiinstaller.utils.DialogUtils;
|
||||
|
@ -43,9 +42,10 @@ import java.util.ArrayList;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import java.util.function.BiConsumer;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
import pxb.android.axml.AxmlReader;
|
||||
import pxb.android.axml.AxmlVisitor;
|
||||
import pxb.android.axml.AxmlWriter;
|
||||
|
@ -196,7 +196,7 @@ public class CommonLogic {
|
|||
if (manifestEntries == null) {
|
||||
return false;
|
||||
}
|
||||
File basePath = new File(Environment.getExternalStorageDirectory() + "/StardewValley/");
|
||||
File basePath = new File(FileUtils.getStadewValleyBasePath() + "/StardewValley/");
|
||||
if (!basePath.exists()) {
|
||||
if (!basePath.mkdir()) {
|
||||
return false;
|
||||
|
@ -244,18 +244,18 @@ public class CommonLogic {
|
|||
* 修改AndroidManifest.xml文件
|
||||
*
|
||||
* @param bytes AndroidManifest.xml文件字符数组
|
||||
* @param processLogic 处理逻辑
|
||||
* @param attrProcessLogic 处理逻辑
|
||||
* @return 修改后的AndroidManifest.xml文件字符数组
|
||||
* @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);
|
||||
AxmlWriter writer = new AxmlWriter();
|
||||
reader.accept(new AxmlVisitor(writer) {
|
||||
@Override
|
||||
public NodeVisitor child(String ns, String name) {
|
||||
NodeVisitor child = super.child(ns, name);
|
||||
return new ManifestTagVisitor(child, processLogic);
|
||||
return new ManifestTagVisitor(child, attrProcessLogic, childProcessLogic);
|
||||
}
|
||||
});
|
||||
return writer.toByteArray();
|
||||
|
|
|
@ -1,7 +1,5 @@
|
|||
package com.zane.smapiinstaller.logic;
|
||||
|
||||
import android.os.Environment;
|
||||
|
||||
import com.zane.smapiinstaller.constant.Constants;
|
||||
import com.zane.smapiinstaller.entity.FrameworkConfig;
|
||||
import com.zane.smapiinstaller.utils.FileUtils;
|
||||
|
@ -16,7 +14,7 @@ public class ConfigManager {
|
|||
private FrameworkConfig config;
|
||||
|
||||
public ConfigManager() {
|
||||
File configFile = new File(Environment.getExternalStorageDirectory(), Constants.CONFIG_PATH);
|
||||
File configFile = new File(FileUtils.getStadewValleyBasePath(), Constants.CONFIG_PATH);
|
||||
if(configFile.exists()) {
|
||||
config = FileUtils.getFileJson(configFile, FrameworkConfig.class);
|
||||
}
|
||||
|
@ -31,7 +29,7 @@ public class ConfigManager {
|
|||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +1,10 @@
|
|||
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;
|
||||
|
||||
/**
|
||||
|
@ -9,38 +12,56 @@ import pxb.android.axml.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);
|
||||
this.attrProcessLogic = attrProcessLogic;
|
||||
this.childProcessLogic = childProcessLogic;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void attr(String ns, String name, int resourceId, int type, Object 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);
|
||||
if(appendAttrs != null) {
|
||||
for (AttrArgs attr: appendAttrs) {
|
||||
super.attr(attr.ns, attr.name, attr.resourceId, attr.type, attr.obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
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 {
|
||||
String ns;
|
||||
String name;
|
||||
int resourceId;
|
||||
int type;
|
||||
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.content.pm.PackageInfo;
|
||||
import android.os.Environment;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Toast;
|
||||
|
@ -17,16 +16,18 @@ import com.google.common.collect.Queues;
|
|||
import com.lzy.okgo.OkGo;
|
||||
import com.lzy.okgo.model.Response;
|
||||
import com.microsoft.appcenter.crashes.Crashes;
|
||||
import com.zane.smapiinstaller.MobileNavigationDirections;
|
||||
import com.zane.smapiinstaller.R;
|
||||
import com.zane.smapiinstaller.constant.Constants;
|
||||
import com.zane.smapiinstaller.constant.DialogAction;
|
||||
import com.zane.smapiinstaller.dto.ModUpdateCheckRequestDto;
|
||||
import com.zane.smapiinstaller.dto.ModUpdateCheckResponseDto;
|
||||
import com.zane.smapiinstaller.dto.Tuple2;
|
||||
import com.zane.smapiinstaller.entity.ModManifestEntry;
|
||||
import com.zane.smapiinstaller.utils.DialogUtils;
|
||||
import com.zane.smapiinstaller.utils.FileUtils;
|
||||
import com.zane.smapiinstaller.utils.JsonUtil;
|
||||
import com.zane.smapiinstaller.utils.JsonCallback;
|
||||
import com.zane.smapiinstaller.utils.JsonUtil;
|
||||
import com.zane.smapiinstaller.utils.VersionUtil;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
@ -35,17 +36,22 @@ import org.zeroturnaround.zip.ZipUtil;
|
|||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import java.util.Objects;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import androidx.navigation.NavController;
|
||||
import androidx.navigation.Navigation;
|
||||
|
||||
/**
|
||||
* Mod资源管理器
|
||||
*
|
||||
* @author Zane
|
||||
*/
|
||||
public class ModAssetsManager {
|
||||
|
@ -68,7 +74,7 @@ public class ModAssetsManager {
|
|||
*/
|
||||
public static ModManifestEntry findFirstModIf(Predicate<ModManifestEntry> filter) {
|
||||
ConcurrentLinkedQueue<File> files = Queues.newConcurrentLinkedQueue();
|
||||
files.add(new File(Environment.getExternalStorageDirectory(), Constants.MOD_PATH));
|
||||
files.add(new File(FileUtils.getStadewValleyBasePath(), Constants.MOD_PATH));
|
||||
do {
|
||||
File currentFile = files.poll();
|
||||
if (currentFile != null && currentFile.exists()) {
|
||||
|
@ -115,7 +121,7 @@ public class ModAssetsManager {
|
|||
*/
|
||||
public static List<ModManifestEntry> findAllInstalledMods(boolean ignoreDisabledMod) {
|
||||
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);
|
||||
do {
|
||||
File currentFile = files.poll();
|
||||
|
@ -162,7 +168,7 @@ public class ModAssetsManager {
|
|||
if (modManifestEntries == null) {
|
||||
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);
|
||||
for (ModManifestEntry mod : modManifestEntries) {
|
||||
if (installedModMap.containsKey(mod.getUniqueID()) || installedModMap.containsKey(mod.getUniqueID().replace("ZaneYork.CustomLocalization", "SMAPI.CustomLocalization"))) {
|
||||
|
@ -253,18 +259,28 @@ public class ModAssetsManager {
|
|||
* @param 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))
|
||||
.filter(Objects::nonNull)
|
||||
.collect(Collectors.toList());
|
||||
if (dependencyErrors.size() > 0) {
|
||||
DialogUtils.showConfirmDialog(root, R.string.error,
|
||||
Joiner.on(";").join(dependencyErrors),
|
||||
R.string.continue_text, R.string.abort,
|
||||
dependencyErrors.stream().map(Tuple2::getFirst).collect(Collectors.joining(";")),
|
||||
R.string.continue_text, R.string.menu_download,
|
||||
((dialog, which) -> {
|
||||
if (which == DialogAction.POSITIVE) {
|
||||
returnCallback.accept(true);
|
||||
} 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);
|
||||
}
|
||||
}));
|
||||
|
@ -280,17 +296,26 @@ public class ModAssetsManager {
|
|||
* @param 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))
|
||||
.filter(Objects::nonNull).collect(Collectors.toList());
|
||||
if (!dependencyErrors.isEmpty()) {
|
||||
DialogUtils.showConfirmDialog(root, R.string.error,
|
||||
Joiner.on(";").join(dependencyErrors),
|
||||
R.string.continue_text, R.string.abort,
|
||||
dependencyErrors.stream().map(Tuple2::getFirst).collect(Collectors.joining(";")),
|
||||
R.string.continue_text, R.string.menu_download,
|
||||
((dialog, which) -> {
|
||||
if (which == DialogAction.POSITIVE) {
|
||||
returnCallback.accept(true);
|
||||
} 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);
|
||||
}
|
||||
}));
|
||||
|
@ -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) {
|
||||
if (checkUpdating.get()) {
|
||||
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) {
|
||||
List<ModManifestEntry> unsatisfiedDependencies = mod.getDependencies().stream()
|
||||
.filter(dependency -> isDependencyIsExist(dependency, installedModMap))
|
||||
.collect(Collectors.toList());
|
||||
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;
|
||||
|
@ -386,7 +452,7 @@ public class ModAssetsManager {
|
|||
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();
|
||||
if (dependency != null) {
|
||||
if (dependency.getIsRequired() != null && !dependency.getIsRequired()) {
|
||||
|
@ -403,7 +469,7 @@ public class ModAssetsManager {
|
|||
}
|
||||
}
|
||||
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();
|
||||
if (!StringUtils.isBlank(version)) {
|
||||
|
@ -411,7 +477,7 @@ public class ModAssetsManager {
|
|||
return null;
|
||||
}
|
||||
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;
|
||||
|
|
|
@ -7,12 +7,19 @@ import android.os.Bundle;
|
|||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
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.R;
|
||||
import com.zane.smapiinstaller.constant.Constants;
|
||||
import com.zane.smapiinstaller.constant.DialogAction;
|
||||
import com.zane.smapiinstaller.databinding.FragmentConfigEditBinding;
|
||||
import com.zane.smapiinstaller.dto.WebViewObject;
|
||||
import com.zane.smapiinstaller.logic.CommonLogic;
|
||||
import com.zane.smapiinstaller.utils.DialogUtils;
|
||||
import com.zane.smapiinstaller.utils.FileUtils;
|
||||
|
@ -21,6 +28,7 @@ import com.zane.smapiinstaller.utils.JsonUtil;
|
|||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.util.Locale;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.FileProvider;
|
||||
|
@ -35,30 +43,72 @@ public class ConfigEditFragment extends Fragment {
|
|||
private String configPath;
|
||||
|
||||
private FragmentConfigEditBinding binding;
|
||||
private WebViewObject webObject = null;
|
||||
|
||||
@Override
|
||||
public View onCreateView(@NonNull LayoutInflater inflater,
|
||||
ViewGroup container, Bundle savedInstanceState) {
|
||||
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 -> {
|
||||
ConfigEditFragmentArgs args = ConfigEditFragmentArgs.fromBundle(arguments);
|
||||
editable = args.getEditable();
|
||||
if (!editable) {
|
||||
binding.editTextConfigEdit.setKeyListener(null);
|
||||
binding.buttonConfigSave.setVisibility(View.INVISIBLE);
|
||||
binding.buttonConfigCancel.setVisibility(View.INVISIBLE);
|
||||
binding.buttonLogParser.setVisibility(View.VISIBLE);
|
||||
}
|
||||
binding.editTextConfigWebview.getSettings().setJavaScriptEnabled(true);
|
||||
binding.editTextConfigWebview.setWebChromeClient(new WebChromeClient());
|
||||
binding.editTextConfigWebview.setWebViewClient(new WebViewClient());
|
||||
configPath = args.getConfigPath();
|
||||
File file = new File(configPath);
|
||||
if (file.exists() && file.length() < Constants.TEXT_FILE_OPEN_SIZE_LIMIT) {
|
||||
String fileText = FileUtils.getFileText(file);
|
||||
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 {
|
||||
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) -> {
|
||||
if (which == DialogAction.POSITIVE) {
|
||||
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
|
||||
|
@ -92,11 +139,14 @@ public class ConfigEditFragment extends Fragment {
|
|||
|
||||
private void onConfigSave() {
|
||||
try {
|
||||
JsonUtil.checkJson(binding.editTextConfigEdit.getText().toString());
|
||||
FileOutputStream outputStream = new FileOutputStream(configPath);
|
||||
try (OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream)) {
|
||||
outputStreamWriter.write(binding.editTextConfigEdit.getText().toString());
|
||||
outputStreamWriter.flush();
|
||||
if(webObject != null) {
|
||||
binding.editTextConfigWebview.loadUrl("javascript:getJson()");
|
||||
JsonUtil.checkJson(webObject.getText());
|
||||
FileOutputStream outputStream = new FileOutputStream(configPath);
|
||||
try (OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream)) {
|
||||
outputStreamWriter.write(webObject.getText());
|
||||
outputStreamWriter.flush();
|
||||
}
|
||||
}
|
||||
} catch (Exception e) {
|
||||
DialogUtils.showAlertDialog(getView(), R.string.error, e.getLocalizedMessage());
|
||||
|
|
|
@ -39,8 +39,13 @@ public class ConfigFragment extends Fragment {
|
|||
return false;
|
||||
});
|
||||
binding.viewModList.addItemDecoration(new DividerItemDecoration(binding.viewModList.getContext(), DividerItemDecoration.VERTICAL));
|
||||
binding.buttonSearch.addTextChangedListener((TextChangedWatcher) (s, start, before, count) -> configViewModel.filter(s));
|
||||
binding.buttonSortBy.setOnClickListener(v -> onSortByClick());
|
||||
binding.buttonSearch.addTextChangedListener(new TextChangedWatcher() {
|
||||
@Override
|
||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||
configViewModel.filter(s);
|
||||
}
|
||||
});
|
||||
binding.buttonSortBy.setOnClickListener(this::onSortByClick);
|
||||
return binding.getRoot();
|
||||
}
|
||||
|
||||
|
@ -50,7 +55,7 @@ public class ConfigFragment extends Fragment {
|
|||
binding = null;
|
||||
}
|
||||
|
||||
void onSortByClick() {
|
||||
public void onSortByClick(View v) {
|
||||
int index = 0;
|
||||
switch (configViewModel.getSortBy()) {
|
||||
case "Name asc":
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
package com.zane.smapiinstaller.ui.help;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
@ -12,6 +11,7 @@ import com.zane.smapiinstaller.databinding.FragmentHelpBinding;
|
|||
import com.zane.smapiinstaller.entity.HelpItemList;
|
||||
import com.zane.smapiinstaller.logic.CommonLogic;
|
||||
import com.zane.smapiinstaller.logic.UpdatableListManager;
|
||||
import com.zane.smapiinstaller.utils.FileUtils;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
|
@ -65,7 +65,7 @@ public class HelpFragment extends Fragment {
|
|||
private void showLog() {
|
||||
CommonLogic.doOnNonNull(this.getView(), 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()) {
|
||||
MobileNavigationDirections.ActionNavAnyToConfigEditFragment action = HelpFragmentDirections.actionNavAnyToConfigEditFragment(logFile.getAbsolutePath());
|
||||
action.setEditable(false);
|
||||
|
|
|
@ -3,7 +3,6 @@ package com.zane.smapiinstaller.ui.install;
|
|||
import android.app.Activity;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Environment;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
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.utils.ConfigUtils;
|
||||
import com.zane.smapiinstaller.utils.DialogUtils;
|
||||
import com.zane.smapiinstaller.utils.FileUtils;
|
||||
|
||||
import org.apache.commons.lang3.RegExUtils;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
@ -55,7 +55,7 @@ public class InstallFragment extends Fragment {
|
|||
binding.layoutAdvInstall.setVisibility(View.VISIBLE);
|
||||
}
|
||||
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)) {
|
||||
String versionString = RegExUtils.removePattern(firstLine, "\\[.+\\]\\s+");
|
||||
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())
|
||||
));
|
||||
}
|
||||
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) {
|
||||
|
|
|
@ -279,7 +279,7 @@ public class FileUtils extends org.zeroturnaround.zip.commons.FileUtils {
|
|||
* @return 移除前缀后的路径
|
||||
*/
|
||||
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;
|
||||
}
|
||||
|
||||
public static String getStadewValleyBasePath() {
|
||||
return Environment.getExternalStorageDirectory().getAbsolutePath();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,8 +5,7 @@ import android.text.Editable;
|
|||
/**
|
||||
* @author Zane
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface TextChangedWatcher extends android.text.TextWatcher {
|
||||
public abstract class TextChangedWatcher implements android.text.TextWatcher {
|
||||
/**
|
||||
* Do nothing
|
||||
* @param s origin string
|
||||
|
@ -15,7 +14,7 @@ public interface TextChangedWatcher extends android.text.TextWatcher {
|
|||
* @param after modified string
|
||||
*/
|
||||
@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
|
||||
|
@ -24,12 +23,12 @@ public interface TextChangedWatcher extends android.text.TextWatcher {
|
|||
* @param count modify count
|
||||
*/
|
||||
@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
|
||||
* @param s target view
|
||||
*/
|
||||
@Override
|
||||
default void afterTextChanged(Editable s) {}
|
||||
public void afterTextChanged(Editable s) {}
|
||||
}
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<ScrollView
|
||||
android:id="@+id/scrollView"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginBottom="60dp"
|
||||
|
@ -11,12 +12,10 @@
|
|||
app:layout_constraintEnd_toStartOf="@id/guideline_v2"
|
||||
app:layout_constraintTop_toBottomOf="@id/guideline_h1"
|
||||
app:layout_constraintBottom_toTopOf="@id/guideline_h2">
|
||||
<EditText
|
||||
android:id="@+id/edit_text_config_edit"
|
||||
<WebView
|
||||
android:id="@+id/edit_text_config_webview"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="textMultiLine"
|
||||
android:gravity="top|start"/>
|
||||
android:layout_height="wrap_content"/>
|
||||
</ScrollView>
|
||||
|
||||
<Button
|
||||
|
|
|
@ -84,4 +84,5 @@
|
|||
<string name="request_unknown_source_permission">需要开启未知源权限以安装Mod框架,是否前往设置开启?</string>
|
||||
<string name="settings_rewrite_missing">重寫增強</string>
|
||||
<string name="settings_set_app_name">設定應用名稱</string>
|
||||
<string name="request_all_files_access_permission">安卓11及以後版本需要授權文件管理權限以完成框架安裝,是否繼續?</string>
|
||||
</resources>
|
||||
|
|
|
@ -84,4 +84,5 @@
|
|||
<string name="request_unknown_source_permission">需要开启未知源权限以安装Mod框架,是否前往设置开启?</string>
|
||||
<string name="settings_rewrite_missing">重写增强</string>
|
||||
<string name="settings_set_app_name">设置应用名称</string>
|
||||
<string name="request_all_files_access_permission">安卓11及以后版本需要授权文件管理权限以完成框架安装,是否继续?</string>
|
||||
</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="settings_rewrite_missing">Rewrite Missing</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>
|
||||
|
|
|
@ -8,7 +8,7 @@ buildscript {
|
|||
mavenCentral()
|
||||
}
|
||||
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 "androidx.navigation:navigation-safe-args-gradle-plugin:2.3.0"
|
||||
classpath 'org.greenrobot:greendao-gradle-plugin:3.3.0'
|
||||
|
|
Loading…
Reference in New Issue