Markdown 基本语法

一级标题

二级标题

三级标题

四级标题

五级标题
六级标题

粗体 斜体 粗斜体 正常 删除线

单行块引用

多个段落的块引用

多个段落的块引用

嵌套块引用

嵌套块引用

带有其它元素的块引用

带有其它元素的块引用

带有其它元素的块引用

带有其它元素的块引用

  1. 有序列表
  2. 有序列表
  3. 有序列表
    1. 有序列表
    2. 有序列表
  4. 有序列表
  • 无序列表
  • 无序列表
  • 无序列表
    • 无序列表
    • 无序列表
  • 无序列表

单行代码块 with Both Chines and English Text

分割线


链接

链接(带有标题)

https://www.lynx3.top/

email@example.com

带格式化的链接

图片

内容类 HTML 嵌入

Markdown 扩展语法

表格

左对齐居中对齐右对齐
/叫做斜线
|叫做管道符

代码框

编译语言

C++

来源:https://raw.githubusercontent.com/argvchs/fastio/master/fastio.hpp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstddef>
#include <cstdio>
#include <cstring>
#include <string>
#include <string_view>
#include <type_traits>
namespace fastio {
namespace symbols {
enum symbol {
endl,
ends,
flush,
bin,
oct,
dec,
hex,
left,
right,
boolalpha,
noboolalpha,
showbase,
noshowbase,
showpoint,
noshowpoint,
showpos,
noshowpos,
ws,
uppercase,
lowercase,
fixed,
defaultfloat,
reset
};
struct setbase {
int base;
setbase(int n) : base(n) {}
};
struct setfill {
char fill;
setfill(char c) : fill(c) {}
};
struct setprecision {
int precision;
setprecision(int n) : precision(n) {}
};
struct setw {
int width;
setw(int n) : width(n) {}
};
} // namespace symbols
namespace interface {
using i64 = long long;
using u64 = unsigned long long;
using i128 = __int128;
using u128 = unsigned __int128;
template <typename T>
constexpr bool is_signed_v =
(std::is_integral_v<T> && std::is_signed_v<T>) || std::is_same_v<T, i128>;
template <typename T>
constexpr bool is_unsigned_v =
(std::is_integral_v<T> && std::is_unsigned_v<T>) || std::is_same_v<T, u128>;
template <typename T> constexpr bool is_integral_v = is_signed_v<T> || is_unsigned_v<T>;
template <typename T> constexpr bool is_floating_v = std::is_floating_point_v<T>;
template <typename T> struct make_unsigned : public std::make_unsigned<T> {};
template <> struct make_unsigned<i128> : public std::type_identity<u128> {};
template <> struct make_unsigned<u128> : public std::type_identity<u128> {};
template <typename T> using make_unsigned_t = typename make_unsigned<T>::type;
struct noncopyable {
noncopyable() = default;
virtual ~noncopyable() = default;
noncopyable(const noncopyable &) = delete;
noncopyable &operator=(const noncopyable &) = delete;
};
class istream : public noncopyable {
private:
int base = 10;
bool unget = false, eof = false, fail = false;
char chr = '\0';
static bool isssign(char c) { return isspace(c) || c == '+' || c == '-'; }
int todigit(char c) {
if (::isdigit(c)) return c - '0';
if (isupper(c)) return c - 'A' + 10;
if (islower(c)) return c - 'a' + 10;
return base;
}
bool isdigit(char c) { return todigit(c) < base; }
virtual char vget() = 0;
public:
char get() {
if (!unget)
if ((chr = vget()) == EOF) eof = true;
unget = false;
return chr;
}
istream &get(char &c) {
c = get();
return *this;
}
explicit operator bool() { return !fail; }
bool operator!() { return fail; }
template <typename T, std::enable_if_t<is_integral_v<T>, int> = 0>
istream &operator>>(T &n) {
n = 0;
bool f = false;
while (isssign(get()) && !eof)
if (chr == '-' && is_integral_v<T>) f = !f;
if (eof) return fail = true, *this;
unget = true;
while (isdigit(get())) n = n * base + todigit(chr);
if (f) n = -n;
unget = true;
return *this;
}
template <typename T, std::enable_if_t<is_floating_v<T>, int> = 0>
istream &operator>>(T &n) {
n = 0;
bool f = false;
while (isssign(get()) && !eof)
if (chr == '-') f = !f;
if (eof) return fail = true, *this;
unget = true;
while (isdigit(get())) n = n * base + todigit(chr);
if (chr == '.') {
i64 pow = 1;
while (isdigit(get())) n += todigit(chr) / (T)(pow *= base);
}
if (f) n = -n;
unget = true;
return *this;
}
istream &operator>>(char &c) {
c = '\0';
while (isspace(get()) && !eof);
if (eof) return fail = true, *this;
c = chr;
return *this;
}
istream &operator>>(bool &f) {
i64 n;
*this >> n;
f = (bool)n;
return *this;
}
istream &operator>>(char *s) {
s[0] = '\0';
int len = 0;
while (isspace(get()) && !eof);
if (eof) return fail = true, *this;
unget = true;
while (isgraph(get())) s[len++] = chr;
unget = true, s[len] = '\0';
return *this;
}
istream &operator>>(std::string &s) {
s.clear();
while (isspace(get()) && !eof);
if (eof) return fail = true, *this;
unget = true;
while (isgraph(get())) s.push_back(chr);
unget = true;
return *this;
}
istream &operator>>(symbols::symbol a) {
switch (a) {
case symbols::bin: base = 2; break;
case symbols::oct: base = 8; break;
case symbols::dec: base = 10; break;
case symbols::hex: base = 16; break;
case symbols::ws:
while (isspace(get()) && !eof);
unget = true;
break;
default: base = 10;
}
return *this;
}
istream &operator>>(symbols::setbase a) {
base = std::max(std::min(a.base, 36), 2);
return *this;
}
istream &ignore(char end = '\n') {
while (get() != end && !eof);
if (eof) return fail = true, *this;
return *this;
}
istream &getline(char *s, char end = '\n') {
s[0] = '\0';
int len = 0;
if (eof) return fail = true, *this;
while (get() != end && !eof) s[len++] = chr;
if (s[len - 1] == '\r' && end == '\n') --len;
s[len] = '\0';
return *this;
}
istream &getline(std::string &s, char end = '\n') {
s.clear();
if (eof) return fail = true, *this;
while (get() != end && !eof) s.push_back(chr);
if (s.back() == '\r' && end == '\n') s.pop_back();
return *this;
}
istream &get(char *s, char end = '\n') {
getline(s, end);
unget = true;
return *this;
}
istream &get(std::string &s, char end = '\n') {
getline(s, end);
unget = true;
return *this;
}
};
class ostream : public noncopyable {
private:
int base = 10, precision = 6, width = 0;
i64 eps = 1e6;
bool adjust = true, boolalpha = false, showbase = false, showpoint = false,
showpos = false, kase = false, fixed = false;
char setfill = ' ';
static i64 qpow(i64 n, int m) {
i64 ret = 1;
for (int i = m; i; i >>= 1, n *= n)
if (i & 1) ret *= n;
return ret;
}
void fill(int n) {
if (width > n) vfill(setfill, width - n);
width = 0;
}
char toalpha(int n) {
if (n < 10) return n + '0';
return n - 10 + (kase ? 'A' : 'a');
}
virtual void vput(char) = 0;
virtual void vputs(const char *, int) = 0;
virtual void vfill(char, int) = 0;
virtual void vflush() = 0;
public:
void put(char c) { vput(c); }
template <typename T, std::enable_if_t<is_integral_v<T>, int> = 0>
ostream &operator<<(T n) {
static char buf[105];
char *p = buf + 100, *q = buf + 100;
bool f = n < 0;
if (f) n = -n;
make_unsigned_t<T> m = n;
if (!m) *p-- = '0';
while (m) *p-- = toalpha(m % base), m /= base;
if (showbase) switch (base) {
case 2: *p-- = kase ? 'B' : 'b', *p-- = '0'; break;
case 8: *p-- = '0'; break;
case 16: *p-- = kase ? 'X' : 'x', *p-- = '0'; break;
}
if (!f) {
if (showpos && is_signed_v<T>) *p-- = '+';
} else *p-- = '-';
if (adjust) fill(q - p);
vputs(p + 1, q - p);
if (!adjust) fill(q - p);
return *this;
}
template <typename T, std::enable_if_t<is_floating_v<T>, int> = 0>
ostream &operator<<(T n) {
static char buf1[105], buf2[105];
if (std::isinf(n)) {
if (n > 0) {
if (showpos) *this << (kase ? "+INF" : "+inf");
else *this << (kase ? "INF" : "inf");
} else *this << (kase ? "-INF" : "-inf");
return *this;
}
if (std::isnan(n)) return *this << (kase ? "NAN" : "nan");
char *p1 = buf1 + 100, *q1 = buf1 + 100, *p2 = buf2 + 100, *q2 = buf2 + 100;
bool f = n < 0;
if (f) n = -n;
i64 m1 = std::floor(n), m2 = std::round((n - m1) * eps);
int len = precision;
if (m2 >= eps) ++m1, m2 = 0;
if (!m1) *p1-- = '0';
while (m1) *p1-- = toalpha(m1 % base), m1 /= base;
while (len--) *p2-- = toalpha(m2 % base), m2 /= base;
if (showbase) switch (base) {
case 2: *p1-- = kase ? 'B' : 'b', *p1-- = '0'; break;
case 8: *p1-- = '0'; break;
case 16: *p1-- = kase ? 'X' : 'x', *p1-- = '0'; break;
}
if (!f) {
if (showpos) *p1-- = '+';
} else *p1-- = '-';
if (!fixed)
while (*q2 == '0' && p2 != q2) --q2;
if (showpoint || p2 != q2) *p2-- = '.';
if (adjust) fill((q1 - p1) + (q2 - p2));
vputs(p1 + 1, q1 - p1);
vputs(p2 + 1, q2 - p2);
if (!adjust) fill((q1 - p1) + (q2 - p2));
return *this;
}
ostream &operator<<(char c) {
if (adjust) fill(1);
vput(c);
if (!adjust) fill(1);
return *this;
}
ostream &operator<<(const char *s) {
int n = strlen(s);
if (adjust) fill(n);
vputs(s, n);
if (!adjust) fill(n);
return *this;
}
ostream &operator<<(const std::string &s) {
int n = s.size();
if (adjust) fill(n);
vputs(s.data(), n);
if (!adjust) fill(n);
return *this;
}
ostream &operator<<(std::string_view sv) {
int n = sv.size();
if (adjust) fill(n);
vputs(sv.data(), n);
if (!adjust) fill(n);
return *this;
}
ostream &operator<<(bool f) {
if (f) {
if (boolalpha) *this << (kase ? "TRUE" : "true");
else *this << '1';
} else {
if (boolalpha) *this << (kase ? "FALSE" : "false");
else *this << '0';
}
return *this;
}
ostream &operator<<(const void *p) {
int n = base;
bool f = showbase;
base = 16, showbase = true;
*this << (u64)p;
base = n, showbase = f;
return *this;
}
ostream &operator<<(std::nullptr_t) {
return *this << (kase ? "NULLPTR" : "nullptr");
}
ostream &operator<<(symbols::symbol a) {
switch (a) {
case symbols::endl: vput('\n'); break;
case symbols::ends: vput(' '); break;
case symbols::flush: vflush(); break;
case symbols::bin: eps = qpow(base = 2, precision); break;
case symbols::oct: eps = qpow(base = 8, precision); break;
case symbols::dec: eps = qpow(base = 10, precision); break;
case symbols::hex: eps = qpow(base = 16, precision); break;
case symbols::left: adjust = false; break;
case symbols::right: adjust = true; break;
case symbols::boolalpha: boolalpha = true; break;
case symbols::noboolalpha: boolalpha = false; break;
case symbols::showbase: showbase = true; break;
case symbols::noshowbase: showbase = false; break;
case symbols::showpoint: showpoint = true; break;
case symbols::noshowpoint: showpoint = false; break;
case symbols::showpos: showpos = true; break;
case symbols::noshowpos: showpos = false; break;
case symbols::uppercase: kase = true; break;
case symbols::lowercase: kase = false; break;
case symbols::fixed: fixed = true; break;
case symbols::defaultfloat: fixed = false; break;
default:
base = 10, precision = 6, width = 0, eps = 1e6;
adjust = true;
boolalpha = showbase = showpoint = showpos = kase = fixed = false;
setfill = ' ';
}
return *this;
}
ostream &operator<<(symbols::setbase a) {
base = std::max(std::min(a.base, 36), 2);
eps = qpow(base, precision);
return *this;
}
ostream &operator<<(symbols::setfill a) {
setfill = a.fill;
return *this;
}
ostream &operator<<(symbols::setprecision a) {
precision = std::max(a.precision, 0);
eps = qpow(base, precision);
return *this;
}
ostream &operator<<(symbols::setw a) {
width = std::max(a.width, 0);
return *this;
}
};
} // namespace interface
const int SIZ = 0xfffff;
class istream : public interface::istream {
private:
char buf[SIZ], *p = buf, *q = buf;
virtual char vget() final {
if (p == q) {
int len = fread(buf, 1, SIZ, stream);
if (!len) return EOF;
p = buf, q = buf + len;
}
return *p++;
}
protected:
FILE *stream = stdin;
public:
virtual ~istream() { fclose(stream); }
};
class ifstream : public istream {
public:
explicit ifstream(FILE *p) { istream::stream = p; }
explicit ifstream(const char *s) { istream::stream = fopen(s, "r"); }
};
class ostream : public interface::ostream {
private:
char buf[SIZ], *p = buf;
virtual void vput(char c) final {
if (p - buf >= SIZ) vflush();
*p++ = c;
}
virtual void vputs(const char *s, int n) final {
int used = p - buf, len = 0;
while (n - len + used >= SIZ) {
memcpy(buf + used, s + len, SIZ - used);
p = buf + SIZ;
vflush();
len += SIZ - used, used = 0;
}
memcpy(buf + used, s + len, n - len);
p = buf + used + n - len;
}
virtual void vfill(char c, int n) final {
int used = p - buf, len = 0;
while (n - len + used >= SIZ) {
memset(buf + used, c, SIZ - used);
p = buf + SIZ;
vflush();
len += SIZ - used, used = 0;
}
memset(buf + used, c, n - len);
p = buf + used + n - len;
}
virtual void vflush() final {
fwrite(buf, 1, p - buf, stream);
p = buf;
fflush(stream);
}
protected:
FILE *stream = stdout;
public:
virtual ~ostream() {
vflush();
fclose(stream);
}
};
class ofstream : public ostream {
public:
explicit ofstream(FILE *p) { ostream::stream = p; }
explicit ofstream(const char *s) { ostream::stream = fopen(s, "w"); }
};
static istream is;
static ostream os;
}; // namespace fastio

