Compare commits
42 Commits
manager-v4
...
manager-v4
Author | SHA1 | Date | |
---|---|---|---|
![]() |
0aefa9599f | ||
![]() |
e279cf0575 | ||
![]() |
a3f0ef8e77 | ||
![]() |
8eba05ed4a | ||
![]() |
2f78155723 | ||
![]() |
6785221479 | ||
![]() |
9bc410dd3d | ||
![]() |
2491ab6bf9 | ||
![]() |
f615ed40cd | ||
![]() |
430f2cafc1 | ||
![]() |
0ad049da88 | ||
![]() |
2c7691567b | ||
![]() |
1d70d0fe94 | ||
![]() |
ac44f05811 | ||
![]() |
d99252f394 | ||
![]() |
b58c7ba7c5 | ||
![]() |
8c5acd1a0a | ||
![]() |
b9b1ebf18c | ||
![]() |
8ca132cef0 | ||
![]() |
a03bb90754 | ||
![]() |
d1c939f48a | ||
![]() |
21b11f1b48 | ||
![]() |
23c84a7803 | ||
![]() |
f9ab060403 | ||
![]() |
df7a5bf149 | ||
![]() |
c4afa069df | ||
![]() |
1bfafdb44f | ||
![]() |
1ef5bd7076 | ||
![]() |
29176fa4f4 | ||
![]() |
958c95732b | ||
![]() |
44b0d4127c | ||
![]() |
1418ec2416 | ||
![]() |
b51978f51c | ||
![]() |
b07361580a | ||
![]() |
d1b5ebad7d | ||
![]() |
f4ce813de9 | ||
![]() |
b44ac994d8 | ||
![]() |
333948814c | ||
![]() |
1a51ad6e01 | ||
![]() |
22a5c11f0d | ||
![]() |
51b22d1ad4 | ||
![]() |
bef5969580 |
@@ -8,8 +8,8 @@ android {
|
||||
applicationId "com.topjohnwu.magisk"
|
||||
minSdkVersion 21
|
||||
targetSdkVersion 25
|
||||
versionCode 20
|
||||
versionName "4.0"
|
||||
versionCode 24
|
||||
versionName "4.2.5"
|
||||
jackOptions {
|
||||
enabled true
|
||||
jackInProcess true
|
||||
@@ -51,11 +51,10 @@ dependencies {
|
||||
compile 'com.android.support:cardview-v7:25.1.1'
|
||||
compile 'com.android.support:design:25.1.1'
|
||||
compile 'com.android.support:support-v4:25.1.1'
|
||||
compile 'com.android.support:support-v13:25.1.1'
|
||||
compile 'com.jakewharton:butterknife:8.5.1'
|
||||
compile 'com.google.code.gson:gson:2.8.0'
|
||||
compile 'com.github.clans:fab:1.6.4'
|
||||
compile 'com.thoughtbot:expandablerecyclerview:1.4'
|
||||
compile 'us.feras.mdv:markdownview:1.1.0'
|
||||
compile 'com.madgag.spongycastle:core:1.54.0.0'
|
||||
compile 'com.madgag.spongycastle:prov:1.54.0.0'
|
||||
compile 'com.madgag.spongycastle:pkix:1.54.0.0'
|
||||
|
21
app/proguard-rules.pro
vendored
@@ -16,27 +16,6 @@
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Gson uses generic type information stored in a class file when working with fields. Proguard
|
||||
# removes such information by default, so configure it to keep all of it.
|
||||
-keepattributes Signature
|
||||
|
||||
# For using GSON @Expose annotation
|
||||
-keepattributes *Annotation*
|
||||
|
||||
# Gson specific classes
|
||||
-keep class sun.misc.Unsafe { *; }
|
||||
-keep class com.google.gson.** { *; }
|
||||
|
||||
# Application classes that will be serialized/deserialized over Gson
|
||||
-keep class com.topjohnwu.magisk.module.** { *; }
|
||||
-keep class com.topjohnwu.magisk.module.ModuleHelper$ValueSortedMap { *; }
|
||||
|
||||
# Prevent proguard from stripping interface information from TypeAdapterFactory,
|
||||
# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter)
|
||||
-keep class * implements com.google.gson.TypeAdapterFactory
|
||||
-keep class * implements com.google.gson.JsonSerializer
|
||||
-keep class * implements com.google.gson.JsonDeserializer
|
||||
|
||||
-keep class android.support.v7.internal.** { *; }
|
||||
-keep interface android.support.v7.internal.** { *; }
|
||||
-keep class android.support.v7.** { *; }
|
||||
|
@@ -8,8 +8,10 @@
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
|
||||
<application
|
||||
android:name=".MagiskManager"
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
@@ -59,6 +61,13 @@
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<service android:name=".services.BootupIntentService" />
|
||||
|
||||
<service
|
||||
android:name=".services.UpdateCheckService"
|
||||
android:permission="android.permission.BIND_JOB_SERVICE"
|
||||
android:exported="true" />
|
||||
|
||||
<provider
|
||||
android:name="android.support.v4.content.FileProvider"
|
||||
android:authorities="com.topjohnwu.magisk.provider"
|
||||
|
276
app/src/main/assets/dark.css
Normal file
@@ -0,0 +1,276 @@
|
||||
body {
|
||||
font-family: Helvetica, arial, sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
background-color: #303030;
|
||||
color: white;
|
||||
padding: 15px; }
|
||||
|
||||
body > *:first-child {
|
||||
margin-top: 0 !important; }
|
||||
body > *:last-child {
|
||||
margin-bottom: 0 !important; }
|
||||
|
||||
a {
|
||||
color: #4183C4; }
|
||||
a.absent {
|
||||
color: #cc0000; }
|
||||
a.anchor {
|
||||
display: block;
|
||||
padding-left: 30px;
|
||||
margin-left: -30px;
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0; }
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin: 20px 0 10px;
|
||||
padding: 0;
|
||||
font-weight: bold;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
cursor: text;
|
||||
position: relative; }
|
||||
|
||||
h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor, h5:hover a.anchor, h6:hover a.anchor {
|
||||
background: url("../../images/modules/styleguide/para.png") no-repeat 10px center;
|
||||
text-decoration: none; }
|
||||
|
||||
h1 tt, h1 code {
|
||||
font-size: inherit; }
|
||||
|
||||
h2 tt, h2 code {
|
||||
font-size: inherit; }
|
||||
|
||||
h3 tt, h3 code {
|
||||
font-size: inherit; }
|
||||
|
||||
h4 tt, h4 code {
|
||||
font-size: inherit; }
|
||||
|
||||
h5 tt, h5 code {
|
||||
font-size: inherit; }
|
||||
|
||||
h6 tt, h6 code {
|
||||
font-size: inherit; }
|
||||
|
||||
h1 {
|
||||
font-size: 28px; }
|
||||
|
||||
h2 {
|
||||
font-size: 24px;
|
||||
border-bottom: 1px solid #cccccc; }
|
||||
|
||||
h3 {
|
||||
font-size: 18px; }
|
||||
|
||||
h4 {
|
||||
font-size: 16px; }
|
||||
|
||||
h5 {
|
||||
font-size: 14px; }
|
||||
|
||||
h6 {
|
||||
color: #888888;
|
||||
font-size: 14px; }
|
||||
|
||||
p, blockquote, ul, ol, dl, li, table, pre {
|
||||
margin: 15px 0; }
|
||||
|
||||
hr {
|
||||
background: transparent url("../../images/modules/pulls/dirty-shade.png") repeat-x 0 0;
|
||||
border: 0 none;
|
||||
color: #cccccc;
|
||||
height: 4px;
|
||||
padding: 0; }
|
||||
|
||||
body > h2:first-child {
|
||||
margin-top: 0;
|
||||
padding-top: 0; }
|
||||
body > h1:first-child {
|
||||
margin-top: 0;
|
||||
padding-top: 0; }
|
||||
body > h1:first-child + h2 {
|
||||
margin-top: 0;
|
||||
padding-top: 0; }
|
||||
body > h3:first-child, body > h4:first-child, body > h5:first-child, body > h6:first-child {
|
||||
margin-top: 0;
|
||||
padding-top: 0; }
|
||||
|
||||
a:first-child h1, a:first-child h2, a:first-child h3, a:first-child h4, a:first-child h5, a:first-child h6 {
|
||||
margin-top: 0;
|
||||
padding-top: 0; }
|
||||
|
||||
h1 p, h2 p, h3 p, h4 p, h5 p, h6 p {
|
||||
margin-top: 0; }
|
||||
|
||||
li p.first {
|
||||
display: inline-block; }
|
||||
|
||||
ul, ol {
|
||||
padding-left: 30px; }
|
||||
|
||||
ul :first-child, ol :first-child {
|
||||
margin-top: 0; }
|
||||
|
||||
ul :last-child, ol :last-child {
|
||||
margin-bottom: 0; }
|
||||
|
||||
dl {
|
||||
padding: 0; }
|
||||
dl dt {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
font-style: italic;
|
||||
padding: 0;
|
||||
margin: 15px 0 5px; }
|
||||
dl dt:first-child {
|
||||
padding: 0; }
|
||||
dl dt > :first-child {
|
||||
margin-top: 0; }
|
||||
dl dt > :last-child {
|
||||
margin-bottom: 0; }
|
||||
dl dd {
|
||||
margin: 0 0 15px;
|
||||
padding: 0 15px; }
|
||||
dl dd > :first-child {
|
||||
margin-top: 0; }
|
||||
dl dd > :last-child {
|
||||
margin-bottom: 0; }
|
||||
|
||||
blockquote {
|
||||
border-left: 4px solid #404040;
|
||||
padding: 0 15px;
|
||||
color: #888888; }
|
||||
blockquote > :first-child {
|
||||
margin-top: 0; }
|
||||
blockquote > :last-child {
|
||||
margin-bottom: 0; }
|
||||
|
||||
table {
|
||||
padding: 0; }
|
||||
table tr {
|
||||
border-top: 1px solid #707070;
|
||||
background-color: #303030;
|
||||
margin: 0;
|
||||
padding: 0; }
|
||||
table tr:nth-child(2n) {
|
||||
background-color: #505050; }
|
||||
table tr th {
|
||||
font-weight: bold;
|
||||
border: 1px solid #707070;
|
||||
text-align: left;
|
||||
margin: 0;
|
||||
padding: 6px 13px; }
|
||||
table tr td {
|
||||
border: 1px solid #707070;
|
||||
text-align: left;
|
||||
margin: 0;
|
||||
padding: 6px 13px; }
|
||||
table tr th :first-child, table tr td :first-child {
|
||||
margin-top: 0; }
|
||||
table tr th :last-child, table tr td :last-child {
|
||||
margin-bottom: 0; }
|
||||
|
||||
img {
|
||||
max-width: 100%; }
|
||||
|
||||
span.frame {
|
||||
display: block;
|
||||
overflow: hidden; }
|
||||
span.frame > span {
|
||||
border: 1px solid #dddddd;
|
||||
display: block;
|
||||
float: left;
|
||||
overflow: hidden;
|
||||
margin: 13px 0 0;
|
||||
padding: 7px;
|
||||
width: auto; }
|
||||
span.frame span img {
|
||||
display: block;
|
||||
float: left; }
|
||||
span.frame span span {
|
||||
clear: both;
|
||||
color: #cccccc;
|
||||
display: block;
|
||||
padding: 5px 0 0; }
|
||||
span.align-center {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
clear: both; }
|
||||
span.align-center > span {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
margin: 13px auto 0;
|
||||
text-align: center; }
|
||||
span.align-center span img {
|
||||
margin: 0 auto;
|
||||
text-align: center; }
|
||||
span.align-right {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
clear: both; }
|
||||
span.align-right > span {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
margin: 13px 0 0;
|
||||
text-align: right; }
|
||||
span.align-right span img {
|
||||
margin: 0;
|
||||
text-align: right; }
|
||||
span.float-left {
|
||||
display: block;
|
||||
margin-right: 13px;
|
||||
overflow: hidden;
|
||||
float: left; }
|
||||
span.float-left span {
|
||||
margin: 13px 0 0; }
|
||||
span.float-right {
|
||||
display: block;
|
||||
margin-left: 13px;
|
||||
overflow: hidden;
|
||||
float: right; }
|
||||
span.float-right > span {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
margin: 13px auto 0;
|
||||
text-align: right; }
|
||||
|
||||
code, tt {
|
||||
margin: 0 2px;
|
||||
padding: 0 5px;
|
||||
white-space: nowrap;
|
||||
border: 1px solid #707070;
|
||||
background-color: #606060;
|
||||
border-radius: 3px; }
|
||||
|
||||
pre code {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
white-space: pre;
|
||||
border: none;
|
||||
background: transparent; }
|
||||
|
||||
.highlight pre {
|
||||
background-color: #3f3f3f;
|
||||
border: 1px solid #707070;
|
||||
font-size: 13px;
|
||||
line-height: 19px;
|
||||
overflow: auto;
|
||||
padding: 6px 10px;
|
||||
border-radius: 3px; }
|
||||
|
||||
pre {
|
||||
background-color: #606060;
|
||||
border: 1px solid #707070;
|
||||
font-size: 13px;
|
||||
line-height: 19px;
|
||||
overflow: auto;
|
||||
padding: 6px 10px;
|
||||
border-radius: 3px; }
|
||||
pre code, pre tt {
|
||||
background-color: transparent;
|
||||
border: none; }
|
277
app/src/main/assets/light.css
Normal file
@@ -0,0 +1,277 @@
|
||||
body {
|
||||
font-family: Helvetica, arial, sans-serif;
|
||||
font-size: 14px;
|
||||
line-height: 1.6;
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
background-color: white;
|
||||
padding: 15px; }
|
||||
|
||||
body > *:first-child {
|
||||
margin-top: 0 !important; }
|
||||
body > *:last-child {
|
||||
margin-bottom: 0 !important; }
|
||||
|
||||
a {
|
||||
color: #4183C4; }
|
||||
a.absent {
|
||||
color: #cc0000; }
|
||||
a.anchor {
|
||||
display: block;
|
||||
padding-left: 30px;
|
||||
margin-left: -30px;
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0; }
|
||||
|
||||
h1, h2, h3, h4, h5, h6 {
|
||||
margin: 20px 0 10px;
|
||||
padding: 0;
|
||||
font-weight: bold;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
cursor: text;
|
||||
position: relative; }
|
||||
|
||||
h1:hover a.anchor, h2:hover a.anchor, h3:hover a.anchor, h4:hover a.anchor, h5:hover a.anchor, h6:hover a.anchor {
|
||||
background: url("../../images/modules/styleguide/para.png") no-repeat 10px center;
|
||||
text-decoration: none; }
|
||||
|
||||
h1 tt, h1 code {
|
||||
font-size: inherit; }
|
||||
|
||||
h2 tt, h2 code {
|
||||
font-size: inherit; }
|
||||
|
||||
h3 tt, h3 code {
|
||||
font-size: inherit; }
|
||||
|
||||
h4 tt, h4 code {
|
||||
font-size: inherit; }
|
||||
|
||||
h5 tt, h5 code {
|
||||
font-size: inherit; }
|
||||
|
||||
h6 tt, h6 code {
|
||||
font-size: inherit; }
|
||||
|
||||
h1 {
|
||||
font-size: 28px;
|
||||
color: black; }
|
||||
|
||||
h2 {
|
||||
font-size: 24px;
|
||||
border-bottom: 1px solid #cccccc;
|
||||
color: black; }
|
||||
|
||||
h3 {
|
||||
font-size: 18px; }
|
||||
|
||||
h4 {
|
||||
font-size: 16px; }
|
||||
|
||||
h5 {
|
||||
font-size: 14px; }
|
||||
|
||||
h6 {
|
||||
color: #777777;
|
||||
font-size: 14px; }
|
||||
|
||||
p, blockquote, ul, ol, dl, li, table, pre {
|
||||
margin: 15px 0; }
|
||||
|
||||
hr {
|
||||
background: transparent url("../../images/modules/pulls/dirty-shade.png") repeat-x 0 0;
|
||||
border: 0 none;
|
||||
color: #cccccc;
|
||||
height: 4px;
|
||||
padding: 0; }
|
||||
|
||||
body > h2:first-child {
|
||||
margin-top: 0;
|
||||
padding-top: 0; }
|
||||
body > h1:first-child {
|
||||
margin-top: 0;
|
||||
padding-top: 0; }
|
||||
body > h1:first-child + h2 {
|
||||
margin-top: 0;
|
||||
padding-top: 0; }
|
||||
body > h3:first-child, body > h4:first-child, body > h5:first-child, body > h6:first-child {
|
||||
margin-top: 0;
|
||||
padding-top: 0; }
|
||||
|
||||
a:first-child h1, a:first-child h2, a:first-child h3, a:first-child h4, a:first-child h5, a:first-child h6 {
|
||||
margin-top: 0;
|
||||
padding-top: 0; }
|
||||
|
||||
h1 p, h2 p, h3 p, h4 p, h5 p, h6 p {
|
||||
margin-top: 0; }
|
||||
|
||||
li p.first {
|
||||
display: inline-block; }
|
||||
|
||||
ul, ol {
|
||||
padding-left: 30px; }
|
||||
|
||||
ul :first-child, ol :first-child {
|
||||
margin-top: 0; }
|
||||
|
||||
ul :last-child, ol :last-child {
|
||||
margin-bottom: 0; }
|
||||
|
||||
dl {
|
||||
padding: 0; }
|
||||
dl dt {
|
||||
font-size: 14px;
|
||||
font-weight: bold;
|
||||
font-style: italic;
|
||||
padding: 0;
|
||||
margin: 15px 0 5px; }
|
||||
dl dt:first-child {
|
||||
padding: 0; }
|
||||
dl dt > :first-child {
|
||||
margin-top: 0; }
|
||||
dl dt > :last-child {
|
||||
margin-bottom: 0; }
|
||||
dl dd {
|
||||
margin: 0 0 15px;
|
||||
padding: 0 15px; }
|
||||
dl dd > :first-child {
|
||||
margin-top: 0; }
|
||||
dl dd > :last-child {
|
||||
margin-bottom: 0; }
|
||||
|
||||
blockquote {
|
||||
border-left: 4px solid #dddddd;
|
||||
padding: 0 15px;
|
||||
color: #777777; }
|
||||
blockquote > :first-child {
|
||||
margin-top: 0; }
|
||||
blockquote > :last-child {
|
||||
margin-bottom: 0; }
|
||||
|
||||
table {
|
||||
padding: 0; }
|
||||
table tr {
|
||||
border-top: 1px solid #cccccc;
|
||||
background-color: white;
|
||||
margin: 0;
|
||||
padding: 0; }
|
||||
table tr:nth-child(2n) {
|
||||
background-color: #f8f8f8; }
|
||||
table tr th {
|
||||
font-weight: bold;
|
||||
border: 1px solid #cccccc;
|
||||
text-align: left;
|
||||
margin: 0;
|
||||
padding: 6px 13px; }
|
||||
table tr td {
|
||||
border: 1px solid #cccccc;
|
||||
text-align: left;
|
||||
margin: 0;
|
||||
padding: 6px 13px; }
|
||||
table tr th :first-child, table tr td :first-child {
|
||||
margin-top: 0; }
|
||||
table tr th :last-child, table tr td :last-child {
|
||||
margin-bottom: 0; }
|
||||
|
||||
img {
|
||||
max-width: 100%; }
|
||||
|
||||
span.frame {
|
||||
display: block;
|
||||
overflow: hidden; }
|
||||
span.frame > span {
|
||||
border: 1px solid #dddddd;
|
||||
display: block;
|
||||
float: left;
|
||||
overflow: hidden;
|
||||
margin: 13px 0 0;
|
||||
padding: 7px;
|
||||
width: auto; }
|
||||
span.frame span img {
|
||||
display: block;
|
||||
float: left; }
|
||||
span.frame span span {
|
||||
clear: both;
|
||||
color: #333333;
|
||||
display: block;
|
||||
padding: 5px 0 0; }
|
||||
span.align-center {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
clear: both; }
|
||||
span.align-center > span {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
margin: 13px auto 0;
|
||||
text-align: center; }
|
||||
span.align-center span img {
|
||||
margin: 0 auto;
|
||||
text-align: center; }
|
||||
span.align-right {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
clear: both; }
|
||||
span.align-right > span {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
margin: 13px 0 0;
|
||||
text-align: right; }
|
||||
span.align-right span img {
|
||||
margin: 0;
|
||||
text-align: right; }
|
||||
span.float-left {
|
||||
display: block;
|
||||
margin-right: 13px;
|
||||
overflow: hidden;
|
||||
float: left; }
|
||||
span.float-left span {
|
||||
margin: 13px 0 0; }
|
||||
span.float-right {
|
||||
display: block;
|
||||
margin-left: 13px;
|
||||
overflow: hidden;
|
||||
float: right; }
|
||||
span.float-right > span {
|
||||
display: block;
|
||||
overflow: hidden;
|
||||
margin: 13px auto 0;
|
||||
text-align: right; }
|
||||
|
||||
code, tt {
|
||||
margin: 0 2px;
|
||||
padding: 0 5px;
|
||||
white-space: nowrap;
|
||||
border: 1px solid #eaeaea;
|
||||
background-color: #f8f8f8;
|
||||
border-radius: 3px; }
|
||||
|
||||
pre code {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
white-space: pre;
|
||||
border: none;
|
||||
background: transparent; }
|
||||
|
||||
.highlight pre {
|
||||
background-color: #f8f8f8;
|
||||
border: 1px solid #cccccc;
|
||||
font-size: 13px;
|
||||
line-height: 19px;
|
||||
overflow: auto;
|
||||
padding: 6px 10px;
|
||||
border-radius: 3px; }
|
||||
|
||||
pre {
|
||||
background-color: #f8f8f8;
|
||||
border: 1px solid #cccccc;
|
||||
font-size: 13px;
|
||||
line-height: 19px;
|
||||
overflow: auto;
|
||||
padding: 6px 10px;
|
||||
border-radius: 3px; }
|
||||
pre code, pre tt {
|
||||
background-color: transparent;
|
||||
border: none; }
|
Before Width: | Height: | Size: 42 KiB After Width: | Height: | Size: 73 KiB |
@@ -1,13 +1,12 @@
|
||||
package com.topjohnwu.magisk;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.text.Html;
|
||||
import android.text.Spanned;
|
||||
@@ -17,8 +16,10 @@ import android.view.View;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.topjohnwu.magisk.components.AboutCardRow;
|
||||
import com.topjohnwu.magisk.components.Activity;
|
||||
import com.topjohnwu.magisk.components.AlertDialogBuilder;
|
||||
import com.topjohnwu.magisk.utils.Logger;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
@@ -26,7 +27,7 @@ import java.io.InputStream;
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
|
||||
public class AboutActivity extends AppCompatActivity {
|
||||
public class AboutActivity extends Activity {
|
||||
|
||||
private static final String DONATION_URL = "http://topjohnwu.github.io/donate";
|
||||
private static final String XDA_THREAD = "http://forum.xda-developers.com/showthread.php?t=3432382";
|
||||
@@ -46,8 +47,8 @@ public class AboutActivity extends AppCompatActivity {
|
||||
super.onCreate(savedInstanceState);
|
||||
String theme = PreferenceManager.getDefaultSharedPreferences(getApplicationContext()).getString("theme", "");
|
||||
Logger.dev("AboutActivity: Theme is " + theme);
|
||||
if (Global.Configs.isDarkTheme) {
|
||||
setTheme(R.style.AppTheme_dh);
|
||||
if (getApplicationContext().isDarkTheme) {
|
||||
setTheme(R.style.AppTheme_Dark);
|
||||
}
|
||||
setContentView(R.layout.activity_about);
|
||||
ButterKnife.bind(this);
|
||||
@@ -85,7 +86,7 @@ public class AboutActivity extends AppCompatActivity {
|
||||
result = Html.fromHtml(changes);
|
||||
}
|
||||
appChangelog.setOnClickListener(v -> {
|
||||
AlertDialog d = Utils.getAlertDialogBuilder(this)
|
||||
AlertDialog d = new AlertDialogBuilder(this)
|
||||
.setTitle(R.string.app_changelog)
|
||||
.setMessage(result)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
@@ -104,7 +105,7 @@ public class AboutActivity extends AppCompatActivity {
|
||||
} else {
|
||||
result = Html.fromHtml(getString(R.string.app_developers_));
|
||||
}
|
||||
AlertDialog d = Utils.getAlertDialogBuilder(this)
|
||||
AlertDialog d = new AlertDialogBuilder(this)
|
||||
.setTitle(R.string.app_developers)
|
||||
.setMessage(result)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
|
@@ -1,133 +0,0 @@
|
||||
package com.topjohnwu.magisk;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.SparseArray;
|
||||
|
||||
import com.topjohnwu.magisk.module.Module;
|
||||
import com.topjohnwu.magisk.module.Repo;
|
||||
import com.topjohnwu.magisk.utils.CallbackHandler;
|
||||
import com.topjohnwu.magisk.utils.Shell;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
import com.topjohnwu.magisk.utils.ValueSortedMap;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
public class Global {
|
||||
|
||||
public static final String MAGISK_DISABLE_FILE = "/cache/.disable_magisk";
|
||||
|
||||
public static class Info {
|
||||
public static double magiskVersion;
|
||||
public static String magiskVersionString = "(none)";
|
||||
public static double remoteMagiskVersion = -1;
|
||||
public static String magiskLink;
|
||||
public static String releaseNoteLink;
|
||||
public static int SNCheckResult = -1;
|
||||
public static String bootBlock = null;
|
||||
public static boolean isSuClient = false;
|
||||
public static String suVersion = null;
|
||||
public static boolean disabled = false;
|
||||
}
|
||||
public static class Data {
|
||||
public static ValueSortedMap<String, Repo> repoMap;
|
||||
public static ValueSortedMap<String, Module> moduleMap;
|
||||
public static List<String> blockList;
|
||||
public static List<ApplicationInfo> appList;
|
||||
public static List<String> magiskHideList;
|
||||
}
|
||||
public static class Events {
|
||||
public static final CallbackHandler.Event blockDetectionDone = new CallbackHandler.Event();
|
||||
public static final CallbackHandler.Event packageLoadDone = new CallbackHandler.Event();
|
||||
public static final CallbackHandler.Event reloadMainActivity = new CallbackHandler.Event();
|
||||
public static final CallbackHandler.Event moduleLoadDone = new CallbackHandler.Event();
|
||||
public static final CallbackHandler.Event repoLoadDone = new CallbackHandler.Event();
|
||||
public static final CallbackHandler.Event updateCheckDone = new CallbackHandler.Event();
|
||||
public static final CallbackHandler.Event safetyNetDone = new CallbackHandler.Event();
|
||||
public static SparseArray<CallbackHandler.Event> uidMap = new SparseArray<>();
|
||||
}
|
||||
public static class Configs {
|
||||
public static boolean isDarkTheme;
|
||||
public static boolean shellLogging;
|
||||
public static boolean devLogging;
|
||||
public static boolean magiskHide;
|
||||
public static int suRequestTimeout;
|
||||
public static int suLogTimeout = 14;
|
||||
public static int suAccessState;
|
||||
public static int suResponseType;
|
||||
public static int suNotificationType;
|
||||
}
|
||||
|
||||
public static void init(Context context) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
Configs.isDarkTheme = prefs.getBoolean("dark_theme", false);
|
||||
Configs.devLogging = prefs.getBoolean("developer_logging", false);
|
||||
Configs.shellLogging = prefs.getBoolean("shell_logging", false);
|
||||
Configs.magiskHide = prefs.getBoolean("magiskhide", false);
|
||||
updateMagiskInfo();
|
||||
initSuAccess();
|
||||
initSuConfigs(context);
|
||||
// Initialize prefs
|
||||
prefs.edit()
|
||||
.putBoolean("dark_theme", Configs.isDarkTheme)
|
||||
.putBoolean("magiskhide", Configs.magiskHide)
|
||||
.putBoolean("busybox", Utils.commandExists("busybox"))
|
||||
.putBoolean("hosts", new File("/magisk/.core/hosts").exists())
|
||||
.putBoolean("disable", Utils.itemExist(MAGISK_DISABLE_FILE))
|
||||
.putString("su_request_timeout", String.valueOf(Configs.suRequestTimeout))
|
||||
.putString("su_auto_response", String.valueOf(Configs.suResponseType))
|
||||
.putString("su_notification", String.valueOf(Configs.suNotificationType))
|
||||
.putString("su_access", String.valueOf(Configs.suAccessState))
|
||||
.apply();
|
||||
}
|
||||
|
||||
public static void initSuConfigs(Context context) {
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
Configs.suRequestTimeout = Utils.getPrefsInt(prefs, "su_request_timeout", 10);
|
||||
Configs.suResponseType = Utils.getPrefsInt(prefs, "su_auto_response", 0);
|
||||
Configs.suNotificationType = Utils.getPrefsInt(prefs, "su_notification", 1);
|
||||
}
|
||||
|
||||
public static void initSuAccess() {
|
||||
List<String> ret = Shell.sh("su -v");
|
||||
if (Utils.isValidShellResponse(ret)) {
|
||||
Info.suVersion = ret.get(0);
|
||||
Info.isSuClient = Info.suVersion.toUpperCase().contains("MAGISK");
|
||||
}
|
||||
if (Info.isSuClient) {
|
||||
ret = Shell.sh("getprop persist.sys.root_access");
|
||||
if (Utils.isValidShellResponse(ret))
|
||||
Configs.suAccessState = Integer.parseInt(ret.get(0));
|
||||
else {
|
||||
Shell.su(true, "setprop persist.sys.root_access 3");
|
||||
Configs.suAccessState = 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void updateMagiskInfo() {
|
||||
List<String> ret = Shell.sh("getprop magisk.version");
|
||||
if (!Utils.isValidShellResponse(ret)) {
|
||||
Info.magiskVersion = -1;
|
||||
} else {
|
||||
try {
|
||||
Info.magiskVersionString = ret.get(0);
|
||||
Info.magiskVersion = Double.parseDouble(ret.get(0));
|
||||
} catch (NumberFormatException e) {
|
||||
// Custom version don't need to receive updates
|
||||
Info.magiskVersion = Double.POSITIVE_INFINITY;
|
||||
}
|
||||
}
|
||||
ret = Shell.sh("getprop ro.magisk.disable");
|
||||
try {
|
||||
Info.disabled = Utils.isValidShellResponse(ret) && Integer.parseInt(ret.get(0)) != 0;
|
||||
} catch (NumberFormatException e) {
|
||||
Info.disabled = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -6,7 +6,7 @@ import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.CountDownTimer;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.design.widget.Snackbar;
|
||||
import android.support.v7.widget.CardView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -17,8 +17,12 @@ import android.widget.CheckBox;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.topjohnwu.magisk.receivers.MagiskDlReceiver;
|
||||
import com.topjohnwu.magisk.utils.CallbackHandler;
|
||||
import com.topjohnwu.magisk.asyncs.ProcessMagiskZip;
|
||||
import com.topjohnwu.magisk.components.AlertDialogBuilder;
|
||||
import com.topjohnwu.magisk.components.Fragment;
|
||||
import com.topjohnwu.magisk.components.SnackbarMaker;
|
||||
import com.topjohnwu.magisk.receivers.DownloadReceiver;
|
||||
import com.topjohnwu.magisk.utils.CallbackEvent;
|
||||
import com.topjohnwu.magisk.utils.Shell;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
|
||||
@@ -28,128 +32,174 @@ import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.OnClick;
|
||||
import butterknife.Unbinder;
|
||||
|
||||
public class InstallFragment extends Fragment implements CallbackHandler.EventListener {
|
||||
public class InstallFragment extends Fragment implements CallbackEvent.Listener<Void> {
|
||||
|
||||
|
||||
private static final String UNINSTALLER = "magisk_uninstaller.sh";
|
||||
|
||||
private Unbinder unbinder;
|
||||
@BindView(R.id.current_version_title) TextView currentVersionTitle;
|
||||
@BindView(R.id.install_title) TextView installTitle;
|
||||
@BindView(R.id.block_spinner) Spinner spinner;
|
||||
@BindView(R.id.detect_bootimage) Button detectButton;
|
||||
@BindView(R.id.flash_button) CardView flashButton;
|
||||
@BindView(R.id.install_button) CardView installButton;
|
||||
@BindView(R.id.install_text) TextView installText;
|
||||
@BindView(R.id.uninstall_button) CardView uninstallButton;
|
||||
@BindView(R.id.keep_force_enc) CheckBox keepEncChkbox;
|
||||
@BindView(R.id.keep_verity) CheckBox keepVerityChkbox;
|
||||
|
||||
@OnClick(R.id.detect_bootimage)
|
||||
public void toAutoDetect() {
|
||||
if (magiskManager.bootBlock != null) {
|
||||
spinner.setSelection(0);
|
||||
}
|
||||
}
|
||||
|
||||
@OnClick(R.id.install_button)
|
||||
public void install() {
|
||||
String bootImage = null;
|
||||
if (magiskManager.blockList != null) {
|
||||
int idx = spinner.getSelectedItemPosition();
|
||||
if (magiskManager.bootBlock != null) {
|
||||
if (idx > 0) {
|
||||
bootImage = magiskManager.blockList.get(idx - 1);
|
||||
}
|
||||
} else {
|
||||
if (idx > 0) {
|
||||
bootImage = magiskManager.blockList.get(idx - 1);
|
||||
} else {
|
||||
SnackbarMaker.make(getActivity(), R.string.manual_boot_image, Snackbar.LENGTH_LONG);
|
||||
}
|
||||
}
|
||||
}
|
||||
final String finalBootImage = bootImage;
|
||||
String filename = "Magisk-v" + magiskManager.remoteMagiskVersion + ".zip";
|
||||
new AlertDialogBuilder(getActivity())
|
||||
.setTitle(getString(R.string.repo_install_title, getString(R.string.magisk)))
|
||||
.setMessage(getString(R.string.repo_install_msg, filename))
|
||||
.setCancelable(true)
|
||||
.setPositiveButton(Shell.rootAccess() ? R.string.install : R.string.download,
|
||||
(dialogInterface, i) -> Utils.dlAndReceive(
|
||||
getActivity(),
|
||||
new DownloadReceiver() {
|
||||
private String boot = finalBootImage;
|
||||
private boolean enc = keepEncChkbox.isChecked();
|
||||
private boolean verity = keepVerityChkbox.isChecked();
|
||||
|
||||
@Override
|
||||
public void onDownloadDone(Uri uri) {
|
||||
new ProcessMagiskZip(getActivity(), uri, boot, enc, verity).exec();
|
||||
}
|
||||
},
|
||||
magiskManager.magiskLink,
|
||||
Utils.getLegalFilename(filename)))
|
||||
.setNeutralButton(R.string.release_notes, (dialog, which) -> {
|
||||
magiskManager.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(magiskManager.releaseNoteLink)));
|
||||
})
|
||||
.setNegativeButton(R.string.no_thanks, null)
|
||||
.show();
|
||||
}
|
||||
|
||||
@OnClick(R.id.uninstall_button)
|
||||
public void uninstall() {
|
||||
new AlertDialogBuilder(getActivity())
|
||||
.setTitle(R.string.uninstall_magisk_title)
|
||||
.setMessage(R.string.uninstall_magisk_msg)
|
||||
.setPositiveButton(R.string.yes, (dialogInterface, i) -> {
|
||||
try {
|
||||
InputStream in = magiskManager.getAssets().open(UNINSTALLER);
|
||||
File uninstaller = new File(magiskManager.getCacheDir(), UNINSTALLER);
|
||||
FileOutputStream out = new FileOutputStream(uninstaller);
|
||||
byte[] bytes = new byte[1024];
|
||||
int read;
|
||||
while ((read = in.read(bytes)) != -1) {
|
||||
out.write(bytes, 0, read);
|
||||
}
|
||||
in.close();
|
||||
out.close();
|
||||
ProgressDialog progress = new ProgressDialog(getActivity());
|
||||
progress.setTitle(R.string.reboot);
|
||||
progress.show();
|
||||
new CountDownTimer(5000, 1000) {
|
||||
@Override
|
||||
public void onTick(long millisUntilFinished) {
|
||||
progress.setMessage(getString(R.string.reboot_countdown, millisUntilFinished / 1000));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
progress.setMessage(getString(R.string.reboot_countdown, 0));
|
||||
Shell.su(true, "mv -f " + uninstaller + " /cache/" + UNINSTALLER,
|
||||
"reboot");
|
||||
}
|
||||
}.start();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.no_thanks, null)
|
||||
.show();
|
||||
}
|
||||
|
||||
private Unbinder unbinder;
|
||||
private MagiskManager magiskManager;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View v = inflater.inflate(R.layout.fragment_install, container, false);
|
||||
unbinder = ButterKnife.bind(this, v);
|
||||
detectButton.setOnClickListener(v1 -> toAutoDetect());
|
||||
currentVersionTitle.setText(getString(R.string.current_magisk_title, Global.Info.magiskVersionString));
|
||||
installTitle.setText(getString(R.string.install_magisk_title, Global.Info.remoteMagiskVersion));
|
||||
flashButton.setOnClickListener(v1 -> {
|
||||
String bootImage;
|
||||
if (Global.Info.bootBlock != null) {
|
||||
if (spinner.getSelectedItemPosition() > 0)
|
||||
bootImage = Global.Data.blockList.get(spinner.getSelectedItemPosition() - 1);
|
||||
else
|
||||
bootImage = Global.Info.bootBlock;
|
||||
} else {
|
||||
bootImage = Global.Data.blockList.get(spinner.getSelectedItemPosition());
|
||||
}
|
||||
String filename = "Magisk-v" + Global.Info.remoteMagiskVersion + ".zip";
|
||||
Utils.getAlertDialogBuilder(getActivity())
|
||||
.setTitle(getString(R.string.repo_install_title, getString(R.string.magisk)))
|
||||
.setMessage(getString(R.string.repo_install_msg, filename))
|
||||
.setCancelable(true)
|
||||
.setPositiveButton(R.string.download_install, (dialogInterface, i) -> Utils.dlAndReceive(
|
||||
getActivity(),
|
||||
new MagiskDlReceiver(bootImage, keepEncChkbox.isChecked(), keepVerityChkbox.isChecked()),
|
||||
Global.Info.magiskLink,
|
||||
Utils.getLegalFilename(filename)))
|
||||
.setNeutralButton(R.string.check_release_notes, (dialog, which) -> {
|
||||
getActivity().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(Global.Info.releaseNoteLink)));
|
||||
})
|
||||
.setNegativeButton(R.string.no_thanks, null)
|
||||
.show();
|
||||
});
|
||||
if (Global.Info.magiskVersion < 10.3) {
|
||||
uninstallButton.setVisibility(View.GONE);
|
||||
magiskManager = getApplication();
|
||||
if (magiskManager.magiskVersion < 0) {
|
||||
currentVersionTitle.setText(getString(R.string.current_magisk_title, getString(R.string.version_none)));
|
||||
} else {
|
||||
uninstallButton.setOnClickListener(vi -> {
|
||||
Utils.getAlertDialogBuilder(getActivity())
|
||||
.setTitle("Uninstall Magisk")
|
||||
.setMessage("This will remove all modules, MagiskSU, and potentially re-encrypt your device\nAre you sure to process?")
|
||||
.setPositiveButton(R.string.yes, (dialogInterface, i) -> {
|
||||
try {
|
||||
InputStream in = getActivity().getAssets().open(UNINSTALLER);
|
||||
File uninstaller = new File(getActivity().getCacheDir().getAbsolutePath() + "/" + UNINSTALLER);
|
||||
FileOutputStream out = new FileOutputStream(uninstaller);
|
||||
byte[] bytes = new byte[1024];
|
||||
int read;
|
||||
while ((read = in.read(bytes)) != -1)
|
||||
out.write(bytes, 0, read);
|
||||
in.close();
|
||||
out.close();
|
||||
ProgressDialog progress = new ProgressDialog(getActivity());
|
||||
progress.setTitle(R.string.reboot);
|
||||
progress.show();
|
||||
new CountDownTimer(5000, 1000) {
|
||||
@Override
|
||||
public void onTick(long millisUntilFinished) {
|
||||
progress.setMessage(getString(R.string.reboot_countdown, millisUntilFinished / 1000));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
progress.setMessage(getString(R.string.reboot_countdown, 0));
|
||||
Shell.su(true, "cp -af " + uninstaller + " /cache/" + UNINSTALLER,
|
||||
"reboot");
|
||||
}
|
||||
}.start();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
})
|
||||
.setNegativeButton(R.string.no_thanks, null)
|
||||
.show();
|
||||
});
|
||||
currentVersionTitle.setText(getString(R.string.current_magisk_title, "v" + magiskManager.magiskVersionString));
|
||||
}
|
||||
installTitle.setText(getString(R.string.install_magisk_title, "v" + String.format(Locale.US, "%.1f", magiskManager.remoteMagiskVersion)));
|
||||
|
||||
if (Global.Events.blockDetectionDone.isTriggered) {
|
||||
updateUI();
|
||||
}
|
||||
updateUI();
|
||||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrigger(CallbackHandler.Event event) {
|
||||
public void onTrigger(CallbackEvent<Void> event) {
|
||||
updateUI();
|
||||
}
|
||||
|
||||
private void updateUI() {
|
||||
List<String> items = new ArrayList<>(Global.Data.blockList);
|
||||
if (Global.Info.bootBlock != null)
|
||||
items.add(0, getString(R.string.auto_detect, Global.Info.bootBlock));
|
||||
ArrayAdapter<String> adapter = new ArrayAdapter<>(getActivity(),
|
||||
android.R.layout.simple_spinner_item, items);
|
||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
spinner.setAdapter(adapter);
|
||||
toAutoDetect();
|
||||
}
|
||||
if (magiskManager.blockList == null || !Shell.rootAccess()) {
|
||||
uninstallButton.setVisibility(View.GONE);
|
||||
installText.setText(R.string.download);
|
||||
detectButton.setEnabled(false);
|
||||
keepEncChkbox.setEnabled(false);
|
||||
keepVerityChkbox.setEnabled(false);
|
||||
spinner.setEnabled(false);
|
||||
} else {
|
||||
uninstallButton.setVisibility(magiskManager.magiskVersion > 10.3 ? View.VISIBLE : View.GONE);
|
||||
installText.setText(R.string.download_install);
|
||||
detectButton.setEnabled(true);
|
||||
keepEncChkbox.setEnabled(true);
|
||||
keepVerityChkbox.setEnabled(true);
|
||||
spinner.setEnabled(true);
|
||||
|
||||
private void toAutoDetect() {
|
||||
if (Global.Info.bootBlock != null) {
|
||||
spinner.setSelection(0);
|
||||
List<String> items = new ArrayList<>();
|
||||
if (magiskManager.bootBlock != null) {
|
||||
items.add(getString(R.string.auto_detect, magiskManager.bootBlock));
|
||||
} else {
|
||||
items.add(getString(R.string.cannot_auto_detect));
|
||||
}
|
||||
items.addAll(magiskManager.blockList);
|
||||
ArrayAdapter<String> adapter = new ArrayAdapter<>(getActivity(),
|
||||
android.R.layout.simple_spinner_item, items);
|
||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
spinner.setAdapter(adapter);
|
||||
toAutoDetect();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -157,12 +207,12 @@ public class InstallFragment extends Fragment implements CallbackHandler.EventLi
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
getActivity().setTitle(R.string.install);
|
||||
CallbackHandler.register(Global.Events.blockDetectionDone, this);
|
||||
magiskManager.blockDetectionDone.register(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
CallbackHandler.unRegister(Global.Events.blockDetectionDone, this);
|
||||
magiskManager.blockDetectionDone.unRegister(this);
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
|
@@ -3,13 +3,13 @@ package com.topjohnwu.magisk;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.design.widget.TabLayout;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.view.ViewPager;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import com.topjohnwu.magisk.adapters.TabFragmentAdapter;
|
||||
import com.topjohnwu.magisk.components.Fragment;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
@@ -33,7 +33,7 @@ public class LogFragment extends Fragment {
|
||||
|
||||
adapter.addTab(new MagiskLogFragment(), getString(R.string.magisk));
|
||||
|
||||
if (Global.Info.isSuClient) {
|
||||
if (getApplication().isSuClient) {
|
||||
adapter.addTab(new SuLogFragment(), getString(R.string.superuser));
|
||||
tab.setupWithViewPager(viewPager);
|
||||
tab.setVisibility(View.VISIBLE);
|
||||
|
@@ -3,7 +3,6 @@ package com.topjohnwu.magisk;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.view.MenuItemCompat;
|
||||
import android.support.v4.widget.SwipeRefreshLayout;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
@@ -16,15 +15,16 @@ import android.view.ViewGroup;
|
||||
import android.widget.SearchView;
|
||||
|
||||
import com.topjohnwu.magisk.adapters.ApplicationAdapter;
|
||||
import com.topjohnwu.magisk.utils.Async;
|
||||
import com.topjohnwu.magisk.utils.CallbackHandler;
|
||||
import com.topjohnwu.magisk.asyncs.MagiskHide;
|
||||
import com.topjohnwu.magisk.components.Fragment;
|
||||
import com.topjohnwu.magisk.utils.CallbackEvent;
|
||||
import com.topjohnwu.magisk.utils.Logger;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.Unbinder;
|
||||
|
||||
public class MagiskHideFragment extends Fragment implements CallbackHandler.EventListener {
|
||||
public class MagiskHideFragment extends Fragment implements CallbackEvent.Listener<Void> {
|
||||
|
||||
private Unbinder unbinder;
|
||||
@BindView(R.id.swipeRefreshLayout) SwipeRefreshLayout mSwipeRefreshLayout;
|
||||
@@ -50,7 +50,7 @@ public class MagiskHideFragment extends Fragment implements CallbackHandler.Even
|
||||
PackageManager packageManager = getActivity().getPackageManager();
|
||||
|
||||
mSwipeRefreshLayout.setRefreshing(true);
|
||||
mSwipeRefreshLayout.setOnRefreshListener(() -> new Async.LoadApps(packageManager).exec());
|
||||
mSwipeRefreshLayout.setOnRefreshListener(() -> new MagiskHide(getActivity()).list());
|
||||
|
||||
appAdapter = new ApplicationAdapter(packageManager);
|
||||
recyclerView.setAdapter(appAdapter);
|
||||
@@ -71,6 +71,10 @@ public class MagiskHideFragment extends Fragment implements CallbackHandler.Even
|
||||
}
|
||||
};
|
||||
|
||||
if (getApplication().magiskHideDone.isTriggered) {
|
||||
onTrigger(getApplication().magiskHideDone);
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@@ -85,15 +89,12 @@ public class MagiskHideFragment extends Fragment implements CallbackHandler.Even
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
getActivity().setTitle(R.string.magiskhide);
|
||||
CallbackHandler.register(Global.Events.packageLoadDone, this);
|
||||
if (Global.Events.packageLoadDone.isTriggered) {
|
||||
onTrigger(Global.Events.packageLoadDone);
|
||||
}
|
||||
getApplication().magiskHideDone.register(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
CallbackHandler.unRegister(Global.Events.packageLoadDone, this);
|
||||
getApplication().magiskHideDone.unRegister(this);
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
@@ -104,9 +105,9 @@ public class MagiskHideFragment extends Fragment implements CallbackHandler.Even
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrigger(CallbackHandler.Event event) {
|
||||
public void onTrigger(CallbackEvent<Void> event) {
|
||||
Logger.dev("MagiskHideFragment: UI refresh");
|
||||
appAdapter.setLists(Global.Data.appList, Global.Data.magiskHideList);
|
||||
appAdapter.setLists(getApplication().appList, getApplication().magiskHideList);
|
||||
mSwipeRefreshLayout.setRefreshing(false);
|
||||
if (!TextUtils.isEmpty(lastFilter)) {
|
||||
appAdapter.filter(lastFilter);
|
||||
|
@@ -11,7 +11,6 @@ import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.design.widget.Snackbar;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
@@ -25,7 +24,9 @@ import android.widget.ScrollView;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.topjohnwu.magisk.utils.Async;
|
||||
import com.topjohnwu.magisk.asyncs.SerialTask;
|
||||
import com.topjohnwu.magisk.components.Fragment;
|
||||
import com.topjohnwu.magisk.components.SnackbarMaker;
|
||||
import com.topjohnwu.magisk.utils.Shell;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
|
||||
@@ -121,12 +122,12 @@ public class MagiskLogFragment extends Fragment {
|
||||
new Handler().postDelayed(() -> onOptionsItemSelected(mClickedMenuItem), 500);
|
||||
}
|
||||
} else {
|
||||
Snackbar.make(txtLog, R.string.permissionNotGranted, Snackbar.LENGTH_LONG).show();
|
||||
SnackbarMaker.make(txtLog, R.string.permissionNotGranted, Snackbar.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public class LogManager extends Async.RootTask<Object, Void, Object> {
|
||||
public class LogManager extends SerialTask<Object, Void, Object> {
|
||||
|
||||
int mode;
|
||||
File targetFile;
|
||||
@@ -150,7 +151,7 @@ public class MagiskLogFragment extends Fragment {
|
||||
|
||||
case 1:
|
||||
Shell.su("echo > " + MAGISK_LOG);
|
||||
Snackbar.make(txtLog, R.string.logs_cleared, Snackbar.LENGTH_SHORT).show();
|
||||
SnackbarMaker.make(txtLog, R.string.logs_cleared, Snackbar.LENGTH_SHORT).show();
|
||||
return "";
|
||||
|
||||
case 2:
|
||||
@@ -161,8 +162,9 @@ public class MagiskLogFragment extends Fragment {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED))
|
||||
if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
Calendar now = Calendar.getInstance();
|
||||
String filename = String.format(
|
||||
@@ -174,8 +176,9 @@ public class MagiskLogFragment extends Fragment {
|
||||
targetFile = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/MagiskManager/" + filename);
|
||||
|
||||
if ((!targetFile.getParentFile().exists() && !targetFile.getParentFile().mkdirs())
|
||||
|| (targetFile.exists() && !targetFile.delete()))
|
||||
|| (targetFile.exists() && !targetFile.delete())) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<String> in = Utils.readFile(MAGISK_LOG);
|
||||
|
||||
@@ -213,10 +216,11 @@ public class MagiskLogFragment extends Fragment {
|
||||
break;
|
||||
case 2:
|
||||
bool = (boolean) o;
|
||||
if (bool)
|
||||
if (bool) {
|
||||
Toast.makeText(getActivity(), targetFile.toString(), Toast.LENGTH_LONG).show();
|
||||
else
|
||||
} else {
|
||||
Toast.makeText(getActivity(), getString(R.string.logs_save_failed), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
169
app/src/main/java/com/topjohnwu/magisk/MagiskManager.java
Normal file
@@ -0,0 +1,169 @@
|
||||
package com.topjohnwu.magisk;
|
||||
|
||||
import android.app.Application;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.os.Handler;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.util.SparseArray;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.topjohnwu.magisk.module.Module;
|
||||
import com.topjohnwu.magisk.module.Repo;
|
||||
import com.topjohnwu.magisk.superuser.Policy;
|
||||
import com.topjohnwu.magisk.utils.CallbackEvent;
|
||||
import com.topjohnwu.magisk.utils.Shell;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
import com.topjohnwu.magisk.utils.ValueSortedMap;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
public class MagiskManager extends Application {
|
||||
|
||||
public static final String MAGISK_DISABLE_FILE = "/cache/.disable_magisk";
|
||||
public static final String MAGISK_HIDE_PATH = "/magisk/.core/magiskhide/";
|
||||
public static final String TMP_FOLDER_PATH = "/dev/tmp";
|
||||
public static final String MAGISK_PATH = "/magisk";
|
||||
public static final String INTENT_SECTION = "section";
|
||||
|
||||
// Events
|
||||
public final CallbackEvent<Void> blockDetectionDone = new CallbackEvent<>();
|
||||
public final CallbackEvent<Void> magiskHideDone = new CallbackEvent<>();
|
||||
public final CallbackEvent<Void> reloadMainActivity = new CallbackEvent<>();
|
||||
public final CallbackEvent<Void> moduleLoadDone = new CallbackEvent<>();
|
||||
public final CallbackEvent<Void> repoLoadDone = new CallbackEvent<>();
|
||||
public final CallbackEvent<Void> updateCheckDone = new CallbackEvent<>();
|
||||
public final CallbackEvent<Void> safetyNetDone = new CallbackEvent<>();
|
||||
public final SparseArray<CallbackEvent<Policy>> uidSuRequest = new SparseArray<>();
|
||||
|
||||
// Info
|
||||
public double magiskVersion;
|
||||
public String magiskVersionString;
|
||||
public double remoteMagiskVersion = -1;
|
||||
public String magiskLink;
|
||||
public String releaseNoteLink;
|
||||
public int SNCheckResult = -1;
|
||||
public String bootBlock = null;
|
||||
public boolean isSuClient = false;
|
||||
public String suVersion = null;
|
||||
public boolean disabled;
|
||||
public boolean magiskHideStarted;
|
||||
|
||||
// Data
|
||||
public ValueSortedMap<String, Repo> repoMap;
|
||||
public ValueSortedMap<String, Module> moduleMap;
|
||||
public List<String> blockList;
|
||||
public List<ApplicationInfo> appList;
|
||||
public List<String> magiskHideList;
|
||||
|
||||
// Configurations
|
||||
public static boolean shellLogging;
|
||||
public static boolean devLogging;
|
||||
|
||||
public boolean magiskHide;
|
||||
public boolean isDarkTheme;
|
||||
public int suRequestTimeout;
|
||||
public int suLogTimeout = 14;
|
||||
public int suAccessState;
|
||||
public int suResponseType;
|
||||
public int suNotificationType;
|
||||
|
||||
public SharedPreferences prefs;
|
||||
|
||||
private static Handler mHandler = new Handler();
|
||||
|
||||
@Override
|
||||
public void onCreate() {
|
||||
super.onCreate();
|
||||
prefs = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
}
|
||||
|
||||
public void toast(String msg, int duration) {
|
||||
mHandler.post(() -> Toast.makeText(this, msg, duration).show());
|
||||
}
|
||||
|
||||
public void toast(int resId, int duration) {
|
||||
mHandler.post(() -> Toast.makeText(this, resId, duration).show());
|
||||
}
|
||||
|
||||
public void init() {
|
||||
isDarkTheme = prefs.getBoolean("dark_theme", false);
|
||||
devLogging = prefs.getBoolean("developer_logging", false);
|
||||
shellLogging = prefs.getBoolean("shell_logging", false);
|
||||
magiskHide = prefs.getBoolean("magiskhide", false);
|
||||
// Always start a new root shell manually, just for safety
|
||||
Shell.init();
|
||||
updateMagiskInfo();
|
||||
initSuAccess();
|
||||
initSuConfigs();
|
||||
// Initialize prefs
|
||||
prefs.edit()
|
||||
.putBoolean("dark_theme", isDarkTheme)
|
||||
.putBoolean("magiskhide", magiskHide)
|
||||
.putBoolean("busybox", Utils.commandExists("busybox"))
|
||||
.putBoolean("hosts", new File("/magisk/.core/hosts").exists())
|
||||
.putBoolean("disable", Utils.itemExist(MAGISK_DISABLE_FILE))
|
||||
.putString("su_request_timeout", String.valueOf(suRequestTimeout))
|
||||
.putString("su_auto_response", String.valueOf(suResponseType))
|
||||
.putString("su_notification", String.valueOf(suNotificationType))
|
||||
.putString("su_access", String.valueOf(suAccessState))
|
||||
.apply();
|
||||
}
|
||||
|
||||
public void initSuConfigs() {
|
||||
suRequestTimeout = Utils.getPrefsInt(prefs, "su_request_timeout", 10);
|
||||
suResponseType = Utils.getPrefsInt(prefs, "su_auto_response", 0);
|
||||
suNotificationType = Utils.getPrefsInt(prefs, "su_notification", 1);
|
||||
}
|
||||
|
||||
public void initSuAccess() {
|
||||
List<String> ret = Shell.sh("su -v");
|
||||
if (Utils.isValidShellResponse(ret)) {
|
||||
suVersion = ret.get(0);
|
||||
isSuClient = suVersion.toUpperCase().contains("MAGISK");
|
||||
}
|
||||
if (isSuClient) {
|
||||
ret = Shell.sh("getprop persist.sys.root_access");
|
||||
if (Utils.isValidShellResponse(ret)) {
|
||||
suAccessState = Integer.parseInt(ret.get(0));
|
||||
} else {
|
||||
Shell.su(true, "setprop persist.sys.root_access 3");
|
||||
suAccessState = 3;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void updateMagiskInfo() {
|
||||
List<String> ret = Shell.sh("getprop magisk.version");
|
||||
if (!Utils.isValidShellResponse(ret)) {
|
||||
magiskVersion = -1;
|
||||
} else {
|
||||
try {
|
||||
magiskVersionString = ret.get(0);
|
||||
magiskVersion = Double.parseDouble(ret.get(0));
|
||||
} catch (NumberFormatException e) {
|
||||
// Custom version don't need to receive updates
|
||||
magiskVersion = Double.POSITIVE_INFINITY;
|
||||
}
|
||||
}
|
||||
ret = Shell.sh("getprop ro.magisk.disable");
|
||||
try {
|
||||
disabled = Utils.isValidShellResponse(ret) && Integer.parseInt(ret.get(0)) != 0;
|
||||
} catch (NumberFormatException e) {
|
||||
disabled = false;
|
||||
}
|
||||
ret = Shell.sh("getprop persist.magisk.hide");
|
||||
try {
|
||||
magiskHideStarted = Utils.isValidShellResponse(ret) && Integer.parseInt(ret.get(0)) != 0;
|
||||
} catch (NumberFormatException e) {
|
||||
magiskHideStarted = false;
|
||||
}
|
||||
|
||||
if (magiskHideStarted) {
|
||||
magiskHide = true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
@@ -7,7 +7,6 @@ import android.content.pm.PackageManager;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.design.widget.NavigationView;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
@@ -15,23 +14,24 @@ import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentTransaction;
|
||||
import android.support.v4.widget.DrawerLayout;
|
||||
import android.support.v7.app.ActionBarDrawerToggle;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
|
||||
import com.topjohnwu.magisk.utils.CallbackHandler;
|
||||
import com.topjohnwu.magisk.components.Activity;
|
||||
import com.topjohnwu.magisk.utils.CallbackEvent;
|
||||
import com.topjohnwu.magisk.utils.Shell;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
|
||||
public class MainActivity extends AppCompatActivity
|
||||
implements NavigationView.OnNavigationItemSelectedListener, CallbackHandler.EventListener {
|
||||
public class MainActivity extends Activity
|
||||
implements NavigationView.OnNavigationItemSelectedListener, CallbackEvent.Listener<Void> {
|
||||
|
||||
private final Handler mDrawerHandler = new Handler();
|
||||
private SharedPreferences prefs;
|
||||
private int mDrawerItem;
|
||||
|
||||
@BindView(R.id.toolbar) Toolbar toolbar;
|
||||
@BindView(R.id.drawer_layout) DrawerLayout drawer;
|
||||
@@ -42,10 +42,10 @@ public class MainActivity extends AppCompatActivity
|
||||
@Override
|
||||
protected void onCreate(final Bundle savedInstanceState) {
|
||||
|
||||
prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
|
||||
prefs = getApplicationContext().prefs;
|
||||
|
||||
if (Global.Configs.isDarkTheme) {
|
||||
setTheme(R.style.AppTheme_dh);
|
||||
if (getApplicationContext().isDarkTheme) {
|
||||
setTheme(R.style.AppTheme_Dark);
|
||||
}
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_main);
|
||||
@@ -76,31 +76,35 @@ public class MainActivity extends AppCompatActivity
|
||||
drawer.addDrawerListener(toggle);
|
||||
toggle.syncState();
|
||||
|
||||
navigate(R.id.status);
|
||||
if (savedInstanceState == null)
|
||||
navigate(getIntent().getStringExtra(MagiskManager.INTENT_SECTION));
|
||||
|
||||
navigationView.setNavigationItemSelectedListener(this);
|
||||
CallbackHandler.register(Global.Events.reloadMainActivity, this);
|
||||
getApplicationContext().reloadMainActivity.register(this);
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
CallbackHandler.register(Global.Events.updateCheckDone, this);
|
||||
if (Global.Events.updateCheckDone.isTriggered)
|
||||
onTrigger(Global.Events.updateCheckDone);
|
||||
checkHideSection();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
CallbackHandler.unRegister(Global.Events.updateCheckDone, this);
|
||||
super.onPause();
|
||||
protected void onRestoreInstanceState(Bundle savedInstanceState) {
|
||||
super.onRestoreInstanceState(savedInstanceState);
|
||||
navigate(savedInstanceState.getInt(MagiskManager.INTENT_SECTION, R.id.status));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putInt(MagiskManager.INTENT_SECTION, mDrawerItem);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
CallbackHandler.unRegister(Global.Events.reloadMainActivity, this);
|
||||
getApplicationContext().reloadMainActivity.unRegister(this);
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@@ -121,29 +125,65 @@ public class MainActivity extends AppCompatActivity
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrigger(CallbackHandler.Event event) {
|
||||
if (event == Global.Events.updateCheckDone) {
|
||||
Menu menu = navigationView.getMenu();
|
||||
menu.findItem(R.id.install).setVisible(Global.Info.remoteMagiskVersion > 0 &&
|
||||
Shell.rootAccess());
|
||||
} else if (event == Global.Events.reloadMainActivity) {
|
||||
recreate();
|
||||
}
|
||||
public void onTrigger(CallbackEvent<Void> event) {
|
||||
recreate();
|
||||
}
|
||||
|
||||
private void checkHideSection() {
|
||||
Menu menu = navigationView.getMenu();
|
||||
if (Shell.rootAccess()) {
|
||||
menu.findItem(R.id.magiskhide).setVisible(
|
||||
Global.Info.magiskVersion >= 8 && prefs.getBoolean("magiskhide", false));
|
||||
menu.findItem(R.id.modules).setVisible(Global.Info.magiskVersion >= 4);
|
||||
menu.findItem(R.id.downloads).setVisible(Global.Info.magiskVersion >= 4);
|
||||
menu.findItem(R.id.log).setVisible(true);
|
||||
menu.findItem(R.id.superuser).setVisible(Global.Info.isSuClient);
|
||||
menu.findItem(R.id.magiskhide).setVisible(
|
||||
Shell.rootAccess() && getApplicationContext().magiskVersion >= 8
|
||||
&& prefs.getBoolean("magiskhide", false));
|
||||
menu.findItem(R.id.modules).setVisible(
|
||||
Shell.rootAccess() && getApplicationContext().magiskVersion >= 4);
|
||||
menu.findItem(R.id.downloads).setVisible(
|
||||
Shell.rootAccess() && getApplicationContext().magiskVersion >= 4);
|
||||
menu.findItem(R.id.log).setVisible(Shell.rootAccess());
|
||||
menu.findItem(R.id.superuser).setVisible(
|
||||
Shell.rootAccess() && getApplicationContext().isSuClient);
|
||||
menu.findItem(R.id.install).setVisible(getApplicationContext().remoteMagiskVersion > 0);
|
||||
}
|
||||
|
||||
public void navigate(String item) {
|
||||
int itemId = R.id.status;
|
||||
if (item != null) {
|
||||
switch (item) {
|
||||
case "status":
|
||||
itemId = R.id.status;
|
||||
break;
|
||||
case "install":
|
||||
itemId = R.id.install;
|
||||
break;
|
||||
case "superuser":
|
||||
itemId = R.id.superuser;
|
||||
break;
|
||||
case "modules":
|
||||
itemId = R.id.modules;
|
||||
break;
|
||||
case "downloads":
|
||||
itemId = R.id.downloads;
|
||||
break;
|
||||
case "magiskhide":
|
||||
itemId = R.id.magiskhide;
|
||||
break;
|
||||
case "log":
|
||||
itemId = R.id.log;
|
||||
break;
|
||||
case "settings":
|
||||
itemId = R.id.settings;
|
||||
break;
|
||||
case "about":
|
||||
itemId = R.id.app_about;
|
||||
break;
|
||||
}
|
||||
}
|
||||
navigate(itemId);
|
||||
}
|
||||
|
||||
public void navigate(int itemId) {
|
||||
int bak = mDrawerItem;
|
||||
mDrawerItem = itemId;
|
||||
navigationView.setCheckedItem(itemId);
|
||||
switch (itemId) {
|
||||
case R.id.status:
|
||||
displayFragment(new StatusFragment(), "status", true);
|
||||
@@ -165,13 +205,14 @@ public class MainActivity extends AppCompatActivity
|
||||
break;
|
||||
case R.id.log:
|
||||
displayFragment(new LogFragment(), "log", false);
|
||||
toolbar.setElevation(0);
|
||||
break;
|
||||
case R.id.settings:
|
||||
startActivity(new Intent(this, SettingsActivity.class));
|
||||
mDrawerItem = bak;
|
||||
break;
|
||||
case R.id.app_about:
|
||||
startActivity(new Intent(this, AboutActivity.class));
|
||||
mDrawerItem = bak;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -182,5 +223,6 @@ public class MainActivity extends AppCompatActivity
|
||||
transaction.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out);
|
||||
transaction.replace(R.id.content_frame, navFragment, tag).commitNow();
|
||||
if (setElevation) toolbar.setElevation(toolbarElevation);
|
||||
else toolbar.setElevation(0);
|
||||
}
|
||||
}
|
||||
|
@@ -5,7 +5,6 @@ import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.widget.SwipeRefreshLayout;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
@@ -15,10 +14,11 @@ import android.widget.TextView;
|
||||
|
||||
import com.github.clans.fab.FloatingActionButton;
|
||||
import com.topjohnwu.magisk.adapters.ModulesAdapter;
|
||||
import com.topjohnwu.magisk.asyncs.FlashZip;
|
||||
import com.topjohnwu.magisk.asyncs.LoadModules;
|
||||
import com.topjohnwu.magisk.components.Fragment;
|
||||
import com.topjohnwu.magisk.module.Module;
|
||||
import com.topjohnwu.magisk.module.ModuleHelper;
|
||||
import com.topjohnwu.magisk.utils.Async;
|
||||
import com.topjohnwu.magisk.utils.CallbackHandler;
|
||||
import com.topjohnwu.magisk.utils.CallbackEvent;
|
||||
import com.topjohnwu.magisk.utils.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -28,7 +28,7 @@ import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.Unbinder;
|
||||
|
||||
public class ModulesFragment extends Fragment implements CallbackHandler.EventListener {
|
||||
public class ModulesFragment extends Fragment implements CallbackEvent.Listener<Void> {
|
||||
|
||||
private static final int FETCH_ZIP_CODE = 2;
|
||||
|
||||
@@ -54,7 +54,7 @@ public class ModulesFragment extends Fragment implements CallbackHandler.EventLi
|
||||
|
||||
mSwipeRefreshLayout.setOnRefreshListener(() -> {
|
||||
recyclerView.setVisibility(View.GONE);
|
||||
new Async.LoadModules().exec();
|
||||
new LoadModules(getActivity()).exec();
|
||||
});
|
||||
|
||||
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
|
||||
@@ -69,7 +69,7 @@ public class ModulesFragment extends Fragment implements CallbackHandler.EventLi
|
||||
}
|
||||
});
|
||||
|
||||
if (Global.Events.moduleLoadDone.isTriggered) {
|
||||
if (getApplication().moduleLoadDone.isTriggered) {
|
||||
updateUI();
|
||||
}
|
||||
|
||||
@@ -77,7 +77,7 @@ public class ModulesFragment extends Fragment implements CallbackHandler.EventLi
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrigger(CallbackHandler.Event event) {
|
||||
public void onTrigger(CallbackEvent<Void> event) {
|
||||
Logger.dev("ModulesFragment: UI refresh triggered");
|
||||
updateUI();
|
||||
}
|
||||
@@ -87,7 +87,7 @@ public class ModulesFragment extends Fragment implements CallbackHandler.EventLi
|
||||
if (requestCode == FETCH_ZIP_CODE && resultCode == Activity.RESULT_OK && data != null) {
|
||||
// Get the URI of the selected file
|
||||
final Uri uri = data.getData();
|
||||
new Async.FlashZIP(getActivity(), uri).exec();
|
||||
new FlashZip(getActivity(), uri).exec();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -95,13 +95,13 @@ public class ModulesFragment extends Fragment implements CallbackHandler.EventLi
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
CallbackHandler.register(Global.Events.moduleLoadDone, this);
|
||||
getApplication().moduleLoadDone.register(this);
|
||||
getActivity().setTitle(R.string.modules);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
CallbackHandler.unRegister(Global.Events.moduleLoadDone, this);
|
||||
getApplication().moduleLoadDone.unRegister(this);
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
@@ -112,7 +112,8 @@ public class ModulesFragment extends Fragment implements CallbackHandler.EventLi
|
||||
}
|
||||
|
||||
private void updateUI() {
|
||||
ModuleHelper.getModuleList(listModules);
|
||||
listModules.clear();
|
||||
listModules.addAll(getApplication().moduleMap.values());
|
||||
if (listModules.size() == 0) {
|
||||
emptyRv.setVisibility(View.VISIBLE);
|
||||
recyclerView.setVisibility(View.GONE);
|
||||
|
@@ -2,7 +2,6 @@ package com.topjohnwu.magisk;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.view.MenuItemCompat;
|
||||
import android.support.v4.widget.SwipeRefreshLayout;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
@@ -16,10 +15,12 @@ import android.widget.TextView;
|
||||
|
||||
import com.topjohnwu.magisk.adapters.ReposAdapter;
|
||||
import com.topjohnwu.magisk.adapters.SimpleSectionedRecyclerViewAdapter;
|
||||
import com.topjohnwu.magisk.module.ModuleHelper;
|
||||
import com.topjohnwu.magisk.asyncs.LoadRepos;
|
||||
import com.topjohnwu.magisk.asyncs.ParallelTask;
|
||||
import com.topjohnwu.magisk.components.Fragment;
|
||||
import com.topjohnwu.magisk.module.Module;
|
||||
import com.topjohnwu.magisk.module.Repo;
|
||||
import com.topjohnwu.magisk.utils.Async;
|
||||
import com.topjohnwu.magisk.utils.CallbackHandler;
|
||||
import com.topjohnwu.magisk.utils.CallbackEvent;
|
||||
import com.topjohnwu.magisk.utils.Logger;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -29,7 +30,7 @@ import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.Unbinder;
|
||||
|
||||
public class ReposFragment extends Fragment implements CallbackHandler.EventListener {
|
||||
public class ReposFragment extends Fragment implements CallbackEvent.Listener<Void> {
|
||||
|
||||
private Unbinder unbinder;
|
||||
@BindView(R.id.recyclerView) RecyclerView recyclerView;
|
||||
@@ -68,10 +69,10 @@ public class ReposFragment extends Fragment implements CallbackHandler.EventList
|
||||
|
||||
mSwipeRefreshLayout.setOnRefreshListener(() -> {
|
||||
recyclerView.setVisibility(View.GONE);
|
||||
new Async.LoadRepos(getActivity()).exec();
|
||||
new LoadRepos(getActivity()).exec();
|
||||
});
|
||||
|
||||
if (Global.Events.repoLoadDone.isTriggered) {
|
||||
if (getApplication().repoLoadDone.isTriggered) {
|
||||
reloadRepos();
|
||||
updateUI();
|
||||
}
|
||||
@@ -93,7 +94,7 @@ public class ReposFragment extends Fragment implements CallbackHandler.EventList
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrigger(CallbackHandler.Event event) {
|
||||
public void onTrigger(CallbackEvent<Void> event) {
|
||||
Logger.dev("ReposFragment: UI refresh triggered");
|
||||
reloadRepos();
|
||||
updateUI();
|
||||
@@ -109,13 +110,13 @@ public class ReposFragment extends Fragment implements CallbackHandler.EventList
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
CallbackHandler.register(Global.Events.repoLoadDone, this);
|
||||
getApplication().repoLoadDone.register(this);
|
||||
getActivity().setTitle(R.string.downloads);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
CallbackHandler.unRegister(Global.Events.repoLoadDone, this);
|
||||
getApplication().repoLoadDone.unRegister(this);
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
@@ -126,7 +127,21 @@ public class ReposFragment extends Fragment implements CallbackHandler.EventList
|
||||
}
|
||||
|
||||
private void reloadRepos() {
|
||||
ModuleHelper.getRepoLists(mUpdateRepos, mInstalledRepos, mOthersRepos);
|
||||
mUpdateRepos.clear();
|
||||
mInstalledRepos.clear();
|
||||
mOthersRepos.clear();
|
||||
for (Repo repo : getApplication().repoMap.values()) {
|
||||
Module module = getApplication().moduleMap.get(repo.getId());
|
||||
if (module != null) {
|
||||
if (repo.getVersionCode() > module.getVersionCode()) {
|
||||
mUpdateRepos.add(repo);
|
||||
} else {
|
||||
mInstalledRepos.add(repo);
|
||||
}
|
||||
} else {
|
||||
mOthersRepos.add(repo);
|
||||
}
|
||||
}
|
||||
fUpdateRepos.clear();
|
||||
fInstalledRepos.clear();
|
||||
fOthersRepos.clear();
|
||||
@@ -158,7 +173,7 @@ public class ReposFragment extends Fragment implements CallbackHandler.EventList
|
||||
mSwipeRefreshLayout.setRefreshing(false);
|
||||
}
|
||||
|
||||
private class FilterApps extends Async.NormalTask<String, Void, Void> {
|
||||
private class FilterApps extends ParallelTask<String, Void, Void> {
|
||||
@Override
|
||||
protected Void doInBackground(String... strings) {
|
||||
String newText = strings[0];
|
||||
|
@@ -9,13 +9,14 @@ import android.preference.PreferenceManager;
|
||||
import android.preference.PreferenceScreen;
|
||||
import android.preference.SwitchPreference;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.topjohnwu.magisk.module.ModuleHelper;
|
||||
import com.topjohnwu.magisk.utils.Async;
|
||||
import com.topjohnwu.magisk.asyncs.MagiskHide;
|
||||
import com.topjohnwu.magisk.asyncs.SerialTask;
|
||||
import com.topjohnwu.magisk.components.Activity;
|
||||
import com.topjohnwu.magisk.components.AlertDialogBuilder;
|
||||
import com.topjohnwu.magisk.utils.Logger;
|
||||
import com.topjohnwu.magisk.utils.Shell;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
@@ -23,15 +24,15 @@ import com.topjohnwu.magisk.utils.Utils;
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
|
||||
public class SettingsActivity extends AppCompatActivity {
|
||||
public class SettingsActivity extends Activity {
|
||||
|
||||
@BindView(R.id.toolbar) Toolbar toolbar;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
if (Global.Configs.isDarkTheme) {
|
||||
setTheme(R.style.AppTheme_dh);
|
||||
if (getApplicationContext().isDarkTheme) {
|
||||
setTheme(R.style.AppTheme_Dark);
|
||||
}
|
||||
|
||||
setContentView(R.layout.activity_container);
|
||||
@@ -77,6 +78,10 @@ public class SettingsActivity extends AppCompatActivity {
|
||||
|
||||
private ListPreference suAccess, autoRes, suNotification, requestTimeout;
|
||||
|
||||
private MagiskManager getApplication() {
|
||||
return Utils.getMagiskManager(getActivity());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
@@ -99,7 +104,7 @@ public class SettingsActivity extends AppCompatActivity {
|
||||
setSummary();
|
||||
|
||||
findPreference("clear").setOnPreferenceClickListener((pref) -> {
|
||||
ModuleHelper.clearRepoCache(getActivity());
|
||||
Utils.clearRepoCache(getActivity());
|
||||
return true;
|
||||
});
|
||||
|
||||
@@ -107,11 +112,13 @@ public class SettingsActivity extends AppCompatActivity {
|
||||
prefScreen.removePreference(magiskCategory);
|
||||
prefScreen.removePreference(suCategory);
|
||||
} else {
|
||||
if (!Global.Info.isSuClient)
|
||||
if (!getApplication().isSuClient) {
|
||||
prefScreen.removePreference(suCategory);
|
||||
if (Global.Info.magiskVersion < 11)
|
||||
}
|
||||
if (getApplication().magiskVersion < 11) {
|
||||
prefScreen.removePreference(magiskCategory);
|
||||
if (Global.Info.disabled) {
|
||||
}
|
||||
if (getApplication().disabled) {
|
||||
busybox.setEnabled(false);
|
||||
magiskHide.setEnabled(false);
|
||||
hosts.setEnabled(false);
|
||||
@@ -139,22 +146,22 @@ public class SettingsActivity extends AppCompatActivity {
|
||||
switch (key) {
|
||||
case "dark_theme":
|
||||
enabled = prefs.getBoolean("dark_theme", false);
|
||||
if (Global.Configs.isDarkTheme != enabled) {
|
||||
Global.Configs.isDarkTheme = enabled;
|
||||
if (getApplication().isDarkTheme != enabled) {
|
||||
getApplication().isDarkTheme = enabled;
|
||||
getApplication().reloadMainActivity.trigger();
|
||||
getActivity().recreate();
|
||||
Global.Events.reloadMainActivity.trigger();
|
||||
}
|
||||
break;
|
||||
case "disable":
|
||||
enabled = prefs.getBoolean("disable", false);
|
||||
new Async.RootTask<Void, Void, Void>() {
|
||||
new SerialTask<Void, Void, Void>() {
|
||||
private boolean enable = enabled;
|
||||
@Override
|
||||
protected Void doInBackground(Void... voids) {
|
||||
if (enable) {
|
||||
Utils.createFile(Global.MAGISK_DISABLE_FILE);
|
||||
Utils.createFile(MagiskManager.MAGISK_DISABLE_FILE);
|
||||
} else {
|
||||
Utils.removeItem(Global.MAGISK_DISABLE_FILE);
|
||||
Utils.removeItem(MagiskManager.MAGISK_DISABLE_FILE);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -163,7 +170,7 @@ public class SettingsActivity extends AppCompatActivity {
|
||||
break;
|
||||
case "busybox":
|
||||
enabled = prefs.getBoolean("busybox", false);
|
||||
new Async.RootTask<Void, Void, Void>() {
|
||||
new SerialTask<Void, Void, Void>() {
|
||||
private boolean enable = enabled;
|
||||
@Override
|
||||
protected Void doInBackground(Void... voids) {
|
||||
@@ -183,20 +190,23 @@ public class SettingsActivity extends AppCompatActivity {
|
||||
case "magiskhide":
|
||||
enabled = prefs.getBoolean("magiskhide", false);
|
||||
if (enabled) {
|
||||
if (!Global.Info.isSuClient) {
|
||||
Utils.getAlertDialogBuilder(getActivity())
|
||||
if (!getApplication().isSuClient) {
|
||||
new AlertDialogBuilder(getActivity())
|
||||
.setTitle(R.string.no_magisksu_title)
|
||||
.setMessage(R.string.no_magisksu_msg)
|
||||
.setPositiveButton(R.string.understand, (dialog, which) -> new Async.MagiskHide().enable())
|
||||
.setPositiveButton(R.string.understand, (dialog, which) -> new MagiskHide().enable())
|
||||
.setCancelable(false)
|
||||
.show();
|
||||
} else new Async.MagiskHide().enable();
|
||||
} else
|
||||
new Async.MagiskHide().disable();
|
||||
} else {
|
||||
new MagiskHide().enable();
|
||||
}
|
||||
} else {
|
||||
new MagiskHide().disable();
|
||||
}
|
||||
break;
|
||||
case "hosts":
|
||||
enabled = prefs.getBoolean("hosts", false);
|
||||
new Async.RootTask<Void, Void, Void>() {
|
||||
new SerialTask<Void, Void, Void>() {
|
||||
private boolean enable = enabled;
|
||||
@Override
|
||||
protected Void doInBackground(Void... voids) {
|
||||
@@ -212,23 +222,23 @@ public class SettingsActivity extends AppCompatActivity {
|
||||
}.exec();
|
||||
break;
|
||||
case "su_access":
|
||||
Global.Configs.suAccessState = Utils.getPrefsInt(prefs, "su_access", 0);
|
||||
Shell.su("setprop persist.sys.root_access " + Global.Configs.suAccessState);
|
||||
getApplication().suAccessState = Utils.getPrefsInt(prefs, "su_access", 0);
|
||||
Shell.su("setprop persist.sys.root_access " + getApplication().suAccessState);
|
||||
break;
|
||||
case "su_request_timeout":
|
||||
Global.Configs.suRequestTimeout = Utils.getPrefsInt(prefs, "su_request_timeout", 10);
|
||||
getApplication().suRequestTimeout = Utils.getPrefsInt(prefs, "su_request_timeout", 10);
|
||||
break;
|
||||
case "su_auto_response":
|
||||
Global.Configs.suResponseType = Utils.getPrefsInt(prefs, "su_auto_response", 0);
|
||||
getApplication().suResponseType = Utils.getPrefsInt(prefs, "su_auto_response", 0);
|
||||
break;
|
||||
case "su_notification":
|
||||
Global.Configs.suNotificationType = Utils.getPrefsInt(prefs, "su_notification", 1);
|
||||
getApplication().suNotificationType = Utils.getPrefsInt(prefs, "su_notification", 1);
|
||||
break;
|
||||
case "developer_logging":
|
||||
Global.Configs.devLogging = prefs.getBoolean("developer_logging", false);
|
||||
MagiskManager.devLogging = prefs.getBoolean("developer_logging", false);
|
||||
break;
|
||||
case "shell_logging":
|
||||
Global.Configs.shellLogging = prefs.getBoolean("shell_logging", false);
|
||||
MagiskManager.shellLogging = prefs.getBoolean("shell_logging", false);
|
||||
break;
|
||||
}
|
||||
setSummary();
|
||||
@@ -236,11 +246,11 @@ public class SettingsActivity extends AppCompatActivity {
|
||||
|
||||
private void setSummary() {
|
||||
suAccess.setSummary(getResources()
|
||||
.getStringArray(R.array.su_access)[Global.Configs.suAccessState]);
|
||||
.getStringArray(R.array.su_access)[getApplication().suAccessState]);
|
||||
autoRes.setSummary(getResources()
|
||||
.getStringArray(R.array.auto_response)[Global.Configs.suResponseType]);
|
||||
.getStringArray(R.array.auto_response)[getApplication().suResponseType]);
|
||||
suNotification.setSummary(getResources()
|
||||
.getStringArray(R.array.su_notification)[Global.Configs.suNotificationType]);
|
||||
.getStringArray(R.array.su_notification)[getApplication().suNotificationType]);
|
||||
requestTimeout.setSummary(
|
||||
getString(R.string.request_timeout_summary, prefs.getString("su_request_timeout", "10")));
|
||||
}
|
||||
|
@@ -1,36 +1,73 @@
|
||||
package com.topjohnwu.magisk;
|
||||
|
||||
import android.app.job.JobInfo;
|
||||
import android.app.job.JobScheduler;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
|
||||
import com.topjohnwu.magisk.utils.Async;
|
||||
import com.topjohnwu.magisk.asyncs.CheckUpdates;
|
||||
import com.topjohnwu.magisk.asyncs.GetBootBlocks;
|
||||
import com.topjohnwu.magisk.asyncs.LoadApps;
|
||||
import com.topjohnwu.magisk.asyncs.LoadModules;
|
||||
import com.topjohnwu.magisk.asyncs.LoadRepos;
|
||||
import com.topjohnwu.magisk.asyncs.MagiskHide;
|
||||
import com.topjohnwu.magisk.components.Activity;
|
||||
import com.topjohnwu.magisk.services.UpdateCheckService;
|
||||
|
||||
public class SplashActivity extends AppCompatActivity {
|
||||
public class SplashActivity extends Activity{
|
||||
|
||||
private static final int UPDATE_SERVICE_ID = 1;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
MagiskManager magiskManager = getApplicationContext();
|
||||
|
||||
// Init the info and configs and root shell
|
||||
Global.init(getApplicationContext());
|
||||
magiskManager.init();
|
||||
|
||||
// Initialize the update check service, notify every 3 hours
|
||||
if (!"install".equals(getIntent().getStringExtra(MagiskManager.INTENT_SECTION))) {
|
||||
ComponentName service = new ComponentName(magiskManager, UpdateCheckService.class);
|
||||
JobInfo jobInfo = new JobInfo.Builder(UPDATE_SERVICE_ID, service)
|
||||
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
|
||||
.setPersisted(true)
|
||||
.setPeriodic(3 * 60 * 60 * 1000)
|
||||
.build();
|
||||
JobScheduler scheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
|
||||
scheduler.schedule(jobInfo);
|
||||
}
|
||||
|
||||
// Now fire all async tasks
|
||||
new Async.CheckUpdates().exec();
|
||||
new Async.GetBootBlocks().exec();
|
||||
new Async.LoadModules() {
|
||||
new GetBootBlocks(this).exec();
|
||||
if (magiskManager.magiskHide && !magiskManager.disabled &&
|
||||
magiskManager.magiskVersion > 11 && !magiskManager.magiskHideStarted) {
|
||||
new MagiskHide().enable();
|
||||
}
|
||||
new LoadModules(this) {
|
||||
@Override
|
||||
protected void onPostExecute(Void v) {
|
||||
super.onPostExecute(v);
|
||||
new Async.LoadRepos(getApplicationContext()).exec();
|
||||
new LoadRepos(activity).exec();
|
||||
}
|
||||
}.exec();
|
||||
new LoadApps(this).exec();
|
||||
new CheckUpdates(this, false){
|
||||
@Override
|
||||
protected void onPostExecute(Void v) {
|
||||
super.onPostExecute(v);
|
||||
String section = getIntent().getStringExtra(MagiskManager.INTENT_SECTION);
|
||||
Intent intent = new Intent(magiskManager, MainActivity.class);
|
||||
if (section != null) {
|
||||
intent.putExtra(MagiskManager.INTENT_SECTION, section);
|
||||
}
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
}.exec();
|
||||
new Async.LoadApps(getPackageManager()).exec();
|
||||
|
||||
// Preparation done, now start main activity
|
||||
Intent intent = new Intent(getApplicationContext(), MainActivity.class);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
}
|
||||
|
@@ -1,11 +1,7 @@
|
||||
package com.topjohnwu.magisk;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.FragmentTransaction;
|
||||
import android.support.v4.widget.SwipeRefreshLayout;
|
||||
import android.view.LayoutInflater;
|
||||
@@ -15,8 +11,10 @@ import android.widget.ImageView;
|
||||
import android.widget.ProgressBar;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.topjohnwu.magisk.utils.Async;
|
||||
import com.topjohnwu.magisk.utils.CallbackHandler;
|
||||
import com.topjohnwu.magisk.asyncs.CheckUpdates;
|
||||
import com.topjohnwu.magisk.components.AlertDialogBuilder;
|
||||
import com.topjohnwu.magisk.components.Fragment;
|
||||
import com.topjohnwu.magisk.utils.CallbackEvent;
|
||||
import com.topjohnwu.magisk.utils.Logger;
|
||||
import com.topjohnwu.magisk.utils.Shell;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
@@ -24,9 +22,10 @@ import com.topjohnwu.magisk.utils.Utils;
|
||||
import butterknife.BindColor;
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
import butterknife.OnClick;
|
||||
import butterknife.Unbinder;
|
||||
|
||||
public class StatusFragment extends Fragment implements CallbackHandler.EventListener {
|
||||
public class StatusFragment extends Fragment implements CallbackEvent.Listener<Void> {
|
||||
|
||||
private static boolean noDialog = false;
|
||||
|
||||
@@ -55,9 +54,27 @@ public class StatusFragment extends Fragment implements CallbackHandler.EventLis
|
||||
@BindColor(R.color.grey500) int colorNeutral;
|
||||
@BindColor(R.color.blue500) int colorInfo;
|
||||
@BindColor(android.R.color.transparent) int trans;
|
||||
int defaultColor;
|
||||
|
||||
private AlertDialog updateMagisk;
|
||||
@OnClick(R.id.safetyNet_container)
|
||||
public void safetyNet() {
|
||||
safetyNetProgress.setVisibility(View.VISIBLE);
|
||||
safetyNetContainer.setBackgroundColor(trans);
|
||||
safetyNetIcon.setImageResource(0);
|
||||
safetyNetStatusText.setText(R.string.checking_safetyNet_status);
|
||||
Utils.checkSafetyNet(getApplication());
|
||||
}
|
||||
|
||||
@OnClick(R.id.magisk_status_container)
|
||||
public void magisk() {
|
||||
((MainActivity) getActivity()).navigationView.setCheckedItem(R.id.install);
|
||||
FragmentTransaction transaction = getFragmentManager().beginTransaction();
|
||||
transaction.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out);
|
||||
try {
|
||||
transaction.replace(R.id.content_frame, new InstallFragment(), "install").commit();
|
||||
} catch (IllegalStateException ignored) {}
|
||||
}
|
||||
|
||||
private int defaultColor;
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
@@ -80,31 +97,23 @@ public class StatusFragment extends Fragment implements CallbackHandler.EventLis
|
||||
safetyNetStatusText.setText(R.string.safetyNet_check_text);
|
||||
safetyNetStatusText.setTextColor(defaultColor);
|
||||
|
||||
Global.Events.safetyNetDone.isTriggered = false;
|
||||
getApplication().safetyNetDone.isTriggered = false;
|
||||
noDialog = false;
|
||||
|
||||
updateUI();
|
||||
new Async.CheckUpdates().exec();
|
||||
new CheckUpdates(getActivity()).exec();
|
||||
});
|
||||
|
||||
safetyNetContainer.setOnClickListener(view -> {
|
||||
safetyNetProgress.setVisibility(View.VISIBLE);
|
||||
safetyNetContainer.setBackgroundColor(trans);
|
||||
safetyNetIcon.setImageResource(0);
|
||||
safetyNetStatusText.setText(R.string.checking_safetyNet_status);
|
||||
Async.checkSafetyNet(getActivity());
|
||||
});
|
||||
|
||||
if (Global.Info.magiskVersion < 0 && Shell.rootAccess() && !noDialog) {
|
||||
if (getApplication().magiskVersion < 0 && Shell.rootAccess() && !noDialog) {
|
||||
noDialog = true;
|
||||
Utils.getAlertDialogBuilder(getActivity())
|
||||
new AlertDialogBuilder(getActivity())
|
||||
.setTitle(R.string.no_magisk_title)
|
||||
.setMessage(R.string.no_magisk_msg)
|
||||
.setCancelable(true)
|
||||
.setPositiveButton(R.string.goto_install, (dialogInterface, i) -> {
|
||||
((MainActivity) getActivity()).navigationView.setCheckedItem(R.id.install);
|
||||
FragmentTransaction transaction = getFragmentManager().beginTransaction();
|
||||
transaction.setCustomAnimations(android.R.animator.fade_in, android.R.animator.fade_out);
|
||||
transaction.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out);
|
||||
try {
|
||||
transaction.replace(R.id.content_frame, new InstallFragment(), "install").commit();
|
||||
} catch (IllegalStateException ignored) {}
|
||||
@@ -115,15 +124,20 @@ public class StatusFragment extends Fragment implements CallbackHandler.EventLis
|
||||
|
||||
updateUI();
|
||||
|
||||
if (getApplication().updateCheckDone.isTriggered)
|
||||
updateCheckUI();
|
||||
if (getApplication().safetyNetDone.isTriggered)
|
||||
updateSafetyNetUI();
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrigger(CallbackHandler.Event event) {
|
||||
if (event == Global.Events.updateCheckDone) {
|
||||
public void onTrigger(CallbackEvent<Void> event) {
|
||||
if (event == getApplication().updateCheckDone) {
|
||||
Logger.dev("StatusFragment: Update Check UI refresh triggered");
|
||||
updateCheckUI();
|
||||
} else if (event == Global.Events.safetyNetDone) {
|
||||
} else if (event == getApplication().safetyNetDone) {
|
||||
Logger.dev("StatusFragment: SafetyNet UI refresh triggered");
|
||||
updateSafetyNetUI();
|
||||
}
|
||||
@@ -132,21 +146,15 @@ public class StatusFragment extends Fragment implements CallbackHandler.EventLis
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
CallbackHandler.register(Global.Events.updateCheckDone, this);
|
||||
CallbackHandler.register(Global.Events.safetyNetDone, this);
|
||||
if (Global.Events.updateCheckDone.isTriggered) {
|
||||
updateCheckUI();
|
||||
}
|
||||
if (Global.Events.safetyNetDone.isTriggered) {
|
||||
updateSafetyNetUI();
|
||||
}
|
||||
getApplication().updateCheckDone.register(this);
|
||||
getApplication().safetyNetDone.register(this);
|
||||
getActivity().setTitle(R.string.status);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStop() {
|
||||
CallbackHandler.unRegister(Global.Events.updateCheckDone, this);
|
||||
CallbackHandler.unRegister(Global.Events.safetyNetDone, this);
|
||||
getApplication().updateCheckDone.unRegister(this);
|
||||
getApplication().safetyNetDone.unRegister(this);
|
||||
super.onStop();
|
||||
}
|
||||
|
||||
@@ -159,14 +167,14 @@ public class StatusFragment extends Fragment implements CallbackHandler.EventLis
|
||||
private void updateUI() {
|
||||
int image, color;
|
||||
|
||||
Global.updateMagiskInfo();
|
||||
getApplication().updateMagiskInfo();
|
||||
|
||||
if (Global.Info.magiskVersion < 0) {
|
||||
if (getApplication().magiskVersion < 0) {
|
||||
magiskVersionText.setText(R.string.magisk_version_error);
|
||||
} else if (Global.Info.disabled) {
|
||||
magiskVersionText.setText(getString(R.string.magisk_version_disable, Global.Info.magiskVersionString));
|
||||
} else if (getApplication().disabled) {
|
||||
magiskVersionText.setText(getString(R.string.magisk_version_disable, getApplication().magiskVersionString));
|
||||
} else {
|
||||
magiskVersionText.setText(getString(R.string.magisk_version, Global.Info.magiskVersionString));
|
||||
magiskVersionText.setText(getString(R.string.magisk_version, getApplication().magiskVersionString));
|
||||
}
|
||||
|
||||
switch (Shell.rootStatus) {
|
||||
@@ -177,11 +185,11 @@ public class StatusFragment extends Fragment implements CallbackHandler.EventLis
|
||||
rootInfoText.setText(R.string.root_info_warning);
|
||||
break;
|
||||
case 1:
|
||||
if (Global.Info.suVersion != null) {
|
||||
if (getApplication().suVersion != null) {
|
||||
color = colorOK;
|
||||
image = R.drawable.ic_check_circle;
|
||||
rootStatusText.setText(R.string.proper_root);
|
||||
rootInfoText.setText(Global.Info.suVersion);
|
||||
rootInfoText.setText(getApplication().suVersion);
|
||||
break;
|
||||
}
|
||||
case -1:
|
||||
@@ -200,24 +208,24 @@ public class StatusFragment extends Fragment implements CallbackHandler.EventLis
|
||||
private void updateCheckUI() {
|
||||
int image, color;
|
||||
|
||||
if (Global.Info.remoteMagiskVersion < 0) {
|
||||
if (getApplication().remoteMagiskVersion < 0) {
|
||||
color = colorNeutral;
|
||||
image = R.drawable.ic_help;
|
||||
magiskUpdateText.setText(R.string.cannot_check_updates);
|
||||
} else if (Global.Info.remoteMagiskVersion > Global.Info.magiskVersion) {
|
||||
} else if (getApplication().remoteMagiskVersion > getApplication().magiskVersion) {
|
||||
color = colorInfo;
|
||||
image = R.drawable.ic_update;
|
||||
magiskUpdateText.setText(getString(R.string.magisk_update_available, Global.Info.remoteMagiskVersion));
|
||||
magiskUpdateText.setText(getString(R.string.magisk_update_available, getApplication().remoteMagiskVersion));
|
||||
} else {
|
||||
color = colorOK;
|
||||
image = R.drawable.ic_check_circle;
|
||||
magiskUpdateText.setText(getString(R.string.up_to_date, getString(R.string.magisk)));
|
||||
}
|
||||
|
||||
if (Global.Info.magiskVersion < 0) {
|
||||
if (getApplication().magiskVersion < 0) {
|
||||
color = colorBad;
|
||||
image = R.drawable.ic_cancel;
|
||||
} else if (Global.Info.disabled) {
|
||||
} else if (getApplication().disabled) {
|
||||
color = colorNeutral;
|
||||
image = R.drawable.ic_cancel;
|
||||
}
|
||||
@@ -229,38 +237,12 @@ public class StatusFragment extends Fragment implements CallbackHandler.EventLis
|
||||
|
||||
magiskCheckUpdatesProgress.setVisibility(View.GONE);
|
||||
mSwipeRefreshLayout.setRefreshing(false);
|
||||
|
||||
updateMagisk = Utils.getAlertDialogBuilder(getActivity())
|
||||
.setTitle(R.string.magisk_update_title)
|
||||
.setMessage(getString(R.string.magisk_update_message, Global.Info.remoteMagiskVersion))
|
||||
.setCancelable(true)
|
||||
.setPositiveButton(R.string.goto_install, (dialogInterface, i) -> {
|
||||
((MainActivity) getActivity()).navigationView.setCheckedItem(R.id.install);
|
||||
FragmentTransaction transaction = getFragmentManager().beginTransaction();
|
||||
transaction.setCustomAnimations(android.R.anim.fade_in, android.R.anim.fade_out);
|
||||
try {
|
||||
transaction.replace(R.id.content_frame, new InstallFragment(), "install").commit();
|
||||
} catch (IllegalStateException ignored) {}
|
||||
})
|
||||
.setNeutralButton(R.string.check_release_notes, (dialog, which) -> {
|
||||
getActivity().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(Global.Info.releaseNoteLink)));
|
||||
})
|
||||
.setNegativeButton(R.string.no_thanks, null)
|
||||
.create();
|
||||
|
||||
if (Global.Info.magiskVersion < Global.Info.remoteMagiskVersion && Shell.rootAccess()) {
|
||||
magiskStatusContainer.setOnClickListener(view -> updateMagisk.show());
|
||||
if (!noDialog) {
|
||||
noDialog = true;
|
||||
updateMagisk.show();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateSafetyNetUI() {
|
||||
int image, color;
|
||||
safetyNetProgress.setVisibility(View.GONE);
|
||||
switch (Global.Info.SNCheckResult) {
|
||||
switch (getApplication().SNCheckResult) {
|
||||
case -3:
|
||||
color = colorNeutral;
|
||||
image = R.drawable.ic_help;
|
||||
|
@@ -2,7 +2,6 @@ package com.topjohnwu.magisk;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
@@ -13,7 +12,8 @@ import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.topjohnwu.magisk.adapters.SuLogAdapter;
|
||||
import com.topjohnwu.magisk.superuser.SuLogDatabaseHelper;
|
||||
import com.topjohnwu.magisk.components.Fragment;
|
||||
import com.topjohnwu.magisk.database.SuLogDatabaseHelper;
|
||||
import com.topjohnwu.magisk.superuser.SuLogEntry;
|
||||
|
||||
import java.util.List;
|
||||
|
@@ -3,7 +3,6 @@ package com.topjohnwu.magisk;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
@@ -11,8 +10,9 @@ import android.view.ViewGroup;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.topjohnwu.magisk.adapters.PolicyAdapter;
|
||||
import com.topjohnwu.magisk.components.Fragment;
|
||||
import com.topjohnwu.magisk.database.SuDatabaseHelper;
|
||||
import com.topjohnwu.magisk.superuser.Policy;
|
||||
import com.topjohnwu.magisk.superuser.SuDatabaseHelper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
|
@@ -13,7 +13,8 @@ import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.utils.Async;
|
||||
import com.topjohnwu.magisk.asyncs.MagiskHide;
|
||||
import com.topjohnwu.magisk.components.SnackbarMaker;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -50,8 +51,8 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
|
||||
}
|
||||
|
||||
public void setLists(List<ApplicationInfo> listApps, List<String> hideList) {
|
||||
mOriginalList = mList = Collections.unmodifiableList(listApps);
|
||||
mHideList = new ArrayList<>(hideList);
|
||||
mOriginalList = mList = listApps;
|
||||
mHideList = hideList;
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
|
||||
@@ -76,20 +77,19 @@ public class ApplicationAdapter extends RecyclerView.Adapter<ApplicationAdapter.
|
||||
if (SNLIST.contains(info.packageName)) {
|
||||
holder.checkBox.setChecked(true);
|
||||
holder.checkBox.setEnabled(false);
|
||||
holder.itemView.setOnClickListener(v -> {
|
||||
Snackbar snackbar = Snackbar.make(holder.itemView, R.string.safetyNet_hide_notice, Snackbar.LENGTH_LONG);
|
||||
((TextView) snackbar.getView().findViewById(android.support.design.R.id.snackbar_text)).setMaxLines(2);
|
||||
snackbar.show();
|
||||
});
|
||||
holder.itemView.setOnClickListener(v ->
|
||||
SnackbarMaker.make(holder.itemView,
|
||||
R.string.safetyNet_hide_notice, Snackbar.LENGTH_LONG).show()
|
||||
);
|
||||
} else {
|
||||
holder.checkBox.setEnabled(true);
|
||||
holder.checkBox.setChecked(mHideList.contains(info.packageName));
|
||||
holder.checkBox.setOnCheckedChangeListener((v, isChecked) -> {
|
||||
if (isChecked) {
|
||||
new Async.MagiskHide().add(info.packageName);
|
||||
new MagiskHide().add(info.packageName);
|
||||
mHideList.add(info.packageName);
|
||||
} else {
|
||||
new Async.MagiskHide().rm(info.packageName);
|
||||
new MagiskHide().rm(info.packageName);
|
||||
mHideList.remove(info.packageName);
|
||||
}
|
||||
});
|
||||
|
@@ -12,8 +12,9 @@ import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.asyncs.SerialTask;
|
||||
import com.topjohnwu.magisk.components.SnackbarMaker;
|
||||
import com.topjohnwu.magisk.module.Module;
|
||||
import com.topjohnwu.magisk.utils.Async;
|
||||
import com.topjohnwu.magisk.utils.Shell;
|
||||
|
||||
import java.util.List;
|
||||
@@ -52,7 +53,7 @@ public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHold
|
||||
|
||||
holder.checkBox.setOnCheckedChangeListener(null);
|
||||
holder.checkBox.setChecked(module.isEnabled());
|
||||
holder.checkBox.setOnCheckedChangeListener((v, isChecked) -> new Async.RootTask<Void, Void, Void>() {
|
||||
holder.checkBox.setOnCheckedChangeListener((v, isChecked) -> new SerialTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... voids) {
|
||||
if (isChecked) {
|
||||
@@ -66,11 +67,11 @@ public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHold
|
||||
@Override
|
||||
protected void onPostExecute(Void v) {
|
||||
int snack = isChecked ? R.string.disable_file_removed : R.string.disable_file_created;
|
||||
Snackbar.make(holder.itemView, snack, Snackbar.LENGTH_SHORT).show();
|
||||
SnackbarMaker.make(holder.itemView, snack, Snackbar.LENGTH_SHORT).show();
|
||||
}
|
||||
}.exec());
|
||||
|
||||
holder.delete.setOnClickListener(v -> new Async.RootTask<Void, Void, Void>() {
|
||||
holder.delete.setOnClickListener(v -> new SerialTask<Void, Void, Void>() {
|
||||
private final boolean removed = module.willBeRemoved();
|
||||
|
||||
@Override
|
||||
@@ -86,7 +87,7 @@ public class ModulesAdapter extends RecyclerView.Adapter<ModulesAdapter.ViewHold
|
||||
@Override
|
||||
protected void onPostExecute(Void v) {
|
||||
int snack = removed ? R.string.remove_file_deleted : R.string.remove_file_created;
|
||||
Snackbar.make(holder.itemView, snack, Snackbar.LENGTH_SHORT).show();
|
||||
SnackbarMaker.make(holder.itemView, snack, Snackbar.LENGTH_SHORT).show();
|
||||
updateDeleteButton(holder, module);
|
||||
}
|
||||
}.exec());
|
||||
|
@@ -15,9 +15,10 @@ import android.widget.Switch;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.components.AlertDialogBuilder;
|
||||
import com.topjohnwu.magisk.components.SnackbarMaker;
|
||||
import com.topjohnwu.magisk.database.SuDatabaseHelper;
|
||||
import com.topjohnwu.magisk.superuser.Policy;
|
||||
import com.topjohnwu.magisk.superuser.SuDatabaseHelper;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
@@ -70,7 +71,7 @@ public class PolicyAdapter extends RecyclerView.Adapter<PolicyAdapter.ViewHolder
|
||||
policy.policy = isChecked ? Policy.ALLOW : Policy.DENY;
|
||||
String message = v.getContext().getString(
|
||||
isChecked ? R.string.su_snack_grant : R.string.su_snack_deny, policy.appName);
|
||||
Snackbar.make(holder.itemView, message, Snackbar.LENGTH_SHORT).show();
|
||||
SnackbarMaker.make(holder.itemView, message, Snackbar.LENGTH_SHORT).show();
|
||||
dbHelper.addPolicy(policy);
|
||||
}
|
||||
});
|
||||
@@ -80,7 +81,7 @@ public class PolicyAdapter extends RecyclerView.Adapter<PolicyAdapter.ViewHolder
|
||||
policy.notification = isChecked;
|
||||
String message = v.getContext().getString(
|
||||
isChecked ? R.string.su_snack_notif_on : R.string.su_snack_notif_off, policy.appName);
|
||||
Snackbar.make(holder.itemView, message, Snackbar.LENGTH_SHORT).show();
|
||||
SnackbarMaker.make(holder.itemView, message, Snackbar.LENGTH_SHORT).show();
|
||||
dbHelper.addPolicy(policy);
|
||||
}
|
||||
});
|
||||
@@ -90,18 +91,18 @@ public class PolicyAdapter extends RecyclerView.Adapter<PolicyAdapter.ViewHolder
|
||||
policy.logging = isChecked;
|
||||
String message = v.getContext().getString(
|
||||
isChecked ? R.string.su_snack_log_on : R.string.su_snack_log_off, policy.appName);
|
||||
Snackbar.make(holder.itemView, message, Snackbar.LENGTH_SHORT).show();
|
||||
SnackbarMaker.make(holder.itemView, message, Snackbar.LENGTH_SHORT).show();
|
||||
dbHelper.addPolicy(policy);
|
||||
}
|
||||
});
|
||||
holder.delete.setOnClickListener(v -> Utils.getAlertDialogBuilder(v.getContext())
|
||||
holder.delete.setOnClickListener(v -> new AlertDialogBuilder(v.getContext())
|
||||
.setTitle(R.string.su_revoke_title)
|
||||
.setMessage(v.getContext().getString(R.string.su_revoke_msg, policy.appName))
|
||||
.setPositiveButton(R.string.yes, (dialog, which) -> {
|
||||
policyList.remove(position);
|
||||
notifyItemRemoved(position);
|
||||
notifyItemRangeChanged(position, policyList.size());
|
||||
Snackbar.make(holder.itemView, v.getContext().getString(R.string.su_snack_revoke, policy.appName),
|
||||
SnackbarMaker.make(holder.itemView, v.getContext().getString(R.string.su_snack_revoke, policy.appName),
|
||||
Snackbar.LENGTH_SHORT).show();
|
||||
dbHelper.deletePolicy(policy.uid);
|
||||
})
|
||||
|
@@ -1,30 +1,25 @@
|
||||
package com.topjohnwu.magisk.adapters;
|
||||
|
||||
import android.animation.Animator;
|
||||
import android.animation.ObjectAnimator;
|
||||
import android.animation.ValueAnimator;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.net.Uri;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.text.TextUtils;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewTreeObserver;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.asyncs.ProcessRepoZip;
|
||||
import com.topjohnwu.magisk.components.AlertDialogBuilder;
|
||||
import com.topjohnwu.magisk.components.MarkDownWindow;
|
||||
import com.topjohnwu.magisk.module.Repo;
|
||||
import com.topjohnwu.magisk.receivers.RepoDlReceiver;
|
||||
import com.topjohnwu.magisk.receivers.DownloadReceiver;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
import com.topjohnwu.magisk.utils.WebWindow;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
@@ -32,7 +27,7 @@ import butterknife.ButterKnife;
|
||||
public class ReposAdapter extends RecyclerView.Adapter<ReposAdapter.ViewHolder> {
|
||||
|
||||
private List<Repo> mUpdateRepos, mInstalledRepos, mOthersRepos;
|
||||
private Set<Repo> expandList = new HashSet<>();
|
||||
private Context mContext;
|
||||
|
||||
public ReposAdapter(List<Repo> update, List<Repo> installed, List<Repo> others) {
|
||||
mUpdateRepos = update;
|
||||
@@ -42,61 +37,52 @@ public class ReposAdapter extends RecyclerView.Adapter<ReposAdapter.ViewHolder>
|
||||
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_repo, parent, false);
|
||||
mContext = parent.getContext();
|
||||
View v = LayoutInflater.from(mContext).inflate(R.layout.list_item_repo, parent, false);
|
||||
return new ViewHolder(v);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(final ViewHolder holder, int position) {
|
||||
Context context = holder.itemView.getContext();
|
||||
Repo repo = getItem(position);
|
||||
|
||||
holder.title.setText(repo.getName());
|
||||
holder.versionName.setText(repo.getVersion());
|
||||
String author = repo.getAuthor();
|
||||
holder.author.setText(TextUtils.isEmpty(author) ? null : context.getString(R.string.author, author));
|
||||
holder.author.setText(TextUtils.isEmpty(author) ? null : mContext.getString(R.string.author, author));
|
||||
holder.description.setText(repo.getDescription());
|
||||
|
||||
holder.setExpanded(expandList.contains(repo));
|
||||
holder.infoLayout.setOnClickListener(v -> new MarkDownWindow(null, repo.getDetailUrl(), mContext));
|
||||
|
||||
holder.itemView.setOnClickListener(view -> {
|
||||
if (holder.mExpanded) {
|
||||
holder.collapse();
|
||||
expandList.remove(repo);
|
||||
} else {
|
||||
holder.expand();
|
||||
expandList.add(repo);
|
||||
}
|
||||
});
|
||||
holder.changeLog.setOnClickListener(view -> {
|
||||
if (!TextUtils.isEmpty(repo.getLogUrl())) {
|
||||
new WebWindow(context.getString(R.string.changelog), repo.getLogUrl(), context);
|
||||
}
|
||||
});
|
||||
holder.updateImage.setOnClickListener(view -> {
|
||||
holder.downloadImage.setOnClickListener(v -> {
|
||||
String filename = repo.getName() + "-" + repo.getVersion() + ".zip";
|
||||
Utils.getAlertDialogBuilder(context)
|
||||
.setTitle(context.getString(R.string.repo_install_title, repo.getName()))
|
||||
.setMessage(context.getString(R.string.repo_install_msg, filename))
|
||||
new AlertDialogBuilder(mContext)
|
||||
.setTitle(mContext.getString(R.string.repo_install_title, repo.getName()))
|
||||
.setMessage(mContext.getString(R.string.repo_install_msg, filename))
|
||||
.setCancelable(true)
|
||||
.setPositiveButton(R.string.download_install, (dialogInterface, i) -> Utils.dlAndReceive(
|
||||
context,
|
||||
new RepoDlReceiver(),
|
||||
.setPositiveButton(R.string.install, (d, i) -> Utils.dlAndReceive(
|
||||
mContext,
|
||||
new DownloadReceiver() {
|
||||
@Override
|
||||
public void onDownloadDone(Uri uri) {
|
||||
new ProcessRepoZip(activity, uri, true).exec();
|
||||
}
|
||||
},
|
||||
repo.getZipUrl(),
|
||||
Utils.getLegalFilename(filename)))
|
||||
.setNeutralButton(R.string.download, (d, i) -> Utils.dlAndReceive(
|
||||
mContext,
|
||||
new DownloadReceiver() {
|
||||
@Override
|
||||
public void onDownloadDone(Uri uri) {
|
||||
new ProcessRepoZip(activity, uri, false).exec();
|
||||
}
|
||||
},
|
||||
repo.getZipUrl(),
|
||||
Utils.getLegalFilename(filename)))
|
||||
.setNegativeButton(R.string.no_thanks, null)
|
||||
.show();
|
||||
});
|
||||
holder.authorLink.setOnClickListener(view -> {
|
||||
if (!TextUtils.isEmpty(repo.getDonateUrl())) {
|
||||
context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(repo.getDonateUrl())));
|
||||
}
|
||||
});
|
||||
holder.supportLink.setOnClickListener(view -> {
|
||||
if (!TextUtils.isEmpty(repo.getSupportUrl())) {
|
||||
context.startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(repo.getSupportUrl())));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -124,97 +110,12 @@ public class ReposAdapter extends RecyclerView.Adapter<ReposAdapter.ViewHolder>
|
||||
@BindView(R.id.version_name) TextView versionName;
|
||||
@BindView(R.id.description) TextView description;
|
||||
@BindView(R.id.author) TextView author;
|
||||
@BindView(R.id.expand_layout) LinearLayout expandLayout;
|
||||
@BindView(R.id.update) ImageView updateImage;
|
||||
@BindView(R.id.changeLog) ImageView changeLog;
|
||||
@BindView(R.id.authorLink) ImageView authorLink;
|
||||
@BindView(R.id.supportLink) ImageView supportLink;
|
||||
|
||||
private ValueAnimator mAnimator;
|
||||
private ObjectAnimator animY2;
|
||||
private boolean mExpanded = false;
|
||||
private static int expandHeight = 0;
|
||||
@BindView(R.id.info_layout) LinearLayout infoLayout;
|
||||
@BindView(R.id.download) ImageView downloadImage;
|
||||
|
||||
ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
ButterKnife.bind(this, itemView);
|
||||
expandLayout.getViewTreeObserver().addOnPreDrawListener(
|
||||
new ViewTreeObserver.OnPreDrawListener() {
|
||||
|
||||
@Override
|
||||
public boolean onPreDraw() {
|
||||
if (expandHeight == 0) {
|
||||
final int widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
|
||||
final int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
|
||||
expandLayout.measure(widthSpec, heightSpec);
|
||||
expandHeight = expandLayout.getMeasuredHeight();
|
||||
}
|
||||
|
||||
expandLayout.getViewTreeObserver().removeOnPreDrawListener(this);
|
||||
expandLayout.setVisibility(View.GONE);
|
||||
mAnimator = slideAnimator(0, expandHeight);
|
||||
animY2 = ObjectAnimator.ofFloat(updateImage, "translationY", expandHeight / 2);
|
||||
return true;
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
private void setExpanded(boolean expanded) {
|
||||
mExpanded = expanded;
|
||||
ViewGroup.LayoutParams layoutParams = expandLayout.getLayoutParams();
|
||||
layoutParams.height = expanded ? expandHeight : 0;
|
||||
expandLayout.setLayoutParams(layoutParams);
|
||||
expandLayout.setVisibility(expanded ? View.VISIBLE : View.GONE);
|
||||
if (expanded) {
|
||||
updateImage.setTranslationY(expandHeight / 2);
|
||||
} else {
|
||||
updateImage.setTranslationY(0);
|
||||
}
|
||||
}
|
||||
|
||||
private void expand() {
|
||||
expandLayout.setVisibility(View.VISIBLE);
|
||||
mAnimator.start();
|
||||
animY2.start();
|
||||
mExpanded = true;
|
||||
}
|
||||
|
||||
private void collapse() {
|
||||
if (!mExpanded) return;
|
||||
int finalHeight = expandLayout.getHeight();
|
||||
ValueAnimator mAnimator = slideAnimator(finalHeight, 0);
|
||||
mAnimator.addListener(new Animator.AnimatorListener() {
|
||||
@Override
|
||||
public void onAnimationEnd(Animator animator) {
|
||||
expandLayout.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAnimationStart(Animator animator) {}
|
||||
|
||||
@Override
|
||||
public void onAnimationCancel(Animator animator) {}
|
||||
|
||||
@Override
|
||||
public void onAnimationRepeat(Animator animator) {}
|
||||
});
|
||||
mAnimator.start();
|
||||
animY2.reverse();
|
||||
mExpanded = false;
|
||||
}
|
||||
|
||||
private ValueAnimator slideAnimator(int start, int end) {
|
||||
|
||||
ValueAnimator animator = ValueAnimator.ofInt(start, end);
|
||||
|
||||
animator.addUpdateListener(valueAnimator -> {
|
||||
int value = (Integer) valueAnimator.getAnimatedValue();
|
||||
ViewGroup.LayoutParams layoutParams = expandLayout.getLayoutParams();
|
||||
layoutParams.height = value;
|
||||
expandLayout.setLayoutParams(layoutParams);
|
||||
});
|
||||
return animator;
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -109,7 +109,9 @@ public class SuLogAdapter {
|
||||
@Override
|
||||
public void onBindGroupViewHolder(LogGroupViewHolder holder, int flatPosition, ExpandableGroup group) {
|
||||
holder.date.setText(group.getTitle());
|
||||
if (isGroupExpanded(flatPosition)) holder.expand();
|
||||
if (isGroupExpanded(flatPosition)) {
|
||||
holder.expand();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -0,0 +1,71 @@
|
||||
package com.topjohnwu.magisk.asyncs;
|
||||
|
||||
import android.app.NotificationManager;
|
||||
import android.app.PendingIntent;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.support.v4.app.TaskStackBuilder;
|
||||
import android.support.v7.app.NotificationCompat;
|
||||
|
||||
import com.topjohnwu.magisk.MagiskManager;
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.SplashActivity;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
import com.topjohnwu.magisk.utils.WebService;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
public class CheckUpdates extends ParallelTask<Void, Void, Void> {
|
||||
|
||||
private static final String UPDATE_JSON = "https://raw.githubusercontent.com/topjohnwu/MagiskManager/updates/magisk_update.json";
|
||||
private static final int NOTIFICATION_ID = 1;
|
||||
|
||||
private boolean showNotification = false;
|
||||
|
||||
public CheckUpdates(Context context, boolean b) {
|
||||
this(context);
|
||||
showNotification = b;
|
||||
}
|
||||
|
||||
public CheckUpdates(Context context) {
|
||||
magiskManager = Utils.getMagiskManager(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... voids) {
|
||||
String jsonStr = WebService.request(UPDATE_JSON, WebService.GET);
|
||||
try {
|
||||
JSONObject json = new JSONObject(jsonStr);
|
||||
JSONObject magisk = json.getJSONObject("magisk");
|
||||
magiskManager.remoteMagiskVersion = magisk.getDouble("versionCode");
|
||||
magiskManager.magiskLink = magisk.getString("link");
|
||||
magiskManager.releaseNoteLink = magisk.getString("note");
|
||||
} catch (JSONException ignored) {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void v) {
|
||||
if (magiskManager.magiskVersion < magiskManager.remoteMagiskVersion && showNotification) {
|
||||
NotificationCompat.Builder builder = new NotificationCompat.Builder(magiskManager);
|
||||
builder.setSmallIcon(R.drawable.ic_magisk)
|
||||
.setContentTitle(magiskManager.getString(R.string.magisk_update_title))
|
||||
.setContentText(magiskManager.getString(R.string.magisk_update_available, magiskManager.remoteMagiskVersion))
|
||||
.setVibrate(new long[]{0, 100, 100, 100})
|
||||
.setAutoCancel(true);
|
||||
Intent intent = new Intent(magiskManager, SplashActivity.class);
|
||||
intent.putExtra(MagiskManager.INTENT_SECTION, "install");
|
||||
TaskStackBuilder stackBuilder = TaskStackBuilder.create(magiskManager);
|
||||
stackBuilder.addParentStack(SplashActivity.class);
|
||||
stackBuilder.addNextIntent(intent);
|
||||
PendingIntent pendingIntent = stackBuilder.getPendingIntent(NOTIFICATION_ID, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
builder.setContentIntent(pendingIntent);
|
||||
NotificationManager notificationManager =
|
||||
(NotificationManager) magiskManager.getSystemService(Context.NOTIFICATION_SERVICE);
|
||||
notificationManager.notify(NOTIFICATION_ID, builder.build());
|
||||
}
|
||||
magiskManager.updateCheckDone.trigger();
|
||||
}
|
||||
}
|
154
app/src/main/java/com/topjohnwu/magisk/asyncs/FlashZip.java
Normal file
@@ -0,0 +1,154 @@
|
||||
package com.topjohnwu.magisk.asyncs;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ProgressDialog;
|
||||
import android.net.Uri;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.topjohnwu.magisk.MagiskManager;
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.components.AlertDialogBuilder;
|
||||
import com.topjohnwu.magisk.utils.Logger;
|
||||
import com.topjohnwu.magisk.utils.Shell;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
import com.topjohnwu.magisk.utils.ZipUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.List;
|
||||
|
||||
public class FlashZip extends SerialTask<Void, String, Integer> {
|
||||
|
||||
private Uri mUri;
|
||||
private File mCachedFile, mScriptFile, mCheckFile;
|
||||
|
||||
private String mFilename;
|
||||
private ProgressDialog progress;
|
||||
|
||||
public FlashZip(Activity context, Uri uri) {
|
||||
super(context);
|
||||
mUri = uri;
|
||||
|
||||
mCachedFile = new File(magiskManager.getCacheDir(), "install.zip");
|
||||
mScriptFile = new File(magiskManager.getCacheDir(), "/META-INF/com/google/android/update-binary");
|
||||
mCheckFile = new File(mScriptFile.getParent(), "updater-script");
|
||||
|
||||
// Try to get the filename ourselves
|
||||
mFilename = Utils.getNameFromUri(magiskManager, mUri);
|
||||
}
|
||||
|
||||
private void copyToCache() throws Throwable {
|
||||
publishProgress(magiskManager.getString(R.string.copying_msg));
|
||||
|
||||
if (mCachedFile.exists() && !mCachedFile.delete()) {
|
||||
Logger.error("FlashZip: Error while deleting already existing file");
|
||||
throw new IOException();
|
||||
}
|
||||
try (
|
||||
InputStream in = magiskManager.getContentResolver().openInputStream(mUri);
|
||||
OutputStream outputStream = new FileOutputStream(mCachedFile)
|
||||
) {
|
||||
byte buffer[] = new byte[1024];
|
||||
int length;
|
||||
if (in == null) throw new FileNotFoundException();
|
||||
while ((length = in.read(buffer)) > 0)
|
||||
outputStream.write(buffer, 0, length);
|
||||
|
||||
Logger.dev("FlashZip: File created successfully - " + mCachedFile.getPath());
|
||||
} catch (FileNotFoundException e) {
|
||||
Logger.error("FlashZip: Invalid Uri");
|
||||
throw e;
|
||||
} catch (IOException e) {
|
||||
Logger.error("FlashZip: Error in creating file");
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean unzipAndCheck() throws Exception {
|
||||
ZipUtils.unzip(mCachedFile, mCachedFile.getParentFile(), "META-INF/com/google/android");
|
||||
List<String> ret;
|
||||
ret = Utils.readFile(mCheckFile.getPath());
|
||||
return Utils.isValidShellResponse(ret) && ret.get(0).contains("#MAGISK");
|
||||
}
|
||||
|
||||
private int cleanup(int ret) {
|
||||
Shell.su(
|
||||
"rm -rf " + mCachedFile.getParent() + "/*",
|
||||
"rm -rf " + MagiskManager.TMP_FOLDER_PATH
|
||||
);
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
progress = new ProgressDialog(activity);
|
||||
progress.setTitle(R.string.zip_install_progress_title);
|
||||
progress.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onProgressUpdate(String... values) {
|
||||
progress.setMessage(values[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Integer doInBackground(Void... voids) {
|
||||
Logger.dev("FlashZip Running... " + mFilename);
|
||||
List<String> ret;
|
||||
try {
|
||||
copyToCache();
|
||||
if (!unzipAndCheck()) return cleanup(0);
|
||||
publishProgress(magiskManager.getString(R.string.zip_install_progress_msg, mFilename));
|
||||
ret = Shell.su(
|
||||
"BOOTMODE=true sh " + mScriptFile + " dummy 1 " + mCachedFile,
|
||||
"if [ $? -eq 0 ]; then echo true; else echo false; fi"
|
||||
);
|
||||
if (!Utils.isValidShellResponse(ret)) return -1;
|
||||
Logger.dev("FlashZip: Console log:");
|
||||
for (String line : ret) {
|
||||
Logger.dev(line);
|
||||
}
|
||||
if (Boolean.parseBoolean(ret.get(ret.size() - 1)))
|
||||
return cleanup(1);
|
||||
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return cleanup(-1);
|
||||
}
|
||||
|
||||
// -1 = error, manual install; 0 = invalid zip; 1 = success
|
||||
@Override
|
||||
protected void onPostExecute(Integer result) {
|
||||
super.onPostExecute(result);
|
||||
progress.dismiss();
|
||||
switch (result) {
|
||||
case -1:
|
||||
Toast.makeText(magiskManager, magiskManager.getString(R.string.install_error), Toast.LENGTH_LONG).show();
|
||||
Utils.showUriSnack(activity, mUri);
|
||||
break;
|
||||
case 0:
|
||||
Toast.makeText(magiskManager, magiskManager.getString(R.string.invalid_zip), Toast.LENGTH_LONG).show();
|
||||
break;
|
||||
case 1:
|
||||
onSuccess();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected void onSuccess() {
|
||||
magiskManager.updateCheckDone.trigger();
|
||||
new LoadModules(activity).exec();
|
||||
|
||||
new AlertDialogBuilder(activity)
|
||||
.setTitle(R.string.reboot_title)
|
||||
.setMessage(R.string.reboot_msg)
|
||||
.setPositiveButton(R.string.reboot, (dialogInterface, i) -> Shell.su(true, "reboot"))
|
||||
.setNegativeButton(R.string.no_thanks, null)
|
||||
.show();
|
||||
}
|
||||
}
|
@@ -0,0 +1,27 @@
|
||||
package com.topjohnwu.magisk.asyncs;
|
||||
|
||||
import android.app.Activity;
|
||||
|
||||
import com.topjohnwu.magisk.utils.Shell;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
|
||||
public class GetBootBlocks extends SerialTask<Void, Void, Void> {
|
||||
|
||||
public GetBootBlocks(Activity context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
magiskManager.blockList = Shell.su("ls /dev/block | grep mmc");
|
||||
if (magiskManager.bootBlock == null) {
|
||||
magiskManager.bootBlock = Utils.detectBootImage();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void v) {
|
||||
magiskManager.blockDetectionDone.trigger();
|
||||
}
|
||||
}
|
39
app/src/main/java/com/topjohnwu/magisk/asyncs/LoadApps.java
Normal file
@@ -0,0 +1,39 @@
|
||||
package com.topjohnwu.magisk.asyncs;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
|
||||
import com.topjohnwu.magisk.adapters.ApplicationAdapter;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public class LoadApps extends ParallelTask<Void, Void, Void> {
|
||||
|
||||
public LoadApps(Activity context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... voids) {
|
||||
PackageManager pm = magiskManager.getPackageManager();
|
||||
List<ApplicationInfo> list = pm.getInstalledApplications(0);
|
||||
for (Iterator<ApplicationInfo> i = list.iterator(); i.hasNext(); ) {
|
||||
ApplicationInfo info = i.next();
|
||||
if (ApplicationAdapter.BLACKLIST.contains(info.packageName) || !info.enabled) {
|
||||
i.remove();
|
||||
}
|
||||
}
|
||||
Collections.sort(list, (a, b) -> a.loadLabel(pm).toString().toLowerCase()
|
||||
.compareTo(b.loadLabel(pm).toString().toLowerCase()));
|
||||
magiskManager.appList = Collections.unmodifiableList(list);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void v) {
|
||||
new MagiskHide(activity).list();
|
||||
}
|
||||
}
|
@@ -0,0 +1,41 @@
|
||||
package com.topjohnwu.magisk.asyncs;
|
||||
|
||||
import android.app.Activity;
|
||||
|
||||
import com.topjohnwu.magisk.MagiskManager;
|
||||
import com.topjohnwu.magisk.module.BaseModule;
|
||||
import com.topjohnwu.magisk.module.Module;
|
||||
import com.topjohnwu.magisk.utils.Logger;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
import com.topjohnwu.magisk.utils.ValueSortedMap;
|
||||
|
||||
public class LoadModules extends SerialTask<Void, Void, Void> {
|
||||
|
||||
public LoadModules(Activity context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... voids) {
|
||||
Logger.dev("LoadModules: Loading modules");
|
||||
|
||||
magiskManager.moduleMap = new ValueSortedMap<>();
|
||||
|
||||
for (String path : Utils.getModList(MagiskManager.MAGISK_PATH)) {
|
||||
Logger.dev("LoadModules: Adding modules from " + path);
|
||||
Module module;
|
||||
try {
|
||||
module = new Module(path);
|
||||
magiskManager.moduleMap.put(module.getId(), module);
|
||||
} catch (BaseModule.CacheModException ignored) {}
|
||||
}
|
||||
|
||||
Logger.dev("LoadModules: Data load done");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void v) {
|
||||
magiskManager.moduleLoadDone.trigger();
|
||||
}
|
||||
}
|
130
app/src/main/java/com/topjohnwu/magisk/asyncs/LoadRepos.java
Normal file
@@ -0,0 +1,130 @@
|
||||
package com.topjohnwu.magisk.asyncs;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.content.SharedPreferences;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.topjohnwu.magisk.database.RepoDatabaseHelper;
|
||||
import com.topjohnwu.magisk.module.BaseModule;
|
||||
import com.topjohnwu.magisk.module.Repo;
|
||||
import com.topjohnwu.magisk.utils.Logger;
|
||||
import com.topjohnwu.magisk.utils.ValueSortedMap;
|
||||
import com.topjohnwu.magisk.utils.WebService;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.File;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
public class LoadRepos extends ParallelTask<Void, Void, Void> {
|
||||
|
||||
public static final String ETAG_KEY = "ETag";
|
||||
|
||||
private static final String REPO_URL = "https://api.github.com/orgs/Magisk-Modules-Repo/repos";
|
||||
|
||||
private String prefsPath;
|
||||
|
||||
public LoadRepos(Activity context) {
|
||||
super(context);
|
||||
prefsPath = context.getApplicationInfo().dataDir + "/shared_prefs";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... voids) {
|
||||
Logger.dev("LoadRepos: Loading repos");
|
||||
|
||||
SharedPreferences prefs = magiskManager.prefs;
|
||||
|
||||
RepoDatabaseHelper dbHelper = new RepoDatabaseHelper(magiskManager);
|
||||
|
||||
// Legacy data cleanup
|
||||
File old = new File(prefsPath, "RepoMap.xml");
|
||||
if (old.exists() || !prefs.getString("repomap", "empty").equals("empty")) {
|
||||
old.delete();
|
||||
prefs.edit().remove("version").remove("repomap").remove(ETAG_KEY).apply();
|
||||
dbHelper.clearRepo();
|
||||
}
|
||||
|
||||
Map<String, String> header = new HashMap<>();
|
||||
// Get cached ETag to add in the request header
|
||||
String etag = prefs.getString(ETAG_KEY, "");
|
||||
header.put("If-None-Match", etag);
|
||||
|
||||
magiskManager.repoMap = new ValueSortedMap<>();
|
||||
|
||||
// Make a request to main URL for repo info
|
||||
String jsonString = WebService.request(REPO_URL, WebService.GET, null, header, false);
|
||||
|
||||
ValueSortedMap<String, Repo> cached = dbHelper.getRepoMap();
|
||||
|
||||
if (!TextUtils.isEmpty(jsonString)) {
|
||||
try {
|
||||
JSONArray jsonArray = new JSONArray(jsonString);
|
||||
// If it gets to this point, the response is valid, update ETag
|
||||
etag = WebService.getLastResponseHeader().get(ETAG_KEY).get(0);
|
||||
// Maybe bug in Android build tools, sometimes the ETag has crap in it...
|
||||
etag = etag.substring(etag.indexOf('\"'), etag.lastIndexOf('\"') + 1);
|
||||
|
||||
// Update repo info
|
||||
for (int i = 0; i < jsonArray.length(); i++) {
|
||||
JSONObject jsonobject = jsonArray.getJSONObject(i);
|
||||
String id = jsonobject.getString("description");
|
||||
String name = jsonobject.getString("name");
|
||||
String lastUpdate = jsonobject.getString("pushed_at");
|
||||
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
|
||||
Date updatedDate;
|
||||
try {
|
||||
updatedDate = format.parse(lastUpdate);
|
||||
} catch (ParseException e) {
|
||||
continue;
|
||||
}
|
||||
Repo repo = cached.get(id);
|
||||
try {
|
||||
if (repo == null) {
|
||||
Logger.dev("LoadRepos: Create new repo " + id);
|
||||
repo = new Repo(name, updatedDate);
|
||||
} else {
|
||||
// Popout from cached
|
||||
cached.remove(id);
|
||||
Logger.dev("LoadRepos: Update cached repo " + id);
|
||||
repo.update(updatedDate);
|
||||
}
|
||||
if (repo.getId() != null) {
|
||||
magiskManager.repoMap.put(id, repo);
|
||||
}
|
||||
} catch (BaseModule.CacheModException ignored) {}
|
||||
}
|
||||
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
// Use cached if no internet or no updates
|
||||
Logger.dev("LoadRepos: No updates, use cached");
|
||||
magiskManager.repoMap.putAll(cached);
|
||||
cached.clear();
|
||||
}
|
||||
|
||||
// Update the database
|
||||
dbHelper.addRepoMap(magiskManager.repoMap);
|
||||
// The leftover cached are those removed remote, cleanup db
|
||||
dbHelper.removeRepo(cached);
|
||||
// Update ETag
|
||||
prefs.edit().putString(ETAG_KEY, etag).apply();
|
||||
|
||||
Logger.dev("LoadRepos: Repo load done");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void v) {
|
||||
magiskManager.repoLoadDone.trigger();
|
||||
}
|
||||
}
|
@@ -0,0 +1,59 @@
|
||||
package com.topjohnwu.magisk.asyncs;
|
||||
|
||||
import android.app.Activity;
|
||||
|
||||
import com.topjohnwu.magisk.MagiskManager;
|
||||
import com.topjohnwu.magisk.utils.Shell;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class MagiskHide extends SerialTask<Object, Void, Void> {
|
||||
|
||||
private boolean isList = false;
|
||||
|
||||
public MagiskHide() {}
|
||||
|
||||
public MagiskHide(Activity context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Object... params) {
|
||||
String command = (String) params[0];
|
||||
List<String> ret = Shell.su(MagiskManager.MAGISK_HIDE_PATH + command);
|
||||
if (isList) {
|
||||
magiskManager.magiskHideList = ret;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void v) {
|
||||
if (isList) {
|
||||
magiskManager.magiskHideDone.trigger();
|
||||
}
|
||||
}
|
||||
|
||||
public void add(CharSequence packageName) {
|
||||
exec("add " + packageName);
|
||||
}
|
||||
|
||||
public void rm(CharSequence packageName) {
|
||||
exec("rm " + packageName);
|
||||
}
|
||||
|
||||
public void enable() {
|
||||
exec("enable; setprop persist.magisk.hide 1");
|
||||
}
|
||||
|
||||
public void disable() {
|
||||
exec("disable; setprop persist.magisk.hide 0");
|
||||
}
|
||||
|
||||
public void list() {
|
||||
isList = true;
|
||||
if (magiskManager == null) return;
|
||||
exec("list");
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
package com.topjohnwu.magisk.asyncs;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.AsyncTask;
|
||||
|
||||
import com.topjohnwu.magisk.MagiskManager;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
|
||||
public abstract class ParallelTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {
|
||||
protected Activity activity;
|
||||
protected MagiskManager magiskManager;
|
||||
|
||||
public ParallelTask() {}
|
||||
|
||||
public ParallelTask(Activity context) {
|
||||
activity = context;
|
||||
magiskManager = Utils.getMagiskManager(context);
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public final void exec(Params... params) {
|
||||
executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
|
||||
}
|
||||
}
|
@@ -0,0 +1,96 @@
|
||||
package com.topjohnwu.magisk.asyncs;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ProgressDialog;
|
||||
import android.net.Uri;
|
||||
|
||||
import com.topjohnwu.magisk.MagiskManager;
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.utils.Logger;
|
||||
import com.topjohnwu.magisk.utils.Shell;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
import com.topjohnwu.magisk.utils.ZipUtils;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class ProcessMagiskZip extends ParallelTask<Void, Void, Boolean> {
|
||||
|
||||
private Uri mUri;
|
||||
private ProgressDialog progressDialog;
|
||||
private String mBoot;
|
||||
private boolean mEnc, mVerity;
|
||||
|
||||
public ProcessMagiskZip(Activity context, Uri uri, String boot, boolean enc, boolean verity) {
|
||||
super(context);
|
||||
mUri = uri;
|
||||
mBoot = boot;
|
||||
mEnc = enc;
|
||||
mVerity = verity;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
progressDialog = ProgressDialog.show(activity,
|
||||
activity.getString(R.string.zip_process_title),
|
||||
activity.getString(R.string.zip_unzip_msg));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Boolean doInBackground(Void... params) {
|
||||
if (Shell.rootAccess()) {
|
||||
try {
|
||||
// We might not have busybox yet, unzip with Java
|
||||
// We shall have complete busybox after Magisk installation
|
||||
File tempdir = new File(magiskManager.getCacheDir(), "magisk");
|
||||
ZipUtils.unzip(magiskManager.getContentResolver().openInputStream(mUri), tempdir);
|
||||
// Running in parallel mode, open new shell
|
||||
Shell.su(true,
|
||||
"rm -f /dev/.magisk",
|
||||
(mBoot != null) ? "echo \"BOOTIMAGE=/dev/block/" + mBoot + "\" >> /dev/.magisk" : "",
|
||||
"echo \"KEEPFORCEENCRYPT=" + String.valueOf(mEnc) + "\" >> /dev/.magisk",
|
||||
"echo \"KEEPVERITY=" + String.valueOf(mVerity) + "\" >> /dev/.magisk",
|
||||
"mkdir -p " + MagiskManager.TMP_FOLDER_PATH,
|
||||
"cp -af " + tempdir + "/. " + MagiskManager.TMP_FOLDER_PATH + "/magisk",
|
||||
"mv -f " + tempdir + "/META-INF " + magiskManager.getCacheDir() + "/META-INF",
|
||||
"rm -rf " + tempdir
|
||||
);
|
||||
} catch (Exception e) {
|
||||
Logger.error("ProcessMagiskZip: Error!");
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Boolean result) {
|
||||
progressDialog.dismiss();
|
||||
if (result) {
|
||||
new FlashZip(activity, mUri) {
|
||||
@Override
|
||||
protected boolean unzipAndCheck() throws Exception {
|
||||
// Don't need to check, as it is downloaded in correct form
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSuccess() {
|
||||
new SerialTask<Void, Void, Void>(activity) {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
Shell.su("setprop magisk.version "
|
||||
+ String.valueOf(magiskManager.remoteMagiskVersion));
|
||||
magiskManager.updateCheckDone.trigger();
|
||||
return null;
|
||||
}
|
||||
}.exec();
|
||||
super.onSuccess();
|
||||
}
|
||||
}.exec();
|
||||
} else {
|
||||
Utils.showUriSnack(activity, mUri);
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,116 @@
|
||||
package com.topjohnwu.magisk.asyncs;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.ProgressDialog;
|
||||
import android.net.Uri;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.utils.Logger;
|
||||
import com.topjohnwu.magisk.utils.Shell;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
import com.topjohnwu.magisk.utils.ZipUtils;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class ProcessRepoZip extends ParallelTask<Void, Void, Boolean> {
|
||||
|
||||
private Uri mUri;
|
||||
private ProgressDialog progressDialog;
|
||||
private boolean mInstall;
|
||||
|
||||
public ProcessRepoZip(Activity context, Uri uri, boolean install) {
|
||||
super(context);
|
||||
mUri = uri;
|
||||
mInstall = install;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
progressDialog = ProgressDialog.show(activity,
|
||||
activity.getString(R.string.zip_process_title),
|
||||
activity.getString(R.string.zip_process_msg));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Boolean doInBackground(Void... params) {
|
||||
|
||||
FileInputStream in;
|
||||
FileOutputStream out;
|
||||
|
||||
try {
|
||||
|
||||
// Create temp file
|
||||
File temp1 = new File(magiskManager.getCacheDir(), "1.zip");
|
||||
File temp2 = new File(magiskManager.getCacheDir(), "2.zip");
|
||||
if (magiskManager.getCacheDir().mkdirs()) {
|
||||
temp1.createNewFile();
|
||||
temp2.createNewFile();
|
||||
}
|
||||
|
||||
out = new FileOutputStream(temp1);
|
||||
|
||||
// First remove top folder in Github source zip, Uri -> temp1
|
||||
ZipUtils.removeTopFolder(activity.getContentResolver().openInputStream(mUri), out);
|
||||
out.flush();
|
||||
out.close();
|
||||
|
||||
out = new FileOutputStream(temp2);
|
||||
|
||||
// Then sign the zip for the first time, temp1 -> temp2
|
||||
ZipUtils.signZip(activity, temp1, out, false);
|
||||
out.flush();
|
||||
out.close();
|
||||
|
||||
// Adjust the zip to prevent unzip issues, temp2 -> temp2
|
||||
ZipUtils.adjustZip(temp2);
|
||||
|
||||
out = new FileOutputStream(temp1);
|
||||
|
||||
// Finally, sign the whole zip file again, temp2 -> temp1
|
||||
ZipUtils.signZip(activity, temp2, out, true);
|
||||
out.flush();
|
||||
out.close();
|
||||
|
||||
in = new FileInputStream(temp1);
|
||||
|
||||
// Write it back to the downloaded zip, temp1 -> Uri
|
||||
try (OutputStream target = activity.getContentResolver().openOutputStream(mUri)) {
|
||||
byte[] buffer = new byte[4096];
|
||||
int length;
|
||||
if (target == null) throw new FileNotFoundException();
|
||||
while ((length = in.read(buffer)) > 0)
|
||||
target.write(buffer, 0, length);
|
||||
}
|
||||
|
||||
// Delete the temp file
|
||||
temp1.delete();
|
||||
temp2.delete();
|
||||
|
||||
return true;
|
||||
} catch (Exception e) {
|
||||
Logger.error("ProcessRepoZip: Error!");
|
||||
e.printStackTrace();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Boolean result) {
|
||||
progressDialog.dismiss();
|
||||
if (result) {
|
||||
if (Shell.rootAccess() && mInstall) {
|
||||
new FlashZip(activity, mUri).exec();
|
||||
} else {
|
||||
Utils.showUriSnack(activity, mUri);
|
||||
}
|
||||
|
||||
} else {
|
||||
Toast.makeText(activity, R.string.process_error, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
package com.topjohnwu.magisk.asyncs;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.os.AsyncTask;
|
||||
|
||||
import com.topjohnwu.magisk.MagiskManager;
|
||||
import com.topjohnwu.magisk.utils.Shell;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
|
||||
/**
|
||||
* This class is only used for running root commands
|
||||
**/
|
||||
|
||||
public abstract class SerialTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {
|
||||
protected Activity activity;
|
||||
protected MagiskManager magiskManager;
|
||||
|
||||
public SerialTask() {}
|
||||
|
||||
public SerialTask(Activity context) {
|
||||
activity = context;
|
||||
magiskManager = Utils.getMagiskManager(context);
|
||||
}
|
||||
|
||||
@SafeVarargs
|
||||
public final void exec(Params... params) {
|
||||
if (!Shell.rootAccess()) return;
|
||||
executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, params);
|
||||
}
|
||||
}
|
@@ -14,7 +14,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.topjohnwu.magisk;
|
||||
package com.topjohnwu.magisk.components;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
@@ -26,6 +26,8 @@ import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.topjohnwu.magisk.R;
|
||||
|
||||
/**
|
||||
* @author dvdandroid
|
||||
*/
|
@@ -0,0 +1,13 @@
|
||||
package com.topjohnwu.magisk.components;
|
||||
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
|
||||
import com.topjohnwu.magisk.MagiskManager;
|
||||
|
||||
public class Activity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
public MagiskManager getApplicationContext() {
|
||||
return (MagiskManager) getApplication();
|
||||
}
|
||||
}
|
@@ -0,0 +1,161 @@
|
||||
package com.topjohnwu.magisk.components;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.support.annotation.StyleRes;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewStub;
|
||||
import android.widget.Button;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.topjohnwu.magisk.R;
|
||||
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
|
||||
public class AlertDialogBuilder extends AlertDialog.Builder {
|
||||
|
||||
@BindView(R.id.button_panel) LinearLayout buttons;
|
||||
@BindView(R.id.message_panel) LinearLayout messagePanel;
|
||||
|
||||
@BindView(R.id.negative) Button negative;
|
||||
@BindView(R.id.positive) Button positive;
|
||||
@BindView(R.id.neutral) Button neutral;
|
||||
@BindView(R.id.message) TextView messageView;
|
||||
@BindView(R.id.custom_view) ViewStub custom;
|
||||
|
||||
private DialogInterface.OnClickListener positiveListener;
|
||||
private DialogInterface.OnClickListener negativeListener;
|
||||
private DialogInterface.OnClickListener neutralListener;
|
||||
|
||||
private AlertDialog dialog;
|
||||
|
||||
public AlertDialogBuilder(@NonNull Context context) {
|
||||
super(context);
|
||||
setup();
|
||||
}
|
||||
|
||||
public AlertDialogBuilder(@NonNull Context context, @StyleRes int themeResId) {
|
||||
super(context, themeResId);
|
||||
setup();
|
||||
}
|
||||
|
||||
private void setup() {
|
||||
View v = LayoutInflater.from(getContext()).inflate(R.layout.alert_dialog, null);
|
||||
ButterKnife.bind(this, v);
|
||||
super.setView(v);
|
||||
negative.setVisibility(View.GONE);
|
||||
positive.setVisibility(View.GONE);
|
||||
neutral.setVisibility(View.GONE);
|
||||
buttons.setVisibility(View.GONE);
|
||||
messagePanel.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlertDialog.Builder setView(int layoutResId) {
|
||||
custom.setLayoutResource(layoutResId);
|
||||
custom.inflate();
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlertDialog.Builder setView(View view) {
|
||||
ViewGroup parent = (ViewGroup) custom.getParent();
|
||||
int idx = parent.indexOfChild(custom);
|
||||
parent.removeView(custom);
|
||||
parent.addView(view, idx);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlertDialog.Builder setMessage(@Nullable CharSequence message) {
|
||||
messageView.setText(message);
|
||||
messagePanel.setVisibility(View.VISIBLE);
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlertDialog.Builder setMessage(@StringRes int messageId) {
|
||||
return setMessage(getContext().getString(messageId));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlertDialog.Builder setPositiveButton(CharSequence text, DialogInterface.OnClickListener listener) {
|
||||
buttons.setVisibility(View.VISIBLE);
|
||||
positive.setVisibility(View.VISIBLE);
|
||||
positive.setText(text);
|
||||
positiveListener = listener;
|
||||
positive.setOnClickListener((v) -> {
|
||||
if (positiveListener != null) {
|
||||
positiveListener.onClick(dialog, DialogInterface.BUTTON_POSITIVE);
|
||||
}
|
||||
dialog.dismiss();
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlertDialog.Builder setPositiveButton(@StringRes int textId, DialogInterface.OnClickListener listener) {
|
||||
return setPositiveButton(getContext().getString(textId), listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlertDialog.Builder setNegativeButton(CharSequence text, DialogInterface.OnClickListener listener) {
|
||||
buttons.setVisibility(View.VISIBLE);
|
||||
negative.setVisibility(View.VISIBLE);
|
||||
negative.setText(text);
|
||||
negativeListener = listener;
|
||||
negative.setOnClickListener((v) -> {
|
||||
if (negativeListener != null) {
|
||||
negativeListener.onClick(dialog, DialogInterface.BUTTON_NEGATIVE);
|
||||
}
|
||||
dialog.dismiss();
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlertDialog.Builder setNegativeButton(@StringRes int textId, DialogInterface.OnClickListener listener) {
|
||||
return setNegativeButton(getContext().getString(textId), listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlertDialog.Builder setNeutralButton(CharSequence text, DialogInterface.OnClickListener listener) {
|
||||
buttons.setVisibility(View.VISIBLE);
|
||||
neutral.setVisibility(View.VISIBLE);
|
||||
neutral.setText(text);
|
||||
neutralListener = listener;
|
||||
neutral.setOnClickListener((v) -> {
|
||||
if (neutralListener != null) {
|
||||
neutralListener.onClick(dialog, DialogInterface.BUTTON_NEUTRAL);
|
||||
}
|
||||
dialog.dismiss();
|
||||
});
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlertDialog.Builder setNeutralButton(@StringRes int textId, DialogInterface.OnClickListener listener) {
|
||||
return setNeutralButton(getContext().getString(textId), listener);
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlertDialog create() {
|
||||
dialog = super.create();
|
||||
return dialog;
|
||||
}
|
||||
|
||||
@Override
|
||||
public AlertDialog show() {
|
||||
create();
|
||||
dialog.show();
|
||||
return dialog;
|
||||
}
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
package com.topjohnwu.magisk.components;
|
||||
|
||||
import com.topjohnwu.magisk.MagiskManager;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
|
||||
public class Fragment extends android.support.v4.app.Fragment {
|
||||
|
||||
public MagiskManager getApplication() {
|
||||
return Utils.getMagiskManager(getActivity());
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
package com.topjohnwu.magisk.components;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
|
||||
import com.topjohnwu.magisk.MagiskManager;
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.utils.Logger;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
|
||||
import us.feras.mdv.MarkdownView;
|
||||
|
||||
public class MarkDownWindow {
|
||||
|
||||
public MarkDownWindow(String title, String url, Context context) {
|
||||
MagiskManager magiskManager = Utils.getMagiskManager(context);
|
||||
AlertDialog.Builder alert = new AlertDialog.Builder(context);
|
||||
alert.setTitle(title);
|
||||
|
||||
Logger.dev("WebView: URL = " + url);
|
||||
|
||||
MarkdownView md = new MarkdownView(context);
|
||||
md.loadMarkdownFile(url, "file:///android_asset/" +
|
||||
(magiskManager.isDarkTheme ? "dark" : "light") + ".css");
|
||||
|
||||
alert.setView(md);
|
||||
alert.setNegativeButton(R.string.close, (dialog, id) -> dialog.dismiss());
|
||||
alert.show();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,39 @@
|
||||
package com.topjohnwu.magisk.components;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.support.design.widget.Snackbar;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import butterknife.ButterKnife;
|
||||
|
||||
public class SnackbarMaker {
|
||||
|
||||
public static Snackbar make(Activity activity, CharSequence text, int duration) {
|
||||
View view = activity.findViewById(android.R.id.content);
|
||||
return make(view, text, duration);
|
||||
}
|
||||
|
||||
public static Snackbar make(Activity activity, @StringRes int resId, int duration) {
|
||||
return make(activity, activity.getString(resId), duration);
|
||||
}
|
||||
|
||||
public static Snackbar make(View view, CharSequence text, int duration) {
|
||||
Snackbar snack = Snackbar.make(view, text, duration);
|
||||
setup(snack);
|
||||
return snack;
|
||||
}
|
||||
|
||||
public static Snackbar make(View view, @StringRes int resId, int duration) {
|
||||
Snackbar snack = Snackbar.make(view, resId, duration);
|
||||
setup(snack);
|
||||
return snack;
|
||||
}
|
||||
|
||||
private static void setup(Snackbar snack) {
|
||||
TextView text = ButterKnife.findById(snack.getView(), android.support.design.R.id.snackbar_text);
|
||||
text.setMaxLines(Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,78 @@
|
||||
package com.topjohnwu.magisk.database;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
|
||||
import com.topjohnwu.magisk.module.Repo;
|
||||
import com.topjohnwu.magisk.utils.Logger;
|
||||
import com.topjohnwu.magisk.utils.ValueSortedMap;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
public class RepoDatabaseHelper extends SQLiteOpenHelper {
|
||||
|
||||
private static final int DATABASE_VER = 1;
|
||||
private static final String TABLE_NAME = "repos";
|
||||
|
||||
public RepoDatabaseHelper(Context context) {
|
||||
super(context, "repo.db", null, DATABASE_VER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
onUpgrade(db, 0, DATABASE_VER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
if (oldVersion == 0) {
|
||||
db.execSQL(
|
||||
"CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " " +
|
||||
"(id TEXT, name TEXT, version TEXT, versionCode INT, " +
|
||||
"author TEXT, description TEXT, repo_name TEXT, last_update INT, " +
|
||||
"PRIMARY KEY(id))");
|
||||
}
|
||||
// No upgrades yet
|
||||
}
|
||||
|
||||
public void addRepoMap(ValueSortedMap<String, Repo> map) {
|
||||
SQLiteDatabase db = getWritableDatabase();
|
||||
Collection<Repo> list = map.values();
|
||||
for (Repo repo : list) {
|
||||
db.replace(TABLE_NAME, null, repo.getContentValues());
|
||||
}
|
||||
db.close();
|
||||
}
|
||||
|
||||
public void clearRepo() {
|
||||
SQLiteDatabase db = getWritableDatabase();
|
||||
db.delete(TABLE_NAME, null, null);
|
||||
db.close();
|
||||
}
|
||||
|
||||
public void removeRepo(ValueSortedMap<String, Repo> map) {
|
||||
SQLiteDatabase db = getWritableDatabase();
|
||||
Collection<Repo> list = map.values();
|
||||
for (Repo repo : list) {
|
||||
db.delete(TABLE_NAME, "id=?", new String[] { repo.getId() });
|
||||
}
|
||||
db.close();
|
||||
}
|
||||
|
||||
public ValueSortedMap<String, Repo> getRepoMap() {
|
||||
ValueSortedMap<String, Repo> ret = new ValueSortedMap<>();
|
||||
SQLiteDatabase db = getReadableDatabase();
|
||||
Repo repo;
|
||||
try (Cursor c = db.query(TABLE_NAME, null, null, null, null, null, null)) {
|
||||
while (c.moveToNext()) {
|
||||
repo = new Repo(c);
|
||||
Logger.dev("Load from cache: " + repo.getId());
|
||||
ret.put(repo.getId(), repo);
|
||||
}
|
||||
}
|
||||
db.close();
|
||||
return ret;
|
||||
}
|
||||
}
|
@@ -1,4 +1,4 @@
|
||||
package com.topjohnwu.magisk.superuser;
|
||||
package com.topjohnwu.magisk.database;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.pm.PackageManager;
|
||||
@@ -6,6 +6,8 @@ import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
|
||||
import com.topjohnwu.magisk.superuser.Policy;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
@@ -20,15 +22,18 @@ public class SuDatabaseHelper extends SQLiteOpenHelper {
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
db.execSQL(
|
||||
"CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " " +
|
||||
"(uid INT, package_name TEXT, app_name TEXT, policy INT, " +
|
||||
"until INT, logging INT, notification INT, " +
|
||||
"PRIMARY KEY(uid))");
|
||||
onUpgrade(db, 0, DATABASE_VER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
if (oldVersion == 0) {
|
||||
db.execSQL(
|
||||
"CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " " +
|
||||
"(uid INT, package_name TEXT, app_name TEXT, policy INT, " +
|
||||
"until INT, logging INT, notification INT, " +
|
||||
"PRIMARY KEY(uid))");
|
||||
}
|
||||
// Currently new database, no upgrading
|
||||
}
|
||||
|
||||
@@ -43,8 +48,9 @@ public class SuDatabaseHelper extends SQLiteOpenHelper {
|
||||
Policy policy = null;
|
||||
SQLiteDatabase db = getReadableDatabase();
|
||||
try (Cursor c = db.query(TABLE_NAME, null, "uid=?", new String[] { String.valueOf(uid) }, null, null, null)) {
|
||||
if (c.moveToNext())
|
||||
if (c.moveToNext()) {
|
||||
policy = new Policy(c);
|
||||
}
|
||||
}
|
||||
db.close();
|
||||
return policy;
|
@@ -1,11 +1,12 @@
|
||||
package com.topjohnwu.magisk.superuser;
|
||||
package com.topjohnwu.magisk.database;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
|
||||
import com.topjohnwu.magisk.Global;
|
||||
import com.topjohnwu.magisk.MagiskManager;
|
||||
import com.topjohnwu.magisk.superuser.SuLogEntry;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
@@ -15,20 +16,26 @@ public class SuLogDatabaseHelper extends SQLiteOpenHelper {
|
||||
private static final int DATABASE_VER = 1;
|
||||
private static final String TABLE_NAME = "logs";
|
||||
|
||||
private MagiskManager magiskManager;
|
||||
|
||||
public SuLogDatabaseHelper(Context context) {
|
||||
super(context, "sulog.db", null, DATABASE_VER);
|
||||
magiskManager = (MagiskManager) context.getApplicationContext();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(SQLiteDatabase db) {
|
||||
db.execSQL(
|
||||
"CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (id INTEGER PRIMARY KEY AUTOINCREMENT, " +
|
||||
"from_uid INT, package_name TEXT, app_name TEXT, from_pid INT, " +
|
||||
"to_uid INT, action INT, time INT, command TEXT)");
|
||||
onUpgrade(db, 0, DATABASE_VER);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
|
||||
if (oldVersion == 0) {
|
||||
db.execSQL(
|
||||
"CREATE TABLE IF NOT EXISTS " + TABLE_NAME + " (id INTEGER PRIMARY KEY AUTOINCREMENT, " +
|
||||
"from_uid INT, package_name TEXT, app_name TEXT, from_pid INT, " +
|
||||
"to_uid INT, action INT, time INT, command TEXT)");
|
||||
}
|
||||
// Currently new database, no upgrading
|
||||
}
|
||||
|
||||
@@ -57,10 +64,11 @@ public class SuLogDatabaseHelper extends SQLiteOpenHelper {
|
||||
SQLiteDatabase db = getWritableDatabase();
|
||||
// Clear outdated logs
|
||||
db.delete(TABLE_NAME, "time < ?", new String[] { String.valueOf(
|
||||
System.currentTimeMillis() / 1000 - Global.Configs.suLogTimeout * 86400) });
|
||||
System.currentTimeMillis() / 1000 - magiskManager.suLogTimeout * 86400) });
|
||||
try (Cursor c = db.query(TABLE_NAME, null, selection, null, null, null, "time DESC")) {
|
||||
while (c.moveToNext())
|
||||
while (c.moveToNext()) {
|
||||
ret.add(new SuLogEntry(c));
|
||||
}
|
||||
}
|
||||
db.close();
|
||||
return ret;
|
@@ -1,6 +1,7 @@
|
||||
package com.topjohnwu.magisk.module;
|
||||
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.topjohnwu.magisk.utils.Logger;
|
||||
@@ -9,66 +10,71 @@ import java.util.List;
|
||||
|
||||
public abstract class BaseModule implements Comparable<BaseModule> {
|
||||
|
||||
protected String mId, mName, mVersion, mAuthor, mDescription, mSupportUrl, mDonateUrl;
|
||||
protected boolean mIsCacheModule = false;
|
||||
protected int mVersionCode = 0;
|
||||
private String mId, mName, mVersion, mAuthor, mDescription;
|
||||
private int mVersionCode = 0;
|
||||
|
||||
protected BaseModule() {}
|
||||
|
||||
protected BaseModule(Cursor c) {
|
||||
mId = c.getString(c.getColumnIndex("id"));
|
||||
mName = c.getString(c.getColumnIndex("name"));
|
||||
mVersion = c.getString(c.getColumnIndex("version"));
|
||||
mVersionCode = c.getInt(c.getColumnIndex("versionCode"));
|
||||
mAuthor = c.getString(c.getColumnIndex("author"));
|
||||
mDescription = c.getString(c.getColumnIndex("description"));
|
||||
}
|
||||
|
||||
protected void parseProps(List<String> props) throws CacheModException { parseProps(props.toArray(new String[props.size()])); }
|
||||
|
||||
protected void parseProps(String[] props) throws CacheModException {
|
||||
for (String line : props) {
|
||||
String[] prop = line.split("=", 2);
|
||||
if (prop.length != 2) {
|
||||
if (prop.length != 2)
|
||||
continue;
|
||||
}
|
||||
|
||||
String key = prop[0].trim();
|
||||
if (key.charAt(0) == '#') {
|
||||
if (key.charAt(0) == '#')
|
||||
continue;
|
||||
}
|
||||
|
||||
switch (key) {
|
||||
case "id":
|
||||
this.mId = prop[1];
|
||||
mId = prop[1];
|
||||
break;
|
||||
case "name":
|
||||
this.mName = prop[1];
|
||||
mName = prop[1];
|
||||
break;
|
||||
case "version":
|
||||
this.mVersion = prop[1];
|
||||
mVersion = prop[1];
|
||||
break;
|
||||
case "versionCode":
|
||||
try {
|
||||
this.mVersionCode = Integer.parseInt(prop[1]);
|
||||
mVersionCode = Integer.parseInt(prop[1]);
|
||||
} catch (NumberFormatException ignored) {}
|
||||
break;
|
||||
case "author":
|
||||
this.mAuthor = prop[1];
|
||||
mAuthor = prop[1];
|
||||
break;
|
||||
case "description":
|
||||
this.mDescription = prop[1];
|
||||
break;
|
||||
case "support":
|
||||
this.mSupportUrl = prop[1];
|
||||
break;
|
||||
case "donate":
|
||||
this.mDonateUrl = prop[1];
|
||||
mDescription = prop[1];
|
||||
break;
|
||||
case "cacheModule":
|
||||
this.mIsCacheModule = Boolean.parseBoolean(prop[1]);
|
||||
if (Boolean.parseBoolean(prop[1]))
|
||||
throw new CacheModException(mId);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (mIsCacheModule)
|
||||
throw new CacheModException(mId);
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return mName;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
mName = name;
|
||||
}
|
||||
|
||||
public String getVersion() {
|
||||
return mVersion;
|
||||
}
|
||||
@@ -77,7 +83,13 @@ public abstract class BaseModule implements Comparable<BaseModule> {
|
||||
return mAuthor;
|
||||
}
|
||||
|
||||
public String getId() {return mId; }
|
||||
public String getId() {
|
||||
return mId;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
mId = id;
|
||||
}
|
||||
|
||||
public String getDescription() {
|
||||
return mDescription;
|
||||
@@ -87,22 +99,14 @@ public abstract class BaseModule implements Comparable<BaseModule> {
|
||||
return mVersionCode;
|
||||
}
|
||||
|
||||
public String getDonateUrl() {
|
||||
return mDonateUrl;
|
||||
}
|
||||
|
||||
public String getSupportUrl() {
|
||||
return mSupportUrl;
|
||||
}
|
||||
|
||||
public static class CacheModException extends Exception {
|
||||
public CacheModException(String id) {
|
||||
Logger.dev("Cache mods are no longer supported! id: " + id);
|
||||
Logger.error("Cache mods are no longer supported! id: " + id);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(@NonNull BaseModule o) {
|
||||
return this.getName().toLowerCase().compareTo(o.getName().toLowerCase());
|
||||
public int compareTo(@NonNull BaseModule module) {
|
||||
return this.getName().toLowerCase().compareTo(module.getName().toLowerCase());
|
||||
}
|
||||
}
|
||||
|
@@ -16,15 +16,16 @@ public class Module extends BaseModule {
|
||||
mDisableFile = path + "/disable";
|
||||
mUpdateFile = path + "/update";
|
||||
|
||||
if (mId == null) {
|
||||
if (getId() == null) {
|
||||
int sep = path.lastIndexOf('/');
|
||||
mId = path.substring(sep + 1);
|
||||
setId(path.substring(sep + 1));
|
||||
}
|
||||
|
||||
if (mName == null)
|
||||
mName = mId;
|
||||
if (getName() == null) {
|
||||
setName(getId());
|
||||
}
|
||||
|
||||
Logger.dev("Creating Data, id: " + mId);
|
||||
Logger.dev("Creating Module, id: " + getId());
|
||||
|
||||
mEnable = !Utils.itemExist(mDisableFile);
|
||||
mRemove = Utils.itemExist(mRemoveFile);
|
||||
|
@@ -1,180 +0,0 @@
|
||||
package com.topjohnwu.magisk.module;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.reflect.TypeToken;
|
||||
import com.topjohnwu.magisk.Global;
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.utils.Async;
|
||||
import com.topjohnwu.magisk.utils.Logger;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
import com.topjohnwu.magisk.utils.ValueSortedMap;
|
||||
import com.topjohnwu.magisk.utils.WebService;
|
||||
|
||||
import org.json.JSONArray;
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
|
||||
public class ModuleHelper {
|
||||
|
||||
private static final String MAGISK_PATH = "/magisk";
|
||||
|
||||
private static final int GSON_DB_VER = 1;
|
||||
private static final String ETAG_KEY = "ETag";
|
||||
private static final String VERSION_KEY = "version";
|
||||
private static final String REPO_KEY = "repomap";
|
||||
private static final String FILE_KEY = "RepoMap";
|
||||
|
||||
public static void createModuleMap() {
|
||||
Logger.dev("ModuleHelper: Loading modules");
|
||||
|
||||
Global.Data.moduleMap = new ValueSortedMap<>();
|
||||
|
||||
for (String path : Utils.getModList(MAGISK_PATH)) {
|
||||
Logger.dev("ModuleHelper: Adding modules from " + path);
|
||||
Module module;
|
||||
try {
|
||||
module = new Module(path);
|
||||
Global.Data.moduleMap.put(module.getId(), module);
|
||||
} catch (BaseModule.CacheModException ignored) {}
|
||||
}
|
||||
|
||||
Logger.dev("ModuleHelper: Data load done");
|
||||
}
|
||||
|
||||
public static void createRepoMap(Context context) {
|
||||
Logger.dev("ModuleHelper: Loading repos");
|
||||
|
||||
SharedPreferences prefs = context.getSharedPreferences(FILE_KEY, Context.MODE_PRIVATE);
|
||||
|
||||
Global.Data.repoMap = new ValueSortedMap<>();
|
||||
|
||||
Gson gson = new Gson();
|
||||
String jsonString;
|
||||
|
||||
int cachedVersion = prefs.getInt(VERSION_KEY, 0);
|
||||
if (cachedVersion != GSON_DB_VER) {
|
||||
// Ignore incompatible cached database
|
||||
jsonString = null;
|
||||
} else {
|
||||
jsonString = prefs.getString(REPO_KEY, null);
|
||||
}
|
||||
|
||||
Map<String, Repo> cached = null;
|
||||
|
||||
if (jsonString != null) {
|
||||
cached = gson.fromJson(jsonString, new TypeToken<ValueSortedMap<String, Repo>>(){}.getType());
|
||||
}
|
||||
|
||||
if (cached == null) {
|
||||
cached = new ValueSortedMap<>();
|
||||
}
|
||||
|
||||
// Get cached ETag to add in the request header
|
||||
String etag = prefs.getString(ETAG_KEY, "");
|
||||
Map<String, String> header = new HashMap<>();
|
||||
header.put("If-None-Match", etag);
|
||||
|
||||
// Making a request to main URL for repo info
|
||||
jsonString = WebService.request(
|
||||
context.getString(R.string.url_main), WebService.GET, null, header, false);
|
||||
|
||||
if (!jsonString.isEmpty()) {
|
||||
try {
|
||||
JSONArray jsonArray = new JSONArray(jsonString);
|
||||
// If it gets to this point, the response is valid, update ETag
|
||||
etag = WebService.getLastResponseHeader().get(ETAG_KEY).get(0);
|
||||
// Maybe bug in Android build tools, sometimes the ETag has crap in it...
|
||||
etag = etag.substring(etag.indexOf('\"'), etag.lastIndexOf('\"') + 1);
|
||||
|
||||
// Update repo info
|
||||
for (int i = 0; i < jsonArray.length(); i++) {
|
||||
JSONObject jsonobject = jsonArray.getJSONObject(i);
|
||||
String id = jsonobject.getString("description");
|
||||
String name = jsonobject.getString("name");
|
||||
String lastUpdate = jsonobject.getString("pushed_at");
|
||||
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US);
|
||||
Date updatedDate;
|
||||
try {
|
||||
updatedDate = format.parse(lastUpdate);
|
||||
} catch (ParseException e) {
|
||||
continue;
|
||||
}
|
||||
Repo repo = cached.get(id);
|
||||
try {
|
||||
if (repo == null) {
|
||||
Logger.dev("ModuleHelper: Create new repo " + id);
|
||||
repo = new Repo(context, name, updatedDate);
|
||||
} else {
|
||||
Logger.dev("ModuleHelper: Update cached repo " + id);
|
||||
repo.update(updatedDate);
|
||||
}
|
||||
if (repo.getId() != null) {
|
||||
Global.Data.repoMap.put(id, repo);
|
||||
}
|
||||
} catch (BaseModule.CacheModException ignored) {}
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
} else {
|
||||
// Use cached if no internet or no updates
|
||||
Logger.dev("ModuleHelper: No updates, use cached");
|
||||
Global.Data.repoMap.putAll(cached);
|
||||
}
|
||||
|
||||
prefs.edit()
|
||||
.putInt(VERSION_KEY, GSON_DB_VER)
|
||||
.putString(REPO_KEY, gson.toJson(Global.Data.repoMap))
|
||||
.putString(ETAG_KEY, etag)
|
||||
.apply();
|
||||
|
||||
Logger.dev("ModuleHelper: Repo load done");
|
||||
}
|
||||
|
||||
public static void getModuleList(List<Module> moduleList) {
|
||||
moduleList.clear();
|
||||
moduleList.addAll(Global.Data.moduleMap.values());
|
||||
}
|
||||
|
||||
public static void getRepoLists(List<Repo> update, List<Repo> installed, List<Repo> others) {
|
||||
update.clear();
|
||||
installed.clear();
|
||||
others.clear();
|
||||
for (Repo repo : Global.Data.repoMap.values()) {
|
||||
Module module = Global.Data.moduleMap.get(repo.getId());
|
||||
if (module != null) {
|
||||
if (repo.getVersionCode() > module.getVersionCode()) {
|
||||
update.add(repo);
|
||||
} else {
|
||||
installed.add(repo);
|
||||
}
|
||||
} else {
|
||||
others.add(repo);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void clearRepoCache(Context context) {
|
||||
SharedPreferences repoMap = context.getSharedPreferences(FILE_KEY, Context.MODE_PRIVATE);
|
||||
repoMap.edit()
|
||||
.remove(ETAG_KEY)
|
||||
.remove(VERSION_KEY)
|
||||
.apply();
|
||||
Global.Events.repoLoadDone.isTriggered = false;
|
||||
new Async.LoadRepos(context).exec();
|
||||
Toast.makeText(context, R.string.repo_cache_cleared, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
}
|
@@ -1,53 +1,71 @@
|
||||
package com.topjohnwu.magisk.module;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.utils.Logger;
|
||||
import com.topjohnwu.magisk.utils.WebService;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
public class Repo extends BaseModule {
|
||||
private String mLogUrl, mManifestUrl, mZipUrl;
|
||||
|
||||
private static final String FILE_URL = "https://raw.githubusercontent.com/Magisk-Modules-Repo/%s/master/%s";
|
||||
private static final String ZIP_URL = "https://github.com/Magisk-Modules-Repo/%s/archive/master.zip";
|
||||
|
||||
private String repoName;
|
||||
private Date mLastUpdate;
|
||||
|
||||
public Repo(Context context, String name, Date lastUpdate) throws CacheModException {
|
||||
public Repo(String name, Date lastUpdate) throws CacheModException {
|
||||
mLastUpdate = lastUpdate;
|
||||
mLogUrl = context.getString(R.string.file_url, name, "changelog.txt");
|
||||
mManifestUrl = context.getString(R.string.file_url, name, "module.prop");
|
||||
mZipUrl = context.getString(R.string.zip_url, name);
|
||||
repoName = name;
|
||||
update();
|
||||
}
|
||||
|
||||
public Repo(Cursor c) {
|
||||
super(c);
|
||||
repoName = c.getString(c.getColumnIndex("repo_name"));
|
||||
mLastUpdate = new Date(c.getLong(c.getColumnIndex("last_update")));
|
||||
}
|
||||
|
||||
public void update() throws CacheModException {
|
||||
Logger.dev("Repo: Re-fetch prop");
|
||||
String props = WebService.request(mManifestUrl, WebService.GET, true);
|
||||
String props = WebService.request(getManifestUrl(), WebService.GET, true);
|
||||
String lines[] = props.split("\\n");
|
||||
parseProps(lines);
|
||||
}
|
||||
|
||||
public void update(Date lastUpdate) throws CacheModException {
|
||||
Logger.dev("Repo: Old: " + mLastUpdate);
|
||||
Logger.dev("Repo: New: " + lastUpdate);
|
||||
if (mIsCacheModule)
|
||||
throw new CacheModException(mId);
|
||||
Logger.dev("Repo: Local: " + mLastUpdate + " Remote: " + lastUpdate);
|
||||
if (lastUpdate.after(mLastUpdate)) {
|
||||
mLastUpdate = lastUpdate;
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
public String getZipUrl() {
|
||||
return mZipUrl;
|
||||
public ContentValues getContentValues() {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put("id", getId());
|
||||
values.put("name", getName());
|
||||
values.put("version", getVersion());
|
||||
values.put("versionCode", getVersionCode());
|
||||
values.put("author", getAuthor());
|
||||
values.put("description", getDescription());
|
||||
values.put("repo_name", repoName);
|
||||
values.put("last_update", mLastUpdate.getTime());
|
||||
return values;
|
||||
}
|
||||
|
||||
public String getLogUrl() {
|
||||
return mLogUrl;
|
||||
public String getZipUrl() {
|
||||
return String.format(ZIP_URL, repoName);
|
||||
}
|
||||
|
||||
public String getManifestUrl() {
|
||||
return mManifestUrl;
|
||||
return String.format(FILE_URL, repoName, "module.prop");
|
||||
}
|
||||
|
||||
public String getDetailUrl() {
|
||||
return String.format(FILE_URL, repoName, "README.md");
|
||||
}
|
||||
|
||||
public Date getLastUpdate() {
|
||||
|
@@ -3,24 +3,14 @@ package com.topjohnwu.magisk.receivers;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.topjohnwu.magisk.Global;
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.utils.Async;
|
||||
import com.topjohnwu.magisk.services.BootupIntentService;
|
||||
|
||||
public class BootReceiver extends BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
Global.initSuAccess();
|
||||
Global.updateMagiskInfo();
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
|
||||
if (prefs.getBoolean("magiskhide", false) && !Global.Info.disabled && Global.Info.magiskVersion > 10.3) {
|
||||
Toast.makeText(context, R.string.start_magiskhide, Toast.LENGTH_SHORT).show();
|
||||
new Async.MagiskHide(true).enable();
|
||||
}
|
||||
context.startService(new Intent(context, BootupIntentService.class));
|
||||
}
|
||||
|
||||
}
|
||||
|
@@ -1,5 +1,6 @@
|
||||
package com.topjohnwu.magisk.receivers;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.DownloadManager;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
@@ -12,7 +13,7 @@ import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
|
||||
public abstract class DownloadReceiver extends BroadcastReceiver {
|
||||
public Context mContext;
|
||||
public Activity activity;
|
||||
public String mFilename;
|
||||
long downloadID;
|
||||
|
||||
@@ -20,7 +21,7 @@ public abstract class DownloadReceiver extends BroadcastReceiver {
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
mContext = context;
|
||||
activity = (Activity) context;
|
||||
DownloadManager downloadManager = (DownloadManager) context.getSystemService(Context.DOWNLOAD_SERVICE);
|
||||
String action = intent.getAction();
|
||||
if (DownloadManager.ACTION_DOWNLOAD_COMPLETE.equals(action)) {
|
||||
|
@@ -1,67 +0,0 @@
|
||||
package com.topjohnwu.magisk.receivers;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import com.topjohnwu.magisk.Global;
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.utils.Async;
|
||||
import com.topjohnwu.magisk.utils.Shell;
|
||||
import com.topjohnwu.magisk.utils.ZipUtils;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
public class MagiskDlReceiver extends DownloadReceiver {
|
||||
|
||||
String mBoot;
|
||||
boolean mEnc, mVerity;
|
||||
|
||||
public MagiskDlReceiver(String bootImage, boolean keepEnc, boolean keepVerity) {
|
||||
mBoot = bootImage;
|
||||
mEnc = keepEnc;
|
||||
mVerity = keepVerity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDownloadDone(Uri uri) {
|
||||
new Async.FlashZIP(mContext, uri, mFilename) {
|
||||
|
||||
@Override
|
||||
protected void preProcessing() throws Throwable {
|
||||
Shell.su(
|
||||
"echo \"BOOTIMAGE=/dev/block/" + mBoot + "\" > /dev/.magisk",
|
||||
"echo \"KEEPFORCEENCRYPT=" + String.valueOf(mEnc) + "\" >> /dev/.magisk",
|
||||
"echo \"KEEPVERITY=" + String.valueOf(mVerity) + "\" >> /dev/.magisk"
|
||||
);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean unzipAndCheck() {
|
||||
publishProgress(mContext.getString(R.string.zip_install_unzip_zip_msg));
|
||||
if (Shell.rootAccess()) {
|
||||
// We might not have busybox yet, unzip with Java
|
||||
// We will have complete busybox after Magisk installation
|
||||
ZipUtils.unzip(mCachedFile, new File(mCachedFile.getParent(), "magisk"));
|
||||
Shell.su(
|
||||
"mkdir -p " + Async.TMP_FOLDER_PATH + "/magisk",
|
||||
"cp -af " + mCachedFile.getParent() + "/magisk/. " + Async.TMP_FOLDER_PATH + "/magisk",
|
||||
"mv -f " + mCachedFile.getParent() + "/magisk/META-INF " + mCachedFile.getParent() + "/META-INF"
|
||||
);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSuccess() {
|
||||
new Async.RootTask<Void, Void, Void>() {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
Shell.su("setprop magisk.version "
|
||||
+ String.valueOf(Global.Info.remoteMagiskVersion));
|
||||
return null;
|
||||
}
|
||||
}.exec();
|
||||
super.onSuccess();
|
||||
}
|
||||
}.exec();
|
||||
}
|
||||
}
|
@@ -1,42 +0,0 @@
|
||||
package com.topjohnwu.magisk.receivers;
|
||||
|
||||
import android.net.Uri;
|
||||
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.utils.Async;
|
||||
import com.topjohnwu.magisk.utils.ByteArrayInOutStream;
|
||||
import com.topjohnwu.magisk.utils.ZipUtils;
|
||||
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class RepoDlReceiver extends DownloadReceiver {
|
||||
@Override
|
||||
public void onDownloadDone(Uri uri) {
|
||||
// Flash the zip
|
||||
new Async.FlashZIP(mContext, uri, mFilename){
|
||||
@Override
|
||||
protected void preProcessing() throws Throwable {
|
||||
// Process and sign the zip
|
||||
publishProgress(mContext.getString(R.string.zip_install_process_zip_msg));
|
||||
ByteArrayInOutStream buffer = new ByteArrayInOutStream();
|
||||
|
||||
// First remove top folder (the folder with the repo name) in Github source zip
|
||||
ZipUtils.removeTopFolder(mContext.getContentResolver().openInputStream(mUri), buffer);
|
||||
|
||||
// Then sign the zip for the first time
|
||||
ZipUtils.signZip(mContext, buffer.getInputStream(), buffer, false);
|
||||
|
||||
// Adjust the zip to prevent unzip issues
|
||||
ZipUtils.adjustZip(buffer);
|
||||
|
||||
// Finally, sign the whole zip file again
|
||||
ZipUtils.signZip(mContext, buffer.getInputStream(), buffer, true);
|
||||
|
||||
// Write it back to the downloaded zip
|
||||
try (OutputStream out = mContext.getContentResolver().openOutputStream(mUri)) {
|
||||
buffer.writeTo(out);
|
||||
}
|
||||
}
|
||||
}.exec();
|
||||
}
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
package com.topjohnwu.magisk.services;
|
||||
|
||||
import android.app.IntentService;
|
||||
import android.content.Intent;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.topjohnwu.magisk.MagiskManager;
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.utils.Shell;
|
||||
import com.topjohnwu.magisk.utils.Utils;
|
||||
|
||||
public class BootupIntentService extends IntentService {
|
||||
|
||||
public BootupIntentService() {
|
||||
super("BootupIntentService");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onHandleIntent(Intent intent) {
|
||||
MagiskManager magiskManager = Utils.getMagiskManager(this);
|
||||
magiskManager.initSuAccess();
|
||||
magiskManager.updateMagiskInfo();
|
||||
if (magiskManager.prefs.getBoolean("magiskhide", false) &&
|
||||
!magiskManager.disabled && !magiskManager.magiskHideStarted && magiskManager.magiskVersion > 11) {
|
||||
magiskManager.toast(R.string.start_magiskhide, Toast.LENGTH_LONG);
|
||||
Shell.su(true, MagiskManager.MAGISK_HIDE_PATH + "enable",
|
||||
"setprop persist.magisk.hide 1");
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
package com.topjohnwu.magisk.services;
|
||||
|
||||
import android.app.job.JobParameters;
|
||||
import android.app.job.JobService;
|
||||
|
||||
import com.topjohnwu.magisk.asyncs.CheckUpdates;
|
||||
|
||||
public class UpdateCheckService extends JobService {
|
||||
@Override
|
||||
public boolean onStartJob(JobParameters params) {
|
||||
new CheckUpdates(this, true){
|
||||
@Override
|
||||
protected Void doInBackground(Void... voids) {
|
||||
magiskManager.updateMagiskInfo();
|
||||
return super.doInBackground(voids);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void v) {
|
||||
jobFinished(params, false);
|
||||
super.onPostExecute(v);
|
||||
}
|
||||
}.exec();
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onStopJob(JobParameters params) {
|
||||
return true;
|
||||
}
|
||||
}
|
@@ -2,11 +2,10 @@ package com.topjohnwu.magisk.superuser;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
|
||||
import com.topjohnwu.magisk.Global;
|
||||
import com.topjohnwu.magisk.components.Activity;
|
||||
|
||||
public class RequestActivity extends AppCompatActivity {
|
||||
public class RequestActivity extends Activity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
@@ -18,9 +17,8 @@ public class RequestActivity extends AppCompatActivity {
|
||||
return;
|
||||
}
|
||||
|
||||
Global.initSuConfigs(this);
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent.setClass(this, SuRequestActivity.class);
|
||||
getApplicationContext().initSuConfigs();
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK).setClass(this, SuRequestActivity.class);
|
||||
startActivity(intent);
|
||||
finish();
|
||||
}
|
||||
|
@@ -6,8 +6,10 @@ import android.content.Intent;
|
||||
import android.os.Process;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.topjohnwu.magisk.Global;
|
||||
import com.topjohnwu.magisk.MagiskManager;
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.database.SuDatabaseHelper;
|
||||
import com.topjohnwu.magisk.database.SuLogDatabaseHelper;
|
||||
|
||||
import java.util.Date;
|
||||
|
||||
@@ -23,6 +25,8 @@ public class SuReceiver extends BroadcastReceiver {
|
||||
String command, action;
|
||||
Policy policy;
|
||||
|
||||
MagiskManager magiskManager = (MagiskManager) context.getApplicationContext();
|
||||
|
||||
if (intent == null) return;
|
||||
|
||||
fromUid = intent.getIntExtra("from.uid", -1);
|
||||
@@ -34,13 +38,15 @@ public class SuReceiver extends BroadcastReceiver {
|
||||
|
||||
SuDatabaseHelper suDbHelper = new SuDatabaseHelper(context);
|
||||
policy = suDbHelper.getPolicy(fromUid);
|
||||
if (policy == null) try {
|
||||
policy = new Policy(fromUid, context.getPackageManager());
|
||||
} catch (Throwable throwable) {
|
||||
return;
|
||||
if (policy == null) {
|
||||
try {
|
||||
policy = new Policy(fromUid, context.getPackageManager());
|
||||
} catch (Throwable throwable) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Global.initSuConfigs(context);
|
||||
magiskManager.initSuConfigs();
|
||||
|
||||
SuLogEntry log = new SuLogEntry(policy);
|
||||
|
||||
@@ -58,7 +64,7 @@ public class SuReceiver extends BroadcastReceiver {
|
||||
return;
|
||||
}
|
||||
|
||||
if (policy.notification && Global.Configs.suNotificationType == TOAST)
|
||||
if (policy.notification && magiskManager.suNotificationType == TOAST)
|
||||
Toast.makeText(context, message, Toast.LENGTH_SHORT).show();
|
||||
|
||||
if (policy.logging) {
|
||||
|
@@ -8,7 +8,6 @@ import android.net.LocalSocketAddress;
|
||||
import android.os.Bundle;
|
||||
import android.os.CountDownTimer;
|
||||
import android.os.FileObserver;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.text.TextUtils;
|
||||
import android.view.Window;
|
||||
import android.widget.ArrayAdapter;
|
||||
@@ -18,10 +17,12 @@ import android.widget.LinearLayout;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.topjohnwu.magisk.Global;
|
||||
import com.topjohnwu.magisk.MagiskManager;
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.utils.Async;
|
||||
import com.topjohnwu.magisk.utils.CallbackHandler;
|
||||
import com.topjohnwu.magisk.asyncs.ParallelTask;
|
||||
import com.topjohnwu.magisk.components.Activity;
|
||||
import com.topjohnwu.magisk.database.SuDatabaseHelper;
|
||||
import com.topjohnwu.magisk.utils.CallbackEvent;
|
||||
|
||||
import java.io.DataInputStream;
|
||||
import java.io.IOException;
|
||||
@@ -29,7 +30,7 @@ import java.io.IOException;
|
||||
import butterknife.BindView;
|
||||
import butterknife.ButterKnife;
|
||||
|
||||
public class SuRequestActivity extends AppCompatActivity implements CallbackHandler.EventListener {
|
||||
public class SuRequestActivity extends Activity implements CallbackEvent.Listener<Policy> {
|
||||
|
||||
private static final int[] timeoutList = {0, -1, 10, 20, 30, 60};
|
||||
private static final int SU_PROTOCOL_PARAM_MAX = 20;
|
||||
@@ -51,12 +52,13 @@ public class SuRequestActivity extends AppCompatActivity implements CallbackHand
|
||||
private String socketPath;
|
||||
private LocalSocket socket;
|
||||
private PackageManager pm;
|
||||
private MagiskManager magiskManager;
|
||||
|
||||
private int uid;
|
||||
private Policy policy;
|
||||
private CountDownTimer timer;
|
||||
private CallbackHandler.EventListener self;
|
||||
private CallbackHandler.Event event = null;
|
||||
private CallbackEvent.Listener<Policy> self;
|
||||
private CallbackEvent<Policy> event = null;
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
@@ -64,6 +66,7 @@ public class SuRequestActivity extends AppCompatActivity implements CallbackHand
|
||||
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||
|
||||
pm = getPackageManager();
|
||||
magiskManager = getApplicationContext();
|
||||
|
||||
Intent intent = getIntent();
|
||||
socketPath = intent.getStringExtra("socket");
|
||||
@@ -73,19 +76,20 @@ public class SuRequestActivity extends AppCompatActivity implements CallbackHand
|
||||
@Override
|
||||
public void onEvent(int fileEvent, String path) {
|
||||
if (fileEvent == FileObserver.DELETE_SELF) {
|
||||
if (event != null)
|
||||
if (event != null) {
|
||||
event.trigger();
|
||||
}
|
||||
finish();
|
||||
}
|
||||
}
|
||||
}.startWatching();
|
||||
|
||||
new SocketManager().exec();
|
||||
new SocketManager(this).exec();
|
||||
}
|
||||
|
||||
void showRequest() {
|
||||
|
||||
switch (Global.Configs.suResponseType) {
|
||||
switch (magiskManager.suResponseType) {
|
||||
case AUTO_DENY:
|
||||
handleAction(Policy.DENY, 0);
|
||||
return;
|
||||
@@ -108,7 +112,7 @@ public class SuRequestActivity extends AppCompatActivity implements CallbackHand
|
||||
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
|
||||
timeout.setAdapter(adapter);
|
||||
|
||||
timer = new CountDownTimer(Global.Configs.suRequestTimeout * 1000, 1000) {
|
||||
timer = new CountDownTimer(magiskManager.suRequestTimeout * 1000, 1000) {
|
||||
@Override
|
||||
public void onTick(long millisUntilFinished) {
|
||||
deny_btn.setText(getString(R.string.deny_with_str, "(" + millisUntilFinished / 1000 + ")"));
|
||||
@@ -141,13 +145,11 @@ public class SuRequestActivity extends AppCompatActivity implements CallbackHand
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTrigger(CallbackHandler.Event event) {
|
||||
Policy policy = (Policy) event.getResult();
|
||||
public void onTrigger(CallbackEvent<Policy> event) {
|
||||
Policy policy = event.getResult();
|
||||
String response = "socket:DENY";
|
||||
if (policy != null) {
|
||||
Global.Events.uidMap.remove(policy.uid);
|
||||
if (policy.policy == Policy.ALLOW)
|
||||
response = "socket:ALLOW";
|
||||
if (policy != null &&policy.policy == Policy.ALLOW ) {
|
||||
response = "socket:ALLOW";
|
||||
}
|
||||
try {
|
||||
socket.getOutputStream().write((response).getBytes());
|
||||
@@ -168,11 +170,15 @@ public class SuRequestActivity extends AppCompatActivity implements CallbackHand
|
||||
}
|
||||
}
|
||||
|
||||
private class SocketManager extends Async.NormalTask<Void, Void, Boolean> {
|
||||
private class SocketManager extends ParallelTask<Void, Void, Boolean> {
|
||||
|
||||
public SocketManager(Activity context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Boolean doInBackground(Void... params) {
|
||||
try{
|
||||
try {
|
||||
socket = new LocalSocket();
|
||||
socket.connect(new LocalSocketAddress(socketPath, LocalSocketAddress.Namespace.FILESYSTEM));
|
||||
|
||||
@@ -224,19 +230,20 @@ public class SuRequestActivity extends AppCompatActivity implements CallbackHand
|
||||
return;
|
||||
}
|
||||
boolean showRequest = false;
|
||||
event = Global.Events.uidMap.get(uid);
|
||||
event = magiskManager.uidSuRequest.get(uid);
|
||||
if (event == null) {
|
||||
showRequest = true;
|
||||
event = new CallbackHandler.Event() {
|
||||
event = new CallbackEvent<Policy>() {
|
||||
@Override
|
||||
public void trigger(Object result) {
|
||||
public void trigger(Policy result) {
|
||||
super.trigger(result);
|
||||
CallbackHandler.unRegister(this);
|
||||
unRegister();
|
||||
magiskManager.uidSuRequest.remove(uid);
|
||||
}
|
||||
};
|
||||
Global.Events.uidMap.put(uid, event);
|
||||
magiskManager.uidSuRequest.put(uid, event);
|
||||
}
|
||||
CallbackHandler.register(event, self);
|
||||
event.register(self);
|
||||
try {
|
||||
if (showRequest) {
|
||||
policy = new Policy(uid, pm);
|
||||
|
@@ -1,344 +0,0 @@
|
||||
package com.topjohnwu.magisk.utils;
|
||||
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.Context;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.AsyncTask;
|
||||
import android.provider.OpenableColumns;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.topjohnwu.magisk.Global;
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.adapters.ApplicationAdapter;
|
||||
import com.topjohnwu.magisk.module.ModuleHelper;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
public class Async {
|
||||
|
||||
public abstract static class RootTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {
|
||||
@SafeVarargs
|
||||
public final void exec(Params... params) {
|
||||
if (!Shell.rootAccess()) return;
|
||||
executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, params);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract static class NormalTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {
|
||||
@SafeVarargs
|
||||
public final void exec(Params... params) {
|
||||
executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, params);
|
||||
}
|
||||
}
|
||||
|
||||
public static final String UPDATE_JSON = "https://raw.githubusercontent.com/topjohnwu/MagiskManager/updates/magisk_update.json";
|
||||
public static final String MAGISK_HIDE_PATH = "/magisk/.core/magiskhide/";
|
||||
public static final String TMP_FOLDER_PATH = "/dev/tmp";
|
||||
|
||||
public static class CheckUpdates extends NormalTask<Void, Void, Void> {
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... voids) {
|
||||
String jsonStr = WebService.request(UPDATE_JSON, WebService.GET);
|
||||
try {
|
||||
JSONObject json = new JSONObject(jsonStr);
|
||||
JSONObject magisk = json.getJSONObject("magisk");
|
||||
Global.Info.remoteMagiskVersion = magisk.getDouble("versionCode");
|
||||
Global.Info.magiskLink = magisk.getString("link");
|
||||
Global.Info.releaseNoteLink = magisk.getString("note");
|
||||
} catch (JSONException ignored) {}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void v) {
|
||||
Global.Events.updateCheckDone.trigger();
|
||||
}
|
||||
}
|
||||
|
||||
public static void checkSafetyNet(Context context) {
|
||||
new SafetyNetHelper(context) {
|
||||
@Override
|
||||
public void handleResults(int i) {
|
||||
Global.Info.SNCheckResult = i;
|
||||
Global.Events.safetyNetDone.trigger();
|
||||
}
|
||||
}.requestTest();
|
||||
}
|
||||
|
||||
public static class LoadModules extends RootTask<Void, Void, Void> {
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... voids) {
|
||||
ModuleHelper.createModuleMap();
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void v) {
|
||||
Global.Events.moduleLoadDone.trigger();
|
||||
}
|
||||
}
|
||||
|
||||
public static class LoadRepos extends NormalTask<Void, Void, Void> {
|
||||
|
||||
private Context mContext;
|
||||
|
||||
public LoadRepos(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... voids) {
|
||||
ModuleHelper.createRepoMap(mContext);
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void v) {
|
||||
Global.Events.repoLoadDone.trigger();
|
||||
}
|
||||
}
|
||||
|
||||
public static class LoadApps extends RootTask<Void, Void, Void> {
|
||||
|
||||
private PackageManager pm;
|
||||
|
||||
public LoadApps(PackageManager packageManager) {
|
||||
pm = packageManager;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Void... voids) {
|
||||
Global.Data.appList = pm.getInstalledApplications(0);
|
||||
for (Iterator<ApplicationInfo> i = Global.Data.appList.iterator(); i.hasNext(); ) {
|
||||
ApplicationInfo info = i.next();
|
||||
if (ApplicationAdapter.BLACKLIST.contains(info.packageName) || !info.enabled)
|
||||
i.remove();
|
||||
}
|
||||
Collections.sort(Global.Data.appList, (a, b) -> a.loadLabel(pm).toString().toLowerCase()
|
||||
.compareTo(b.loadLabel(pm).toString().toLowerCase()));
|
||||
Global.Data.magiskHideList = Shell.su(Async.MAGISK_HIDE_PATH + "list");
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void v) {
|
||||
Global.Events.packageLoadDone.trigger();
|
||||
}
|
||||
}
|
||||
|
||||
public static class FlashZIP extends RootTask<Void, String, Integer> {
|
||||
|
||||
protected Uri mUri;
|
||||
protected File mCachedFile;
|
||||
private String mFilename;
|
||||
protected ProgressDialog progress;
|
||||
private Context mContext;
|
||||
|
||||
public FlashZIP(Context context, Uri uri, String filename) {
|
||||
mContext = context;
|
||||
mUri = uri;
|
||||
mFilename = filename;
|
||||
}
|
||||
|
||||
public FlashZIP(Context context, Uri uri) {
|
||||
mContext = context;
|
||||
mUri = uri;
|
||||
|
||||
// Try to get the filename ourselves
|
||||
Cursor c = mContext.getContentResolver().query(uri, null, null, null, null);
|
||||
if (c != null) {
|
||||
int nameIndex = c.getColumnIndex(OpenableColumns.DISPLAY_NAME);
|
||||
c.moveToFirst();
|
||||
if (nameIndex != -1) {
|
||||
mFilename = c.getString(nameIndex);
|
||||
}
|
||||
c.close();
|
||||
}
|
||||
if (mFilename == null) {
|
||||
int idx = uri.getPath().lastIndexOf('/');
|
||||
mFilename = uri.getPath().substring(idx + 1);
|
||||
}
|
||||
}
|
||||
|
||||
protected void preProcessing() throws Throwable {}
|
||||
|
||||
protected void copyToCache() throws Throwable {
|
||||
publishProgress(mContext.getString(R.string.copying_msg));
|
||||
mCachedFile = new File(mContext.getCacheDir().getAbsolutePath() + "/install.zip");
|
||||
if (mCachedFile.exists() && !mCachedFile.delete()) {
|
||||
Logger.error("FlashZip: Error while deleting already existing file");
|
||||
throw new IOException();
|
||||
}
|
||||
try (
|
||||
InputStream in = mContext.getContentResolver().openInputStream(mUri);
|
||||
OutputStream outputStream = new FileOutputStream(mCachedFile)
|
||||
) {
|
||||
byte buffer[] = new byte[1024];
|
||||
int length;
|
||||
if (in == null) throw new FileNotFoundException();
|
||||
while ((length = in.read(buffer)) > 0) {
|
||||
outputStream.write(buffer, 0, length);
|
||||
}
|
||||
Logger.dev("FlashZip: File created successfully - " + mCachedFile.getPath());
|
||||
} catch (FileNotFoundException e) {
|
||||
Logger.error("FlashZip: Invalid Uri");
|
||||
throw e;
|
||||
} catch (IOException e) {
|
||||
Logger.error("FlashZip: Error in creating file");
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
protected boolean unzipAndCheck() {
|
||||
ZipUtils.unzip(mCachedFile, mCachedFile.getParentFile(), "META-INF/com/google/android");
|
||||
List<String> ret;
|
||||
ret = Utils.readFile(mCachedFile.getParent() + "/META-INF/com/google/android/updater-script");
|
||||
return Utils.isValidShellResponse(ret) && ret.get(0).contains("#MAGISK");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
progress = new ProgressDialog(mContext);
|
||||
progress.setTitle(R.string.zip_install_progress_title);
|
||||
progress.show();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onProgressUpdate(String... values) {
|
||||
progress.setMessage(values[0]);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Integer doInBackground(Void... voids) {
|
||||
Logger.dev("FlashZip Running... " + mFilename);
|
||||
List<String> ret;
|
||||
try {
|
||||
preProcessing();
|
||||
copyToCache();
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
return -1;
|
||||
}
|
||||
if (!unzipAndCheck()) return 0;
|
||||
publishProgress(mContext.getString(R.string.zip_install_progress_msg, mFilename));
|
||||
ret = Shell.su(
|
||||
"BOOTMODE=true sh " + mCachedFile.getParent() +
|
||||
"/META-INF/com/google/android/update-binary dummy 1 " + mCachedFile.getPath(),
|
||||
"if [ $? -eq 0 ]; then echo true; else echo false; fi"
|
||||
);
|
||||
if (!Utils.isValidShellResponse(ret)) return -1;
|
||||
Logger.dev("FlashZip: Console log:");
|
||||
for (String line : ret) {
|
||||
Logger.dev(line);
|
||||
}
|
||||
Shell.su(
|
||||
"rm -rf " + mCachedFile.getParent() + "/*",
|
||||
"rm -rf " + TMP_FOLDER_PATH
|
||||
);
|
||||
if (Boolean.parseBoolean(ret.get(ret.size() - 1))) {
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
// -1 = error, manual install; 0 = invalid zip; 1 = success
|
||||
@Override
|
||||
protected void onPostExecute(Integer result) {
|
||||
super.onPostExecute(result);
|
||||
progress.dismiss();
|
||||
switch (result) {
|
||||
case -1:
|
||||
Toast.makeText(mContext, mContext.getString(R.string.install_error), Toast.LENGTH_LONG).show();
|
||||
Toast.makeText(mContext, mContext.getString(R.string.manual_install_1, mUri.getPath()), Toast.LENGTH_LONG).show();
|
||||
Toast.makeText(mContext, mContext.getString(R.string.manual_install_2), Toast.LENGTH_LONG).show();
|
||||
break;
|
||||
case 0:
|
||||
Toast.makeText(mContext, mContext.getString(R.string.invalid_zip), Toast.LENGTH_LONG).show();
|
||||
break;
|
||||
case 1:
|
||||
onSuccess();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected void onSuccess() {
|
||||
Global.Events.updateCheckDone.trigger();
|
||||
new LoadModules().exec();
|
||||
|
||||
Utils.getAlertDialogBuilder(mContext)
|
||||
.setTitle(R.string.reboot_title)
|
||||
.setMessage(R.string.reboot_msg)
|
||||
.setPositiveButton(R.string.reboot, (dialogInterface, i) -> Shell.su(true, "reboot"))
|
||||
.setNegativeButton(R.string.no_thanks, null)
|
||||
.show();
|
||||
}
|
||||
}
|
||||
|
||||
public static class MagiskHide extends RootTask<Object, Void, Void> {
|
||||
|
||||
private boolean newShell = false;
|
||||
|
||||
public MagiskHide() {}
|
||||
|
||||
public MagiskHide(boolean b) {
|
||||
newShell = b;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Void doInBackground(Object... params) {
|
||||
String command = (String) params[0];
|
||||
Shell.su(newShell, MAGISK_HIDE_PATH + command);
|
||||
return null;
|
||||
}
|
||||
|
||||
public void add(CharSequence packageName) {
|
||||
exec("add " + packageName);
|
||||
}
|
||||
|
||||
public void rm(CharSequence packageName) {
|
||||
exec("rm " + packageName);
|
||||
}
|
||||
|
||||
public void enable() {
|
||||
exec("enable");
|
||||
}
|
||||
|
||||
public void disable() {
|
||||
exec("disable");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static class GetBootBlocks extends RootTask<Void, Void, Void> {
|
||||
@Override
|
||||
protected Void doInBackground(Void... params) {
|
||||
if (Shell.rootAccess()) {
|
||||
Global.Data.blockList = Shell.su("ls /dev/block | grep mmc");
|
||||
if (Global.Info.bootBlock == null)
|
||||
Global.Info.bootBlock = Utils.detectBootImage();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Void v) {
|
||||
Global.Events.blockDetectionDone.trigger();
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,50 @@
|
||||
package com.topjohnwu.magisk.utils;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class CallbackEvent<Result> {
|
||||
|
||||
public boolean isTriggered = false;
|
||||
private Result result;
|
||||
private Set<Listener<Result>> listeners;
|
||||
|
||||
public void register(Listener<Result> l) {
|
||||
if (listeners == null) {
|
||||
listeners = new HashSet<>();
|
||||
}
|
||||
listeners.add(l);
|
||||
}
|
||||
|
||||
public void unRegister() {
|
||||
listeners = null;
|
||||
}
|
||||
|
||||
public void unRegister(Listener<Result> l) {
|
||||
if (listeners != null) {
|
||||
listeners.remove(l);
|
||||
}
|
||||
}
|
||||
|
||||
public void trigger() {
|
||||
trigger(null);
|
||||
}
|
||||
|
||||
public void trigger(Result r) {
|
||||
result = r;
|
||||
isTriggered = true;
|
||||
if (listeners != null) {
|
||||
for (Listener<Result> listener : listeners) {
|
||||
listener.onTrigger(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Result getResult() {
|
||||
return result;
|
||||
}
|
||||
|
||||
public interface Listener<R> {
|
||||
void onTrigger(CallbackEvent<R> event);
|
||||
}
|
||||
}
|
@@ -1,67 +0,0 @@
|
||||
package com.topjohnwu.magisk.utils;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
public class CallbackHandler {
|
||||
|
||||
private static Map<Event, Set<EventListener>> listeners = new HashMap<>();
|
||||
|
||||
public static void register(Event event, EventListener listener) {
|
||||
Set<EventListener> list = listeners.get(event);
|
||||
if (list == null) {
|
||||
list = new HashSet<>();
|
||||
listeners.put(event, list);
|
||||
}
|
||||
list.add(listener);
|
||||
}
|
||||
|
||||
public static void unRegister(Event event) {
|
||||
Set<EventListener> list = listeners.remove(event);
|
||||
if (list != null) {
|
||||
list.clear();
|
||||
}
|
||||
}
|
||||
|
||||
public static void unRegister(Event event, EventListener listener) {
|
||||
Set<EventListener> list = listeners.get(event);
|
||||
if (list != null) {
|
||||
list.remove(listener);
|
||||
}
|
||||
}
|
||||
|
||||
private static void triggerCallback(Event event) {
|
||||
Set<EventListener> list = listeners.get(event);
|
||||
if (list != null) {
|
||||
for (EventListener listener : list) {
|
||||
listener.onTrigger(event);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static class Event {
|
||||
|
||||
public boolean isTriggered = false;
|
||||
private Object result;
|
||||
|
||||
public void trigger() {
|
||||
trigger(null);
|
||||
}
|
||||
|
||||
public void trigger(Object result) {
|
||||
this.result = result;
|
||||
isTriggered = true;
|
||||
triggerCallback(this);
|
||||
}
|
||||
|
||||
public Object getResult() {
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
public interface EventListener {
|
||||
void onTrigger(Event event);
|
||||
}
|
||||
}
|
@@ -2,7 +2,7 @@ package com.topjohnwu.magisk.utils;
|
||||
|
||||
import android.util.Log;
|
||||
|
||||
import com.topjohnwu.magisk.Global;
|
||||
import com.topjohnwu.magisk.MagiskManager;
|
||||
|
||||
public class Logger {
|
||||
|
||||
@@ -17,7 +17,7 @@ public class Logger {
|
||||
}
|
||||
|
||||
public static void dev(String msg, Object... args) {
|
||||
if (Global.Configs.devLogging) {
|
||||
if (MagiskManager.devLogging) {
|
||||
if (args.length == 1 && args[0] instanceof Throwable) {
|
||||
Log.d(TAG, "DEV: " + msg, (Throwable) args[0]);
|
||||
} else {
|
||||
@@ -27,13 +27,13 @@ public class Logger {
|
||||
}
|
||||
|
||||
public static void dev(String msg) {
|
||||
if (Global.Configs.devLogging) {
|
||||
Log.d(TAG, "DEBUG: " + msg);
|
||||
if (MagiskManager.devLogging) {
|
||||
Log.d(TAG, "DEV: " + msg);
|
||||
}
|
||||
}
|
||||
|
||||
public static void shell(boolean root, String msg) {
|
||||
if (Global.Configs.shellLogging) {
|
||||
if (MagiskManager.shellLogging) {
|
||||
Log.d(root ? "SU" : "SH", msg);
|
||||
}
|
||||
}
|
||||
|
@@ -120,11 +120,13 @@ public class Shell {
|
||||
StreamGobbler STDOUT;
|
||||
|
||||
// Create the default shell if not init
|
||||
if (!newShell && !isInit)
|
||||
if (!newShell && !isInit) {
|
||||
init();
|
||||
}
|
||||
|
||||
if (!newShell && !rootAccess())
|
||||
if (!newShell && !rootAccess()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (newShell) {
|
||||
res = Collections.synchronizedList(new ArrayList<String>());
|
||||
@@ -132,6 +134,13 @@ public class Shell {
|
||||
process = Runtime.getRuntime().exec("su");
|
||||
STDIN = new DataOutputStream(process.getOutputStream());
|
||||
STDOUT = new StreamGobbler(process.getInputStream(), res);
|
||||
|
||||
// Run the new shell with busybox and proper umask
|
||||
STDIN.write(("umask 022\n").getBytes("UTF-8"));
|
||||
STDIN.flush();
|
||||
STDIN.write(("PATH=`[ -e /dev/busybox ] && echo /dev/busybox || " +
|
||||
"echo /data/busybox`:$PATH\n").getBytes("UTF-8"));
|
||||
STDIN.flush();
|
||||
} catch (IOException err) {
|
||||
return null;
|
||||
}
|
||||
|
@@ -1,20 +1,26 @@
|
||||
package com.topjohnwu.magisk.utils;
|
||||
|
||||
import android.Manifest;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.Activity;
|
||||
import android.app.DownloadManager;
|
||||
import android.content.Context;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Environment;
|
||||
import android.provider.OpenableColumns;
|
||||
import android.support.design.widget.Snackbar;
|
||||
import android.support.v4.app.ActivityCompat;
|
||||
import android.text.TextUtils;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.topjohnwu.magisk.Global;
|
||||
import com.topjohnwu.magisk.MagiskManager;
|
||||
import com.topjohnwu.magisk.R;
|
||||
import com.topjohnwu.magisk.asyncs.LoadRepos;
|
||||
import com.topjohnwu.magisk.components.SnackbarMaker;
|
||||
import com.topjohnwu.magisk.database.RepoDatabaseHelper;
|
||||
import com.topjohnwu.magisk.receivers.DownloadReceiver;
|
||||
|
||||
import java.io.File;
|
||||
@@ -66,9 +72,8 @@ public class Utils {
|
||||
}
|
||||
|
||||
public static void dlAndReceive(Context context, DownloadReceiver receiver, String link, String filename) {
|
||||
if (isDownloading) {
|
||||
if (isDownloading)
|
||||
return;
|
||||
}
|
||||
|
||||
if (ActivityCompat.checkSelfPermission(context, Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
|
||||
Toast.makeText(context, R.string.permissionNotGranted, Toast.LENGTH_LONG).show();
|
||||
@@ -109,17 +114,10 @@ public class Utils {
|
||||
"echo \"${BOOTIMAGE##*/}\""
|
||||
};
|
||||
List<String> ret = Shell.su(commands);
|
||||
if (isValidShellResponse(ret))
|
||||
if (isValidShellResponse(ret)) {
|
||||
return ret.get(0);
|
||||
return null;
|
||||
}
|
||||
|
||||
public static AlertDialog.Builder getAlertDialogBuilder(Context context) {
|
||||
if (Global.Configs.isDarkTheme) {
|
||||
return new AlertDialog.Builder(context, R.style.AlertDialog_dh);
|
||||
} else {
|
||||
return new AlertDialog.Builder(context);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public static boolean lowercaseContains(CharSequence string, CharSequence nonNullLowercaseSearch) {
|
||||
@@ -140,13 +138,49 @@ public class Utils {
|
||||
return Integer.parseInt(prefs.getString(key, String.valueOf(def)));
|
||||
}
|
||||
|
||||
public static void checkAndStartMagiskHide() {
|
||||
String command = "ps | grep magiskhide >/dev/null; echo $?";
|
||||
List<String> ret = Shell.su(command);
|
||||
if (!isValidShellResponse(ret))
|
||||
return;
|
||||
if (Integer.parseInt(ret.get(0)) != 0)
|
||||
new Async.MagiskHide().enable();
|
||||
public static MagiskManager getMagiskManager(Context context) {
|
||||
return (MagiskManager) context.getApplicationContext();
|
||||
}
|
||||
|
||||
public static void checkSafetyNet(MagiskManager magiskManager) {
|
||||
new SafetyNetHelper(magiskManager) {
|
||||
@Override
|
||||
public void handleResults(int i) {
|
||||
magiskManager.SNCheckResult = i;
|
||||
magiskManager.safetyNetDone.trigger();
|
||||
}
|
||||
}.requestTest();
|
||||
}
|
||||
|
||||
public static void clearRepoCache(Activity activity) {
|
||||
MagiskManager magiskManager = getMagiskManager(activity);
|
||||
magiskManager.prefs.edit().remove(LoadRepos.ETAG_KEY).apply();
|
||||
new RepoDatabaseHelper(activity).clearRepo();
|
||||
Toast.makeText(activity, R.string.repo_cache_cleared, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
public static String getNameFromUri(Context context, Uri uri) {
|
||||
String name = null;
|
||||
try (Cursor c = context.getContentResolver().query(uri, null, null, null, null)) {
|
||||
if (c != null) {
|
||||
int nameIndex = c.getColumnIndex(OpenableColumns.DISPLAY_NAME);
|
||||
if (nameIndex != -1) {
|
||||
c.moveToFirst();
|
||||
name = c.getString(nameIndex);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (name == null) {
|
||||
int idx = uri.getPath().lastIndexOf('/');
|
||||
name = uri.getPath().substring(idx + 1);
|
||||
}
|
||||
return name;
|
||||
}
|
||||
|
||||
public static void showUriSnack(Activity activity, Uri uri) {
|
||||
SnackbarMaker.make(activity, activity.getString(R.string.internal_storage,
|
||||
"/MagiskManager/" + Utils.getNameFromUri(activity, uri)),
|
||||
Snackbar.LENGTH_LONG)
|
||||
.setAction(R.string.ok, (v)->{}).show();
|
||||
}
|
||||
}
|
@@ -77,10 +77,11 @@ public class WebService {
|
||||
StringBuilder result = new StringBuilder();
|
||||
boolean first = true;
|
||||
for (Map.Entry<String, String> entry : params.entrySet()) {
|
||||
if (first)
|
||||
if (first) {
|
||||
first = false;
|
||||
else
|
||||
} else {
|
||||
result.append("&");
|
||||
}
|
||||
|
||||
result.append(URLEncoder.encode(entry.getKey(), "UTF-8"));
|
||||
result.append("=");
|
||||
|
@@ -1,32 +0,0 @@
|
||||
package com.topjohnwu.magisk.utils;
|
||||
|
||||
import android.app.AlertDialog;
|
||||
import android.content.Context;
|
||||
import android.webkit.WebResourceRequest;
|
||||
import android.webkit.WebView;
|
||||
import android.webkit.WebViewClient;
|
||||
|
||||
public class WebWindow {
|
||||
|
||||
public WebWindow(String title, String url, Context context) {
|
||||
AlertDialog.Builder alert = Utils.getAlertDialogBuilder(context);
|
||||
alert.setTitle(title);
|
||||
|
||||
Logger.dev("WebView: URL = " + url);
|
||||
|
||||
WebView wv = new WebView(context);
|
||||
wv.loadUrl(url);
|
||||
wv.setWebViewClient(new WebViewClient() {
|
||||
@Override
|
||||
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
|
||||
view.loadUrl(url);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
alert.setView(wv);
|
||||
alert.setNegativeButton("Close", (dialog, id) -> dialog.dismiss());
|
||||
alert.show();
|
||||
}
|
||||
|
||||
}
|
@@ -4,10 +4,13 @@
|
||||
|
||||
#include <jni.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include "zipadjust.h"
|
||||
|
||||
JNIEXPORT jbyteArray JNICALL
|
||||
Java_com_topjohnwu_magisk_utils_ZipUtils_zipAdjust(JNIEnv *env, jclass type, jbyteArray jbytes, jint size) {
|
||||
Java_com_topjohnwu_magisk_utils_ZipUtils_zipAdjust___3BI(JNIEnv *env, jclass type,
|
||||
jbyteArray jbytes, jint size) {
|
||||
fin = (*env)->GetPrimitiveArrayCritical(env, jbytes, NULL);
|
||||
insize = (size_t) size;
|
||||
|
||||
@@ -21,3 +24,37 @@ Java_com_topjohnwu_magisk_utils_ZipUtils_zipAdjust(JNIEnv *env, jclass type, jby
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_com_topjohnwu_magisk_utils_ZipUtils_zipAdjust__Ljava_lang_String_2(JNIEnv *env, jclass type, jstring name) {
|
||||
const char *filename = (*env)->GetStringUTFChars(env, name, NULL);
|
||||
int fd = open(filename, O_RDONLY);
|
||||
if (fd < 0)
|
||||
return;
|
||||
|
||||
// Load the file to memory
|
||||
insize = lseek(fd, 0, SEEK_END);
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
fin = malloc(insize);
|
||||
read(fd, fin, insize);
|
||||
|
||||
zipadjust(0);
|
||||
|
||||
close(fd);
|
||||
|
||||
// Open file for output
|
||||
fd = open(filename, O_WRONLY | O_TRUNC);
|
||||
if (fd < 0)
|
||||
return;
|
||||
|
||||
(*env)->ReleaseStringUTFChars(env, name, filename);
|
||||
|
||||
// Write back to file
|
||||
lseek(fd, 0, SEEK_SET);
|
||||
write(fd, fout, outsize);
|
||||
|
||||
close(fd);
|
||||
free(fin);
|
||||
free(fout);
|
||||
|
||||
}
|
||||
|
@@ -1,14 +1,8 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <zlib.h>
|
||||
#include <android/log.h>
|
||||
#include "zipadjust.h"
|
||||
|
||||
#define LOG_TAG "zipadjust"
|
||||
|
||||
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
|
||||
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
|
||||
|
||||
size_t insize = 0, outsize = 0, alloc = 0;
|
||||
unsigned char *fin = NULL, *fout = NULL;
|
||||
|
||||
|
@@ -1,9 +1,16 @@
|
||||
#ifndef MAGISKMANAGER_ZIPADJUST_H_H
|
||||
#define MAGISKMANAGER_ZIPADJUST_H_H
|
||||
|
||||
#include <android/log.h>
|
||||
|
||||
int zipadjust(int decompress);
|
||||
|
||||
extern size_t insize, outsize, alloc;
|
||||
extern unsigned char *fin, *fout;
|
||||
|
||||
#define LOG_TAG "zipadjust"
|
||||
|
||||
#define LOGD(...) __android_log_print(ANDROID_LOG_DEBUG, LOG_TAG, __VA_ARGS__)
|
||||
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
|
||||
|
||||
#endif //MAGISKMANAGER_ZIPADJUST_H_H
|
||||
|
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0">
|
||||
<path
|
||||
android:fillColor="#000"
|
||||
android:pathData="M13,3c-4.97,0 -9,4.03 -9,9L1,12l3.89,3.89 0.07,0.14L9,12L6,12c0,-3.87 3.13,-7 7,-7s7,3.13 7,7 -3.13,7 -7,7c-1.93,0 -3.68,-0.79 -4.94,-2.06l-1.42,1.42C8.27,19.99 10.51,21 13,21c4.97,0 9,-4.03 9,-9s-4.03,-9 -9,-9zM12,8v5l4.28,2.54 0.72,-1.21 -3.5,-2.08L13.5,8L12,8z"/>
|
||||
</vector>
|
@@ -56,35 +56,35 @@
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Headline"/>
|
||||
</LinearLayout>
|
||||
|
||||
<com.topjohnwu.magisk.AboutCardRow
|
||||
<com.topjohnwu.magisk.components.AboutCardRow
|
||||
android:id="@+id/app_version_info"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:icon="@drawable/ic_info_outline"
|
||||
app:text="@string/app_version"/>
|
||||
|
||||
<com.topjohnwu.magisk.AboutCardRow
|
||||
<com.topjohnwu.magisk.components.AboutCardRow
|
||||
android:id="@+id/app_changelog"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:icon="@drawable/ic_history"
|
||||
app:text="@string/app_changelog"/>
|
||||
|
||||
<com.topjohnwu.magisk.AboutCardRow
|
||||
<com.topjohnwu.magisk.components.AboutCardRow
|
||||
android:id="@+id/app_developers"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:icon="@drawable/ic_person"
|
||||
app:text="@string/app_developers"/>
|
||||
|
||||
<com.topjohnwu.magisk.AboutCardRow
|
||||
<com.topjohnwu.magisk.components.AboutCardRow
|
||||
android:id="@+id/app_translators"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:icon="@drawable/ic_language"
|
||||
app:text="@string/app_translators"/>
|
||||
|
||||
<com.topjohnwu.magisk.AboutCardRow
|
||||
<com.topjohnwu.magisk.components.AboutCardRow
|
||||
android:id="@+id/app_source_code"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -109,14 +109,14 @@
|
||||
android:layout_marginBottom="8dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.topjohnwu.magisk.AboutCardRow
|
||||
<com.topjohnwu.magisk.components.AboutCardRow
|
||||
android:id="@+id/support_thread"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:icon="@drawable/ic_xda"
|
||||
app:text="@string/support_thread"/>
|
||||
|
||||
<com.topjohnwu.magisk.AboutCardRow
|
||||
<com.topjohnwu.magisk.components.AboutCardRow
|
||||
android:id="@+id/donation"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
81
app/src/main/res/layout/alert_dialog.xml
Normal file
@@ -0,0 +1,81 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/message_panel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:overScrollMode="ifContentScrolls"
|
||||
android:paddingBottom="12dip"
|
||||
android:paddingEnd="20dip"
|
||||
android:paddingStart="20dip"
|
||||
android:paddingTop="12dip">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/message"
|
||||
style="?android:attr/textAppearanceMedium"
|
||||
android:textColor="?android:attr/textColorPrimary"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="5dip" />
|
||||
|
||||
</ScrollView>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ViewStub
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:id="@+id/custom_view"/>
|
||||
|
||||
<LinearLayout
|
||||
style="?android:attr/buttonBarStyle"
|
||||
android:id="@+id/button_panel"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="54dip"
|
||||
android:measureWithLargestChild="true"
|
||||
android:orientation="horizontal"
|
||||
android:paddingEnd="2dip"
|
||||
android:paddingStart="2dip"
|
||||
android:paddingTop="4dip">
|
||||
|
||||
<Button
|
||||
android:id="@+id/negative"
|
||||
style="?android:attr/buttonBarButtonStyle"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="start"
|
||||
android:layout_weight="1"
|
||||
android:maxLines="2" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/neutral"
|
||||
style="?android:attr/buttonBarButtonStyle"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"
|
||||
android:layout_weight="1"
|
||||
android:maxLines="2" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/positive"
|
||||
style="?android:attr/buttonBarButtonStyle"
|
||||
android:layout_width="0dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:layout_weight="1"
|
||||
android:maxLines="2" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
@@ -159,7 +159,7 @@
|
||||
</android.support.v7.widget.CardView>
|
||||
|
||||
<android.support.v7.widget.CardView
|
||||
android:id="@+id/flash_button"
|
||||
android:id="@+id/install_button"
|
||||
android:layout_gravity="center"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="4dp"
|
||||
@@ -192,7 +192,7 @@
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:text="@string/magiskify"
|
||||
android:id="@+id/install_text"
|
||||
android:ems="10"
|
||||
android:gravity="center"
|
||||
android:layout_weight="1"
|
||||
|
@@ -51,9 +51,8 @@
|
||||
fab:menu_shadowRadius="4dp"
|
||||
fab:menu_shadowXOffset="1dp"
|
||||
fab:menu_shadowYOffset="3dp"
|
||||
fab:menu_colorNormal="#cddc39"
|
||||
fab:menu_colorPressed="#9e9d24"
|
||||
fab:menu_colorRipple="#99FFFFFF"
|
||||
fab:menu_colorNormal="?android:colorAccent"
|
||||
fab:menu_colorPressed="?attr/colorAccentFallback"
|
||||
fab:menu_animationDelayPerItem="50"
|
||||
fab:menu_icon="@drawable/ic_add"
|
||||
fab:menu_buttonSpacing="0dp"
|
||||
@@ -64,6 +63,8 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:src="@drawable/ic_archive"
|
||||
fab:fab_colorNormal="?android:colorAccent"
|
||||
fab:fab_colorPressed="?attr/colorAccentFallback"
|
||||
fab:fab_size="mini"
|
||||
fab:fab_label="@string/fab_flash_zip" />
|
||||
|
||||
|
@@ -14,135 +14,74 @@
|
||||
card_view:cardCornerRadius="@dimen/card_corner_radius"
|
||||
card_view:cardElevation="@dimen/card_elevation">
|
||||
|
||||
<RelativeLayout
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="?android:attr/listPreferredItemHeight"
|
||||
android:orientation="horizontal"
|
||||
android:padding="@dimen/card_layout_padding">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:id="@+id/info_layout"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:id="@+id/info_layout">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="horizontal"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<LinearLayout
|
||||
android:orientation="vertical"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="0dp"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:textIsSelectable="false"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/version_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/no_info_provided"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textColor="@android:color/tertiary_text_dark"
|
||||
android:textIsSelectable="false"
|
||||
android:textStyle="bold|italic"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/author"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/no_info_provided"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textColor="@android:color/tertiary_text_dark"
|
||||
android:textIsSelectable="false"
|
||||
android:textStyle="bold|italic"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/description"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:text="@string/no_info_provided"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textIsSelectable="false" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/update"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:background="@drawable/ic_file_download_black"
|
||||
android:backgroundTint="@color/icon_grey"
|
||||
android:focusable="false"
|
||||
android:gravity="end" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/expand_layout"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentStart="true"
|
||||
android:layout_below="@id/info_layout"
|
||||
android:layout_weight="1"
|
||||
android:clickable="true"
|
||||
android:foreground="?android:attr/selectableItemBackground"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
<TextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:orientation="horizontal">
|
||||
android:layout_marginTop="0dp"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:textIsSelectable="false" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/changeLog"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="@dimen/card_imageview_margin"
|
||||
android:layout_marginStart="@dimen/card_imageview_margin"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:padding="15dp"
|
||||
android:src="@drawable/ic_changelog"
|
||||
android:tint="@color/icon_grey"/>
|
||||
<TextView
|
||||
android:id="@+id/version_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/no_info_provided"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textColor="@android:color/tertiary_text_dark"
|
||||
android:textIsSelectable="false"
|
||||
android:textStyle="bold|italic" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/authorLink"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="@dimen/card_imageview_margin"
|
||||
android:layout_marginStart="@dimen/card_imageview_margin"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:padding="15dp"
|
||||
android:src="@drawable/ic_person"
|
||||
android:tint="@color/icon_grey"/>
|
||||
<TextView
|
||||
android:id="@+id/author"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/no_info_provided"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textColor="@android:color/tertiary_text_dark"
|
||||
android:textIsSelectable="false"
|
||||
android:textStyle="bold|italic" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/supportLink"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="@dimen/card_imageview_margin"
|
||||
android:layout_marginStart="@dimen/card_imageview_margin"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:padding="15dp"
|
||||
android:src="@drawable/ic_help"
|
||||
android:tint="@color/icon_grey"/>
|
||||
|
||||
</LinearLayout>
|
||||
<TextView
|
||||
android:id="@+id/description"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:text="@string/no_info_provided"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textIsSelectable="false" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
<ImageView
|
||||
android:id="@+id/download"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:background="@drawable/ic_file_download_black"
|
||||
android:backgroundTint="@color/icon_grey"
|
||||
android:clickable="true"
|
||||
android:foreground="?android:attr/selectableItemBackground"
|
||||
android:focusable="false"
|
||||
android:gravity="end" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
</android.support.v7.widget.CardView>
|
||||
|
@@ -6,7 +6,7 @@
|
||||
android:gravity="center_vertical"
|
||||
android:paddingStart="16dp"
|
||||
android:textAllCaps="true"
|
||||
android:textColor="@color/accent"
|
||||
android:textColor="?android:colorAccent"
|
||||
android:background="@android:color/transparent"
|
||||
android:textSize="16sp"
|
||||
android:id="@+id/section_text"
|
||||
|
Before Width: | Height: | Size: 4.4 KiB After Width: | Height: | Size: 5.8 KiB |
Before Width: | Height: | Size: 2.6 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 8.3 KiB |
Before Width: | Height: | Size: 9.6 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 20 KiB |
@@ -4,7 +4,6 @@
|
||||
<!--Welcome Activity-->
|
||||
<string name="navigation_drawer_open">فتح درج التنقل</string>
|
||||
<string name="navigation_drawer_close">إغلاق درج التنقل</string>
|
||||
<string name="magiskhide">Magisk إخفاء</string>
|
||||
<string name="modules">الإضافات</string>
|
||||
<string name="downloads">التنزيلات</string>
|
||||
<string name="log">السجل</string>
|
||||
@@ -35,7 +34,6 @@
|
||||
<string name="update_available">يتوفر تحديث</string>
|
||||
<string name="installed">مثبت</string>
|
||||
<string name="not_installed">غير مثبت</string>
|
||||
<string name="changelog">التغييرات</string>
|
||||
|
||||
<!--Log Fragment-->
|
||||
<string name="menuSaveToSd">حفظ إلى بطاقة ذاكرة SD</string>
|
||||
@@ -65,16 +63,14 @@
|
||||
<string name="download_install">تنزيل وتثبيت</string>
|
||||
<string name="download_file_error">خطأ تنزيل الملف</string>
|
||||
<string name="install_error">خطأ في التثبيت!</string>
|
||||
<string name="manual_install_1">الملف المضغوط وضع في %1$s</string>
|
||||
<string name="manual_install_2">التثبيت في الإسترداد يدوياً</string>
|
||||
<string name="invalid_zip">الملف المضغوط ليس إضافة Magisk!!</string>
|
||||
<string name="reboot_title">التثبيت نجح!</string>
|
||||
<string name="reboot_msg">هل تريد إعادة التشغيل الآن؟</string>
|
||||
<string name="reboot">إعادة التشغيل</string>
|
||||
<string name="zip_install_progress_title">تثبيت</string>
|
||||
<string name="zip_install_unzip_zip_msg">فك الضغط عن الملف المضغوط …</string>
|
||||
<string name="zip_install_process_zip_msg">معالجة الملف المضغوط …</string>
|
||||
<string name="zip_install_progress_msg">"تثبيت %1$s …"</string>
|
||||
<string name="zip_unzip_msg">فك الضغط عن الملف المضغوط …</string>
|
||||
<string name="zip_process_msg">معالجة الملف المضغوط …</string>
|
||||
<string name="zip_install_progress_msg">تثبيت %1$s …</string>
|
||||
<string name="no_magisk_title">لا يوجد Magisk مثبت!</string>
|
||||
<string name="no_magisk_msg">هل ترغب في تنزيل وتثبيت Magisk؟</string>
|
||||
|
||||
@@ -98,7 +94,7 @@
|
||||
<string name="settings_shell_logging_summary">حدد هذا الخيار لتمكين سجل جميع الأوامر الدفعية والمخرجات</string>
|
||||
|
||||
<string name="settings_reboot_toast">إعادة التشغيل لتطبيق الإعدادات</string>
|
||||
<string name="auto_detect">\"(تلقائي) %1$s\"</string>
|
||||
<string name="auto_detect">(تلقائي) %1$s</string>
|
||||
<string name="checking_for_updates">البحث عن تحديثات…</string>
|
||||
<string name="install">التثبيت</string>
|
||||
<string name="not_rooted">غير مروت</string>
|
||||
@@ -109,7 +105,7 @@
|
||||
<string name="copying_msg">نسخ الملف المضغوط إلى دليل مؤقت</string>
|
||||
<string name="detect_button">تحقق</string>
|
||||
<string name="downloading_toast">جاري التنزيل %1$s</string>
|
||||
<string name="install_magisk_title">تثبيت Magisk الإصدار: v%1$.1f</string>
|
||||
<string name="install_magisk_title">تثبيت Magisk الإصدار: %1$s</string>
|
||||
<string name="keep_force_encryption">إبقاء التشفير القوى</string>
|
||||
<string name="keep_dm_verity">إبقاء dm-verity</string>
|
||||
<string name="root_error">مروت لكن لا يوجد إذن الروت، غير مسموح به؟</string>
|
||||
|
@@ -6,7 +6,7 @@
|
||||
<!--Welcome Activity-->
|
||||
<string name="navigation_drawer_open">Navigationsmenü öffnen</string>
|
||||
<string name="navigation_drawer_close">Navigationsmenü schließen</string>
|
||||
<string name="magiskhide">Magisk Hide</string>
|
||||
|
||||
<string name="modules">Module</string>
|
||||
<string name="downloads">Download</string>
|
||||
<string name="superuser">Superuser</string>
|
||||
@@ -16,36 +16,40 @@
|
||||
<string name="install">Installieren</string>
|
||||
|
||||
<!--Status Fragment-->
|
||||
<string name="magisk_version">Magisk v%1$s ist installiert</string>
|
||||
<string name="magisk_version_disable">Magisk v%1$s ist deaktiviert</string>
|
||||
<string name="magisk_version">Magisk %1$s ist installiert</string>
|
||||
<string name="magisk_version_disable">Magisk %1$s ist deaktiviert</string>
|
||||
<string name="magisk_version_error">Magisk ist nicht installiert</string>
|
||||
|
||||
<string name="checking_for_updates">Suche nach Updates…</string>
|
||||
<string name="magisk_update_available">Magisk v%1$.1f ist verfügbar!</string>
|
||||
<string name="magisk_update_available">Magisk %1$.1f ist verfügbar!</string>
|
||||
<string name="cannot_check_updates">Updatesuche fehlgeschlagen.\nIst eine Internetverbindung verfügbar?</string>
|
||||
<string name="up_to_date">Die neuste Version von %1$s ist bereits installiert</string>
|
||||
<string name="up_to_date">Die neueste Version von %1$s ist bereits installiert</string>
|
||||
<string name="root_error">Gerootet, aber keine root-Rechte. Wurde der root-Zugriff verweigert?</string>
|
||||
<string name="not_rooted">Nicht gerootet</string>
|
||||
<string name="proper_root">Ordnungsgemäß gerootet</string>
|
||||
<string name="safetyNet_check_text">SafetyNet-Status abfragen</string>
|
||||
<string name="checking_safetyNet_status">Prüfe SafetyNet-Status…</string>
|
||||
<string name="safetyNet_connection_failed">Verbindung zur Google-API fehlgeschlagen</string>
|
||||
<string name="safetyNet_connection_suspended">Verbindung zur Google-API wurde ausgesetzt</string>
|
||||
<string name="safetyNet_connection_suspended">Verbindung zur Google-API wurde ausgesetzt</string>
|
||||
<string name="safetyNet_error">SafetyNet-Status konnte nicht geprüft werden. Ist eine Internetverbindung verfügbar?</string>
|
||||
<string name="safetyNet_fail">SafetyNet nicht bestanden: \"CTS profile mismatch\"</string>
|
||||
<string name="safetyNet_pass">SafetyNet bestanden</string>
|
||||
<string name="root_info_warning">Funktionalität stark eingeschränkt</string>
|
||||
|
||||
<!--Install Fragment-->
|
||||
<string name="auto_detect">"%1$s (autom.)"</string>
|
||||
<string name="auto_detect">%1$s (autom.)</string>
|
||||
<string name="boot_image_title">Boot-Image-Pfad</string>
|
||||
<string name="detect_button">Automatisch</string>
|
||||
<string name="advanced_settings_title">Erweiterte Einstellungen</string>
|
||||
<string name="advanced_settings_title">Erweiterte Optionen</string>
|
||||
<string name="keep_force_encryption">\"force encryption\" beibehalten</string>
|
||||
<string name="keep_dm_verity">\"dm-verity\"-Test beibehalten</string>
|
||||
<string name="current_magisk_title">Installierte Magisk-Version: v%1$s</string>
|
||||
<string name="install_magisk_title">Neuste Magisk-Version: v%1$.1f</string>
|
||||
|
||||
<string name="current_magisk_title">Installierte Magisk-Version: %1$s</string>
|
||||
<string name="install_magisk_title">Neueste Magisk-Version: %1$s</string>
|
||||
<string name="uninstall">Deinstallieren</string>
|
||||
<string name="reboot_countdown">Neustart in %1$d</string>
|
||||
<string name="uninstall_magisk_title">Magisk deinstallieren</string>
|
||||
<string name="uninstall_magisk_msg">Dies entfernt alle Module, MagiskSU und verschlüsselt unter Umständen deine Daten, falls nicht bereits verschlüsselt.\nMöchtest du wirklich fortfahren?</string>
|
||||
<string name="version_none">(keine)</string>
|
||||
|
||||
<!--Module Fragment-->
|
||||
<string name="no_info_provided">(Nichts angegeben)</string>
|
||||
@@ -62,13 +66,12 @@
|
||||
<string name="update_available">Update verfügbar</string>
|
||||
<string name="installed">Installiert</string>
|
||||
<string name="not_installed">Nicht installiert</string>
|
||||
<string name="changelog">Änderungen</string>
|
||||
|
||||
<!--Log Fragment-->
|
||||
<string name="menuSaveToSd">Log auf SD-Karte speichern</string>
|
||||
<string name="menuReload">Log erneut laden</string>
|
||||
<string name="menuClearLog">Log löschen</string>
|
||||
<string name="logs_cleared">Log erfolgreich gelöscht</string>
|
||||
<string name="logs_cleared">Log gelöscht</string>
|
||||
<string name="log_is_empty">Log ist leer</string>
|
||||
<string name="logs_save_failed">Konnte den Log nicht auf der SD-Karte speichern:</string>
|
||||
|
||||
@@ -88,45 +91,46 @@
|
||||
<string name="permissionNotGranted">Diese Funktion benötigt Rechte zum Schreiben auf den externen Speicher.</string>
|
||||
<string name="no_thanks">Nein danke</string>
|
||||
<string name="yes">Ja</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="close">Schließen</string>
|
||||
<string name="repo_install_title">Installiere %1$s</string>
|
||||
<string name="repo_install_msg">Möchtest du %1$s installieren?</string>
|
||||
<string name="download_install">Herunterladen & installieren</string>
|
||||
<string name="download">Herunterladen</string>
|
||||
<string name="goto_install">Zu \"Installieren\" wechseln</string>
|
||||
<string name="download_file_error">Fehler beim Herunterladen der Datei</string>
|
||||
<string name="install_error">Fehler bei der Installation!</string>
|
||||
<string name="manual_install_1">Zip-Datei unter %1$s gespeichert</string>
|
||||
<string name="manual_install_2">Manuell mittels Recovery flashen</string>
|
||||
<string name="invalid_zip">Die Zip-Datei ist kein Magisk-Modul!</string>
|
||||
<string name="reboot_title">Installation erfolgreich!</string>
|
||||
<string name="reboot_msg">Möchtest du jetzt neustarten?</string>
|
||||
<string name="reboot">Neustart</string>
|
||||
<string name="copying_msg">Kopiere Zip ins temp-Verzeichnis</string>
|
||||
<string name="zip_install_progress_title">Installiere</string>
|
||||
<string name="zip_install_unzip_zip_msg">Entpacke Zip-Datei…</string>
|
||||
<string name="zip_install_process_zip_msg">Verarbeite Zip-Datei…</string>
|
||||
<string name="zip_install_progress_msg">"Installiere %1$s…"</string>
|
||||
<string name="zip_unzip_msg">Entpacke Zip-Datei…</string>
|
||||
<string name="zip_process_msg">Verarbeite Zip-Datei…</string>
|
||||
<string name="zip_install_progress_msg">Installiere %1$s…</string>
|
||||
<string name="no_magisk_title">Magisk ist nicht installiert!</string>
|
||||
<string name="no_magisk_msg">Möchtest du Magisk herunterladen und installieren?</string>
|
||||
<string name="downloading_toast">Herunterladen von %1$s</string>
|
||||
<string name="magisk_update_title">Neues Magisk-Update verfügbar!</string>
|
||||
<string name="magisk_update_message">Magisk-Update v%1$.1f ist verfügbar, möchtest du es installieren?</string>
|
||||
<string name="settings_reboot_toast">Neustarten, um die Änderungen anzuwenden</string>
|
||||
<string name="check_release_notes">Änderungen anzeigen</string>
|
||||
<string name="release_notes">Änderungen</string>
|
||||
<string name="repo_cache_cleared">Repo-Cache gelöscht</string>
|
||||
<string name="safetyNet_hide_notice">Diese App benutzt SafetyNet, welches standardmäßig von Magisk Hide gehandhabt</string>
|
||||
<string name="start_magiskhide">Starte Magisk Hide…</string>
|
||||
|
||||
<!--URL Templates-->
|
||||
|
||||
|
||||
|
||||
<string name="no_magisksu_title">Du benutzt nicht MagiskSU!</string>
|
||||
<string name="no_magisksu_msg">Deine root-Lösung wird nicht offiziell unterstützt und Magisk Hide allein ist eventuell nicht ausreichend!\nEventuell benötigst du weitere Programme (z.B. \"suhide\"), um SafetyNet zu bestehen.</string>
|
||||
<string name="understand">Ich verstehe</string>
|
||||
<string name="process_error">Prozessfehler</string>
|
||||
<string name="internal_storage">Die zip-Datei ist gespeichert unter:\n[Interner Speicher]%1$s</string>
|
||||
<string name="zip_process_title">Verarbeite</string>
|
||||
|
||||
<!--Settings Activity -->
|
||||
<string name="settings_general_category">Allgemein</string>
|
||||
<string name="settings_dark_theme_title">Dunkles Theme</string>
|
||||
<string name="settings_dark_theme_summary">Dunkles Theme aktivieren</string>
|
||||
<string name="settings_clear_cache_title">Repo-Cache löschen</string>
|
||||
<string name="settings_clear_cache_summary">Löscht die zwischengespeicherten Informtionen der Online-Repos. Erzwingt eine Aktualisierung</string>
|
||||
<string name="settings_clear_cache_summary">Löscht die zwischengespeicherten Informationen der Online-Repos. Erzwingt eine Aktualisierung</string>
|
||||
|
||||
<string name="settings_disable_title">Magisk deaktivieren</string>
|
||||
<string name="settings_disable_summary">Deaktiviert alles außer den root-Zugang (MagiskSU)</string>
|
||||
@@ -162,6 +166,7 @@
|
||||
<string name="deny">Verweigern</string>
|
||||
<string name="prompt">Nachfragen</string>
|
||||
<string name="grant">Gewähren</string>
|
||||
|
||||
<string name="su_warning">Erlaubt den vollen Zugriff auf das Gerät.\nVerweigere, wenn du dir unsicher bist!</string>
|
||||
<string name="forever">Dauerhaft</string>
|
||||
<string name="once">Nur diesmal</string>
|
||||
@@ -188,5 +193,5 @@
|
||||
<string name="pid">PID:\u0020</string>
|
||||
<string name="target_uid">Ziel-UID:\u0020</string>
|
||||
<string name="command">Befehl:\u0020</string>
|
||||
|
||||
|
||||
</resources>
|
||||
|
@@ -2,23 +2,48 @@
|
||||
<!--Universal-->
|
||||
|
||||
<!--Welcome Activity-->
|
||||
<string name="navigation_drawer_open">Abrir menú lateral</string>
|
||||
<string name="navigation_drawer_close">Cerrar menú lateral</string>
|
||||
<string name="magiskhide">Magisk Hide</string>
|
||||
<string name="modules">Modulos</string>
|
||||
<string name="navigation_drawer_open">Abrir menú de navegación</string>
|
||||
<string name="navigation_drawer_close">Cerrar menú de navegación</string>
|
||||
<string name="modules">Módulos</string>
|
||||
<string name="downloads">Descargas</string>
|
||||
<string name="log">Log</string>
|
||||
<string name="superuser">Superusuario</string>
|
||||
<string name="log">Registro</string>
|
||||
<string name="settings">Ajustes</string>
|
||||
<string name="status">Estado</string>
|
||||
<string name="install">Instalar</string>
|
||||
|
||||
<!--Magisk Fragment-->
|
||||
<string name="magisk_version">Instalado Magisk v%1$s</string>
|
||||
<string name="magisk_version_error">¿Has instalado Magisk?</string>
|
||||
<string name="magisk_version_disable">Magisk v%1$s está deshabilitado</string>
|
||||
<string name="magisk_version_error">Magisk no está instalado</string>
|
||||
|
||||
<string name="magisk_update_available">¡Actualización Magisk v%1$.1f!</string>
|
||||
<string name="cannot_check_updates">No se pueden buscar actualizaciones</string>
|
||||
<string name="checking_for_updates">Comprobando actualizaciones…</string>
|
||||
<string name="magisk_update_available">¡Disponible Magisk v%1$.1f!</string>
|
||||
<string name="cannot_check_updates">No se pueden comprobar actualizaciones ¿No tiene internet?</string>
|
||||
<string name="up_to_date">Última versión de %1$s instalada</string>
|
||||
<string name="root_error">Rooteado pero sin permiso root, ¿No lo permitiste?</string>
|
||||
<string name="not_rooted">No rooteado</string>
|
||||
<string name="proper_root">Correctamente rooteado</string>
|
||||
<string name="safetyNet_check_text">Toque para empezar la comprobación de SafetyNet</string>
|
||||
<string name="checking_safetyNet_status">Comprobando estado de SafetyNet…</string>
|
||||
<string name="safetyNet_connection_failed">No puede conectar con la API de Google</string>
|
||||
<string name="safetyNet_connection_suspended">La conexión con la API de Google API fue suspendida</string>
|
||||
<string name="safetyNet_error">No puede comprobar SafetyNet, ¿No tiene internet?</string>
|
||||
<string name="safetyNet_fail">SafetyNet falló: No coincide el perfil CTS</string>
|
||||
<string name="safetyNet_pass">SafetyNet pasó</string>
|
||||
<string name="root_info_warning">Funcionalidad enormemente limitada</string>
|
||||
|
||||
<!--Root Fragment-->
|
||||
<!--Install Fragment-->
|
||||
<string name="auto_detect">(Auto) %1$s</string>
|
||||
<string name="boot_image_title">Ubicación de imagen boot</string>
|
||||
<string name="detect_button">Detectar</string>
|
||||
<string name="advanced_settings_title">Ajustes avanzados</string>
|
||||
<string name="keep_force_encryption">Mantener cifrado forzado</string>
|
||||
<string name="keep_dm_verity">Mantener dm-verity</string>
|
||||
<string name="current_magisk_title">Versión de Magisk instalada: %1$s</string>
|
||||
<string name="install_magisk_title">Última versión de Magisk: %1$s</string>
|
||||
<string name="uninstall">Desinstalar</string>
|
||||
<string name="reboot_countdown">Reiniciando en %1$d</string>
|
||||
|
||||
<!--Module Fragment-->
|
||||
<string name="no_info_provided">(No hay información)</string>
|
||||
@@ -29,28 +54,27 @@
|
||||
<string name="disable_file_created">El módulo se desactivará en el siguiente reinicio</string>
|
||||
<string name="disable_file_removed">El móodulo se activará en el siguiente reinicio</string>
|
||||
<string name="author">Creado por %1$s</string>
|
||||
<string name="fab_flash_zip">Flashear Módulo Zip</string>
|
||||
<string name="fab_flash_zip">Flashear el zip del módulo</string>
|
||||
|
||||
<!--Repo Fragment-->
|
||||
<string name="update_available">Actualización disponible</string>
|
||||
<string name="installed">Instalado</string>
|
||||
<string name="not_installed">No Instalado</string>
|
||||
<string name="changelog">Cambios</string>
|
||||
|
||||
<!--Log Fragment-->
|
||||
<string name="menuSaveToSd">Salvar a SD</string>
|
||||
<string name="menuSaveToSd">Guardar en la SD</string>
|
||||
<string name="menuReload">Recargar</string>
|
||||
<string name="menuClearLog">Vaciar Log ahora</string>
|
||||
<string name="logs_cleared">Log vaciado correctamente</string>
|
||||
<string name="log_is_empty">El Log está vacio</string>
|
||||
<string name="logs_save_failed">No se ha podido escribir el log en la tarjeta SD:</string>
|
||||
<string name="menuClearLog">Limpiar registro ahora</string>
|
||||
<string name="logs_cleared">Registro Limpiado correctamente</string>
|
||||
<string name="log_is_empty">El registro está vacio</string>
|
||||
<string name="logs_save_failed">No se ha podido escribir el registro en la tarjeta SD:</string>
|
||||
|
||||
<!--About Activity-->
|
||||
<string name="about">Acerca de</string>
|
||||
<string name="app_developers">Desarroladores principales</string>
|
||||
<string name="app_developers_"><![CDATA[App created by <a href="https://github.com/topjohnwu">topjohnwu</a> in collaboration with <a href="https://github.com/d8ahazard">Digitalhigh</a> and <a href="https://github.com/dvdandroid">Dvdandroid</a>.]]></string>
|
||||
<string name="app_changelog">Cambios en la aplicación</string>
|
||||
<string name="translators">Gawenda, netizen</string>
|
||||
<string name="app_developers_"><![CDATA[Aplicación creada por <a href="https://github.com/topjohnwu">topjohnwu</a> en colaboración con <a href="https://github.com/d8ahazard">Digitalhigh</a> y <a href="https://github.com/dvdandroid">Dvdandroid</a>.]]></string>
|
||||
<string name="app_changelog">Registro de cambios de la aplicación</string>
|
||||
<string name="translators"><![CDATA[Gawenda, netizen, <a href="https://github.com/DeicPro">Deiki</a>]]></string>
|
||||
<string name="app_version">Versión de la aplicación</string>
|
||||
<string name="app_source_code">Código fuente</string>
|
||||
<string name="donation">Donar</string>
|
||||
@@ -58,42 +82,109 @@
|
||||
<string name="support_thread">Hilo de soporte</string>
|
||||
|
||||
<!--Toasts, Dialogs-->
|
||||
<string name="permissionNotGranted">Esta opción no funcionará sin permiso de escritura en la memoria externa</string>
|
||||
<string name="permissionNotGranted">Esta opción no funcionará sin permiso de escritura en la memoria externa.</string>
|
||||
<string name="no_thanks">No gracias</string>
|
||||
<string name="yes">Si</string>
|
||||
<string name="ok">Vale</string>
|
||||
<string name="close">Cerrar</string>
|
||||
<string name="repo_install_title">Instalar %1$s</string>
|
||||
<string name="repo_install_msg">¿ Quieres instalar %1$s ?</string>
|
||||
<string name="download_install">Descargar e instalar</string>
|
||||
<string name="download_file_error">Error descargando fichero</string>
|
||||
<string name="download">Descargar</string>
|
||||
<string name="goto_install">Ir a la sección \"Instalar\"</string>
|
||||
<string name="download_file_error">Error descargando archivo</string>
|
||||
<string name="install_error">¡Error en la instalación!</string>
|
||||
<string name="manual_install_1">Error flasheando fichero, zip colocado en %1$s\nFlashear manualmente desde recovery</string>
|
||||
<string name="invalid_zip">¡El zip no es un Módulo Magisk!</string>
|
||||
<string name="invalid_zip">¡El zip no es un módulo Magisk!</string>
|
||||
<string name="reboot_title">¡Instalación correcta!</string>
|
||||
<string name="reboot_msg">¿Deseas reiniciar ahora?</string>
|
||||
<string name="reboot">Reinciar</string>
|
||||
<string name="reboot">Reiniciar</string>
|
||||
<string name="copying_msg">Copiando zip a un directorio temporal</string>
|
||||
<string name="zip_install_progress_title">Instalando</string>
|
||||
<string name="zip_install_progress_msg">"Instalando %1$s …"</string>
|
||||
<string name="zip_unzip_msg">Descomprimiendo archivo zip …</string>
|
||||
<string name="zip_process_msg">Procesando archivo zip …</string>
|
||||
<string name="zip_install_progress_msg">Instalando %1$s …</string>
|
||||
<string name="no_magisk_title">¡Magisk no instalado!</string>
|
||||
<string name="no_magisk_msg">¿Deseas descargar e instalar Magisk?</string>
|
||||
|
||||
<!--URL Templates-->
|
||||
<string name="downloading_toast">Descargando %1$s</string>
|
||||
<string name="magisk_update_title">¡Nueva actualización de Magisk disponible!</string>
|
||||
<string name="settings_reboot_toast">Reinicia para aplicar los ajustes</string>
|
||||
<string name="release_notes">Notas de lanzamiento</string>
|
||||
<string name="repo_cache_cleared">Caché del repositorio limpiada</string>
|
||||
<string name="safetyNet_hide_notice">Esta aplicación usa SafetyNet\nYa manejado por defecto por MagiskHide</string>
|
||||
<string name="start_magiskhide">Iniciando MagiskHide …</string>
|
||||
<string name="no_magisksu_title">¡No está usando MagiskSU!</string>
|
||||
<string name="no_magisksu_msg">No estás rooteado con MagiskSU, ¡Usando MagiskHide por si mismo podría no ser suficiente!\nNo está oficialmente soportado, y necesitaria herramientas adiccionales (ej. suhide) para pasar Safety Net.</string>
|
||||
<string name="understand">Entiendo</string>
|
||||
<string name="process_error">Error de proceso</string>
|
||||
<string name="internal_storage">El zip es almacenado en:\n[Internal Storage]%1$s</string>
|
||||
<string name="zip_process_title">Procesando</string>
|
||||
|
||||
<!--Settings Activity -->
|
||||
<string name="settings_general_category">General</string>
|
||||
<string name="settings_dark_theme_title">Tema</string>
|
||||
<string name="settings_dark_theme_summary">Selecciona un tema</string>
|
||||
<string name="settings_dark_theme_title">Tema oscuro</string>
|
||||
<string name="settings_dark_theme_summary">Habilita el tema oscuro</string>
|
||||
<string name="settings_clear_cache_title">Limpiar caché del repositorio</string>
|
||||
<string name="settings_clear_cache_summary">Limpiar la información en caché para los repositorios en línea, fuerza a la aplicación a actualizar en línea</string>
|
||||
|
||||
<string name="settings_disable_title">Deshabilitar Magisk</string>
|
||||
<string name="settings_disable_summary">Todo será desactivado excepto el root (MagiskSU)</string>
|
||||
<string name="settings_magiskhide_summary">Ocultar Magisk de varias detecciones</string>
|
||||
<string name="settings_busybox_title">Habilitar BusyBox</string>
|
||||
<string name="settings_busybox_summary">Montar el busybox interno de Magisk en xbin</string>
|
||||
<string name="settings_hosts_title">Habilitar fichero hosts fuera de la partición de sistema</string>
|
||||
<string name="settings_hosts_title">Habilitar archivo hosts fuera de la partición de sistema</string>
|
||||
<string name="settings_hosts_summary">Soporte para aplicaciones de bloqueo de publicidad fuera de la partición de sistema</string>
|
||||
|
||||
<string name="settings_development_category">Desarrollo</string>
|
||||
<string name="settings_developer_logging_title">Habilitar información de depuración en el log</string>
|
||||
<string name="settings_developer_logging_summary">Activar esto para grabar más información en el log</string>
|
||||
<string name="settings_shell_logging_title">Grabar comandos de terminal en el log</string>
|
||||
<string name="settings_su_app_adb">Aplicaciones y ADB</string>
|
||||
<string name="settings_su_app">Sólo aplicaciones</string>
|
||||
<string name="settings_su_adb">Sólo ADB</string>
|
||||
<string name="settings_su_disable">Deshabilitado</string>
|
||||
<string name="settings_su_request_10">10 segundos</string>
|
||||
<string name="settings_su_request_20">20 segundos</string>
|
||||
<string name="settings_su_request_30">30 segundos</string>
|
||||
<string name="settings_su_request_60">60 segundos</string>
|
||||
<string name="superuser_access">Acceso de superusuario</string>
|
||||
<string name="auto_response">Respuesta automática</string>
|
||||
<string name="request_timeout">Tiempo de petición</string>
|
||||
<string name="superuser_notification">Notificación de superusuario</string>
|
||||
<string name="request_timeout_summary">%1$s segundos</string>
|
||||
|
||||
<string name="settings_development_category">Desarrollo de la aplicación</string>
|
||||
<string name="settings_developer_logging_title">Habilitar información avanzada de depuración en el registro</string>
|
||||
<string name="settings_developer_logging_summary">Activar esto para grabar más información en el registro</string>
|
||||
<string name="settings_shell_logging_title">Grabar comandos de terminal en el registro</string>
|
||||
<string name="settings_shell_logging_summary">Activar esto para grabar en el log todos los comandos ejecutados y su resultado</string>
|
||||
|
||||
<string name="settings_reboot_toast">Reiniciar para aplicar ajustes</string>
|
||||
<!--Superuser-->
|
||||
<string name="su_request_title">Petición de superusuario</string>
|
||||
<string name="deny_with_str">Denegar%1$s</string>
|
||||
<string name="deny">Denegrar</string>
|
||||
<string name="prompt">Preguntar</string>
|
||||
<string name="grant">Permitir</string>
|
||||
<string name="su_warning">Permite acceso total a tu dispositivo.\n¡Denegar si no está seguro!</string>
|
||||
<string name="forever">Siempre</string>
|
||||
<string name="once">Una vez</string>
|
||||
<string name="tenmin">10 min</string>
|
||||
<string name="twentymin">20 min</string>
|
||||
<string name="thirtymin">30 min</string>
|
||||
<string name="sixtymin">60 min</string>
|
||||
<string name="su_allow_toast">Permitidos derechos de superusuario para %1$s</string>
|
||||
<string name="su_deny_toast">Denegados derechos de superusuario para %1$s</string>
|
||||
<string name="no_apps_found">No se encontraron aplicaciones</string>
|
||||
<string name="su_snack_grant">Derechos de superusuario para %1$s permitidos</string>
|
||||
<string name="su_snack_deny">Derechos de superusuario para %1$s denegados</string>
|
||||
<string name="su_snack_notif_on">Noticiaciones de %1$s habilitadas</string>
|
||||
<string name="su_snack_notif_off">Notificaciones de %1$s deshabilitadas</string>
|
||||
<string name="su_snack_log_on">Registros de %1$s habilitados</string>
|
||||
<string name="su_snack_log_off">Registros de %1$s deshabilitados</string>
|
||||
<string name="su_snack_revoke">Anulados derechos de %1$s</string>
|
||||
<string name="su_revoke_title">¿Anular?</string>
|
||||
<string name="su_revoke_msg">¿Confirmar para anular derechos de %1$s?</string>
|
||||
<string name="toast">Aviso</string>
|
||||
<string name="none">Nada</string>
|
||||
|
||||
<!--Superuser logs-->
|
||||
<string name="pid">PID:\u0020</string>
|
||||
<string name="target_uid">UID de objetivo:\u0020</string>
|
||||
<string name="command">Comando:\u0020</string>
|
||||
|
||||
</resources>
|
||||
|
@@ -4,7 +4,6 @@
|
||||
<!--Welcome Activity-->
|
||||
<string name="navigation_drawer_open">Apri drawer di navigazione</string>
|
||||
<string name="navigation_drawer_close">Chiudi drawer di navigazione</string>
|
||||
<string name="magiskhide">Magisk Hide</string>
|
||||
<string name="modules">Moduli</string>
|
||||
<string name="downloads">Downloads</string>
|
||||
<string name="log">Log</string>
|
||||
@@ -35,14 +34,14 @@
|
||||
|
||||
|
||||
<!--Install Fragment-->
|
||||
<string name="auto_detect">"(Auto) %1$s"</string>
|
||||
<string name="auto_detect">(Auto) %1$s</string>
|
||||
<string name="boot_image_title">Boot Image</string>
|
||||
<string name="detect_button">Identifica</string>
|
||||
<string name="advanced_settings_title">Impostazioni Avanzate</string>
|
||||
<string name="keep_force_encryption">Mantieni crittografia forzata</string>
|
||||
<string name="keep_dm_verity">Mantieni dm-verity</string>
|
||||
<string name="current_magisk_title">Versione Magisk installata: v%1$s</string>
|
||||
<string name="install_magisk_title">Ultima versione Magisk: v%1$.1f</string>
|
||||
<string name="current_magisk_title">Versione Magisk installata: %1$s</string>
|
||||
<string name="install_magisk_title">Ultima versione Magisk: %1$s</string>
|
||||
|
||||
|
||||
<!--Module Fragment-->
|
||||
@@ -60,7 +59,6 @@
|
||||
<string name="update_available">Aggiornamento disponibile</string>
|
||||
<string name="installed">Installato</string>
|
||||
<string name="not_installed">Non installato</string>
|
||||
<string name="changelog">Changelog</string>
|
||||
|
||||
<!--Log Fragment-->
|
||||
<string name="menuSaveToSd">Salva nella SD</string>
|
||||
@@ -90,13 +88,12 @@
|
||||
<string name="download_install">Scarica e installa</string>
|
||||
<string name="download_file_error">Errore nel download del file</string>
|
||||
<string name="install_error">Errore di installazione!</string>
|
||||
<string name="manual_install_1">Errore nel flash del file, il file zip è in %1$s\nFlash esegui il flash manuale</string>
|
||||
<string name="invalid_zip">Lo zip non è un Modulo Magisk!!</string>
|
||||
<string name="reboot_title">Installazione completata</string>
|
||||
<string name="reboot_msg">Vuoi riavviare ora?</string>
|
||||
<string name="reboot">Riavvia</string>
|
||||
<string name="zip_install_progress_title">Installazione</string>
|
||||
<string name="zip_install_progress_msg">"Installazione %1$s …"</string>
|
||||
<string name="zip_install_progress_msg">Installazione %1$s …</string>
|
||||
<string name="no_magisk_title">Magisk non installato!</string>
|
||||
<string name="no_magisk_msg">Vuoi scaricare ed installare Magisk?</string>
|
||||
|
||||
|
188
app/src/main/res/values-ko/strings.xml
Normal file
@@ -0,0 +1,188 @@
|
||||
<resources>
|
||||
<!--Universal-->
|
||||
|
||||
<!--Welcome Activity-->
|
||||
<string name="navigation_drawer_open">내비게이션 서랍 열기</string>
|
||||
<string name="navigation_drawer_close">내비게이션 서랍 닫기</string>
|
||||
<string name="modules">모듈</string>
|
||||
<string name="downloads">다운로드</string>
|
||||
<string name="superuser">슈퍼유저</string>
|
||||
<string name="log">로그</string>
|
||||
<string name="settings">설정</string>
|
||||
<string name="status">상태</string>
|
||||
<string name="install">설치</string>
|
||||
|
||||
<!--Status Fragment-->
|
||||
<string name="magisk_version">Magisk v%1$s 설치됨</string>
|
||||
<string name="magisk_version_disable">Magisk v%1$s 비활성화됨</string>
|
||||
<string name="magisk_version_error">Magisk가 설치되지 않음</string>
|
||||
|
||||
<string name="checking_for_updates">업데이트 확인 중…</string>
|
||||
<string name="magisk_update_available">Magisk v%1$.1f 사용 가능!</string>
|
||||
<string name="cannot_check_updates">업데이트를 확인할 수 없음. 인터넷 연결을 확인하세요.</string>
|
||||
<string name="up_to_date">최신 버전의 %1$s 설치됨</string>
|
||||
<string name="root_error">루팅은 되어 있으나 루트 권한을 얻을 수 없음. 루트 권한 허용 상태를 확인하세요.</string>
|
||||
<string name="not_rooted">루팅이 되어 있지 않음</string>
|
||||
<string name="proper_root">정상적으로 루팅됨</string>
|
||||
<string name="safetyNet_check_text">SafetyNet 체크를 시작하려면 누르기</string>
|
||||
<string name="checking_safetyNet_status">SafetyNet 상태 확인 중…</string>
|
||||
<string name="safetyNet_connection_failed">Google API에 연결할 수 없음</string>
|
||||
<string name="safetyNet_connection_suspended">Google API에 대한 연결이 일시 중지됨</string>
|
||||
<string name="safetyNet_error">SafetyNet 체크 실패. 인터넷 연결을 확인하세요.</string>
|
||||
<string name="safetyNet_fail">SafetyNet 실패: CTS 프로필 불일치</string>
|
||||
<string name="safetyNet_pass">SafetyNet 통과</string>
|
||||
<string name="root_info_warning">기능이 크게 제한됨</string>
|
||||
|
||||
<!--Install Fragment-->
|
||||
<string name="auto_detect">(자동) %1$s</string>
|
||||
<string name="boot_image_title">부트 이미지 위치</string>
|
||||
<string name="detect_button">감지</string>
|
||||
<string name="advanced_settings_title">고급 설정</string>
|
||||
<string name="keep_force_encryption">강제 암호화 유지</string>
|
||||
<string name="keep_dm_verity">dm-verity 유지</string>
|
||||
<string name="current_magisk_title">설치된 Magisk 버전: %1$s</string>
|
||||
<string name="install_magisk_title">최신 Magisk 버전: %1$s</string>
|
||||
<string name="uninstall">제거</string>
|
||||
<string name="reboot_countdown">%1$d초 안에 다시 시작됨</string>
|
||||
|
||||
<!--Module Fragment-->
|
||||
<string name="no_info_provided">(제공된 정보 없음)</string>
|
||||
<string name="no_modules_found">검색된 모듈 없음</string>
|
||||
<string name="update_file_created">모듈이 다음 다시 시작 시 업데이트됨</string>
|
||||
<string name="remove_file_created">모듈이 다음 다시 시작 시 제거됨</string>
|
||||
<string name="remove_file_deleted">모듈이 다음 다시 시작 시 제거되지 않음</string>
|
||||
<string name="disable_file_created">모듈이 다음 다시 시작 시 비활성화됨</string>
|
||||
<string name="disable_file_removed">모듈이 다음 다시 시작 시 활성화됨</string>
|
||||
<string name="author">제작: %1$s</string>
|
||||
<string name="fab_flash_zip">모듈 ZIp 파일 플래싱</string>
|
||||
|
||||
<!--Repo Fragment-->
|
||||
<string name="update_available">업데이트 있음</string>
|
||||
<string name="installed">설치됨</string>
|
||||
<string name="not_installed">설치되지 않음</string>
|
||||
|
||||
<!--Log Fragment-->
|
||||
<string name="menuSaveToSd">SD 카드에 저장</string>
|
||||
<string name="menuReload">다시 불러오기</string>
|
||||
<string name="menuClearLog">지금 로그 비우기</string>
|
||||
<string name="logs_cleared">로그가 성공적으로 비워짐</string>
|
||||
<string name="log_is_empty">로그가 비어 있음</string>
|
||||
<string name="logs_save_failed">SD 카드에 로그를 쓸 수 없음:</string>
|
||||
|
||||
<!--About Activity-->
|
||||
<string name="about">정보</string>
|
||||
<string name="app_developers">주요 개발자</string>
|
||||
<string name="app_developers_"><![CDATA[<a href="https://github.com/d8ahazard">Digitalhigh</a>와 <a href="https://github.com/dvdandroid">Dvdandroid</a>의 도움으로 <a href="https://github.com/topjohnwu">topjohnwu</a>가 앱을 개발하였습니다.]]></string>
|
||||
<string name="app_changelog">앱 변경 사항</string>
|
||||
<string name="translators">lilymaniac</string>
|
||||
<string name="app_version">앱 버전</string>
|
||||
<string name="app_source_code">소스 코드</string>
|
||||
<string name="donation">기부</string>
|
||||
<string name="app_translators">앱 번역자</string>
|
||||
<string name="support_thread">지원 스레드</string>
|
||||
|
||||
<!--Toasts, Dialogs-->
|
||||
<string name="permissionNotGranted">이 기능은 외부 저장소 쓰기 권한 없이는 작동하지 않습니다.</string>
|
||||
<string name="no_thanks">아니오, 괜찮습니다</string>
|
||||
<string name="yes">예</string>
|
||||
<string name="ok">확인</string>
|
||||
<string name="close">닫기</string>
|
||||
<string name="repo_install_title">%1$s 설치</string>
|
||||
<string name="repo_install_msg">정말 %1$s을(를) 설치하시겠습니까?</string>
|
||||
<string name="download_install">다운로드 및 설치</string>
|
||||
<string name="goto_install">\"설치\" 섹션으로 이동</string>
|
||||
<string name="download_file_error">파일 다운로드 오류</string>
|
||||
<string name="install_error">설치 오류!</string>
|
||||
<string name="invalid_zip">이 zip 파일은 Magisk 모듈이 아닙니다!!</string>
|
||||
<string name="reboot_title">설치 성공!</string>
|
||||
<string name="reboot_msg">지금 기기를 다시 시작하시겠습니까?</string>
|
||||
<string name="reboot">다시 시작</string>
|
||||
<string name="copying_msg">임시 디렉터리에 zip 파일 복사 중</string>
|
||||
<string name="zip_install_progress_title">설치 중</string>
|
||||
<string name="zip_unzip_msg">zip 파일 압축 해제 중…</string>
|
||||
<string name="zip_process_msg">zip 파일 처리 중…</string>
|
||||
<string name="zip_install_progress_msg">%1$s 설치 중…</string>
|
||||
<string name="no_magisk_title">Magisk가 설치되어 있지 않습니다!</string>
|
||||
<string name="no_magisk_msg">Magisk를 다운로드하고 설치하시겠습니까?</string>
|
||||
<string name="downloading_toast">%1$s 다운로드 중</string>
|
||||
<string name="magisk_update_title">새 버전의 Magisk를 사용할 수 있습니다!</string>
|
||||
<string name="settings_reboot_toast">기기를 다시 시작하면 설정이 적용됩니다.</string>
|
||||
<string name="release_notes">릴리즈 노트</string>
|
||||
<string name="repo_cache_cleared">저장소 캐시 비워짐</string>
|
||||
<string name="safetyNet_hide_notice">이 앱은 MagiskHide에서\n이미 기본적으로 관리하는 SafetyNet을 사용합니다.</string>
|
||||
<string name="start_magiskhide">MagiskHide 시작 중…</string>
|
||||
<string name="no_magisksu_title">MagiskSU를 사용하고 있지 않습니다!</string>
|
||||
<string name="no_magisksu_msg">MagiskSU로 루팅이 되어있지 않아 MagiskHide 하나만으로는 충분하지 않을 수 있습니다!\n이는 공식적으로 지원되지 않으며, SafetyNet을 통과하려면 추가적인 도구(suhide 등)가 필요할 수 있습니다.</string>
|
||||
<string name="understand">알겠습니다</string>
|
||||
<string name="process_error">처리 오류</string>
|
||||
<string name="internal_storage">zip 파일이 다음 위치에 저장됨:\n[내부 저장소]%1$s</string>
|
||||
|
||||
<!--Settings Activity -->
|
||||
<string name="settings_general_category">일반</string>
|
||||
<string name="settings_dark_theme_title">어두운 테마</string>
|
||||
<string name="settings_dark_theme_summary">어두운 테마 사용</string>
|
||||
<string name="settings_clear_cache_title">저장소 캐시 비우기</string>
|
||||
<string name="settings_clear_cache_summary">온라인 저장소에 대해 캐시된 정보를 지우고, 온라인에서 정보를 강제로 새로 고칩니다.</string>
|
||||
|
||||
<string name="settings_disable_title">Magisk 비활성화</string>
|
||||
<string name="settings_disable_summary">루팅(MagiskSU)을 제외한 모든 것이 비활성화됩니다.</string>
|
||||
<string name="settings_magiskhide_summary">다양한 감지로부터 Magisk를 숨깁니다.</string>
|
||||
<string name="settings_busybox_title">BusyBox 사용</string>
|
||||
<string name="settings_busybox_summary">xbin 디렉터리에 Magisk의 빌트인 busybox를 바인드합니다.</string>
|
||||
<string name="settings_hosts_title">시스템리스 hosts</string>
|
||||
<string name="settings_hosts_summary">애드블록 앱들에서 사용하는 시스템리스 hosts를 지원합니다.</string>
|
||||
|
||||
<string name="settings_su_app_adb">앱 및 ADB</string>
|
||||
<string name="settings_su_app">앱만</string>
|
||||
<string name="settings_su_adb">ADB만</string>
|
||||
<string name="settings_su_disable">사용 안 함</string>
|
||||
<string name="settings_su_request_10">10초</string>
|
||||
<string name="settings_su_request_20">20초</string>
|
||||
<string name="settings_su_request_30">30초</string>
|
||||
<string name="settings_su_request_60">60초</string>
|
||||
<string name="superuser_access">슈퍼유저 액세스</string>
|
||||
<string name="auto_response">자동 응답</string>
|
||||
<string name="request_timeout">요청 시간 제한</string>
|
||||
<string name="superuser_notification">슈퍼유저 알림</string>
|
||||
<string name="request_timeout_summary">%1$s초</string>
|
||||
|
||||
<string name="settings_development_category">앱 개발</string>
|
||||
<string name="settings_developer_logging_title">고급 디버그 로깅 사용</string>
|
||||
<string name="settings_developer_logging_summary">더 자세한 로그를 보려면 체크하세요.</string>
|
||||
<string name="settings_shell_logging_title">셸 커맨드 디버그 로깅 사용</string>
|
||||
<string name="settings_shell_logging_summary">모든 셸 커맨드와 그 출력을 로그에 기록하려면 체크하세요.</string>
|
||||
|
||||
<!--Superuser-->
|
||||
<string name="su_request_title">슈퍼유저 요청</string>
|
||||
<string name="deny_with_str">거부%1$s</string>
|
||||
<string name="deny">거부</string>
|
||||
<string name="prompt">메시지</string>
|
||||
<string name="grant">허용</string>
|
||||
<string name="su_warning">기기에 대한 전체 액세스 권한을 부여합니다.\n확실하지 않은 경우 거부하세요!</string>
|
||||
<string name="forever">영구적으로</string>
|
||||
<string name="once">한 번만</string>
|
||||
<string name="tenmin">10분</string>
|
||||
<string name="twentymin">20분</string>
|
||||
<string name="thirtymin">30분</string>
|
||||
<string name="sixtymin">60분</string>
|
||||
<string name="su_allow_toast">%1$s에 슈퍼유저 권한이 허용됨</string>
|
||||
<string name="su_deny_toast">%1$s에 슈퍼유저 권한이 거부됨</string>
|
||||
<string name="no_apps_found">검색된 앱 없음</string>
|
||||
<string name="su_snack_grant">%1$s의 슈퍼유저 권한이 허용됨</string>
|
||||
<string name="su_snack_deny">%1$s의 슈퍼유저 권한이 거부됨</string>
|
||||
<string name="su_snack_notif_on">%1$s의 알림이 활성화됨</string>
|
||||
<string name="su_snack_notif_off">%1$s의 알림이 비활성화됨</string>
|
||||
<string name="su_snack_log_on">%1$s의 로깅이 활성화됨</string>
|
||||
<string name="su_snack_log_off">%1$s의 로깅이 비활성화됨</string>
|
||||
<string name="su_snack_revoke">%1$s의 권한이 취소됨</string>
|
||||
<string name="su_revoke_title">취소하시겠습니까?</string>
|
||||
<string name="su_revoke_msg">정말 %1$s의 권한을 취소하시겠습니까?</string>
|
||||
<string name="toast">토스트</string>
|
||||
<string name="none">없음</string>
|
||||
|
||||
<!--Superuser logs-->
|
||||
<string name="pid">PID:\u0020</string>
|
||||
<string name="target_uid">대상 UID:\u0020</string>
|
||||
<string name="command">명령:\u0020</string>
|
||||
|
||||
</resources>
|
@@ -33,7 +33,6 @@
|
||||
<string name="update_available">Update Beschikbaar</string>
|
||||
<string name="installed">Geïnstalleerd</string>
|
||||
<string name="not_installed">Niet geïnstalleerd</string>
|
||||
<string name="changelog">Changelog</string>
|
||||
|
||||
<!--Log Fragment-->
|
||||
<string name="menuSaveToSd">Opslaan op SD-kaart</string>
|
||||
@@ -63,13 +62,12 @@
|
||||
<string name="download_install">Downloaden en installeren</string>
|
||||
<string name="download_file_error">Er is een fout opgetreden bij het downloaden van het bestand</string>
|
||||
<string name="install_error">Er is een fout opgetreden in de installatie</string>
|
||||
<string name="manual_install_1">Er is een fout opgetreden in het flashen van het bestand. Het zip bestand is opgeslagen in %1$s\nFlash het handmatig in recovery modus.</string>
|
||||
<string name="invalid_zip">Zip bestand is geen Magisk Module!</string>
|
||||
<string name="reboot_title">Installatie succesvol!</string>
|
||||
<string name="reboot_msg">Wilt u nu rebooten?</string>
|
||||
<string name="reboot">Reboot</string>
|
||||
<string name="zip_install_progress_title">Installeren</string>
|
||||
<string name="zip_install_progress_msg">"Instalatievoortgang: %1$s …"</string>
|
||||
<string name="zip_install_progress_msg">Instalatievoortgang: %1$s …</string>
|
||||
<string name="no_magisk_title">Geen Magisk geïnstalleerd!</string>
|
||||
<string name="no_magisk_msg">Wilt u Magisk downloaden en installeren?</string>
|
||||
|
||||
|
@@ -6,7 +6,6 @@
|
||||
<!--Welcome Activity-->
|
||||
<string name="navigation_drawer_open">Otwórz szufladę nawigacji</string>
|
||||
<string name="navigation_drawer_close">Zamknij szufladę nawigacji</string>
|
||||
<string name="magiskhide">Magisk Hide</string>
|
||||
<string name="modules">Moduły</string>
|
||||
<string name="downloads">Pobieranie</string>
|
||||
<string name="superuser">Superuser</string>
|
||||
@@ -22,30 +21,32 @@
|
||||
|
||||
<string name="checking_for_updates">Sprawdzanie aktualizacji…</string>
|
||||
<string name="magisk_update_available">Magisk v%1$.1f dostępny!</string>
|
||||
<string name="cannot_check_updates">Nie można sprawdzić dostępność aktualizacji, brak internetu</string>
|
||||
<string name="cannot_check_updates">Nie można sprawdzić dostępności aktualizacji, brak internetu</string>
|
||||
<string name="up_to_date">Zainstalowana najnowsza wersja %1$s</string>
|
||||
<string name="root_error">Zrotowany ale bez dostępu do roota</string>
|
||||
<string name="not_rooted">Nie zrootowany</string>
|
||||
<string name="proper_root">Prawidłowo zrootowany</string>
|
||||
<string name="root_error">Root dostępny, ale dostęp nie przyznany</string>
|
||||
<string name="not_rooted">Brak Roota</string>
|
||||
<string name="proper_root">Root Dostępny</string>
|
||||
<string name="safetyNet_check_text">Dotknij aby sprawdzić SafetyNet</string>
|
||||
<string name="checking_safetyNet_status">Sprawdzanie statusu SafetyNet…</string>
|
||||
<string name="safetyNet_connection_failed">Nie można połączyć się z Google API</string>
|
||||
<string name="safetyNet_connection_suspended">Połączenie z Google API zostało zawieszone</string>
|
||||
<string name="safetyNet_error">Nie można sprawdzić SafetyNet bez Internetu</string>
|
||||
<string name="safetyNet_fail">SafetyNet Failed: CTS profile mismatch</string>
|
||||
<string name="safetyNet_pass">SafetyNet Prawidłowy</string>
|
||||
<string name="safetyNet_error">Nie można sprawdzić SafetyNet bez internetu</string>
|
||||
<string name="safetyNet_fail">Błąd SafetyNet: Niezgodność profilu CTS</string>
|
||||
<string name="safetyNet_pass">SafetyNet Poprawny</string>
|
||||
<string name="root_info_warning">Funkcjonalność znacznie ograniczona</string>
|
||||
|
||||
<!--Install Fragment-->
|
||||
<string name="auto_detect">"(Auto) %1$s"</string>
|
||||
<string name="auto_detect">(Auto) %1$s</string>
|
||||
<string name="boot_image_title">Lokalizacja Boot Image</string>
|
||||
<string name="detect_button">Wykryj</string>
|
||||
<string name="advanced_settings_title">Zaawansowane Ustawienia</string>
|
||||
<string name="keep_force_encryption">Keep force encryption</string>
|
||||
<string name="keep_dm_verity">Keep dm-verity</string>
|
||||
<string name="current_magisk_title">Zainstalowana Wersja Magisk: v%1$s</string>
|
||||
<string name="install_magisk_title">Ostatnia Wersja Magisk: v%1$.1f</string>
|
||||
|
||||
<string name="current_magisk_title">Zainstalowana Wersja Magisk: %1$s</string>
|
||||
<string name="install_magisk_title">Ostatnia Wersja Magisk: %1$s</string>
|
||||
|
||||
<string name="uninstall">Odinstaluj</string>
|
||||
<string name="reboot_countdown">Restartuj do %1$d</string>
|
||||
|
||||
<!--Module Fragment-->
|
||||
<string name="no_info_provided">(Nie umieszczono informacji)</string>
|
||||
@@ -55,14 +56,13 @@
|
||||
<string name="remove_file_deleted">Moduł nie zostanie usunięty podczas następnego restartu</string>
|
||||
<string name="disable_file_created">Moduł zostanie wyłączony przy następnym restarcie</string>
|
||||
<string name="disable_file_removed">Moduł zostanie włączony przy następnym restarcie</string>
|
||||
<string name="author">Autor modu: %1$s</string>
|
||||
<string name="fab_flash_zip">Zflashuj Moduł Zip</string>
|
||||
<string name="author">Autor: %1$s</string>
|
||||
<string name="fab_flash_zip">Zainstaluj Moduł Zip</string>
|
||||
|
||||
<!--Repo Fragment-->
|
||||
<string name="update_available">Aktualizacja jest dostępna</string>
|
||||
<string name="installed">Zainstalowany</string>
|
||||
<string name="not_installed">Nie zainstalowany</string>
|
||||
<string name="changelog">Zmiany</string>
|
||||
|
||||
<!--Log Fragment-->
|
||||
<string name="menuSaveToSd">Zapisz na SD</string>
|
||||
@@ -79,7 +79,7 @@
|
||||
<string name="app_changelog">Zmiany w Aplikacji</string>
|
||||
<string name="translators" />
|
||||
<string name="app_version">Wersja Aplikacji</string>
|
||||
<string name="app_source_code">Source code</string>
|
||||
<string name="app_source_code">Kod Źródłowy</string>
|
||||
<string name="donation">Dotacja</string>
|
||||
<string name="app_translators">Tłumacze Aplikacji</string>
|
||||
<string name="support_thread">Strona Wsparcia</string>
|
||||
@@ -88,48 +88,49 @@
|
||||
<string name="permissionNotGranted">Ta funkcja nie będzie działać bez uprawnień do zapisu na pamięci zewnętrznej.</string>
|
||||
<string name="no_thanks">Nie dziękuję</string>
|
||||
<string name="yes">Tak</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="close">Zamknij</string>
|
||||
<string name="repo_install_title">Zainstaluj %1$s</string>
|
||||
<string name="repo_install_msg">Czy chcesz zainstalować %1$s ?</string>
|
||||
<string name="download_install">Pobierz i zainstaluj</string>
|
||||
<string name="download">Pobierz</string>
|
||||
<string name="goto_install">Idź do sekcji \"Instalacja\"</string>
|
||||
<string name="download_file_error">Błąd pobierania pliku</string>
|
||||
<string name="install_error">Błąd instalacji!</string>
|
||||
<string name="manual_install_1">Plik zip umieszczony w %1$s</string>
|
||||
<string name="manual_install_2">Zflashuj go ręcznie w recovery</string>
|
||||
<string name="invalid_zip">Ten zip nie jest Modułem Magisk!!</string>
|
||||
<string name="reboot_title">Instalacja zakończona powodzeniem!</string>
|
||||
<string name="reboot_msg">Czy chcesz teraz ponownie uruchomić?</string>
|
||||
<string name="reboot">Restart</string>
|
||||
<string name="copying_msg">Kopiowanie zip do katalogu temp</string>
|
||||
<string name="zip_install_progress_title">Instalacja</string>
|
||||
<string name="zip_install_unzip_zip_msg">Rozpakowywanie pliku zip …</string>
|
||||
<string name="zip_install_process_zip_msg">Przetwarzanie pliku zip …</string>
|
||||
<string name="zip_install_progress_msg">"Instalowanie %1$s …"</string>
|
||||
<string name="zip_unzip_msg">Rozpakowywanie pliku zip …</string>
|
||||
<string name="zip_process_msg">Przetwarzanie pliku zip …</string>
|
||||
<string name="zip_install_progress_msg">Instalowanie %1$s …</string>
|
||||
<string name="no_magisk_title">Brak zainstalowanego Magisk!</string>
|
||||
<string name="no_magisk_msg">Chcesz pobrać i zainstalować Magisk?</string>
|
||||
<string name="downloading_toast">Pobieranie %1$s</string>
|
||||
<string name="magisk_update_title">Nowa Wersja Magisk Dostepna!</string>
|
||||
<string name="magisk_update_message">Magisk v%1$.1f jest dostępny, chcesz zainstalować?</string>
|
||||
<string name="magisk_update_title">Nowa Wersja Magisk Dostępna!</string>
|
||||
<string name="settings_reboot_toast">Uruchom ponownie, aby zastosować ustawienia</string>
|
||||
<string name="check_release_notes">Sprawdź informacje o wydaniu</string>
|
||||
<string name="release_notes">Zmiany</string>
|
||||
<string name="repo_cache_cleared">Cache repozytorium wyczyszczone</string>
|
||||
<string name="safetyNet_hide_notice">Ta aplikacja wykorzystuje SafetyNet\nJuż jest domyślnie obsługiwana przez MagiskHide</string>
|
||||
<string name="safetyNet_hide_notice">Ta aplikacja wykorzystuje SafetyNet\nJest już domyślnie obsługiwana przez MagiskHide</string>
|
||||
<string name="start_magiskhide">Uruchamianie MagiskHide …</string>
|
||||
|
||||
<!--URL Templates-->
|
||||
|
||||
|
||||
|
||||
|
||||
<string name="no_magisksu_title">Nie Używaj MagiskSU!</string>
|
||||
<string name="no_magisksu_msg">Jeśli nie masz roota z MagiskSU, używanie samego MagiskHide może nie wystarczyć! Inne metody nie są oficjalnie obsługiwane. Do poprawnego działania SaftyNet potrzebne będą dodatkowe narzędzia (np suhide)</string>
|
||||
<string name="understand">Rozumiem</string>
|
||||
<string name="process_error">Błąd procesu</string>
|
||||
<string name="internal_storage">Zip jest przechowywany w:\n[Pamięć Wewnętrzna]%1$s</string>
|
||||
<string name="zip_process_title">Przetwarzanie</string>
|
||||
|
||||
<!--Settings Activity -->
|
||||
<string name="settings_general_category">Ogólne</string>
|
||||
<string name="settings_dark_theme_title">Ciemny Motyw</string>
|
||||
<string name="settings_dark_theme_summary">Włącz ciemny motyw</string>
|
||||
<string name="settings_clear_cache_title">Wyczyść Cache Repozytorium</string>
|
||||
<string name="settings_clear_cache_summary">Wyczyść cache dla repozytorium, wymusza odświeżenie online przez aplikację</string>
|
||||
<string name="settings_clear_cache_title">Wyczyść Pamięć Repozytorium</string>
|
||||
<string name="settings_clear_cache_summary">Wymusza na aplikacji odświeżenie online repozytorium</string>
|
||||
|
||||
<string name="settings_disable_title">Wyłącz Montowanie Magisk</string>
|
||||
<string name="settings_disable_summary">Disable Magic Mount, co uniemożliwi wszystkim modułom działanie</string>
|
||||
<string name="settings_disable_title">Wyłącz Magisk</string>
|
||||
<string name="settings_disable_summary">Wszystko zostanie wyłączone za wyjątkiem roota (MagiskSU)</string>
|
||||
<string name="settings_magiskhide_summary">Włącz Hide Magisk dla wykrytych aplikacji</string>
|
||||
<string name="settings_busybox_title">Włącz BusyBox</string>
|
||||
<string name="settings_busybox_summary">Zmień montowanie Magisk z wbudowanego busybox do xbin</string>
|
||||
@@ -139,14 +140,14 @@
|
||||
<string name="settings_su_app_adb">Aplikacje i ADB</string>
|
||||
<string name="settings_su_app">Tylko aplikacje</string>
|
||||
<string name="settings_su_adb">Tylko ADB</string>
|
||||
<string name="settings_su_disable">Wyłaczone</string>
|
||||
<string name="settings_su_disable">Wyłączone</string>
|
||||
<string name="settings_su_request_10">10 sekund</string>
|
||||
<string name="settings_su_request_20">20 sekund</string>
|
||||
<string name="settings_su_request_30">30 ssekund</string>
|
||||
<string name="settings_su_request_30">30 sekund</string>
|
||||
<string name="settings_su_request_60">60 sekund</string>
|
||||
<string name="superuser_access">Dostęp Superuser</string>
|
||||
<string name="auto_response">Automatyczna Odpowiedź</string>
|
||||
<string name="request_timeout">Czas Prośby</string>
|
||||
<string name="request_timeout">Czas na decyzję</string>
|
||||
<string name="superuser_notification">Powiadomienia Superusera</string>
|
||||
<string name="request_timeout_summary">%1$s sekund</string>
|
||||
|
||||
@@ -157,7 +158,7 @@
|
||||
<string name="settings_shell_logging_summary">Włącz, aby rejestrować wszystkie polecenia powłoki i wyjścia</string>
|
||||
|
||||
<!--Superuser-->
|
||||
<string name="su_request_title">Prośba dostępu Superusera</string>
|
||||
<string name="su_request_title">Prośba o dostęp Superusera</string>
|
||||
<string name="deny_with_str">Odmów%1$s</string>
|
||||
<string name="deny">Odmów</string>
|
||||
<string name="prompt">Zapytaj</string>
|
||||
@@ -181,12 +182,12 @@
|
||||
<string name="su_snack_revoke">%1$s uprawnienia są odwołane</string>
|
||||
<string name="su_revoke_title">Odwołać?</string>
|
||||
<string name="su_revoke_msg">Potwierdzasz odwołanie uprawnień %1$s?</string>
|
||||
<string name="toast">Toast</string>
|
||||
<string name="toast">Powiadomienie</string>
|
||||
<string name="none">Brak</string>
|
||||
|
||||
<!--Superuser logs-->
|
||||
<string name="pid">PID:\u0020</string>
|
||||
<string name="target_uid">Indentyfikator UID:\u0020</string>
|
||||
<string name="target_uid">Identyfikator UID:\u0020</string>
|
||||
<string name="command">Komenda:\u0020</string>
|
||||
|
||||
</resources>
|
||||
|
@@ -6,7 +6,6 @@
|
||||
<!--Welcome Activity-->
|
||||
<string name="navigation_drawer_open">Abrir gaveta de notificação</string>
|
||||
<string name="navigation_drawer_close">Fechar gaveta de notificação</string>
|
||||
<string name="magiskhide">Magisk Hide</string>
|
||||
<string name="modules">Módulos</string>
|
||||
<string name="downloads">Baixar</string>
|
||||
<string name="superuser">Superusuário</string>
|
||||
@@ -36,14 +35,14 @@
|
||||
<string name="root_info_warning">Funcionalidade muito limitada</string>
|
||||
|
||||
<!--Install Fragment-->
|
||||
<string name="auto_detect">"(Auto) %1$s"</string>
|
||||
<string name="auto_detect">(Auto) %1$s</string>
|
||||
<string name="boot_image_title">Local da Boot Image</string>
|
||||
<string name="detect_button">Detectar</string>
|
||||
<string name="advanced_settings_title">Configurações avançadas</string>
|
||||
<string name="keep_force_encryption">Keep force encryption</string>
|
||||
<string name="keep_dm_verity">Keep dm-verity</string>
|
||||
<string name="current_magisk_title">Versão instalada do Magisk: v%1$s</string>
|
||||
<string name="install_magisk_title">Última versão do Magisk: v%1$.1f</string>
|
||||
<string name="current_magisk_title">Versão instalada do Magisk: %1$s</string>
|
||||
<string name="install_magisk_title">Última versão do Magisk: %1$s</string>
|
||||
|
||||
|
||||
<!--Module Fragment-->
|
||||
@@ -61,7 +60,6 @@
|
||||
<string name="update_available">Atualização disponível</string>
|
||||
<string name="installed">Instalado</string>
|
||||
<string name="not_installed">Não Instalado</string>
|
||||
<string name="changelog">Registro de mudança</string>
|
||||
|
||||
<!--Log Fragment-->
|
||||
<string name="menuSaveToSd">Salvar no SD</string>
|
||||
@@ -93,24 +91,21 @@
|
||||
<string name="goto_install">Ir na seção \"Instalar\"</string>
|
||||
<string name="download_file_error">Erro ao baixar o arquivo</string>
|
||||
<string name="install_error">Erro na instalação!</string>
|
||||
<string name="manual_install_1">Erro ao flashear o arquivo, arquivo zip colocado em %1$s</string>
|
||||
<string name="manual_install_2">Flashear isto na recuperação manualmente</string>
|
||||
<string name="invalid_zip">O zip não é um Módulo Magisk!!</string>
|
||||
<string name="reboot_title">Instalação bem-sucedida!</string>
|
||||
<string name="reboot_msg">Você quer reiniciar agora?</string>
|
||||
<string name="reboot">Reiniciar</string>
|
||||
<string name="copying_msg">Copiando zip para diretório temporário</string>
|
||||
<string name="zip_install_progress_title">Instalando</string>
|
||||
<string name="zip_install_unzip_zip_msg">Descompactando arquivo zip …</string>
|
||||
<string name="zip_install_process_zip_msg">Processando arquivo zip …</string>
|
||||
<string name="zip_install_progress_msg">"Instalando %1$s …"</string>
|
||||
<string name="zip_unzip_msg">Descompactando arquivo zip …</string>
|
||||
<string name="zip_process_msg">Processando arquivo zip …</string>
|
||||
<string name="zip_install_progress_msg">Instalando %1$s …</string>
|
||||
<string name="no_magisk_title">Magisk Não Instalado!</string>
|
||||
<string name="no_magisk_msg">Você quer baixar e instalar o Magisk?</string>
|
||||
<string name="downloading_toast">Baixando %1$s</string>
|
||||
<string name="magisk_update_title">Nova atualização do Magisk disponível!</string>
|
||||
<string name="magisk_update_message">Magisk v%1$.1f Atualização está pronta, você quer instalar?</string>
|
||||
<string name="settings_reboot_toast">Reinicie para aplicar configurações</string>
|
||||
<string name="check_release_notes">Verificar as notas da atualização</string>
|
||||
<string name="settings_reboot_toast">Reinicie para aplicar configurações</string>
|
||||
<string name="release_notes">Notas da atualização</string>
|
||||
<string name="repo_cache_cleared">Cache do Repo. limpado</string>
|
||||
<string name="safetyNet_hide_notice">Este aplicativo usa SafetyNet\nJá manipulado pelo MagiskHide por padrão</string>
|
||||
<string name="start_magiskhide">Iniciando MagiskHide …</string>
|
||||
|
183
app/src/main/res/values-ru/strings.xml
Normal file
@@ -0,0 +1,183 @@
|
||||
<resources>
|
||||
<!--Welcome Activity-->
|
||||
<string name="navigation_drawer_open">Открыть меню навигации</string>
|
||||
<string name="navigation_drawer_close">Закрыть меню навигации</string>
|
||||
<string name="modules">Модули</string>
|
||||
<string name="downloads">Загрузки</string>
|
||||
<string name="superuser">Суперпользователь</string>
|
||||
<string name="log">Лог</string>
|
||||
<string name="settings">Настройки</string>
|
||||
<string name="status">Статус</string>
|
||||
<string name="install">Установка</string>
|
||||
|
||||
<!--Status Fragment-->
|
||||
<string name="magisk_version">Установлен Magisk v%1$s</string>
|
||||
<string name="magisk_version_disable">Magisk v%1$s выключен</string>
|
||||
<string name="magisk_version_error">Magisk не установлен</string>
|
||||
|
||||
<string name="checking_for_updates">Проверка обновлений…</string>
|
||||
<string name="magisk_update_available">Доступен Magisk v%1$.1f!</string>
|
||||
<string name="cannot_check_updates">Невозможно проверить обновления, нет соединения?</string>
|
||||
<string name="up_to_date">Установлена последняя версия %1$s</string>
|
||||
<string name="root_error">Рут есть, но нет разрешения, не разрешено?</string>
|
||||
<string name="not_rooted">Нет рута</string>
|
||||
<string name="proper_root">Рут получен правильно</string>
|
||||
<string name="safetyNet_check_text">Нажмите для запуска проверки SafetyNet</string>
|
||||
<string name="checking_safetyNet_status">Проверка статуса SafetyNet…</string>
|
||||
<string name="safetyNet_connection_failed">Невозможно соединиться с API Google</string>
|
||||
<string name="safetyNet_connection_suspended">Соединение с API Google было приостановлено</string>
|
||||
<string name="safetyNet_error">Невозможно выполнить проверку SafetyNet, нет соединения?</string>
|
||||
<string name="safetyNet_fail">SafetyNet не пройден: несовпадение профиля CTS</string>
|
||||
<string name="safetyNet_pass">SafetyNet пройден</string>
|
||||
<string name="root_info_warning">Функциональность значительно ограничена</string>
|
||||
|
||||
<!--Install Fragment-->
|
||||
<string name="auto_detect">(Авто) %1$s</string>
|
||||
<string name="boot_image_title">Местоположение образа Boot</string>
|
||||
<string name="detect_button">Определить</string>
|
||||
<string name="advanced_settings_title">Дополнительные настройки</string>
|
||||
<string name="keep_force_encryption">Оставить шифрование</string>
|
||||
<string name="keep_dm_verity">Оставить dm-verity</string>
|
||||
<string name="current_magisk_title">Установленная версия Magisk: %1$s</string>
|
||||
<string name="install_magisk_title">Последняя версия Magisk: %1$s</string>
|
||||
<string name="uninstall">Удалить</string>
|
||||
<string name="reboot_countdown">Перезагрузка через %1$d</string>
|
||||
|
||||
<!--Module Fragment-->
|
||||
<string name="no_info_provided">(Нет информации)</string>
|
||||
<string name="no_modules_found">Модули не найдены</string>
|
||||
<string name="update_file_created">Модуль будет обновлён при перезагрузке</string>
|
||||
<string name="remove_file_created">Модуль будет удалён при перезагрузке</string>
|
||||
<string name="remove_file_deleted">Модуль не будет удалён при перезагрузке</string>
|
||||
<string name="disable_file_created">Модуль будет выключён при перезагрузке</string>
|
||||
<string name="disable_file_removed">Модуль будет включён при перезагрузке</string>
|
||||
<string name="author">Автор: %1$s</string>
|
||||
<string name="fab_flash_zip">Прошить модуль из zip-архива</string>
|
||||
|
||||
<!--Repo Fragment-->
|
||||
<string name="update_available">Доступно обновление</string>
|
||||
<string name="installed">Установлен</string>
|
||||
<string name="not_installed">Не установлен</string>
|
||||
|
||||
<!--Log Fragment-->
|
||||
<string name="menuSaveToSd">Сохранить на SD-карту</string>
|
||||
<string name="menuReload">Обновить</string>
|
||||
<string name="menuClearLog">Очистить</string>
|
||||
<string name="logs_cleared">Лог успешно очищен</string>
|
||||
<string name="log_is_empty">Лог пуст</string>
|
||||
<string name="logs_save_failed">Не удалось сохранить лог на SD-карту:</string>
|
||||
|
||||
<!--About Activity-->
|
||||
<string name="about">О приложении</string>
|
||||
<string name="app_developers">Основные разработчики</string>
|
||||
<string name="app_developers_"><![CDATA[Приложение создано <a href="https://github.com/topjohnwu">topjohnwu</a> совместно с <a href="https://github.com/d8ahazard">Digitalhigh</a> и <a href="https://github.com/dvdandroid">Dvdandroid</a>.]]></string>
|
||||
<string name="app_changelog">Список изменений</string>
|
||||
<string name="translators">Exalm</string>
|
||||
<string name="app_version">Версия</string>
|
||||
<string name="app_source_code">Исходный код</string>
|
||||
<string name="donation">Пожертвовать</string>
|
||||
<string name="app_translators">Переводчики</string>
|
||||
<string name="support_thread">Страница поддержки</string>
|
||||
|
||||
<!--Toasts, Dialogs-->
|
||||
<string name="permissionNotGranted">Это не будет работать без доступа к внешнему хранилищу</string>
|
||||
<string name="no_thanks">Нет, спасибо</string>
|
||||
<string name="yes">Да</string>
|
||||
<string name="repo_install_title">Установить %1$s</string>
|
||||
<string name="repo_install_msg">Вы хотите установить %1$s ?</string>
|
||||
<string name="download_install">Скачать и установить</string>
|
||||
<string name="goto_install">Перейти в раздел «Установка»</string>
|
||||
<string name="download_file_error">Ошибка при скачивании файла</string>
|
||||
<string name="install_error">Ошибка при установке!</string>
|
||||
а
|
||||
<string name="invalid_zip">Этот архив не содержит модуль Magisk!!</string>
|
||||
<string name="reboot_title">Установка успешна!</string>
|
||||
<string name="reboot_msg">Вы хотите перезагрузиться?</string>
|
||||
<string name="reboot">Перезагрузка</string>
|
||||
<string name="copying_msg">Копирование архива во временную директорию</string>
|
||||
<string name="zip_install_progress_title">Установка</string>
|
||||
<string name="zip_unzip_msg">Распаковка zip-файла…</string>
|
||||
<string name="zip_process_msg">Обработка zip-файла…</string>
|
||||
<string name="zip_install_progress_msg">Установка %1$s…</string>
|
||||
<string name="no_magisk_title">Magisk не установлен!</string>
|
||||
<string name="no_magisk_msg">Вы хотите скачать и установить Magisk?</string>
|
||||
<string name="downloading_toast">Скачивание %1$s</string>
|
||||
<string name="magisk_update_title">Доступно обновление Magisk!</string>
|
||||
<string name="settings_reboot_toast">Перезагрузитесь для применения изменений</string>
|
||||
<string name="release_notes">Примечания к выпуску</string>
|
||||
<string name="repo_cache_cleared">Кэш репозиториев очищен</string>
|
||||
<string name="safetyNet_hide_notice">Это приложение использует SafetyNet\nУже обработано MagiskHide по умолчанию</string>
|
||||
<string name="start_magiskhide">Запуск MagiskHide…</string>
|
||||
<string name="no_magisksu_title">MagiskSU не используется!</string>
|
||||
<string name="no_magisksu_msg">Если рут получен не через MagiskSU, использования MagiskHide может не хватить!\nЭто официально не поддерживается, и вам могут понадобиться дополнительные инструменты (например, suhide), чтобы пройти SafetyNet.</string>
|
||||
<string name="understand">Я понимаю</string>
|
||||
|
||||
<!--Settings Activity -->
|
||||
<string name="settings_general_category">Основные</string>
|
||||
<string name="settings_dark_theme_title">Тёмная тема</string>
|
||||
<string name="settings_dark_theme_summary">Включить тёмную тему</string>
|
||||
<string name="settings_clear_cache_title">Очистить кэш репозиториев</string>
|
||||
<string name="settings_clear_cache_summary">Удалить сохранённую информацию о сетевых репозиториях, чтобы приложение обновило информацию из сети</string>
|
||||
|
||||
<string name="settings_disable_title">Отключить Magisk</string>
|
||||
<string name="settings_disable_summary">Будет выключено все, кроме рута (MagiskSU)</string>
|
||||
<string name="settings_magiskhide_summary">Скрыть Magisk от различных проверок</string>
|
||||
<string name="settings_busybox_title">Включить BusyBox</string>
|
||||
<string name="settings_busybox_summary">Примонтировать встроенный busybox из Magisk в xbin</string>
|
||||
<string name="settings_hosts_title">Включить Systemless hosts</string>
|
||||
<string name="settings_hosts_summary">Поддержа Systemless hosts для блокировщиков рекламы</string>
|
||||
|
||||
<string name="settings_su_app_adb">Для приложений и ADB</string>
|
||||
<string name="settings_su_app">Только для приложений</string>
|
||||
<string name="settings_su_adb">Только для ADB</string>
|
||||
<string name="settings_su_disable">Выключен</string>
|
||||
<string name="settings_su_request_10">10 секунд</string>
|
||||
<string name="settings_su_request_20">20 секунд</string>
|
||||
<string name="settings_su_request_30">30 секунд</string>
|
||||
<string name="settings_su_request_60">60 секунд</string>
|
||||
<string name="superuser_access">Доступ суперпользователя</string>
|
||||
<string name="auto_response">Автоматический ответ</string>
|
||||
<string name="request_timeout">Таймаут запроса</string>
|
||||
<string name="superuser_notification">Уведомление суперпользователя</string>
|
||||
<string name="request_timeout_summary">%1$s секунд</string>
|
||||
|
||||
<string name="settings_development_category">Разработка</string>
|
||||
<string name="settings_developer_logging_title">Включить подробное логгирование</string>
|
||||
<string name="settings_developer_logging_summary">Нажмите, чтобы включить подробную запись</string>
|
||||
<string name="settings_shell_logging_title">Включить подробное логгирование команд оболочки</string>
|
||||
<string name="settings_shell_logging_summary">Нажмите, чтобы включить запись всех команд оболочки и их вывод</string>
|
||||
|
||||
<!--Superuser-->
|
||||
<string name="su_request_title">Запрос прав суперпользователя</string>
|
||||
<string name="deny_with_str">Отказать %1$s</string>
|
||||
<string name="deny">Отказать</string>
|
||||
<string name="prompt">Запрос</string>
|
||||
<string name="grant">Предоставить</string>
|
||||
<string name="su_warning">Предоставить полный доступ к устройству.\nОтклоните, если не уверены!</string>
|
||||
<string name="forever">Навсегда</string>
|
||||
<string name="once">Один раз</string>
|
||||
<string name="tenmin">На 10 минут</string>
|
||||
<string name="twentymin">На 20 минут</string>
|
||||
<string name="thirtymin">На 30 минут</string>
|
||||
<string name="sixtymin">На 60 минут</string>
|
||||
<string name="su_allow_toast">Права суперпользователя предоставлены для %1$s</string>
|
||||
<string name="su_deny_toast">Отказано в получении прав суперпользователя для %1$s</string>
|
||||
<string name="no_apps_found">Приложения не найдены</string>
|
||||
<string name="su_snack_grant">Права суперпользователя предоставлены для %1$s</string>
|
||||
<string name="su_snack_deny">Права суперпользователя для %1$s не предоставлены</string>
|
||||
<string name="su_snack_notif_on">Включены уведомления для %1$s</string>
|
||||
<string name="su_snack_notif_off">Выключены уведомления для %1$s</string>
|
||||
<string name="su_snack_log_on">Включено логгирование для %1$s</string>
|
||||
<string name="su_snack_log_off">Выключено логгирование для %1$s</string>
|
||||
<string name="su_snack_revoke">Права для %1$s убраны</string>
|
||||
<string name="su_revoke_title">Убрать?</string>
|
||||
<string name="su_revoke_msg">Вы действительно хотите убрать права суперпользователя для %1$s?</string>
|
||||
<string name="toast">Сообщение</string>
|
||||
<string name="none">Ничего</string>
|
||||
|
||||
<!--Superuser logs-->
|
||||
<string name="pid">PID:\u0020</string>
|
||||
<string name="target_uid">Идентификатор UID:\u0020</string>
|
||||
<string name="command">Команда:\u0020</string>
|
||||
|
||||
</resources>
|
@@ -4,7 +4,6 @@
|
||||
<!--Welcome Activity-->
|
||||
<string name="navigation_drawer_open">Open navigation drawer</string>
|
||||
<string name="navigation_drawer_close">Close navigation drawer</string>
|
||||
<string name="magiskhide">Magisk 隐藏</string>
|
||||
<string name="modules">模块</string>
|
||||
<string name="downloads">下载</string>
|
||||
<string name="superuser">超级用户</string>
|
||||
@@ -15,6 +14,7 @@
|
||||
|
||||
<!--Status Fragment-->
|
||||
<string name="magisk_version">已安装 Magisk v%1$s</string>
|
||||
<string name="magisk_version_disable">已禁用 Magisk v%1$s</string>
|
||||
<string name="magisk_version_error">未安装 Magisk</string>
|
||||
|
||||
<string name="checking_for_updates">正在检查更新…</string>
|
||||
@@ -34,14 +34,18 @@
|
||||
<string name="root_info_warning">功能严重受限</string>
|
||||
|
||||
<!--Install Fragment-->
|
||||
<string name="auto_detect">"(自动) %1$s"</string>
|
||||
<string name="auto_detect">(自动) %1$s</string>
|
||||
<string name="boot_image_title">Boot 镜像位置</string>
|
||||
<string name="detect_button">检测</string>
|
||||
<string name="advanced_settings_title">高级设置</string>
|
||||
<string name="keep_force_encryption">保持强制加密</string>
|
||||
<string name="keep_dm_verity">保持 dm-verity</string>
|
||||
<string name="current_magisk_title">已安装 Magisk 版本:v%1$s</string>
|
||||
<string name="install_magisk_title">最新的 Magisk 版本:v%1$.1f</string>
|
||||
<string name="keep_dm_verity">保留 dm-verity</string>
|
||||
<string name="current_magisk_title">已安装 Magisk 版本:%1$s</string>
|
||||
<string name="install_magisk_title">最新的 Magisk 版本:%1$s</string>
|
||||
<string name="uninstall">卸载</string>
|
||||
<string name="reboot_countdown">将在 %1$d 后重启</string>
|
||||
<string name="uninstall_magisk_title">卸载 Magisk</string>
|
||||
<string name="uninstall_magisk_msg">将会删除所有模块及 MagiskSU,并有可能在目前未加密的情况下加密你的数据\n你确定要继续吗?</string>
|
||||
|
||||
<!--Module Fragment-->
|
||||
<string name="no_info_provided">(未提供信息)</string>
|
||||
@@ -58,7 +62,6 @@
|
||||
<string name="update_available">可更新</string>
|
||||
<string name="installed">已安装</string>
|
||||
<string name="not_installed">未安装</string>
|
||||
<string name="changelog">更新日志</string>
|
||||
|
||||
<!--Log Fragment-->
|
||||
<string name="menuSaveToSd">保存到 SD 卡</string>
|
||||
@@ -84,43 +87,49 @@
|
||||
<string name="permissionNotGranted">未授予写入外置存储权限,此功能无法正常工作。</string>
|
||||
<string name="no_thanks">不,谢谢</string>
|
||||
<string name="yes">是</string>
|
||||
<string name="ok">确定</string>
|
||||
<string name="close">关闭</string>
|
||||
<string name="repo_install_title">安装 %1$s</string>
|
||||
<string name="repo_install_msg">你想要安装 %1$s 吗?</string>
|
||||
<string name="download_install">下载并安装</string>
|
||||
<string name="download">下载</string>
|
||||
<string name="goto_install">前往“安装”界面</string>
|
||||
<string name="download_file_error">下载文件时出错</string>
|
||||
<string name="install_error">安装出错!</string>
|
||||
<string name="manual_install_1">Zip 文件已保存至 %1$s</string>
|
||||
<string name="manual_install_2">请在 Recovery 中手动刷入</string>
|
||||
<string name="invalid_zip">此 zip 文件不是 Magisk 模块!!</string>
|
||||
<string name="reboot_title">安装成功!</string>
|
||||
<string name="reboot_msg">你想要立即重启吗?</string>
|
||||
<string name="reboot">重启</string>
|
||||
<string name="copying_msg">正在复制 zip 到临时目录</string>
|
||||
<string name="zip_install_progress_title">正在安装</string>
|
||||
<string name="zip_install_unzip_zip_msg">正在解压 zip 文件 …</string>
|
||||
<string name="zip_install_process_zip_msg">正在处理 zip 文件 …</string>
|
||||
<string name="zip_install_progress_msg">"正在安装 %1$s …"</string>
|
||||
<string name="zip_unzip_msg">正在解压 zip 文件 …</string>
|
||||
<string name="zip_process_msg">正在处理 zip 文件 …</string>
|
||||
<string name="zip_install_progress_msg">正在安装 %1$s …</string>
|
||||
<string name="no_magisk_title">未安装 Magisk!</string>
|
||||
<string name="no_magisk_msg">你想要下载并安装 Magisk 吗?</string>
|
||||
<string name="downloading_toast">正在下载 %1$s</string>
|
||||
<string name="magisk_update_title">Magisk 可更新!</string>
|
||||
<string name="magisk_update_message">Magisk 已有新版本 v%1$.1f,你想要安装吗?</string>
|
||||
<string name="settings_reboot_toast">重启以应用设置</string>
|
||||
<string name="check_release_notes">查看发布说明</string>
|
||||
<string name="release_notes">发布说明</string>
|
||||
<string name="repo_cache_cleared">资源库缓存已清除</string>
|
||||
<string name="safetyNet_hide_notice">此应用使用了 SafetyNet\n已默认由 MagiskHide 处理</string>
|
||||
<string name="start_magiskhide">正在启动 MagiskHide …</string>
|
||||
|
||||
<!--URL Templates-->
|
||||
<string name="no_magisksu_title">未使用 MagiskSU!</string>
|
||||
<string name="no_magisksu_msg">你未使用 MagiskSU 进行 ROOT,仅使用 MagiskHide 可能并不足够!\n官方并不支持,你需要额外的工具(如 suhide)才能通过 Safety Net。</string>
|
||||
<string name="understand">我明白</string>
|
||||
<string name="process_error">处理失败</string>
|
||||
<string name="internal_storage">Zip 已被储存到:\n[内部存储]%1$s</string>
|
||||
<string name="zip_process_title">正在处理</string>
|
||||
|
||||
<!--Settings Activity -->
|
||||
<string name="settings_general_category">常规</string>
|
||||
<string name="settings_dark_theme_title">深色主题</string>
|
||||
<string name="settings_dark_theme_summary">使用深色主题</string>
|
||||
<string name="settings_clear_cache_title">清除资源库缓存</string>
|
||||
<string name="settings_clear_cache_summary">清除已缓存的在线资源库信息,强制应用刷新在线数据</string>
|
||||
<string name="settings_clear_cache_summary">清除已缓存的在线资源库信息,强制刷新在线数据</string>
|
||||
|
||||
<string name="settings_disable_title">禁用 Magisk</string>
|
||||
<string name="settings_disable_summary">除 ROOT (MagiskSU) 以外,其他都将被禁用</string>
|
||||
<string name="settings_magiskhide_summary">隐藏 Magisk 使其不被多种方法检测到</string>
|
||||
<string name="settings_busybox_title">启用 BusyBox</string>
|
||||
<string name="settings_busybox_summary">将 Magisk 内置的 Busybox 挂载到 xbin</string>
|
||||
@@ -142,10 +151,10 @@
|
||||
<string name="request_timeout_summary">%1$s 秒</string>
|
||||
|
||||
<string name="settings_development_category">开发</string>
|
||||
<string name="settings_developer_logging_title">启用高级调试日志记录</string>
|
||||
<string name="settings_developer_logging_title">启用高级日志记录</string>
|
||||
<string name="settings_developer_logging_summary">勾选此项以启用更加详细的日志记录。</string>
|
||||
<string name="settings_shell_logging_title">启用 shell 命令调试日志记录</string>
|
||||
<string name="settings_shell_logging_summary">勾选此项以启用对所有 shell 命令及输出的日志记录</string>
|
||||
<string name="settings_shell_logging_title">启用 shell 命令日志记录</string>
|
||||
<string name="settings_shell_logging_summary">勾选此项以启用对所有 shell 命令及输出进行日志记录</string>
|
||||
|
||||
<!--Superuser-->
|
||||
<string name="su_request_title">超级用户请求</string>
|
||||
@@ -153,7 +162,7 @@
|
||||
<string name="deny">拒绝</string>
|
||||
<string name="prompt">提示</string>
|
||||
<string name="grant">允许</string>
|
||||
<string name="su_warning">将授予对你设备的完全访问权限。\n如果你不确定,请拒绝!</string>
|
||||
<string name="su_warning">将授予对你设备的最高权限。\n如果你不确定,请拒绝!</string>
|
||||
<string name="forever">永久</string>
|
||||
<string name="once">一次</string>
|
||||
<string name="tenmin">10 分钟</string>
|
||||
@@ -165,11 +174,11 @@
|
||||
<string name="no_apps_found">未找到应用</string>
|
||||
<string name="su_snack_grant">已授予 %1$s 超级用户权限</string>
|
||||
<string name="su_snack_deny">已拒绝 %1$s 超级用户权限</string>
|
||||
<string name="su_snack_notif_on">%1$s 的通知已启用</string>
|
||||
<string name="su_snack_notif_off">%1$s 的通知已禁用</string>
|
||||
<string name="su_snack_log_on">对 %1$s 的日志记录已启用</string>
|
||||
<string name="su_snack_log_off">对 %1$s 的日志记录已禁用</string>
|
||||
<string name="su_snack_revoke">%1$s 的权限已撤销</string>
|
||||
<string name="su_snack_notif_on">已启用 %1$s 的通知</string>
|
||||
<string name="su_snack_notif_off">已禁用 %1$s 的通知</string>
|
||||
<string name="su_snack_log_on">已启用对 %1$s 的日志记录</string>
|
||||
<string name="su_snack_log_off">已禁用对 %1$s 的日志记录</string>
|
||||
<string name="su_snack_revoke">已撤销 %1$s 的权限</string>
|
||||
<string name="su_revoke_title">撤销</string>
|
||||
<string name="su_revoke_msg">确认撤销 %1$s 的权限?</string>
|
||||
<string name="toast">消息提示</string>
|
||||
@@ -179,7 +188,5 @@
|
||||
<string name="pid">PID:\u0020</string>
|
||||
<string name="target_uid">目标 UID:\u0020</string>
|
||||
<string name="command">命令:\u0020</string>
|
||||
<string name="settings_disable_title">禁用魔法挂载(Magic Mount)</string>
|
||||
<string name="settings_disable_summary">禁用魔法挂载,所有模块将不会生效</string>
|
||||
|
||||
</resources>
|
||||
|
195
app/src/main/res/values-zh-rTW/strings.xml
Normal file
@@ -0,0 +1,195 @@
|
||||
<resources>
|
||||
<!--Universal-->
|
||||
|
||||
<!--Welcome Activity-->
|
||||
<string name="navigation_drawer_open">Open navigation drawer</string>
|
||||
<string name="navigation_drawer_close">Close navigation drawer</string>
|
||||
<string name="modules">模組</string>
|
||||
<string name="downloads">下載</string>
|
||||
<string name="superuser">超級用戶</string>
|
||||
<string name="log">日誌</string>
|
||||
<string name="settings">設置</string>
|
||||
<string name="status">狀態</string>
|
||||
<string name="install">安裝</string>
|
||||
|
||||
<!--Status Fragment-->
|
||||
<string name="magisk_version">已安裝 Magisk v%1$s</string>
|
||||
<string name="magisk_version_disable">已禁用 Magisk v%1$s</string>
|
||||
<string name="magisk_version_error">未安裝 Magisk</string>
|
||||
|
||||
<string name="checking_for_updates">正在檢查更新…</string>
|
||||
<string name="magisk_update_available">Magisk 可更新到 v%1$.1f!</string>
|
||||
<string name="cannot_check_updates">無法檢查更新,沒有網絡連線?</string>
|
||||
<string name="up_to_date">已安裝最新版本的 %1$s</string>
|
||||
<string name="root_error">已 ROOT 但沒有 ROOT 權限,未授予權限?</string>
|
||||
<string name="not_rooted">未 ROOT</string>
|
||||
<string name="proper_root">已正確 ROOT</string>
|
||||
<string name="safetyNet_check_text">點擊啟動 SafetyNet 檢查</string>
|
||||
<string name="checking_safetyNet_status">正在檢查 SafetyNet 狀態…</string>
|
||||
<string name="safetyNet_connection_failed">無法連接至 Google API</string>
|
||||
<string name="safetyNet_connection_suspended">與 Google API 的連接已暫停</string>
|
||||
<string name="safetyNet_error">無法檢查 SafetyNet,沒有網絡連線?</string>
|
||||
<string name="safetyNet_fail">SafetyNet 失敗:CTS 配置文件不匹配</string>
|
||||
<string name="safetyNet_pass">SafetyNet 已通過</string>
|
||||
<string name="root_info_warning">功能嚴重受限</string>
|
||||
|
||||
<!--Install Fragment-->
|
||||
<string name="auto_detect">(自動) %1$s</string>
|
||||
<string name="boot_image_title">Boot 映像位置</string>
|
||||
<string name="detect_button">偵測</string>
|
||||
<string name="advanced_settings_title">高級設置</string>
|
||||
<string name="keep_force_encryption">保持強制加密</string>
|
||||
<string name="keep_dm_verity">保留 dm-verity</string>
|
||||
<string name="current_magisk_title">已安裝 Magisk 版本:%1$s</string>
|
||||
<string name="install_magisk_title">最新的 Magisk 版本:%1$s</string>
|
||||
<string name="uninstall">解除安裝</string>
|
||||
<string name="reboot_countdown">將在 %1$d 後重啟</string>
|
||||
|
||||
<!--Module Fragment-->
|
||||
<string name="no_info_provided">(未提供資訊)</string>
|
||||
<string name="no_modules_found">未找到模組</string>
|
||||
<string name="update_file_created">模組將在下次重啟後更新</string>
|
||||
<string name="remove_file_created">模組將在下次重啟後移除</string>
|
||||
<string name="remove_file_deleted">模組將不會在下次重啟後移除</string>
|
||||
<string name="disable_file_created">模組將在下次重啟後禁用</string>
|
||||
<string name="disable_file_removed">模組將在下次重啟後啟用</string>
|
||||
<string name="author">作者:%1$s</string>
|
||||
<string name="fab_flash_zip">刷入模組 Zip</string>
|
||||
|
||||
<!--Repo Fragment-->
|
||||
<string name="update_available">可更新</string>
|
||||
<string name="installed">已安裝</string>
|
||||
<string name="not_installed">未安裝</string>
|
||||
|
||||
<!--Log Fragment-->
|
||||
<string name="menuSaveToSd">保存到 SD 卡</string>
|
||||
<string name="menuReload">重載</string>
|
||||
<string name="menuClearLog">清除日誌</string>
|
||||
<string name="logs_cleared">日誌已成功清除</string>
|
||||
<string name="log_is_empty">日誌為空</string>
|
||||
<string name="logs_save_failed">無法將日誌寫入 SD 卡:</string>
|
||||
|
||||
<!--About Activity-->
|
||||
<string name="about">關於</string>
|
||||
<string name="app_developers">主要開發者</string>
|
||||
<string name="app_developers_"><![CDATA[此應用由 <a href="https://github.com/topjohnwu">topjohnwu</a> 與 <a href="https://github.com/d8ahazard">Digitalhigh</a> 和 <a href="https://github.com/dvdandroid">Dvdandroid</a> 合作開發。]]></string>
|
||||
<string name="app_changelog">應用更新日誌</string>
|
||||
<string name="translators">topjohnwu</string>
|
||||
<string name="app_version">應用版本</string>
|
||||
<string name="app_source_code">原始碼</string>
|
||||
<string name="donation">捐贈</string>
|
||||
<string name="app_translators">翻譯者</string>
|
||||
<string name="support_thread">討論串</string>
|
||||
|
||||
<!--Toasts, Dialogs-->
|
||||
<string name="permissionNotGranted">未授予寫入外部存儲權限,此功能無法正常工作。</string>
|
||||
<string name="no_thanks">不,謝謝</string>
|
||||
<string name="yes">是</string>
|
||||
<string name="repo_install_title">安裝 %1$s</string>
|
||||
<string name="repo_install_msg">你想要安裝 %1$s 嗎?</string>
|
||||
<string name="download_install">下載並安裝</string>
|
||||
<string name="goto_install">前往“安裝”界面</string>
|
||||
<string name="download_file_error">下載文件時出錯</string>
|
||||
<string name="install_error">安裝出錯!</string>
|
||||
<string name="invalid_zip">此 zip 文件不是 Magisk 模組!!</string>
|
||||
<string name="reboot_title">安裝成功!</string>
|
||||
<string name="reboot_msg">你想要立即重啟嗎?</string>
|
||||
<string name="reboot">重啟</string>
|
||||
<string name="copying_msg">正在復制 zip 到暫存目錄</string>
|
||||
<string name="zip_install_progress_title">正在安裝</string>
|
||||
<string name="zip_unzip_msg">正在解壓 zip 文件 …</string>
|
||||
<string name="zip_process_msg">正在處理 zip 文件 …</string>
|
||||
<string name="zip_install_progress_msg">正在安裝 %1$s …</string>
|
||||
<string name="no_magisk_title">未安裝 Magisk!</string>
|
||||
<string name="no_magisk_msg">你想要下載並安裝 Magisk 嗎?</string>
|
||||
<string name="downloading_toast">正在下載 %1$s</string>
|
||||
<string name="magisk_update_title">Magisk 可更新!</string>
|
||||
<string name="settings_reboot_toast">重啟以完成設定</string>
|
||||
<string name="release_notes">發布說明</string>
|
||||
<string name="repo_cache_cleared">資源庫暫存已清除</string>
|
||||
<string name="safetyNet_hide_notice">此應用使用了 SafetyNet\n已默認由 MagiskHide 處理</string>
|
||||
<string name="start_magiskhide">正在啟動 MagiskHide …</string>
|
||||
<string name="no_magisksu_title">未使用 MagiskSU!</string>
|
||||
<string name="no_magisksu_msg">你未使用 MagiskSU 進行 ROOT,僅使用 MagiskHide 可能並不足夠!\n官方並不支持,你需要額外的工具(如 suhide)才能通過 Safety Net。</string>
|
||||
<string name="understand">我明白</string>
|
||||
|
||||
<!--Settings Activity -->
|
||||
<string name="settings_general_category">常規</string>
|
||||
<string name="settings_dark_theme_title">深色主題</string>
|
||||
<string name="settings_dark_theme_summary">使用深色主題</string>
|
||||
<string name="settings_clear_cache_title">清除資源庫快取</string>
|
||||
<string name="settings_clear_cache_summary">清除已暫存的在線資源庫快取,強制刷新在線數據</string>
|
||||
|
||||
<string name="settings_disable_title">禁用 Magisk</string>
|
||||
<string name="settings_disable_summary">除 ROOT (MagiskSU) 以外,其他都將被禁用</string>
|
||||
<string name="settings_magiskhide_summary">隱藏 Magisk 使其不被多種方法檢測到</string>
|
||||
<string name="settings_busybox_title">啟用 BusyBox</string>
|
||||
<string name="settings_busybox_summary">將 Magisk 內置的 Busybox 掛載到 xbin</string>
|
||||
<string name="settings_hosts_title">Systemless hosts</string>
|
||||
<string name="settings_hosts_summary">為廣告屏蔽應用提供 Systemless hosts 支持</string>
|
||||
|
||||
<string name="settings_su_app_adb">應用和 ADB</string>
|
||||
<string name="settings_su_app">僅應用</string>
|
||||
<string name="settings_su_adb">僅 ADB</string>
|
||||
<string name="settings_su_disable">已禁用</string>
|
||||
<string name="settings_su_request_10">10 秒</string>
|
||||
<string name="settings_su_request_20">20 秒</string>
|
||||
<string name="settings_su_request_30">30 秒</string>
|
||||
<string name="settings_su_request_60">60 秒</string>
|
||||
<string name="superuser_access">超級用戶訪問權限</string>
|
||||
<string name="auto_response">自動回應</string>
|
||||
<string name="request_timeout">請求逾時</string>
|
||||
<string name="superuser_notification">超級用戶通知</string>
|
||||
<string name="request_timeout_summary">%1$s 秒</string>
|
||||
|
||||
<string name="settings_development_category">開發</string>
|
||||
<string name="settings_developer_logging_title">啟用進階日誌記錄</string>
|
||||
<string name="settings_developer_logging_summary">勾選此項以啟用更加詳細的日誌記錄。</string>
|
||||
<string name="settings_shell_logging_title">啟用 shell 命令日誌記錄</string>
|
||||
<string name="settings_shell_logging_summary">勾選此項以啟用對所有 shell 命令及輸出進行日誌記錄</string>
|
||||
|
||||
<!--Superuser-->
|
||||
<string name="su_request_title">超級用戶請求</string>
|
||||
<string name="deny_with_str">拒絕 %1$s</string>
|
||||
<string name="deny">拒絕</string>
|
||||
<string name="prompt">提示</string>
|
||||
<string name="grant">允許</string>
|
||||
<string name="su_warning">將授予對你設備的最高權限。\n如果你不確定,請拒絕!</string>
|
||||
<string name="forever">永久</string>
|
||||
<string name="once">一次</string>
|
||||
<string name="tenmin">10 分鐘</string>
|
||||
<string name="twentymin">20 分鐘</string>
|
||||
<string name="thirtymin">30 分鐘</string>
|
||||
<string name="sixtymin">60 分鐘</string>
|
||||
<string name="su_allow_toast">%1$s 已被授予超級用戶權限</string>
|
||||
<string name="su_deny_toast">%1$s 已被拒絕超級用戶權限</string>
|
||||
<string name="no_apps_found">未找到應用</string>
|
||||
<string name="su_snack_grant">已授予 %1$s 超級用戶權限</string>
|
||||
<string name="su_snack_deny">已拒絕 %1$s 超級用戶權限</string>
|
||||
<string name="su_snack_notif_on">已啟用 %1$s 的通知</string>
|
||||
<string name="su_snack_notif_off">已禁用 %1$s 的通知</string>
|
||||
<string name="su_snack_log_on">已啟用對 %1$s 的日誌記錄</string>
|
||||
<string name="su_snack_log_off">已禁用對 %1$s 的日誌記錄</string>
|
||||
<string name="su_snack_revoke">已撤銷 %1$s 的權限</string>
|
||||
<string name="su_revoke_title">撤銷</string>
|
||||
<string name="su_revoke_msg">確認撤銷 %1$s 的權限?</string>
|
||||
<string name="toast">消息提示</string>
|
||||
<string name="none">無</string>
|
||||
|
||||
<!--Superuser logs-->
|
||||
<string name="pid">PID:\u0020</string>
|
||||
<string name="target_uid">目標 UID:\u0020</string>
|
||||
<string name="command">"指令: "</string>
|
||||
<string name="close">關閉</string>
|
||||
<string name="internal_storage">Zip 已被儲存到:\n[內部儲存空間]%1$s</string>
|
||||
<string name="ok">好</string>
|
||||
<string name="process_error">處裡失敗</string>
|
||||
<string name="download">下載</string>
|
||||
<string name="zip_process_title">處理中</string>
|
||||
<string name="uninstall_magisk_title">解除安裝 Magisk</string>
|
||||
<string name="uninstall_magisk_msg">這將會刪除所有模組,MagiskSU,並有可能在目前資料未加密的情況下進行加密\n你確定要繼續嗎?</string>
|
||||
<string name="version_none">(無)</string>
|
||||
<string name="manual_boot_image">請手動選取 boot 映像位置</string>
|
||||
<string name="cannot_auto_detect">(無法自動偵測)</string>
|
||||
|
||||
</resources>
|
@@ -1,6 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ Copyright 2016 dvdandroid
|
||||
~ Copyright 2017 topjohnwu
|
||||
~ Copyright 2017 dvdandroid
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
@@ -28,4 +29,6 @@
|
||||
<attr name="ColorNeutral" format="reference"/>
|
||||
</declare-styleable>
|
||||
|
||||
<attr name="colorAccentFallback" format="reference" />
|
||||
|
||||
</resources>
|
@@ -4,6 +4,7 @@
|
||||
<color name="primary">#009688</color>
|
||||
<color name="primary_dark">#00796B</color>
|
||||
<color name="accent">#FFC107</color>
|
||||
<color name="accentFallback">#FBC02D</color>
|
||||
<color name="icon_grey">#757575</color>
|
||||
|
||||
<color name="red500">#F44336</color>
|
||||
@@ -12,13 +13,7 @@
|
||||
<color name="blue500">#2196F3</color>
|
||||
<color name="yellow500">#FFC107</color>
|
||||
|
||||
<color name="dh_primary">#3F51B5</color>
|
||||
<color name="dh_primary_dark">#303F9F</color>
|
||||
<color name="dh_accent">#448AFF</color>
|
||||
|
||||
<color name="dh_primary_text">#dedede</color>
|
||||
<color name="dh_icons">#dedede</color>
|
||||
<color name="dh_divider">#313131</color>
|
||||
<color name="dark_secondary_text">#dedede</color>
|
||||
|
||||
<color name="su_request_background">#e0e0e0</color>
|
||||
|
||||
|
@@ -10,6 +10,5 @@
|
||||
<dimen name="card_layout_padding">10dp</dimen>
|
||||
<dimen name="card_textview_width">300dip</dimen>
|
||||
<dimen name="checkbox_padding">3dp</dimen>
|
||||
<dimen name="card_imageview_margin">10dp</dimen>
|
||||
<dimen name="card_appicon_size">50dp</dimen>
|
||||
</resources>
|
@@ -6,7 +6,7 @@
|
||||
<!--Welcome Activity-->
|
||||
<string name="navigation_drawer_open">Open navigation drawer</string>
|
||||
<string name="navigation_drawer_close">Close navigation drawer</string>
|
||||
<string name="magiskhide">Magisk Hide</string>
|
||||
<string name="magiskhide" translatable="false">Magisk Hide</string>
|
||||
<string name="modules">Modules</string>
|
||||
<string name="downloads">Downloads</string>
|
||||
<string name="superuser">Superuser</string>
|
||||
@@ -37,17 +37,20 @@
|
||||
<string name="root_info_warning">Functionality greatly limited</string>
|
||||
|
||||
<!--Install Fragment-->
|
||||
<string name="auto_detect">"(Auto) %1$s"</string>
|
||||
<string name="auto_detect">(Auto) %1$s</string>
|
||||
<string name="cannot_auto_detect">(Cannot auto detect)</string>
|
||||
<string name="boot_image_title">Boot Image Location</string>
|
||||
<string name="detect_button">Detect</string>
|
||||
<string name="advanced_settings_title">Advanced Settings</string>
|
||||
<string name="keep_force_encryption">Keep force encryption</string>
|
||||
<string name="keep_dm_verity">Keep dm-verity</string>
|
||||
<string name="current_magisk_title">Installed Magisk Version: v%1$s</string>
|
||||
<string name="install_magisk_title">Latest Magisk Version: v%1$.1f</string>
|
||||
<string name="magiskify" translatable="false">Magiskify</string>
|
||||
<string name="current_magisk_title">Installed Magisk Version: %1$s</string>
|
||||
<string name="install_magisk_title">Latest Magisk Version: %1$s</string>
|
||||
<string name="uninstall">Uninstall</string>
|
||||
<string name="reboot_countdown">Rebooting in %1$d</string>
|
||||
<string name="uninstall_magisk_title">Uninstall Magisk</string>
|
||||
<string name="uninstall_magisk_msg">This will remove all modules, MagiskSU, and potentially encrypt your data if not encrypted\nAre you sure to continue?</string>
|
||||
<string name="version_none">(None)</string>
|
||||
|
||||
<!--Module Fragment-->
|
||||
<string name="no_info_provided">(No info provided)</string>
|
||||
@@ -64,7 +67,6 @@
|
||||
<string name="update_available">Update Available</string>
|
||||
<string name="installed">Installed</string>
|
||||
<string name="not_installed">Not Installed</string>
|
||||
<string name="changelog">Changelog</string>
|
||||
|
||||
<!--Log Fragment-->
|
||||
<string name="menuSaveToSd">Save to SD</string>
|
||||
@@ -90,41 +92,40 @@
|
||||
<string name="permissionNotGranted">This feature will not work without permission to write external storage.</string>
|
||||
<string name="no_thanks">No thanks</string>
|
||||
<string name="yes">Yes</string>
|
||||
<string name="ok">OK</string>
|
||||
<string name="close">Close</string>
|
||||
<string name="repo_install_title">Install %1$s</string>
|
||||
<string name="repo_install_msg">Do you want to install %1$s ?</string>
|
||||
<string name="download_install">Download & install</string>
|
||||
<string name="download_install">Download & Install</string>
|
||||
<string name="download">Download</string>
|
||||
<string name="goto_install">Go to \"Install\" section</string>
|
||||
<string name="download_file_error">Error downloading file</string>
|
||||
<string name="install_error">Installation error!</string>
|
||||
<string name="manual_install_1">Zip file placed in %1$s</string>
|
||||
<string name="manual_install_2">Flash it in recovery manually</string>
|
||||
<string name="invalid_zip">The zip is not a Magisk Module!!</string>
|
||||
<string name="reboot_title">Installation succeeded!</string>
|
||||
<string name="reboot_msg">Do you want to reboot now?</string>
|
||||
<string name="reboot">Reboot</string>
|
||||
<string name="copying_msg">Copying zip to temp directory</string>
|
||||
<string name="zip_install_progress_title">Installing</string>
|
||||
<string name="zip_install_unzip_zip_msg">Unzipping zip file …</string>
|
||||
<string name="zip_install_process_zip_msg">Processing zip file …</string>
|
||||
<string name="zip_install_progress_msg">"Installing %1$s …"</string>
|
||||
<string name="zip_unzip_msg">Unzipping zip file …</string>
|
||||
<string name="zip_process_msg">Processing zip file …</string>
|
||||
<string name="zip_install_progress_msg">Installing %1$s …</string>
|
||||
<string name="no_magisk_title">No Magisk Installed!</string>
|
||||
<string name="no_magisk_msg">Do you want to download and install Magisk?</string>
|
||||
<string name="downloading_toast">Downloading %1$s</string>
|
||||
<string name="magisk_update_title">New Magisk Update Available!</string>
|
||||
<string name="magisk_update_message">Magisk v%1$.1f update is live, do you want to install?</string>
|
||||
<string name="settings_reboot_toast">Reboot to apply settings</string>
|
||||
<string name="check_release_notes">Check release notes</string>
|
||||
<string name="release_notes">Release notes</string>
|
||||
<string name="repo_cache_cleared">Repo cache cleared</string>
|
||||
<string name="safetyNet_hide_notice">This app uses SafetyNet\nAlready handled by MagiskHide by default</string>
|
||||
<string name="start_magiskhide">Starting MagiskHide …</string>
|
||||
<string name="no_magisksu_title">Not using MagiskSU!</string>
|
||||
<string name="no_magisksu_msg">You are not rooted with MagiskSU, using MagiskHide itself might not be enough!\nIt\'s not officially supported, and you would need additional tools (e.g suhide) to pass Safety Net.</string>
|
||||
<string name="understand">I understand</string>
|
||||
|
||||
<!--URL Templates-->
|
||||
<string name="url_main" translatable="false">https://api.github.com/orgs/Magisk-Modules-Repo/repos</string>
|
||||
<string name="file_url" translatable="false">https://raw.githubusercontent.com/Magisk-Modules-Repo/%1$s/master/%2$s</string>
|
||||
<string name="zip_url" translatable="false">https://github.com/Magisk-Modules-Repo/%1$s/archive/master.zip</string>
|
||||
<string name="process_error">Process error</string>
|
||||
<string name="internal_storage">The zip is stored in:\n[Internal Storage]%1$s</string>
|
||||
<string name="zip_process_title">Processing</string>
|
||||
<string name="manual_boot_image">Please manually select a boot image!</string>
|
||||
|
||||
<!--Settings Activity -->
|
||||
<string name="settings_general_category">General</string>
|
||||
|
@@ -5,6 +5,7 @@
|
||||
<item name="colorPrimary">@color/primary</item>
|
||||
<item name="colorPrimaryDark">@color/primary_dark</item>
|
||||
<item name="colorAccent">@color/accent</item>
|
||||
<item name="colorAccentFallback">@color/accentFallback</item>
|
||||
<item name="windowActionModeOverlay">true</item>
|
||||
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
|
||||
<item name="cardStyle">@style/CardViewStyle.Light</item>
|
||||
@@ -13,101 +14,22 @@
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.dh" parent="ThemeOverlay.AppCompat.Dark">
|
||||
<item name="colorPrimary">@color/dh_primary</item>
|
||||
<item name="colorPrimaryDark">@color/dh_primary_dark</item>
|
||||
<item name="colorAccent">@color/dh_accent</item>
|
||||
<style name="AppTheme.Dark" parent="Theme.AppCompat.NoActionBar">
|
||||
<item name="colorAccent">@color/accent</item>
|
||||
<item name="colorAccentFallback">@color/accentFallback</item>
|
||||
<item name="windowActionModeOverlay">true</item>
|
||||
<item name="android:windowDrawsSystemBarBackgrounds">true</item>
|
||||
<item name="cardBackgroundColor">@color/dh_accent</item>
|
||||
<item name="cardStyle">@style/CardViewStyle.Dark</item>
|
||||
<item name="windowActionBar">false</item>
|
||||
<item name="windowNoTitle">true</item>
|
||||
<item name="cardStyle">@style/CardViewStyle.Dark</item>
|
||||
<item name="android:textColorPrimary">@color/dh_primary_text</item>
|
||||
<item name="android:textColorSecondary">@color/dh_primary_text</item>
|
||||
<item name="android:alertDialogTheme">@style/AlertDialog.dh</item>
|
||||
<item name="android:dialogTheme">@style/AlertDialog.dh</item>
|
||||
<item name="android:actionOverflowButtonStyle">@style/OverFlow</item>
|
||||
<item name="colorControlNormal">@color/dh_primary_text</item>
|
||||
<item name="colorControlActivated">@color/dh_accent</item>
|
||||
<item name="colorControlHighlight">@color/dh_icons</item>
|
||||
</style>
|
||||
|
||||
<style name="OverFlow" parent="@android:style/Widget.ActionBar">
|
||||
<item name="android:backgroundTint">@color/dh_icons</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.Toolbar.dh" parent="ThemeOverlay.AppCompat.Dark">
|
||||
<item name="colorPrimary">@color/dh_primary</item>
|
||||
<item name="android:elevation">4dp</item>
|
||||
<item name="android:textColorPrimary">@color/dh_primary_text</item>
|
||||
<item name="android:textColorSecondary">@color/dh_primary_text</item> <!-- force -->
|
||||
<item name="actionMenuTextColor">@color/dh_icons</item>
|
||||
<item name="android:statusBarColor">@android:color/transparent</item>
|
||||
<item name="android:textColorSecondary">@color/dark_secondary_text</item>
|
||||
</style>
|
||||
|
||||
<style name="CardViewStyle.Dark" parent="CardView">
|
||||
<item name="cardBackgroundColor">@android:color/background_dark</item>
|
||||
</style>
|
||||
|
||||
<style name="AlertDialog.dh" parent="ThemeOverlay.AppCompat.Dark">
|
||||
<item name="android:textColor">@color/dh_primary_text</item>
|
||||
<item name="android:textColorSecondary">@color/dh_primary_text</item>
|
||||
<item name="colorAccent">@color/dh_accent</item>
|
||||
<item name="android:textColorPrimary">@color/dh_primary_text</item>
|
||||
<item name="android:background">@color/dh_divider</item>
|
||||
<item name="android:textColorAlertDialogListItem">@color/dh_primary_text</item>
|
||||
<item name="android:windowContentOverlay">@null</item>
|
||||
<item name="android:windowIsFloating">true</item>
|
||||
<item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
|
||||
<item name="android:windowMinWidthMajor">@android:dimen/dialog_min_width_major</item>
|
||||
<item name="android:windowTitleStyle">@style/MyTitleTextStyle</item>
|
||||
<item name="android:windowMinWidthMinor">@android:dimen/dialog_min_width_minor</item>
|
||||
<item name="android:alertDialogStyle">@style/ListPrefAlertDialogStyle</item>
|
||||
<item name="android:windowBackground">@android:color/transparent</item>
|
||||
<item name="android:textAppearanceMedium">@style/MyAlertTextAppearance</item>
|
||||
|
||||
</style>
|
||||
|
||||
<style name="MyAlertTextAppearance">
|
||||
<!-- Set text size and color of title and message here -->
|
||||
<item name="android:textColor">@color/dh_primary_text</item>
|
||||
</style>
|
||||
|
||||
<style name="ListPrefAlertDialogStyle">
|
||||
<item name="android:fullDark" >@android:color/transparent</item>
|
||||
<item name="android:topDark" >@android:color/transparent</item>
|
||||
<item name="android:centerDark" >@android:color/transparent</item>
|
||||
<item name="android:bottomDark" >@android:color/transparent</item>
|
||||
<item name="android:fullBright" >@android:color/transparent</item>
|
||||
<item name="android:topBright" >@android:color/transparent</item>
|
||||
<item name="android:centerBright">@android:color/transparent</item>
|
||||
<item name="android:bottomBright">@android:color/transparent</item>
|
||||
<item name="android:bottomMedium">@android:color/transparent</item>
|
||||
<item name="android:centerMedium">@android:color/transparent</item>
|
||||
</style>
|
||||
|
||||
<style name="ListViewStyle.dh" parent="android:Widget.Material.PopupMenu">
|
||||
<item name="android:divider">@color/dh_divider</item>
|
||||
<item name="android:dividerHeight">1dp</item>
|
||||
<item name="android:listSelector">@android:color/background_dark</item>
|
||||
<item name="android:drawSelectorOnTop">true</item>
|
||||
<item name="android:cacheColorHint">@color/dh_divider</item>
|
||||
<item name="android:foregroundTint">@color/dh_primary_text</item>
|
||||
</style>
|
||||
|
||||
<style name="TransparentListView" parent="@android:style/Widget.ListView">
|
||||
<item name="android:cacheColorHint">@android:color/transparent</item>
|
||||
<item name="android:background">#FFFF00</item>
|
||||
<item name="android:listSelector">@android:color/transparent</item>
|
||||
<item name="android:divider">#F78181</item>
|
||||
<item name="android:dividerHeight">3dp</item>
|
||||
</style>
|
||||
|
||||
<style name="MyTitleTextStyle">
|
||||
<item name="android:textColor">@color/dh_primary_text</item>
|
||||
<item name="android:textAppearance">@style/TextAppearance.AppCompat.Title</item>
|
||||
</style>
|
||||
|
||||
<style name="CardViewStyle.Light" parent="CardView">
|
||||
<item name="cardBackgroundColor">@android:color/background_light</item>
|
||||
</style>
|
||||
|
@@ -6,7 +6,7 @@ buildscript {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:2.3.0-beta3'
|
||||
classpath 'com.android.tools.build:gradle:2.3.0-beta4'
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
|