Java

来源:https://raw.githubusercontent.com/tiann/KernelSU/master/manager/app/src/main/java/me/weishu/kernelsu/ui/KsuService.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package me.weishu.kernelsu.ui;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.IBinder;
import android.os.UserHandle;
import android.os.UserManager;
import android.util.Log;
import androidx.annotation.NonNull;
import com.topjohnwu.superuser.ipc.RootService;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import me.weishu.kernelsu.IKsuInterface;
import rikka.parcelablelist.ParcelableListSlice;
/**
* @author weishu
* @date 2023/4/18.
*/
public class KsuService extends RootService {
private static final String TAG = "KsuService";
class Stub extends IKsuInterface.Stub {
@Override
public ParcelableListSlice<PackageInfo> getPackages(int flags) {
List<PackageInfo> list = getInstalledPackagesAll(flags);
Log.i(TAG, "getPackages: " + list.size());
return new ParcelableListSlice<>(list);
}
}
@Override
public IBinder onBind(@NonNull Intent intent) {
return new Stub();
}
List<Integer> getUserIds() {
List<Integer> result = new ArrayList<>();
UserManager um = (UserManager) getSystemService(Context.USER_SERVICE);
List<UserHandle> userProfiles = um.getUserProfiles();
for (UserHandle userProfile : userProfiles) {
int userId = userProfile.hashCode();
result.add(userProfile.hashCode());
}
return result;
}
ArrayList<PackageInfo> getInstalledPackagesAll(int flags) {
ArrayList<PackageInfo> packages = new ArrayList<>();
for (Integer userId : getUserIds()) {
Log.i(TAG, "getInstalledPackagesAll: " + userId);
packages.addAll(getInstalledPackagesAsUser(flags, userId));
}
return packages;
}
List<PackageInfo> getInstalledPackagesAsUser(int flags, int userId) {
try {
PackageManager pm = getPackageManager();
Method getInstalledPackagesAsUser = pm.getClass().getDeclaredMethod("getInstalledPackagesAsUser", int.class, int.class);
return (List<PackageInfo>) getInstalledPackagesAsUser.invoke(pm, flags, userId);
} catch (Throwable e) {
Log.e(TAG, "err", e);
}
return new ArrayList<>();
}
}

Kotlin

来源:https://raw.githubusercontent.com/tiann/KernelSU/master/manager/app/src/main/java/me/weishu/kernelsu/ui/screen/Flash.kt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
package me.weishu.kernelsu.ui.screen
import android.net.Uri
import android.os.Environment
import android.os.Parcelable
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Refresh
import androidx.compose.material.icons.filled.Save
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExtendedFloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.key.Key
import androidx.compose.ui.input.key.key
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.ramcosta.composedestinations.annotation.Destination
import com.ramcosta.composedestinations.navigation.DestinationsNavigator
import com.ramcosta.composedestinations.navigation.EmptyDestinationsNavigator
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import kotlinx.parcelize.Parcelize
import me.weishu.kernelsu.R
import me.weishu.kernelsu.ui.component.KeyEventBlocker
import me.weishu.kernelsu.ui.util.LkmSelection
import me.weishu.kernelsu.ui.util.LocalSnackbarHost
import me.weishu.kernelsu.ui.util.installBoot
import me.weishu.kernelsu.ui.util.flashModule
import me.weishu.kernelsu.ui.util.reboot
import me.weishu.kernelsu.ui.util.restoreBoot
import me.weishu.kernelsu.ui.util.uninstallPermanently
import java.io.File
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
enum class FlashingStatus {
FLASHING,
SUCCESS,
FAILED
}
/**
* @author weishu
* @date 2023/1/1.
*/
@OptIn(ExperimentalComposeUiApi::class)
@Composable
@Destination
fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
var text by rememberSaveable { mutableStateOf("") }
val logContent = rememberSaveable { StringBuilder() }
var showFloatAction by rememberSaveable { mutableStateOf(false) }
val snackBarHost = LocalSnackbarHost.current
val scope = rememberCoroutineScope()
val scrollState = rememberScrollState()
var flashing by rememberSaveable {
mutableStateOf(FlashingStatus.FLASHING)
}
LaunchedEffect(Unit) {
if (text.isNotEmpty()) {
return@LaunchedEffect
}
withContext(Dispatchers.IO) {
flashIt(flashIt, onFinish = { showReboot, code ->
if (code != 0) {
text += "Error: exit code = $code.\nPlease save and check the log.\n"
}
if (showReboot) {
text += "\n\n\n"
showFloatAction = true
}
flashing = if (code == 0) FlashingStatus.SUCCESS else FlashingStatus.FAILED
}, onStdout = {
text += "$it\n"
logContent.append(it).append("\n")
}, onStderr = {
logContent.append(it).append("\n")
});
}
}
Scaffold(
topBar = {
TopBar(
flashing,
onBack = {
navigator.popBackStack()
},
onSave = {
scope.launch {
val format = SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.getDefault())
val date = format.format(Date())
val file = File(
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
"KernelSU_install_log_${date}.log"
)
file.writeText(logContent.toString())
snackBarHost.showSnackbar("Log saved to ${file.absolutePath}")
}
}
)
},
floatingActionButton = {
if (showFloatAction) {
val reboot = stringResource(id = R.string.reboot)
ExtendedFloatingActionButton(
onClick = {
scope.launch {
withContext(Dispatchers.IO) {
reboot()
}
}
},
icon = { Icon(Icons.Filled.Refresh, reboot) },
text = { Text(text = reboot) },
)
}
}
) { innerPadding ->
KeyEventBlocker {
it.key == Key.VolumeDown || it.key == Key.VolumeUp
}
Column(
modifier = Modifier
.fillMaxSize(1f)
.padding(innerPadding)
.verticalScroll(scrollState),
) {
LaunchedEffect(text) {
scrollState.animateScrollTo(scrollState.maxValue)
}
Text(
modifier = Modifier.padding(8.dp),
text = text,
fontSize = MaterialTheme.typography.bodySmall.fontSize,
fontFamily = FontFamily.Monospace,
lineHeight = MaterialTheme.typography.bodySmall.lineHeight,
)
}
}
}
@Parcelize
sealed class FlashIt : Parcelable {
data class FlashBoot(val boot: Uri? = null, val lkm: LkmSelection, val ota: Boolean) :
FlashIt()
data class FlashModule(val uri: Uri) : FlashIt()
data object FlashRestore : FlashIt()
data object FlashUninstall : FlashIt()
}
fun flashIt(
flashIt: FlashIt, onFinish: (Boolean, Int) -> Unit,
onStdout: (String) -> Unit,
onStderr: (String) -> Unit
) {
when (flashIt) {
is FlashIt.FlashBoot -> installBoot(
flashIt.boot,
flashIt.lkm,
flashIt.ota,
onFinish,
onStdout,
onStderr
)
is FlashIt.FlashModule -> flashModule(flashIt.uri, onFinish, onStdout, onStderr)
FlashIt.FlashRestore -> restoreBoot(onFinish, onStdout, onStderr)
FlashIt.FlashUninstall -> uninstallPermanently(onFinish, onStdout, onStderr)
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun TopBar(status: FlashingStatus, onBack: () -> Unit = {}, onSave: () -> Unit = {}) {
TopAppBar(
title = {
Text(
stringResource(
when (status) {
FlashingStatus.FLASHING -> R.string.flashing
FlashingStatus.SUCCESS -> R.string.flash_success
FlashingStatus.FAILED -> R.string.flash_failed
}
)
)
},
navigationIcon = {
IconButton(
onClick = onBack
) { Icon(Icons.AutoMirrored.Filled.ArrowBack, contentDescription = null) }
},
actions = {
IconButton(onClick = onSave) {
Icon(
imageVector = Icons.Filled.Save,
contentDescription = "Localized description"
)
}
}
)
}
@Preview
@Composable
fun InstallPreview() {
InstallScreen(EmptyDestinationsNavigator)
}

解释语言

Python

来源:https://raw.githubusercontent.com/yt-dlp/yt-dlp/master/yt_dlp/downloader/dash.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
import time
import urllib.parse
from . import get_suitable_downloader
from .fragment import FragmentFD
from ..utils import update_url_query, urljoin

class DashSegmentsFD(FragmentFD):
"""
Download segments in a DASH manifest. External downloaders can take over
the fragment downloads by supporting the 'dash_frag_urls' protocol
"""
FD_NAME = 'dashsegments'
def real_download(self, filename, info_dict):
if 'http_dash_segments_generator' in info_dict['protocol'].split('+'):
real_downloader = None # No external FD can support --live-from-start
else:
if info_dict.get('is_live'):
self.report_error('Live DASH videos are not supported')
real_downloader = get_suitable_downloader(
info_dict, self.params, None, protocol='dash_frag_urls', to_stdout=(filename == '-'))
real_start = time.time()
requested_formats = [{**info_dict, **fmt} for fmt in info_dict.get('requested_formats', [])]
args = []
for fmt in requested_formats or [info_dict]:
try:
fragment_count = 1 if self.params.get('test') else len(fmt['fragments'])
except TypeError:
fragment_count = None
ctx = {
'filename': fmt.get('filepath') or filename,
'live': 'is_from_start' if fmt.get('is_from_start') else fmt.get('is_live'),
'total_frags': fragment_count,
}
if real_downloader:
self._prepare_external_frag_download(ctx)
else:
self._prepare_and_start_frag_download(ctx, fmt)
ctx['start'] = real_start
extra_query = None
extra_param_to_segment_url = info_dict.get('extra_param_to_segment_url')
if extra_param_to_segment_url:
extra_query = urllib.parse.parse_qs(extra_param_to_segment_url)
fragments_to_download = self._get_fragments(fmt, ctx, extra_query)
if real_downloader:
self.to_screen(
f'[{self.FD_NAME}] Fragment downloads will be delegated to {real_downloader.get_basename()}')
info_dict['fragments'] = list(fragments_to_download)
fd = real_downloader(self.ydl, self.params)
return fd.real_download(filename, info_dict)
args.append([ctx, fragments_to_download, fmt])
return self.download_and_append_fragments_multiple(*args, is_fatal=lambda idx: idx == 0)
def _resolve_fragments(self, fragments, ctx):
fragments = fragments(ctx) if callable(fragments) else fragments
return [next(iter(fragments))] if self.params.get('test') else fragments
def _get_fragments(self, fmt, ctx, extra_query):
fragment_base_url = fmt.get('fragment_base_url')
fragments = self._resolve_fragments(fmt['fragments'], ctx)
frag_index = 0
for i, fragment in enumerate(fragments):
frag_index += 1
if frag_index <= ctx['fragment_index']:
continue
fragment_url = fragment.get('url')
if not fragment_url:
assert fragment_base_url
fragment_url = urljoin(fragment_base_url, fragment['path'])
if extra_query:
fragment_url = update_url_query(fragment_url, extra_query)
yield {
'frag_index': frag_index,
'fragment_count': fragment.get('fragment_count'),
'index': i,
'url': fragment_url,
}

Shell

来源:https://raw.githubusercontent.com/f-droid/fdroidclient/master/create_ota.sh

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#!/bin/bash
#
# Script to prepare an update.zip containing F-Droid

set -e

PROG_DIR=$(dirname $(realpath $0))

TMP_DIR=$(mktemp -d -t fdroidclient.tmp.XXXXXXXX)
trap "rm -rf $TMP_DIR" EXIT

function error() {
echo "*** ERROR: " $@
usage
}

function usage() {
cat << EOFU
Usage: $0 variant
where:
- variant is one of: debug, release, or binary
EOFU
exit 1
}

# Parse input
VARIANT="$1"
[[ -z "$VARIANT" ]] && error "Missing variant"

VERSIONCODE=$2

GPG="gpg --keyring $PROG_DIR/f-droid.org-signing-key.gpg --no-default-keyring --trust-model always"

GITVERSION=$(git describe --tags --always)

FDROID_APK=F-Droid.apk

# Collect files
mkdir -p $TMP_DIR/META-INF/com/google/android/
cp app/src/main/scripts/update-binary $TMP_DIR/META-INF/com/google/android/

if [ $VARIANT == "binary" ] ; then
if [ -z $VERSIONCODE ]; then
curl -L https://f-droid.org/$FDROID_APK > $TMP_DIR/$FDROID_APK
curl -L https://f-droid.org/${FDROID_APK}.asc > $TMP_DIR/${FDROID_APK}.asc
else
GITVERSION=$VERSIONCODE
DL_APK=org.fdroid.fdroid_${VERSIONCODE}.apk
curl -L https://f-droid.org/repo/$DL_APK > $TMP_DIR/$FDROID_APK
curl -L https://f-droid.org/repo/${DL_APK}.asc > $TMP_DIR/${FDROID_APK}.asc
fi
$GPG --verify $TMP_DIR/${FDROID_APK}.asc
rm $TMP_DIR/${FDROID_APK}.asc
else
cd $PROG_DIR
./gradlew assemble$(echo $VARIANT | tr 'dr' 'DR')
OUT_DIR=$PROG_DIR/app/build/outputs/apk
if [ $VARIANT == "debug" ]; then
cp $OUT_DIR/app-${VARIANT}.apk \
$TMP_DIR/$FDROID_APK
elif [ -f $OUT_DIR/app-${VARIANT}-signed.apk ]; then
cp $OUT_DIR/app-${VARIANT}-signed.apk \
$TMP_DIR/$FDROID_APK
else
cp $OUT_DIR/app-${VARIANT}-unsigned.apk \
$TMP_DIR/$FDROID_APK
fi
fi

# Make zip
if [ $VARIANT == "binary" ] ; then
ZIPBASE=F-DroidFromBinaries-${GITVERSION}
else
ZIPBASE=F-Droid-${GITVERSION}
fi
if [ $VARIANT == "debug" ]; then
ZIP=${ZIPBASE}-debug.zip
else
ZIP=${ZIPBASE}.zip
fi
OUT_DIR=$PROG_DIR/app/build/distributions
mkdir -p $OUT_DIR
[ -f $OUT_DIR/$ZIP ] && rm -f $OUT_DIR/$ZIP
pushd $TMP_DIR
zip -r $OUT_DIR/$ZIP .
popd

JavaScript

来源:https://raw.githubusercontent.com/jerryc127/hexo-theme-butterfly/master/source/js/search/local-search.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
/**
* Refer to hexo-generator-searchdb
* https://github.com/next-theme/hexo-generator-searchdb/blob/main/dist/search.js
* Modified by hexo-theme-butterfly
*/
class LocalSearch {
constructor ({
path = '',
unescape = false,
top_n_per_article = 1
}) {
this.path = path
this.unescape = unescape
this.top_n_per_article = top_n_per_article
this.isfetched = false
this.datas = null
}
getIndexByWord (words, text, caseSensitive = false) {
const index = []
const included = new Set()
if (!caseSensitive) {
text = text.toLowerCase()
}
words.forEach(word => {
if (this.unescape) {
const div = document.createElement('div')
div.innerText = word
word = div.innerHTML
}
const wordLen = word.length
if (wordLen === 0) return
let startPosition = 0
let position = -1
if (!caseSensitive) {
word = word.toLowerCase()
}
while ((position = text.indexOf(word, startPosition)) > -1) {
index.push({ position, word })
included.add(word)
startPosition = position + wordLen
}
})
// Sort index by position of keyword
index.sort((left, right) => {
if (left.position !== right.position) {
return left.position - right.position
}
return right.word.length - left.word.length
})
return [index, included]
}
// Merge hits into slices
mergeIntoSlice (start, end, index) {
let item = index[0]
let { position, word } = item
const hits = []
const count = new Set()
while (position + word.length <= end && index.length !== 0) {
count.add(word)
hits.push({
position,
length: word.length
})
const wordEnd = position + word.length
// Move to next position of hit
index.shift()
while (index.length !== 0) {
item = index[0]
position = item.position
word = item.word
if (wordEnd > position) {
index.shift()
} else {
break
}
}
}
return {
hits,
start,
end,
count: count.size
}
}
// Highlight title and content
highlightKeyword (val, slice) {
let result = ''
let index = slice.start
for (const { position, length } of slice.hits) {
result += val.substring(index, position)
index = position + length
result += `<mark class="search-keyword">${val.substr(position, length)}</mark>`
}
result += val.substring(index, slice.end)
return result
}
getResultItems (keywords) {
const resultItems = []
this.datas.forEach(({ title, content, url }) => {
// The number of different keywords included in the article.
const [indexOfTitle, keysOfTitle] = this.getIndexByWord(keywords, title)
const [indexOfContent, keysOfContent] = this.getIndexByWord(keywords, content)
const includedCount = new Set([...keysOfTitle, ...keysOfContent]).size
// Show search results
const hitCount = indexOfTitle.length + indexOfContent.length
if (hitCount === 0) return
const slicesOfTitle = []
if (indexOfTitle.length !== 0) {
slicesOfTitle.push(this.mergeIntoSlice(0, title.length, indexOfTitle))
}
let slicesOfContent = []
while (indexOfContent.length !== 0) {
const item = indexOfContent[0]
const { position } = item
// Cut out 120 characters. The maxlength of .search-input is 80.
const start = Math.max(0, position - 20)
const end = Math.min(content.length, position + 100)
slicesOfContent.push(this.mergeIntoSlice(start, end, indexOfContent))
}
// Sort slices in content by included keywords' count and hits' count
slicesOfContent.sort((left, right) => {
if (left.count !== right.count) {
return right.count - left.count
} else if (left.hits.length !== right.hits.length) {
return right.hits.length - left.hits.length
}
return left.start - right.start
})
// Select top N slices in content
const upperBound = parseInt(this.top_n_per_article, 10)
if (upperBound >= 0) {
slicesOfContent = slicesOfContent.slice(0, upperBound)
}
let resultItem = ''
url = new URL(url, location.origin)
url.searchParams.append('highlight', keywords.join(' '))
if (slicesOfTitle.length !== 0) {
resultItem += `<div class="local-search-hit-item"><a href="${url.href}"><span class="search-result-title">${this.highlightKeyword(title, slicesOfTitle[0])}</span>`
} else {
resultItem += `<div class="local-search-hit-item"><a href="${url.href}"><span class="search-result-title">${title}</span>`
}
slicesOfContent.forEach(slice => {
resultItem += `<p class="search-result">${this.highlightKeyword(content, slice)}...</p></a>`
})
resultItem += '</div>'
resultItems.push({
item: resultItem,
id: resultItems.length,
hitCount,
includedCount
})
})
return resultItems
}
fetchData () {
const isXml = !this.path.endsWith('json')
fetch(this.path)
.then(response => response.text())
.then(res => {
// Get the contents from search data
this.isfetched = true
this.datas = isXml
? [...new DOMParser().parseFromString(res, 'text/xml').querySelectorAll('entry')].map(element => ({
title: element.querySelector('title').textContent,
content: element.querySelector('content').textContent,
url: element.querySelector('url').textContent
}))
: JSON.parse(res)
// Only match articles with non-empty titles
this.datas = this.datas.filter(data => data.title).map(data => {
data.title = data.title.trim()
data.content = data.content ? data.content.trim().replace(/<[^>]+>/g, '') : ''
data.url = decodeURIComponent(data.url).replace(/\/{2,}/g, '/')
return data
})
// Remove loading animation
window.dispatchEvent(new Event('search:loaded'))
})
}
// Highlight by wrapping node in mark elements with the given class name
highlightText (node, slice, className) {
const val = node.nodeValue
let index = slice.start
const children = []
for (const { position, length } of slice.hits) {
const text = document.createTextNode(val.substring(index, position))
index = position + length
const mark = document.createElement('mark')
mark.className = className
mark.appendChild(document.createTextNode(val.substr(position, length)))
children.push(text, mark)
}
node.nodeValue = val.substring(index, slice.end)
children.forEach(element => {
node.parentNode.insertBefore(element, node)
})
}
// Highlight the search words provided in the url in the text
highlightSearchWords (body) {
const params = new URL(location.href).searchParams.get('highlight')
const keywords = params ? params.split(' ') : []
if (!keywords.length || !body) return
const walk = document.createTreeWalker(body, NodeFilter.SHOW_TEXT, null)
const allNodes = []
while (walk.nextNode()) {
if (!walk.currentNode.parentNode.matches('button, select, textarea, .mermaid')) allNodes.push(walk.currentNode)
}
allNodes.forEach(node => {
const [indexOfNode] = this.getIndexByWord(keywords, node.nodeValue)
if (!indexOfNode.length) return
const slice = this.mergeIntoSlice(0, node.nodeValue.length, indexOfNode)
this.highlightText(node, slice, 'search-keyword')
})
}
}
window.addEventListener('load', () => {
// Search
const { path, top_n_per_article, unescape, languages } = GLOBAL_CONFIG.localSearch
const localSearch = new LocalSearch({
path,
top_n_per_article,
unescape
})
const input = document.querySelector('#local-search-input input')
const statsItem = document.getElementById('local-search-stats-wrap')
const $loadingStatus = document.getElementById('loading-status')
const isXml = !path.endsWith('json')
const inputEventFunction = () => {
if (!localSearch.isfetched) return
let searchText = input.value.trim().toLowerCase()
isXml && (searchText = searchText.replace(/</g, '&lt;').replace(/>/g, '&gt;'))
if (searchText !== '') $loadingStatus.innerHTML = '<i class="fas fa-spinner fa-pulse"></i>'
const keywords = searchText.split(/[-\s]+/)
const container = document.getElementById('local-search-results')
let resultItems = []
if (searchText.length > 0) {
// Perform local searching
resultItems = localSearch.getResultItems(keywords)
}
if (keywords.length === 1 && keywords[0] === '') {
container.textContent = ''
statsItem.textContent = ''
} else if (resultItems.length === 0) {
container.textContent = ''
const statsDiv = document.createElement('div')
statsDiv.className = 'search-result-stats'
statsDiv.textContent = languages.hits_empty.replace(/\$\{query}/, searchText)
statsItem.innerHTML = statsDiv.outerHTML
} else {
resultItems.sort((left, right) => {
if (left.includedCount !== right.includedCount) {
return right.includedCount - left.includedCount
} else if (left.hitCount !== right.hitCount) {
return right.hitCount - left.hitCount
}
return right.id - left.id
})
const stats = languages.hits_stats.replace(/\$\{hits}/, resultItems.length)
container.innerHTML = `<div class="search-result-list">${resultItems.map(result => result.item).join('')}</div>`
statsItem.innerHTML = `<hr><div class="search-result-stats">${stats}</div>`
window.pjax && window.pjax.refresh(container)
}
$loadingStatus.textContent = ''
}
let loadFlag = false
const $searchMask = document.getElementById('search-mask')
const $searchDialog = document.querySelector('#local-search .search-dialog')
// fix safari
const fixSafariHeight = () => {
if (window.innerWidth < 768) {
$searchDialog.style.setProperty('--search-height', window.innerHeight + 'px')
}
}
const openSearch = () => {
const bodyStyle = document.body.style
bodyStyle.width = '100%'
bodyStyle.overflow = 'hidden'
btf.animateIn($searchMask, 'to_show 0.5s')
btf.animateIn($searchDialog, 'titleScale 0.5s')
setTimeout(() => { input.focus() }, 300)
if (!loadFlag) {
!localSearch.isfetched && localSearch.fetchData()
input.addEventListener('input', inputEventFunction)
loadFlag = true
}
// shortcut: ESC
document.addEventListener('keydown', function f (event) {
if (event.code === 'Escape') {
closeSearch()
document.removeEventListener('keydown', f)
}
})
fixSafariHeight()
window.addEventListener('resize', fixSafariHeight)
}
const closeSearch = () => {
const bodyStyle = document.body.style
bodyStyle.width = ''
bodyStyle.overflow = ''
btf.animateOut($searchDialog, 'search_close .5s')
btf.animateOut($searchMask, 'to_hide 0.5s')
window.removeEventListener('resize', fixSafariHeight)
}
const searchClickFn = () => {
btf.addEventListenerPjax(document.querySelector('#search-button > .search'), 'click', openSearch)
}
const searchFnOnce = () => {
document.querySelector('#local-search .search-close-button').addEventListener('click', closeSearch)
$searchMask.addEventListener('click', closeSearch)
if (GLOBAL_CONFIG.localSearch.preload) {
localSearch.fetchData()
}
localSearch.highlightSearchWords(document.getElementById('article-container'))
}
window.addEventListener('search:loaded', () => {
const $loadDataItem = document.getElementById('loading-database')
$loadDataItem.nextElementSibling.style.display = 'block'
$loadDataItem.remove()
})
searchClickFn()
searchFnOnce()
// pjax
window.addEventListener('pjax:complete', () => {
!btf.isHidden($searchMask) && closeSearch()
localSearch.highlightSearchWords(document.getElementById('article-container'))
searchClickFn()
})
})

数据与前端

XML

来源:https://raw.githubusercontent.com/f-droid/fdroidclient/master/app/lint.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
<?xml version="1.0" encoding="UTF-8"?>
<lint>
<!-- TODO bump our targetSdkVersion when we are ready for it -->
<issue id="ExpiredTargetSdkVersion" severity="ignore" />
<!-- TODO This should be handled as part of an overhaul of Bluetooth swap -->
<issue id="MissingPermission" severity="">
<ignore path="src/full/java/org/fdroid/fdroid/nearby/BluetoothManager.java" />
<ignore path="src/full/java/org/fdroid/fdroid/nearby/SwapWorkflowActivity.java" />
</issue>
<!-- Our translations are crowd-sourced -->
<issue id="MissingTranslation" severity="ignore" />
<issue id="ExtraTranslation" severity="warning" />
<!-- to make CI fail on errors until this is fixed
https://github.com/rtyley/spongycastle/issues/7 -->
<issue id="InvalidPackage" severity="warning" />
<issue id="ImpliedQuantity" severity="error" />
<issue id="DefaultLocale" severity="error" />
<issue id="SimpleDateFormat" severity="error" />
<issue id="NewApi" severity="error" />
<issue id="InlinedApi" severity="error" />
<!-- These are important to us, so promote from warning to error -->
<issue id="UnusedResources" severity="error">
<ignore path="src/main/res/drawable/category_**.png" />
<ignore path="src/main/res/values/dimens.xml" />
<ignore path="src/main/res/values/styles.xml" />
<ignore path="src/full/res/values/styles.xml" />
<!-- keep a single strings.xml for all build flavors -->
<ignore path="src/main/res/values**/strings.xml" />
</issue>
<issue id="AppCompatMethod" severity="error" />
<issue id="NestedScrolling" severity="error" />
<issue id="Typos" severity="error" />
<issue id="StringFormatCount" severity="error" />
<issue id="UnsafeProtectedBroadcastReceiver" severity="error" />
<issue id="GetInstance" severity="error" />
<issue id="PackageManagerGetSignatures" severity="error" />
<issue id="HardwareIds" severity="error" />
<issue id="TrustAllX509TrustManager" severity="error">
<!-- these come from included libraries -->
<ignore path="org/apache/commons/net/ftp/FTPSTrustManager.class" />
<ignore path="org/bouncycastle/est/jcajce/JcaJceUtils$1.class" />
<ignore path="org/bouncycastle/est/jcajce/JcaJceUtils$2.class" />
<ignore path="org/apache/commons/net/util/TrustManagerUtils$TrustManager.class" />
<ignore path="\*/bcpkix-jdk15to18-1.71.jar" />
<ignore path="\*/commons-net-3.6.jar" />
</issue>
<issue id="PluralsCandidate" severity="error" />
<issue id="HardcodedText" severity="error" />
<issue id="RtlCompat" severity="error" />
<issue id="RtlEnabled" severity="error" />
<!-- both the correct and deprecated locales need to be present for
them to be recognized on all devices -->
<issue id="LocaleFolder" severity="error">
<ignore path="src/main/res/values-he" />
<ignore path="src/main/res/values-id" />
</issue>
<!-- Weblate doesn't handle these yet: https://github.com/WeblateOrg/weblate/issues/7520 -->
<issue id="MissingQuantity" severity="error">
<ignore path="src/main/res/values-cs" />
<ignore path="src/main/res/values-fr" />
<ignore path="src/main/res/values-lt" />
<ignore path="src/main/res/values-sk" />
</issue>
<issue id="SetWorldReadable" severity="error">
<ignore path="src/main/java/org/fdroid/fdroid/installer/ApkFileProvider.java" />
</issue>
<issue id="ProtectedPermissions" severity="error">
<ignore path="src/debug/AndroidManifest.xml" />
<ignore path="src/full/AndroidManifest.xml" />
</issue>
<!-- these should be fixed, but it'll be a chunk of work -->
<issue id="SetTextI18n" severity="error">
<ignore path="src/main/java/org/fdroid/fdroid/views/AppDetailsRecyclerViewAdapter.java" />
<ignore path="src/main/java/org/fdroid/fdroid/views/apps/AppListItemController.java" />
</issue>
</lint>

Stylus

来源:https://raw.githubusercontent.com/jerryc127/hexo-theme-butterfly/master/source/css/_layout/post.styl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
beautify()
headStyle(fontsize)
padding-left: unit(fontsize + 8, 'px')
&:before
font-size: unit(fontsize - 2, 'px')
&:hover
padding-left: unit(fontsize + 12, 'px')
h1,
h2,
h3,
h4,
h5,
h6
transition: all .2s ease-out
&:before
position: absolute
top: calc(50% - 7px)
color: $title-prefix-icon-color
content: $title-prefix-icon
left: 0
line-height: 1
transition: all .2s ease-out
@extend .fontawesomeIcon
&:hover
&:before
color: $light-blue
h1
headStyle(20)
h2
headStyle(18)
h3
headStyle(16)
h4
headStyle(14)
h5
headStyle(12)
h6
headStyle(12)
ol,
ul
p
margin: 0 0 8px
li
&::marker
color: $light-blue
font-weight: 600
font-size: 1.05em
&:hover
&::marker
color: var(--pseudo-hover)
ul > li
list-style-type: circle

hr
@extend .custom-hr
#article-container
word-wrap: break-word
overflow-wrap: break-word
if hexo-config('text_align_justify')
text-align: justify
a
color: $theme-link-color
&:hover
text-decoration: underline
img
display: block
margin: 0 auto 20px
max-width: 100%
transition: filter 375ms ease-in .2s
p
margin: 0 0 16px
iframe
margin: 0 0 20px
kbd
margin: 0 3px
padding: 3px 5px
border: 1px solid #b4b4b4
border-radius: 3px
background-color: #f8f8f8
box-shadow: 0 1px 3px rgba(0, 0, 0, .25), 0 2px 1px 0 rgba(255, 255, 255, .6) inset
color: #34495e
white-space: nowrap
font-weight: 600
font-size: .9em
font-family: Monaco, 'Ubuntu Mono', monospace
line-height: 1em
if hexo-config('anchor.click_to_scroll')
h1,
h2,
h3,
h4,
h5,
h6
width: fit-content
a:not(.headerlink)
position relative
z-index 10
a.headerlink
position: absolute
top: 0
right: 0
left 0
bottom: 0
width 100%
height: 100%
ol,
ul
ol,
ul
padding-left: 20px
li
margin: 4px 0
p
margin: 0 0 8px
> :last-child
margin-bottom: 0 !important
hr
margin: 20px 0
if hexo-config('beautify.enable')
if hexo-config('beautify.field') == 'site'
beautify()
else if hexo-config('beautify.field') == 'post'
&.post-content
beautify()
#post
.tag_share
&:after
display: block
clear: both
content: ''
.post-meta
&__tag-list
display: inline-block
&__tags
display: inline-block
margin: 8px 8px 8px 0
padding: 0 12px
width: fit-content
border: 1px solid $light-blue
border-radius: 12px
color: $light-blue
font-size: .85em
transition: all .2s ease-in-out
&:hover
background: $light-blue
color: var(--white)
.post_share
display: inline-block
float: right
margin: 8px 0 0
width: fit-content
.social-share
font-size: .85em
.social-share-icon
margin: 0 4px
width: w = 1.85em
height: w
font-size: 1.2em
line-height: w
.post-copyright
position: relative
margin: 40px 0 10px
padding: 10px 16px
border: 1px solid var(--light-grey)
transition: box-shadow .3s ease-in-out
&:before
@extend .fontawesomeIcon
position: absolute
top: 2px
right: 12px
color: $theme-color
content: '\f1f9'
font-size: 1.3em
&:hover
box-shadow: 0 0 8px 0 rgba(232, 237, 250, .6), 0 2px 4px 0 rgba(232, 237, 250, .5)
.post-copyright
&-meta
color: $light-blue
font-weight: bold
i
margin-right: 3px
&-info
padding-left: 6px
a
text-decoration: underline
word-break: break-word
&:hover
text-decoration: none
.post-outdate-notice
position: relative
margin: 0 0 20px
padding: .5em 1.2em
border-radius: 3px
background-color: $noticeOutdate-bg
color: $noticeOutdate-color
if hexo-config('noticeOutdate.style') == 'flat'
padding: .5em 1em .5em 2.6em
border-left: 5px solid $noticeOutdate-border
&:before
@extend .fontawesomeIcon
position: absolute
top: 50%
left: .9em
color: $noticeOutdate-border
content: '\f071'
transform: translateY(-50%)
.ads-wrap
margin: 40px 0

HTML

来源:https://raw.githubusercontent.com/h5bp/html5-boilerplate/main/src/index.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<!doctype html>
<html class="no-js" lang="">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title></title>
<link rel="stylesheet" href="css/style.css">
<meta name="description" content="">
<meta property="og:title" content="">
<meta property="og:type" content="">
<meta property="og:url" content="">
<meta property="og:image" content="">
<meta property="og:image:alt" content="">
<link rel="icon" href="/favicon.ico" sizes="any">
<link rel="icon" href="/icon.svg" type="image/svg+xml">
<link rel="apple-touch-icon" href="icon.png">
<link rel="manifest" href="site.webmanifest">
<meta name="theme-color" content="#fafafa">
</head>
<body>
<!-- Add your site or application content here -->
<p>Hello world! This is HTML5 Boilerplate.</p>
<script src="js/app.js"></script>
</body>
</html>

数学

单行公式

F=G=mg=1kg9.8N/kg=9.8NF=G=m_\textsf {物} g=1kg・9.8N/kg=9.8N

数学文字混杂

让我们尝试证明一个稍微复杂一些的等式:

k=0n(nk)=2n\sum_{k=0}^{n} \binom{n}{k} = 2^n

我们可以使用二项式定理证明这个等式。二项式定理表述为:

(a+b)n=k=0n(nk)ankbk(a + b)^n = \sum_{k=0}^{n} \binom{n}{k} a^{n-k} b^k

现在,令 a=1a = 1, b=1b = 1,然后代入:

(1+1)n=k=0n(nk)1nk1k(1 + 1)^n = \sum_{k=0}^{n} \binom{n}{k} 1^{n-k} 1^k

2n=k=0n(nk)2^n = \sum_{k=0}^{n} \binom{n}{k}

这证明了 k=0n(nk)=2n\sum_{k=0}^{n} \binom{n}{k} = 2^n

渲染器拓展语法测试

这是 被注释文本

上标示例

下标示例

脚注示例 [1]

Butterfly 拓展语法测试 [2]

Note

simple

默认 提示块标签

default 提示块标签

primary 提示块标签

success 提示块标签

info 提示块标签

warning 提示块标签

danger 提示块标签

modern

默认 提示块标签

default 提示块标签

primary 提示块标签

success 提示块标签

info 提示块标签

warning 提示块标签

danger 提示块标签

flat

默认 提示块标签

default 提示块标签

primary 提示块标签

success 提示块标签

info 提示块标签

warning 提示块标签

danger 提示块标签

disable

默认 提示块标签

default 提示块标签

primary 提示块标签

success 提示块标签

info 提示块标签

warning 提示块标签

danger 提示块标签

Tag Hide

哪个英文字母最酷? 因为西装裤 (C 装酷)

Mermaid[^5]

流程图

时序图

甘特图

类图

状态图

饼图

用户体验旅程图

C4 图

Tabs

This is Tab 1.

This is Tab 2.

This is Tab 3.

Button

Butterfly Butterfly Butterfly Butterfly Butterfly Butterfly Butterfly

label

臣亮言:先帝创业未半,而中道崩殂。今天下三分,益州疲敝,此诚危急存亡之秋也!然侍衞之臣,不懈于内;忠志之士,忘身于外者,盖追先帝之殊遇,欲报之于陛下也。诚宜开张圣听,以光先帝遗德,恢弘志士之气;不宜妄自菲薄,引喻失义,以塞忠谏之路也。

宫中、府中,俱为一体;陟罚臧否,不宜异同。若有作奸犯科,及为忠善者,宜付有司,论其刑赏,以昭陛下平明之治;不宜偏私,使内外异法也。

Timeline

2022

01-02

这是测试页面

Chart.js

除了计算机编程外,我想不出还有其他让我感兴趣的工作。 我可以无中生有地创造出精美的范式结构, 在此过程中也解决了无数的小谜团。 I can't think of any other job other than computer programming that interests me. I can create beautiful paradigms and structures out of nothing, Countless small mysteries are also solved in the process.


  1. 脚注是指附在文章页面的最底端的,对某些东西加以说明的注文。 ↩︎

  2. 代码来自 Butterfly 安裝文檔 (三) 主題配置 - 1 | Butterfly,采用 CC-BY-NC-SA 协议授权 ↩︎

  3. 画作由 TysonTan 绘制,采用 CC-BY-SA 协议授权 ↩︎

  4. 代码来自 Mermaid ReadMe ↩︎