Compare commits
33 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bdddca8637 | ||
|
|
032c3d4c54 | ||
|
|
2baaecc7f0 | ||
|
|
ad5042b66e | ||
|
|
2e067fa5d1 | ||
|
|
2c1beac1ca | ||
|
|
84be28708e | ||
|
|
6242c6ca8b | ||
|
|
ca5e93f221 | ||
|
|
e382a83895 | ||
|
|
2bc727bfbd | ||
|
|
8bdde66eaf | ||
|
|
0582a7554c | ||
|
|
48016fac7b | ||
|
|
823a3f9767 | ||
|
|
56a028d60d | ||
|
|
d0e8faea77 | ||
|
|
a772a0f82d | ||
|
|
de6dc65a56 | ||
|
|
c81e0c6c0f | ||
|
|
f87e705e2f | ||
|
|
078ffdb5e1 | ||
|
|
5a6ab43ea4 | ||
|
|
de6b2794b7 | ||
|
|
c574f39ae3 | ||
|
|
954ecd9644 | ||
|
|
8250c0ecc2 | ||
|
|
cc78812f50 | ||
|
|
088996da9b | ||
|
|
52a3a04b11 | ||
|
|
f290653088 | ||
|
|
33c498078f | ||
|
|
fb0eea2994 |
19
.github/workflows/build-manager.yml
vendored
@@ -2,7 +2,7 @@ name: Build Manager
|
|||||||
|
|
||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
branches: [ "main", "dev", "ci" ]
|
branches: [ "main", "dev", "ci", "miuix" ]
|
||||||
paths:
|
paths:
|
||||||
- '.github/workflows/build-manager.yml'
|
- '.github/workflows/build-manager.yml'
|
||||||
- '.github/workflows/build-lkm.yml'
|
- '.github/workflows/build-lkm.yml'
|
||||||
@@ -11,13 +11,14 @@ on:
|
|||||||
- 'userspace/ksud/**'
|
- 'userspace/ksud/**'
|
||||||
- 'userspace/user_scanner/**'
|
- 'userspace/user_scanner/**'
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [ "main", "dev" ]
|
branches: [ "main", "dev", "miuix" ]
|
||||||
paths:
|
paths:
|
||||||
- '.github/workflows/build-manager.yml'
|
- '.github/workflows/build-manager.yml'
|
||||||
- '.github/workflows/build-lkm.yml'
|
- '.github/workflows/build-lkm.yml'
|
||||||
- 'manager/**'
|
- 'manager/**'
|
||||||
- 'kernel/**'
|
- 'kernel/**'
|
||||||
- 'userspace/ksud/**'
|
- 'userspace/ksud/**'
|
||||||
|
- 'userspace/user_scanner/**'
|
||||||
workflow_call:
|
workflow_call:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -81,7 +82,14 @@ jobs:
|
|||||||
- name: Determine manager variant for telegram bot
|
- name: Determine manager variant for telegram bot
|
||||||
id: determine
|
id: determine
|
||||||
run: |
|
run: |
|
||||||
if [ "${{ matrix.spoofed }}" == "true" ]; then
|
if [ "${{ github.ref_name }}" == "miuix" ] && [ "${{ matrix.spoofed }}" == "true" ]; then
|
||||||
|
echo "SKIP=true" >> $GITHUB_OUTPUT
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
if [ "${{ github.ref_name }}" == "miuix" ]; then
|
||||||
|
echo "title=Manager" >> $GITHUB_OUTPUT
|
||||||
|
echo "topicid=${{ vars.MESSAGE_MIUIX_THREAD_ID }}" >> $GITHUB_OUTPUT
|
||||||
|
elif [ "${{ matrix.spoofed }}" == "true" ]; then
|
||||||
echo "title=Spoofed-Manager" >> $GITHUB_OUTPUT
|
echo "title=Spoofed-Manager" >> $GITHUB_OUTPUT
|
||||||
# maybe need a new var
|
# maybe need a new var
|
||||||
echo "topicid=${{ vars.MESSAGE_SPOOFED_THREAD_ID }}" >> $GITHUB_OUTPUT
|
echo "topicid=${{ vars.MESSAGE_SPOOFED_THREAD_ID }}" >> $GITHUB_OUTPUT
|
||||||
@@ -91,7 +99,7 @@ jobs:
|
|||||||
fi
|
fi
|
||||||
|
|
||||||
- name: Run randomizer
|
- name: Run randomizer
|
||||||
if: ${{ matrix.spoofed == 'true' }}
|
if: ${{ matrix.spoofed == 'true' && steps.determine.outputs.SKIP != 'true' }}
|
||||||
run: |
|
run: |
|
||||||
chmod +x randomizer
|
chmod +x randomizer
|
||||||
./randomizer
|
./randomizer
|
||||||
@@ -164,6 +172,7 @@ jobs:
|
|||||||
cp -f ../armeabi-v7a/uid_scanner ../manager/app/src/main/jniLibs/armeabi-v7a/libuid_scanner.so
|
cp -f ../armeabi-v7a/uid_scanner ../manager/app/src/main/jniLibs/armeabi-v7a/libuid_scanner.so
|
||||||
|
|
||||||
- name: Build with Gradle
|
- name: Build with Gradle
|
||||||
|
if: ${{ steps.determine.outputs.SKIP != 'true' }}
|
||||||
run: ./gradlew clean assembleRelease
|
run: ./gradlew clean assembleRelease
|
||||||
|
|
||||||
- name: Upload build artifact
|
- name: Upload build artifact
|
||||||
@@ -181,7 +190,7 @@ jobs:
|
|||||||
path: "manager/app/build/outputs/mapping/release/"
|
path: "manager/app/build/outputs/mapping/release/"
|
||||||
|
|
||||||
- name: Upload to telegram
|
- name: Upload to telegram
|
||||||
if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true'
|
if: github.event_name != 'pull_request' && steps.need_upload.outputs.UPLOAD == 'true' && steps.determine.outputs.SKIP != 'true'
|
||||||
env:
|
env:
|
||||||
CHAT_ID: ${{ vars.CHAT_ID }}
|
CHAT_ID: ${{ vars.CHAT_ID }}
|
||||||
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
|
BOT_TOKEN: ${{ secrets.BOT_TOKEN }}
|
||||||
|
|||||||
65
.github/workflows/notify.yml
vendored
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
name: Notify
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
- 'release/**'
|
||||||
|
paths-ignore:
|
||||||
|
- 'docs/**'
|
||||||
|
- '*.md'
|
||||||
|
pull_request:
|
||||||
|
types:
|
||||||
|
- opened
|
||||||
|
- reopened
|
||||||
|
- synchronize
|
||||||
|
branches:
|
||||||
|
- main
|
||||||
|
|
||||||
|
defaults:
|
||||||
|
run:
|
||||||
|
working-directory: ts
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
notify:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
concurrency:
|
||||||
|
group: notify-telegram-${{ github.ref }}
|
||||||
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository with sparse-checkout
|
||||||
|
uses: actions/checkout@v5
|
||||||
|
with:
|
||||||
|
fetch-depth: 1
|
||||||
|
sparse-checkout: |
|
||||||
|
ts/**
|
||||||
|
sparse-checkout-cone-mode: false
|
||||||
|
|
||||||
|
- name: Setup Bun runtime
|
||||||
|
uses: oven-sh/setup-bun@v2
|
||||||
|
with:
|
||||||
|
bun-version: latest
|
||||||
|
|
||||||
|
- name: Cache Bun modules
|
||||||
|
uses: actions/cache@v4
|
||||||
|
with:
|
||||||
|
path: |
|
||||||
|
~/.bun
|
||||||
|
ts/.node_modules
|
||||||
|
key: ${{ runner.os }}-bun-${{ hashFiles('ts/bun.lock') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-bun-
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: bun install --cache
|
||||||
|
|
||||||
|
- name: Run telegram bot script
|
||||||
|
env:
|
||||||
|
BOT_TOKEN: ${{ secrets.TELEGRAM_BOT_TOKEN }}
|
||||||
|
TELEGRAM_GROUP_ID: ${{ secrets.TELEGRAM_GROUP_ID }}
|
||||||
|
TELEGRAM_TOPIC_COMMITS: ${{ secrets.TELEGRAM_TOPIC_COMMITS }}
|
||||||
|
TELEGRAM_TOPIC_PRS: ${{ secrets.TELEGRAM_TOPIC_PRS }}
|
||||||
|
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
GITHUB_EVENT_PATH: ${{ github.event_path }}
|
||||||
|
run: bun run send.ts
|
||||||
89
Website/.gitignore
vendored
@@ -1,89 +0,0 @@
|
|||||||
node_modules
|
|
||||||
pnpm-lock.yaml
|
|
||||||
package-lock.json
|
|
||||||
yarn.lock
|
|
||||||
.wrangler
|
|
||||||
.DS_Store
|
|
||||||
.vscode
|
|
||||||
.idea
|
|
||||||
.vite_opt_cache
|
|
||||||
|
|
||||||
# Build artifacts
|
|
||||||
docs/.vitepress/dist/
|
|
||||||
docs/.vitepress/cache/
|
|
||||||
docs/.vitepress/.temp/
|
|
||||||
dist/
|
|
||||||
|
|
||||||
# Generated files
|
|
||||||
docs/public/sw.js
|
|
||||||
docs/public/favicon*.png
|
|
||||||
docs/public/favicon.ico
|
|
||||||
docs/public/android-chrome-*.png
|
|
||||||
docs/public/apple-touch-icon*.png
|
|
||||||
docs/public/safari-pinned-tab.svg
|
|
||||||
docs/public/browserconfig.xml
|
|
||||||
docs/public/site.webmanifest
|
|
||||||
docs/public/manifest.webmanifest
|
|
||||||
docs/public/mstile-*.png
|
|
||||||
docs/public/favicons.html
|
|
||||||
favicon-data.json
|
|
||||||
|
|
||||||
# Logs
|
|
||||||
*.log
|
|
||||||
npm-debug.log*
|
|
||||||
yarn-debug.log*
|
|
||||||
yarn-error.log*
|
|
||||||
pnpm-debug.log*
|
|
||||||
|
|
||||||
# Runtime data
|
|
||||||
pids
|
|
||||||
*.pid
|
|
||||||
*.seed
|
|
||||||
*.pid.lock
|
|
||||||
|
|
||||||
# Coverage directory used by tools like istanbul
|
|
||||||
coverage/
|
|
||||||
*.lcov
|
|
||||||
|
|
||||||
# Dependency directories
|
|
||||||
.pnpm-store/
|
|
||||||
|
|
||||||
# Optional npm cache directory
|
|
||||||
.npm
|
|
||||||
|
|
||||||
# Optional eslint cache
|
|
||||||
.eslintcache
|
|
||||||
|
|
||||||
# Microbundle cache
|
|
||||||
.rpt2_cache/
|
|
||||||
.rts2_cache_cjs/
|
|
||||||
.rts2_cache_es/
|
|
||||||
.rts2_cache_umd/
|
|
||||||
|
|
||||||
# Optional REPL history
|
|
||||||
.node_repl_history
|
|
||||||
|
|
||||||
# Output of 'npm pack'
|
|
||||||
*.tgz
|
|
||||||
|
|
||||||
# Yarn Integrity file
|
|
||||||
.yarn-integrity
|
|
||||||
|
|
||||||
# parcel-bundler cache (https://parceljs.org/)
|
|
||||||
.cache
|
|
||||||
.parcel-cache
|
|
||||||
|
|
||||||
# TypeScript cache
|
|
||||||
*.tsbuildinfo
|
|
||||||
|
|
||||||
# Optional npm cache directory
|
|
||||||
.npm
|
|
||||||
|
|
||||||
# Optional REPL history
|
|
||||||
.node_repl_history
|
|
||||||
|
|
||||||
# Output of 'npm pack'
|
|
||||||
*.tgz
|
|
||||||
|
|
||||||
# Stores VSCode versions used for testing VSCode extensions
|
|
||||||
.vscode-test
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
# Build artifacts
|
|
||||||
node_modules/
|
|
||||||
dist/
|
|
||||||
docs/.vitepress/cache/
|
|
||||||
docs/.vitepress/dist/
|
|
||||||
|
|
||||||
# Logs
|
|
||||||
*.log
|
|
||||||
|
|
||||||
# Lockfiles backup
|
|
||||||
pnpm-debug.log*
|
|
||||||
|
|
||||||
# Generated
|
|
||||||
.DS_Store
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://json.schemastore.org/prettierrc",
|
|
||||||
"singleQuote": true,
|
|
||||||
"semi": false,
|
|
||||||
"trailingComma": "es5",
|
|
||||||
"printWidth": 100,
|
|
||||||
"tabWidth": 2,
|
|
||||||
"arrowParens": "always",
|
|
||||||
"endOfLine": "lf"
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
# SukiSU-Ultra Documentation
|
|
||||||
|
|
||||||
[](https://sukisu.org)
|
|
||||||
[](https://vitepress.dev)
|
|
||||||
[](https://sukisu.org/guide/license)
|
|
||||||
[](https://github.com/sukisu-ultra/sukisu-ultra/commits/main)
|
|
||||||
[](https://github.com/sukisu-ultra/sukisu-ultra/stargazers)
|
|
||||||
18
Website/docs/.gitignore
vendored
@@ -1,18 +0,0 @@
|
|||||||
/coverage
|
|
||||||
/src/client/shared.ts
|
|
||||||
/src/node/shared.ts
|
|
||||||
*.log
|
|
||||||
*.tgz
|
|
||||||
.DS_Store
|
|
||||||
.idea
|
|
||||||
.temp
|
|
||||||
.vite_opt_cache
|
|
||||||
.vscode
|
|
||||||
dist
|
|
||||||
cache
|
|
||||||
temp
|
|
||||||
examples-temp
|
|
||||||
node_modules
|
|
||||||
pnpm-global
|
|
||||||
TODOs.md
|
|
||||||
*.timestamp-*.mjs
|
|
||||||
@@ -1,248 +0,0 @@
|
|||||||
import { defineConfig } from 'vitepress'
|
|
||||||
import { groupIconMdPlugin, groupIconVitePlugin } from 'vitepress-plugin-group-icons'
|
|
||||||
import {
|
|
||||||
GitChangelog,
|
|
||||||
GitChangelogMarkdownSection,
|
|
||||||
} from '@nolebase/vitepress-plugin-git-changelog/vite'
|
|
||||||
|
|
||||||
import footnote from 'markdown-it-footnote'
|
|
||||||
import mark from 'markdown-it-mark'
|
|
||||||
import sub from 'markdown-it-sub'
|
|
||||||
import taskLists from 'markdown-it-task-lists'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
title: 'SukiSU-Ultra',
|
|
||||||
description: 'Next-Generation Android root solution.',
|
|
||||||
|
|
||||||
lastUpdated: true,
|
|
||||||
cleanUrls: true,
|
|
||||||
metaChunk: true,
|
|
||||||
|
|
||||||
// Global performance optimizations
|
|
||||||
cacheDir: './.vitepress/cache',
|
|
||||||
ignoreDeadLinks: false,
|
|
||||||
|
|
||||||
// Enhanced markdown with performance focus
|
|
||||||
markdown: {
|
|
||||||
math: true,
|
|
||||||
config(md) {
|
|
||||||
md.use(groupIconMdPlugin)
|
|
||||||
md.use(footnote)
|
|
||||||
md.use(mark)
|
|
||||||
md.use(sub)
|
|
||||||
md.use(taskLists)
|
|
||||||
},
|
|
||||||
linkify: true,
|
|
||||||
typographer: true,
|
|
||||||
lineNumbers: true,
|
|
||||||
image: {
|
|
||||||
lazyLoading: true,
|
|
||||||
},
|
|
||||||
toc: {
|
|
||||||
level: [1, 2, 3],
|
|
||||||
},
|
|
||||||
theme: {
|
|
||||||
light: 'github-light',
|
|
||||||
dark: 'github-dark',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
sitemap: {
|
|
||||||
hostname: 'https://sukisu.org',
|
|
||||||
transformItems(items) {
|
|
||||||
return items
|
|
||||||
.filter((item) => !item.url.includes('404'))
|
|
||||||
.map((item) => ({
|
|
||||||
...item,
|
|
||||||
changefreq:
|
|
||||||
item.url === '/' ? 'daily' : item.url.includes('/guide/') ? 'weekly' : 'monthly',
|
|
||||||
priority: item.url === '/' ? 1.0 : item.url.includes('/guide/') ? 0.9 : 0.7,
|
|
||||||
}))
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
// Critical performance transformations
|
|
||||||
transformPageData(pageData) {
|
|
||||||
const canonicalUrl = `https://sukisu.org${pageData.relativePath}`
|
|
||||||
.replace(/index\.md$/, '')
|
|
||||||
.replace(/\.md$/, '')
|
|
||||||
|
|
||||||
pageData.frontmatter.head ??= []
|
|
||||||
pageData.frontmatter.head.push(
|
|
||||||
['link', { rel: 'canonical', href: canonicalUrl }],
|
|
||||||
['meta', { property: 'og:url', content: canonicalUrl }],
|
|
||||||
['link', { rel: 'preload', href: '/logo.svg', as: 'image' }]
|
|
||||||
)
|
|
||||||
|
|
||||||
return pageData
|
|
||||||
},
|
|
||||||
|
|
||||||
head: [
|
|
||||||
// Critical resource hints for global performance
|
|
||||||
['link', { rel: 'dns-prefetch', href: '//github.com' }],
|
|
||||||
['link', { rel: 'dns-prefetch', href: '//t.me' }],
|
|
||||||
['link', { rel: 'dns-prefetch', href: '//sukisu.org' }],
|
|
||||||
|
|
||||||
// Essential favicon setup - synced from /favicon during build/dev
|
|
||||||
['link', { rel: 'icon', type: 'image/x-icon', href: '/favicon.ico' }],
|
|
||||||
['link', { rel: 'icon', type: 'image/svg+xml', href: '/favicon.svg' }],
|
|
||||||
['link', { rel: 'icon', type: 'image/png', sizes: '96x96', href: '/favicon-96x96.png' }],
|
|
||||||
['link', { rel: 'apple-touch-icon', sizes: '180x180', href: '/apple-touch-icon.png' }],
|
|
||||||
['link', { rel: 'mask-icon', href: '/safari-pinned-tab.svg', color: '#64edff' }],
|
|
||||||
// (Removed msapplication meta to avoid referencing non-existent files)
|
|
||||||
|
|
||||||
// Web App Manifest
|
|
||||||
['link', { rel: 'manifest', href: '/site.webmanifest' }],
|
|
||||||
|
|
||||||
// Theme and app configuration
|
|
||||||
['meta', { name: 'theme-color', content: '#64edff' }],
|
|
||||||
['meta', { name: 'application-name', content: 'SukiSU-Ultra' }],
|
|
||||||
['meta', { name: 'apple-mobile-web-app-title', content: 'SukiSU-Ultra' }],
|
|
||||||
['meta', { name: 'apple-mobile-web-app-capable', content: 'yes' }],
|
|
||||||
['meta', { name: 'apple-mobile-web-app-status-bar-style', content: 'default' }],
|
|
||||||
|
|
||||||
// Viewport and mobile optimization
|
|
||||||
[
|
|
||||||
'meta',
|
|
||||||
{ name: 'viewport', content: 'width=device-width, initial-scale=1.0, viewport-fit=cover' },
|
|
||||||
],
|
|
||||||
['meta', { name: 'format-detection', content: 'telephone=no' }],
|
|
||||||
['meta', { property: 'og:type', content: 'website' }],
|
|
||||||
['meta', { property: 'og:site_name', content: 'SukiSU-Ultra' }],
|
|
||||||
['meta', { property: 'og:url', content: 'https://sukisu.org/' }],
|
|
||||||
['meta', { property: 'og:locale', content: 'en_US' }],
|
|
||||||
['meta', { property: 'og:locale:alternate', content: 'zh_CN' }],
|
|
||||||
|
|
||||||
// Twitter optimization for global audience
|
|
||||||
['meta', { property: 'twitter:card', content: 'summary_large_image' }],
|
|
||||||
['meta', { property: 'twitter:site', content: '@sukisu_ultra' }],
|
|
||||||
['meta', { property: 'twitter:creator', content: '@sukisu_ultra' }],
|
|
||||||
// (Removed Twitter image as no PNG social image is provided)
|
|
||||||
|
|
||||||
// Additional SEO optimizations
|
|
||||||
[
|
|
||||||
'meta',
|
|
||||||
{
|
|
||||||
name: 'robots',
|
|
||||||
content: 'index, follow, max-image-preview:large, max-snippet:-1, max-video-preview:-1',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
['meta', { name: 'bingbot', content: 'index, follow' }],
|
|
||||||
['meta', { name: 'referrer', content: 'strict-origin-when-cross-origin' }],
|
|
||||||
|
|
||||||
// Global SEO optimization
|
|
||||||
[
|
|
||||||
'meta',
|
|
||||||
{
|
|
||||||
name: 'keywords',
|
|
||||||
content:
|
|
||||||
'Android root, KernelSU, SukiSU-Ultra, Android kernel, root management, 安卓 root, カーネル, рут',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
['meta', { name: 'author', content: 'SukiSU-Ultra Team' }],
|
|
||||||
|
|
||||||
// Enhanced structured data for global search engines
|
|
||||||
[
|
|
||||||
'script',
|
|
||||||
{ type: 'application/ld+json' },
|
|
||||||
JSON.stringify({
|
|
||||||
'@context': 'https://schema.org',
|
|
||||||
'@type': 'SoftwareApplication',
|
|
||||||
name: 'SukiSU-Ultra',
|
|
||||||
description: 'Next-Generation Android Root Solution',
|
|
||||||
applicationCategory: 'SystemApplication',
|
|
||||||
operatingSystem: 'Android',
|
|
||||||
url: 'https://sukisu.org',
|
|
||||||
downloadUrl: 'https://github.com/sukisu-ultra/sukisu-ultra/releases',
|
|
||||||
supportingData: {
|
|
||||||
'@type': 'DataCatalog',
|
|
||||||
name: 'Compatibility Database',
|
|
||||||
},
|
|
||||||
offers: {
|
|
||||||
'@type': 'Offer',
|
|
||||||
price: '0',
|
|
||||||
priceCurrency: 'USD',
|
|
||||||
},
|
|
||||||
author: {
|
|
||||||
'@type': 'Organization',
|
|
||||||
name: 'SukiSU-Ultra Team',
|
|
||||||
url: 'https://github.com/sukisu-ultra',
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
|
|
||||||
// PWA optimization for global mobile users (manifest declared above)
|
|
||||||
['meta', { name: 'apple-mobile-web-app-capable', content: 'yes' }],
|
|
||||||
['meta', { name: 'apple-mobile-web-app-status-bar-style', content: 'black-translucent' }],
|
|
||||||
['meta', { name: 'apple-mobile-web-app-title', content: 'SukiSU-Ultra' }],
|
|
||||||
|
|
||||||
// Cloudflare Web Analytics
|
|
||||||
[
|
|
||||||
'script',
|
|
||||||
{
|
|
||||||
defer: '',
|
|
||||||
src: 'https://static.cloudflareinsights.com/beacon.min.js',
|
|
||||||
'data-cf-beacon': '{"token": "dcc5feef58bf4c56a170a99f4cec4798"}',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
],
|
|
||||||
|
|
||||||
themeConfig: {
|
|
||||||
logo: { src: '/logo.svg', width: 24, height: 24 },
|
|
||||||
|
|
||||||
socialLinks: [
|
|
||||||
{ icon: 'github', link: 'https://github.com/sukisu-ultra/sukisu-ultra' },
|
|
||||||
{
|
|
||||||
icon: {
|
|
||||||
svg: '<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="icon icon-tabler icons-tabler-outline icon-tabler-brand-telegram"><path stroke="none" d="M0 0h24v24H0z" fill="none"/><path d="M15 10l-4 4l6 6l4 -16l-18 7l4 2l2 6l3 -4" /></svg>',
|
|
||||||
},
|
|
||||||
link: 'https://t.me/sukiksu',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
search: {
|
|
||||||
provider: 'local',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
rewrites: {
|
|
||||||
'en/:rest*': ':rest*',
|
|
||||||
},
|
|
||||||
|
|
||||||
locales: {
|
|
||||||
root: {
|
|
||||||
label: 'English',
|
|
||||||
},
|
|
||||||
zh: {
|
|
||||||
label: '简体中文',
|
|
||||||
link: '/zh/',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
vite: {
|
|
||||||
plugins: [
|
|
||||||
groupIconVitePlugin({
|
|
||||||
customIcon: {
|
|
||||||
bash: '<svg xmlns="http://www.w3.org/2000/svg" width="256" height="256" viewBox="0 0 256 256"><g fill="none"><rect width="256" height="256" fill="#242938" rx="60"/><path fill="#242938" fill-rule="evenodd" d="m203.819 68.835l-63.14-37.48a23.79 23.79 0 0 0-24.361 0l-63.14 37.48C45.642 73.31 41 81.575 41 90.522v74.961c0 8.945 4.643 17.215 12.18 21.689l63.14 37.473a23.8 23.8 0 0 0 12.179 3.354a23.8 23.8 0 0 0 12.178-3.354l63.14-37.473c7.536-4.474 12.182-12.744 12.182-21.689v-74.96c0-8.948-4.646-17.214-12.18-21.688" clip-rule="evenodd"/><path fill="#fff" fill-rule="evenodd" d="m118.527 220.808l-63.14-37.474c-6.176-3.666-10.013-10.506-10.013-17.852V90.523c0-7.346 3.837-14.186 10.01-17.85l63.143-37.48a19.55 19.55 0 0 1 9.972-2.747c3.495 0 6.943.95 9.973 2.747l63.14 37.48c5.204 3.089 8.714 8.438 9.701 14.437c-2.094-4.469-6.817-5.684-12.32-2.47l-59.734 36.897c-7.448 4.354-12.94 9.24-12.945 18.221v73.604c-.004 5.378 2.168 8.861 5.504 9.871c-1.096.19-2.201.322-3.319.322a19.55 19.55 0 0 1-9.972-2.747m85.292-151.974l-63.14-37.478A23.8 23.8 0 0 0 128.499 28a23.8 23.8 0 0 0-12.181 3.356l-63.14 37.478C45.642 73.308 41 81.576 41 90.524v74.958c0 8.945 4.643 17.215 12.18 21.689l63.14 37.475A23.84 23.84 0 0 0 128.499 228a23.83 23.83 0 0 0 12.178-3.354l63.142-37.475c7.536-4.474 12.18-12.744 12.18-21.689V90.523c0-8.947-4.644-17.215-12.18-21.689" clip-rule="evenodd"/><path fill="#47b353" fill-rule="evenodd" d="m187.267 172.729l-15.722 9.41c-.417.243-.723.516-.726 1.017v4.114c0 .503.338.712.754.467l15.966-9.703c.416-.243.48-.708.483-1.209v-3.629c0-.5-.338-.71-.755-.467" clip-rule="evenodd"/><path fill="#242938" fill-rule="evenodd" d="M153.788 138.098c.509-.258.928.059.935.725l.053 5.439c2.277-.906 4.255-1.148 6.047-.734c.389.104.561.633.402 1.261l-1.197 4.82c-.093.364-.298.732-.545.961a1.3 1.3 0 0 1-.315.234a.7.7 0 0 1-.472.077c-.818-.185-2.763-.61-5.823.94c-3.21 1.625-4.333 4.414-4.311 6.484c.027 2.472 1.295 3.221 5.673 3.296c5.834.097 8.355 2.646 8.416 8.522c.06 5.77-3.02 11.966-7.732 15.763l.104 5.384c.006.648-.415 1.391-.924 1.649l-3.189 1.837c-.511.258-.93-.06-.937-.708l-.055-5.296c-2.731 1.135-5.499 1.409-7.267.699c-.333-.13-.476-.622-.344-1.182l1.156-4.868c.092-.384.295-.768.571-1.012q.147-.142.299-.219c.183-.092.362-.112.514-.055c1.905.642 4.342.342 6.685-.844c2.977-1.506 4.968-4.542 4.937-7.558c-.029-2.737-1.51-3.874-5.113-3.901c-4.586.013-8.861-.891-8.932-7.642c-.057-5.558 2.833-11.342 7.408-14.999l-.057-5.435c-.007-.668.401-1.403.926-1.667z" clip-rule="evenodd"/></g></svg>',
|
|
||||||
},
|
|
||||||
}),
|
|
||||||
GitChangelog({ repoURL: () => 'https://github.com/SukiSU-Ultra/Website' }),
|
|
||||||
GitChangelogMarkdownSection({
|
|
||||||
exclude: (id) => id.endsWith('index.md'),
|
|
||||||
sections: { disableContributors: true },
|
|
||||||
}),
|
|
||||||
],
|
|
||||||
build: {
|
|
||||||
minify: 'terser',
|
|
||||||
chunkSizeWarningLimit: 800,
|
|
||||||
assetsInlineLimit: 8192,
|
|
||||||
target: 'esnext',
|
|
||||||
cssCodeSplit: true,
|
|
||||||
sourcemap: false,
|
|
||||||
},
|
|
||||||
|
|
||||||
server: {
|
|
||||||
fs: {
|
|
||||||
allow: ['..'],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
@@ -1,195 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import { ref, computed } from 'vue'
|
|
||||||
import { useData } from 'vitepress'
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
shareText: {
|
|
||||||
type: String,
|
|
||||||
default: undefined,
|
|
||||||
},
|
|
||||||
copiedText: {
|
|
||||||
type: String,
|
|
||||||
default: undefined,
|
|
||||||
},
|
|
||||||
includeQuery: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
includeHash: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
copiedTimeout: {
|
|
||||||
type: Number,
|
|
||||||
default: 2000,
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
defineOptions({ name: 'ArticleShare' })
|
|
||||||
|
|
||||||
const copied = ref(false)
|
|
||||||
const isClient = typeof window !== 'undefined' && typeof document !== 'undefined'
|
|
||||||
|
|
||||||
const { theme, lang } = useData()
|
|
||||||
|
|
||||||
const defaultShareText = computed(() =>
|
|
||||||
lang.value?.toLowerCase().startsWith('zh') ? '分享链接' : 'Share link'
|
|
||||||
)
|
|
||||||
const defaultCopiedText = computed(() =>
|
|
||||||
lang.value?.toLowerCase().startsWith('zh') ? '已复制!' : 'Copied!'
|
|
||||||
)
|
|
||||||
const defaultCopyFailedText = computed(() =>
|
|
||||||
lang.value?.toLowerCase().startsWith('zh') ? '复制链接失败:' : 'Failed to copy link:'
|
|
||||||
)
|
|
||||||
|
|
||||||
const i18nShareText = computed(
|
|
||||||
() => props.shareText ?? (theme.value as any)?.articleShare?.shareText ?? defaultShareText.value
|
|
||||||
)
|
|
||||||
const i18nCopiedText = computed(
|
|
||||||
() =>
|
|
||||||
props.copiedText ?? (theme.value as any)?.articleShare?.copiedText ?? defaultCopiedText.value
|
|
||||||
)
|
|
||||||
const i18nCopyFailedText = computed(
|
|
||||||
() => (theme.value as any)?.articleShare?.copyFailed ?? defaultCopyFailedText.value
|
|
||||||
)
|
|
||||||
|
|
||||||
const shareLink = computed(() => {
|
|
||||||
if (!isClient) return ''
|
|
||||||
|
|
||||||
const { origin, pathname, search, hash } = window.location
|
|
||||||
const finalSearch = props.includeQuery ? search : ''
|
|
||||||
const finalHash = props.includeHash ? hash : ''
|
|
||||||
return `${origin}${pathname}${finalSearch}${finalHash}`
|
|
||||||
})
|
|
||||||
|
|
||||||
async function copyToClipboard() {
|
|
||||||
if (copied.value || !isClient) return
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (navigator.clipboard) {
|
|
||||||
await navigator.clipboard.writeText(shareLink.value)
|
|
||||||
} else {
|
|
||||||
const input = document.createElement('input')
|
|
||||||
input.setAttribute('readonly', 'readonly')
|
|
||||||
input.setAttribute('value', shareLink.value)
|
|
||||||
document.body.appendChild(input)
|
|
||||||
input.select()
|
|
||||||
document.execCommand('copy')
|
|
||||||
document.body.removeChild(input)
|
|
||||||
}
|
|
||||||
|
|
||||||
copied.value = true
|
|
||||||
setTimeout(() => {
|
|
||||||
copied.value = false
|
|
||||||
}, props.copiedTimeout)
|
|
||||||
} catch (error) {
|
|
||||||
console.error(i18nCopyFailedText.value, error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const shareIconSvg = `
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
||||||
<path d="M4 12v8a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-8"></path>
|
|
||||||
<polyline points="16 6 12 2 8 6"></polyline>
|
|
||||||
<line x1="12" y1="2" x2="12" y2="15"></line>
|
|
||||||
</svg>
|
|
||||||
`
|
|
||||||
|
|
||||||
const copiedIconSvg = `
|
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
||||||
<path d="M20 6 9 17l-5-5"></path>
|
|
||||||
</svg>
|
|
||||||
`
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<div class="article-share">
|
|
||||||
<button
|
|
||||||
:class="['article-share__button', { copied: copied }]"
|
|
||||||
:aria-label="copied ? i18nCopiedText : i18nShareText"
|
|
||||||
aria-live="polite"
|
|
||||||
@click="copyToClipboard"
|
|
||||||
>
|
|
||||||
<div v-if="!copied" class="content-wrapper">
|
|
||||||
<span class="icon" v-html="shareIconSvg"></span>
|
|
||||||
{{ i18nShareText }}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div v-else class="content-wrapper">
|
|
||||||
<span class="icon" v-html="copiedIconSvg"></span>
|
|
||||||
{{ i18nCopiedText }}
|
|
||||||
</div>
|
|
||||||
</button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.article-share {
|
|
||||||
padding: 14px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article-share__button {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
font-weight: 500;
|
|
||||||
font-size: 14px;
|
|
||||||
position: relative;
|
|
||||||
z-index: 1;
|
|
||||||
transition: all 0.4s var(--ease-out-cubic, cubic-bezier(0.33, 1, 0.68, 1));
|
|
||||||
cursor: pointer;
|
|
||||||
border: 1px solid transparent;
|
|
||||||
border-radius: 14px;
|
|
||||||
padding: 7px 14px;
|
|
||||||
width: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
color: var(--vp-c-text-1, #333);
|
|
||||||
background-color: var(--vp-c-bg-alt, #f6f6f7);
|
|
||||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.02);
|
|
||||||
will-change: transform, box-shadow;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article-share__button::before {
|
|
||||||
content: '';
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
left: -100%;
|
|
||||||
z-index: -1;
|
|
||||||
transition: left 0.6s ease;
|
|
||||||
background-color: var(--vp-c-brand-soft, #ddf4ff);
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.article-share__button:hover {
|
|
||||||
transform: translateY(-1px);
|
|
||||||
border-color: var(--vp-c-brand-soft, #ddf4ff);
|
|
||||||
background-color: var(--vp-c-brand-soft, #ddf4ff);
|
|
||||||
}
|
|
||||||
|
|
||||||
.article-share__button:active {
|
|
||||||
transform: scale(0.9);
|
|
||||||
}
|
|
||||||
|
|
||||||
.article-share__button.copied {
|
|
||||||
color: var(--vp-c-brand-1, #007acc);
|
|
||||||
background-color: var(--vp-c-brand-soft, #ddf4ff);
|
|
||||||
}
|
|
||||||
|
|
||||||
.article-share__button.copied::before {
|
|
||||||
left: 0;
|
|
||||||
background-color: var(--vp-c-brand-soft, #ddf4ff);
|
|
||||||
}
|
|
||||||
|
|
||||||
.content-wrapper {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
display: inline-flex;
|
|
||||||
align-items: center;
|
|
||||||
margin-right: 6px;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
<script setup lang="ts">
|
|
||||||
import confetti from 'canvas-confetti'
|
|
||||||
|
|
||||||
const media = window.matchMedia('(prefers-reduced-motion: reduce)')
|
|
||||||
if (!media.matches) {
|
|
||||||
confetti({
|
|
||||||
particleCount: 100,
|
|
||||||
spread: 170,
|
|
||||||
origin: { y: 0.6 },
|
|
||||||
})
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -1,150 +0,0 @@
|
|||||||
<script setup>
|
|
||||||
import { onBeforeUnmount, onMounted, ref, computed } from 'vue'
|
|
||||||
|
|
||||||
const showBackTop = ref(false) // 初始状态设为false
|
|
||||||
const scrollProgress = ref(0)
|
|
||||||
|
|
||||||
// 圆形进度条计算
|
|
||||||
const radius = 42
|
|
||||||
const circumference = computed(() => 2 * Math.PI * radius)
|
|
||||||
|
|
||||||
function scrollToTop() {
|
|
||||||
window.scrollTo({
|
|
||||||
top: 0,
|
|
||||||
behavior: 'smooth',
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// 使用更高效的节流函数
|
|
||||||
function throttle(fn, delay = 50) {
|
|
||||||
let timer = null
|
|
||||||
return function (...args) {
|
|
||||||
if (!timer) {
|
|
||||||
timer = setTimeout(() => {
|
|
||||||
fn.apply(this, args)
|
|
||||||
timer = null
|
|
||||||
}, delay)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const updateScrollProgress = () => {
|
|
||||||
const { scrollY, innerHeight } = window
|
|
||||||
const { scrollHeight } = document.documentElement
|
|
||||||
const totalScroll = scrollHeight - innerHeight
|
|
||||||
scrollProgress.value = totalScroll > 0 ? Math.min(scrollY / totalScroll, 1) : 0
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleScroll = throttle(() => {
|
|
||||||
// 当滚动超过100px时显示,否则隐藏
|
|
||||||
const shouldShow = window.scrollY > 100
|
|
||||||
showBackTop.value = shouldShow
|
|
||||||
updateScrollProgress()
|
|
||||||
})
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
window.addEventListener('scroll', handleScroll)
|
|
||||||
updateScrollProgress()
|
|
||||||
})
|
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
|
||||||
window.removeEventListener('scroll', handleScroll)
|
|
||||||
})
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<template>
|
|
||||||
<Transition name="fade">
|
|
||||||
<div class="back-top-container" v-show="showBackTop">
|
|
||||||
<svg class="progress-ring" viewBox="0 0 100 100">
|
|
||||||
<circle class="progress-ring-background" cx="50" cy="50" r="42" />
|
|
||||||
<circle
|
|
||||||
class="progress-ring-circle"
|
|
||||||
cx="50"
|
|
||||||
cy="50"
|
|
||||||
r="42"
|
|
||||||
:style="{ 'stroke-dashoffset': circumference - scrollProgress * circumference }"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
<div class="vitepress-backTop-main" title="返回顶部" @click="scrollToTop()">
|
|
||||||
<svg class="icon" viewBox="0 0 1024 1024">
|
|
||||||
<path
|
|
||||||
d="M752.736 431.063C757.159 140.575 520.41 8.97 504.518 0.41V0l-0.45 0.205-0.41-0.205v0.41c-15.934 8.56-252.723 140.165-248.259 430.653-48.21 31.457-98.713 87.368-90.685 184.074 8.028 96.666 101.007 160.768 136.601 157.287 35.595-3.482 25.232-30.31 25.232-30.31l12.206-50.095s52.47 80.569 69.304 80.528c15.114-1.23 87-0.123 95.6 0h0.82c8.602-0.123 80.486-1.23 95.6 0 16.794 0 69.305-80.528 69.305-80.528l12.165 50.094s-10.322 26.83 25.272 30.31c35.595 3.482 128.574-60.62 136.602-157.286 8.028-96.665-42.475-152.617-90.685-184.074z m-248.669-4.26c-6.758-0.123-94.781-3.359-102.891-107.192 2.95-98.714 95.97-107.438 102.891-107.93 6.964 0.492 99.943 9.216 102.892 107.93-8.11 103.833-96.174 107.07-102.892 107.192z m-52.019 500.531c0 11.838-9.42 21.382-21.012 21.382a21.217 21.217 0 0 1-21.054-21.34V821.74c0-11.797 9.421-21.382 21.054-21.382 11.591 0 21.012 9.585 21.012 21.382v105.635z m77.333 57.222a21.504 21.504 0 0 1-21.34 21.626 21.504 21.504 0 0 1-21.34-21.626V827.474c0-11.96 9.543-21.668 21.299-21.668 11.796 0 21.38 9.708 21.38 21.668v157.082z m71.147-82.043c0 11.796-9.42 21.34-21.053 21.34a21.217 21.217 0 0 1-21.013-21.34v-75.367c0-11.755 9.421-21.299 21.013-21.299 11.632 0 21.053 9.544 21.053 21.3v75.366z"
|
|
||||||
fill="#FFF"
|
|
||||||
/>
|
|
||||||
</svg>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</Transition>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.back-top-container {
|
|
||||||
position: fixed;
|
|
||||||
bottom: 20px;
|
|
||||||
right: 20px;
|
|
||||||
width: 60px;
|
|
||||||
height: 60px;
|
|
||||||
z-index: 999;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vitepress-backTop-main {
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
cursor: pointer;
|
|
||||||
width: 44px;
|
|
||||||
height: 44px;
|
|
||||||
border-radius: 50%;
|
|
||||||
background-color: #3eaf7c;
|
|
||||||
padding: 8px;
|
|
||||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
z-index: 2;
|
|
||||||
transition: background-color 0.2s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vitepress-backTop-main:hover {
|
|
||||||
background-color: #71cda3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress-ring {
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
transform: rotate(-90deg);
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress-ring-background {
|
|
||||||
fill: none;
|
|
||||||
stroke: rgba(62, 175, 124, 0.15);
|
|
||||||
stroke-width: 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress-ring-circle {
|
|
||||||
fill: none;
|
|
||||||
stroke: #3eaf7c;
|
|
||||||
stroke-width: 3;
|
|
||||||
stroke-dasharray: 264; /* 2 * π * 42 */
|
|
||||||
stroke-linecap: round;
|
|
||||||
transition: stroke-dashoffset 0.15s ease-out;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
width: 24px;
|
|
||||||
height: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fade-enter-active,
|
|
||||||
.fade-leave-active {
|
|
||||||
transition: opacity 0.3s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.fade-enter-from,
|
|
||||||
.fade-leave-to {
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
// .vitepress/theme/index.ts
|
|
||||||
import DefaultTheme from 'vitepress/theme'
|
|
||||||
import { NolebaseGitChangelogPlugin } from '@nolebase/vitepress-plugin-git-changelog/client'
|
|
||||||
import 'virtual:group-icons.css'
|
|
||||||
import { h, onMounted } from 'vue'
|
|
||||||
import './style/style.css'
|
|
||||||
import ArticleShare from './components/ArticleShare.vue'
|
|
||||||
import backtotop from './components/backtotop.vue'
|
|
||||||
import '@nolebase/vitepress-plugin-git-changelog/client/style.css'
|
|
||||||
|
|
||||||
export default {
|
|
||||||
extends: DefaultTheme,
|
|
||||||
Layout: () => {
|
|
||||||
return h(DefaultTheme.Layout, null, {
|
|
||||||
'aside-outline-before': () => h(ArticleShare),
|
|
||||||
'doc-footer-before': () => h(backtotop),
|
|
||||||
})
|
|
||||||
},
|
|
||||||
enhanceApp({ app }) {
|
|
||||||
app.use(NolebaseGitChangelogPlugin)
|
|
||||||
|
|
||||||
// Register service worker in production for offline support and caching
|
|
||||||
if (
|
|
||||||
typeof window !== 'undefined' &&
|
|
||||||
'serviceWorker' in navigator &&
|
|
||||||
(import.meta as any).env?.PROD
|
|
||||||
) {
|
|
||||||
onMounted(() => {
|
|
||||||
navigator.serviceWorker.register('/sw.js').catch(() => {})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
@@ -1,192 +0,0 @@
|
|||||||
/* .vitepress/theme/style/custom-block.css */
|
|
||||||
/* 深浅色卡 */
|
|
||||||
:root {
|
|
||||||
--custom-block-info-left: #cccccc;
|
|
||||||
--custom-block-info-bg: #fafafa;
|
|
||||||
|
|
||||||
--custom-block-tip-left: #009400;
|
|
||||||
--custom-block-tip-bg: #e6f6e6;
|
|
||||||
|
|
||||||
--custom-block-warning-left: #e6a700;
|
|
||||||
--custom-block-warning-bg: #fff8e6;
|
|
||||||
|
|
||||||
--custom-block-danger-left: #e13238;
|
|
||||||
--custom-block-danger-bg: #ffebec;
|
|
||||||
|
|
||||||
--custom-block-note-left: #4cb3d4;
|
|
||||||
--custom-block-note-bg: #eef9fd;
|
|
||||||
|
|
||||||
--custom-block-important-left: #a371f7;
|
|
||||||
--custom-block-important-bg: #f4eefe;
|
|
||||||
|
|
||||||
--custom-block-caution-left: #e0575b;
|
|
||||||
--custom-block-caution-bg: #fde4e8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark {
|
|
||||||
--custom-block-info-left: #cccccc;
|
|
||||||
--custom-block-info-bg: #474748;
|
|
||||||
|
|
||||||
--custom-block-tip-left: #009400;
|
|
||||||
--custom-block-tip-bg: #003100;
|
|
||||||
|
|
||||||
--custom-block-warning-left: #e6a700;
|
|
||||||
--custom-block-warning-bg: #4d3800;
|
|
||||||
|
|
||||||
--custom-block-danger-left: #e13238;
|
|
||||||
--custom-block-danger-bg: #4b1113;
|
|
||||||
|
|
||||||
--custom-block-note-left: #4cb3d4;
|
|
||||||
--custom-block-note-bg: #193c47;
|
|
||||||
|
|
||||||
--custom-block-important-left: #a371f7;
|
|
||||||
--custom-block-important-bg: #230555;
|
|
||||||
|
|
||||||
--custom-block-caution-left: #e0575b;
|
|
||||||
--custom-block-caution-bg: #391c22;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 标题字体大小 */
|
|
||||||
.custom-block-title {
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* info容器:背景色、左侧 */
|
|
||||||
.custom-block.info {
|
|
||||||
border-left: 5px solid var(--custom-block-info-left);
|
|
||||||
background-color: var(--custom-block-info-bg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* info容器:svg图 */
|
|
||||||
.custom-block.info [class*='custom-block-title']::before {
|
|
||||||
content: '';
|
|
||||||
background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-1-11v6h2v-6h-2zm0-4v2h2V7h-2z' fill='%23ccc'/%3E%3C/svg%3E");
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: middle;
|
|
||||||
position: relative;
|
|
||||||
margin-right: 4px;
|
|
||||||
left: -5px;
|
|
||||||
top: -1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 提示容器:边框色、背景色、左侧 */
|
|
||||||
.custom-block.tip {
|
|
||||||
/* border-color: var(--custom-block-tip); */
|
|
||||||
border-left: 5px solid var(--custom-block-tip-left);
|
|
||||||
background-color: var(--custom-block-tip-bg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 提示容器:svg图 */
|
|
||||||
.custom-block.tip [class*='custom-block-title']::before {
|
|
||||||
content: '';
|
|
||||||
background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath fill='%23009400' d='M7.941 18c-.297-1.273-1.637-2.314-2.187-3a8 8 0 1 1 12.49.002c-.55.685-1.888 1.726-2.185 2.998H7.94zM16 20v1a2 2 0 0 1-2 2h-4a2 2 0 0 1-2-2v-1h8zm-3-9.995V6l-4.5 6.005H11v4l4.5-6H13z'/%3E%3C/svg%3E");
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: middle;
|
|
||||||
position: relative;
|
|
||||||
margin-right: 4px;
|
|
||||||
left: -5px;
|
|
||||||
top: -2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 警告容器:背景色、左侧 */
|
|
||||||
.custom-block.warning {
|
|
||||||
border-left: 5px solid var(--custom-block-warning-left);
|
|
||||||
background-color: var(--custom-block-warning-bg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 警告容器:svg图 */
|
|
||||||
.custom-block.warning [class*='custom-block-title']::before {
|
|
||||||
content: '';
|
|
||||||
background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1024 1024'%3E%3Cpath d='M576.286 752.57v-95.425q0-7.031-4.771-11.802t-11.3-4.772h-96.43q-6.528 0-11.3 4.772t-4.77 11.802v95.424q0 7.031 4.77 11.803t11.3 4.77h96.43q6.528 0 11.3-4.77t4.77-11.803zm-1.005-187.836 9.04-230.524q0-6.027-5.022-9.543-6.529-5.524-12.053-5.524H456.754q-5.524 0-12.053 5.524-5.022 3.516-5.022 10.547l8.538 229.52q0 5.023 5.022 8.287t12.053 3.265h92.913q7.032 0 11.803-3.265t5.273-8.287zM568.25 95.65l385.714 707.142q17.578 31.641-1.004 63.282-8.538 14.564-23.354 23.102t-31.892 8.538H126.286q-17.076 0-31.892-8.538T71.04 866.074q-18.582-31.641-1.004-63.282L455.75 95.65q8.538-15.57 23.605-24.61T512 62t32.645 9.04 23.605 24.61z' fill='%23e6a700'/%3E%3C/svg%3E");
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: middle;
|
|
||||||
position: relative;
|
|
||||||
margin-right: 4px;
|
|
||||||
left: -5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 危险容器:背景色、左侧 */
|
|
||||||
.custom-block.danger {
|
|
||||||
border-left: 5px solid var(--custom-block-danger-left);
|
|
||||||
background-color: var(--custom-block-danger-bg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 危险容器:svg图 */
|
|
||||||
.custom-block.danger [class*='custom-block-title']::before {
|
|
||||||
content: '';
|
|
||||||
background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2c5.523 0 10 4.477 10 10v3.764a2 2 0 0 1-1.106 1.789L18 19v1a3 3 0 0 1-2.824 2.995L14.95 23a2.5 2.5 0 0 0 .044-.33L15 22.5V22a2 2 0 0 0-1.85-1.995L13 20h-2a2 2 0 0 0-1.995 1.85L9 22v.5c0 .171.017.339.05.5H9a3 3 0 0 1-3-3v-1l-2.894-1.447A2 2 0 0 1 2 15.763V12C2 6.477 6.477 2 12 2zm-4 9a2 2 0 1 0 0 4 2 2 0 0 0 0-4zm8 0a2 2 0 1 0 0 4 2 2 0 0 0 0-4z' fill='%23e13238'/%3E%3C/svg%3E");
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: middle;
|
|
||||||
position: relative;
|
|
||||||
margin-right: 4px;
|
|
||||||
left: -5px;
|
|
||||||
top: -1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 提醒容器:背景色、左侧 */
|
|
||||||
.custom-block.note {
|
|
||||||
border-left: 5px solid var(--custom-block-note-left);
|
|
||||||
background-color: var(--custom-block-note-bg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 提醒容器:svg图 */
|
|
||||||
.custom-block.note [class*='custom-block-title']::before {
|
|
||||||
content: '';
|
|
||||||
background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zm-1-11v6h2v-6h-2zm0-4v2h2V7h-2z' fill='%234cb3d4'/%3E%3C/svg%3E");
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: middle;
|
|
||||||
position: relative;
|
|
||||||
margin-right: 4px;
|
|
||||||
left: -5px;
|
|
||||||
top: -1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 重要容器:背景色、左侧 */
|
|
||||||
.custom-block.important {
|
|
||||||
border-left: 5px solid var(--custom-block-important-left);
|
|
||||||
background-color: var(--custom-block-important-bg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 重要容器:svg图 */
|
|
||||||
.custom-block.important [class*='custom-block-title']::before {
|
|
||||||
content: '';
|
|
||||||
background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 1024 1024'%3E%3Cpath d='M512 981.333a84.992 84.992 0 0 1-84.907-84.906h169.814A84.992 84.992 0 0 1 512 981.333zm384-128H128v-42.666l85.333-85.334v-256A298.325 298.325 0 0 1 448 177.92V128a64 64 0 0 1 128 0v49.92a298.325 298.325 0 0 1 234.667 291.413v256L896 810.667v42.666zm-426.667-256v85.334h85.334v-85.334h-85.334zm0-256V512h85.334V341.333h-85.334z' fill='%23a371f7'/%3E%3C/svg%3E");
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: middle;
|
|
||||||
position: relative;
|
|
||||||
margin-right: 4px;
|
|
||||||
left: -5px;
|
|
||||||
top: -1px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 注意容器:背景色、左侧 */
|
|
||||||
.custom-block.caution {
|
|
||||||
border-left: 5px solid var(--custom-block-caution-left);
|
|
||||||
background-color: var(--custom-block-caution-bg);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 注意容器:svg图 */
|
|
||||||
.custom-block.caution [class*='custom-block-title']::before {
|
|
||||||
content: '';
|
|
||||||
background-image: url("data:image/svg+xml;utf8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24'%3E%3Cpath d='M12 2c5.523 0 10 4.477 10 10v3.764a2 2 0 0 1-1.106 1.789L18 19v1a3 3 0 0 1-2.824 2.995L14.95 23a2.5 2.5 0 0 0 .044-.33L15 22.5V22a2 2 0 0 0-1.85-1.995L13 20h-2a2 2 0 0 0-1.995 1.85L9 22v.5c0 .171.017.339.05.5H9a3 3 0 0 1-3-3v-1l-2.894-1.447A2 2 0 0 1 2 15.763V12C2 6.477 6.477 2 12 2zm-4 9a2 2 0 1 0 0 4 2 2 0 0 0 0-4zm8 0a2 2 0 1 0 0 4 2 2 0 0 0 0-4z' fill='%23e13238'/%3E%3C/svg%3E");
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
display: inline-block;
|
|
||||||
vertical-align: middle;
|
|
||||||
position: relative;
|
|
||||||
margin-right: 4px;
|
|
||||||
left: -5px;
|
|
||||||
top: -1px;
|
|
||||||
}
|
|
||||||
@@ -1,125 +0,0 @@
|
|||||||
#app a:focus-visible,
|
|
||||||
#app button:focus-visible,
|
|
||||||
#app input[type='checkbox']:focus-visible {
|
|
||||||
--at-apply: outline-1 outline-primary ring-2 ring-primary;
|
|
||||||
}
|
|
||||||
|
|
||||||
.VPSidebar::-webkit-scrollbar {
|
|
||||||
background: transparent;
|
|
||||||
height: 8px;
|
|
||||||
width: 8px;
|
|
||||||
margin-right: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#app > div > div.VPLocalNav > div > div > div.outline {
|
|
||||||
outline-style: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.vp-doc .color-swatch {
|
|
||||||
display: inline-block;
|
|
||||||
width: 0.85em;
|
|
||||||
height: 0.85em;
|
|
||||||
min-width: 12px;
|
|
||||||
min-height: 12px;
|
|
||||||
font-size: inherit;
|
|
||||||
border: 0;
|
|
||||||
border-radius: 2px;
|
|
||||||
margin: 0 3px 0 6px;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.medium-zoom-overlay {
|
|
||||||
z-index: 20;
|
|
||||||
}
|
|
||||||
|
|
||||||
.medium-zoom-image {
|
|
||||||
z-index: 21;
|
|
||||||
}
|
|
||||||
|
|
||||||
@keyframes slide-enter {
|
|
||||||
0% {
|
|
||||||
transform: translateY(10px);
|
|
||||||
opacity: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
to {
|
|
||||||
transform: translateY(0);
|
|
||||||
opacity: 100;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (prefers-reduced-motion: no-preference) {
|
|
||||||
html:not(.no-sliding) [slide-enter],
|
|
||||||
html:not(.no-sliding) .slide-enter,
|
|
||||||
html:not(.no-sliding) .main > div > *,
|
|
||||||
html:not(.no-sliding) #VPContent > div > div.VPFeatures.VPHomeFeatures > *,
|
|
||||||
html:not(.no-sliding) .TeamPage > *,
|
|
||||||
html:not(.no-sliding) .VPHomeHero > * {
|
|
||||||
--enter-stage: 0;
|
|
||||||
--enter-step: 90ms;
|
|
||||||
--enter-initial: 0ms;
|
|
||||||
animation: slide-enter 1s both 1;
|
|
||||||
animation-delay: calc(var(--enter-initial) + var(--enter-stage) * var(--enter-step));
|
|
||||||
}
|
|
||||||
|
|
||||||
.main > div > *:nth-child(1) {
|
|
||||||
--enter-stage: 1;
|
|
||||||
}
|
|
||||||
.main > div > *:nth-child(2) {
|
|
||||||
--enter-stage: 2;
|
|
||||||
}
|
|
||||||
.main > div > *:nth-child(3) {
|
|
||||||
--enter-stage: 3;
|
|
||||||
}
|
|
||||||
.main > div > *:nth-child(4) {
|
|
||||||
--enter-stage: 4;
|
|
||||||
}
|
|
||||||
.main > div > *:nth-child(5) {
|
|
||||||
--enter-stage: 5;
|
|
||||||
}
|
|
||||||
.main > div > *:nth-child(6) {
|
|
||||||
--enter-stage: 6;
|
|
||||||
}
|
|
||||||
.main > div > *:nth-child(7) {
|
|
||||||
--enter-stage: 7;
|
|
||||||
}
|
|
||||||
.main > div > *:nth-child(8) {
|
|
||||||
--enter-stage: 8;
|
|
||||||
}
|
|
||||||
.main > div > *:nth-child(9) {
|
|
||||||
--enter-stage: 9;
|
|
||||||
}
|
|
||||||
.main > div > *:nth-child(10) {
|
|
||||||
--enter-stage: 10;
|
|
||||||
}
|
|
||||||
.main > div > *:nth-child(11) {
|
|
||||||
--enter-stage: 11;
|
|
||||||
}
|
|
||||||
.main > div > *:nth-child(12) {
|
|
||||||
--enter-stage: 12;
|
|
||||||
}
|
|
||||||
.main > div > *:nth-child(13) {
|
|
||||||
--enter-stage: 13;
|
|
||||||
}
|
|
||||||
.main > div > *:nth-child(14) {
|
|
||||||
--enter-stage: 14;
|
|
||||||
}
|
|
||||||
.main > div > *:nth-child(15) {
|
|
||||||
--enter-stage: 15;
|
|
||||||
}
|
|
||||||
.main > div > *:nth-child(16) {
|
|
||||||
--enter-stage: 16;
|
|
||||||
}
|
|
||||||
.main > div > *:nth-child(17) {
|
|
||||||
--enter-stage: 17;
|
|
||||||
}
|
|
||||||
.main > div > *:nth-child(18) {
|
|
||||||
--enter-stage: 18;
|
|
||||||
}
|
|
||||||
.main > div > *:nth-child(19) {
|
|
||||||
--enter-stage: 19;
|
|
||||||
}
|
|
||||||
.main > div > *:nth-child(20) {
|
|
||||||
--enter-stage: 20;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,158 +0,0 @@
|
|||||||
@import './custom-block.css';
|
|
||||||
@import './doc-fade-in.css';
|
|
||||||
|
|
||||||
:root:where(:lang(fa)) {
|
|
||||||
--vp-font-family-base:
|
|
||||||
'Vazirmatn', 'Inter', ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji',
|
|
||||||
'Segoe UI Emoji', 'Segoe UI Symbol', 'Noto Color Emoji';
|
|
||||||
}
|
|
||||||
|
|
||||||
:root {
|
|
||||||
--vp-home-hero-name-color: transparent;
|
|
||||||
--vp-home-hero-name-background: -webkit-linear-gradient(120deg, #bd34fe 30%, #41d1ff);
|
|
||||||
--vp-home-hero-image-background-image: linear-gradient(-45deg, #bd34fe 50%, #47caff 50%);
|
|
||||||
--vp-home-hero-image-filter: blur(44px);
|
|
||||||
|
|
||||||
/* Enhanced brand color for better contrast */
|
|
||||||
--vp-c-brand-1: #1e40af;
|
|
||||||
--vp-c-brand-2: #2563eb;
|
|
||||||
--vp-c-brand-3: #3b82f6;
|
|
||||||
--vp-c-brand-soft: rgba(30, 64, 175, 0.14);
|
|
||||||
|
|
||||||
/* Button contrast improvements */
|
|
||||||
--vp-button-brand-bg: #1e40af;
|
|
||||||
--vp-button-brand-text: #ffffff;
|
|
||||||
--vp-button-brand-hover-bg: #1d4ed8;
|
|
||||||
--vp-button-brand-hover-text: #ffffff;
|
|
||||||
--vp-button-brand-active-bg: #1e3a8a;
|
|
||||||
--vp-button-brand-active-text: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Dark mode color overrides for better contrast */
|
|
||||||
.dark:root {
|
|
||||||
--vp-c-brand-1: #60a5fa;
|
|
||||||
--vp-c-brand-2: #3b82f6;
|
|
||||||
--vp-c-brand-3: #2563eb;
|
|
||||||
--vp-c-brand-soft: rgba(96, 165, 250, 0.16);
|
|
||||||
|
|
||||||
--vp-button-brand-bg: #3b82f6;
|
|
||||||
--vp-button-brand-text: #000000;
|
|
||||||
--vp-button-brand-hover-bg: #60a5fa;
|
|
||||||
--vp-button-brand-hover-text: #000000;
|
|
||||||
--vp-button-brand-active-bg: #2563eb;
|
|
||||||
--vp-button-brand-active-text: #000000;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 640px) {
|
|
||||||
:root {
|
|
||||||
--vp-home-hero-image-filter: blur(56px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media (min-width: 960px) {
|
|
||||||
:root {
|
|
||||||
--vp-home-hero-image-filter: blur(68px);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.VPHero .VPImage {
|
|
||||||
filter: drop-shadow(-2px 4px 6px rgba(0, 0, 0, 0.2));
|
|
||||||
padding: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* used in reference/default-theme-search */
|
|
||||||
img[src='/search.png'] {
|
|
||||||
width: 100%;
|
|
||||||
aspect-ratio: 1 / 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Enhanced button contrast for accessibility */
|
|
||||||
.VPButton.brand {
|
|
||||||
background-color: var(--vp-button-brand-bg) !important;
|
|
||||||
color: var(--vp-button-brand-text) !important;
|
|
||||||
border: none;
|
|
||||||
font-weight: 600;
|
|
||||||
text-shadow: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.VPButton.brand:hover {
|
|
||||||
background-color: var(--vp-button-brand-hover-bg) !important;
|
|
||||||
color: var(--vp-button-brand-hover-text) !important;
|
|
||||||
transform: translateY(-1px);
|
|
||||||
box-shadow: 0 4px 12px rgba(30, 64, 175, 0.4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.VPButton.brand:active {
|
|
||||||
background-color: var(--vp-button-brand-active-bg) !important;
|
|
||||||
color: var(--vp-button-brand-active-text) !important;
|
|
||||||
transform: translateY(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Dark mode support for buttons */
|
|
||||||
.dark .VPButton.brand {
|
|
||||||
background-color: #3b82f6 !important;
|
|
||||||
color: #000000 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark .VPButton.brand:hover {
|
|
||||||
background-color: #60a5fa !important;
|
|
||||||
color: #000000 !important;
|
|
||||||
box-shadow: 0 4px 12px rgba(59, 130, 246, 0.4);
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark .VPButton.brand:active {
|
|
||||||
background-color: #2563eb !important;
|
|
||||||
color: #000000 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Ensure proper contrast for all text elements */
|
|
||||||
.VPButton.brand .text {
|
|
||||||
color: inherit !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Focus states for accessibility */
|
|
||||||
.VPButton.brand:focus-visible {
|
|
||||||
outline: 2px solid #ffffff;
|
|
||||||
outline-offset: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.dark .VPButton.brand:focus-visible {
|
|
||||||
outline: 2px solid #000000;
|
|
||||||
outline-offset: 2px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@font-face {
|
|
||||||
font-family: 'HarmonyOS Sans SC';
|
|
||||||
src: url('/HarmonyOS_Sans_SC.ttf') format('truetype');
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: 'HarmonyOS Sans SC', sans-serif;
|
|
||||||
line-height: 1.8;
|
|
||||||
letter-spacing: 0.05em;
|
|
||||||
word-spacing: 0.05em;
|
|
||||||
}
|
|
||||||
|
|
||||||
p,
|
|
||||||
li,
|
|
||||||
a,
|
|
||||||
span,
|
|
||||||
div,
|
|
||||||
h1,
|
|
||||||
h2,
|
|
||||||
h3,
|
|
||||||
h4,
|
|
||||||
h5,
|
|
||||||
h6 {
|
|
||||||
word-break: break-word;
|
|
||||||
text-justify: inter-ideograph;
|
|
||||||
-ms-text-autospace: ideograph-alpha;
|
|
||||||
-webkit-font-smoothing: antialiased;
|
|
||||||
-moz-osx-font-smoothing: grayscale;
|
|
||||||
}
|
|
||||||
|
|
||||||
pre,
|
|
||||||
code {
|
|
||||||
letter-spacing: normal;
|
|
||||||
word-spacing: normal;
|
|
||||||
}
|
|
||||||
@@ -1,101 +0,0 @@
|
|||||||
import { defineConfig, type DefaultTheme } from 'vitepress'
|
|
||||||
import { groupIconMdPlugin, groupIconVitePlugin } from 'vitepress-plugin-group-icons'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
lang: 'en-US',
|
|
||||||
description:
|
|
||||||
'Next-Generation Android Root Solution - Advanced kernel-based root management for Android devices with KernelSU integration',
|
|
||||||
|
|
||||||
themeConfig: {
|
|
||||||
nav: nav(),
|
|
||||||
|
|
||||||
sidebar: {
|
|
||||||
'/': { base: '/', items: sidebar() },
|
|
||||||
},
|
|
||||||
|
|
||||||
search: { options: searchOptions() },
|
|
||||||
editLink: {
|
|
||||||
pattern: 'https://github.com/sukisu-ultra/sukisu-ultra/edit/main/docs/:path',
|
|
||||||
text: 'Edit this page on GitHub',
|
|
||||||
},
|
|
||||||
|
|
||||||
docFooter: {
|
|
||||||
prev: 'Previous',
|
|
||||||
next: 'Next',
|
|
||||||
},
|
|
||||||
|
|
||||||
outline: {
|
|
||||||
label: 'On this page',
|
|
||||||
},
|
|
||||||
|
|
||||||
lastUpdated: {
|
|
||||||
text: 'Last updated',
|
|
||||||
},
|
|
||||||
|
|
||||||
notFound: {
|
|
||||||
title: 'Page Not Found',
|
|
||||||
quote: "Sorry, we couldn't find what you're looking for.",
|
|
||||||
linkLabel: 'Go to home',
|
|
||||||
linkText: 'Take me home',
|
|
||||||
},
|
|
||||||
|
|
||||||
langMenuLabel: 'Languages',
|
|
||||||
returnToTopLabel: 'Return to top',
|
|
||||||
sidebarMenuLabel: 'Menu',
|
|
||||||
darkModeSwitchLabel: 'Theme',
|
|
||||||
lightModeSwitchTitle: 'Switch to light theme',
|
|
||||||
darkModeSwitchTitle: 'Switch to dark theme',
|
|
||||||
skipToContentLabel: 'Skip to content',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
function nav(): DefaultTheme.NavItem[] {
|
|
||||||
return [
|
|
||||||
{ text: 'Home', link: '/' },
|
|
||||||
{
|
|
||||||
text: 'Getting Started',
|
|
||||||
items: [
|
|
||||||
{ text: 'Introduction', link: '/guide/' },
|
|
||||||
{ text: 'Installation', link: '/guide/installation' },
|
|
||||||
{ text: 'Compatibility', link: '/guide/compatibility' },
|
|
||||||
{ text: 'Links', link: '/guide/links' },
|
|
||||||
{ text: 'license', link: '/guide/license' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
function sidebar(): DefaultTheme.SidebarItem[] {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
text: 'Getting Started',
|
|
||||||
items: [
|
|
||||||
{ text: 'Introduction', link: '/guide/' },
|
|
||||||
{ text: 'Installation', link: '/guide/installation' },
|
|
||||||
{ text: 'Compatibility', link: '/guide/compatibility' },
|
|
||||||
{ text: 'Links', link: '/guide/links' },
|
|
||||||
{ text: 'license', link: '/guide/license' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
function searchOptions(): Partial<DefaultTheme.LocalSearchOptions> {
|
|
||||||
return {
|
|
||||||
translations: {
|
|
||||||
button: {
|
|
||||||
buttonText: 'Search docs',
|
|
||||||
buttonAriaLabel: 'Search docs',
|
|
||||||
},
|
|
||||||
modal: {
|
|
||||||
noResultsText: 'No results found',
|
|
||||||
resetButtonTitle: 'Clear query',
|
|
||||||
footer: {
|
|
||||||
selectText: 'Select',
|
|
||||||
navigateText: 'Navigate',
|
|
||||||
closeText: 'Close',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
# Compatibility Status
|
|
||||||
|
|
||||||
::: info KernelSU
|
|
||||||
KernelSU (versions prior to v0.9.5) officially supports Android GKI 2.0 devices (kernel 5.10+)
|
|
||||||
:::
|
|
||||||
|
|
||||||
::: warning Legacy Kernel Support
|
|
||||||
Older kernels (4.4+) are also compatible, but the kernel must be built manually
|
|
||||||
:::
|
|
||||||
|
|
||||||
::: tip Extended Compatibility
|
|
||||||
SukiSu-Ultra can support 3.x kernels (3.4-3.18) through additional back ports
|
|
||||||
:::
|
|
||||||
|
|
||||||
## Architecture Support
|
|
||||||
|
|
||||||
Currently supports the following processor architectures:
|
|
||||||
|
|
||||||
| Architecture | Support Level | Notes |
|
|
||||||
| --------------- | ------------------ | --------------------------- |
|
|
||||||
| **arm64-v8a** | ✅ Full Support | Primary target architecture |
|
|
||||||
| **armeabi-v7a** | ✅ Basic Support | Bare minimum functionality |
|
|
||||||
| **X86_64** | 🟡 Partial Support | Some devices supported |
|
|
||||||
@@ -1,85 +0,0 @@
|
|||||||
# Introduction
|
|
||||||
|
|
||||||
Welcome to SukiSU-Ultra, the next-generation Android root solution that provides advanced kernel-based root management for Android devices.
|
|
||||||
|
|
||||||
## What is SukiSU-Ultra?
|
|
||||||
|
|
||||||
SukiSU-Ultra is a modern, secure, and powerful root solution designed specifically for Android devices. It offers kernel-level root access management with enhanced security features and improved compatibility.
|
|
||||||
|
|
||||||
## Key Features
|
|
||||||
|
|
||||||
### 🔒 Kernel-based su and root access management
|
|
||||||
|
|
||||||
Secure root access management at the kernel level, providing better security and performance compared to traditional solutions.
|
|
||||||
|
|
||||||
### 🚫 Not based on OverlayFS module system
|
|
||||||
|
|
||||||
Built on Magic Mount technology from 5ec1cff, offering a more stable and reliable foundation.
|
|
||||||
|
|
||||||
### 📱 App Profile
|
|
||||||
|
|
||||||
Advanced application profiling system that allows you to lock root privileges in a controlled environment.
|
|
||||||
|
|
||||||
### 🔧 Enhanced Device Support
|
|
||||||
|
|
||||||
Bringing back support for non-GKI/GKI 1.0 devices, ensuring compatibility with older Android devices.
|
|
||||||
|
|
||||||
### ⚙️ Extensive Customization
|
|
||||||
|
|
||||||
Comprehensive customization options to tailor the root experience to your specific needs.
|
|
||||||
|
|
||||||
### 🔌 KPM Kernel Module Support
|
|
||||||
|
|
||||||
Full KernelPatch Module (KPM) functionality for advanced kernel modifications and enhancements.
|
|
||||||
|
|
||||||
## Why Choose SukiSU-Ultra?
|
|
||||||
|
|
||||||
- **Security First**: Advanced security features protect your device and data
|
|
||||||
- **Modern Architecture**: Built with modern Android security models in mind
|
|
||||||
- **Wide Compatibility**: Supports both GKI and non-GKI devices
|
|
||||||
- **Active Development**: Continuously updated with latest Android versions
|
|
||||||
- **Community Driven**: Open source with active community support
|
|
||||||
|
|
||||||
## Getting Started
|
|
||||||
|
|
||||||
Ready to get started with SukiSU-Ultra? Follow our step-by-step guide:
|
|
||||||
|
|
||||||
1. **[Installation](./installation)** - Learn how to install SukiSU-Ultra on your device
|
|
||||||
2. **[Compatibility](./compatibility)** - Check device compatibility requirements
|
|
||||||
3. **[Links](./links)** - Find additional resources and downloads
|
|
||||||
|
|
||||||
## System Requirements
|
|
||||||
|
|
||||||
Before installing SukiSU-Ultra, ensure your device meets these requirements:
|
|
||||||
|
|
||||||
- **Android Version**: Android 8.0 (API 26) or higher
|
|
||||||
- **Bootloader**: Unlocked bootloader
|
|
||||||
- **Recovery**: Custom recovery (TWRP recommended)
|
|
||||||
- **Storage**: At least 100MB free space
|
|
||||||
- **Knowledge**: Basic understanding of Android modding
|
|
||||||
|
|
||||||
## Safety Notice
|
|
||||||
|
|
||||||
::: danger Important
|
|
||||||
⚠️ **Rooting your device can void your warranty and may cause permanent damage if done incorrectly.**
|
|
||||||
|
|
||||||
Always:
|
|
||||||
|
|
||||||
- Create a full backup before proceeding
|
|
||||||
- Ensure your device is compatible
|
|
||||||
- Follow instructions carefully
|
|
||||||
- Have a recovery plan ready
|
|
||||||
:::
|
|
||||||
|
|
||||||
## Support
|
|
||||||
|
|
||||||
Need help? We're here to assist:
|
|
||||||
|
|
||||||
- **📖 Documentation**: Comprehensive guides and tutorials
|
|
||||||
- **💬 Community**: Active community forums and chat
|
|
||||||
- **🐛 Bug Reports**: GitHub issue tracker
|
|
||||||
- **📧 Direct Support**: Contact developers for critical issues
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Ready to unlock the full potential of your Android device?** Start with our [installation guide](./installation) and join thousands of users who trust SukiSU-Ultra for their root needs.
|
|
||||||
@@ -1,121 +0,0 @@
|
|||||||
# Installation Guide
|
|
||||||
|
|
||||||
This guide provides comprehensive instructions for installing SukiSU-Ultra on your Android device. Please follow the steps carefully.
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
Before you begin, ensure you have the following:
|
|
||||||
|
|
||||||
- [ ] A compatible device. Check the [Compatibility Guide](./compatibility.md) for details.
|
|
||||||
- [ ] Unlocked bootloader.
|
|
||||||
- [ ] Custom recovery installed, such as TWRP.
|
|
||||||
- [ ] Basic knowledge of flashing custom ROMs and kernels.
|
|
||||||
- [ ] Your device's kernel source or a compatible pre-built kernel.
|
|
||||||
|
|
||||||
## Installation Methods
|
|
||||||
|
|
||||||
There are several ways to install SukiSU-Ultra, depending on your device and preference.
|
|
||||||
|
|
||||||
### Method 1: Using Pre-built GKI Packages
|
|
||||||
|
|
||||||
This is the recommended method for devices with Generic Kernel Image (GKI) 2.0, such as many Xiaomi, Redmi, and Samsung models.[^1]
|
|
||||||
|
|
||||||
[^1]: This method is not suitable for devices from manufacturers that heavily modify the kernel, like Meizu, OnePlus, Realme, and Oppo.
|
|
||||||
|
|
||||||
#### Steps:
|
|
||||||
|
|
||||||
1. **Download GKI Build**: Visit our [resources section](./links.md) to find the appropriate GKI build for your device's kernel version. Download the `.zip` file that includes `AnyKernel3` in its name.
|
|
||||||
2. **Flash via Recovery**:
|
|
||||||
- [ ] Boot your device into TWRP recovery.
|
|
||||||
- [ ] Select "Install".
|
|
||||||
- [ ] Navigate to the downloaded `AnyKernel3` zip file and select it.
|
|
||||||
- [ ] Swipe to confirm the flash.
|
|
||||||
- [ ] Once flashing is complete, reboot your system.
|
|
||||||
3. **Verify Installation**:
|
|
||||||
- [ ] Install the SukiSU-Ultra Manager app.
|
|
||||||
- [ ] Open the app and check if root access is granted and working correctly.
|
|
||||||
- [ ] You can also verify the new kernel version in your device's settings.
|
|
||||||
|
|
||||||
::: details File Format Guide
|
|
||||||
The `.zip` archive without a suffix is uncompressed. The `.gz` suffix indicates compression used for specific models.
|
|
||||||
:::
|
|
||||||
|
|
||||||
### Method 2: Custom Build for OnePlus Devices
|
|
||||||
|
|
||||||
For OnePlus devices, you'll need to create a custom build.
|
|
||||||
|
|
||||||
#### Steps:
|
|
||||||
|
|
||||||
1. **Gather Device Information**: You will need:
|
|
||||||
- Your kernel version (e.g., `5.10`, `5.15`).
|
|
||||||
- Your processor's codename.
|
|
||||||
- The branch and configuration files from the OnePlus open-source kernel repository.
|
|
||||||
2. **Create Custom Build**: Use the link in our [resources section](./links.md) to generate a custom build with your device's information.
|
|
||||||
3. **Flash the Build**:
|
|
||||||
- [ ] Download the generated `AnyKernel3` zip file.
|
|
||||||
- [ ] Boot into recovery.
|
|
||||||
- [ ] Flash the zip file.
|
|
||||||
- [ ] Reboot and verify the installation.
|
|
||||||
|
|
||||||
### Method 3: Manual Kernel Integration (Advanced)
|
|
||||||
|
|
||||||
This method is for advanced users who are building a kernel from source.
|
|
||||||
|
|
||||||
#### Integration Scripts:
|
|
||||||
|
|
||||||
- **Main Branch (GKI)**:
|
|
||||||
```sh [bash]
|
|
||||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s main
|
|
||||||
```
|
|
||||||
- **Non-GKI Branch**:
|
|
||||||
```sh [bash]
|
|
||||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s nongki
|
|
||||||
```
|
|
||||||
- **SUSFS-Dev Branch (Recommended)**:
|
|
||||||
```sh [bash]
|
|
||||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s susfs-main
|
|
||||||
```
|
|
||||||
|
|
||||||
::: warning Required Kernel Configs
|
|
||||||
For KPM support, you must enable `CONFIG_KPM=y`.
|
|
||||||
For non-GKI devices, you also need to enable `CONFIG_KALLSYMS=y` and `CONFIG_KALLSYMS_ALL=y`.
|
|
||||||
:::
|
|
||||||
|
|
||||||
## Post-Installation
|
|
||||||
|
|
||||||
### Maintaining Root After OTA Updates
|
|
||||||
|
|
||||||
To keep root access after an Over-the-Air (OTA) update, follow these steps ==before rebooting==.
|
|
||||||
|
|
||||||
1. **Flash to Inactive Slot**:
|
|
||||||
- [ ] After the OTA update is downloaded and installed, **do not reboot**.
|
|
||||||
- [ ] Open the SukiSU-Ultra Manager.
|
|
||||||
- [ ] Go to the flashing/patching interface.
|
|
||||||
- [ ] Select your `AnyKernel3` kernel zip file.
|
|
||||||
- [ ] Choose to install it to the inactive slot.
|
|
||||||
- [ ] Once flashed, you can safely reboot.
|
|
||||||
2. **Alternative: LKM Mode**: You can also use LKM mode to install to the unused slot after an OTA.
|
|
||||||
|
|
||||||
::: tip
|
|
||||||
For non-GKI devices, the safest method to retain root after an OTA is to use TWRP to flash the kernel again.
|
|
||||||
:::
|
|
||||||
|
|
||||||
## Verification Checklist
|
|
||||||
|
|
||||||
After installation, please verify the following:
|
|
||||||
|
|
||||||
- [ ] **Manager App**: The SukiSU-Ultra Manager app opens and shows a successful root status.
|
|
||||||
- [ ] **Root Access**: Root checker apps confirm that root access is working.
|
|
||||||
- [ ] **Kernel Version**: The kernel version in `Settings > About Phone` reflects the SukiSU-Ultra kernel.
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
If you encounter any issues:
|
|
||||||
|
|
||||||
1. Double-check the [Compatibility Guide](./compatibility.md).
|
|
||||||
2. Visit our [GitHub repository](https://github.com/sukisu-ultra/sukisu-ultra) for issues and solutions.
|
|
||||||
3. Join our [Telegram community](https://t.me/sukiksu) for live support.
|
|
||||||
|
|
||||||
::: danger Safety Reminder
|
|
||||||
⚠️ **Always have a backup!** Keep a copy of your original `boot.img` and be prepared to restore your device if something goes wrong.
|
|
||||||
:::
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
# License
|
|
||||||
|
|
||||||
## 📄 Software Licensing
|
|
||||||
|
|
||||||
### Kernel Components
|
|
||||||
|
|
||||||
::: info GPL-2.0 License
|
|
||||||
The files in the "kernel" directory are under GPL-2.0-only license
|
|
||||||
:::
|
|
||||||
|
|
||||||
**License:** [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
|
||||||
|
|
||||||
### Application Core
|
|
||||||
|
|
||||||
::: tip GPL-3.0 License
|
|
||||||
All other parts (except mentioned below) are under GPL-3.0 or later license
|
|
||||||
:::
|
|
||||||
|
|
||||||
**License:** [GPL-3.0 or later](https://www.gnu.org/licenses/gpl-3.0.html)
|
|
||||||
|
|
||||||
## Artwork & Brand Assets
|
|
||||||
|
|
||||||
### Launcher Icons & Character Art
|
|
||||||
|
|
||||||
::: warning Copyright Notice
|
|
||||||
Special licensing requirements for anime character artwork
|
|
||||||
:::
|
|
||||||
|
|
||||||
The images of the files `ic_launcher(?!.*alt.*).*` with anime character emoticons have specific copyright terms:
|
|
||||||
|
|
||||||
**Copyright Holders:**
|
|
||||||
|
|
||||||
- **Anime Character Art:** [五十根大虾仁](https://space.bilibili.com/370927)
|
|
||||||
- **Brand Intellectual Property:** [明风OuO](https://space.bilibili.com/274939213)
|
|
||||||
- **Vectorization:** @MiRinChan
|
|
||||||
|
|
||||||
**License Requirements:**
|
|
||||||
|
|
||||||
1. **Creative Commons License:** [CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.txt)
|
|
||||||
2. **Author Authorization:** Required from both copyright holders
|
|
||||||
3. **Attribution:** Must credit all contributors listed above
|
|
||||||
|
|
||||||
::: details Usage Requirements
|
|
||||||
Before using these artistic assets, you must:
|
|
||||||
|
|
||||||
- Comply with Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International license
|
|
||||||
- Obtain authorization from both original authors for use of artistic content
|
|
||||||
- Provide proper attribution to all contributors
|
|
||||||
:::
|
|
||||||
|
|
||||||
## 📋 License Summary
|
|
||||||
|
|
||||||
| Component | License | Notes |
|
|
||||||
| -------------------- | ------------------------------------------------------------------------- | ------------------------------------- |
|
|
||||||
| **Kernel Files** | [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) | Files in `/kernel/` directory |
|
|
||||||
| **Application Code** | [GPL-3.0+](https://www.gnu.org/licenses/gpl-3.0.html) | Main application components |
|
|
||||||
| **Character Art** | [CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/) | + Author authorization required |
|
|
||||||
| **Brand Assets** | Mixed Licensing | See specific attribution requirements |
|
|
||||||
|
|
||||||
## 🔗 License Links
|
|
||||||
|
|
||||||
- **GPL-2.0:** [Full License Text](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
|
||||||
- **GPL-3.0:** [Full License Text](https://www.gnu.org/licenses/gpl-3.0.html)
|
|
||||||
- **CC BY-NC-SA 4.0:** [Full License Text](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.txt)
|
|
||||||
|
|
||||||
## 📞 Licensing Questions
|
|
||||||
|
|
||||||
For questions about licensing or usage permissions:
|
|
||||||
|
|
||||||
1. **Code Licensing:** Refer to respective GPL license terms
|
|
||||||
2. **Artwork Usage:** Contact original authors for authorization
|
|
||||||
3. **Commercial Use:** Review CC BY-NC-SA 4.0 restrictions
|
|
||||||
4. **Distribution:** Ensure compliance with all applicable licenses
|
|
||||||
|
|
||||||
::: tip Compliance Note
|
|
||||||
When redistributing or modifying SukiSU-Ultra, ensure you comply with all applicable licenses and attribution requirements for each component.
|
|
||||||
:::
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
# More Links
|
|
||||||
|
|
||||||
## 🌐 Translation & Localization
|
|
||||||
|
|
||||||
::: info Contribute Translations
|
|
||||||
If you need to submit a translation for the manager, please visit our Crowdin project
|
|
||||||
:::
|
|
||||||
|
|
||||||
**Translation Platform:** [Crowdin - SukiSU-Ultra](https://crowdin.com/project/SukiSU-Ultra)
|
|
||||||
|
|
||||||
## 🔧 Projects & Builds
|
|
||||||
|
|
||||||
Projects compiled based on Sukisu and susfs:
|
|
||||||
|
|
||||||
### GKI Builds
|
|
||||||
|
|
||||||
::: tip Universal GKI Support
|
|
||||||
Generic Kernel Image builds with KernelSU and SUSFS integration
|
|
||||||
:::
|
|
||||||
|
|
||||||
**Repository:** [GKI_KernelSU_SUSFS](https://github.com/ShirkNeko/GKI_KernelSU_SUSFS)
|
|
||||||
|
|
||||||
### OnePlus Builds
|
|
||||||
|
|
||||||
::: tip Device-Specific Builds
|
|
||||||
Automated OnePlus kernel builds with MKSU and SUSFS
|
|
||||||
:::
|
|
||||||
|
|
||||||
**Repository:** [Action_OnePlus_MKSU_SUSFS](https://github.com/ShirkNeko/Action_OnePlus_MKSU_SUSFS)
|
|
||||||
|
|
||||||
## 📱 Community & Support
|
|
||||||
|
|
||||||
### Telegram Community
|
|
||||||
|
|
||||||
::: info Join Our Community
|
|
||||||
Connect with other users, get support, and stay updated
|
|
||||||
:::
|
|
||||||
|
|
||||||
**Main Group:** [Tg Group](https://t.me/sukiksu)
|
|
||||||
|
|
||||||
### Test Builds
|
|
||||||
|
|
||||||
::: warning Experimental Builds
|
|
||||||
Test builds are experimental and may be unstable
|
|
||||||
:::
|
|
||||||
|
|
||||||
**Test Builds Channel:** [Latest Test Build](https://t.me/Sukiksu/7114)
|
|
||||||
|
|
||||||
## Downloads & Releases
|
|
||||||
|
|
||||||
### Official Releases
|
|
||||||
|
|
||||||
::: tip Stable Releases
|
|
||||||
Download the latest stable versions from our GitHub releases
|
|
||||||
:::
|
|
||||||
|
|
||||||
**GitHub Releases:** [SukiSU-Ultra Releases](https://github.com/sukisu-ultra/sukisu-ultra/releases)
|
|
||||||
|
|
||||||
### Issue Reporting
|
|
||||||
|
|
||||||
::: info Bug Reports & Feature Requests
|
|
||||||
Report bugs or request new features on our GitHub repository
|
|
||||||
:::
|
|
||||||
|
|
||||||
**GitHub Issues:** [Report Issues](https://github.com/sukisu-ultra/sukisu-ultra/issues)
|
|
||||||
|
|
||||||
## 🔗 Quick Links Summary
|
|
||||||
|
|
||||||
| Resource | Link | Description |
|
|
||||||
| ------------------ | ---------------------------------------------------------------------------- | ---------------------- |
|
|
||||||
| **Translations** | [Crowdin](https://crowdin.com/project/SukiSU-Ultra) | Submit translations |
|
|
||||||
| **Telegram Group** | [t.me/sukiksu](https://t.me/sukiksu) | Community support |
|
|
||||||
| **Test Builds** | [Test Channel](https://t.me/Sukiksu/7114) | Experimental builds |
|
|
||||||
| **Releases** | [GitHub Releases](https://github.com/sukisu-ultra/sukisu-ultra/releases) | Stable downloads |
|
|
||||||
| **Issues** | [GitHub Issues](https://github.com/sukisu-ultra/sukisu-ultra/issues) | Bug reports |
|
|
||||||
| **GKI Builds** | [GKI Repository](https://github.com/ShirkNeko/GKI_KernelSU_SUSFS) | Universal builds |
|
|
||||||
| **OnePlus Builds** | [OnePlus Repository](https://github.com/ShirkNeko/Action_OnePlus_MKSU_SUSFS) | Device-specific builds |
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
---
|
|
||||||
layout: home
|
|
||||||
|
|
||||||
hero:
|
|
||||||
name: 'SukiSU-Ultra'
|
|
||||||
text: 'Next-Generation Android Root Solution'
|
|
||||||
tagline: Advanced kernel-based root management for Android devices
|
|
||||||
image:
|
|
||||||
src: /logo.svg
|
|
||||||
alt: SukiSU-Ultra
|
|
||||||
actions:
|
|
||||||
- theme: brand
|
|
||||||
text: Get Started
|
|
||||||
link: /guide/
|
|
||||||
- theme: alt
|
|
||||||
text: View on GitHub
|
|
||||||
link: https://github.com/sukisu-ultra/sukisu-ultra
|
|
||||||
|
|
||||||
features:
|
|
||||||
- title: Kernel-based su and root access management
|
|
||||||
details: Secure root access management at the kernel level.
|
|
||||||
|
|
||||||
- title: Not based on OverlayFS module system
|
|
||||||
details: Based on Magic Mount from 5ec1cff.
|
|
||||||
|
|
||||||
- title: App Profile
|
|
||||||
details: Lock root privileges in a cage.
|
|
||||||
|
|
||||||
- title: Bringing back non-GKI/GKI 1.0 support
|
|
||||||
details: Enhanced compatibility for older devices.
|
|
||||||
|
|
||||||
- title: More customization
|
|
||||||
details: Extensive customization options available.
|
|
||||||
|
|
||||||
- title: Support for KPM kernel modules
|
|
||||||
details: Full KernelPatch Module functionality.
|
|
||||||
---
|
|
||||||
|
Before Width: | Height: | Size: 200 KiB |
@@ -1,60 +0,0 @@
|
|||||||
# Global headers for security and performance
|
|
||||||
/*
|
|
||||||
X-Frame-Options: DENY
|
|
||||||
X-Content-Type-Options: nosniff
|
|
||||||
Referrer-Policy: strict-origin-when-cross-origin
|
|
||||||
Strict-Transport-Security: "max-age=31536000; includeSubDomains; preload"
|
|
||||||
Permissions-Policy: "camera=(), microphone=(), geolocation=()"
|
|
||||||
Content-Security-Policy: "default-src 'self'; script-src 'self' https://static.cloudflareinsights.com; img-src 'self' data: https:; style-src 'self' 'unsafe-inline'; font-src 'self' data:; connect-src 'self'; object-src 'none'; base-uri 'self'; frame-ancestors 'none'"
|
|
||||||
|
|
||||||
# Cache static assets for maximum performance
|
|
||||||
/assets/*
|
|
||||||
Cache-Control: public, max-age=31536000, immutable
|
|
||||||
|
|
||||||
/*.js
|
|
||||||
Cache-Control: public, max-age=31536000, immutable
|
|
||||||
|
|
||||||
/*.css
|
|
||||||
Cache-Control: public, max-age=31536000, immutable
|
|
||||||
|
|
||||||
/*.woff2
|
|
||||||
Cache-Control: public, max-age=31536000, immutable
|
|
||||||
|
|
||||||
/*.woff
|
|
||||||
Cache-Control: public, max-age=31536000, immutable
|
|
||||||
|
|
||||||
# Images - 30 days cache
|
|
||||||
/*.svg
|
|
||||||
Cache-Control: public, max-age=2592000
|
|
||||||
|
|
||||||
/*.png
|
|
||||||
Cache-Control: public, max-age=2592000
|
|
||||||
|
|
||||||
/*.jpg
|
|
||||||
Cache-Control: public, max-age=2592000
|
|
||||||
|
|
||||||
/*.jpeg
|
|
||||||
Cache-Control: public, max-age=2592000
|
|
||||||
|
|
||||||
/*.webp
|
|
||||||
Cache-Control: public, max-age=2592000
|
|
||||||
|
|
||||||
/*.avif
|
|
||||||
Cache-Control: public, max-age=2592000
|
|
||||||
|
|
||||||
/*.ico
|
|
||||||
Cache-Control: public, max-age=2592000
|
|
||||||
|
|
||||||
# Manifest and service worker
|
|
||||||
/site.webmanifest
|
|
||||||
Cache-Control: public, max-age=86400
|
|
||||||
|
|
||||||
/sw.js
|
|
||||||
Cache-Control: public, max-age=0, must-revalidate
|
|
||||||
|
|
||||||
# Offline page and HTML caching
|
|
||||||
/*.html
|
|
||||||
Cache-Control: public, max-age=60, must-revalidate
|
|
||||||
|
|
||||||
/offline.html
|
|
||||||
Cache-Control: public, max-age=3600
|
|
||||||
|
Before Width: | Height: | Size: 58 KiB |
@@ -1,535 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
|
|
||||||
<svg
|
|
||||||
width="512"
|
|
||||||
height="512"
|
|
||||||
viewBox="0 0 512 512"
|
|
||||||
version="1.1"
|
|
||||||
id="svg1"
|
|
||||||
xml:space="preserve"
|
|
||||||
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
|
|
||||||
sodipodi:docname="zako.optizmism.svg"
|
|
||||||
inkscape:export-filename="zako.plain.svg"
|
|
||||||
inkscape:export-xdpi="96"
|
|
||||||
inkscape:export-ydpi="96"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
|
||||||
id="namedview1"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#999999"
|
|
||||||
borderopacity="1"
|
|
||||||
inkscape:showpageshadow="2"
|
|
||||||
inkscape:pageopacity="0"
|
|
||||||
inkscape:pagecheckerboard="0"
|
|
||||||
inkscape:deskcolor="#d1d1d1"
|
|
||||||
inkscape:document-units="px"
|
|
||||||
showguides="false"
|
|
||||||
inkscape:zoom="2"
|
|
||||||
inkscape:cx="219"
|
|
||||||
inkscape:cy="370"
|
|
||||||
inkscape:window-width="1280"
|
|
||||||
inkscape:window-height="702"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="0"
|
|
||||||
inkscape:window-maximized="1"
|
|
||||||
inkscape:current-layer="layer1"
|
|
||||||
showgrid="false"><inkscape:page
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
width="512"
|
|
||||||
height="512"
|
|
||||||
id="page2"
|
|
||||||
margin="0"
|
|
||||||
bleed="0" /></sodipodi:namedview><defs
|
|
||||||
id="defs1"><inkscape:path-effect
|
|
||||||
effect="simplify"
|
|
||||||
id="path-effect79"
|
|
||||||
is_visible="true"
|
|
||||||
lpeversion="1.3"
|
|
||||||
threshold="32.467532"
|
|
||||||
steps="1"
|
|
||||||
smooth_angles="360"
|
|
||||||
helper_size="0"
|
|
||||||
simplify_individual_paths="false"
|
|
||||||
simplify_just_coalesce="false" /><inkscape:path-effect
|
|
||||||
effect="simplify"
|
|
||||||
id="path-effect78"
|
|
||||||
is_visible="true"
|
|
||||||
lpeversion="1.3"
|
|
||||||
threshold="32.467532"
|
|
||||||
steps="1"
|
|
||||||
smooth_angles="360"
|
|
||||||
helper_size="0"
|
|
||||||
simplify_individual_paths="false"
|
|
||||||
simplify_just_coalesce="false" /><inkscape:path-effect
|
|
||||||
effect="simplify"
|
|
||||||
id="path-effect77"
|
|
||||||
is_visible="true"
|
|
||||||
lpeversion="1.3"
|
|
||||||
threshold="32.467532"
|
|
||||||
steps="1"
|
|
||||||
smooth_angles="360"
|
|
||||||
helper_size="0"
|
|
||||||
simplify_individual_paths="false"
|
|
||||||
simplify_just_coalesce="false" /><inkscape:path-effect
|
|
||||||
effect="simplify"
|
|
||||||
id="path-effect76"
|
|
||||||
is_visible="true"
|
|
||||||
lpeversion="1.3"
|
|
||||||
threshold="32.467532"
|
|
||||||
steps="1"
|
|
||||||
smooth_angles="360"
|
|
||||||
helper_size="0"
|
|
||||||
simplify_individual_paths="false"
|
|
||||||
simplify_just_coalesce="false" /><inkscape:path-effect
|
|
||||||
effect="simplify"
|
|
||||||
id="path-effect75"
|
|
||||||
is_visible="true"
|
|
||||||
lpeversion="1.3"
|
|
||||||
threshold="32.467532"
|
|
||||||
steps="1"
|
|
||||||
smooth_angles="360"
|
|
||||||
helper_size="0"
|
|
||||||
simplify_individual_paths="false"
|
|
||||||
simplify_just_coalesce="false" /><inkscape:path-effect
|
|
||||||
effect="simplify"
|
|
||||||
id="path-effect61"
|
|
||||||
is_visible="true"
|
|
||||||
lpeversion="1.3"
|
|
||||||
threshold="32.467532"
|
|
||||||
steps="1"
|
|
||||||
smooth_angles="360"
|
|
||||||
helper_size="0"
|
|
||||||
simplify_individual_paths="false"
|
|
||||||
simplify_just_coalesce="false" /><inkscape:path-effect
|
|
||||||
effect="simplify"
|
|
||||||
id="path-effect42"
|
|
||||||
is_visible="true"
|
|
||||||
lpeversion="1.3"
|
|
||||||
threshold="32.467532"
|
|
||||||
steps="1"
|
|
||||||
smooth_angles="360"
|
|
||||||
helper_size="0"
|
|
||||||
simplify_individual_paths="false"
|
|
||||||
simplify_just_coalesce="false" /></defs><g
|
|
||||||
inkscape:label="main"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer1"><path
|
|
||||||
style="display:inline;opacity:1;fill:#fff9f6;fill-opacity:1;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 164.23888,424.64081 -31.07097,26.05282 c 0,0 -5.19317,18.17613 25.96589,28.93342 31.15907,10.7573 213.29123,1.48377 213.29123,1.48377 0,0 52.30272,11.87012 80.86519,-37.09413 -0.37094,0.74188 -8.90259,19.28895 -8.90259,19.28895 l 1.48377,19.28894 c 0,0 57.73516,-9.41425 30.45647,-148.95524 4.19672,-9.44262 8.39344,-15.21311 8.39344,-15.21311 l -3.14754,-21.77049 c 0,0 5.65758,-40.33533 7.88323,-45.52851 2.22564,-5.19317 9.64447,-11.12823 9.64447,-11.12823 0,0 -2.78206,-20.40177 -6.67694,-26.15136 -0.74189,-3.33847 6.86241,-35.05395 2.04017,-58.05231 -7.04788,-11.87012 -4.82223,-11.87012 -18.918,-25.59495 0,0.37094 8.90259,-44.88389 -0.74188,-73.446367 -2.96753,0 -32.27189,-10.757296 -96.81567,23.740241 -0.37094,1.112824 -26.33683,-30.046242 -153.56969,-15.950474 -0.74188,0 -43.40012,-37.836009 -66.76942,-40.432598 -1.11283,-0.370941 -22.62742,-4.080354 -25.22401,48.964247 0,1.112824 -44.14201,15.950474 -56.01213,64.914721 -11.87012,48.96425 -11.128238,80.86519 -11.128238,80.86519 0,0 -18.904912,27.6632 -9.798121,25.03308 5.346826,-1.54421 4.975885,23.56023 4.975885,23.56023 l -2.225648,39.31977 c 0,0 -14.466709,35.23942 -14.83765,59.72155 -0.370942,24.48212 -0.741883,34.12659 4.822236,48.5933 5.564119,14.46671 20.772711,46.7386 42.658245,63.06002 8.902591,0.37094 18.918001,1.8547 18.918001,1.8547 l 3.70942,-20.77271 c 0,0 18.918,15.95048 32.27189,14.83765 13.35388,-1.11282 -0.74189,-0.74188 -0.74189,-0.74188 l -13.72482,-17.80518 z"
|
|
||||||
id="path2"
|
|
||||||
sodipodi:nodetypes="ccscccccccscccccccccscsccsscccsccc"
|
|
||||||
inkscape:label="整个人的边框" /><path
|
|
||||||
id="path91"
|
|
||||||
style="opacity:1;fill:#93d4fa;fill-opacity:1;stroke:#4c4f59;stroke-width:6.9;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 133.91016,110.16992 -69.738285,47.48047 18.263976,30.75433 -31.617492,55.67536 28.5625,17.0625 14.837891,-1.48438 2.226562,-55.64062 2.577791,-4.39109 18.193697,-21.94485 23.74023,-49.70703 z"
|
|
||||||
sodipodi:nodetypes="ccccccccccc" /><path
|
|
||||||
style="display:inline;opacity:1;fill:#ace0fe;fill-opacity:1;stroke:#4c4f59;stroke-width:6.54357;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 217.01397,405.82758 c 0,0 7.41728,-25.94462 41.16595,-27.07265 33.74866,-1.12802 53.77534,4.1361 53.77534,4.1361 0,0 11.12594,6.39215 17.80149,14.66435 6.67557,8.2722 28.92743,40.60895 41.53682,48.88115 12.60939,8.27219 15.20545,15.41636 15.20545,15.41636 l -1.85433,11.28027 c 0,0 -14.83458,10.15224 -30.78175,11.65627 -15.94717,1.50404 -147.60404,0 -147.60404,0 l -30.04002,-3.00807"
|
|
||||||
id="path71"
|
|
||||||
inkscape:label="衣服底色" /><path
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
|
||||||
d="m 363.54127,444.68178 c 0,0 -6.89429,4.06586 -4.86135,6.8059 2.03293,2.74004 5.92201,3.44715 5.92201,3.44715 0,0 4.5962,0.26516 5.12653,-3.53554 0.53033,-3.8007 -3.00521,-6.8059 -3.00521,-6.8059"
|
|
||||||
id="path60" /><path
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
|
||||||
d="m 349.67678,459.82583 c 0,0 4.5,-7.25 7,-5.5 2.5,1.75 -4.25,7.25 -4.25,7.25"
|
|
||||||
id="path59" /><path
|
|
||||||
id="path73-8"
|
|
||||||
style="fill:#ffffff;stroke-width:8.7;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
|
||||||
d="m 228.96985,465.10941 a 8.0341015,5.6995392 0 0 1 -8.0341,5.69953 8.0341015,5.6995392 0 0 1 -8.03411,-5.69953 8.0341015,5.6995392 0 0 1 8.03411,-5.69954 8.0341015,5.6995392 0 0 1 8.0341,5.69954 z" /><path
|
|
||||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#6c9cb2;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 220.85246,408.91803 c -0.22678,0.7374 0.77024,1.47887 0.20184,0.3709 -0.55918,-1.08999 0.45864,1.0858 0.58075,1.39596 2.10089,5.33666 1.96407,11.16153 3.60742,16.6426 0.30205,1.00744 0.58829,2.039 0.5936,3.09874"
|
|
||||||
id="path75"
|
|
||||||
inkscape:path-effect="#path-effect75"
|
|
||||||
inkscape:original-d="m 220.85246,408.91803 c 0.0874,0.34973 -0.0874,1.13662 0.26229,1.04918 0.34973,-0.0874 -0.42351,-1.37161 -0.26229,-1.04918 2.70315,5.4063 2.32215,10.60009 3.67213,16 0.44415,1.77662 1.31148,3.74741 1.31148,5.5082" /><path
|
|
||||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#6c9cb2;stroke-width:6.9;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 225.57377,436.98361 c -1.22213,4.37272 0.0774,9.22771 2.41807,12.85195 2.34183,3.626 0.85866,8.30929 2.56554,12.06608 1.71568,3.77616 2.21068,8.39349 -0.30117,12.16042 -1.9802,2.96963 -5.23922,5.13567 -6.38674,8.61546 0,0 -0.0736,0.66095 -0.0736,0.66095"
|
|
||||||
id="path76"
|
|
||||||
inkscape:path-effect="#path-effect76"
|
|
||||||
inkscape:original-d="m 225.57377,436.98361 c -1.27879,3.14891 0.64852,9.42818 3.40984,14.95082 0.92316,1.84632 0.76302,8.34571 1.57377,9.96721 1.05661,2.11323 2.18032,6.57378 1.31147,9.18033 -1.55473,4.66419 -8.07296,8.77008 -8.07296,12.2565"
|
|
||||||
sodipodi:nodetypes="csssc" /><path
|
|
||||||
style="opacity:1;fill:#fbf3ef;fill-opacity:1;stroke:none;stroke-width:5.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 259.14754,381.90164 c 0,0 8.91803,15.47541 9.18033,20.98361 0.26229,5.50819 32.26229,-1.83607 32.26229,-1.83607 0,0 -4.45901,-16.78688 -7.34426,-19.40983 -2.88524,-2.62296 -34.09836,0.26229 -34.09836,0.26229 z"
|
|
||||||
id="path89" /><path
|
|
||||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#6c9cb2;stroke-width:4.6;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 247.86885,386.36066 c 0.62439,0.76152 1.31219,1.32242 0.35251,0.10604 -0.98535,-1.24891 -0.63523,-0.56343 0.0935,0.24171 1.40155,1.54849 2.30926,3.40606 3.10338,5.34166 1.48771,3.62621 2.15649,7.53104 4.02075,11.02472 1.24886,2.3404 2.74915,4.53757 3.97084,6.89242"
|
|
||||||
id="path77"
|
|
||||||
inkscape:path-effect="#path-effect77"
|
|
||||||
inkscape:original-d="m 247.86885,386.36066 c 3.34102,3.56326 -3.68025,-4.79705 1.83607,2.09836 1.23825,1.54782 3.18087,7.73986 3.67213,9.70492 1.00623,4.02493 4.53306,8.80383 6.03279,11.80327" /><path
|
|
||||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#6c9cb2;stroke-width:4.6;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 306.62295,386.88525 c 1.62958,6.07854 4.04229,11.93568 6.81967,17.57377"
|
|
||||||
id="path78"
|
|
||||||
inkscape:path-effect="#path-effect78"
|
|
||||||
inkscape:original-d="m 306.62295,386.88525 c 1.57569,6.03983 4.44925,12.83291 6.81967,17.57377" /><path
|
|
||||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#6c9cb2;stroke-width:5.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 335.73771,431.21312 c 0.7696,3.76393 3.24862,7.26774 5.48726,10.89168 2.09754,3.39553 3.33574,7.13199 4.87926,10.7955 2.06975,4.91248 6.75146,8.7424 7.699,13.95386 0.92029,5.06165 5.24206,8.89669 5.27874,14.19502"
|
|
||||||
id="path79"
|
|
||||||
inkscape:path-effect="#path-effect79"
|
|
||||||
inkscape:original-d="m 335.73771,431.21312 c -1.15649,1.03916 3.66917,4.97179 4.45901,8.13114 0.96754,3.87016 3.8848,6.88344 4.72131,10.22951 1.09547,4.38186 5.88097,12.49359 8.39345,16.2623 0.80744,1.21116 0.89968,3.11084 1.57377,4.45901 1.7539,3.50782 4.19672,6.9619 4.19672,10.7541" /><path
|
|
||||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#494d55;stroke-width:5.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 258.62295,384.2623 c 0,0 8.13115,6.81967 11.27869,27.01639"
|
|
||||||
id="path80" /><path
|
|
||||||
style="opacity:1;fill:#fbf3ef;fill-opacity:1;stroke:#4c4f59;stroke-width:6.9;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 127.23285,329.0249 c 0,0 -2.59659,60.46343 13.72483,75.30108 8.16071,-8.16071 10.01541,-10.38636 10.01541,-10.38636 l 12.24107,8.53165 5.19317,-4.45129 2.22565,-19.65989 5.19318,-10.01542 5.93506,-12.24106 -8.53165,-8.53165 -15.95047,-19.28894 -3.33848,-4.82224 -4.45129,9.27353 c 0,0 -12.24106,0 -15.95048,-2.59659 -3.70941,-2.59658 -6.306,-1.11282 -6.306,-1.11282 z"
|
|
||||||
id="path90" /><path
|
|
||||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#494d55;stroke-width:5.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 291.67213,381.37705 c 0,0 7.08197,6.55738 10.22951,26.4918"
|
|
||||||
id="path81" /><path
|
|
||||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#494d55;stroke-width:5.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 270.16393,404.19672 c 0,0 16.00001,-3.93442 27.80328,-2.62295"
|
|
||||||
id="path82" /><path
|
|
||||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#494d55;stroke-width:5.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 253.90164,417.83607 c 0,0 37.2459,-13.11476 69.77049,-9.18033 2.09836,3.67213 5.77049,16.78688 -5.77049,20.98361 -4.72131,0 -19.14754,-1.57378 -22.55738,-0.5246 -3.40983,1.04919 -12.59016,0.5246 -18.36065,4.45902 -5.77049,3.93443 -14.68853,8.91803 -18.36066,3.14754 -3.67213,-5.77049 -4.72131,-12.32787 -4.72131,-12.32787 z"
|
|
||||||
id="path83" /><path
|
|
||||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#494d55;stroke-width:5.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 276.45902,482.62295 c 0,0 -9.70492,-1.57377 1.04918,-46.42623"
|
|
||||||
id="path84" /><path
|
|
||||||
style="opacity:1;fill:#fcf6fa;fill-opacity:1;stroke:#494d55;stroke-width:5.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 287.21312,432.78689 6.29508,50.88524 13.90164,-0.78688 -6.81968,-54.03279 z"
|
|
||||||
id="path88" /><path
|
|
||||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#494d55;stroke-width:5.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 287.73771,430.95082 5.77049,50.88525"
|
|
||||||
id="path85" /><path
|
|
||||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#494d55;stroke-width:5.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 300.32787,428.06557 c 0,0 2.09836,37.7705 7.34426,52.98361"
|
|
||||||
id="path86" /><path
|
|
||||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#494d55;stroke-width:5.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 313.18033,431.21312 c 0,0 7.60656,27.54098 10.22951,35.93442 2.62295,8.39344 -8.13115,15.73771 -8.13115,15.73771"
|
|
||||||
id="path87" /><path
|
|
||||||
style="opacity:1;fill:#bce4fd;fill-opacity:1;stroke:none;stroke-width:8.7;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 164.23888,424.64081 -31.07097,26.05282 5.00771,13.72483 20.95818,15.20859 17.08508,2.15431 -11.1287,-20.55238 -1.54637,-22.78395 z"
|
|
||||||
id="path74" /><path
|
|
||||||
id="path73"
|
|
||||||
style="opacity:1;fill:#ffffff;stroke-width:8.7;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
|
||||||
d="m 163.81755,451.10144 a 8.0341015,5.6995392 0 0 1 -8.0341,5.69954 8.0341015,5.6995392 0 0 1 -8.0341,-5.69954 8.0341015,5.6995392 0 0 1 8.0341,-5.69954 8.0341015,5.6995392 0 0 1 8.0341,5.69954 z" /><path
|
|
||||||
style="opacity:1;fill:#fff9f6;fill-opacity:1;stroke:none;stroke-width:8.7;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 338.36066,376.13115 c 0,0 1.83605,30.95081 -6.29509,51.40983 2.88525,1.04918 14.48107,0.26606 26.91267,-7.34238 12.4316,-7.60844 11.38242,-22.03467 11.38242,-22.03467 l 2.09836,28.85246 c 0,0 24.31867,-28.09873 24.2249,-52.60674 -0.0938,-24.50801 -1.66753,-26.08178 -1.66753,-26.08178 l -11.83407,-1.12685 z"
|
|
||||||
id="path72" /><path
|
|
||||||
style="opacity:1;fill:#fde9e7;fill-opacity:1;stroke:none;stroke-width:2.845;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 339.7822,389.85927 c 0,0 38.20695,-7.78977 56.38307,-25.96589 5.56412,8.53165 8.16071,32.64283 8.16071,32.64283 l 2.59659,12.98294 16.32141,-62.68907 -6.67694,-5.19318 -5.93506,-17.43424 c 0,0 -15.20859,15.2086 -27.44966,22.99836 -12.24106,7.78977 -34.12659,19.28895 -34.12659,19.28895 l -10.7573,3.70941 z"
|
|
||||||
id="path70" /><path
|
|
||||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#c3b4b0;stroke-width:2.845;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 347.01555,369.64297 c 0,0 2.22565,10.01541 -1.8547,19.84536 -4.08036,9.82994 -5.00771,9.459 -5.00771,9.459"
|
|
||||||
id="path69" /><path
|
|
||||||
style="opacity:1;fill:#fde9e7;fill-opacity:1;stroke:none;stroke-width:6.945;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 380.85246,480 c 0,0 44.59016,-17.31147 59.27869,-87.08197 6.81967,-2.62295 34.09836,-14.68852 39.34426,-25.70491 2.62295,-9.96722 -3.67213,-32 -3.67213,-32 l -24.65574,0.52459 -26.7541,4.72131 -4.72131,11.54098 -8.39344,52.45902 -13.11475,28.32787 -15.73771,36.19672 z"
|
|
||||||
id="path68" /><path
|
|
||||||
style="fill:none;fill-opacity:1;stroke:#4c4f59;stroke-width:8.74203;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 147.6556,277.03379 c 0,0 -11.12655,104.13607 127.58449,103.38689 138.71104,-0.74917 149.83759,-100.39017 149.83759,-100.39017"
|
|
||||||
id="path41"
|
|
||||||
inkscape:label="脸蛋" /><path
|
|
||||||
style="opacity:1;fill:#fff9f6;fill-opacity:1;stroke:none;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 123.27869,147.93443 304.78688,21.50819 -24.65573,-40.39344 c 0,0 44.06557,-58.754098 50.88524,-60.327868 6.81967,-1.573771 15.73771,-9.967214 15.73771,-9.967214 l -13.11476,-3.672131 -77.11475,22.032787 c 0,0 -122.7541,-33.57377 -168.39344,-6.295082 -45.63935,27.278689 -69.7705,46.163938 -69.7705,46.163938 z"
|
|
||||||
id="path33"
|
|
||||||
inkscape:label="头发底色2" /><path
|
|
||||||
style="fill:#fde9e7;fill-opacity:1;stroke:none;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 331.44501,66.651771 16.22882,30.84274 47.82991,31.367399 6.30629,1.07795 26.88325,-35.335399 -32.10312,-22.211018 -18.88524,5.245901 -30.42623,-11.540983 z"
|
|
||||||
id="path35"
|
|
||||||
inkscape:label="头发部件111" /><path
|
|
||||||
style="opacity:1;fill:#ace0fe;fill-opacity:1;stroke:none;stroke-width:14.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 431.03375,175.82616 17.06329,-17.0633 50.44802,71.96261 -5.93506,17.80518 -33.38472,18.54706 -12.612,-16.32142 17.80518,-11.12823 z"
|
|
||||||
id="path29"
|
|
||||||
inkscape:label="蓝色发卡上" /><path
|
|
||||||
style="opacity:1;fill:#ace0fe;fill-opacity:1;stroke:#4c4f59;stroke-width:6.945;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 465.90223,261.88453 c 0,0 35.61036,54.52837 2.96753,73.44637 -25.22401,4.08036 -48.22237,2.22565 -48.22237,2.22565 l 10.38636,-26.33683 21.51459,-41.91636 z"
|
|
||||||
id="path66"
|
|
||||||
inkscape:label="蓝色发卡下" /><path
|
|
||||||
style="opacity:1;fill:#febdc7;fill-opacity:1;stroke:none;stroke-width:4.6;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 471.86885,57.704918 c 0,0 17.04918,68.983602 -11.01639,110.426232 -7.34426,0.26229 -9.18033,-10.49181 -9.18033,-10.49181 l 2.09836,-6.55737 -1.04918,-9.18033 2.36066,-10.7541 1.83606,-5.5082 -6.29508,-6.03278 -6.55738,-5.2459 -2.88524,-5.24591 2.88524,-5.2459 -2.62295,-5.245899 -4.19672,-2.885246 -3.14754,-1.57377 5.2459,-11.540984 11.80328,-7.344262 z"
|
|
||||||
id="path64" /><path
|
|
||||||
id="path57"
|
|
||||||
style="opacity:1;fill:#ff1c1c;fill-opacity:0.0509804;stroke-width:3.92397;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
|
||||||
d="m 244.26691,326.81357 a 35.587568,21.480417 0 0 1 -35.58757,21.48042 35.587568,21.480417 0 0 1 -35.58757,-21.48042 35.587568,21.480417 0 0 1 35.58757,-21.48042 35.587568,21.480417 0 0 1 35.58757,21.48042 z" /><path
|
|
||||||
id="path57-1"
|
|
||||||
style="fill:#ff1c1c;fill-opacity:0.05044398;stroke-width:3.65169;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
|
||||||
d="m 355.20429,336.75188 a 31.611719,20.942554 0 0 1 -31.61171,20.94255 31.611719,20.942554 0 0 1 -31.61172,-20.94255 31.611719,20.942554 0 0 1 31.61172,-20.94255 31.611719,20.942554 0 0 1 31.61171,20.94255 z" /><path
|
|
||||||
style="display:inline;opacity:1;fill:#fbf3ef;fill-opacity:1;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 188.32787,343.60656 c 0,0 12.06557,-28.32787 15.7377,6.29508 5.24591,-8.91803 9.96722,-17.31148 17.31148,-8.39344 6.81967,2.09836 15.21311,7.34426 4.72131,22.03278 -10.4918,14.68853 16.33814,-18.37136 15.445,-1.63289 -0.72099,13.51206 -24.52214,46.81129 -24.52214,46.81129 0,0 -6.33782,64.18547 -33.41466,70.23144 -36.5485,8.16089 -12.06558,-103.86885 -12.06558,-103.86885 z"
|
|
||||||
id="path1"
|
|
||||||
sodipodi:nodetypes="cccsscscc"
|
|
||||||
inkscape:label="右手" /><path
|
|
||||||
id="path5"
|
|
||||||
style="fill:#fbe7e5;stroke-width:10.4;stroke-linecap:round;stroke-miterlimit:1.3;paint-order:fill markers stroke;fill-opacity:0.40581462"
|
|
||||||
inkscape:label="右手踝阴影"
|
|
||||||
d="m 193.63133,383.92419 a 12.612,12.241061 0 0 1 -12.612,12.24107 12.612,12.241061 0 0 1 -12.612,-12.24107 12.612,12.241061 0 0 1 12.612,-12.24106 12.612,12.241061 0 0 1 12.612,12.24106 z" /><path
|
|
||||||
style="fill:#fee4e0;fill-opacity:0.42411327;stroke:none;stroke-width:10.4;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 179.09741,356.52608 14.09576,-20.77271 7.04789,1.48377 0.74188,13.72482 11.49918,-11.49918 9.27353,4.08036 8.53165,5.93506 -5.56412,14.83765 13.72483,-4.82224 -3.33847,17.80518 -47.85143,-27.44965 z"
|
|
||||||
id="path9"
|
|
||||||
inkscape:label="右手手指阴影" /><path
|
|
||||||
style="display:inline;fill:none;fill-opacity:1;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 188.32787,343.60656 c 0,0 12.06557,-28.32787 15.7377,6.29508 5.24591,-8.91803 9.96722,-17.31148 17.31148,-8.39344 6.81967,2.09836 15.21311,7.34426 4.72131,22.03278 -10.4918,14.68853 16.33814,-18.37136 15.445,-1.63289 -0.72099,13.51206 -24.52214,46.81129 -24.52214,46.81129 0,0 -6.33782,64.18547 -33.41466,70.23144 -36.5485,8.16089 -12.06558,-103.86885 -12.06558,-103.86885 z"
|
|
||||||
id="path1-6"
|
|
||||||
sodipodi:nodetypes="cccsscscc"
|
|
||||||
inkscape:label="右手遮挡" /><path
|
|
||||||
style="opacity:1;fill:#fff9f6;fill-opacity:1;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 137.44262,123.2787 c 0,0 49.33852,-48.649769 86.97684,-58.734919 0.10513,0.76769 -120.16719,-122.15824 -86.97684,58.734919 z"
|
|
||||||
id="path3"
|
|
||||||
inkscape:label="头发部件19 左猫耳" /><path
|
|
||||||
style="fill:#fde9e7;fill-opacity:1;stroke-width:1.18016;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
|
||||||
d="m 198.85246,42 -63.6342,39.016573 3.84123,40.939237 L 184.82787,87 224,64.5 Z"
|
|
||||||
id="path58"
|
|
||||||
sodipodi:nodetypes="cccccc" /><path
|
|
||||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0.465732;stroke:#a18f90;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
id="path4"
|
|
||||||
d="m 404.96447,131.7729 c 0.52512,-0.76931 1.0357,-1.54875 1.57536,-2.30793 2.82418,-3.97291 5.82035,-7.82036 8.67758,-11.76948 2.39309,-3.30762 3.93978,-5.54326 6.29133,-8.89998 5.32612,-7.58044 11.18023,-14.760796 17.20894,-21.78699 3.02173,-3.55064 6.06655,-7.098864 9.47662,-10.289082 1.96198,-1.835492 4.21906,-3.672787 6.27415,-5.392122 5.10823,-4.357455 10.66931,-8.109574 16.22169,-11.862072 0,0 -2.92504,-1.864176 -2.92504,-1.864176 v 0 c -5.41592,3.900149 -10.90348,7.705144 -15.99184,12.040062 -1.99887,1.645368 -4.44389,3.611151 -6.36286,5.369248 -3.45237,3.162941 -6.48944,6.731577 -9.48752,10.316639 -5.95526,7.087894 -11.63254,14.399231 -17.00034,21.944173 -5.47138,7.72885 -10.96322,15.45542 -17.11251,22.66867 z"
|
|
||||||
inkscape:label="头发部件18" /><path
|
|
||||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0.465732;stroke:#a18f90;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
id="path6"
|
|
||||||
d="m 431.7055,96.885136 c 0.68345,-0.01683 1.36993,0.008 2.05351,0.03239 1.26616,0.04274 2.52519,0.175792 3.77564,0.374299 1.34943,0.219358 2.67703,0.547853 3.99756,0.89747 1.30448,0.342355 2.58658,0.760225 3.8587,1.206247 0.34787,0.132122 0.88919,0.332358 1.23164,0.483411 0.1893,0.0835 0.73774,0.373687 0.5597,0.268307 -4.92017,-2.912189 -2.83776,-1.86942 -2.01609,-0.911603 0.4665,0.942833 -0.25414,1.810973 -0.83258,2.515753 -1.05305,1.11546 -2.37988,1.92349 -3.65077,2.76486 -0.70455,0.52564 -1.84263,0.99551 -2.02707,1.9792 -0.13577,0.72412 0.14689,1.00706 0.45808,1.64499 0.25805,0.31484 0.49646,0.64686 0.77415,0.94453 0.63085,0.67625 1.54885,1.45348 2.25243,2.03623 1.28413,1.06359 2.61277,2.07518 3.92585,3.10243 2.22765,1.73231 4.46676,3.44711 6.4408,5.4696 1.24472,1.36548 2.33589,2.86124 3.10757,4.54226 0.57402,1.31239 0.61894,2.68201 0.46471,4.08008 -0.15236,1.542 -1.01177,2.74753 -1.96982,3.90485 -1.25092,1.39253 -2.70297,2.57966 -3.98322,3.94302 -0.61776,0.76458 -1.19371,1.60995 -1.3359,2.60746 -0.0756,0.53059 -0.0152,0.87319 0.031,1.40055 0.18192,1.34638 0.59785,2.64226 1.03422,3.92358 0.46172,1.36123 1.10075,2.65093 1.71056,3.94949 0.57934,1.12566 0.88081,2.32829 0.97901,3.58284 0.0285,1.01273 -0.14083,2.01608 -0.35002,3.00302 -0.18321,0.8594 -0.39147,1.62037 -0.92358,2.32955 -0.69384,0.72591 -1.52468,1.30006 -2.31082,1.91913 0,0 3.13155,1.96912 3.13155,1.96912 v 0 c 0.80747,-0.64335 1.66352,-1.23803 2.37418,-1.9921 0.58364,-0.77755 0.80933,-1.52536 1.00495,-2.4688 0.21825,-1.01967 0.38801,-2.05558 0.38421,-3.10149 -0.0761,-1.29512 -0.34601,-2.55043 -0.93895,-3.71574 -0.60728,-1.2882 -1.24849,-2.56502 -1.72185,-3.91075 -0.43768,-1.25837 -0.85635,-2.529 -1.07055,-3.84806 -0.053,-0.43231 -0.12968,-0.85315 -0.0855,-1.29165 0.0948,-0.94059 0.64999,-1.73394 1.21835,-2.45227 1.27656,-1.3752 2.72864,-2.57131 3.98645,-3.96542 1.00742,-1.20382 1.92485,-2.46496 2.104,-4.07351 0.17452,-1.45227 0.1673,-2.88462 -0.39573,-4.26425 -0.74413,-1.73945 -1.85028,-3.26977 -3.10544,-4.6809 -0.76822,-0.80351 -0.91233,-0.98548 -1.76144,-1.74089 -1.5106,-1.3439 -3.15419,-2.52947 -4.72414,-3.80078 -1.30934,-1.02359 -2.63319,-2.03185 -3.9193,-3.08473 -0.71006,-0.5813 -1.57278,-1.30278 -2.22387,-1.96185 -0.27049,-0.2738 -0.51217,-0.57463 -0.76826,-0.86194 -0.2502,-0.39692 -0.62512,-0.79772 -0.57512,-1.31981 0.0792,-0.82671 1.33713,-1.38331 1.899,-1.81756 1.29884,-0.86671 2.65474,-1.69887 3.73107,-2.84377 0.6873,-0.83746 1.4042,-1.79633 1.075,-2.93575 -0.0816,-0.13585 -0.14456,-0.28482 -0.24481,-0.40755 -0.1097,-0.13429 -0.23095,-0.26613 -0.3791,-0.356228 -1.82458,-1.109627 -3.48552,-2.166773 -5.4739,-2.822822 -1.27815,-0.438878 -2.56689,-0.847162 -3.87638,-1.183261 -1.32827,-0.341807 -2.66392,-0.6605 -4.021,-0.867076 -1.26646,-0.185139 -2.54065,-0.306124 -3.82011,-0.354118 -0.68835,-0.02484 -1.39159,-0.0088 -2.07079,-0.120664 z"
|
|
||||||
inkscape:label="头发部件17" /><path
|
|
||||||
style="display:inline;opacity:1;fill:#1a1a1a;fill-opacity:0.465732;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
id="path7"
|
|
||||||
d="m 472.56515,132.89174 c 0.0148,0.77317 -0.10076,1.54619 -0.19578,2.31209 -0.22663,1.66469 -0.65036,3.29152 -1.09324,4.90909 -0.61155,2.16519 -1.31643,4.30298 -2.07388,6.4211 -0.85906,2.40245 -1.88628,4.73722 -3.04362,7.0095 -1.12519,2.18837 -2.40662,4.28829 -3.77255,6.3331 -1.20044,1.78353 -2.51232,3.48695 -3.85961,5.16088 -0.77567,0.94691 -1.56273,1.88443 -2.34905,2.82249 0,0 3.09439,1.85764 3.09439,1.85764 v 0 c 0.78058,-0.94434 1.56325,-1.88703 2.331,-2.84187 1.33972,-1.69155 2.6509,-3.40675 3.84535,-5.20534 1.36483,-2.06191 2.64248,-4.17929 3.77385,-6.37958 1.16439,-2.28454 2.19746,-4.6314 3.07763,-7.04092 0.77266,-2.11878 1.49327,-4.25705 2.12518,-6.4224 0.46749,-1.62041 0.91079,-3.25003 1.1854,-4.9163 0.11547,-0.76691 0.21504,-1.53597 0.32143,-2.30416 z"
|
|
||||||
inkscape:label="头发部件16" /><path
|
|
||||||
style="display:inline;opacity:1;fill:#fff9f6;fill-opacity:1;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 322.62295,86.032787 c 0,0 75.43341,26.120553 110.16394,89.180323 33.44261,60.72132 33.04918,62.42623 33.04918,62.42623 0,0 0.52459,8.39345 -19.93443,11.54099 -2.62295,0 14.68852,30.95082 19.93443,37.2459 -14.68853,7.34426 -16.78689,8.91803 -16.78689,8.91803 0,0 6.29508,7.86885 4.72131,15.73771 -17.31147,-0.52459 -22.55738,-1.04918 -22.55738,-1.04918 0,0 -4.19672,17.31147 -15.7377,33.04918 C 410.22951,333.11475 410.22951,320 410.22951,320 c 0,0 -24.13115,36.72131 -72.39344,41.44262 -1.57377,-2.62295 12.59016,-14.16393 17.83606,-28.32787 -11.54098,1.57377 -14.68852,2.09836 -14.68852,2.09836 l 10.4918,-16.78688 c 0,0 11.01639,-24.13115 9.44262,-28.32787 -1.04918,-2.62295 -32,-57.70492 -32,-57.70492 0,0 -24.65573,33.04918 -43.54098,49.83607 -6.81967,-4.19672 -13.63934,-14.68853 -13.63934,-14.68853 l -12.59017,11.54099 c 0,0 -48.43778,-50.76436 -42.0945,-90.18318 -3.14754,12.06557 0.12729,56.60941 -5.6432,62.3799 -5.24591,-2.09836 -16.2623,-14.68853 -14.16394,-40.91803 -0.52459,-0.52459 -29.90164,41.44262 -29.90164,41.44262 0,0 -4.19672,48.78688 16.2623,73.44262 1.57377,2.62295 -1.57377,16.2623 -31.47541,-2.62295 1.04918,-1.57377 -1.04918,9.96721 -1.04918,9.96721 0,0 -46.68853,2.09836 -45.63935,-60.85245 -2.62295,-2.62296 -13.639341,34.09836 -13.639341,34.09836 0,0 -24.131151,-32 -0.52459,-92.85246 11.803281,-30.42623 23.344261,-52.85246 31.934421,-67.67213 8.59017,-14.81968 14.22951,-22.03279 14.22951,-22.03279"
|
|
||||||
id="path8"
|
|
||||||
sodipodi:nodetypes="csccccccccccccccccccccccccccssc"
|
|
||||||
inkscape:label="头发部件15 头发底色" /><path
|
|
||||||
id="path36"
|
|
||||||
style="fill:#fffdfc;stroke-width:7.4278;stroke-linecap:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
|
||||||
inkscape:label="头发打光"
|
|
||||||
d="m 355.67214,146.88524 a 91.803276,41.967213 0 0 1 -91.80327,41.96721 91.803276,41.967213 0 0 1 -91.80328,-41.96721 91.803276,41.967213 0 0 1 91.80328,-41.96721 91.803276,41.967213 0 0 1 91.80327,41.96721 z" /><path
|
|
||||||
style="fill:#fde9e7;fill-opacity:1;stroke:none;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 349.37705,329.44262 c 0,0 71.34426,-41.96721 78.68852,-91.27868 0,1.04918 26.22951,28.32786 26.22951,28.32786 l 11.54099,24.13115 -15.73771,7.34426 2.09836,13.63935 -22.03279,-1.04918 -12.59016,30.42623 -12.59016,-17.83607 -23.08197,26.22951 -45.11475,14.68852 z"
|
|
||||||
id="path37"
|
|
||||||
inkscape:label="头发阴影"
|
|
||||||
sodipodi:nodetypes="cccccccccccc" /><path
|
|
||||||
style="fill:#fde9e7;fill-opacity:1;stroke:none;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 325.2459,144.78689 c 0,0 10.49181,44.06557 2.09836,78.68852 0,10.4918 33.57377,60.85246 33.57377,60.85246 0,0 14.68853,-71.34426 -25.18032,-132.19672 -8.39345,-5.2459 -10.49181,-7.34426 -10.49181,-7.34426 z"
|
|
||||||
id="path38"
|
|
||||||
inkscape:label="头发阴影2" /><path
|
|
||||||
style="fill:#fde9e7;fill-opacity:1;stroke:none;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 186.7541,166.81967 23.08197,1.04918 c 0,0 -14.68853,26.22951 -13.63935,46.16394 -2.09836,-3.14754 -22.03279,31.47541 -22.03279,31.47541 0,0 -9.44262,-14.68853 -3.14754,-50.36066 9.44263,-16.78688 15.73771,-28.32787 15.73771,-28.32787 z"
|
|
||||||
id="path39"
|
|
||||||
inkscape:label="头发阴影" /><path
|
|
||||||
id="path36-5"
|
|
||||||
style="fill:#fffdfc;stroke-width:6.94619;stroke-linecap:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
|
||||||
inkscape:label="头发打光"
|
|
||||||
transform="matrix(0.99965843,-0.02613486,0,1,0,0)"
|
|
||||||
d="m 340.28017,112.23744 a 80.289719,41.967213 0 0 1 -80.28972,41.96721 80.289719,41.967213 0 0 1 -80.28972,-41.96721 80.289719,41.967213 0 0 1 80.28972,-41.967211 80.289719,41.967213 0 0 1 80.28972,41.967211 z" /><path
|
|
||||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#a18f90;stroke-width:7.7;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 138.62295,122.4918 c 0,0 53.63935,-52.983604 92.98361,-58.229505"
|
|
||||||
id="path65"
|
|
||||||
sodipodi:nodetypes="cc" /><path
|
|
||||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#c3b4b0;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 436.45902,268.06557 c 0,0 3.67213,18.36066 -2.62295,42.49181"
|
|
||||||
id="path17"
|
|
||||||
inkscape:label="头发部件7" /><path
|
|
||||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#c3b4b0;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 418.09836,305.83607 -8.91803,17.83606"
|
|
||||||
id="path16"
|
|
||||||
inkscape:label="头发部件8" /><path
|
|
||||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#c3b4b0;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 377.70492,300.59016 c 0,0 2.62295,12.06558 -22.03279,32"
|
|
||||||
id="path15"
|
|
||||||
inkscape:label="头发部件9" /><path
|
|
||||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#c3b4b0;stroke-width:4.2;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 284.32787,97.573771 c 0,0 12.06557,39.344259 -27.80328,119.081969 0.52459,0 53.5082,-38.81967 56.13115,-89.18033"
|
|
||||||
id="path10"
|
|
||||||
inkscape:label="头发部件14" /><path
|
|
||||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#c3b4b0;stroke-width:5.1;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 272.78689,87.081967 c 0,0 13.63934,-8.393442 18.88524,-0.52459 17.17267,10.839383 13.03281,30.236103 24.19672,44.393443 1.79263,1.85342 7.1718,-0.78131 7.80328,1.77049 4.88126,19.72513 14.81582,50.80617 4.32401,100.11764"
|
|
||||||
id="path11"
|
|
||||||
sodipodi:nodetypes="csssc"
|
|
||||||
inkscape:label="头发部件13" /><path
|
|
||||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#c3b4b0;stroke-width:4.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 171.00392,241.8537 c 0,0 -28.56248,-60.09248 92.36438,-147.634619 2.22564,0.741882 -59.35061,54.157419 -66.02755,111.282379"
|
|
||||||
id="path12"
|
|
||||||
inkscape:label="头发部件12" /><path
|
|
||||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#c3b4b0;stroke-width:3;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 327.34426,143.7377 c 0,0 25.18033,20.45902 32,65.57378"
|
|
||||||
id="path13"
|
|
||||||
inkscape:label="头发部件11" /><path
|
|
||||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 362.4918,268.59016 -3.14754,19.93443"
|
|
||||||
id="path14"
|
|
||||||
inkscape:label="头发部件10" /><path
|
|
||||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 421.77049,344.13115 c 0,0 -2.62295,58.22951 -33.57377,111.7377 -30.95082,53.5082 38.29508,15.73771 38.29508,15.73771"
|
|
||||||
id="path18"
|
|
||||||
inkscape:label="头发部件6" /><path
|
|
||||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 395.01639,348.32787 12.06558,60.32787"
|
|
||||||
id="path19"
|
|
||||||
inkscape:label="头发部件5" /><path
|
|
||||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 371.93443,423.86885 c 0,0 30.95082,-25.70492 24.65573,-73.96721"
|
|
||||||
id="path20"
|
|
||||||
inkscape:label="头发部件4" /><path
|
|
||||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 332.06557,427.54098 c 0,0 36.72132,1.57377 38.81968,-29.37705"
|
|
||||||
id="path21"
|
|
||||||
inkscape:label="头发部件3" /><path
|
|
||||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 372.45902,427.01639 -2.09836,-28.85246"
|
|
||||||
id="path22"
|
|
||||||
inkscape:label="头发部件2" /><path
|
|
||||||
style="fill:#93cefc;fill-opacity:1;stroke:#4d4e59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 377.50929,228.61046 c 0,0 24.13115,1.57377 24.13115,10.49181 -0.52459,3.67213 -11.54099,17.83606 -23.60656,18.88524 -2.09836,-1.57377 -0.52459,-29.37705 -0.52459,-29.37705 z"
|
|
||||||
id="path34"
|
|
||||||
sodipodi:nodetypes="cccc"
|
|
||||||
inkscape:label="兔子发卡后" /><path
|
|
||||||
style="opacity:1;fill:#fffdfe;fill-opacity:1;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 350.95082,235.54098 c 0,0 -6.55738,-22.03278 1.04918,-22.03278 7.60656,0 13.37705,13.90164 13.37705,13.90164 0,0 5.05941,-17.72738 9.96722,-15.21311 11.79758,6.04389 3.40983,21.77048 3.40983,21.77048 0,0 18.62295,25.96722 -12.06558,27.27869 -30.16717,1.28919 -15.7377,-25.70492 -15.7377,-25.70492 z"
|
|
||||||
id="path24"
|
|
||||||
sodipodi:nodetypes="cscscsc"
|
|
||||||
inkscape:label="兔子发卡" /><path
|
|
||||||
style="display:inline;opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 332.06557,425.96721 c 0,0 10.49181,-25.18032 6.29509,-49.83606"
|
|
||||||
id="path23"
|
|
||||||
inkscape:label="头发部件" /><path
|
|
||||||
style="display:inline;opacity:1;fill:#e85240;fill-opacity:1;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 192.2623,268.85246 c 0,0 -15.47541,22.03279 -2.62296,35.40984 42.7541,0.26229 43.27869,0.52459 43.27869,0.52459 0,0 11.27869,-8.39345 5.5082,-32 -25.44262,-9.96722 -46.16393,-3.93443 -46.16393,-3.93443 z"
|
|
||||||
id="path25"
|
|
||||||
inkscape:label="左眼白" /><path
|
|
||||||
style="opacity:1;fill:#fbb579;fill-opacity:1;stroke:none;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 188.06557,301.11475 c -0.44151,-8.78192 13.4528,-14.01636 22.29509,-14.42623 8.67244,-0.402 23.2398,3.38526 23.08196,12.06558 -0.0775,4.26198 -11.54098,5.5082 -11.54098,5.5082 0,0 -33.2673,8.16555 -33.83607,-3.14755 z"
|
|
||||||
id="path27"
|
|
||||||
sodipodi:nodetypes="saacs"
|
|
||||||
inkscape:label="左眼瞳" /><path
|
|
||||||
id="path26"
|
|
||||||
style="display:inline;opacity:1;fill:#ffffff;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
|
||||||
inkscape:label="眼光"
|
|
||||||
d="m 217.96721,273.04919 a 7.8688526,7.0819674 0 0 1 -7.86885,7.08197 7.8688526,7.0819674 0 0 1 -7.86885,-7.08197 7.8688526,7.0819674 0 0 1 7.86885,-7.08196 7.8688526,7.0819674 0 0 1 7.86885,7.08196 z" /><path
|
|
||||||
style="fill:none;fill-opacity:1;stroke:#ebc2bf;stroke-width:2.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 174.16393,245.5082 c 15.08644,-4.76649 31.53934,-7.45293 47.09358,-3.51527 3.29779,0.83486 6.51821,1.99257 9.56216,3.51527"
|
|
||||||
id="path42"
|
|
||||||
inkscape:path-effect="#path-effect42"
|
|
||||||
inkscape:original-d="m 174.16393,245.5082 c 11.60252,-3.79212 36.99244,-9.83166 56.65574,0"
|
|
||||||
transform="translate(0,4)" /><path
|
|
||||||
style="opacity:1;fill:#fde9e7;fill-opacity:1;stroke:none;stroke-width:6.945;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 477.77235,132.79697 c 0,0 13.35388,10.01542 14.83765,17.43424 1.48376,7.41883 1.48376,57.4959 1.48376,57.4959 l -4.45129,1.11282 -30.78813,-37.46507 c 0,0 8.90259,-14.46671 11.12824,-22.62741 2.22565,-8.16071 7.78977,-15.95048 7.78977,-15.95048 z"
|
|
||||||
id="path67" /><path
|
|
||||||
style="fill:#e85240;fill-opacity:0;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 192.2623,268.85246 c 0,0 -15.47541,22.03279 -2.62296,35.40984 42.7541,0.26229 43.27869,0.52459 43.27869,0.52459 0,0 11.27869,-8.39345 5.5082,-32 -25.44262,-9.96722 -46.16393,-3.93443 -46.16393,-3.93443 z"
|
|
||||||
id="path25-9"
|
|
||||||
inkscape:label="左眼遮挡上" /><path
|
|
||||||
style="display:inline;fill:#e85240;fill-opacity:1;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 325.52138,272.81871 c 17.02505,1.06627 38.81967,23.08196 17.83607,48.26229 -1.57377,0.52459 -48.98481,-5.30599 -49.57378,-9.18033 -0.80561,-5.29953 -7.95666,-41.56799 31.73771,-39.08196 z"
|
|
||||||
id="path40"
|
|
||||||
sodipodi:nodetypes="scss"
|
|
||||||
inkscape:label="右眼白" /><path
|
|
||||||
style="fill:#fbb579;fill-opacity:1;stroke:none;stroke-width:7.14054;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 293.04592,306.32136 c 2.55323,-8.53469 20.43105,-9.52962 30.78942,-7.36618 10.1594,2.12188 25.66784,9.9581 22.45763,18.22228 -1.5762,4.05766 -15.25453,1.93723 -15.25453,1.93723 0,0 -41.28165,-1.79873 -37.99252,-12.79333 z"
|
|
||||||
id="path27-0"
|
|
||||||
sodipodi:nodetypes="saacs"
|
|
||||||
inkscape:label="左眼瞳" /><path
|
|
||||||
style="display:inline;fill:none;fill-opacity:1;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 325.52138,272.81871 c 17.02505,1.06627 38.81967,23.08196 17.83607,48.26229 -1.57377,0.52459 -48.98481,-5.30599 -49.57378,-9.18033 -0.80561,-5.29953 -7.95666,-41.56799 31.73771,-39.08196 z"
|
|
||||||
id="path40-2"
|
|
||||||
sodipodi:nodetypes="scss"
|
|
||||||
inkscape:label="右眼遮挡上" /><path
|
|
||||||
style="fill:none;fill-opacity:1;stroke:#ebc2bf;stroke-width:2.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 309.32819,258.43459 c 0,0 26.2295,-5.2459 36.72131,4.19672"
|
|
||||||
id="path43" /><path
|
|
||||||
id="path26-9"
|
|
||||||
style="display:inline;fill:#ffffff;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
|
||||||
inkscape:label="眼光"
|
|
||||||
d="m 329.6342,284.99301 a 7.8688526,7.0819674 0 0 1 -7.86885,7.08197 7.8688526,7.0819674 0 0 1 -7.86885,-7.08197 7.8688526,7.0819674 0 0 1 7.86885,-7.08197 7.8688526,7.0819674 0 0 1 7.86885,7.08197 z" /><path
|
|
||||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#4c4f59;stroke-width:14.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 170.4918,266.4918 c 0,0 32,-19.93442 70.29509,-2.09836"
|
|
||||||
id="path28"
|
|
||||||
inkscape:label="眉毛" /><path
|
|
||||||
style="fill:none;fill-opacity:1;stroke:#4c4f59;stroke-width:14.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 292.9971,274.17683 c 0,0 35.55098,-12.55026 69.08832,13.13797"
|
|
||||||
id="path28-2"
|
|
||||||
inkscape:label="眉毛" /><path
|
|
||||||
style="display:inline;opacity:1;fill:none;fill-opacity:0;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 431.03375,176.1971 18.17612,-17.43424 41.54542,54.15742"
|
|
||||||
id="path30"
|
|
||||||
inkscape:label="蓝色发卡遮挡上" /><path
|
|
||||||
style="opacity:1;fill:none;fill-opacity:0;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 454.0321,267.44865 43.21466,-23.92571"
|
|
||||||
id="path31"
|
|
||||||
sodipodi:nodetypes="cc"
|
|
||||||
inkscape:label="蓝色发卡遮挡下" /><path
|
|
||||||
style="display:inline;fill:#fff9f6;fill-opacity:0;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 164.23888,424.64081 -31.07097,26.05282 c 0,0 -5.19317,18.17613 25.96589,28.93342 31.15907,10.7573 213.29123,1.48377 213.29123,1.48377 0,0 52.30272,11.87012 80.86519,-37.09413 -0.37094,0.74188 -8.90259,19.28895 -8.90259,19.28895 l 1.48377,19.28894 c 0,0 57.73516,-9.41425 30.45647,-148.95524 4.19672,-9.44262 8.39344,-15.21311 8.39344,-15.21311 l -3.14754,-21.77049 c 0,0 5.65758,-40.33533 7.88323,-45.52851 2.22564,-5.19317 8.51558,-6.35116 9.64447,-11.12823 2.06907,-8.75561 -2.78206,-20.40177 -6.67694,-26.15136 -0.74189,-3.33847 6.86241,-35.05395 2.04017,-58.05231 -7.04788,-11.87012 -4.82223,-11.87012 -18.918,-25.59495 0,0.37094 8.90259,-44.88389 -0.74188,-73.446367 -2.96753,0 -32.27189,-10.757296 -96.81567,23.740241 -0.37094,1.112824 -26.33683,-30.046242 -153.56969,-15.950474 -0.74188,0 -43.40012,-37.836009 -66.76942,-40.432598 -1.11283,-0.370941 -22.62742,-4.080354 -25.22401,48.964247 0,1.112824 -44.14201,15.950474 -56.01213,64.914721 -11.87012,48.96425 -11.128238,80.86519 -11.128238,80.86519 0,0 -18.904912,27.6632 -9.798121,25.03308 5.346826,-1.54421 4.975885,23.56023 4.975885,23.56023 l -2.225648,39.31977 c 0,0 -14.466709,35.23942 -14.83765,59.72155 -0.370942,24.48212 -0.741883,34.12659 4.822236,48.5933 5.564119,14.46671 20.772711,46.7386 42.658245,63.06002 8.902591,0.37094 18.918001,1.8547 18.918001,1.8547 l 3.70942,-20.77271 c 0,0 18.918,15.95048 32.27189,14.83765 13.35388,-1.11282 -0.74189,-0.74188 -0.74189,-0.74188 l -13.72482,-17.80518 z"
|
|
||||||
id="path2-4"
|
|
||||||
sodipodi:nodetypes="ccscccccccsaccccccccscsccsscccsccc"
|
|
||||||
inkscape:label="上发层" /><path
|
|
||||||
style="fill:none;fill-opacity:1;stroke:#c3b4b0;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 197.5082,220.06557 18.36065,-12.32786"
|
|
||||||
id="path46" /><path
|
|
||||||
style="fill:none;fill-opacity:1;stroke:#c3b4b0;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 327.54114,234.24941 c 0,0 -6.27192,4.90452 -15.85821,-0.991"
|
|
||||||
id="path47"
|
|
||||||
sodipodi:nodetypes="cc" /><path
|
|
||||||
style="display:inline;fill:none;fill-opacity:1;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 322.62295,86.03279 c 0,0 75.43341,26.12055 110.16394,89.18032 33.44261,60.72132 33.04918,62.42623 33.04918,62.42623 0,0 0.52459,8.39345 -19.93443,11.54099 -2.62295,0 14.68852,30.95082 19.93443,37.2459 -14.68853,7.34426 -16.78689,8.91803 -16.78689,8.91803 0,0 6.29508,7.86885 4.72131,15.73771 -17.31147,-0.52459 -22.55738,-1.04918 -22.55738,-1.04918 0,0 -4.19672,17.31147 -15.7377,33.04918 C 410.22951,333.11475 410.22951,320 410.22951,320 c 0,0 -24.13115,36.72131 -72.39344,41.44262 -1.57377,-2.62295 12.59016,-14.16393 17.83606,-28.32787 -11.54098,1.57377 -14.68852,2.09836 -14.68852,2.09836 l 10.4918,-16.78688 c 0,0 11.01639,-24.13115 9.44262,-28.32787 -1.04918,-2.62295 -32,-57.70492 -32,-57.70492 0,0 -24.65573,33.04918 -43.54098,49.83607 -6.81967,-4.19672 -13.63934,-14.68853 -13.63934,-14.68853 l -12.59017,11.54099 c 0,0 -48.43778,-50.76436 -42.0945,-90.18318 -3.14754,12.06557 0.12729,56.60941 -5.6432,62.3799 -5.24591,-2.09836 -16.2623,-14.68853 -14.16394,-40.91803 -0.52459,-0.52459 -29.90164,41.44262 -29.90164,41.44262 0,0 -4.19672,48.78688 16.2623,73.44262 1.57377,2.62295 -1.57377,16.2623 -31.47541,-2.62295 1.04918,-1.57377 -1.04918,9.96721 -1.04918,9.96721 0,0 -46.68853,2.09836 -45.63935,-60.85245 -2.62295,-2.62296 -13.63934,34.09836 -13.63934,34.09836 0,0 -24.13115,-32 -0.52459,-92.85246 11.80328,-30.42623 23.34426,-52.85246 31.93442,-67.67213 8.59017,-14.81968 14.22951,-22.03279 14.22951,-22.03279"
|
|
||||||
id="path8-0"
|
|
||||||
sodipodi:nodetypes="csccccccccccccccccccccccccccssc"
|
|
||||||
inkscape:label="头发部件15 头发底色" /><path
|
|
||||||
id="path44-3"
|
|
||||||
style="fill:#a88a8f;stroke:#4c4f59;stroke-width:2.8623;stroke-linecap:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
|
||||||
d="m 216.29183,255.53888 a 8.8904896,7.4928694 0 0 1 -8.89049,7.49287 8.8904896,7.4928694 0 0 1 -8.89049,-7.49287 8.8904896,7.4928694 0 0 1 8.89049,-7.49287 8.8904896,7.4928694 0 0 1 8.89049,7.49287 z" /><path
|
|
||||||
id="path44"
|
|
||||||
style="fill:#a88a8f;stroke:#4c4f59;stroke-width:2.8623;stroke-linecap:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
|
||||||
d="m 336.16933,270.5473 a 8.8904896,7.4928694 0 0 1 -8.89049,7.49287 8.8904896,7.4928694 0 0 1 -8.89049,-7.49287 8.8904896,7.4928694 0 0 1 8.89049,-7.49287 8.8904896,7.4928694 0 0 1 8.89049,7.49287 z" /><path
|
|
||||||
style="opacity:1;fill:none;fill-opacity:0;stroke:#a18f90;stroke-width:10.4;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 318.7189,85.461784 c 0,0 50.44801,17.063296 76.78484,43.400126 26.33683,26.33683 32.64283,39.31978 32.64283,39.31978"
|
|
||||||
id="path32"
|
|
||||||
inkscape:label="上方头发边缘加厚" /><path
|
|
||||||
style="fill:none;fill-opacity:1;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 186.7541,224.52459 10.4918,1.04918"
|
|
||||||
id="path45" /><path
|
|
||||||
style="opacity:1;fill:#fcbeb5;fill-opacity:1;stroke:#e99c9b;stroke-width:3.4;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 250.94176,349.98308 c 0,0 -13.91029,-8.90259 -10.0154,-16.69235 3.89488,-7.78977 17.80517,-4.4513 17.80517,-4.4513 0,0 12.98295,-1.29829 20.03083,-3.89488 7.04788,-2.59659 9.64447,5.0077 9.64447,5.0077 0,0 4.76461,6.06544 -4.26583,16.50689 -5.93505,6.86241 -21.14364,7.04788 -21.14364,7.04788"
|
|
||||||
id="path48"
|
|
||||||
sodipodi:nodetypes="cscscsc" /><path
|
|
||||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#e99c9b;stroke-width:1.8;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 276.20884,325.76493 c 0,0 4.35031,3.01904 5.37456,7.95235 0.57119,0.10629 3.33355,-3.08729 2.97647,-6.87263 -0.67968,-1.48531 -8.35103,-1.07972 -8.35103,-1.07972 z"
|
|
||||||
id="path49" /><path
|
|
||||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#e99c9b;stroke-width:1.8;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 245.0067,329.39584 -4.17308,2.78206 -0.0927,6.0278 -0.0956,-0.0355 7.07328,-8.8174 z"
|
|
||||||
id="path50"
|
|
||||||
sodipodi:nodetypes="cccccc" /><path
|
|
||||||
style="fill:none;fill-opacity:1;stroke:#e99c9b;stroke-width:3.4;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 250.94176,349.98308 c 0,0 -13.91029,-8.90259 -10.0154,-16.69235 3.89488,-7.78977 17.80517,-4.4513 17.80517,-4.4513 0,0 12.98295,-1.29829 20.03083,-3.89488 7.04788,-2.59659 9.64447,5.0077 9.64447,5.0077 0,0 4.76461,6.06544 -4.26583,16.50689 -5.93505,6.86241 -21.14364,7.04788 -21.14364,7.04788"
|
|
||||||
id="path48-2"
|
|
||||||
sodipodi:nodetypes="cscscsc" /><path
|
|
||||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#e89493;stroke-width:2.8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 190.84928,314.92913 6.12053,15.02312"
|
|
||||||
id="path51" /><path
|
|
||||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#e89493;stroke-width:2.8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 204.94505,316.96931 5.56412,14.28124"
|
|
||||||
id="path52" /><path
|
|
||||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#e89493;stroke-width:2.8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 217.92799,317.34025 7.41883,16.50689"
|
|
||||||
id="path53" /><path
|
|
||||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#e89493;stroke-width:2.8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 300.64789,328.46849 6.86242,18.17612"
|
|
||||||
id="path54" /><path
|
|
||||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#e89493;stroke-width:2.8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 312.70348,329.21037 6.67695,17.0633"
|
|
||||||
id="path55" /><path
|
|
||||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#e89493;stroke-width:2.8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 326.24284,328.65396 7.04788,17.99065"
|
|
||||||
id="path56" /><path
|
|
||||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#e49589;stroke-width:4.6;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 218.4918,348.98361 c -1.87307,1.30472 -3.33608,3.11401 -4.70333,4.91603 -1.02497,1.35089 -2.00187,2.75003 -2.77208,4.2643"
|
|
||||||
id="path61"
|
|
||||||
inkscape:path-effect="#path-effect61"
|
|
||||||
inkscape:original-d="m 218.4918,348.98361 c -1.70592,0.99645 -5.82749,5.88449 -7.47541,9.18033"
|
|
||||||
transform="rotate(-3.3120949,219.55256,344.63259)" /><path
|
|
||||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#4c4f59;stroke-width:3.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 127.47541,375.08197 c 0,0 -14.42623,-19.14754 -12.06557,-59.54099"
|
|
||||||
id="path92" /><path
|
|
||||||
style="opacity:1;fill:#ffffff;fill-opacity:0.734285;stroke:#c1545a;stroke-width:7.8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 136.39344,322.09836 c 0,0 9.44263,-18.88525 -4.19672,-39.86885 -3.14754,-0.52459 27.80328,-27.80328 5.2459,-77.11476 -1.57377,0.52459 23.60656,-55.60655 -24.13115,-82.88524 -4.19672,-8.39344 -45.639336,-69.245904 -89.180316,13.11475 0,11.54099 -1.04918,36.19672 -1.04918,36.19672 0,0 -14.6885299,38.81968 -6.29509,71.86886 2.09837,3.14754 -20.9835999,49.83606 11.0164,106.4918 0.52459,0.52459 -16.78689,57.70492 12.59016,82.88525 1.04918,4.72131 13.63935,33.57377 42.49181,39.34426 12.59016,0.52459 19.409826,-0.52459 19.409826,-0.52459 0,0 31.47541,-4.72131 47.73771,-67.14754 -2.09836,-27.80328 -4.19672,-50.36066 -11.0164,-70.81968"
|
|
||||||
id="path93" /><path
|
|
||||||
id="path101"
|
|
||||||
style="opacity:1;fill:#ffffff;fill-opacity:0.734285;stroke:#000000;stroke-width:9;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 109.69769,219.48859 c 0,0 -15.208592,-11.87012 -21.143652,-19.65989 m -37.09413,20.03083 22.25648,-21.88553 m 0.37094,-14.09577 5.93506,41.91636 m -35.23942,-24.11118 56.012132,-4.82224 m 11.12824,-17.43424 -19.288952,0.37094 m -0.37094,-0.74188 -1.48376,-29.6753 m -38.94883,3.33847 37.836,-2.96753 m -24.85306,-11.49918 0.74188,39.69072"
|
|
||||||
inkscape:label="杂" /><path
|
|
||||||
id="path111"
|
|
||||||
style="opacity:1;fill:#ffffff;fill-opacity:0.734285;stroke:#000000;stroke-width:9;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 58.507798,352.28556 54.157422,-3.33847 m -46.367662,-13.72483 35.610372,-2.96753 m -21.514602,-32.27189 2.59659,33.38472 m -18.918,-16.32142 37.094122,-3.33847 m 1.48377,18.54706 0.74188,-33.75565 m -0.37094,0.37094 -40.803542,2.22565 m 0,-0.37094 4.4513,33.38471 m 27.44965,-48.22237 3.33847,-14.4667 m 0,-1.48377 -27.44965,1.48377 m -1.48377,14.83765 0.74188,-20.03083"
|
|
||||||
inkscape:label="鱼" /><path
|
|
||||||
d="m 69.453989,376.46984 c -4.220665,0.22086 -8.079406,1.8518 -10.783191,4.54882 -2.980547,2.98155 -4.241957,7.10139 -3.534076,11.54824 0.904812,5.67433 4.763552,11.7394 11.384623,17.90216 1.405115,1.30393 2.39508,2.14912 4.390984,3.74184 3.507462,2.79468 6.679618,5.01174 11.04931,7.70876 1.559468,0.96838 4.635815,2.76496 5.796096,3.38931 l 0.335314,0.18263 0.48966,-0.26758 c 1.783005,-0.98111 4.960482,-2.86264 6.844609,-4.05189 7.126702,-4.49782 12.832312,-9.01266 17.063632,-13.50625 6.86057,-7.28827 9.77725,-14.38543 8.43068,-20.49298 -0.93142,-4.20475 -4.04502,-7.68327 -8.46261,-9.45012 -1.90011,-0.76026 -3.74699,-1.15525 -5.83869,-1.24869 -2.7091,-0.12316 -5.37562,0.33977 -7.91973,1.37185 -4.332435,1.75413 -7.951675,5.17315 -10.229661,9.66674 -0.159681,0.3143 -0.308701,0.59038 -0.329985,0.60737 -0.0692,0.0553 -0.143707,-0.051 -0.431122,-0.62859 -0.681263,-1.35913 -2.01719,-3.3341 -3.065699,-4.52331 -1.07513,-1.22747 -2.804909,-2.72251 -4.092931,-3.54224 -3.326503,-2.11937 -7.222503,-3.15569 -11.097213,-2.95607 z"
|
|
||||||
id="path1-4"
|
|
||||||
style="fill:#d96477;fill-opacity:1;stroke:none;stroke-width:0.00475452" /></g></svg>
|
|
||||||
|
Before Width: | Height: | Size: 60 KiB |
@@ -1,80 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html lang="en">
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8" />
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
||||||
<title>Offline | SukiSU-Ultra</title>
|
|
||||||
<meta name="theme-color" content="#64edff" />
|
|
||||||
<style>
|
|
||||||
html,
|
|
||||||
body {
|
|
||||||
height: 100%;
|
|
||||||
margin: 0;
|
|
||||||
font-family:
|
|
||||||
system-ui,
|
|
||||||
-apple-system,
|
|
||||||
Segoe UI,
|
|
||||||
Roboto,
|
|
||||||
Ubuntu,
|
|
||||||
Cantarell,
|
|
||||||
'Noto Sans',
|
|
||||||
sans-serif;
|
|
||||||
}
|
|
||||||
.wrap {
|
|
||||||
min-height: 100%;
|
|
||||||
display: grid;
|
|
||||||
place-items: center;
|
|
||||||
padding: 2rem;
|
|
||||||
}
|
|
||||||
.card {
|
|
||||||
max-width: 560px;
|
|
||||||
background: #fff;
|
|
||||||
color: #111;
|
|
||||||
border-radius: 12px;
|
|
||||||
box-shadow: 0 6px 30px rgba(0, 0, 0, 0.06);
|
|
||||||
padding: 24px;
|
|
||||||
}
|
|
||||||
.card h1 {
|
|
||||||
margin: 0 0 8px;
|
|
||||||
font-size: 1.4rem;
|
|
||||||
}
|
|
||||||
.card p {
|
|
||||||
margin: 0.5rem 0;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
.muted {
|
|
||||||
color: #666;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
}
|
|
||||||
.actions {
|
|
||||||
margin-top: 1rem;
|
|
||||||
display: flex;
|
|
||||||
gap: 0.75rem;
|
|
||||||
}
|
|
||||||
.btn {
|
|
||||||
padding: 0.6rem 0.9rem;
|
|
||||||
border-radius: 8px;
|
|
||||||
border: 1px solid #d0d0d0;
|
|
||||||
background: #f7f7f7;
|
|
||||||
color: #111;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div class="wrap">
|
|
||||||
<div class="card">
|
|
||||||
<h1>You're offline</h1>
|
|
||||||
<p>We couldn't load this page because your device has no internet connection.</p>
|
|
||||||
<p class="muted">
|
|
||||||
When you're back online, try again. Some pages you visited before may still work from
|
|
||||||
cache.
|
|
||||||
</p>
|
|
||||||
<div class="actions">
|
|
||||||
<a class="btn" href="/">Go to home</a>
|
|
||||||
<a class="btn" href="/guide/">Docs guide</a>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
User-agent: *
|
|
||||||
Allow: /
|
|
||||||
Disallow: /admin/
|
|
||||||
Disallow: /.git/
|
|
||||||
Disallow: /node_modules/
|
|
||||||
Disallow: /api/
|
|
||||||
Disallow: /.vitepress/
|
|
||||||
|
|
||||||
# Sitemap
|
|
||||||
Sitemap: https://sukisu.org/sitemap.xml
|
|
||||||
|
|
||||||
# Crawl-delay for high-traffic optimization
|
|
||||||
Crawl-delay: 0.5
|
|
||||||
|
|
||||||
# Major search engines (global optimization)
|
|
||||||
User-agent: Googlebot
|
|
||||||
Allow: /
|
|
||||||
Crawl-delay: 0.5
|
|
||||||
|
|
||||||
User-agent: Bingbot
|
|
||||||
Allow: /
|
|
||||||
Crawl-delay: 1
|
|
||||||
|
|
||||||
User-agent: Slurp
|
|
||||||
Allow: /
|
|
||||||
Crawl-delay: 1
|
|
||||||
|
|
||||||
User-agent: DuckDuckBot
|
|
||||||
Allow: /
|
|
||||||
Crawl-delay: 0.5
|
|
||||||
|
|
||||||
User-agent: Baiduspider
|
|
||||||
Allow: /
|
|
||||||
Crawl-delay: 2
|
|
||||||
|
|
||||||
# Asian search engines (for China, Japan, etc.)
|
|
||||||
User-agent: YandexBot
|
|
||||||
Allow: /
|
|
||||||
Crawl-delay: 1
|
|
||||||
|
|
||||||
User-agent: NaverBot
|
|
||||||
Allow: /
|
|
||||||
Crawl-delay: 1
|
|
||||||
|
|
||||||
User-agent: SogouSpider
|
|
||||||
Allow: /
|
|
||||||
Crawl-delay: 2
|
|
||||||
|
|
||||||
# Block resource-intensive bots for performance
|
|
||||||
User-agent: AhrefsBot
|
|
||||||
Disallow: /
|
|
||||||
|
|
||||||
User-agent: MJ12bot
|
|
||||||
Disallow: /
|
|
||||||
|
|
||||||
User-agent: SemrushBot
|
|
||||||
Disallow: /
|
|
||||||
|
|
||||||
User-agent: DotBot
|
|
||||||
Disallow: /
|
|
||||||
|
|
||||||
# Block AI training crawlers to save bandwidth
|
|
||||||
User-agent: GPTBot
|
|
||||||
Disallow: /
|
|
||||||
|
|
||||||
User-agent: ChatGPT-User
|
|
||||||
Disallow: /
|
|
||||||
|
|
||||||
User-agent: CCBot
|
|
||||||
Disallow: /
|
|
||||||
|
|
||||||
User-agent: anthropic-ai
|
|
||||||
Disallow: /
|
|
||||||
|
|
||||||
User-agent: Claude-Web
|
|
||||||
Disallow: /
|
|
||||||
@@ -1,535 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
|
||||||
|
|
||||||
<svg
|
|
||||||
width="512"
|
|
||||||
height="512"
|
|
||||||
viewBox="0 0 512 512"
|
|
||||||
version="1.1"
|
|
||||||
id="svg1"
|
|
||||||
xml:space="preserve"
|
|
||||||
inkscape:version="1.4 (e7c3feb100, 2024-10-09)"
|
|
||||||
sodipodi:docname="zako.optizmism.svg"
|
|
||||||
inkscape:export-filename="zako.plain.svg"
|
|
||||||
inkscape:export-xdpi="96"
|
|
||||||
inkscape:export-ydpi="96"
|
|
||||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
||||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
xmlns:svg="http://www.w3.org/2000/svg"><sodipodi:namedview
|
|
||||||
id="namedview1"
|
|
||||||
pagecolor="#ffffff"
|
|
||||||
bordercolor="#999999"
|
|
||||||
borderopacity="1"
|
|
||||||
inkscape:showpageshadow="2"
|
|
||||||
inkscape:pageopacity="1"
|
|
||||||
inkscape:pagecheckerboard="0"
|
|
||||||
inkscape:deskcolor="#d1d1d1"
|
|
||||||
inkscape:document-units="px"
|
|
||||||
showguides="false"
|
|
||||||
inkscape:zoom="2"
|
|
||||||
inkscape:cx="219"
|
|
||||||
inkscape:cy="370"
|
|
||||||
inkscape:window-width="1280"
|
|
||||||
inkscape:window-height="702"
|
|
||||||
inkscape:window-x="0"
|
|
||||||
inkscape:window-y="0"
|
|
||||||
inkscape:window-maximized="1"
|
|
||||||
inkscape:current-layer="layer1"
|
|
||||||
showgrid="false"><inkscape:page
|
|
||||||
x="0"
|
|
||||||
y="0"
|
|
||||||
width="512"
|
|
||||||
height="512"
|
|
||||||
id="page2"
|
|
||||||
margin="0"
|
|
||||||
bleed="0" /></sodipodi:namedview><defs
|
|
||||||
id="defs1"><inkscape:path-effect
|
|
||||||
effect="simplify"
|
|
||||||
id="path-effect79"
|
|
||||||
is_visible="true"
|
|
||||||
lpeversion="1.3"
|
|
||||||
threshold="32.467532"
|
|
||||||
steps="1"
|
|
||||||
smooth_angles="360"
|
|
||||||
helper_size="0"
|
|
||||||
simplify_individual_paths="false"
|
|
||||||
simplify_just_coalesce="false" /><inkscape:path-effect
|
|
||||||
effect="simplify"
|
|
||||||
id="path-effect78"
|
|
||||||
is_visible="true"
|
|
||||||
lpeversion="1.3"
|
|
||||||
threshold="32.467532"
|
|
||||||
steps="1"
|
|
||||||
smooth_angles="360"
|
|
||||||
helper_size="0"
|
|
||||||
simplify_individual_paths="false"
|
|
||||||
simplify_just_coalesce="false" /><inkscape:path-effect
|
|
||||||
effect="simplify"
|
|
||||||
id="path-effect77"
|
|
||||||
is_visible="true"
|
|
||||||
lpeversion="1.3"
|
|
||||||
threshold="32.467532"
|
|
||||||
steps="1"
|
|
||||||
smooth_angles="360"
|
|
||||||
helper_size="0"
|
|
||||||
simplify_individual_paths="false"
|
|
||||||
simplify_just_coalesce="false" /><inkscape:path-effect
|
|
||||||
effect="simplify"
|
|
||||||
id="path-effect76"
|
|
||||||
is_visible="true"
|
|
||||||
lpeversion="1.3"
|
|
||||||
threshold="32.467532"
|
|
||||||
steps="1"
|
|
||||||
smooth_angles="360"
|
|
||||||
helper_size="0"
|
|
||||||
simplify_individual_paths="false"
|
|
||||||
simplify_just_coalesce="false" /><inkscape:path-effect
|
|
||||||
effect="simplify"
|
|
||||||
id="path-effect75"
|
|
||||||
is_visible="true"
|
|
||||||
lpeversion="1.3"
|
|
||||||
threshold="32.467532"
|
|
||||||
steps="1"
|
|
||||||
smooth_angles="360"
|
|
||||||
helper_size="0"
|
|
||||||
simplify_individual_paths="false"
|
|
||||||
simplify_just_coalesce="false" /><inkscape:path-effect
|
|
||||||
effect="simplify"
|
|
||||||
id="path-effect61"
|
|
||||||
is_visible="true"
|
|
||||||
lpeversion="1.3"
|
|
||||||
threshold="32.467532"
|
|
||||||
steps="1"
|
|
||||||
smooth_angles="360"
|
|
||||||
helper_size="0"
|
|
||||||
simplify_individual_paths="false"
|
|
||||||
simplify_just_coalesce="false" /><inkscape:path-effect
|
|
||||||
effect="simplify"
|
|
||||||
id="path-effect42"
|
|
||||||
is_visible="true"
|
|
||||||
lpeversion="1.3"
|
|
||||||
threshold="32.467532"
|
|
||||||
steps="1"
|
|
||||||
smooth_angles="360"
|
|
||||||
helper_size="0"
|
|
||||||
simplify_individual_paths="false"
|
|
||||||
simplify_just_coalesce="false" /></defs><g
|
|
||||||
inkscape:label="main"
|
|
||||||
inkscape:groupmode="layer"
|
|
||||||
id="layer1"><path
|
|
||||||
style="display:inline;opacity:1;fill:#fff9f6;fill-opacity:1;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 164.23888,424.64081 -31.07097,26.05282 c 0,0 -5.19317,18.17613 25.96589,28.93342 31.15907,10.7573 213.29123,1.48377 213.29123,1.48377 0,0 52.30272,11.87012 80.86519,-37.09413 -0.37094,0.74188 -8.90259,19.28895 -8.90259,19.28895 l 1.48377,19.28894 c 0,0 57.73516,-9.41425 30.45647,-148.95524 4.19672,-9.44262 8.39344,-15.21311 8.39344,-15.21311 l -3.14754,-21.77049 c 0,0 5.65758,-40.33533 7.88323,-45.52851 2.22564,-5.19317 9.64447,-11.12823 9.64447,-11.12823 0,0 -2.78206,-20.40177 -6.67694,-26.15136 -0.74189,-3.33847 6.86241,-35.05395 2.04017,-58.05231 -7.04788,-11.87012 -4.82223,-11.87012 -18.918,-25.59495 0,0.37094 8.90259,-44.88389 -0.74188,-73.446367 -2.96753,0 -32.27189,-10.757296 -96.81567,23.740241 -0.37094,1.112824 -26.33683,-30.046242 -153.56969,-15.950474 -0.74188,0 -43.40012,-37.836009 -66.76942,-40.432598 -1.11283,-0.370941 -22.62742,-4.080354 -25.22401,48.964247 0,1.112824 -44.14201,15.950474 -56.01213,64.914721 -11.87012,48.96425 -11.128238,80.86519 -11.128238,80.86519 0,0 -18.904912,27.6632 -9.798121,25.03308 5.346826,-1.54421 4.975885,23.56023 4.975885,23.56023 l -2.225648,39.31977 c 0,0 -14.466709,35.23942 -14.83765,59.72155 -0.370942,24.48212 -0.741883,34.12659 4.822236,48.5933 5.564119,14.46671 20.772711,46.7386 42.658245,63.06002 8.902591,0.37094 18.918001,1.8547 18.918001,1.8547 l 3.70942,-20.77271 c 0,0 18.918,15.95048 32.27189,14.83765 13.35388,-1.11282 -0.74189,-0.74188 -0.74189,-0.74188 l -13.72482,-17.80518 z"
|
|
||||||
id="path2"
|
|
||||||
sodipodi:nodetypes="ccscccccccscccccccccscsccsscccsccc"
|
|
||||||
inkscape:label="整个人的边框" /><path
|
|
||||||
id="path91"
|
|
||||||
style="opacity:1;fill:#93d4fa;fill-opacity:1;stroke:#4c4f59;stroke-width:6.9;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 133.91016,110.16992 -69.738285,47.48047 18.263976,30.75433 -31.617492,55.67536 28.5625,17.0625 14.837891,-1.48438 2.226562,-55.64062 2.577791,-4.39109 18.193697,-21.94485 23.74023,-49.70703 z"
|
|
||||||
sodipodi:nodetypes="ccccccccccc" /><path
|
|
||||||
style="display:inline;opacity:1;fill:#ace0fe;fill-opacity:1;stroke:#4c4f59;stroke-width:6.54357;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 217.01397,405.82758 c 0,0 7.41728,-25.94462 41.16595,-27.07265 33.74866,-1.12802 53.77534,4.1361 53.77534,4.1361 0,0 11.12594,6.39215 17.80149,14.66435 6.67557,8.2722 28.92743,40.60895 41.53682,48.88115 12.60939,8.27219 15.20545,15.41636 15.20545,15.41636 l -1.85433,11.28027 c 0,0 -14.83458,10.15224 -30.78175,11.65627 -15.94717,1.50404 -147.60404,0 -147.60404,0 l -30.04002,-3.00807"
|
|
||||||
id="path71"
|
|
||||||
inkscape:label="衣服底色" /><path
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
|
||||||
d="m 363.54127,444.68178 c 0,0 -6.89429,4.06586 -4.86135,6.8059 2.03293,2.74004 5.92201,3.44715 5.92201,3.44715 0,0 4.5962,0.26516 5.12653,-3.53554 0.53033,-3.8007 -3.00521,-6.8059 -3.00521,-6.8059"
|
|
||||||
id="path60" /><path
|
|
||||||
style="fill:#ffffff;fill-opacity:1;stroke-width:1.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
|
||||||
d="m 349.67678,459.82583 c 0,0 4.5,-7.25 7,-5.5 2.5,1.75 -4.25,7.25 -4.25,7.25"
|
|
||||||
id="path59" /><path
|
|
||||||
id="path73-8"
|
|
||||||
style="fill:#ffffff;stroke-width:8.7;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
|
||||||
d="m 228.96985,465.10941 a 8.0341015,5.6995392 0 0 1 -8.0341,5.69953 8.0341015,5.6995392 0 0 1 -8.03411,-5.69953 8.0341015,5.6995392 0 0 1 8.03411,-5.69954 8.0341015,5.6995392 0 0 1 8.0341,5.69954 z" /><path
|
|
||||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#6c9cb2;stroke-width:3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 220.85246,408.91803 c -0.22678,0.7374 0.77024,1.47887 0.20184,0.3709 -0.55918,-1.08999 0.45864,1.0858 0.58075,1.39596 2.10089,5.33666 1.96407,11.16153 3.60742,16.6426 0.30205,1.00744 0.58829,2.039 0.5936,3.09874"
|
|
||||||
id="path75"
|
|
||||||
inkscape:path-effect="#path-effect75"
|
|
||||||
inkscape:original-d="m 220.85246,408.91803 c 0.0874,0.34973 -0.0874,1.13662 0.26229,1.04918 0.34973,-0.0874 -0.42351,-1.37161 -0.26229,-1.04918 2.70315,5.4063 2.32215,10.60009 3.67213,16 0.44415,1.77662 1.31148,3.74741 1.31148,5.5082" /><path
|
|
||||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#6c9cb2;stroke-width:6.9;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 225.57377,436.98361 c -1.22213,4.37272 0.0774,9.22771 2.41807,12.85195 2.34183,3.626 0.85866,8.30929 2.56554,12.06608 1.71568,3.77616 2.21068,8.39349 -0.30117,12.16042 -1.9802,2.96963 -5.23922,5.13567 -6.38674,8.61546 0,0 -0.0736,0.66095 -0.0736,0.66095"
|
|
||||||
id="path76"
|
|
||||||
inkscape:path-effect="#path-effect76"
|
|
||||||
inkscape:original-d="m 225.57377,436.98361 c -1.27879,3.14891 0.64852,9.42818 3.40984,14.95082 0.92316,1.84632 0.76302,8.34571 1.57377,9.96721 1.05661,2.11323 2.18032,6.57378 1.31147,9.18033 -1.55473,4.66419 -8.07296,8.77008 -8.07296,12.2565"
|
|
||||||
sodipodi:nodetypes="csssc" /><path
|
|
||||||
style="opacity:1;fill:#fbf3ef;fill-opacity:1;stroke:none;stroke-width:5.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 259.14754,381.90164 c 0,0 8.91803,15.47541 9.18033,20.98361 0.26229,5.50819 32.26229,-1.83607 32.26229,-1.83607 0,0 -4.45901,-16.78688 -7.34426,-19.40983 -2.88524,-2.62296 -34.09836,0.26229 -34.09836,0.26229 z"
|
|
||||||
id="path89" /><path
|
|
||||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#6c9cb2;stroke-width:4.6;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 247.86885,386.36066 c 0.62439,0.76152 1.31219,1.32242 0.35251,0.10604 -0.98535,-1.24891 -0.63523,-0.56343 0.0935,0.24171 1.40155,1.54849 2.30926,3.40606 3.10338,5.34166 1.48771,3.62621 2.15649,7.53104 4.02075,11.02472 1.24886,2.3404 2.74915,4.53757 3.97084,6.89242"
|
|
||||||
id="path77"
|
|
||||||
inkscape:path-effect="#path-effect77"
|
|
||||||
inkscape:original-d="m 247.86885,386.36066 c 3.34102,3.56326 -3.68025,-4.79705 1.83607,2.09836 1.23825,1.54782 3.18087,7.73986 3.67213,9.70492 1.00623,4.02493 4.53306,8.80383 6.03279,11.80327" /><path
|
|
||||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#6c9cb2;stroke-width:4.6;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 306.62295,386.88525 c 1.62958,6.07854 4.04229,11.93568 6.81967,17.57377"
|
|
||||||
id="path78"
|
|
||||||
inkscape:path-effect="#path-effect78"
|
|
||||||
inkscape:original-d="m 306.62295,386.88525 c 1.57569,6.03983 4.44925,12.83291 6.81967,17.57377" /><path
|
|
||||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#6c9cb2;stroke-width:5.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 335.73771,431.21312 c 0.7696,3.76393 3.24862,7.26774 5.48726,10.89168 2.09754,3.39553 3.33574,7.13199 4.87926,10.7955 2.06975,4.91248 6.75146,8.7424 7.699,13.95386 0.92029,5.06165 5.24206,8.89669 5.27874,14.19502"
|
|
||||||
id="path79"
|
|
||||||
inkscape:path-effect="#path-effect79"
|
|
||||||
inkscape:original-d="m 335.73771,431.21312 c -1.15649,1.03916 3.66917,4.97179 4.45901,8.13114 0.96754,3.87016 3.8848,6.88344 4.72131,10.22951 1.09547,4.38186 5.88097,12.49359 8.39345,16.2623 0.80744,1.21116 0.89968,3.11084 1.57377,4.45901 1.7539,3.50782 4.19672,6.9619 4.19672,10.7541" /><path
|
|
||||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#494d55;stroke-width:5.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 258.62295,384.2623 c 0,0 8.13115,6.81967 11.27869,27.01639"
|
|
||||||
id="path80" /><path
|
|
||||||
style="opacity:1;fill:#fbf3ef;fill-opacity:1;stroke:#4c4f59;stroke-width:6.9;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 127.23285,329.0249 c 0,0 -2.59659,60.46343 13.72483,75.30108 8.16071,-8.16071 10.01541,-10.38636 10.01541,-10.38636 l 12.24107,8.53165 5.19317,-4.45129 2.22565,-19.65989 5.19318,-10.01542 5.93506,-12.24106 -8.53165,-8.53165 -15.95047,-19.28894 -3.33848,-4.82224 -4.45129,9.27353 c 0,0 -12.24106,0 -15.95048,-2.59659 -3.70941,-2.59658 -6.306,-1.11282 -6.306,-1.11282 z"
|
|
||||||
id="path90" /><path
|
|
||||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#494d55;stroke-width:5.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 291.67213,381.37705 c 0,0 7.08197,6.55738 10.22951,26.4918"
|
|
||||||
id="path81" /><path
|
|
||||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#494d55;stroke-width:5.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 270.16393,404.19672 c 0,0 16.00001,-3.93442 27.80328,-2.62295"
|
|
||||||
id="path82" /><path
|
|
||||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#494d55;stroke-width:5.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 253.90164,417.83607 c 0,0 37.2459,-13.11476 69.77049,-9.18033 2.09836,3.67213 5.77049,16.78688 -5.77049,20.98361 -4.72131,0 -19.14754,-1.57378 -22.55738,-0.5246 -3.40983,1.04919 -12.59016,0.5246 -18.36065,4.45902 -5.77049,3.93443 -14.68853,8.91803 -18.36066,3.14754 -3.67213,-5.77049 -4.72131,-12.32787 -4.72131,-12.32787 z"
|
|
||||||
id="path83" /><path
|
|
||||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#494d55;stroke-width:5.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 276.45902,482.62295 c 0,0 -9.70492,-1.57377 1.04918,-46.42623"
|
|
||||||
id="path84" /><path
|
|
||||||
style="opacity:1;fill:#fcf6fa;fill-opacity:1;stroke:#494d55;stroke-width:5.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 287.21312,432.78689 6.29508,50.88524 13.90164,-0.78688 -6.81968,-54.03279 z"
|
|
||||||
id="path88" /><path
|
|
||||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#494d55;stroke-width:5.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 287.73771,430.95082 5.77049,50.88525"
|
|
||||||
id="path85" /><path
|
|
||||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#494d55;stroke-width:5.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 300.32787,428.06557 c 0,0 2.09836,37.7705 7.34426,52.98361"
|
|
||||||
id="path86" /><path
|
|
||||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#494d55;stroke-width:5.3;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 313.18033,431.21312 c 0,0 7.60656,27.54098 10.22951,35.93442 2.62295,8.39344 -8.13115,15.73771 -8.13115,15.73771"
|
|
||||||
id="path87" /><path
|
|
||||||
style="opacity:1;fill:#bce4fd;fill-opacity:1;stroke:none;stroke-width:8.7;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 164.23888,424.64081 -31.07097,26.05282 5.00771,13.72483 20.95818,15.20859 17.08508,2.15431 -11.1287,-20.55238 -1.54637,-22.78395 z"
|
|
||||||
id="path74" /><path
|
|
||||||
id="path73"
|
|
||||||
style="opacity:1;fill:#ffffff;stroke-width:8.7;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
|
||||||
d="m 163.81755,451.10144 a 8.0341015,5.6995392 0 0 1 -8.0341,5.69954 8.0341015,5.6995392 0 0 1 -8.0341,-5.69954 8.0341015,5.6995392 0 0 1 8.0341,-5.69954 8.0341015,5.6995392 0 0 1 8.0341,5.69954 z" /><path
|
|
||||||
style="opacity:1;fill:#fff9f6;fill-opacity:1;stroke:none;stroke-width:8.7;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 338.36066,376.13115 c 0,0 1.83605,30.95081 -6.29509,51.40983 2.88525,1.04918 14.48107,0.26606 26.91267,-7.34238 12.4316,-7.60844 11.38242,-22.03467 11.38242,-22.03467 l 2.09836,28.85246 c 0,0 24.31867,-28.09873 24.2249,-52.60674 -0.0938,-24.50801 -1.66753,-26.08178 -1.66753,-26.08178 l -11.83407,-1.12685 z"
|
|
||||||
id="path72" /><path
|
|
||||||
style="opacity:1;fill:#fde9e7;fill-opacity:1;stroke:none;stroke-width:2.845;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 339.7822,389.85927 c 0,0 38.20695,-7.78977 56.38307,-25.96589 5.56412,8.53165 8.16071,32.64283 8.16071,32.64283 l 2.59659,12.98294 16.32141,-62.68907 -6.67694,-5.19318 -5.93506,-17.43424 c 0,0 -15.20859,15.2086 -27.44966,22.99836 -12.24106,7.78977 -34.12659,19.28895 -34.12659,19.28895 l -10.7573,3.70941 z"
|
|
||||||
id="path70" /><path
|
|
||||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#c3b4b0;stroke-width:2.845;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 347.01555,369.64297 c 0,0 2.22565,10.01541 -1.8547,19.84536 -4.08036,9.82994 -5.00771,9.459 -5.00771,9.459"
|
|
||||||
id="path69" /><path
|
|
||||||
style="opacity:1;fill:#fde9e7;fill-opacity:1;stroke:none;stroke-width:6.945;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 380.85246,480 c 0,0 44.59016,-17.31147 59.27869,-87.08197 6.81967,-2.62295 34.09836,-14.68852 39.34426,-25.70491 2.62295,-9.96722 -3.67213,-32 -3.67213,-32 l -24.65574,0.52459 -26.7541,4.72131 -4.72131,11.54098 -8.39344,52.45902 -13.11475,28.32787 -15.73771,36.19672 z"
|
|
||||||
id="path68" /><path
|
|
||||||
style="fill:none;fill-opacity:1;stroke:#4c4f59;stroke-width:8.74203;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 147.6556,277.03379 c 0,0 -11.12655,104.13607 127.58449,103.38689 138.71104,-0.74917 149.83759,-100.39017 149.83759,-100.39017"
|
|
||||||
id="path41"
|
|
||||||
inkscape:label="脸蛋" /><path
|
|
||||||
style="opacity:1;fill:#fff9f6;fill-opacity:1;stroke:none;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 123.27869,147.93443 304.78688,21.50819 -24.65573,-40.39344 c 0,0 44.06557,-58.754098 50.88524,-60.327868 6.81967,-1.573771 15.73771,-9.967214 15.73771,-9.967214 l -13.11476,-3.672131 -77.11475,22.032787 c 0,0 -122.7541,-33.57377 -168.39344,-6.295082 -45.63935,27.278689 -69.7705,46.163938 -69.7705,46.163938 z"
|
|
||||||
id="path33"
|
|
||||||
inkscape:label="头发底色2" /><path
|
|
||||||
style="fill:#fde9e7;fill-opacity:1;stroke:none;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 331.44501,66.651771 16.22882,30.84274 47.82991,31.367399 6.30629,1.07795 26.88325,-35.335399 -32.10312,-22.211018 -18.88524,5.245901 -30.42623,-11.540983 z"
|
|
||||||
id="path35"
|
|
||||||
inkscape:label="头发部件111" /><path
|
|
||||||
style="opacity:1;fill:#ace0fe;fill-opacity:1;stroke:none;stroke-width:14.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 431.03375,175.82616 17.06329,-17.0633 50.44802,71.96261 -5.93506,17.80518 -33.38472,18.54706 -12.612,-16.32142 17.80518,-11.12823 z"
|
|
||||||
id="path29"
|
|
||||||
inkscape:label="蓝色发卡上" /><path
|
|
||||||
style="opacity:1;fill:#ace0fe;fill-opacity:1;stroke:#4c4f59;stroke-width:6.945;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 465.90223,261.88453 c 0,0 35.61036,54.52837 2.96753,73.44637 -25.22401,4.08036 -48.22237,2.22565 -48.22237,2.22565 l 10.38636,-26.33683 21.51459,-41.91636 z"
|
|
||||||
id="path66"
|
|
||||||
inkscape:label="蓝色发卡下" /><path
|
|
||||||
style="opacity:1;fill:#febdc7;fill-opacity:1;stroke:none;stroke-width:4.6;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 471.86885,57.704918 c 0,0 17.04918,68.983602 -11.01639,110.426232 -7.34426,0.26229 -9.18033,-10.49181 -9.18033,-10.49181 l 2.09836,-6.55737 -1.04918,-9.18033 2.36066,-10.7541 1.83606,-5.5082 -6.29508,-6.03278 -6.55738,-5.2459 -2.88524,-5.24591 2.88524,-5.2459 -2.62295,-5.245899 -4.19672,-2.885246 -3.14754,-1.57377 5.2459,-11.540984 11.80328,-7.344262 z"
|
|
||||||
id="path64" /><path
|
|
||||||
id="path57"
|
|
||||||
style="opacity:1;fill:#ff1c1c;fill-opacity:0.0509804;stroke-width:3.92397;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
|
||||||
d="m 244.26691,326.81357 a 35.587568,21.480417 0 0 1 -35.58757,21.48042 35.587568,21.480417 0 0 1 -35.58757,-21.48042 35.587568,21.480417 0 0 1 35.58757,-21.48042 35.587568,21.480417 0 0 1 35.58757,21.48042 z" /><path
|
|
||||||
id="path57-1"
|
|
||||||
style="fill:#ff1c1c;fill-opacity:0.05044398;stroke-width:3.65169;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
|
||||||
d="m 355.20429,336.75188 a 31.611719,20.942554 0 0 1 -31.61171,20.94255 31.611719,20.942554 0 0 1 -31.61172,-20.94255 31.611719,20.942554 0 0 1 31.61172,-20.94255 31.611719,20.942554 0 0 1 31.61171,20.94255 z" /><path
|
|
||||||
style="display:inline;opacity:1;fill:#fbf3ef;fill-opacity:1;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 188.32787,343.60656 c 0,0 12.06557,-28.32787 15.7377,6.29508 5.24591,-8.91803 9.96722,-17.31148 17.31148,-8.39344 6.81967,2.09836 15.21311,7.34426 4.72131,22.03278 -10.4918,14.68853 16.33814,-18.37136 15.445,-1.63289 -0.72099,13.51206 -24.52214,46.81129 -24.52214,46.81129 0,0 -6.33782,64.18547 -33.41466,70.23144 -36.5485,8.16089 -12.06558,-103.86885 -12.06558,-103.86885 z"
|
|
||||||
id="path1"
|
|
||||||
sodipodi:nodetypes="cccsscscc"
|
|
||||||
inkscape:label="右手" /><path
|
|
||||||
id="path5"
|
|
||||||
style="fill:#fbe7e5;stroke-width:10.4;stroke-linecap:round;stroke-miterlimit:1.3;paint-order:fill markers stroke;fill-opacity:0.40581462"
|
|
||||||
inkscape:label="右手踝阴影"
|
|
||||||
d="m 193.63133,383.92419 a 12.612,12.241061 0 0 1 -12.612,12.24107 12.612,12.241061 0 0 1 -12.612,-12.24107 12.612,12.241061 0 0 1 12.612,-12.24106 12.612,12.241061 0 0 1 12.612,12.24106 z" /><path
|
|
||||||
style="fill:#fee4e0;fill-opacity:0.42411327;stroke:none;stroke-width:10.4;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 179.09741,356.52608 14.09576,-20.77271 7.04789,1.48377 0.74188,13.72482 11.49918,-11.49918 9.27353,4.08036 8.53165,5.93506 -5.56412,14.83765 13.72483,-4.82224 -3.33847,17.80518 -47.85143,-27.44965 z"
|
|
||||||
id="path9"
|
|
||||||
inkscape:label="右手手指阴影" /><path
|
|
||||||
style="display:inline;fill:none;fill-opacity:1;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 188.32787,343.60656 c 0,0 12.06557,-28.32787 15.7377,6.29508 5.24591,-8.91803 9.96722,-17.31148 17.31148,-8.39344 6.81967,2.09836 15.21311,7.34426 4.72131,22.03278 -10.4918,14.68853 16.33814,-18.37136 15.445,-1.63289 -0.72099,13.51206 -24.52214,46.81129 -24.52214,46.81129 0,0 -6.33782,64.18547 -33.41466,70.23144 -36.5485,8.16089 -12.06558,-103.86885 -12.06558,-103.86885 z"
|
|
||||||
id="path1-6"
|
|
||||||
sodipodi:nodetypes="cccsscscc"
|
|
||||||
inkscape:label="右手遮挡" /><path
|
|
||||||
style="opacity:1;fill:#fff9f6;fill-opacity:1;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 137.44262,123.2787 c 0,0 49.33852,-48.649769 86.97684,-58.734919 0.10513,0.76769 -120.16719,-122.15824 -86.97684,58.734919 z"
|
|
||||||
id="path3"
|
|
||||||
inkscape:label="头发部件19 左猫耳" /><path
|
|
||||||
style="fill:#fde9e7;fill-opacity:1;stroke-width:1.18016;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
|
||||||
d="m 198.85246,42 -63.6342,39.016573 3.84123,40.939237 L 184.82787,87 224,64.5 Z"
|
|
||||||
id="path58"
|
|
||||||
sodipodi:nodetypes="cccccc" /><path
|
|
||||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0.465732;stroke:#a18f90;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
id="path4"
|
|
||||||
d="m 404.96447,131.7729 c 0.52512,-0.76931 1.0357,-1.54875 1.57536,-2.30793 2.82418,-3.97291 5.82035,-7.82036 8.67758,-11.76948 2.39309,-3.30762 3.93978,-5.54326 6.29133,-8.89998 5.32612,-7.58044 11.18023,-14.760796 17.20894,-21.78699 3.02173,-3.55064 6.06655,-7.098864 9.47662,-10.289082 1.96198,-1.835492 4.21906,-3.672787 6.27415,-5.392122 5.10823,-4.357455 10.66931,-8.109574 16.22169,-11.862072 0,0 -2.92504,-1.864176 -2.92504,-1.864176 v 0 c -5.41592,3.900149 -10.90348,7.705144 -15.99184,12.040062 -1.99887,1.645368 -4.44389,3.611151 -6.36286,5.369248 -3.45237,3.162941 -6.48944,6.731577 -9.48752,10.316639 -5.95526,7.087894 -11.63254,14.399231 -17.00034,21.944173 -5.47138,7.72885 -10.96322,15.45542 -17.11251,22.66867 z"
|
|
||||||
inkscape:label="头发部件18" /><path
|
|
||||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0.465732;stroke:#a18f90;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
id="path6"
|
|
||||||
d="m 431.7055,96.885136 c 0.68345,-0.01683 1.36993,0.008 2.05351,0.03239 1.26616,0.04274 2.52519,0.175792 3.77564,0.374299 1.34943,0.219358 2.67703,0.547853 3.99756,0.89747 1.30448,0.342355 2.58658,0.760225 3.8587,1.206247 0.34787,0.132122 0.88919,0.332358 1.23164,0.483411 0.1893,0.0835 0.73774,0.373687 0.5597,0.268307 -4.92017,-2.912189 -2.83776,-1.86942 -2.01609,-0.911603 0.4665,0.942833 -0.25414,1.810973 -0.83258,2.515753 -1.05305,1.11546 -2.37988,1.92349 -3.65077,2.76486 -0.70455,0.52564 -1.84263,0.99551 -2.02707,1.9792 -0.13577,0.72412 0.14689,1.00706 0.45808,1.64499 0.25805,0.31484 0.49646,0.64686 0.77415,0.94453 0.63085,0.67625 1.54885,1.45348 2.25243,2.03623 1.28413,1.06359 2.61277,2.07518 3.92585,3.10243 2.22765,1.73231 4.46676,3.44711 6.4408,5.4696 1.24472,1.36548 2.33589,2.86124 3.10757,4.54226 0.57402,1.31239 0.61894,2.68201 0.46471,4.08008 -0.15236,1.542 -1.01177,2.74753 -1.96982,3.90485 -1.25092,1.39253 -2.70297,2.57966 -3.98322,3.94302 -0.61776,0.76458 -1.19371,1.60995 -1.3359,2.60746 -0.0756,0.53059 -0.0152,0.87319 0.031,1.40055 0.18192,1.34638 0.59785,2.64226 1.03422,3.92358 0.46172,1.36123 1.10075,2.65093 1.71056,3.94949 0.57934,1.12566 0.88081,2.32829 0.97901,3.58284 0.0285,1.01273 -0.14083,2.01608 -0.35002,3.00302 -0.18321,0.8594 -0.39147,1.62037 -0.92358,2.32955 -0.69384,0.72591 -1.52468,1.30006 -2.31082,1.91913 0,0 3.13155,1.96912 3.13155,1.96912 v 0 c 0.80747,-0.64335 1.66352,-1.23803 2.37418,-1.9921 0.58364,-0.77755 0.80933,-1.52536 1.00495,-2.4688 0.21825,-1.01967 0.38801,-2.05558 0.38421,-3.10149 -0.0761,-1.29512 -0.34601,-2.55043 -0.93895,-3.71574 -0.60728,-1.2882 -1.24849,-2.56502 -1.72185,-3.91075 -0.43768,-1.25837 -0.85635,-2.529 -1.07055,-3.84806 -0.053,-0.43231 -0.12968,-0.85315 -0.0855,-1.29165 0.0948,-0.94059 0.64999,-1.73394 1.21835,-2.45227 1.27656,-1.3752 2.72864,-2.57131 3.98645,-3.96542 1.00742,-1.20382 1.92485,-2.46496 2.104,-4.07351 0.17452,-1.45227 0.1673,-2.88462 -0.39573,-4.26425 -0.74413,-1.73945 -1.85028,-3.26977 -3.10544,-4.6809 -0.76822,-0.80351 -0.91233,-0.98548 -1.76144,-1.74089 -1.5106,-1.3439 -3.15419,-2.52947 -4.72414,-3.80078 -1.30934,-1.02359 -2.63319,-2.03185 -3.9193,-3.08473 -0.71006,-0.5813 -1.57278,-1.30278 -2.22387,-1.96185 -0.27049,-0.2738 -0.51217,-0.57463 -0.76826,-0.86194 -0.2502,-0.39692 -0.62512,-0.79772 -0.57512,-1.31981 0.0792,-0.82671 1.33713,-1.38331 1.899,-1.81756 1.29884,-0.86671 2.65474,-1.69887 3.73107,-2.84377 0.6873,-0.83746 1.4042,-1.79633 1.075,-2.93575 -0.0816,-0.13585 -0.14456,-0.28482 -0.24481,-0.40755 -0.1097,-0.13429 -0.23095,-0.26613 -0.3791,-0.356228 -1.82458,-1.109627 -3.48552,-2.166773 -5.4739,-2.822822 -1.27815,-0.438878 -2.56689,-0.847162 -3.87638,-1.183261 -1.32827,-0.341807 -2.66392,-0.6605 -4.021,-0.867076 -1.26646,-0.185139 -2.54065,-0.306124 -3.82011,-0.354118 -0.68835,-0.02484 -1.39159,-0.0088 -2.07079,-0.120664 z"
|
|
||||||
inkscape:label="头发部件17" /><path
|
|
||||||
style="display:inline;opacity:1;fill:#1a1a1a;fill-opacity:0.465732;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
id="path7"
|
|
||||||
d="m 472.56515,132.89174 c 0.0148,0.77317 -0.10076,1.54619 -0.19578,2.31209 -0.22663,1.66469 -0.65036,3.29152 -1.09324,4.90909 -0.61155,2.16519 -1.31643,4.30298 -2.07388,6.4211 -0.85906,2.40245 -1.88628,4.73722 -3.04362,7.0095 -1.12519,2.18837 -2.40662,4.28829 -3.77255,6.3331 -1.20044,1.78353 -2.51232,3.48695 -3.85961,5.16088 -0.77567,0.94691 -1.56273,1.88443 -2.34905,2.82249 0,0 3.09439,1.85764 3.09439,1.85764 v 0 c 0.78058,-0.94434 1.56325,-1.88703 2.331,-2.84187 1.33972,-1.69155 2.6509,-3.40675 3.84535,-5.20534 1.36483,-2.06191 2.64248,-4.17929 3.77385,-6.37958 1.16439,-2.28454 2.19746,-4.6314 3.07763,-7.04092 0.77266,-2.11878 1.49327,-4.25705 2.12518,-6.4224 0.46749,-1.62041 0.91079,-3.25003 1.1854,-4.9163 0.11547,-0.76691 0.21504,-1.53597 0.32143,-2.30416 z"
|
|
||||||
inkscape:label="头发部件16" /><path
|
|
||||||
style="display:inline;opacity:1;fill:#fff9f6;fill-opacity:1;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 322.62295,86.032787 c 0,0 75.43341,26.120553 110.16394,89.180323 33.44261,60.72132 33.04918,62.42623 33.04918,62.42623 0,0 0.52459,8.39345 -19.93443,11.54099 -2.62295,0 14.68852,30.95082 19.93443,37.2459 -14.68853,7.34426 -16.78689,8.91803 -16.78689,8.91803 0,0 6.29508,7.86885 4.72131,15.73771 -17.31147,-0.52459 -22.55738,-1.04918 -22.55738,-1.04918 0,0 -4.19672,17.31147 -15.7377,33.04918 C 410.22951,333.11475 410.22951,320 410.22951,320 c 0,0 -24.13115,36.72131 -72.39344,41.44262 -1.57377,-2.62295 12.59016,-14.16393 17.83606,-28.32787 -11.54098,1.57377 -14.68852,2.09836 -14.68852,2.09836 l 10.4918,-16.78688 c 0,0 11.01639,-24.13115 9.44262,-28.32787 -1.04918,-2.62295 -32,-57.70492 -32,-57.70492 0,0 -24.65573,33.04918 -43.54098,49.83607 -6.81967,-4.19672 -13.63934,-14.68853 -13.63934,-14.68853 l -12.59017,11.54099 c 0,0 -48.43778,-50.76436 -42.0945,-90.18318 -3.14754,12.06557 0.12729,56.60941 -5.6432,62.3799 -5.24591,-2.09836 -16.2623,-14.68853 -14.16394,-40.91803 -0.52459,-0.52459 -29.90164,41.44262 -29.90164,41.44262 0,0 -4.19672,48.78688 16.2623,73.44262 1.57377,2.62295 -1.57377,16.2623 -31.47541,-2.62295 1.04918,-1.57377 -1.04918,9.96721 -1.04918,9.96721 0,0 -46.68853,2.09836 -45.63935,-60.85245 -2.62295,-2.62296 -13.639341,34.09836 -13.639341,34.09836 0,0 -24.131151,-32 -0.52459,-92.85246 11.803281,-30.42623 23.344261,-52.85246 31.934421,-67.67213 8.59017,-14.81968 14.22951,-22.03279 14.22951,-22.03279"
|
|
||||||
id="path8"
|
|
||||||
sodipodi:nodetypes="csccccccccccccccccccccccccccssc"
|
|
||||||
inkscape:label="头发部件15 头发底色" /><path
|
|
||||||
id="path36"
|
|
||||||
style="fill:#fffdfc;stroke-width:7.4278;stroke-linecap:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
|
||||||
inkscape:label="头发打光"
|
|
||||||
d="m 355.67214,146.88524 a 91.803276,41.967213 0 0 1 -91.80327,41.96721 91.803276,41.967213 0 0 1 -91.80328,-41.96721 91.803276,41.967213 0 0 1 91.80328,-41.96721 91.803276,41.967213 0 0 1 91.80327,41.96721 z" /><path
|
|
||||||
style="fill:#fde9e7;fill-opacity:1;stroke:none;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 349.37705,329.44262 c 0,0 71.34426,-41.96721 78.68852,-91.27868 0,1.04918 26.22951,28.32786 26.22951,28.32786 l 11.54099,24.13115 -15.73771,7.34426 2.09836,13.63935 -22.03279,-1.04918 -12.59016,30.42623 -12.59016,-17.83607 -23.08197,26.22951 -45.11475,14.68852 z"
|
|
||||||
id="path37"
|
|
||||||
inkscape:label="头发阴影"
|
|
||||||
sodipodi:nodetypes="cccccccccccc" /><path
|
|
||||||
style="fill:#fde9e7;fill-opacity:1;stroke:none;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 325.2459,144.78689 c 0,0 10.49181,44.06557 2.09836,78.68852 0,10.4918 33.57377,60.85246 33.57377,60.85246 0,0 14.68853,-71.34426 -25.18032,-132.19672 -8.39345,-5.2459 -10.49181,-7.34426 -10.49181,-7.34426 z"
|
|
||||||
id="path38"
|
|
||||||
inkscape:label="头发阴影2" /><path
|
|
||||||
style="fill:#fde9e7;fill-opacity:1;stroke:none;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 186.7541,166.81967 23.08197,1.04918 c 0,0 -14.68853,26.22951 -13.63935,46.16394 -2.09836,-3.14754 -22.03279,31.47541 -22.03279,31.47541 0,0 -9.44262,-14.68853 -3.14754,-50.36066 9.44263,-16.78688 15.73771,-28.32787 15.73771,-28.32787 z"
|
|
||||||
id="path39"
|
|
||||||
inkscape:label="头发阴影" /><path
|
|
||||||
id="path36-5"
|
|
||||||
style="fill:#fffdfc;stroke-width:6.94619;stroke-linecap:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
|
||||||
inkscape:label="头发打光"
|
|
||||||
transform="matrix(0.99965843,-0.02613486,0,1,0,0)"
|
|
||||||
d="m 340.28017,112.23744 a 80.289719,41.967213 0 0 1 -80.28972,41.96721 80.289719,41.967213 0 0 1 -80.28972,-41.96721 80.289719,41.967213 0 0 1 80.28972,-41.967211 80.289719,41.967213 0 0 1 80.28972,41.967211 z" /><path
|
|
||||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#a18f90;stroke-width:7.7;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 138.62295,122.4918 c 0,0 53.63935,-52.983604 92.98361,-58.229505"
|
|
||||||
id="path65"
|
|
||||||
sodipodi:nodetypes="cc" /><path
|
|
||||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#c3b4b0;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 436.45902,268.06557 c 0,0 3.67213,18.36066 -2.62295,42.49181"
|
|
||||||
id="path17"
|
|
||||||
inkscape:label="头发部件7" /><path
|
|
||||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#c3b4b0;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 418.09836,305.83607 -8.91803,17.83606"
|
|
||||||
id="path16"
|
|
||||||
inkscape:label="头发部件8" /><path
|
|
||||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#c3b4b0;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 377.70492,300.59016 c 0,0 2.62295,12.06558 -22.03279,32"
|
|
||||||
id="path15"
|
|
||||||
inkscape:label="头发部件9" /><path
|
|
||||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#c3b4b0;stroke-width:4.2;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 284.32787,97.573771 c 0,0 12.06557,39.344259 -27.80328,119.081969 0.52459,0 53.5082,-38.81967 56.13115,-89.18033"
|
|
||||||
id="path10"
|
|
||||||
inkscape:label="头发部件14" /><path
|
|
||||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#c3b4b0;stroke-width:5.1;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 272.78689,87.081967 c 0,0 13.63934,-8.393442 18.88524,-0.52459 17.17267,10.839383 13.03281,30.236103 24.19672,44.393443 1.79263,1.85342 7.1718,-0.78131 7.80328,1.77049 4.88126,19.72513 14.81582,50.80617 4.32401,100.11764"
|
|
||||||
id="path11"
|
|
||||||
sodipodi:nodetypes="csssc"
|
|
||||||
inkscape:label="头发部件13" /><path
|
|
||||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#c3b4b0;stroke-width:4.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 171.00392,241.8537 c 0,0 -28.56248,-60.09248 92.36438,-147.634619 2.22564,0.741882 -59.35061,54.157419 -66.02755,111.282379"
|
|
||||||
id="path12"
|
|
||||||
inkscape:label="头发部件12" /><path
|
|
||||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#c3b4b0;stroke-width:3;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 327.34426,143.7377 c 0,0 25.18033,20.45902 32,65.57378"
|
|
||||||
id="path13"
|
|
||||||
inkscape:label="头发部件11" /><path
|
|
||||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 362.4918,268.59016 -3.14754,19.93443"
|
|
||||||
id="path14"
|
|
||||||
inkscape:label="头发部件10" /><path
|
|
||||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 421.77049,344.13115 c 0,0 -2.62295,58.22951 -33.57377,111.7377 -30.95082,53.5082 38.29508,15.73771 38.29508,15.73771"
|
|
||||||
id="path18"
|
|
||||||
inkscape:label="头发部件6" /><path
|
|
||||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 395.01639,348.32787 12.06558,60.32787"
|
|
||||||
id="path19"
|
|
||||||
inkscape:label="头发部件5" /><path
|
|
||||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 371.93443,423.86885 c 0,0 30.95082,-25.70492 24.65573,-73.96721"
|
|
||||||
id="path20"
|
|
||||||
inkscape:label="头发部件4" /><path
|
|
||||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 332.06557,427.54098 c 0,0 36.72132,1.57377 38.81968,-29.37705"
|
|
||||||
id="path21"
|
|
||||||
inkscape:label="头发部件3" /><path
|
|
||||||
style="opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 372.45902,427.01639 -2.09836,-28.85246"
|
|
||||||
id="path22"
|
|
||||||
inkscape:label="头发部件2" /><path
|
|
||||||
style="fill:#93cefc;fill-opacity:1;stroke:#4d4e59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 377.50929,228.61046 c 0,0 24.13115,1.57377 24.13115,10.49181 -0.52459,3.67213 -11.54099,17.83606 -23.60656,18.88524 -2.09836,-1.57377 -0.52459,-29.37705 -0.52459,-29.37705 z"
|
|
||||||
id="path34"
|
|
||||||
sodipodi:nodetypes="cccc"
|
|
||||||
inkscape:label="兔子发卡后" /><path
|
|
||||||
style="opacity:1;fill:#fffdfe;fill-opacity:1;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 350.95082,235.54098 c 0,0 -6.55738,-22.03278 1.04918,-22.03278 7.60656,0 13.37705,13.90164 13.37705,13.90164 0,0 5.05941,-17.72738 9.96722,-15.21311 11.79758,6.04389 3.40983,21.77048 3.40983,21.77048 0,0 18.62295,25.96722 -12.06558,27.27869 -30.16717,1.28919 -15.7377,-25.70492 -15.7377,-25.70492 z"
|
|
||||||
id="path24"
|
|
||||||
sodipodi:nodetypes="cscscsc"
|
|
||||||
inkscape:label="兔子发卡" /><path
|
|
||||||
style="display:inline;opacity:1;fill:#1a1a1a;fill-opacity:0;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 332.06557,425.96721 c 0,0 10.49181,-25.18032 6.29509,-49.83606"
|
|
||||||
id="path23"
|
|
||||||
inkscape:label="头发部件" /><path
|
|
||||||
style="display:inline;opacity:1;fill:#e85240;fill-opacity:1;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 192.2623,268.85246 c 0,0 -15.47541,22.03279 -2.62296,35.40984 42.7541,0.26229 43.27869,0.52459 43.27869,0.52459 0,0 11.27869,-8.39345 5.5082,-32 -25.44262,-9.96722 -46.16393,-3.93443 -46.16393,-3.93443 z"
|
|
||||||
id="path25"
|
|
||||||
inkscape:label="左眼白" /><path
|
|
||||||
style="opacity:1;fill:#fbb579;fill-opacity:1;stroke:none;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 188.06557,301.11475 c -0.44151,-8.78192 13.4528,-14.01636 22.29509,-14.42623 8.67244,-0.402 23.2398,3.38526 23.08196,12.06558 -0.0775,4.26198 -11.54098,5.5082 -11.54098,5.5082 0,0 -33.2673,8.16555 -33.83607,-3.14755 z"
|
|
||||||
id="path27"
|
|
||||||
sodipodi:nodetypes="saacs"
|
|
||||||
inkscape:label="左眼瞳" /><path
|
|
||||||
id="path26"
|
|
||||||
style="display:inline;opacity:1;fill:#ffffff;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
|
||||||
inkscape:label="眼光"
|
|
||||||
d="m 217.96721,273.04919 a 7.8688526,7.0819674 0 0 1 -7.86885,7.08197 7.8688526,7.0819674 0 0 1 -7.86885,-7.08197 7.8688526,7.0819674 0 0 1 7.86885,-7.08196 7.8688526,7.0819674 0 0 1 7.86885,7.08196 z" /><path
|
|
||||||
style="fill:none;fill-opacity:1;stroke:#ebc2bf;stroke-width:2.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 174.16393,245.5082 c 15.08644,-4.76649 31.53934,-7.45293 47.09358,-3.51527 3.29779,0.83486 6.51821,1.99257 9.56216,3.51527"
|
|
||||||
id="path42"
|
|
||||||
inkscape:path-effect="#path-effect42"
|
|
||||||
inkscape:original-d="m 174.16393,245.5082 c 11.60252,-3.79212 36.99244,-9.83166 56.65574,0"
|
|
||||||
transform="translate(0,4)" /><path
|
|
||||||
style="opacity:1;fill:#fde9e7;fill-opacity:1;stroke:none;stroke-width:6.945;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 477.77235,132.79697 c 0,0 13.35388,10.01542 14.83765,17.43424 1.48376,7.41883 1.48376,57.4959 1.48376,57.4959 l -4.45129,1.11282 -30.78813,-37.46507 c 0,0 8.90259,-14.46671 11.12824,-22.62741 2.22565,-8.16071 7.78977,-15.95048 7.78977,-15.95048 z"
|
|
||||||
id="path67" /><path
|
|
||||||
style="fill:#e85240;fill-opacity:0;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 192.2623,268.85246 c 0,0 -15.47541,22.03279 -2.62296,35.40984 42.7541,0.26229 43.27869,0.52459 43.27869,0.52459 0,0 11.27869,-8.39345 5.5082,-32 -25.44262,-9.96722 -46.16393,-3.93443 -46.16393,-3.93443 z"
|
|
||||||
id="path25-9"
|
|
||||||
inkscape:label="左眼遮挡上" /><path
|
|
||||||
style="display:inline;fill:#e85240;fill-opacity:1;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 325.52138,272.81871 c 17.02505,1.06627 38.81967,23.08196 17.83607,48.26229 -1.57377,0.52459 -48.98481,-5.30599 -49.57378,-9.18033 -0.80561,-5.29953 -7.95666,-41.56799 31.73771,-39.08196 z"
|
|
||||||
id="path40"
|
|
||||||
sodipodi:nodetypes="scss"
|
|
||||||
inkscape:label="右眼白" /><path
|
|
||||||
style="fill:#fbb579;fill-opacity:1;stroke:none;stroke-width:7.14054;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 293.04592,306.32136 c 2.55323,-8.53469 20.43105,-9.52962 30.78942,-7.36618 10.1594,2.12188 25.66784,9.9581 22.45763,18.22228 -1.5762,4.05766 -15.25453,1.93723 -15.25453,1.93723 0,0 -41.28165,-1.79873 -37.99252,-12.79333 z"
|
|
||||||
id="path27-0"
|
|
||||||
sodipodi:nodetypes="saacs"
|
|
||||||
inkscape:label="左眼瞳" /><path
|
|
||||||
style="display:inline;fill:none;fill-opacity:1;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 325.52138,272.81871 c 17.02505,1.06627 38.81967,23.08196 17.83607,48.26229 -1.57377,0.52459 -48.98481,-5.30599 -49.57378,-9.18033 -0.80561,-5.29953 -7.95666,-41.56799 31.73771,-39.08196 z"
|
|
||||||
id="path40-2"
|
|
||||||
sodipodi:nodetypes="scss"
|
|
||||||
inkscape:label="右眼遮挡上" /><path
|
|
||||||
style="fill:none;fill-opacity:1;stroke:#ebc2bf;stroke-width:2.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 309.32819,258.43459 c 0,0 26.2295,-5.2459 36.72131,4.19672"
|
|
||||||
id="path43" /><path
|
|
||||||
id="path26-9"
|
|
||||||
style="display:inline;fill:#ffffff;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
|
||||||
inkscape:label="眼光"
|
|
||||||
d="m 329.6342,284.99301 a 7.8688526,7.0819674 0 0 1 -7.86885,7.08197 7.8688526,7.0819674 0 0 1 -7.86885,-7.08197 7.8688526,7.0819674 0 0 1 7.86885,-7.08197 7.8688526,7.0819674 0 0 1 7.86885,7.08197 z" /><path
|
|
||||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#4c4f59;stroke-width:14.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 170.4918,266.4918 c 0,0 32,-19.93442 70.29509,-2.09836"
|
|
||||||
id="path28"
|
|
||||||
inkscape:label="眉毛" /><path
|
|
||||||
style="fill:none;fill-opacity:1;stroke:#4c4f59;stroke-width:14.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 292.9971,274.17683 c 0,0 35.55098,-12.55026 69.08832,13.13797"
|
|
||||||
id="path28-2"
|
|
||||||
inkscape:label="眉毛" /><path
|
|
||||||
style="display:inline;opacity:1;fill:none;fill-opacity:0;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 431.03375,176.1971 18.17612,-17.43424 41.54542,54.15742"
|
|
||||||
id="path30"
|
|
||||||
inkscape:label="蓝色发卡遮挡上" /><path
|
|
||||||
style="opacity:1;fill:none;fill-opacity:0;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 454.0321,267.44865 43.21466,-23.92571"
|
|
||||||
id="path31"
|
|
||||||
sodipodi:nodetypes="cc"
|
|
||||||
inkscape:label="蓝色发卡遮挡下" /><path
|
|
||||||
style="display:inline;fill:#fff9f6;fill-opacity:0;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 164.23888,424.64081 -31.07097,26.05282 c 0,0 -5.19317,18.17613 25.96589,28.93342 31.15907,10.7573 213.29123,1.48377 213.29123,1.48377 0,0 52.30272,11.87012 80.86519,-37.09413 -0.37094,0.74188 -8.90259,19.28895 -8.90259,19.28895 l 1.48377,19.28894 c 0,0 57.73516,-9.41425 30.45647,-148.95524 4.19672,-9.44262 8.39344,-15.21311 8.39344,-15.21311 l -3.14754,-21.77049 c 0,0 5.65758,-40.33533 7.88323,-45.52851 2.22564,-5.19317 8.51558,-6.35116 9.64447,-11.12823 2.06907,-8.75561 -2.78206,-20.40177 -6.67694,-26.15136 -0.74189,-3.33847 6.86241,-35.05395 2.04017,-58.05231 -7.04788,-11.87012 -4.82223,-11.87012 -18.918,-25.59495 0,0.37094 8.90259,-44.88389 -0.74188,-73.446367 -2.96753,0 -32.27189,-10.757296 -96.81567,23.740241 -0.37094,1.112824 -26.33683,-30.046242 -153.56969,-15.950474 -0.74188,0 -43.40012,-37.836009 -66.76942,-40.432598 -1.11283,-0.370941 -22.62742,-4.080354 -25.22401,48.964247 0,1.112824 -44.14201,15.950474 -56.01213,64.914721 -11.87012,48.96425 -11.128238,80.86519 -11.128238,80.86519 0,0 -18.904912,27.6632 -9.798121,25.03308 5.346826,-1.54421 4.975885,23.56023 4.975885,23.56023 l -2.225648,39.31977 c 0,0 -14.466709,35.23942 -14.83765,59.72155 -0.370942,24.48212 -0.741883,34.12659 4.822236,48.5933 5.564119,14.46671 20.772711,46.7386 42.658245,63.06002 8.902591,0.37094 18.918001,1.8547 18.918001,1.8547 l 3.70942,-20.77271 c 0,0 18.918,15.95048 32.27189,14.83765 13.35388,-1.11282 -0.74189,-0.74188 -0.74189,-0.74188 l -13.72482,-17.80518 z"
|
|
||||||
id="path2-4"
|
|
||||||
sodipodi:nodetypes="ccscccccccsaccccccccscsccsscccsccc"
|
|
||||||
inkscape:label="上发层" /><path
|
|
||||||
style="fill:none;fill-opacity:1;stroke:#c3b4b0;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 197.5082,220.06557 18.36065,-12.32786"
|
|
||||||
id="path46" /><path
|
|
||||||
style="fill:none;fill-opacity:1;stroke:#c3b4b0;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 327.54114,234.24941 c 0,0 -6.27192,4.90452 -15.85821,-0.991"
|
|
||||||
id="path47"
|
|
||||||
sodipodi:nodetypes="cc" /><path
|
|
||||||
style="display:inline;fill:none;fill-opacity:1;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 322.62295,86.03279 c 0,0 75.43341,26.12055 110.16394,89.18032 33.44261,60.72132 33.04918,62.42623 33.04918,62.42623 0,0 0.52459,8.39345 -19.93443,11.54099 -2.62295,0 14.68852,30.95082 19.93443,37.2459 -14.68853,7.34426 -16.78689,8.91803 -16.78689,8.91803 0,0 6.29508,7.86885 4.72131,15.73771 -17.31147,-0.52459 -22.55738,-1.04918 -22.55738,-1.04918 0,0 -4.19672,17.31147 -15.7377,33.04918 C 410.22951,333.11475 410.22951,320 410.22951,320 c 0,0 -24.13115,36.72131 -72.39344,41.44262 -1.57377,-2.62295 12.59016,-14.16393 17.83606,-28.32787 -11.54098,1.57377 -14.68852,2.09836 -14.68852,2.09836 l 10.4918,-16.78688 c 0,0 11.01639,-24.13115 9.44262,-28.32787 -1.04918,-2.62295 -32,-57.70492 -32,-57.70492 0,0 -24.65573,33.04918 -43.54098,49.83607 -6.81967,-4.19672 -13.63934,-14.68853 -13.63934,-14.68853 l -12.59017,11.54099 c 0,0 -48.43778,-50.76436 -42.0945,-90.18318 -3.14754,12.06557 0.12729,56.60941 -5.6432,62.3799 -5.24591,-2.09836 -16.2623,-14.68853 -14.16394,-40.91803 -0.52459,-0.52459 -29.90164,41.44262 -29.90164,41.44262 0,0 -4.19672,48.78688 16.2623,73.44262 1.57377,2.62295 -1.57377,16.2623 -31.47541,-2.62295 1.04918,-1.57377 -1.04918,9.96721 -1.04918,9.96721 0,0 -46.68853,2.09836 -45.63935,-60.85245 -2.62295,-2.62296 -13.63934,34.09836 -13.63934,34.09836 0,0 -24.13115,-32 -0.52459,-92.85246 11.80328,-30.42623 23.34426,-52.85246 31.93442,-67.67213 8.59017,-14.81968 14.22951,-22.03279 14.22951,-22.03279"
|
|
||||||
id="path8-0"
|
|
||||||
sodipodi:nodetypes="csccccccccccccccccccccccccccssc"
|
|
||||||
inkscape:label="头发部件15 头发底色" /><path
|
|
||||||
id="path44-3"
|
|
||||||
style="fill:#a88a8f;stroke:#4c4f59;stroke-width:2.8623;stroke-linecap:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
|
||||||
d="m 216.29183,255.53888 a 8.8904896,7.4928694 0 0 1 -8.89049,7.49287 8.8904896,7.4928694 0 0 1 -8.89049,-7.49287 8.8904896,7.4928694 0 0 1 8.89049,-7.49287 8.8904896,7.4928694 0 0 1 8.89049,7.49287 z" /><path
|
|
||||||
id="path44"
|
|
||||||
style="fill:#a88a8f;stroke:#4c4f59;stroke-width:2.8623;stroke-linecap:round;stroke-miterlimit:1.3;paint-order:fill markers stroke"
|
|
||||||
d="m 336.16933,270.5473 a 8.8904896,7.4928694 0 0 1 -8.89049,7.49287 8.8904896,7.4928694 0 0 1 -8.89049,-7.49287 8.8904896,7.4928694 0 0 1 8.89049,-7.49287 8.8904896,7.4928694 0 0 1 8.89049,7.49287 z" /><path
|
|
||||||
style="opacity:1;fill:none;fill-opacity:0;stroke:#a18f90;stroke-width:10.4;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 318.7189,85.461784 c 0,0 50.44801,17.063296 76.78484,43.400126 26.33683,26.33683 32.64283,39.31978 32.64283,39.31978"
|
|
||||||
id="path32"
|
|
||||||
inkscape:label="上方头发边缘加厚" /><path
|
|
||||||
style="fill:none;fill-opacity:1;stroke:#4c4f59;stroke-width:6.5;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 186.7541,224.52459 10.4918,1.04918"
|
|
||||||
id="path45" /><path
|
|
||||||
style="opacity:1;fill:#fcbeb5;fill-opacity:1;stroke:#e99c9b;stroke-width:3.4;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 250.94176,349.98308 c 0,0 -13.91029,-8.90259 -10.0154,-16.69235 3.89488,-7.78977 17.80517,-4.4513 17.80517,-4.4513 0,0 12.98295,-1.29829 20.03083,-3.89488 7.04788,-2.59659 9.64447,5.0077 9.64447,5.0077 0,0 4.76461,6.06544 -4.26583,16.50689 -5.93505,6.86241 -21.14364,7.04788 -21.14364,7.04788"
|
|
||||||
id="path48"
|
|
||||||
sodipodi:nodetypes="cscscsc" /><path
|
|
||||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#e99c9b;stroke-width:1.8;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 276.20884,325.76493 c 0,0 4.35031,3.01904 5.37456,7.95235 0.57119,0.10629 3.33355,-3.08729 2.97647,-6.87263 -0.67968,-1.48531 -8.35103,-1.07972 -8.35103,-1.07972 z"
|
|
||||||
id="path49" /><path
|
|
||||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#e99c9b;stroke-width:1.8;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 245.0067,329.39584 -4.17308,2.78206 -0.0927,6.0278 -0.0956,-0.0355 7.07328,-8.8174 z"
|
|
||||||
id="path50"
|
|
||||||
sodipodi:nodetypes="cccccc" /><path
|
|
||||||
style="fill:none;fill-opacity:1;stroke:#e99c9b;stroke-width:3.4;stroke-linecap:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 250.94176,349.98308 c 0,0 -13.91029,-8.90259 -10.0154,-16.69235 3.89488,-7.78977 17.80517,-4.4513 17.80517,-4.4513 0,0 12.98295,-1.29829 20.03083,-3.89488 7.04788,-2.59659 9.64447,5.0077 9.64447,5.0077 0,0 4.76461,6.06544 -4.26583,16.50689 -5.93505,6.86241 -21.14364,7.04788 -21.14364,7.04788"
|
|
||||||
id="path48-2"
|
|
||||||
sodipodi:nodetypes="cscscsc" /><path
|
|
||||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#e89493;stroke-width:2.8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 190.84928,314.92913 6.12053,15.02312"
|
|
||||||
id="path51" /><path
|
|
||||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#e89493;stroke-width:2.8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 204.94505,316.96931 5.56412,14.28124"
|
|
||||||
id="path52" /><path
|
|
||||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#e89493;stroke-width:2.8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 217.92799,317.34025 7.41883,16.50689"
|
|
||||||
id="path53" /><path
|
|
||||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#e89493;stroke-width:2.8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 300.64789,328.46849 6.86242,18.17612"
|
|
||||||
id="path54" /><path
|
|
||||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#e89493;stroke-width:2.8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 312.70348,329.21037 6.67695,17.0633"
|
|
||||||
id="path55" /><path
|
|
||||||
style="opacity:1;fill:#ffffff;fill-opacity:1;stroke:#e89493;stroke-width:2.8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 326.24284,328.65396 7.04788,17.99065"
|
|
||||||
id="path56" /><path
|
|
||||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#e49589;stroke-width:4.6;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 218.4918,348.98361 c -1.87307,1.30472 -3.33608,3.11401 -4.70333,4.91603 -1.02497,1.35089 -2.00187,2.75003 -2.77208,4.2643"
|
|
||||||
id="path61"
|
|
||||||
inkscape:path-effect="#path-effect61"
|
|
||||||
inkscape:original-d="m 218.4918,348.98361 c -1.70592,0.99645 -5.82749,5.88449 -7.47541,9.18033"
|
|
||||||
transform="rotate(-3.3120949,219.55256,344.63259)" /><path
|
|
||||||
style="opacity:1;fill:none;fill-opacity:1;stroke:#4c4f59;stroke-width:3.2;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 127.47541,375.08197 c 0,0 -14.42623,-19.14754 -12.06557,-59.54099"
|
|
||||||
id="path92" /><path
|
|
||||||
style="opacity:1;fill:#ffffff;fill-opacity:0.734285;stroke:#c1545a;stroke-width:7.8;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 136.39344,322.09836 c 0,0 9.44263,-18.88525 -4.19672,-39.86885 -3.14754,-0.52459 27.80328,-27.80328 5.2459,-77.11476 -1.57377,0.52459 23.60656,-55.60655 -24.13115,-82.88524 -4.19672,-8.39344 -45.639336,-69.245904 -89.180316,13.11475 0,11.54099 -1.04918,36.19672 -1.04918,36.19672 0,0 -14.6885299,38.81968 -6.29509,71.86886 2.09837,3.14754 -20.9835999,49.83606 11.0164,106.4918 0.52459,0.52459 -16.78689,57.70492 12.59016,82.88525 1.04918,4.72131 13.63935,33.57377 42.49181,39.34426 12.59016,0.52459 19.409826,-0.52459 19.409826,-0.52459 0,0 31.47541,-4.72131 47.73771,-67.14754 -2.09836,-27.80328 -4.19672,-50.36066 -11.0164,-70.81968"
|
|
||||||
id="path93" /><path
|
|
||||||
id="path101"
|
|
||||||
style="opacity:1;fill:#ffffff;fill-opacity:0.734285;stroke:#000000;stroke-width:9;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 109.69769,219.48859 c 0,0 -15.208592,-11.87012 -21.143652,-19.65989 m -37.09413,20.03083 22.25648,-21.88553 m 0.37094,-14.09577 5.93506,41.91636 m -35.23942,-24.11118 56.012132,-4.82224 m 11.12824,-17.43424 -19.288952,0.37094 m -0.37094,-0.74188 -1.48376,-29.6753 m -38.94883,3.33847 37.836,-2.96753 m -24.85306,-11.49918 0.74188,39.69072"
|
|
||||||
inkscape:label="杂" /><path
|
|
||||||
id="path111"
|
|
||||||
style="opacity:1;fill:#ffffff;fill-opacity:0.734285;stroke:#000000;stroke-width:9;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:1.3;stroke-dasharray:none;stroke-opacity:1;paint-order:fill markers stroke"
|
|
||||||
d="m 58.507798,352.28556 54.157422,-3.33847 m -46.367662,-13.72483 35.610372,-2.96753 m -21.514602,-32.27189 2.59659,33.38472 m -18.918,-16.32142 37.094122,-3.33847 m 1.48377,18.54706 0.74188,-33.75565 m -0.37094,0.37094 -40.803542,2.22565 m 0,-0.37094 4.4513,33.38471 m 27.44965,-48.22237 3.33847,-14.4667 m 0,-1.48377 -27.44965,1.48377 m -1.48377,14.83765 0.74188,-20.03083"
|
|
||||||
inkscape:label="鱼" /><path
|
|
||||||
d="m 69.453989,376.46984 c -4.220665,0.22086 -8.079406,1.8518 -10.783191,4.54882 -2.980547,2.98155 -4.241957,7.10139 -3.534076,11.54824 0.904812,5.67433 4.763552,11.7394 11.384623,17.90216 1.405115,1.30393 2.39508,2.14912 4.390984,3.74184 3.507462,2.79468 6.679618,5.01174 11.04931,7.70876 1.559468,0.96838 4.635815,2.76496 5.796096,3.38931 l 0.335314,0.18263 0.48966,-0.26758 c 1.783005,-0.98111 4.960482,-2.86264 6.844609,-4.05189 7.126702,-4.49782 12.832312,-9.01266 17.063632,-13.50625 6.86057,-7.28827 9.77725,-14.38543 8.43068,-20.49298 -0.93142,-4.20475 -4.04502,-7.68327 -8.46261,-9.45012 -1.90011,-0.76026 -3.74699,-1.15525 -5.83869,-1.24869 -2.7091,-0.12316 -5.37562,0.33977 -7.91973,1.37185 -4.332435,1.75413 -7.951675,5.17315 -10.229661,9.66674 -0.159681,0.3143 -0.308701,0.59038 -0.329985,0.60737 -0.0692,0.0553 -0.143707,-0.051 -0.431122,-0.62859 -0.681263,-1.35913 -2.01719,-3.3341 -3.065699,-4.52331 -1.07513,-1.22747 -2.804909,-2.72251 -4.092931,-3.54224 -3.326503,-2.11937 -7.222503,-3.15569 -11.097213,-2.95607 z"
|
|
||||||
id="path1-4"
|
|
||||||
style="fill:#d96477;fill-opacity:1;stroke:none;stroke-width:0.00475452" /></g></svg>
|
|
||||||
|
Before Width: | Height: | Size: 60 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 66 KiB |
@@ -1,101 +0,0 @@
|
|||||||
import { defineConfig, type DefaultTheme } from 'vitepress'
|
|
||||||
|
|
||||||
export default defineConfig({
|
|
||||||
lang: 'zh-Hans',
|
|
||||||
description: '下一代 Android Root 解决方案 - Android 上的内核级的高级 root 方案',
|
|
||||||
|
|
||||||
themeConfig: {
|
|
||||||
nav: nav(),
|
|
||||||
|
|
||||||
sidebar: {
|
|
||||||
'/zh/': { base: '/zh/', items: sidebar() },
|
|
||||||
},
|
|
||||||
|
|
||||||
search: { options: searchOptions() },
|
|
||||||
editLink: {
|
|
||||||
pattern: 'https://github.com/sukisu-ultra/sukisu-ultra/edit/main/docs/:path',
|
|
||||||
text: '在 GitHub 上编辑此页面',
|
|
||||||
},
|
|
||||||
|
|
||||||
docFooter: {
|
|
||||||
prev: '上一页',
|
|
||||||
next: '下一页',
|
|
||||||
},
|
|
||||||
|
|
||||||
outline: {
|
|
||||||
label: '页面导航',
|
|
||||||
},
|
|
||||||
|
|
||||||
lastUpdated: {
|
|
||||||
text: '最后更新于',
|
|
||||||
},
|
|
||||||
|
|
||||||
notFound: {
|
|
||||||
title: '页面未找到',
|
|
||||||
quote: '抱歉,我们无法找到您要查找的页面。',
|
|
||||||
linkLabel: '前往首页',
|
|
||||||
linkText: '带我回首页',
|
|
||||||
},
|
|
||||||
|
|
||||||
langMenuLabel: '多语言',
|
|
||||||
returnToTopLabel: '回到顶部',
|
|
||||||
sidebarMenuLabel: '菜单',
|
|
||||||
darkModeSwitchLabel: '主题',
|
|
||||||
lightModeSwitchTitle: '切换到浅色模式',
|
|
||||||
darkModeSwitchTitle: '切换到深色模式',
|
|
||||||
skipToContentLabel: '跳转到内容',
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
function nav(): DefaultTheme.NavItem[] {
|
|
||||||
return [
|
|
||||||
{ text: '首页', link: '/zh/' },
|
|
||||||
{
|
|
||||||
text: '开始使用',
|
|
||||||
items: [
|
|
||||||
{ text: '介绍', link: '/zh/guide/' },
|
|
||||||
{ text: '安装', link: '/zh/guide/installation' },
|
|
||||||
{ text: '集成', link: '/zh/guide/how-to-integrate' },
|
|
||||||
{ text: '兼容性', link: '/zh/guide/compatibility' },
|
|
||||||
{ text: '链接', link: '/zh/guide/links' },
|
|
||||||
{ text: '许可', link: '/zh/guide/license' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
function sidebar(): DefaultTheme.SidebarItem[] {
|
|
||||||
return [
|
|
||||||
{
|
|
||||||
text: '开始使用',
|
|
||||||
items: [
|
|
||||||
{ text: '介绍', link: '/guide/' },
|
|
||||||
{ text: '安装', link: '/guide/installation' },
|
|
||||||
{ text: '集成', link: '/guide/how-to-integrate' },
|
|
||||||
{ text: '兼容性', link: '/guide/compatibility' },
|
|
||||||
{ text: '链接', link: '/guide/links' },
|
|
||||||
{ text: '许可', link: '/guide/license' },
|
|
||||||
],
|
|
||||||
},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
function searchOptions(): Partial<DefaultTheme.LocalSearchOptions> {
|
|
||||||
return {
|
|
||||||
translations: {
|
|
||||||
button: {
|
|
||||||
buttonText: '搜索文档',
|
|
||||||
buttonAriaLabel: '搜索文档',
|
|
||||||
},
|
|
||||||
modal: {
|
|
||||||
noResultsText: '无法找到相关结果',
|
|
||||||
resetButtonTitle: '清除查询条件',
|
|
||||||
footer: {
|
|
||||||
selectText: '选择',
|
|
||||||
navigateText: '切换',
|
|
||||||
closeText: '关闭',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
# 兼容性状态
|
|
||||||
|
|
||||||
::: info KernelSU
|
|
||||||
KernelSU(v0.9.5 之前的版本)官方支持 Android GKI 2.0 设备(内核 5.10+)
|
|
||||||
:::
|
|
||||||
|
|
||||||
::: warning 传统内核支持
|
|
||||||
较旧的内核(4.4+)也兼容,但内核必须手动构建
|
|
||||||
:::
|
|
||||||
|
|
||||||
::: tip 扩展兼容性
|
|
||||||
SukiSU-Ultra 可以通过额外的反向移植支持 3.x 内核(3.4-3.18)
|
|
||||||
:::
|
|
||||||
|
|
||||||
## 架构支持
|
|
||||||
|
|
||||||
目前支持以下处理器架构:
|
|
||||||
|
|
||||||
| 架构 | 支持级别 | 备注 |
|
|
||||||
| --------------- | :---------: | -----------: |
|
|
||||||
| **arm64-v8a** | ✅ 完全支持 | 主要目标架构 |
|
|
||||||
| **armeabi-v7a** | ✅ 基础支持 | 最低功能要求 |
|
|
||||||
| **X86_64** | 🟡 部分支持 | 支持部分设备 |
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
# 集成指导
|
|
||||||
|
|
||||||
SukiSU 可以集成到 GKI 和 non-GKI 内核中,并且已反向移植到 4.14 版本。
|
|
||||||
|
|
||||||
<!-- 应该是 3.4 版本,但 backslashxx 的 syscall manual hook 无法在 SukiSU 中使用-->
|
|
||||||
|
|
||||||
有些 OEM 定制可能导致多达 50% 的内核代码超出内核树代码,而非来自上游 Linux 内核或 ACK。因此,non-GKI 内核的定制特性导致了严重的内核碎片化,而且我们缺乏构建它们的通用方法。因此,我们无法提供 non-GKI 内核的启动映像。
|
|
||||||
|
|
||||||
前提条件:开源的、可启动的内核。
|
|
||||||
|
|
||||||
## Hook 方法
|
|
||||||
|
|
||||||
1. **KPROBES hook:**
|
|
||||||
- GKI kernels 的默认 hook 方法。
|
|
||||||
- 需要 `# CONFIG_KSU_MANUAL_HOOK is not set`(未设定) & `CONFIG_KPROBES=y`
|
|
||||||
- 用作可加载的内核模块 (LKM).
|
|
||||||
|
|
||||||
2. **Manual hook:**
|
|
||||||
|
|
||||||
<!-- - backslashxx's syscall manual hook: https://github.com/backslashxx/KernelSU/issues/5 (v1.5 version is not available at the moment, if you want to use it, please use v1.4 version, or standard KernelSU hooks)-->
|
|
||||||
- 需要 `CONFIG_KSU_MANUAL_HOOK=y`
|
|
||||||
- 需要 [`guide/how-to-integrate.md`](how-to-integrate.md)
|
|
||||||
- 需要 [https://github.com/~](https://github.com/tiann/KernelSU/blob/main/website/docs/guide/how-to-integrate-for-non-gki.md#manually-modify-the-kernel-source)
|
|
||||||
|
|
||||||
3. **Tracepoint Hook:**
|
|
||||||
- 自 SukiSU commit [49b01aad](https://github.com/SukiSU-Ultra/SukiSU-Ultra/commit/49b01aad74bcca6dba5a8a2e053bb54b648eb124) 引入的 hook 方法
|
|
||||||
- 需要 `CONFIG_KSU_TRACEPOINT_HOOK=y`
|
|
||||||
- 需要 [`guide/tracepoint-hook.md`](tracepoint-hook.md)
|
|
||||||
|
|
||||||
<!-- This part refer to [rsuntk/KernelSU](https://github.com/rsuntk/KernelSU). -->
|
|
||||||
|
|
||||||
如果您能够构建可启动内核,有两种方法可以将 KernelSU 集成到内核源代码中:
|
|
||||||
|
|
||||||
1. 使用 `kprobe` 自动集成
|
|
||||||
2. 手动集成
|
|
||||||
|
|
||||||
## 与 kprobe 集成
|
|
||||||
|
|
||||||
适用:
|
|
||||||
|
|
||||||
- GKI 内核
|
|
||||||
|
|
||||||
不适用:
|
|
||||||
|
|
||||||
- non-GKI 内核
|
|
||||||
|
|
||||||
KernelSU 使用 kprobe 机制来做内核的相关 hook,如果 _kprobe_ 可以在你编译的内核中正常运行,那么推荐用这个方法来集成。
|
|
||||||
|
|
||||||
请参阅此文档 [https://github.com/~](https://github.com/tiann/KernelSU/blob/main/website/docs/guide/how-to-integrate-for-non-gki.md#integrate-with-kprobe)。虽然标题为“适用于 non-GKI”,但仅适用于 GKI。
|
|
||||||
|
|
||||||
替换 KernelSU 添加到内核源代码树的步骤的执行命令为:
|
|
||||||
|
|
||||||
```sh [bash]
|
|
||||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s main
|
|
||||||
```
|
|
||||||
|
|
||||||
## 手动修改内核源代码
|
|
||||||
|
|
||||||
适用:
|
|
||||||
|
|
||||||
- GKI 内核
|
|
||||||
- non-GKI 内核
|
|
||||||
|
|
||||||
请参考此文档 [https://github.com/~ (non-GKI 内核集成)](https://github.com/tiann/KernelSU/blob/main/website/docs/guide/how-to-integrate-for-non-gki.md#manually-modify-the-kernel-source) 和 [https://github.com/~ (GKI 内核构建)](https://kernelsu.org/zh_CN/guide/how-to-build.html) 进行手动集成。虽然第一个链接的标题是“适用于 non-GKI”,但它也适用于 GKI。两者都可以正常工作。
|
|
||||||
|
|
||||||
还有另一种集成方法,但是仍在开发中。
|
|
||||||
|
|
||||||
<!-- 这是 backslashxx 的syscall manual hook,但目前无法使用。 -->
|
|
||||||
|
|
||||||
将 KernelSU(SukiSU)添加到内核源代码树的步骤的运行命令将被替换为:
|
|
||||||
|
|
||||||
### GKI 内核
|
|
||||||
|
|
||||||
```sh [bash]
|
|
||||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s main
|
|
||||||
```
|
|
||||||
|
|
||||||
### non-GKI 内核
|
|
||||||
|
|
||||||
```sh [bash]
|
|
||||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s nongki
|
|
||||||
```
|
|
||||||
|
|
||||||
### 带有 susfs 的 GKI / non-GKI 内核(实验)
|
|
||||||
|
|
||||||
```sh [bash]
|
|
||||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s susfs-{{branch}}
|
|
||||||
```
|
|
||||||
|
|
||||||
分支:
|
|
||||||
|
|
||||||
- `main` (susfs-main)
|
|
||||||
- `test` (susfs-test)
|
|
||||||
- 版本号 (例如: susfs-1.5.7, 你需要在 [分支](https://github.com/SukiSU-Ultra/SukiSU-Ultra/branches) 里找到它)
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
# 什么是 SukiSU-Ultra?
|
|
||||||
|
|
||||||
一个 Android 上基于内核的 root 方案,由 [`tiann/KernelSU`](https://github.com/tiann/KernelSU) 分叉而来,添加了一些有趣的变更。
|
|
||||||
|
|
||||||
## 特性
|
|
||||||
|
|
||||||
1. 基于内核的 su 和权限管理。
|
|
||||||
2. 基于 Magic Mount 的模块系统。
|
|
||||||
3. App Profile: 把 Root 权限关进笼子里。
|
|
||||||
4. 支持 non-GKI 与 GKI 1.0。
|
|
||||||
5. KPM 支持
|
|
||||||
6. 可调整管理器外观,可自定义 susfs 配置。
|
|
||||||
|
|
||||||
## 如何安装
|
|
||||||
|
|
||||||
了解如何在您的设备上 **[安装](./installation)** SukiSU-Ultra
|
|
||||||
|
|
||||||
## 如何集成
|
|
||||||
|
|
||||||
阅读 **[集成](./how-to-integrate.md)** 将 SukiSU 集成到你的内核中
|
|
||||||
|
|
||||||
## 了解兼容性
|
|
||||||
|
|
||||||
检查设备 **[兼容性](./compatibility)** 要求
|
|
||||||
|
|
||||||
## 资源获取
|
|
||||||
|
|
||||||
查找其他资源和 **[下载](./links)**
|
|
||||||
|
|
||||||
## 获取支持
|
|
||||||
|
|
||||||
需要帮助?我们在这里为您提供帮助:
|
|
||||||
|
|
||||||
- **错误报告**: [GitHub Issues](https://github.com/SukiSU-Ultra/SukiSU-Ultra/issues)
|
|
||||||
- **直接支持**: 联系开发者处理关键问题
|
|
||||||
@@ -1,149 +0,0 @@
|
|||||||
# 安装参考
|
|
||||||
|
|
||||||
::: details 将会了解 SukiSU-Ultra 安装过程
|
|
||||||
[[toc]]
|
|
||||||
:::
|
|
||||||
|
|
||||||
::: danger 警告
|
|
||||||
**Root 您的设备可能会使保修失效,如果操作不当可能会造成永久性损坏。**
|
|
||||||
请务必在继续之前创建完整备份,阅读文档确保与您的设备兼容,遵循文档参考,准备好恢复计划
|
|
||||||
:::
|
|
||||||
|
|
||||||
## 通用 GKI
|
|
||||||
|
|
||||||
请**全部**参考 [KernelSU 安装指南](https://kernelsu.org/zh_CN/guide/installation.html)
|
|
||||||
|
|
||||||
1. 适用于 GKI 2.0 设备,如小米、红米、三星等(不包括内核修改的制造商,如魅族、一加、真我和 OPPO)
|
|
||||||
2. 在[更多链接](./links)中查找 GKI 构建。找到设备内核版本,然后下载并使用 TWRP 或内核刷写工具刷入带有 `AnyKernel3` 后缀的
|
|
||||||
zip 文件。
|
|
||||||
3. 无后缀的 `.zip` 档案是未压缩的,`gz` 后缀是特定型号使用的压缩格式。
|
|
||||||
|
|
||||||
## 一加设备
|
|
||||||
|
|
||||||
使用[更多链接](./links)部分提到的链接,用您的设备信息创建自定义构建,然后刷入带有 AnyKernel3 后缀的 zip 文件。
|
|
||||||
|
|
||||||
::: details 展开
|
|
||||||
|
|
||||||
- 只需要填写内核版本的前两部分,如 `5.10`、`5.15`、`6.1` 或 `6.6`。
|
|
||||||
- 请自行搜索处理器代号,通常是不含数字的英文字母。
|
|
||||||
- 可以从一加开源内核仓库中找到分支和配置文件。
|
|
||||||
- 第三方 Recovery(推荐 TWRP)
|
|
||||||
|
|
||||||
:::
|
|
||||||
|
|
||||||
## 开始安装
|
|
||||||
|
|
||||||
### 通用 GKI 安装
|
|
||||||
|
|
||||||
::: tip
|
|
||||||
适用于 GKI 2.0 设备,如小米、红米、三星等(不包括内核修改的制造商,如魅族、一加、真我和 OPPO)
|
|
||||||
:::
|
|
||||||
|
|
||||||
#### 步骤:
|
|
||||||
|
|
||||||
1. 下载 GKI 构建文件
|
|
||||||
|
|
||||||
从我们的[资源部分](./links)查找 GKI 构建。查找您设备的内核版本并下载带有 `AnyKernel3` 后缀的 `zip` 文件。
|
|
||||||
|
|
||||||
2. 通过 Recovery 刷入
|
|
||||||
|
|
||||||
启动到 `TWRP recovery`,选择 **Install**,导航到下载的 `AnyKernel3 zip` 文件,滑动刷入后重启系统
|
|
||||||
|
|
||||||
3. [验证安装](#验证)
|
|
||||||
|
|
||||||
::: details 文件格式说明
|
|
||||||
无后缀的 `.zip` 档案是未压缩的,`gz` 后缀是特定型号使用的压缩格式。
|
|
||||||
:::
|
|
||||||
|
|
||||||
### 一加设备安装
|
|
||||||
|
|
||||||
1. 获取设备信息
|
|
||||||
|
|
||||||
- 内核版本(前两部分,例如 `5.10`、`5.15`、`6.1`、`6.6`)
|
|
||||||
- 处理器代号(通常是不含数字的英文)
|
|
||||||
- 来自一加开源内核仓库的分支和配置文件
|
|
||||||
|
|
||||||
2. 创建自定义构建
|
|
||||||
|
|
||||||
使用我们[资源部分](./links)中提到的链接,用您的设备信息创建自定义构建。
|
|
||||||
|
|
||||||
3. 刷入构建
|
|
||||||
|
|
||||||
下载生成的带有 AnyKernel3 后缀的 zip 文件,启动到 `TWRP recovery`,选择 **Install**,导航到下载的 `AnyKernel3 zip` 文件,滑动刷入后重启系统
|
|
||||||
|
|
||||||
::: tip
|
|
||||||
您只需要填写内核版本的前两部分。自行搜索处理器代号
|
|
||||||
:::
|
|
||||||
|
|
||||||
### 手动内核集成
|
|
||||||
|
|
||||||
面向希望将 SukiSU Ultra 集成到自己内核构建中的高级用户
|
|
||||||
|
|
||||||
#### 主分支(GKI)
|
|
||||||
|
|
||||||
```sh [bash]
|
|
||||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s main
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 非 GKI 分支
|
|
||||||
|
|
||||||
```sh [bash]
|
|
||||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s nongki
|
|
||||||
```
|
|
||||||
|
|
||||||
#### SUSFS-Dev 分支(推荐)
|
|
||||||
|
|
||||||
```sh [bash]
|
|
||||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s susfs-dev
|
|
||||||
```
|
|
||||||
|
|
||||||
```bash
|
|
||||||
curl -LSs "https://raw.githubusercontent.com/SukiSU-Ultra/SukiSU-Ultra/main/kernel/setup.sh" | bash -s susfs-main
|
|
||||||
```
|
|
||||||
|
|
||||||
::: warning 必需的内核配置
|
|
||||||
为了支持 KPM,添加 `CONFIG_KPM=y`
|
|
||||||
|
|
||||||
对于非 GKI 设备,还要添加 `CONFIG_KALLSYMS=y` 和 `CONFIG_KALLSYMS_ALL=y`
|
|
||||||
:::
|
|
||||||
|
|
||||||
## 安装后设置
|
|
||||||
|
|
||||||
### 系统更新时保持 Root
|
|
||||||
|
|
||||||
> [!IMPORTANT]
|
|
||||||
> 如何在 OTA 更新后保持 root 访问权限:
|
|
||||||
|
|
||||||
#### 步骤:
|
|
||||||
|
|
||||||
1. 系统更新后重启前
|
|
||||||
- OTA 安装后不要立即重启,将 SukiSU Ultra 安装到第二插槽
|
|
||||||
- 打开 SukiSU Ultra 管理器,在**刷入/修补内核**界面选择 **GKI/non_GKI install**,选择您的 `AnyKernel3` 内核 `zip` 文件,选择与当前运行插槽相对的插槽然后刷入重启。
|
|
||||||
|
|
||||||
2. 替代方案:LKM 模式
|
|
||||||
|
|
||||||
使用 [LKM 模式](#通用-gki) 在 OTA 后安装到未使用的插槽。
|
|
||||||
|
|
||||||
::: warning 警告
|
|
||||||
**非 GKI 设备注意事项:** 此方法不支持所有非 GKI 设备。对于非 GKI 设备,使用 TWRP 是最安全的方法。
|
|
||||||
:::
|
|
||||||
|
|
||||||
## 验证
|
|
||||||
|
|
||||||
安装后,验证一切是否正常工作,
|
|
||||||
|
|
||||||
- 打开 SukiSU Ultra 管理器检查 root 工作状态
|
|
||||||
- 使用 root 权限的应用程序来验证 root 访问是否工作正常
|
|
||||||
- 在设置 -> 关于手机中检查内核版本
|
|
||||||
|
|
||||||
## 需要帮助?
|
|
||||||
|
|
||||||
如果在安装过程中遇到问题:
|
|
||||||
|
|
||||||
1. 查看我们的[兼容性指南](./compatibility)了解设备要求
|
|
||||||
2. 访问我们的 [GitHub 仓库](https://github.com/sukisu-ultra/sukisu-ultra)获取支持
|
|
||||||
3. 加入我们的 [Telegram 社区](https://t.me/sukiksu)获取帮助
|
|
||||||
|
|
||||||
::: danger 安全提醒
|
|
||||||
**始终有备用计划!** 保留您的原始 `boot.img/init_boot.img` 并知道如何在出现问题时恢复设备。
|
|
||||||
:::
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
# 许可证
|
|
||||||
|
|
||||||
## 软件许可
|
|
||||||
|
|
||||||
### 内核组件
|
|
||||||
|
|
||||||
::: info GPL-2.0 许可证
|
|
||||||
"kernel" 目录中的文件采用 GPL-2.0-only 许可证
|
|
||||||
:::
|
|
||||||
|
|
||||||
**许可证:** [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
|
||||||
|
|
||||||
### 应用程序核心
|
|
||||||
|
|
||||||
::: tip GPL-3.0 许可证
|
|
||||||
所有其他部分(除下述特别说明外)采用 GPL-3.0 或更高版本许可证
|
|
||||||
:::
|
|
||||||
|
|
||||||
**许可证:** [GPL-3.0 或更高版本](https://www.gnu.org/licenses/gpl-3.0.html)
|
|
||||||
|
|
||||||
## 艺术作品与品牌资产
|
|
||||||
|
|
||||||
### 启动器图标与角色艺术
|
|
||||||
|
|
||||||
::: warning 版权声明
|
|
||||||
动画角色艺术作品有特殊许可要求
|
|
||||||
:::
|
|
||||||
|
|
||||||
包含动画角色表情的文件 `ic_launcher(?!.*alt.*).*` 图像有特定的版权条款:
|
|
||||||
|
|
||||||
**版权持有者:**
|
|
||||||
|
|
||||||
- **动画角色艺术:** [怡子曰曰](https://space.bilibili.com/10545509)
|
|
||||||
- **品牌知识产权:** [明风OuO](https://space.bilibili.com/274939213)
|
|
||||||
- **矢量化制作:** @MiRinChan
|
|
||||||
|
|
||||||
**许可要求:**
|
|
||||||
|
|
||||||
1. **知识共享许可证:** [CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.txt)
|
|
||||||
2. **作者授权:** 需要获得两位版权持有者的授权
|
|
||||||
3. **署名要求:** 必须署名上述所有贡献者
|
|
||||||
|
|
||||||
::: details 使用要求
|
|
||||||
在使用这些艺术资产之前,您必须:
|
|
||||||
|
|
||||||
- 遵守知识共享署名-非商业性使用-相同方式共享 4.0 国际许可证
|
|
||||||
- 获得两位原作者对艺术内容使用的授权
|
|
||||||
- 为所有贡献者提供适当的署名
|
|
||||||
:::
|
|
||||||
|
|
||||||
## 摘要
|
|
||||||
|
|
||||||
| 组件 | 许可证 | 备注 |
|
|
||||||
| ---------------- | ------------------------------------------------------------------------- | ----------------------- |
|
|
||||||
| **内核文件** | [GPL-2.0-only](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html) | `/kernel/` 目录中的文件 |
|
|
||||||
| **应用程序代码** | [GPL-3.0+](https://www.gnu.org/licenses/gpl-3.0.html) | 主要应用程序组件 |
|
|
||||||
| **角色艺术** | [CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/) | + 需要作者授权 |
|
|
||||||
| **品牌资产** | 混合许可 | 查看具体署名要求 |
|
|
||||||
|
|
||||||
## 链接
|
|
||||||
|
|
||||||
- **GPL-2.0:** [完整许可证文本](https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html)
|
|
||||||
- **GPL-3.0:** [完整许可证文本](https://www.gnu.org/licenses/gpl-3.0.html)
|
|
||||||
- **CC BY-NC-SA 4.0:** [完整许可证文本](https://creativecommons.org/licenses/by-nc-sa/4.0/legalcode.txt)
|
|
||||||
|
|
||||||
## 问题
|
|
||||||
|
|
||||||
关于许可证或使用权限的问题:
|
|
||||||
|
|
||||||
1. **代码许可:** 参考相应的 GPL 许可证条款
|
|
||||||
2. **艺术作品使用:** 联系原作者获取授权
|
|
||||||
3. **商业使用:** 查看 CC BY-NC-SA 4.0 限制条款
|
|
||||||
4. **分发:** 确保遵守所有适用的许可证
|
|
||||||
|
|
||||||
::: tip 合规提示
|
|
||||||
在重新分发或修改 SukiSU-Ultra 时,请确保遵守各个组件的所有适用许可证和署名要求。
|
|
||||||
:::
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
# 更多链接
|
|
||||||
|
|
||||||
## 参与翻译
|
|
||||||
|
|
||||||
::: info 贡献翻译
|
|
||||||
如果您需要为管理器提交翻译,请访问我们的 Crowdin 项目
|
|
||||||
:::
|
|
||||||
|
|
||||||
**翻译平台:** [Crowdin - SukiSU-Ultra](https://crowdin.com/project/SukiSU-Ultra)
|
|
||||||
|
|
||||||
## 项目与构建
|
|
||||||
|
|
||||||
基于 Sukisu 和 susfs 编译的项目:
|
|
||||||
|
|
||||||
### GKI 构建
|
|
||||||
|
|
||||||
::: tip 通用 GKI 支持
|
|
||||||
集成 KernelSU 和 SUSFS 的通用内核映像构建
|
|
||||||
:::
|
|
||||||
|
|
||||||
**仓库:** [GKI_KernelSU_SUSFS](https://github.com/ShirkNeko/GKI_KernelSU_SUSFS)
|
|
||||||
|
|
||||||
### OnePlus 构建
|
|
||||||
|
|
||||||
::: tip 设备特定构建
|
|
||||||
带有 MKSU 和 SUSFS 的自动化 OnePlus 内核构建
|
|
||||||
:::
|
|
||||||
|
|
||||||
**仓库:** [Action_OnePlus_MKSU_SUSFS](https://github.com/ShirkNeko/Action_OnePlus_MKSU_SUSFS)
|
|
||||||
|
|
||||||
## 社区与支持
|
|
||||||
|
|
||||||
### Telegram 社区
|
|
||||||
|
|
||||||
> 与其他用户联系,获取支持并保持更新
|
|
||||||
|
|
||||||
**主群组:** [Tg Group](https://t.me/sukiksu)
|
|
||||||
|
|
||||||
### 测试构建
|
|
||||||
|
|
||||||
::: warning 实验性构建
|
|
||||||
测试构建是实验性的,可能不稳定
|
|
||||||
:::
|
|
||||||
|
|
||||||
**测试构建频道:** [最新测试构建](https://t.me/Sukiksu/7114)
|
|
||||||
|
|
||||||
## 下载与发布
|
|
||||||
|
|
||||||
### 官方发布
|
|
||||||
|
|
||||||
> 从我们的 GitHub 发布页面下载最新稳定版本
|
|
||||||
|
|
||||||
**GitHub 发布:** [SukiSU-Ultra 发布](https://github.com/sukisu-ultra/sukisu-ultra/releases)
|
|
||||||
|
|
||||||
### 问题报告
|
|
||||||
|
|
||||||
> 在我们的 GitHub 仓库上报告错误或请求新功能
|
|
||||||
|
|
||||||
**GitHub 问题:** [报告问题](https://github.com/sukisu-ultra/sukisu-ultra/issues)
|
|
||||||
|
|
||||||
## 快速链接汇总
|
|
||||||
|
|
||||||
| 资源 | 链接 | 描述 |
|
|
||||||
| ----------------- | :--------------------------------------------------------------------: | -----------: |
|
|
||||||
| **翻译** | [Crowdin](https://crowdin.com/project/SukiSU-Ultra) | 提交翻译 |
|
|
||||||
| **Telegram 群组** | [t.me/sukiksu](https://t.me/sukiksu) | 社区支持 |
|
|
||||||
| **测试构建** | [测试频道](https://t.me/Sukiksu/7114) | 实验性构建 |
|
|
||||||
| **发布** | [GitHub 发布](https://github.com/sukisu-ultra/sukisu-ultra/releases) | 稳定下载 |
|
|
||||||
| **问题** | [GitHub 问题](https://github.com/sukisu-ultra/sukisu-ultra/issues) | 错误报告 |
|
|
||||||
| **GKI 构建** | [GKI 仓库](https://github.com/ShirkNeko/GKI_KernelSU_SUSFS) | 通用构建 |
|
|
||||||
| **OnePlus 构建** | [OnePlus 仓库](https://github.com/ShirkNeko/Action_OnePlus_MKSU_SUSFS) | 设备特定构建 |
|
|
||||||
@@ -1,266 +0,0 @@
|
|||||||
# Tracepoint Hook 集成
|
|
||||||
|
|
||||||
## 介绍
|
|
||||||
|
|
||||||
自 commit [49b01aad](https://github.com/SukiSU-Ultra/SukiSU-Ultra/commit/49b01aad74bcca6dba5a8a2e053bb54b648eb124) 起,SukiSU 引入了 Tracepoint Hook
|
|
||||||
|
|
||||||
**该 Hook 理论上相比于 Kprobes Hook,性能开销更小,但次于 Manual Hook / Syscall Hook**
|
|
||||||
|
|
||||||
::: warning
|
|
||||||
目前 Tracepoint Hook 在 6.x 设备上并不稳定,请勿使用,否则可能出现无法开机或无法获取 `ROOT` 权限等问题。
|
|
||||||
:::
|
|
||||||
|
|
||||||
> [!NOTE]
|
|
||||||
> 本教程参考了 [backslashxx/KernelSU#5](https://github.com/backslashxx/KernelSU/issues/5) 的 syscall hook v1.4 版本钩子,以及原版 KernelSU 的 [Manual Hook](https://kernelsu.org/guide/how-to-integrate-for-non-gki.html#manually-modify-the-kernel-source)
|
|
||||||
|
|
||||||
## 在内核中放置 TP 钩子
|
|
||||||
|
|
||||||
::: code-group
|
|
||||||
|
|
||||||
```diff[exec.c]
|
|
||||||
--- a/fs/exec.c
|
|
||||||
+++ b/fs/exec.c
|
|
||||||
@@ -78,6 +78,10 @@
|
|
||||||
#include <trace/hooks/sched.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
|
||||||
+#include <../drivers/kernelsu/ksu_trace.h>
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
EXPORT_TRACEPOINT_SYMBOL_GPL(task_rename);
|
|
||||||
|
|
||||||
static int bprm_creds_from_file(struct linux_binprm *bprm);
|
|
||||||
@@ -2037,6 +2041,9 @@ static int do_execve(struct filename *filename,
|
|
||||||
{
|
|
||||||
struct user_arg_ptr argv = { .ptr.native = __argv };
|
|
||||||
struct user_arg_ptr envp = { .ptr.native = __envp };
|
|
||||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
|
||||||
+ trace_ksu_trace_execveat_hook((int *)AT_FDCWD, &filename, &argv, &envp, 0);
|
|
||||||
+#endif
|
|
||||||
return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -2064,6 +2071,9 @@ static int compat_do_execve(struct filename *filename,
|
|
||||||
.is_compat = true,
|
|
||||||
.ptr.compat = __envp,
|
|
||||||
};
|
|
||||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
|
||||||
+ trace_ksu_trace_execveat_sucompat_hook((int *)AT_FDCWD, &filename, NULL, NULL, NULL); /* 32-bit su */
|
|
||||||
+#endif
|
|
||||||
return do_execveat_common(AT_FDCWD, filename, argv, envp, 0);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```diff[open.c]
|
|
||||||
--- a/fs/open.c
|
|
||||||
+++ b/fs/open.c
|
|
||||||
@@ -37,6 +37,10 @@
|
|
||||||
#include "internal.h"
|
|
||||||
#include <trace/hooks/syscall_check.h>
|
|
||||||
|
|
||||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
|
||||||
+#include <../drivers/kernelsu/ksu_trace.h>
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
int do_truncate(struct user_namespace *mnt_userns, struct dentry *dentry,
|
|
||||||
loff_t length, unsigned int time_attrs, struct file *filp)
|
|
||||||
{
|
|
||||||
@@ -468,6 +472,9 @@ static long do_faccessat(int dfd, const char __user *filename, int mode, int fla
|
|
||||||
|
|
||||||
SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
|
|
||||||
{
|
|
||||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
|
||||||
+ trace_ksu_trace_faccessat_hook(&dfd, &filename, &mode, NULL);
|
|
||||||
+#endif
|
|
||||||
return do_faccessat(dfd, filename, mode, 0);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```diff[read_write.c]
|
|
||||||
--- a/fs/read_write.c
|
|
||||||
+++ b/fs/read_write.c
|
|
||||||
@@ -25,6 +25,10 @@
|
|
||||||
#include <linux/uaccess.h>
|
|
||||||
#include <asm/unistd.h>
|
|
||||||
|
|
||||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
|
||||||
+#include <../drivers/kernelsu/ksu_trace.h>
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
const struct file_operations generic_ro_fops = {
|
|
||||||
.llseek = generic_file_llseek,
|
|
||||||
.read_iter = generic_file_read_iter,
|
|
||||||
@@ -630,6 +634,9 @@ ssize_t ksys_read(unsigned int fd, char __user *buf, size_t count)
|
|
||||||
|
|
||||||
SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
|
|
||||||
{
|
|
||||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
|
||||||
+ trace_ksu_trace_sys_read_hook(fd, &buf, &count);
|
|
||||||
+#endif
|
|
||||||
return ksys_read(fd, buf, count);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```diff[stat.c]
|
|
||||||
--- a/fs/stat.c
|
|
||||||
+++ b/fs/stat.c
|
|
||||||
@@ -24,6 +24,10 @@
|
|
||||||
#include "internal.h"
|
|
||||||
#include "mount.h"
|
|
||||||
|
|
||||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
|
||||||
+#include <../drivers/kernelsu/ksu_trace.h>
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
/**
|
|
||||||
* generic_fillattr - Fill in the basic attributes from the inode struct
|
|
||||||
* @mnt_userns: user namespace of the mount the inode was found from
|
|
||||||
@@ -408,6 +412,10 @@ SYSCALL_DEFINE4(newfstatat, int, dfd, const char __user *, filename,
|
|
||||||
struct kstat stat;
|
|
||||||
int error;
|
|
||||||
|
|
||||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
|
||||||
+ trace_ksu_trace_stat_hook(&dfd, &filename, &flag);
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
error = vfs_fstatat(dfd, filename, &stat, flag);
|
|
||||||
if (error)
|
|
||||||
return error;
|
|
||||||
@@ -559,6 +567,10 @@ SYSCALL_DEFINE4(fstatat64, int, dfd, const char __user *, filename,
|
|
||||||
struct kstat stat;
|
|
||||||
int error;
|
|
||||||
|
|
||||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
|
||||||
+ trace_ksu_trace_stat_hook(&dfd, &filename, &flag); /* 32-bit su support */
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
error = vfs_fstatat(dfd, filename, &stat, flag);
|
|
||||||
if (error)
|
|
||||||
return error;
|
|
||||||
```
|
|
||||||
|
|
||||||
:::
|
|
||||||
通常是要改四个地方:
|
|
||||||
|
|
||||||
1. compat_do_execve,通常位于 `fs/exec.c`
|
|
||||||
2. do_faccessat,通常位于 `/fs/open.c`
|
|
||||||
3. sys_read,通常位于 `fs/read_write.c`
|
|
||||||
4. newfstatat SYSCALL,通常位于 `fs/stat.c`
|
|
||||||
|
|
||||||
如果没有 do_faccessat 方法,可以找 faccessat 的 SYSCALL 定义(对于早于 4.17 的内核)
|
|
||||||
|
|
||||||
```diff
|
|
||||||
--- a/fs/open.c
|
|
||||||
+++ b/fs/open.c
|
|
||||||
@@ -31,6 +31,9 @@
|
|
||||||
#include <linux/ima.h>
|
|
||||||
#include <linux/dnotify.h>
|
|
||||||
#include <linux/compat.h>
|
|
||||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
|
||||||
+#include <../drivers/kernelsu/ksu_trace.h>
|
|
||||||
+#endif
|
|
||||||
|
|
||||||
#include "internal.h"
|
|
||||||
|
|
||||||
@@ -369,6 +372,9 @@ SYSCALL_DEFINE3(faccessat, int, dfd, const char __user *, filename, int, mode)
|
|
||||||
int res;
|
|
||||||
unsigned int lookup_flags = LOOKUP_FOLLOW;
|
|
||||||
|
|
||||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
|
||||||
+ trace_ksu_trace_faccessat_hook(&dfd, &filename, &mode, NULL);
|
|
||||||
+#endif
|
|
||||||
if (mode & ~S_IRWXO) /* where's F_OK, X_OK, W_OK, R_OK? */
|
|
||||||
return -EINVAL;
|
|
||||||
```
|
|
||||||
|
|
||||||
如果没有 sys_read 方法,并且 4.14 及以下需要修改 read 的 SYSCALL 定义
|
|
||||||
|
|
||||||
```diff
|
|
||||||
--- a/fs/read_write.c
|
|
||||||
+++ b/fs/read_write.c
|
|
||||||
@@ -25,6 +25,11 @@
|
|
||||||
#include <linux/uaccess.h>
|
|
||||||
#include <asm/unistd.h>
|
|
||||||
|
|
||||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
|
||||||
+#include <../drivers/kernelsu/ksu_trace.h>
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
+
|
|
||||||
const struct file_operations generic_ro_fops = {
|
|
||||||
.llseek = generic_file_llseek,
|
|
||||||
.read_iter = generic_file_read_iter,
|
|
||||||
@@ -575,6 +580,9 @@ SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
|
|
||||||
|
|
||||||
if (f.file) {
|
|
||||||
loff_t pos = file_pos_read(f.file);
|
|
||||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
|
||||||
+ trace_ksu_trace_sys_read_hook(fd, &buf, &count);
|
|
||||||
+#endif
|
|
||||||
ret = vfs_read(f.file, buf, count, &pos);
|
|
||||||
if (ret >= 0)
|
|
||||||
file_pos_write(f.file, pos);
|
|
||||||
```
|
|
||||||
|
|
||||||
## 安全模式
|
|
||||||
|
|
||||||
要使用 KernelSU 内置的安全模式,你还需要修改 `drivers/input/input.c` 中的 input_handle_event 方法:
|
|
||||||
|
|
||||||
```diff
|
|
||||||
--- a/drivers/input/input.c
|
|
||||||
+++ b/drivers/input/input.c
|
|
||||||
@@ -26,6 +26,10 @@
|
|
||||||
#include "input-compat.h"
|
|
||||||
#include "input-poller.h"
|
|
||||||
|
|
||||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
|
||||||
+#include <../../drivers/kernelsu/ksu_trace.h>
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
|
|
||||||
MODULE_DESCRIPTION("Input core");
|
|
||||||
MODULE_LICENSE("GPL");
|
|
||||||
@@ -451,6 +455,10 @@ void input_event(struct input_dev *dev,
|
|
||||||
{
|
|
||||||
unsigned long flags;
|
|
||||||
|
|
||||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
|
||||||
+ trace_ksu_trace_input_hook(&type, &code, &value);
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
if (is_event_supported(type, dev->evbit, EV_MAX)) {
|
|
||||||
|
|
||||||
spin_lock_irqsave(&dev->event_lock, flags);
|
|
||||||
```
|
|
||||||
|
|
||||||
## pm 命令执行失败?
|
|
||||||
|
|
||||||
你需要修改 `drivers/tty/pty.c`
|
|
||||||
|
|
||||||
```diff
|
|
||||||
--- a/drivers/tty/pty.c
|
|
||||||
+++ b/drivers/tty/pty.c
|
|
||||||
@@ -31,6 +31,10 @@
|
|
||||||
#include <linux/compat.h>
|
|
||||||
#include "tty.h"
|
|
||||||
|
|
||||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
|
||||||
+#include <../../drivers/kernelsu/ksu_trace.h>
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
#undef TTY_DEBUG_HANGUP
|
|
||||||
#ifdef TTY_DEBUG_HANGUP
|
|
||||||
# define tty_debug_hangup(tty, f, args...) tty_debug(tty, f, ##args)
|
|
||||||
@@ -707,6 +711,10 @@ static struct tty_struct *pts_unix98_lookup(struct tty_driver *driver,
|
|
||||||
{
|
|
||||||
struct tty_struct *tty;
|
|
||||||
|
|
||||||
+#if defined(CONFIG_KSU) && defined(CONFIG_KSU_TRACEPOINT_HOOK)
|
|
||||||
+ trace_ksu_trace_devpts_hook((struct inode *)file->f_path.dentry->d_inode);
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
mutex_lock(&devpts_mutex);
|
|
||||||
tty = devpts_get_priv(file->f_path.dentry);
|
|
||||||
mutex_unlock(&devpts_mutex);
|
|
||||||
```
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
---
|
|
||||||
layout: home
|
|
||||||
|
|
||||||
hero:
|
|
||||||
name: 'SukiSU-Ultra'
|
|
||||||
text: '下一代 Android Root 解决方案'
|
|
||||||
tagline: Android 上的内核级的高级 root 方案
|
|
||||||
image:
|
|
||||||
src: /logo.svg
|
|
||||||
alt: SukiSU-Ultra
|
|
||||||
actions:
|
|
||||||
- theme: brand
|
|
||||||
text: 开始使用
|
|
||||||
link: /zh/guide/
|
|
||||||
- theme: alt
|
|
||||||
text: 在 GitHub 上查看
|
|
||||||
link: https://github.com/sukisu-ultra/sukisu-ultra
|
|
||||||
|
|
||||||
features:
|
|
||||||
- title: 内核级的 su 和 root 权限管理
|
|
||||||
details: 在内核进行安全的 root 权限管理。
|
|
||||||
|
|
||||||
- title: 不基于 OverlayFS 的模块系统
|
|
||||||
details: 模块系统基于来自 5ec1cff 的 Magic Mount。
|
|
||||||
|
|
||||||
- title: App Profile
|
|
||||||
details: 把 root 权限关进笼子里。
|
|
||||||
|
|
||||||
- title: 重新支持非 GKI 与 GKI 1.0 内核
|
|
||||||
details: 增强对旧设备的兼容性。
|
|
||||||
|
|
||||||
- title: 更多自定义选项
|
|
||||||
details: 提供广泛的自定义选项。
|
|
||||||
|
|
||||||
- title: 支持 KPM
|
|
||||||
details: 完整的基于 KernelPatch 的 KPM 功能。
|
|
||||||
---
|
|
||||||
|
Before Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 144 KiB |
|
Before Width: | Height: | Size: 58 KiB |
@@ -1,26 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "SukiSU",
|
|
||||||
"short_name": "SukiSU",
|
|
||||||
"icons": [
|
|
||||||
{
|
|
||||||
"src": "/favicon.svg",
|
|
||||||
"type": "image/svg+xml",
|
|
||||||
"purpose": "any"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "/web-app-manifest-192x192.png",
|
|
||||||
"sizes": "192x192",
|
|
||||||
"type": "image/png",
|
|
||||||
"purpose": "any maskable"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"src": "/web-app-manifest-512x512.png",
|
|
||||||
"sizes": "512x512",
|
|
||||||
"type": "image/png",
|
|
||||||
"purpose": "any maskable"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"theme_color": "#000000",
|
|
||||||
"background_color": "#000000",
|
|
||||||
"display": "standalone"
|
|
||||||
}
|
|
||||||
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 66 KiB |
@@ -1,57 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "sukisu-ultra-docs",
|
|
||||||
"version": "2.0.0",
|
|
||||||
"description": "SukiSU-Ultra Documentation - Next-Generation Android Root Solution",
|
|
||||||
"type": "module",
|
|
||||||
"scripts": {
|
|
||||||
"dev": "tsx scripts/rebuild-ico.ts && tsx scripts/sync-favicons.ts && vitepress dev docs --host",
|
|
||||||
"build": "tsx scripts/sync-favicons.ts && vitepress build docs && pnpm build:sw:dist",
|
|
||||||
"preview": "vitepress preview docs",
|
|
||||||
"build:sw": "tsc scripts/sw.ts --target ES2022 --lib ES2022,WebWorker --outDir docs/public --skipLibCheck",
|
|
||||||
"build:sw:dist": "tsc scripts/sw.ts --target ES2022 --lib ES2022,WebWorker --outDir docs/.vitepress/dist --skipLibCheck",
|
|
||||||
"build:scripts": "pnpm build:sw",
|
|
||||||
"type-check": "tsc --noEmit",
|
|
||||||
"format": "prettier --write .",
|
|
||||||
"format:check": "prettier --check ."
|
|
||||||
},
|
|
||||||
"keywords": [
|
|
||||||
"android",
|
|
||||||
"root",
|
|
||||||
"kernelsu",
|
|
||||||
"sukisu",
|
|
||||||
"documentation",
|
|
||||||
"vitepress"
|
|
||||||
],
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/sukisu-ultra/sukisu-ultra.git"
|
|
||||||
},
|
|
||||||
"homepage": "https://sukisu.org",
|
|
||||||
"bugs": {
|
|
||||||
"url": "https://github.com/sukisu-ultra/sukisu-ultra/issues"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@types/node": "^24.3.0",
|
|
||||||
"@types/serviceworker": "^0.0.152",
|
|
||||||
"markdown-it-footnote": "^4.0.0",
|
|
||||||
"markdown-it-mark": "^4.0.0",
|
|
||||||
"markdown-it-mathjax3": "^4.3.2",
|
|
||||||
"markdown-it-sub": "^2.0.0",
|
|
||||||
"markdown-it-task-lists": "^2.1.1",
|
|
||||||
"prettier": "^3.6.2",
|
|
||||||
"terser": "^5.44.0",
|
|
||||||
"ts-node": "^10.9.2",
|
|
||||||
"tsx": "^4.20.5",
|
|
||||||
"typescript": "^5.9.2",
|
|
||||||
"vitepress": "1.6.4",
|
|
||||||
"vue": "^3.5.21"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": ">=22"
|
|
||||||
},
|
|
||||||
"packageManager": "pnpm@10.17.1",
|
|
||||||
"dependencies": {
|
|
||||||
"@nolebase/vitepress-plugin-git-changelog": "^2.18.2",
|
|
||||||
"vitepress-plugin-group-icons": "^1.6.3"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
packages:
|
|
||||||
- docs
|
|
||||||
- .
|
|
||||||
|
|
||||||
onlyBuiltDependencies:
|
|
||||||
- esbuild
|
|
||||||
@@ -1,207 +0,0 @@
|
|||||||
/// <reference lib="webworker" />
|
|
||||||
|
|
||||||
const CACHE_NAME: string = 'sukisu-ultra-v2.0'
|
|
||||||
const STATIC_CACHE: string = 'sukisu-static-v2.0'
|
|
||||||
const RUNTIME_CACHE: string = 'sukisu-runtime-v2.0'
|
|
||||||
const IMAGE_CACHE: string = 'sukisu-images-v2.0'
|
|
||||||
|
|
||||||
const CRITICAL_ASSETS: string[] = [
|
|
||||||
'/',
|
|
||||||
'/guide/',
|
|
||||||
'/guide/installation',
|
|
||||||
'/guide/compatibility',
|
|
||||||
'/zh/',
|
|
||||||
'/zh/guide/',
|
|
||||||
'/logo.svg',
|
|
||||||
'/favicon.ico',
|
|
||||||
'/offline.html',
|
|
||||||
]
|
|
||||||
|
|
||||||
const CACHE_STRATEGIES: {
|
|
||||||
static: string[]
|
|
||||||
images: string[]
|
|
||||||
documents: string[]
|
|
||||||
} = {
|
|
||||||
static: ['.css', '.js', '.woff2', '.woff', '.ttf', '.otf'],
|
|
||||||
images: ['.png', '.jpg', '.jpeg', '.svg', '.webp', '.avif', '.gif', '.ico'],
|
|
||||||
documents: ['.html', '.json', '.xml'],
|
|
||||||
}
|
|
||||||
|
|
||||||
self.addEventListener('install', (event: ExtendableEvent) => {
|
|
||||||
event.waitUntil(
|
|
||||||
(async (): Promise<void> => {
|
|
||||||
const cache: Cache = await caches.open(STATIC_CACHE)
|
|
||||||
try {
|
|
||||||
await cache.addAll(CRITICAL_ASSETS)
|
|
||||||
} catch (error) {
|
|
||||||
console.warn('Failed to cache some assets:', error)
|
|
||||||
}
|
|
||||||
;(self as any).skipWaiting()
|
|
||||||
})()
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
self.addEventListener('activate', (event: ExtendableEvent) => {
|
|
||||||
event.waitUntil(
|
|
||||||
(async (): Promise<void> => {
|
|
||||||
const cacheNames: string[] = await caches.keys()
|
|
||||||
const validCaches: string[] = [STATIC_CACHE, RUNTIME_CACHE, IMAGE_CACHE, CACHE_NAME]
|
|
||||||
|
|
||||||
await Promise.all(
|
|
||||||
cacheNames
|
|
||||||
.filter((name: string) => !validCaches.includes(name))
|
|
||||||
.map((name: string) => caches.delete(name))
|
|
||||||
)
|
|
||||||
;(self as any).clients.claim()
|
|
||||||
})()
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
self.addEventListener('fetch', (event: FetchEvent) => {
|
|
||||||
const { request } = event
|
|
||||||
const url: URL = new URL(request.url)
|
|
||||||
|
|
||||||
if (request.method !== 'GET' || url.origin !== location.origin) return
|
|
||||||
if (url.pathname.startsWith('/api/')) return
|
|
||||||
|
|
||||||
event.respondWith(handleRequest(request, url))
|
|
||||||
})
|
|
||||||
|
|
||||||
async function handleRequest(request: Request, url: URL): Promise<Response> {
|
|
||||||
const isStatic: boolean = CACHE_STRATEGIES.static.some((ext: string) =>
|
|
||||||
url.pathname.endsWith(ext)
|
|
||||||
)
|
|
||||||
const isImage: boolean = CACHE_STRATEGIES.images.some((ext: string) => url.pathname.endsWith(ext))
|
|
||||||
const isDocument: boolean =
|
|
||||||
CACHE_STRATEGIES.documents.some((ext: string) => url.pathname.endsWith(ext)) ||
|
|
||||||
url.pathname.endsWith('/')
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (isStatic) {
|
|
||||||
return await handleStatic(request)
|
|
||||||
} else if (isImage) {
|
|
||||||
return await handleImage(request)
|
|
||||||
} else if (isDocument) {
|
|
||||||
return await handleDocument(request)
|
|
||||||
} else {
|
|
||||||
return await handleDefault(request)
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
return await handleOffline(request)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleStatic(request: Request): Promise<Response> {
|
|
||||||
const cache: Cache = await caches.open(STATIC_CACHE)
|
|
||||||
const cached: Response | undefined = await cache.match(request)
|
|
||||||
|
|
||||||
if (cached) {
|
|
||||||
fetch(request)
|
|
||||||
.then((response: Response) => {
|
|
||||||
if (response.ok) cache.put(request, response.clone())
|
|
||||||
})
|
|
||||||
.catch(() => {})
|
|
||||||
|
|
||||||
return cached
|
|
||||||
}
|
|
||||||
|
|
||||||
const response: Response = await fetch(request)
|
|
||||||
if (response.ok) {
|
|
||||||
cache.put(request, response.clone())
|
|
||||||
}
|
|
||||||
return response
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleImage(request: Request): Promise<Response> {
|
|
||||||
const cache: Cache = await caches.open(IMAGE_CACHE)
|
|
||||||
const cached: Response | undefined = await cache.match(request)
|
|
||||||
|
|
||||||
if (cached) return cached
|
|
||||||
|
|
||||||
const response: Response = await fetch(request)
|
|
||||||
if (response.ok) {
|
|
||||||
cache.put(request, response.clone())
|
|
||||||
}
|
|
||||||
return response
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleDocument(request: Request): Promise<Response> {
|
|
||||||
const cache: Cache = await caches.open(RUNTIME_CACHE)
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response: Response = await fetch(request)
|
|
||||||
if (response.ok) {
|
|
||||||
cache.put(request, response.clone())
|
|
||||||
}
|
|
||||||
return response
|
|
||||||
} catch (error) {
|
|
||||||
const cached: Response | undefined = await cache.match(request)
|
|
||||||
if (cached) return cached
|
|
||||||
|
|
||||||
if (request.mode === 'navigate') {
|
|
||||||
const offlinePage: Response | undefined = await caches.match('/offline.html')
|
|
||||||
if (offlinePage) return offlinePage
|
|
||||||
}
|
|
||||||
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleDefault(request: Request): Promise<Response> {
|
|
||||||
const cache: Cache = await caches.open(RUNTIME_CACHE)
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response: Response = await fetch(request)
|
|
||||||
if (response.ok) {
|
|
||||||
cache.put(request, response.clone())
|
|
||||||
}
|
|
||||||
return response
|
|
||||||
} catch (error) {
|
|
||||||
const cached: Response | undefined = await cache.match(request)
|
|
||||||
if (cached) return cached
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function handleOffline(request: Request): Promise<Response> {
|
|
||||||
const cache: Cache = await caches.open(STATIC_CACHE)
|
|
||||||
|
|
||||||
if (request.mode === 'navigate') {
|
|
||||||
const offlinePage: Response | undefined = await caches.match('/offline.html')
|
|
||||||
if (offlinePage) return offlinePage
|
|
||||||
}
|
|
||||||
|
|
||||||
return new Response('Offline', {
|
|
||||||
status: 503,
|
|
||||||
statusText: 'Service Unavailable',
|
|
||||||
headers: new Headers({
|
|
||||||
'Content-Type': 'text/plain',
|
|
||||||
}),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
self.addEventListener('sync', (event: any) => {
|
|
||||||
if (event.tag === 'data-sync') {
|
|
||||||
event.waitUntil(syncData())
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
async function syncData(): Promise<void> {
|
|
||||||
try {
|
|
||||||
console.log('Syncing application data...')
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Data sync failed:', error)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
interface PerformanceMessage {
|
|
||||||
type: string
|
|
||||||
name: string
|
|
||||||
}
|
|
||||||
|
|
||||||
self.addEventListener('message', (event: MessageEvent) => {
|
|
||||||
const data = event.data as PerformanceMessage
|
|
||||||
if (data && data.type === 'PERFORMANCE_MARK') {
|
|
||||||
performance.mark(data.name)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
import fs from 'fs'
|
|
||||||
import path from 'path'
|
|
||||||
import { fileURLToPath } from 'url'
|
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url)
|
|
||||||
const __dirname = path.dirname(__filename)
|
|
||||||
|
|
||||||
const srcDir = path.join(__dirname, '../favicon')
|
|
||||||
const dstDir = path.join(__dirname, '../docs/public')
|
|
||||||
|
|
||||||
function ensureDir(dir: string) {
|
|
||||||
if (!fs.existsSync(dir)) fs.mkdirSync(dir, { recursive: true })
|
|
||||||
}
|
|
||||||
|
|
||||||
function copyAll(src: string, dst: string) {
|
|
||||||
ensureDir(dst)
|
|
||||||
const entries = fs.readdirSync(src, { withFileTypes: true })
|
|
||||||
let count = 0
|
|
||||||
for (const entry of entries) {
|
|
||||||
const s = path.join(src, entry.name)
|
|
||||||
const d = path.join(dst, entry.name)
|
|
||||||
if (entry.isDirectory()) {
|
|
||||||
copyAll(s, d)
|
|
||||||
} else if (entry.isFile()) {
|
|
||||||
fs.copyFileSync(s, d)
|
|
||||||
count++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return count
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!fs.existsSync(srcDir)) {
|
|
||||||
console.warn('Favicons source folder not found:', srcDir)
|
|
||||||
process.exit(0)
|
|
||||||
}
|
|
||||||
|
|
||||||
const copied = copyAll(srcDir, dstDir)
|
|
||||||
console.log(`✅ Synced ${copied} favicon file(s) from \'favicon\' to docs/public`)
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"target": "ES2022",
|
|
||||||
"module": "ESNext",
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"allowSyntheticDefaultImports": true,
|
|
||||||
"strict": true,
|
|
||||||
"skipLibCheck": true,
|
|
||||||
"forceConsistentCasingInFileNames": true,
|
|
||||||
"resolveJsonModule": true,
|
|
||||||
"declaration": true,
|
|
||||||
"outDir": "./dist",
|
|
||||||
"rootDir": "./",
|
|
||||||
"types": ["node", "serviceworker"],
|
|
||||||
"lib": ["ES2022", "WebWorker"],
|
|
||||||
"ignoreDeprecations": "5.0"
|
|
||||||
},
|
|
||||||
"ts-node": {
|
|
||||||
"esm": true,
|
|
||||||
"experimentalSpecifierResolution": "node"
|
|
||||||
},
|
|
||||||
"include": ["scripts/**/*"],
|
|
||||||
"exclude": ["node_modules", "dist", "docs/.vitepress/dist"]
|
|
||||||
}
|
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
# Cloudflare Pages Configuration for SukiSU-Ultra
|
|
||||||
name = "sukisu-ultra-docs"
|
|
||||||
compatibility_date = "2025-08-04"
|
|
||||||
compatibility_flags = ["nodejs_compat"]
|
|
||||||
pages_build_output_dir = "docs/.vitepress/dist"
|
|
||||||
|
|
||||||
[env.production]
|
|
||||||
name = "sukisu-ultra-docs"
|
|
||||||
|
|
||||||
[env.preview]
|
|
||||||
name = "sukisu-ultra-docs-preview"
|
|
||||||
@@ -8,6 +8,8 @@
|
|||||||
#define PER_USER_RANGE 100000
|
#define PER_USER_RANGE 100000
|
||||||
#define FIRST_APPLICATION_UID 10000
|
#define FIRST_APPLICATION_UID 10000
|
||||||
#define LAST_APPLICATION_UID 19999
|
#define LAST_APPLICATION_UID 19999
|
||||||
|
#define FIRST_ISOLATED_UID 99000
|
||||||
|
#define LAST_ISOLATED_UID 99999
|
||||||
|
|
||||||
void ksu_allowlist_init(void);
|
void ksu_allowlist_init(void);
|
||||||
|
|
||||||
@@ -41,6 +43,12 @@ static inline bool is_appuid(uid_t uid)
|
|||||||
return appid >= FIRST_APPLICATION_UID && appid <= LAST_APPLICATION_UID;
|
return appid >= FIRST_APPLICATION_UID && appid <= LAST_APPLICATION_UID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool is_isolated_process(uid_t uid)
|
||||||
|
{
|
||||||
|
uid_t appid = uid % PER_USER_RANGE;
|
||||||
|
return appid >= FIRST_ISOLATED_UID && appid <= LAST_ISOLATED_UID;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef CONFIG_KSU_MANUAL_SU
|
#ifdef CONFIG_KSU_MANUAL_SU
|
||||||
bool ksu_temp_grant_root_once(uid_t uid);
|
bool ksu_temp_grant_root_once(uid_t uid);
|
||||||
void ksu_temp_revoke_root_once(uid_t uid);
|
void ksu_temp_revoke_root_once(uid_t uid);
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ int ksu_handle_umount(uid_t old_uid, uid_t new_uid)
|
|||||||
{
|
{
|
||||||
struct umount_tw *tw;
|
struct umount_tw *tw;
|
||||||
|
|
||||||
// this hook is used for umounting overlayfs for some uid, if there isn't any module mounted, just ignore it!
|
// if there isn't any module mounted, just ignore it!
|
||||||
if (!ksu_module_mounted) {
|
if (!ksu_module_mounted) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -115,18 +115,24 @@ int ksu_handle_umount(uid_t old_uid, uid_t new_uid)
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: isolated process which directly forks from zygote is not handled
|
// There are 5 scenarios:
|
||||||
if (!is_appuid(new_uid)) {
|
// 1. Normal app: zygote -> appuid
|
||||||
|
// 2. Isolated process forked from zygote: zygote -> isolated_process
|
||||||
|
// 3. App zygote forked from zygote: zygote -> appuid
|
||||||
|
// 4. Isolated process froked from app zygote: appuid -> isolated_process (already handled by 3)
|
||||||
|
// 5. Isolated process froked from webview zygote (no need to handle, app cannot run custom code)
|
||||||
|
if (!is_appuid(new_uid) && !is_isolated_process(new_uid)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ksu_uid_should_umount(new_uid)) {
|
if (!ksu_uid_should_umount(new_uid) && !is_isolated_process(new_uid)) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// check old process's selinux context, if it is not zygote, ignore it!
|
// check old process's selinux context, if it is not zygote, ignore it!
|
||||||
// because some su apps may setuid to untrusted_app but they are in global mount namespace
|
// because some su apps may setuid to untrusted_app but they are in global mount namespace
|
||||||
// when we umount for such process, that is a disaster!
|
// when we umount for such process, that is a disaster!
|
||||||
|
// also handle case 4 and 5
|
||||||
bool is_zygote_child = is_zygote(get_current_cred());
|
bool is_zygote_child = is_zygote(get_current_cred());
|
||||||
if (!is_zygote_child) {
|
if (!is_zygote_child) {
|
||||||
pr_info("handle umount ignore non zygote child: %d\n", current->pid);
|
pr_info("handle umount ignore non zygote child: %d\n", current->pid);
|
||||||
|
|||||||
@@ -107,20 +107,6 @@ int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags)
|
|||||||
|
|
||||||
char path[sizeof(su) + 1];
|
char path[sizeof(su) + 1];
|
||||||
memset(path, 0, sizeof(path));
|
memset(path, 0, sizeof(path));
|
||||||
// Remove this later!! we use syscall hook, so this will never happen!!!!!
|
|
||||||
#if LINUX_VERSION_CODE >= KERNEL_VERSION(5, 18, 0) && 0
|
|
||||||
// it becomes a `struct filename *` after 5.18
|
|
||||||
// https://elixir.bootlin.com/linux/v5.18/source/fs/stat.c#L216
|
|
||||||
const char sh[] = SH_PATH;
|
|
||||||
struct filename *filename = *((struct filename **)filename_user);
|
|
||||||
if (IS_ERR(filename)) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
if (likely(memcmp(filename->name, su, sizeof(su))))
|
|
||||||
return 0;
|
|
||||||
pr_info("vfs_statx su->sh!\n");
|
|
||||||
memcpy((void *)filename->name, sh, sizeof(sh));
|
|
||||||
#else
|
|
||||||
strncpy_from_user_nofault(path, *filename_user, sizeof(path));
|
strncpy_from_user_nofault(path, *filename_user, sizeof(path));
|
||||||
|
|
||||||
if (unlikely(!memcmp(path, su, sizeof(su)))) {
|
if (unlikely(!memcmp(path, su, sizeof(su)))) {
|
||||||
@@ -130,7 +116,6 @@ int ksu_handle_stat(int *dfd, const char __user **filename_user, int *flags)
|
|||||||
pr_info("newfstatat su->sh!\n");
|
pr_info("newfstatat su->sh!\n");
|
||||||
*filename_user = sh_user_path();
|
*filename_user = sh_user_path();
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
static struct umount_manager g_umount_mgr = {
|
static struct umount_manager g_umount_mgr = {
|
||||||
.entry_count = 0,
|
.entry_count = 0,
|
||||||
.max_entries = 64,
|
.max_entries = 512,
|
||||||
};
|
};
|
||||||
|
|
||||||
static void try_umount_path(struct umount_entry *entry)
|
static void try_umount_path(struct umount_entry *entry)
|
||||||
@@ -33,35 +33,123 @@ static struct umount_entry *find_entry_locked(const char *path)
|
|||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int init_default_entries(void)
|
static bool is_path_in_mount_list(const char *path)
|
||||||
{
|
{
|
||||||
int ret;
|
struct mount_entry *entry;
|
||||||
|
bool found = false;
|
||||||
|
|
||||||
const struct {
|
down_read(&mount_list_lock);
|
||||||
const char *path;
|
list_for_each_entry(entry, &mount_list, list) {
|
||||||
int flags;
|
if (entry->umountable && strcmp(entry->umountable, path) == 0) {
|
||||||
} defaults[] = {
|
found = true;
|
||||||
{ "/odm", 0 },
|
break;
|
||||||
{ "/system", 0 },
|
|
||||||
{ "/vendor", 0 },
|
|
||||||
{ "/product", 0 },
|
|
||||||
{ "/system_ext", 0 },
|
|
||||||
{ "/data/adb/modules", MNT_DETACH },
|
|
||||||
{ "/debug_ramdisk", MNT_DETACH },
|
|
||||||
};
|
|
||||||
|
|
||||||
for (int i = 0; i < ARRAY_SIZE(defaults); i++) {
|
|
||||||
ret = ksu_umount_manager_add(defaults[i].path,
|
|
||||||
defaults[i].flags,
|
|
||||||
true); // is_default = true
|
|
||||||
if (ret) {
|
|
||||||
pr_err("Failed to add default entry: %s, ret=%d\n",
|
|
||||||
defaults[i].path, ret);
|
|
||||||
return ret;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
up_read(&mount_list_lock);
|
||||||
|
|
||||||
pr_info("Initialized %zu default umount entries\n", ARRAY_SIZE(defaults));
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int copy_mount_entry_to_user(struct ksu_umount_entry_info __user *entries,
|
||||||
|
u32 idx, const char *path, int flags)
|
||||||
|
{
|
||||||
|
struct ksu_umount_entry_info info;
|
||||||
|
|
||||||
|
memset(&info, 0, sizeof(info));
|
||||||
|
strncpy(info.path, path, sizeof(info.path) - 1);
|
||||||
|
info.path[sizeof(info.path) - 1] = '\0';
|
||||||
|
info.flags = flags;
|
||||||
|
info.is_default = 1;
|
||||||
|
info.state = UMOUNT_STATE_IDLE;
|
||||||
|
info.ref_count = 0;
|
||||||
|
|
||||||
|
if (copy_to_user(&entries[idx], &info, sizeof(info))) {
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int copy_umount_entry_to_user(struct ksu_umount_entry_info __user *entries,
|
||||||
|
u32 idx, struct umount_entry *entry)
|
||||||
|
{
|
||||||
|
struct ksu_umount_entry_info info;
|
||||||
|
|
||||||
|
memset(&info, 0, sizeof(info));
|
||||||
|
strncpy(info.path, entry->path, sizeof(info.path) - 1);
|
||||||
|
info.path[sizeof(info.path) - 1] = '\0';
|
||||||
|
info.flags = entry->flags;
|
||||||
|
info.is_default = entry->is_default;
|
||||||
|
info.state = entry->state;
|
||||||
|
info.ref_count = entry->ref_count;
|
||||||
|
|
||||||
|
if (copy_to_user(&entries[idx], &info, sizeof(info))) {
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int collect_mount_list_entries(struct ksu_umount_entry_info __user *entries,
|
||||||
|
u32 max_count, u32 *out_idx)
|
||||||
|
{
|
||||||
|
struct mount_entry *mount_entry;
|
||||||
|
u32 idx = 0;
|
||||||
|
|
||||||
|
down_read(&mount_list_lock);
|
||||||
|
list_for_each_entry(mount_entry, &mount_list, list) {
|
||||||
|
if (idx >= max_count) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mount_entry->umountable) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (copy_mount_entry_to_user(entries, idx, mount_entry->umountable,
|
||||||
|
mount_entry->flags)) {
|
||||||
|
up_read(&mount_list_lock);
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
idx++;
|
||||||
|
}
|
||||||
|
up_read(&mount_list_lock);
|
||||||
|
|
||||||
|
*out_idx = idx;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int collect_umount_manager_entries(struct ksu_umount_entry_info __user *entries,
|
||||||
|
u32 start_idx, u32 max_count, u32 *out_idx)
|
||||||
|
{
|
||||||
|
struct umount_entry *entry;
|
||||||
|
unsigned long flags;
|
||||||
|
u32 idx = start_idx;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&g_umount_mgr.lock, flags);
|
||||||
|
|
||||||
|
list_for_each_entry(entry, &g_umount_mgr.entry_list, list) {
|
||||||
|
if (idx >= max_count) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (is_path_in_mount_list(entry->path)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&g_umount_mgr.lock, flags);
|
||||||
|
|
||||||
|
if (copy_umount_entry_to_user(entries, idx, entry)) {
|
||||||
|
return -EFAULT;
|
||||||
|
}
|
||||||
|
|
||||||
|
idx++;
|
||||||
|
spin_lock_irqsave(&g_umount_mgr.lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&g_umount_mgr.lock, flags);
|
||||||
|
*out_idx = idx;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -70,7 +158,7 @@ int ksu_umount_manager_init(void)
|
|||||||
INIT_LIST_HEAD(&g_umount_mgr.entry_list);
|
INIT_LIST_HEAD(&g_umount_mgr.entry_list);
|
||||||
spin_lock_init(&g_umount_mgr.lock);
|
spin_lock_init(&g_umount_mgr.lock);
|
||||||
|
|
||||||
return init_default_entries();
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void ksu_umount_manager_exit(void)
|
void ksu_umount_manager_exit(void)
|
||||||
@@ -104,6 +192,11 @@ int ksu_umount_manager_add(const char *path, int flags, bool is_default)
|
|||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (is_path_in_mount_list(path)) {
|
||||||
|
pr_warn("Umount manager: path already exists in mount_list: %s\n", path);
|
||||||
|
return -EEXIST;
|
||||||
|
}
|
||||||
|
|
||||||
spin_lock_irqsave(&g_umount_mgr.lock, irqflags);
|
spin_lock_irqsave(&g_umount_mgr.lock, irqflags);
|
||||||
|
|
||||||
if (g_umount_mgr.entry_count >= g_umount_mgr.max_entries) {
|
if (g_umount_mgr.entry_count >= g_umount_mgr.max_entries) {
|
||||||
@@ -216,37 +309,23 @@ void ksu_umount_manager_execute_all(const struct cred *cred)
|
|||||||
|
|
||||||
int ksu_umount_manager_get_entries(struct ksu_umount_entry_info __user *entries, u32 *count)
|
int ksu_umount_manager_get_entries(struct ksu_umount_entry_info __user *entries, u32 *count)
|
||||||
{
|
{
|
||||||
struct umount_entry *entry;
|
|
||||||
struct ksu_umount_entry_info info;
|
|
||||||
unsigned long flags;
|
|
||||||
u32 idx = 0;
|
|
||||||
u32 max_count = *count;
|
u32 max_count = *count;
|
||||||
|
u32 idx;
|
||||||
|
int ret;
|
||||||
|
|
||||||
spin_lock_irqsave(&g_umount_mgr.lock, flags);
|
ret = collect_mount_list_entries(entries, max_count, &idx);
|
||||||
|
if (ret) {
|
||||||
list_for_each_entry(entry, &g_umount_mgr.entry_list, list) {
|
return ret;
|
||||||
if (idx >= max_count) {
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
memset(&info, 0, sizeof(info));
|
if (idx < max_count) {
|
||||||
strncpy(info.path, entry->path, sizeof(info.path) - 1);
|
ret = collect_umount_manager_entries(entries, idx, max_count, &idx);
|
||||||
info.flags = entry->flags;
|
if (ret) {
|
||||||
info.is_default = entry->is_default;
|
return ret;
|
||||||
info.state = entry->state;
|
|
||||||
info.ref_count = entry->ref_count;
|
|
||||||
|
|
||||||
if (copy_to_user(&entries[idx], &info, sizeof(info))) {
|
|
||||||
spin_unlock_irqrestore(&g_umount_mgr.lock, flags);
|
|
||||||
return -EFAULT;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
idx++;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
*count = idx;
|
*count = idx;
|
||||||
|
|
||||||
spin_unlock_irqrestore(&g_umount_mgr.lock, flags);
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ import androidx.compose.ui.tooling.preview.Preview
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
|
import androidx.compose.material.icons.outlined.Info
|
||||||
|
import androidx.compose.ui.platform.LocalUriHandler
|
||||||
import com.ramcosta.composedestinations.annotation.Destination
|
import com.ramcosta.composedestinations.annotation.Destination
|
||||||
import com.ramcosta.composedestinations.annotation.RootGraph
|
import com.ramcosta.composedestinations.annotation.RootGraph
|
||||||
import com.ramcosta.composedestinations.generated.destinations.FlashScreenDestination
|
import com.ramcosta.composedestinations.generated.destinations.FlashScreenDestination
|
||||||
@@ -52,8 +54,10 @@ import java.io.File
|
|||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import androidx.core.content.edit
|
import androidx.core.content.edit
|
||||||
|
import com.sukisu.ultra.ui.component.rememberCustomDialog
|
||||||
import com.sukisu.ultra.ui.util.module.ModuleOperationUtils
|
import com.sukisu.ultra.ui.util.module.ModuleOperationUtils
|
||||||
import com.sukisu.ultra.ui.util.module.ModuleUtils
|
import com.sukisu.ultra.ui.util.module.ModuleUtils
|
||||||
|
import com.topjohnwu.superuser.io.SuFile
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author ShirkNeko
|
* @author ShirkNeko
|
||||||
@@ -152,6 +156,7 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
|||||||
var tempText: String
|
var tempText: String
|
||||||
val logContent = rememberSaveable { StringBuilder() }
|
val logContent = rememberSaveable { StringBuilder() }
|
||||||
var showFloatAction by rememberSaveable { mutableStateOf(false) }
|
var showFloatAction by rememberSaveable { mutableStateOf(false) }
|
||||||
|
var shouldWarningUserMetaModule by rememberSaveable { mutableStateOf(false) }
|
||||||
// 添加状态跟踪是否已经完成刷写
|
// 添加状态跟踪是否已经完成刷写
|
||||||
var hasFlashCompleted by rememberSaveable { mutableStateOf(false) }
|
var hasFlashCompleted by rememberSaveable { mutableStateOf(false) }
|
||||||
var hasExecuted by rememberSaveable { mutableStateOf(false) }
|
var hasExecuted by rememberSaveable { mutableStateOf(false) }
|
||||||
@@ -170,6 +175,39 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
|||||||
val logSavedString = stringResource(R.string.log_saved)
|
val logSavedString = stringResource(R.string.log_saved)
|
||||||
val installingModuleString = stringResource(R.string.installing_module)
|
val installingModuleString = stringResource(R.string.installing_module)
|
||||||
|
|
||||||
|
val alertDialog = rememberCustomDialog { dismiss: () -> Unit ->
|
||||||
|
val uriHandler = LocalUriHandler.current
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = { dismiss() },
|
||||||
|
icon = {
|
||||||
|
Icon(Icons.Outlined.Info, contentDescription = null)
|
||||||
|
},
|
||||||
|
title = {
|
||||||
|
Row(modifier = Modifier
|
||||||
|
.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
|
Text(text = stringResource(R.string.warning_of_meta_module_title))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
text = {
|
||||||
|
Text(text = stringResource(R.string.warning_of_meta_module_summary))
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
FilledTonalButton(onClick = { dismiss() }) {
|
||||||
|
Text(text = stringResource(id = android.R.string.ok))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
OutlinedButton(onClick = {
|
||||||
|
uriHandler.openUri("https://kernelsu.org/guide/metamodule.html")
|
||||||
|
}) {
|
||||||
|
Text(text = stringResource(id = R.string.learn_more))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
// 当前模块安装状态
|
// 当前模块安装状态
|
||||||
val currentStatus = moduleInstallStatus.value
|
val currentStatus = moduleInstallStatus.value
|
||||||
|
|
||||||
@@ -182,16 +220,19 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
|||||||
totalModules = flashIt.uris.size,
|
totalModules = flashIt.uris.size,
|
||||||
currentModule = 1
|
currentModule = 1
|
||||||
)
|
)
|
||||||
|
shouldWarningUserMetaModule = false
|
||||||
hasFlashCompleted = false
|
hasFlashCompleted = false
|
||||||
hasExecuted = false
|
hasExecuted = false
|
||||||
moduleVerificationMap.clear()
|
moduleVerificationMap.clear()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is FlashIt.FlashModuleUpdate -> {
|
is FlashIt.FlashModuleUpdate -> {
|
||||||
|
shouldWarningUserMetaModule = false
|
||||||
hasUpdateCompleted = false
|
hasUpdateCompleted = false
|
||||||
hasUpdateExecuted = false
|
hasUpdateExecuted = false
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
|
shouldWarningUserMetaModule = false
|
||||||
hasFlashCompleted = false
|
hasFlashCompleted = false
|
||||||
hasExecuted = false
|
hasExecuted = false
|
||||||
}
|
}
|
||||||
@@ -240,10 +281,29 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
|||||||
}
|
}
|
||||||
hasUpdateCompleted = true
|
hasUpdateCompleted = true
|
||||||
|
|
||||||
|
if (!hasMetaModule() && code == 0) {
|
||||||
|
// 如果没安装 MetaModule,且此模块需要挂载,并且当前模块安装成功,警告用户
|
||||||
|
scope.launch {
|
||||||
|
val mountOldDirectory = SuFile.open("/data/adb/modules/${getModuleIdFromUri(context,flashIt.uri)}/system")
|
||||||
|
val mountNewDirectory = SuFile.open("/data/adb/modules_update/${getModuleIdFromUri(context,flashIt.uri)}/system")
|
||||||
|
if (!(mountNewDirectory.isDirectory) && !(mountOldDirectory.isDirectory)) return@launch
|
||||||
|
shouldWarningUserMetaModule = true
|
||||||
|
|
||||||
|
alertDialog.show()
|
||||||
|
shouldWarningUserMetaModule = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 如果是外部安装或需要自动退出的模块更新且不需要重启,延迟后自动返回
|
// 如果是外部安装或需要自动退出的模块更新且不需要重启,延迟后自动返回
|
||||||
if (isExternalInstall || shouldAutoExit) {
|
if (isExternalInstall || shouldAutoExit) {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
|
while (shouldWarningUserMetaModule) {
|
||||||
|
kotlinx.coroutines.delay(100)
|
||||||
|
}
|
||||||
kotlinx.coroutines.delay(1000)
|
kotlinx.coroutines.delay(1000)
|
||||||
|
while (shouldWarningUserMetaModule) {
|
||||||
|
kotlinx.coroutines.delay(100)
|
||||||
|
}
|
||||||
if (shouldAutoExit) {
|
if (shouldAutoExit) {
|
||||||
val sharedPref = context.getSharedPreferences("kernel_flash_prefs", Context.MODE_PRIVATE)
|
val sharedPref = context.getSharedPreferences("kernel_flash_prefs", Context.MODE_PRIVATE)
|
||||||
sharedPref.edit { remove("auto_exit_after_flash") }
|
sharedPref.edit { remove("auto_exit_after_flash") }
|
||||||
@@ -334,6 +394,38 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
hasFlashCompleted = true
|
hasFlashCompleted = true
|
||||||
|
if (!hasMetaModule() && code == 0) {
|
||||||
|
// 没有 MetaModule,且安装成功,检查此模块是否有自动挂载
|
||||||
|
scope.launch {
|
||||||
|
var mountOldDirectory : File
|
||||||
|
var mountNewDirectory : File
|
||||||
|
when (flashIt) {
|
||||||
|
is FlashIt.FlashModules -> {
|
||||||
|
mountOldDirectory = SuFile.open("/data/adb/modules/${getModuleIdFromUri(context,flashIt.uris[flashIt.currentIndex])}/system")
|
||||||
|
mountNewDirectory = SuFile.open("/data/adb/modules_update/${getModuleIdFromUri(context,flashIt.uris[flashIt.currentIndex])}/system")
|
||||||
|
}
|
||||||
|
|
||||||
|
is FlashIt.FlashModule -> {
|
||||||
|
mountOldDirectory = SuFile.open("/data/adb/modules/${getModuleIdFromUri(context,flashIt.uri)}/system")
|
||||||
|
mountNewDirectory = SuFile.open("/data/adb/modules_update/${getModuleIdFromUri(context,flashIt.uri)}/system")
|
||||||
|
}
|
||||||
|
|
||||||
|
is FlashIt.FlashModuleUpdate -> {
|
||||||
|
mountOldDirectory = SuFile.open("/data/adb/modules/${getModuleIdFromUri(context,flashIt.uri)}/system")
|
||||||
|
mountNewDirectory = SuFile.open("/data/adb/modules_update/${getModuleIdFromUri(context,flashIt.uri)}/system")
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> return@launch
|
||||||
|
}
|
||||||
|
if (!mountNewDirectory.isDirectory && !mountOldDirectory.isDirectory) return@launch
|
||||||
|
shouldWarningUserMetaModule = true
|
||||||
|
|
||||||
|
if (!hasMetaModule() && (flashIt !is FlashIt.FlashModules || flashIt.currentIndex >= flashIt.uris.size - 1)) {
|
||||||
|
// 如果没有 MetaModule,且当前不是多模块刷写或是最后一个需要自动刷写的模块,而且有模块需要挂载,警告用户
|
||||||
|
alertDialog.show()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (flashIt is FlashIt.FlashModules && flashIt.currentIndex < flashIt.uris.size - 1) {
|
if (flashIt is FlashIt.FlashModules && flashIt.currentIndex < flashIt.uris.size - 1) {
|
||||||
val nextFlashIt = flashIt.copy(
|
val nextFlashIt = flashIt.copy(
|
||||||
@@ -346,7 +438,13 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
|||||||
} else if ((isExternalInstall || shouldAutoExit) && flashIt is FlashIt.FlashModules && flashIt.currentIndex >= flashIt.uris.size - 1) {
|
} else if ((isExternalInstall || shouldAutoExit) && flashIt is FlashIt.FlashModules && flashIt.currentIndex >= flashIt.uris.size - 1) {
|
||||||
// 如果是外部安装或需要自动退出且是最后一个模块,安装完成后自动返回
|
// 如果是外部安装或需要自动退出且是最后一个模块,安装完成后自动返回
|
||||||
scope.launch {
|
scope.launch {
|
||||||
|
while (shouldWarningUserMetaModule) {
|
||||||
|
kotlinx.coroutines.delay(100)
|
||||||
|
}
|
||||||
kotlinx.coroutines.delay(1000)
|
kotlinx.coroutines.delay(1000)
|
||||||
|
while (shouldWarningUserMetaModule) {
|
||||||
|
kotlinx.coroutines.delay(100)
|
||||||
|
}
|
||||||
if (shouldAutoExit) {
|
if (shouldAutoExit) {
|
||||||
val sharedPref = context.getSharedPreferences("kernel_flash_prefs", Context.MODE_PRIVATE)
|
val sharedPref = context.getSharedPreferences("kernel_flash_prefs", Context.MODE_PRIVATE)
|
||||||
sharedPref.edit { remove("auto_exit_after_flash") }
|
sharedPref.edit { remove("auto_exit_after_flash") }
|
||||||
@@ -356,7 +454,13 @@ fun FlashScreen(navigator: DestinationsNavigator, flashIt: FlashIt) {
|
|||||||
} else if ((isExternalInstall || shouldAutoExit) && flashIt is FlashIt.FlashModule) {
|
} else if ((isExternalInstall || shouldAutoExit) && flashIt is FlashIt.FlashModule) {
|
||||||
// 如果是外部安装或需要自动退出的单个模块,安装完成后自动返回
|
// 如果是外部安装或需要自动退出的单个模块,安装完成后自动返回
|
||||||
scope.launch {
|
scope.launch {
|
||||||
|
while (shouldWarningUserMetaModule) {
|
||||||
|
kotlinx.coroutines.delay(100)
|
||||||
|
}
|
||||||
kotlinx.coroutines.delay(1000)
|
kotlinx.coroutines.delay(1000)
|
||||||
|
while (shouldWarningUserMetaModule) {
|
||||||
|
kotlinx.coroutines.delay(100)
|
||||||
|
}
|
||||||
if (shouldAutoExit) {
|
if (shouldAutoExit) {
|
||||||
val sharedPref = context.getSharedPreferences("kernel_flash_prefs", Context.MODE_PRIVATE)
|
val sharedPref = context.getSharedPreferences("kernel_flash_prefs", Context.MODE_PRIVATE)
|
||||||
sharedPref.edit { remove("auto_exit_after_flash") }
|
sharedPref.edit { remove("auto_exit_after_flash") }
|
||||||
@@ -705,6 +809,22 @@ suspend fun getModuleNameFromUri(context: Context, uri: Uri): String {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun getModuleIdFromUri(context: Context, uri: Uri): String? {
|
||||||
|
return withContext(Dispatchers.IO) {
|
||||||
|
try {
|
||||||
|
if (uri == Uri.EMPTY) {
|
||||||
|
return@withContext null
|
||||||
|
}
|
||||||
|
if (!ModuleUtils.isUriAccessible(context, uri)) {
|
||||||
|
return@withContext null
|
||||||
|
}
|
||||||
|
ModuleUtils.extractModuleId(context, uri)
|
||||||
|
} catch (_: Exception) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
sealed class FlashIt : Parcelable {
|
sealed class FlashIt : Parcelable {
|
||||||
data class FlashBoot(val boot: Uri? = null, val lkm: LkmSelection, val ota: Boolean, val partition: String? = null) : FlashIt()
|
data class FlashBoot(val boot: Uri? = null, val lkm: LkmSelection, val ota: Boolean, val partition: String? = null) : FlashIt()
|
||||||
|
|||||||
@@ -182,6 +182,7 @@ fun HomeScreen(navigator: DestinationsNavigator) {
|
|||||||
isSimpleMode = viewModel.isSimpleMode,
|
isSimpleMode = viewModel.isSimpleMode,
|
||||||
isHideSusfsStatus = viewModel.isHideSusfsStatus,
|
isHideSusfsStatus = viewModel.isHideSusfsStatus,
|
||||||
isHideZygiskImplement = viewModel.isHideZygiskImplement,
|
isHideZygiskImplement = viewModel.isHideZygiskImplement,
|
||||||
|
isHideMetaModuleImplement = viewModel.isHideMetaModuleImplement,
|
||||||
showKpmInfo = viewModel.showKpmInfo,
|
showKpmInfo = viewModel.showKpmInfo,
|
||||||
lkmMode = viewModel.systemStatus.lkmMode,
|
lkmMode = viewModel.systemStatus.lkmMode,
|
||||||
)
|
)
|
||||||
@@ -652,6 +653,7 @@ private fun InfoCard(
|
|||||||
isSimpleMode: Boolean,
|
isSimpleMode: Boolean,
|
||||||
isHideSusfsStatus: Boolean,
|
isHideSusfsStatus: Boolean,
|
||||||
isHideZygiskImplement: Boolean,
|
isHideZygiskImplement: Boolean,
|
||||||
|
isHideMetaModuleImplement: Boolean,
|
||||||
showKpmInfo: Boolean,
|
showKpmInfo: Boolean,
|
||||||
lkmMode: Boolean?
|
lkmMode: Boolean?
|
||||||
) {
|
) {
|
||||||
@@ -784,6 +786,14 @@ private fun InfoCard(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!isHideMetaModuleImplement && !isSimpleMode && systemInfo.metaModuleImplement != "None") {
|
||||||
|
InfoCardItem(
|
||||||
|
stringResource(R.string.home_meta_module_implement),
|
||||||
|
systemInfo.metaModuleImplement,
|
||||||
|
icon = Icons.Default.Extension,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
if (!isSimpleMode) {
|
if (!isSimpleMode) {
|
||||||
if (lkmMode != true && !showKpmInfo) {
|
if (lkmMode != true && !showKpmInfo) {
|
||||||
val displayVersion =
|
val displayVersion =
|
||||||
|
|||||||
@@ -752,6 +752,7 @@ private fun ModuleList(
|
|||||||
val uninstall = stringResource(R.string.uninstall)
|
val uninstall = stringResource(R.string.uninstall)
|
||||||
val cancel = stringResource(android.R.string.cancel)
|
val cancel = stringResource(android.R.string.cancel)
|
||||||
val moduleUninstallConfirm = stringResource(R.string.module_uninstall_confirm)
|
val moduleUninstallConfirm = stringResource(R.string.module_uninstall_confirm)
|
||||||
|
val metaModuleUninstallConfirm = stringResource(R.string.metamodule_uninstall_confirm)
|
||||||
val updateText = stringResource(R.string.module_update)
|
val updateText = stringResource(R.string.module_update)
|
||||||
val changelogText = stringResource(R.string.module_changelog)
|
val changelogText = stringResource(R.string.module_changelog)
|
||||||
val downloadingText = stringResource(R.string.module_downloading)
|
val downloadingText = stringResource(R.string.module_downloading)
|
||||||
@@ -847,9 +848,10 @@ private fun ModuleList(
|
|||||||
suspend fun onModuleUninstallClicked(module: ModuleViewModel.ModuleInfo) {
|
suspend fun onModuleUninstallClicked(module: ModuleViewModel.ModuleInfo) {
|
||||||
val isUninstall = !module.remove
|
val isUninstall = !module.remove
|
||||||
if (isUninstall) {
|
if (isUninstall) {
|
||||||
|
val formatter = if (module.metamodule) metaModuleUninstallConfirm else moduleUninstallConfirm
|
||||||
val confirmResult = confirmDialog.awaitConfirm(
|
val confirmResult = confirmDialog.awaitConfirm(
|
||||||
moduleStr,
|
moduleStr,
|
||||||
content = moduleUninstallConfirm.format(module.name),
|
content = formatter.format(module.name),
|
||||||
confirm = uninstall,
|
confirm = uninstall,
|
||||||
dismiss = cancel
|
dismiss = cancel
|
||||||
)
|
)
|
||||||
@@ -865,7 +867,7 @@ private fun ModuleList(
|
|||||||
ModuleOperationUtils.handleModuleUninstall(module.dirId)
|
ModuleOperationUtils.handleModuleUninstall(module.dirId)
|
||||||
uninstallModule(module.dirId)
|
uninstallModule(module.dirId)
|
||||||
} else {
|
} else {
|
||||||
restoreModule(module.dirId)
|
undoUninstallModule(module.dirId)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1199,6 +1201,22 @@ fun ModuleItem(
|
|||||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
||||||
modifier = Modifier.fillMaxWidth()
|
modifier = Modifier.fillMaxWidth()
|
||||||
) {
|
) {
|
||||||
|
if (module.metamodule) {
|
||||||
|
Surface(
|
||||||
|
shape = RoundedCornerShape(4.dp),
|
||||||
|
color = MaterialTheme.colorScheme.primary,
|
||||||
|
modifier = Modifier
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "META",
|
||||||
|
style = MaterialTheme.typography.labelSmall,
|
||||||
|
modifier = Modifier.padding(horizontal = 4.dp, vertical = 1.dp),
|
||||||
|
color = MaterialTheme.colorScheme.onPrimary,
|
||||||
|
maxLines = 1,
|
||||||
|
overflow = TextOverflow.Ellipsis
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
Surface(
|
Surface(
|
||||||
shape = RoundedCornerShape(4.dp),
|
shape = RoundedCornerShape(4.dp),
|
||||||
color = MaterialTheme.colorScheme.primary,
|
color = MaterialTheme.colorScheme.primary,
|
||||||
@@ -1329,8 +1347,9 @@ fun ModuleItemPreview() {
|
|||||||
update = true,
|
update = true,
|
||||||
remove = false,
|
remove = false,
|
||||||
updateJson = "",
|
updateJson = "",
|
||||||
hasWebUi = false,
|
hasWebUi = true,
|
||||||
hasActionScript = false,
|
hasActionScript = true,
|
||||||
|
metamodule = true,
|
||||||
dirId = "dirId",
|
dirId = "dirId",
|
||||||
config = ModuleConfig(),
|
config = ModuleConfig(),
|
||||||
isVerified = true,
|
isVerified = true,
|
||||||
|
|||||||
@@ -11,12 +11,10 @@ import androidx.compose.animation.*
|
|||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
|
||||||
import androidx.compose.foundation.rememberScrollState
|
import androidx.compose.foundation.rememberScrollState
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.verticalScroll
|
import androidx.compose.foundation.verticalScroll
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.automirrored.filled.ArrowForward
|
|
||||||
import androidx.compose.material.icons.automirrored.filled.Undo
|
import androidx.compose.material.icons.automirrored.filled.Undo
|
||||||
import androidx.compose.material.icons.filled.*
|
import androidx.compose.material.icons.filled.*
|
||||||
import androidx.compose.material.icons.rounded.EnhancedEncryption
|
import androidx.compose.material.icons.rounded.EnhancedEncryption
|
||||||
@@ -56,7 +54,6 @@ import com.sukisu.ultra.ui.theme.CardConfig.cardAlpha
|
|||||||
import com.sukisu.ultra.ui.theme.getCardColors
|
import com.sukisu.ultra.ui.theme.getCardColors
|
||||||
import com.sukisu.ultra.ui.theme.getCardElevation
|
import com.sukisu.ultra.ui.theme.getCardElevation
|
||||||
import com.sukisu.ultra.ui.util.*
|
import com.sukisu.ultra.ui.util.*
|
||||||
import kotlinx.coroutines.CoroutineScope
|
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
@@ -151,11 +148,28 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
val enhancedStatus by produceState(initialValue = "") {
|
||||||
|
value = getFeatureStatus("enhanced_security")
|
||||||
|
}
|
||||||
|
val enhancedSummary = when (enhancedStatus) {
|
||||||
|
"unsupported" -> stringResource(id = R.string.feature_status_unsupported_summary)
|
||||||
|
"managed" -> stringResource(id = R.string.feature_status_managed_summary)
|
||||||
|
else -> stringResource(id = R.string.settings_enable_enhanced_security_summary)
|
||||||
|
}
|
||||||
SuperDropdown(
|
SuperDropdown(
|
||||||
icon = Icons.Rounded.EnhancedEncryption,
|
icon = Icons.Rounded.EnhancedEncryption,
|
||||||
title = stringResource(id = R.string.settings_enable_enhanced_security),
|
title = stringResource(id = R.string.settings_enable_enhanced_security),
|
||||||
summary = stringResource(id = R.string.settings_enable_enhanced_security_summary),
|
summary = enhancedSummary,
|
||||||
items = modeItems,
|
items = modeItems,
|
||||||
|
leftAction = {
|
||||||
|
Icon(
|
||||||
|
Icons.Rounded.EnhancedEncryption,
|
||||||
|
modifier = Modifier.padding(end = 16.dp),
|
||||||
|
contentDescription = stringResource(id = R.string.settings_enable_enhanced_security),
|
||||||
|
tint = MaterialTheme.colorScheme.onBackground
|
||||||
|
)
|
||||||
|
},
|
||||||
|
enabled = enhancedStatus == "supported",
|
||||||
selectedIndex = enhancedSecurityMode,
|
selectedIndex = enhancedSecurityMode,
|
||||||
onSelectedIndexChange = { index ->
|
onSelectedIndexChange = { index ->
|
||||||
when (index) {
|
when (index) {
|
||||||
@@ -194,11 +208,28 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
val suStatus by produceState(initialValue = "") {
|
||||||
|
value = getFeatureStatus("su_compat")
|
||||||
|
}
|
||||||
|
val suSummary = when (suStatus) {
|
||||||
|
"unsupported" -> stringResource(id = R.string.feature_status_unsupported_summary)
|
||||||
|
"managed" -> stringResource(id = R.string.feature_status_managed_summary)
|
||||||
|
else -> stringResource(id = R.string.settings_disable_su_summary)
|
||||||
|
}
|
||||||
SuperDropdown(
|
SuperDropdown(
|
||||||
icon = Icons.Rounded.RemoveModerator,
|
icon = Icons.Rounded.RemoveModerator,
|
||||||
title = stringResource(id = R.string.settings_disable_su),
|
title = stringResource(id = R.string.settings_disable_su),
|
||||||
summary = stringResource(id = R.string.settings_disable_su_summary),
|
summary = suSummary,
|
||||||
items = modeItems,
|
items = modeItems,
|
||||||
|
leftAction = {
|
||||||
|
Icon(
|
||||||
|
Icons.Rounded.RemoveModerator,
|
||||||
|
modifier = Modifier.padding(end = 16.dp),
|
||||||
|
contentDescription = stringResource(id = R.string.settings_disable_su),
|
||||||
|
tint = MaterialTheme.colorScheme.onBackground
|
||||||
|
)
|
||||||
|
},
|
||||||
|
enabled = suStatus == "supported",
|
||||||
selectedIndex = suCompatMode,
|
selectedIndex = suCompatMode,
|
||||||
onSelectedIndexChange = { index ->
|
onSelectedIndexChange = { index ->
|
||||||
when (index) {
|
when (index) {
|
||||||
@@ -237,11 +268,28 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
val umountStatus by produceState(initialValue = "") {
|
||||||
|
value = getFeatureStatus("kernel_umount")
|
||||||
|
}
|
||||||
|
val umountSummary = when (umountStatus) {
|
||||||
|
"unsupported" -> stringResource(id = R.string.feature_status_unsupported_summary)
|
||||||
|
"managed" -> stringResource(id = R.string.feature_status_managed_summary)
|
||||||
|
else -> stringResource(id = R.string.settings_disable_kernel_umount_summary)
|
||||||
|
}
|
||||||
SuperDropdown(
|
SuperDropdown(
|
||||||
icon = Icons.Rounded.RemoveCircle,
|
icon = Icons.Rounded.RemoveCircle,
|
||||||
title = stringResource(id = R.string.settings_disable_kernel_umount),
|
title = stringResource(id = R.string.settings_disable_kernel_umount),
|
||||||
summary = stringResource(id = R.string.settings_disable_kernel_umount_summary),
|
summary = umountSummary,
|
||||||
items = modeItems,
|
items = modeItems,
|
||||||
|
leftAction = {
|
||||||
|
Icon(
|
||||||
|
Icons.Rounded.RemoveCircle,
|
||||||
|
modifier = Modifier.padding(end = 16.dp),
|
||||||
|
contentDescription = stringResource(id = R.string.settings_disable_kernel_umount),
|
||||||
|
tint = MaterialTheme.colorScheme.onBackground
|
||||||
|
)
|
||||||
|
},
|
||||||
|
enabled = umountStatus == "supported",
|
||||||
selectedIndex = kernelUmountMode,
|
selectedIndex = kernelUmountMode,
|
||||||
onSelectedIndexChange = { index ->
|
onSelectedIndexChange = { index ->
|
||||||
when (index) {
|
when (index) {
|
||||||
@@ -271,28 +319,44 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
var kernelSuLogMode by rememberSaveable {
|
var suLogMode by rememberSaveable {
|
||||||
mutableIntStateOf(
|
mutableIntStateOf(
|
||||||
run {
|
run {
|
||||||
val currentEnabled = Natives.isSuLogEnabled()
|
val currentEnabled = Natives.isSuLogEnabled()
|
||||||
val savedPersist = prefs.getInt("kernel_sulog_mode", 0)
|
val savedPersist = prefs.getInt("sulog_mode", 0)
|
||||||
if (savedPersist == 2) 2 else if (!currentEnabled) 1 else 0
|
if (savedPersist == 2) 2 else if (!currentEnabled) 1 else 0
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
val suLogStatus by produceState(initialValue = "") {
|
||||||
|
value = getFeatureStatus("sulog")
|
||||||
|
}
|
||||||
|
val suLogSummary = when (suLogStatus) {
|
||||||
|
"unsupported" -> stringResource(id = R.string.feature_status_unsupported_summary)
|
||||||
|
"managed" -> stringResource(id = R.string.feature_status_managed_summary)
|
||||||
|
else -> stringResource(id = R.string.settings_disable_sulog_summary)
|
||||||
|
}
|
||||||
SuperDropdown(
|
SuperDropdown(
|
||||||
icon = Icons.Filled.NoAccounts,
|
|
||||||
title = stringResource(id = R.string.settings_disable_sulog),
|
title = stringResource(id = R.string.settings_disable_sulog),
|
||||||
summary = stringResource(id = R.string.settings_disable_sulog_summary),
|
summary = suLogSummary,
|
||||||
items = modeItems,
|
items = modeItems,
|
||||||
selectedIndex = kernelSuLogMode,
|
leftAction = {
|
||||||
|
Icon(
|
||||||
|
Icons.Rounded.RemoveCircle,
|
||||||
|
modifier = Modifier.padding(end = 16.dp),
|
||||||
|
contentDescription = stringResource(id = R.string.settings_disable_sulog),
|
||||||
|
tint = MaterialTheme.colorScheme.onBackground
|
||||||
|
)
|
||||||
|
},
|
||||||
|
enabled = suLogStatus == "supported",
|
||||||
|
selectedIndex = suLogMode,
|
||||||
onSelectedIndexChange = { index ->
|
onSelectedIndexChange = { index ->
|
||||||
when (index) {
|
when (index) {
|
||||||
// Default: enable and save to persist
|
// Default: enable and save to persist
|
||||||
0 -> if (Natives.setSuLogEnabled(true)) {
|
0 -> if (Natives.setSuLogEnabled(true)) {
|
||||||
execKsud("feature save", true)
|
execKsud("feature save", true)
|
||||||
prefs.edit { putInt("kernel_sulog_mode", 0) }
|
prefs.edit { putInt("sulog_mode", 0) }
|
||||||
kernelSuLogMode = 0
|
suLogMode = 0
|
||||||
isSuLogEnabled = true
|
isSuLogEnabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -300,8 +364,8 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
|||||||
1 -> if (Natives.setSuLogEnabled(true)) {
|
1 -> if (Natives.setSuLogEnabled(true)) {
|
||||||
execKsud("feature save", true)
|
execKsud("feature save", true)
|
||||||
if (Natives.setSuLogEnabled(false)) {
|
if (Natives.setSuLogEnabled(false)) {
|
||||||
prefs.edit { putInt("kernel_sulog_mode", 0) }
|
prefs.edit { putInt("sulog_mode", 0) }
|
||||||
kernelSuLogMode = 1
|
suLogMode = 1
|
||||||
isSuLogEnabled = false
|
isSuLogEnabled = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -309,8 +373,8 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
|||||||
// Permanently disable: disable and save
|
// Permanently disable: disable and save
|
||||||
2 -> if (Natives.setSuLogEnabled(false)) {
|
2 -> if (Natives.setSuLogEnabled(false)) {
|
||||||
execKsud("feature save", true)
|
execKsud("feature save", true)
|
||||||
prefs.edit { putInt("kernel_sulog_mode", 2) }
|
prefs.edit { putInt("sulog_mode", 2) }
|
||||||
kernelSuLogMode = 2
|
suLogMode = 2
|
||||||
isSuLogEnabled = false
|
isSuLogEnabled = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -441,9 +505,7 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val lkmMode = Natives.isLkmMode
|
|
||||||
KsuIsValid {
|
KsuIsValid {
|
||||||
if (lkmMode) {
|
|
||||||
SettingItem(
|
SettingItem(
|
||||||
icon = Icons.Filled.FolderOff,
|
icon = Icons.Filled.FolderOff,
|
||||||
title = stringResource(R.string.umount_path_manager),
|
title = stringResource(R.string.umount_path_manager),
|
||||||
@@ -453,7 +515,6 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (showBottomsheet) {
|
if (showBottomsheet) {
|
||||||
LogBottomSheet(
|
LogBottomSheet(
|
||||||
@@ -496,7 +557,7 @@ fun SettingScreen(navigator: DestinationsNavigator) {
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
if (lkmMode) {
|
if (Natives.isLkmMode) {
|
||||||
UninstallItem(navigator) {
|
UninstallItem(navigator) {
|
||||||
loadingDialog.withLoading(it)
|
loadingDialog.withLoading(it)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -39,7 +39,6 @@ private val SPACING_LARGE = 16.dp
|
|||||||
data class UmountPathEntry(
|
data class UmountPathEntry(
|
||||||
val path: String,
|
val path: String,
|
||||||
val flags: Int,
|
val flags: Int,
|
||||||
val isDefault: Boolean
|
|
||||||
)
|
)
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@@ -291,10 +290,7 @@ fun UmountPathCard(
|
|||||||
Icon(
|
Icon(
|
||||||
imageVector = Icons.Filled.Folder,
|
imageVector = Icons.Filled.Folder,
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
tint = if (entry.isDefault)
|
tint = MaterialTheme.colorScheme.primary,
|
||||||
MaterialTheme.colorScheme.primary
|
|
||||||
else
|
|
||||||
MaterialTheme.colorScheme.secondary,
|
|
||||||
modifier = Modifier.size(24.dp)
|
modifier = Modifier.size(24.dp)
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -311,17 +307,11 @@ fun UmountPathCard(
|
|||||||
append(context.getString(R.string.flags))
|
append(context.getString(R.string.flags))
|
||||||
append(": ")
|
append(": ")
|
||||||
append(entry.flags.toUmountFlagName(context))
|
append(entry.flags.toUmountFlagName(context))
|
||||||
if (entry.isDefault) {
|
|
||||||
append(" | ")
|
|
||||||
append(context.getString(R.string.default_entry))
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
style = MaterialTheme.typography.bodySmall,
|
style = MaterialTheme.typography.bodySmall,
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!entry.isDefault) {
|
|
||||||
IconButton(
|
IconButton(
|
||||||
onClick = {
|
onClick = {
|
||||||
scope.launch {
|
scope.launch {
|
||||||
@@ -342,7 +332,6 @@ fun UmountPathCard(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
@@ -404,11 +393,10 @@ private fun parseUmountPaths(output: String): List<UmountPathEntry> {
|
|||||||
|
|
||||||
return lines.drop(2).mapNotNull { line ->
|
return lines.drop(2).mapNotNull { line ->
|
||||||
val parts = line.trim().split(Regex("\\s+"))
|
val parts = line.trim().split(Regex("\\s+"))
|
||||||
if (parts.size >= 3) {
|
if (parts.size >= 2) {
|
||||||
UmountPathEntry(
|
UmountPathEntry(
|
||||||
path = parts[0],
|
path = parts[0],
|
||||||
flags = parts[1].toIntOrNull() ?: -1,
|
flags = parts[1].toIntOrNull() ?: -1
|
||||||
isDefault = parts[2].equals("Yes", ignoreCase = true)
|
|
||||||
)
|
)
|
||||||
} else null
|
} else null
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,8 +47,6 @@ enum class SuSFSTab(val displayNameRes: Int) {
|
|||||||
SUS_PATHS(R.string.susfs_tab_sus_paths),
|
SUS_PATHS(R.string.susfs_tab_sus_paths),
|
||||||
SUS_LOOP_PATHS(R.string.susfs_tab_sus_loop_paths),
|
SUS_LOOP_PATHS(R.string.susfs_tab_sus_loop_paths),
|
||||||
SUS_MAPS(R.string.susfs_tab_sus_maps),
|
SUS_MAPS(R.string.susfs_tab_sus_maps),
|
||||||
SUS_MOUNTS(R.string.susfs_tab_sus_mounts),
|
|
||||||
TRY_UMOUNT(R.string.susfs_tab_try_umount),
|
|
||||||
KSTAT_CONFIG(R.string.susfs_tab_kstat_config),
|
KSTAT_CONFIG(R.string.susfs_tab_kstat_config),
|
||||||
PATH_SETTINGS(R.string.susfs_tab_path_settings),
|
PATH_SETTINGS(R.string.susfs_tab_path_settings),
|
||||||
ENABLED_FEATURES(R.string.susfs_tab_enabled_features);
|
ENABLED_FEATURES(R.string.susfs_tab_enabled_features);
|
||||||
@@ -99,8 +97,6 @@ fun SuSFSConfigScreen(
|
|||||||
var susPaths by remember { mutableStateOf(emptySet<String>()) }
|
var susPaths by remember { mutableStateOf(emptySet<String>()) }
|
||||||
var susLoopPaths by remember { mutableStateOf(emptySet<String>()) }
|
var susLoopPaths by remember { mutableStateOf(emptySet<String>()) }
|
||||||
var susMaps by remember { mutableStateOf(emptySet<String>()) }
|
var susMaps by remember { mutableStateOf(emptySet<String>()) }
|
||||||
var susMounts by remember { mutableStateOf(emptySet<String>()) }
|
|
||||||
var tryUmounts by remember { mutableStateOf(emptySet<String>()) }
|
|
||||||
var androidDataPath by remember { mutableStateOf("") }
|
var androidDataPath by remember { mutableStateOf("") }
|
||||||
var sdcardPath by remember { mutableStateOf("") }
|
var sdcardPath by remember { mutableStateOf("") }
|
||||||
|
|
||||||
@@ -125,8 +121,6 @@ fun SuSFSConfigScreen(
|
|||||||
var showAddLoopPathDialog by remember { mutableStateOf(false) }
|
var showAddLoopPathDialog by remember { mutableStateOf(false) }
|
||||||
var showAddSusMapDialog by remember { mutableStateOf(false) }
|
var showAddSusMapDialog by remember { mutableStateOf(false) }
|
||||||
var showAddAppPathDialog by remember { mutableStateOf(false) }
|
var showAddAppPathDialog by remember { mutableStateOf(false) }
|
||||||
var showAddMountDialog by remember { mutableStateOf(false) }
|
|
||||||
var showAddUmountDialog by remember { mutableStateOf(false) }
|
|
||||||
var showAddKstatStaticallyDialog by remember { mutableStateOf(false) }
|
var showAddKstatStaticallyDialog by remember { mutableStateOf(false) }
|
||||||
var showAddKstatDialog by remember { mutableStateOf(false) }
|
var showAddKstatDialog by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
@@ -134,8 +128,6 @@ fun SuSFSConfigScreen(
|
|||||||
var editingPath by remember { mutableStateOf<String?>(null) }
|
var editingPath by remember { mutableStateOf<String?>(null) }
|
||||||
var editingLoopPath by remember { mutableStateOf<String?>(null) }
|
var editingLoopPath by remember { mutableStateOf<String?>(null) }
|
||||||
var editingSusMap by remember { mutableStateOf<String?>(null) }
|
var editingSusMap by remember { mutableStateOf<String?>(null) }
|
||||||
var editingMount by remember { mutableStateOf<String?>(null) }
|
|
||||||
var editingUmount by remember { mutableStateOf<String?>(null) }
|
|
||||||
var editingKstatConfig by remember { mutableStateOf<String?>(null) }
|
var editingKstatConfig by remember { mutableStateOf<String?>(null) }
|
||||||
var editingKstatPath by remember { mutableStateOf<String?>(null) }
|
var editingKstatPath by remember { mutableStateOf<String?>(null) }
|
||||||
|
|
||||||
@@ -143,8 +135,6 @@ fun SuSFSConfigScreen(
|
|||||||
var showResetPathsDialog by remember { mutableStateOf(false) }
|
var showResetPathsDialog by remember { mutableStateOf(false) }
|
||||||
var showResetLoopPathsDialog by remember { mutableStateOf(false) }
|
var showResetLoopPathsDialog by remember { mutableStateOf(false) }
|
||||||
var showResetSusMapsDialog by remember { mutableStateOf(false) }
|
var showResetSusMapsDialog by remember { mutableStateOf(false) }
|
||||||
var showResetMountsDialog by remember { mutableStateOf(false) }
|
|
||||||
var showResetUmountsDialog by remember { mutableStateOf(false) }
|
|
||||||
var showResetKstatDialog by remember { mutableStateOf(false) }
|
var showResetKstatDialog by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
// 备份还原相关状态
|
// 备份还原相关状态
|
||||||
@@ -304,8 +294,6 @@ fun SuSFSConfigScreen(
|
|||||||
susPaths = SuSFSManager.getSusPaths(context)
|
susPaths = SuSFSManager.getSusPaths(context)
|
||||||
susLoopPaths = SuSFSManager.getSusLoopPaths(context)
|
susLoopPaths = SuSFSManager.getSusLoopPaths(context)
|
||||||
susMaps = SuSFSManager.getSusMaps(context)
|
susMaps = SuSFSManager.getSusMaps(context)
|
||||||
susMounts = SuSFSManager.getSusMounts(context)
|
|
||||||
tryUmounts = SuSFSManager.getTryUmounts(context)
|
|
||||||
androidDataPath = SuSFSManager.getAndroidDataPath(context)
|
androidDataPath = SuSFSManager.getAndroidDataPath(context)
|
||||||
sdcardPath = SuSFSManager.getSdcardPath(context)
|
sdcardPath = SuSFSManager.getSdcardPath(context)
|
||||||
kstatConfigs = SuSFSManager.getKstatConfigs(context)
|
kstatConfigs = SuSFSManager.getKstatConfigs(context)
|
||||||
@@ -477,8 +465,6 @@ fun SuSFSConfigScreen(
|
|||||||
susPaths = SuSFSManager.getSusPaths(context)
|
susPaths = SuSFSManager.getSusPaths(context)
|
||||||
susLoopPaths = SuSFSManager.getSusLoopPaths(context)
|
susLoopPaths = SuSFSManager.getSusLoopPaths(context)
|
||||||
susMaps = SuSFSManager.getSusMaps(context)
|
susMaps = SuSFSManager.getSusMaps(context)
|
||||||
susMounts = SuSFSManager.getSusMounts(context)
|
|
||||||
tryUmounts = SuSFSManager.getTryUmounts(context)
|
|
||||||
androidDataPath = SuSFSManager.getAndroidDataPath(context)
|
androidDataPath = SuSFSManager.getAndroidDataPath(context)
|
||||||
sdcardPath = SuSFSManager.getSdcardPath(context)
|
sdcardPath = SuSFSManager.getSdcardPath(context)
|
||||||
kstatConfigs = SuSFSManager.getKstatConfigs(context)
|
kstatConfigs = SuSFSManager.getKstatConfigs(context)
|
||||||
@@ -649,62 +635,6 @@ fun SuSFSConfigScreen(
|
|||||||
existingSusPaths = susPaths
|
existingSusPaths = susPaths
|
||||||
)
|
)
|
||||||
|
|
||||||
AddPathDialog(
|
|
||||||
showDialog = showAddMountDialog,
|
|
||||||
onDismiss = {
|
|
||||||
showAddMountDialog = false
|
|
||||||
editingMount = null
|
|
||||||
},
|
|
||||||
onConfirm = { mount ->
|
|
||||||
coroutineScope.launch {
|
|
||||||
isLoading = true
|
|
||||||
val success = if (editingMount != null) {
|
|
||||||
SuSFSManager.editSusMount(context, editingMount!!, mount)
|
|
||||||
} else {
|
|
||||||
SuSFSManager.addSusMount(context, mount)
|
|
||||||
}
|
|
||||||
if (success) {
|
|
||||||
susMounts = SuSFSManager.getSusMounts(context)
|
|
||||||
}
|
|
||||||
isLoading = false
|
|
||||||
showAddMountDialog = false
|
|
||||||
editingMount = null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
isLoading = isLoading,
|
|
||||||
titleRes = if (editingMount != null) R.string.susfs_edit_sus_mount else R.string.susfs_add_sus_mount,
|
|
||||||
labelRes = R.string.susfs_mount_path_label,
|
|
||||||
placeholderRes = R.string.susfs_path_placeholder,
|
|
||||||
initialValue = editingMount ?: ""
|
|
||||||
)
|
|
||||||
|
|
||||||
AddTryUmountDialog(
|
|
||||||
showDialog = showAddUmountDialog,
|
|
||||||
onDismiss = {
|
|
||||||
showAddUmountDialog = false
|
|
||||||
editingUmount = null
|
|
||||||
},
|
|
||||||
onConfirm = { path, mode ->
|
|
||||||
coroutineScope.launch {
|
|
||||||
isLoading = true
|
|
||||||
val success = if (editingUmount != null) {
|
|
||||||
SuSFSManager.editTryUmount(context, editingUmount!!, path, mode)
|
|
||||||
} else {
|
|
||||||
SuSFSManager.addTryUmount(context, path, mode)
|
|
||||||
}
|
|
||||||
if (success) {
|
|
||||||
tryUmounts = SuSFSManager.getTryUmounts(context)
|
|
||||||
}
|
|
||||||
isLoading = false
|
|
||||||
showAddUmountDialog = false
|
|
||||||
editingUmount = null
|
|
||||||
}
|
|
||||||
},
|
|
||||||
isLoading = isLoading,
|
|
||||||
initialPath = editingUmount?.split("|")?.get(0) ?: "",
|
|
||||||
initialMode = editingUmount?.split("|")?.get(1)?.toIntOrNull() ?: 0
|
|
||||||
)
|
|
||||||
|
|
||||||
AddKstatStaticallyDialog(
|
AddKstatStaticallyDialog(
|
||||||
showDialog = showAddKstatStaticallyDialog,
|
showDialog = showAddKstatStaticallyDialog,
|
||||||
onDismiss = {
|
onDismiss = {
|
||||||
@@ -865,48 +795,6 @@ fun SuSFSConfigScreen(
|
|||||||
isDestructive = true
|
isDestructive = true
|
||||||
)
|
)
|
||||||
|
|
||||||
ConfirmDialog(
|
|
||||||
showDialog = showResetMountsDialog,
|
|
||||||
onDismiss = { showResetMountsDialog = false },
|
|
||||||
onConfirm = {
|
|
||||||
coroutineScope.launch {
|
|
||||||
isLoading = true
|
|
||||||
SuSFSManager.saveSusMounts(context, emptySet())
|
|
||||||
susMounts = emptySet()
|
|
||||||
if (SuSFSManager.isAutoStartEnabled(context)) {
|
|
||||||
SuSFSManager.configureAutoStart(context, true)
|
|
||||||
}
|
|
||||||
isLoading = false
|
|
||||||
showResetMountsDialog = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
titleRes = R.string.susfs_reset_mounts_title,
|
|
||||||
messageRes = R.string.susfs_reset_mounts_message,
|
|
||||||
isLoading = isLoading,
|
|
||||||
isDestructive = true
|
|
||||||
)
|
|
||||||
|
|
||||||
ConfirmDialog(
|
|
||||||
showDialog = showResetUmountsDialog,
|
|
||||||
onDismiss = { showResetUmountsDialog = false },
|
|
||||||
onConfirm = {
|
|
||||||
coroutineScope.launch {
|
|
||||||
isLoading = true
|
|
||||||
SuSFSManager.saveTryUmounts(context, emptySet())
|
|
||||||
tryUmounts = emptySet()
|
|
||||||
if (SuSFSManager.isAutoStartEnabled(context)) {
|
|
||||||
SuSFSManager.configureAutoStart(context, true)
|
|
||||||
}
|
|
||||||
isLoading = false
|
|
||||||
showResetUmountsDialog = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
titleRes = R.string.susfs_reset_umounts_title,
|
|
||||||
messageRes = R.string.susfs_reset_umounts_message,
|
|
||||||
isLoading = isLoading,
|
|
||||||
isDestructive = true
|
|
||||||
)
|
|
||||||
|
|
||||||
ConfirmDialog(
|
ConfirmDialog(
|
||||||
showDialog = showResetKstatDialog,
|
showDialog = showResetKstatDialog,
|
||||||
onDismiss = { showResetKstatDialog = false },
|
onDismiss = { showResetKstatDialog = false },
|
||||||
@@ -1106,50 +994,6 @@ fun SuSFSConfigScreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
SuSFSTab.SUS_MOUNTS -> {
|
|
||||||
OutlinedButton(
|
|
||||||
onClick = { showResetMountsDialog = true },
|
|
||||||
enabled = !isLoading && susMounts.isNotEmpty(),
|
|
||||||
shape = RoundedCornerShape(8.dp),
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.height(40.dp)
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Default.RestoreFromTrash,
|
|
||||||
contentDescription = null,
|
|
||||||
modifier = Modifier.size(16.dp)
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.width(6.dp))
|
|
||||||
Text(
|
|
||||||
stringResource(R.string.susfs_reset_mounts_title),
|
|
||||||
fontWeight = FontWeight.Medium
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SuSFSTab.TRY_UMOUNT -> {
|
|
||||||
OutlinedButton(
|
|
||||||
onClick = { showResetUmountsDialog = true },
|
|
||||||
enabled = !isLoading && tryUmounts.isNotEmpty(),
|
|
||||||
shape = RoundedCornerShape(8.dp),
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.height(40.dp)
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Default.RestoreFromTrash,
|
|
||||||
contentDescription = null,
|
|
||||||
modifier = Modifier.size(16.dp)
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.width(6.dp))
|
|
||||||
Text(
|
|
||||||
stringResource(R.string.susfs_reset_umounts_title),
|
|
||||||
fontWeight = FontWeight.Medium
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
SuSFSTab.KSTAT_CONFIG -> {
|
SuSFSTab.KSTAT_CONFIG -> {
|
||||||
OutlinedButton(
|
OutlinedButton(
|
||||||
onClick = { showResetKstatDialog = true },
|
onClick = { showResetKstatDialog = true },
|
||||||
@@ -1325,6 +1169,32 @@ fun SuSFSConfigScreen(
|
|||||||
}
|
}
|
||||||
isLoading = false
|
isLoading = false
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
hideSusMountsForAllProcs = hideSusMountsForAllProcs,
|
||||||
|
onHideSusMountsForAllProcsChange = { hideForAll ->
|
||||||
|
coroutineScope.launch {
|
||||||
|
isLoading = true
|
||||||
|
if (SuSFSManager.setHideSusMountsForAllProcs(
|
||||||
|
context,
|
||||||
|
hideForAll
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
hideSusMountsForAllProcs = hideForAll
|
||||||
|
}
|
||||||
|
isLoading = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
umountForZygoteIsoService = umountForZygoteIsoService,
|
||||||
|
onUmountForZygoteIsoServiceChange = { enabled ->
|
||||||
|
coroutineScope.launch {
|
||||||
|
isLoading = true
|
||||||
|
val success =
|
||||||
|
SuSFSManager.setUmountForZygoteIsoService(context, enabled)
|
||||||
|
if (success) {
|
||||||
|
umountForZygoteIsoService = enabled
|
||||||
|
}
|
||||||
|
isLoading = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -1390,76 +1260,6 @@ fun SuSFSConfigScreen(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
SuSFSTab.SUS_MOUNTS -> {
|
|
||||||
val isSusVersion158 = remember { isSusVersion158() }
|
|
||||||
|
|
||||||
SusMountsContent(
|
|
||||||
susMounts = susMounts,
|
|
||||||
hideSusMountsForAllProcs = hideSusMountsForAllProcs,
|
|
||||||
isSusVersion158 = isSusVersion158,
|
|
||||||
isLoading = isLoading,
|
|
||||||
onAddMount = { showAddMountDialog = true },
|
|
||||||
onRemoveMount = { mount ->
|
|
||||||
coroutineScope.launch {
|
|
||||||
isLoading = true
|
|
||||||
if (SuSFSManager.removeSusMount(context, mount)) {
|
|
||||||
susMounts = SuSFSManager.getSusMounts(context)
|
|
||||||
}
|
|
||||||
isLoading = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onEditMount = { mount ->
|
|
||||||
editingMount = mount
|
|
||||||
showAddMountDialog = true
|
|
||||||
},
|
|
||||||
onToggleHideSusMountsForAllProcs = { hideForAll ->
|
|
||||||
coroutineScope.launch {
|
|
||||||
isLoading = true
|
|
||||||
if (SuSFSManager.setHideSusMountsForAllProcs(
|
|
||||||
context,
|
|
||||||
hideForAll
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
hideSusMountsForAllProcs = hideForAll
|
|
||||||
}
|
|
||||||
isLoading = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
SuSFSTab.TRY_UMOUNT -> {
|
|
||||||
TryUmountContent(
|
|
||||||
tryUmounts = tryUmounts,
|
|
||||||
umountForZygoteIsoService = umountForZygoteIsoService,
|
|
||||||
isLoading = isLoading,
|
|
||||||
onAddUmount = { showAddUmountDialog = true },
|
|
||||||
onRemoveUmount = { umountEntry ->
|
|
||||||
coroutineScope.launch {
|
|
||||||
isLoading = true
|
|
||||||
if (SuSFSManager.removeTryUmount(context, umountEntry)) {
|
|
||||||
tryUmounts = SuSFSManager.getTryUmounts(context)
|
|
||||||
}
|
|
||||||
isLoading = false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onEditUmount = { umountEntry ->
|
|
||||||
editingUmount = umountEntry
|
|
||||||
showAddUmountDialog = true
|
|
||||||
},
|
|
||||||
onToggleUmountForZygoteIsoService = { enabled ->
|
|
||||||
coroutineScope.launch {
|
|
||||||
isLoading = true
|
|
||||||
val success =
|
|
||||||
SuSFSManager.setUmountForZygoteIsoService(context, enabled)
|
|
||||||
if (success) {
|
|
||||||
umountForZygoteIsoService = enabled
|
|
||||||
}
|
|
||||||
isLoading = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
SuSFSTab.KSTAT_CONFIG -> {
|
SuSFSTab.KSTAT_CONFIG -> {
|
||||||
KstatConfigContent(
|
KstatConfigContent(
|
||||||
@@ -1570,7 +1370,11 @@ private fun BasicSettingsContent(
|
|||||||
enableCleanupResidue: Boolean,
|
enableCleanupResidue: Boolean,
|
||||||
onEnableCleanupResidueChange: (Boolean) -> Unit,
|
onEnableCleanupResidueChange: (Boolean) -> Unit,
|
||||||
enableAvcLogSpoofing: Boolean,
|
enableAvcLogSpoofing: Boolean,
|
||||||
onEnableAvcLogSpoofingChange: (Boolean) -> Unit
|
onEnableAvcLogSpoofingChange: (Boolean) -> Unit,
|
||||||
|
hideSusMountsForAllProcs: Boolean,
|
||||||
|
onHideSusMountsForAllProcsChange: (Boolean) -> Unit,
|
||||||
|
umountForZygoteIsoService: Boolean,
|
||||||
|
onUmountForZygoteIsoServiceChange: (Boolean) -> Unit
|
||||||
) {
|
) {
|
||||||
var scriptLocationExpanded by remember { mutableStateOf(false) }
|
var scriptLocationExpanded by remember { mutableStateOf(false) }
|
||||||
val isAbDevice = produceState(initialValue = false) {
|
val isAbDevice = produceState(initialValue = false) {
|
||||||
@@ -1946,6 +1750,69 @@ private fun BasicSettingsContent(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 对所有进程隐藏sus挂载开关(仅在1.5.8+版本显示)
|
||||||
|
val isSusVersion158 = isSusVersion158()
|
||||||
|
if (isSusVersion158) {
|
||||||
|
SusMountHidingControlCard(
|
||||||
|
hideSusMountsForAllProcs = hideSusMountsForAllProcs,
|
||||||
|
isLoading = isLoading,
|
||||||
|
onToggleHiding = onHideSusMountsForAllProcsChange
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 卸载 Zygote 隔离服务开关(仅在1.5.8+版本显示)
|
||||||
|
if (isSusVersion158) {
|
||||||
|
Card(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
colors = CardDefaults.cardColors(
|
||||||
|
containerColor = MaterialTheme.colorScheme.surface
|
||||||
|
),
|
||||||
|
shape = RoundedCornerShape(12.dp)
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(12.dp),
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween,
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.Security,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = MaterialTheme.colorScheme.primary,
|
||||||
|
modifier = Modifier.size(18.dp)
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.umount_zygote_iso_service),
|
||||||
|
style = MaterialTheme.typography.titleMedium,
|
||||||
|
fontWeight = FontWeight.Medium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Spacer(modifier = Modifier.height(6.dp))
|
||||||
|
Text(
|
||||||
|
text = stringResource(R.string.umount_zygote_iso_service_description),
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
|
lineHeight = 14.sp
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Switch(
|
||||||
|
checked = umountForZygoteIsoService,
|
||||||
|
onCheckedChange = onUmountForZygoteIsoServiceChange,
|
||||||
|
enabled = !isLoading
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 槽位信息按钮
|
// 槽位信息按钮
|
||||||
if (isAbDevice) {
|
if (isAbDevice) {
|
||||||
Card(
|
Card(
|
||||||
|
|||||||
@@ -401,126 +401,6 @@ fun AppIcon(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 添加尝试卸载对话框
|
|
||||||
*/
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
|
||||||
@Composable
|
|
||||||
fun AddTryUmountDialog(
|
|
||||||
showDialog: Boolean,
|
|
||||||
onDismiss: () -> Unit,
|
|
||||||
onConfirm: (String, Int) -> Unit,
|
|
||||||
isLoading: Boolean,
|
|
||||||
initialPath: String = "",
|
|
||||||
initialMode: Int = 0
|
|
||||||
) {
|
|
||||||
var newUmountPath by remember { mutableStateOf("") }
|
|
||||||
var newUmountMode by remember { mutableIntStateOf(0) }
|
|
||||||
var umountModeExpanded by remember { mutableStateOf(false) }
|
|
||||||
|
|
||||||
// 当对话框显示时,设置初始值
|
|
||||||
LaunchedEffect(showDialog, initialPath, initialMode) {
|
|
||||||
if (showDialog) {
|
|
||||||
newUmountPath = initialPath
|
|
||||||
newUmountMode = initialMode
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (showDialog) {
|
|
||||||
AlertDialog(
|
|
||||||
onDismissRequest = onDismiss,
|
|
||||||
title = {
|
|
||||||
Text(
|
|
||||||
stringResource(if (initialPath.isNotEmpty()) R.string.susfs_edit_try_umount else R.string.susfs_add_try_umount),
|
|
||||||
style = MaterialTheme.typography.titleLarge,
|
|
||||||
fontWeight = FontWeight.Bold
|
|
||||||
)
|
|
||||||
},
|
|
||||||
text = {
|
|
||||||
Column(
|
|
||||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
|
||||||
) {
|
|
||||||
OutlinedTextField(
|
|
||||||
value = newUmountPath,
|
|
||||||
onValueChange = { newUmountPath = it },
|
|
||||||
label = { Text(stringResource(R.string.susfs_path_label)) },
|
|
||||||
placeholder = { Text(stringResource(R.string.susfs_path_placeholder)) },
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
shape = RoundedCornerShape(8.dp)
|
|
||||||
)
|
|
||||||
|
|
||||||
ExposedDropdownMenuBox(
|
|
||||||
expanded = umountModeExpanded,
|
|
||||||
onExpandedChange = { umountModeExpanded = !umountModeExpanded }
|
|
||||||
) {
|
|
||||||
OutlinedTextField(
|
|
||||||
value = if (newUmountMode == 0)
|
|
||||||
stringResource(R.string.susfs_umount_mode_normal)
|
|
||||||
else
|
|
||||||
stringResource(R.string.susfs_umount_mode_detach),
|
|
||||||
onValueChange = { },
|
|
||||||
readOnly = true,
|
|
||||||
label = { Text(stringResource(R.string.susfs_umount_mode_label)) },
|
|
||||||
trailingIcon = { ExposedDropdownMenuDefaults.TrailingIcon(expanded = umountModeExpanded) },
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.menuAnchor(ExposedDropdownMenuAnchorType.PrimaryEditable, true),
|
|
||||||
shape = RoundedCornerShape(8.dp)
|
|
||||||
)
|
|
||||||
ExposedDropdownMenu(
|
|
||||||
expanded = umountModeExpanded,
|
|
||||||
onDismissRequest = { umountModeExpanded = false }
|
|
||||||
) {
|
|
||||||
DropdownMenuItem(
|
|
||||||
text = { Text(stringResource(R.string.susfs_umount_mode_normal)) },
|
|
||||||
onClick = {
|
|
||||||
newUmountMode = 0
|
|
||||||
umountModeExpanded = false
|
|
||||||
}
|
|
||||||
)
|
|
||||||
DropdownMenuItem(
|
|
||||||
text = { Text(stringResource(R.string.susfs_umount_mode_detach)) },
|
|
||||||
onClick = {
|
|
||||||
newUmountMode = 1
|
|
||||||
umountModeExpanded = false
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
confirmButton = {
|
|
||||||
Button(
|
|
||||||
onClick = {
|
|
||||||
if (newUmountPath.isNotBlank()) {
|
|
||||||
onConfirm(newUmountPath.trim(), newUmountMode)
|
|
||||||
newUmountPath = ""
|
|
||||||
newUmountMode = 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
enabled = newUmountPath.isNotBlank() && !isLoading,
|
|
||||||
shape = RoundedCornerShape(8.dp)
|
|
||||||
) {
|
|
||||||
Text(stringResource(if (initialPath.isNotEmpty()) R.string.susfs_save else R.string.add))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
dismissButton = {
|
|
||||||
TextButton(
|
|
||||||
onClick = {
|
|
||||||
onDismiss()
|
|
||||||
newUmountPath = ""
|
|
||||||
newUmountMode = 0
|
|
||||||
},
|
|
||||||
shape = RoundedCornerShape(8.dp)
|
|
||||||
) {
|
|
||||||
Text(stringResource(R.string.cancel))
|
|
||||||
}
|
|
||||||
},
|
|
||||||
shape = RoundedCornerShape(12.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 添加Kstat静态配置对话框
|
* 添加Kstat静态配置对话框
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ fun SusPathsContent(
|
|||||||
|
|
||||||
val (appPathGroups, otherPaths) = remember(susPaths) {
|
val (appPathGroups, otherPaths) = remember(susPaths) {
|
||||||
val appPathRegex = Regex(".*/Android/data/([^/]+)/?.*")
|
val appPathRegex = Regex(".*/Android/data/([^/]+)/?.*")
|
||||||
val uidPathRegex = Regex("/sys/fs/cgroup/uid_([0-9]+)")
|
val uidPathRegex = Regex("/sys/fs/cgroup(?:/[^/]+)*/uid_([0-9]+)")
|
||||||
val appPathMap = mutableMapOf<String, MutableList<String>>()
|
val appPathMap = mutableMapOf<String, MutableList<String>>()
|
||||||
val uidToPackageMap = mutableMapOf<String, String>()
|
val uidToPackageMap = mutableMapOf<String, String>()
|
||||||
val others = mutableListOf<String>()
|
val others = mutableListOf<String>()
|
||||||
@@ -420,210 +420,6 @@ fun SusMapsContent(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* SUS挂载内容组件
|
|
||||||
*/
|
|
||||||
@Composable
|
|
||||||
fun SusMountsContent(
|
|
||||||
susMounts: Set<String>,
|
|
||||||
hideSusMountsForAllProcs: Boolean,
|
|
||||||
isSusVersion158: Boolean,
|
|
||||||
isLoading: Boolean,
|
|
||||||
onAddMount: () -> Unit,
|
|
||||||
onRemoveMount: (String) -> Unit,
|
|
||||||
onEditMount: ((String) -> Unit)? = null,
|
|
||||||
onToggleHideSusMountsForAllProcs: (Boolean) -> Unit
|
|
||||||
) {
|
|
||||||
Box(modifier = Modifier.fillMaxSize()) {
|
|
||||||
LazyColumn(
|
|
||||||
modifier = Modifier.fillMaxSize(),
|
|
||||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
|
||||||
) {
|
|
||||||
if (isSusVersion158) {
|
|
||||||
item {
|
|
||||||
SusMountHidingControlCard(
|
|
||||||
hideSusMountsForAllProcs = hideSusMountsForAllProcs,
|
|
||||||
isLoading = isLoading,
|
|
||||||
onToggleHiding = onToggleHideSusMountsForAllProcs
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (susMounts.isEmpty()) {
|
|
||||||
item {
|
|
||||||
EmptyStateCard(
|
|
||||||
message = stringResource(R.string.susfs_no_mounts_configured)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
items(susMounts.toList()) { mount ->
|
|
||||||
PathItemCard(
|
|
||||||
path = mount,
|
|
||||||
icon = Icons.Default.Storage,
|
|
||||||
onDelete = { onRemoveMount(mount) },
|
|
||||||
onEdit = if (onEditMount != null) { { onEditMount(mount) } } else null,
|
|
||||||
isLoading = isLoading
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
item {
|
|
||||||
Row(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(vertical = 16.dp),
|
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
|
||||||
) {
|
|
||||||
Button(
|
|
||||||
onClick = onAddMount,
|
|
||||||
modifier = Modifier
|
|
||||||
.weight(1f)
|
|
||||||
.height(48.dp),
|
|
||||||
shape = RoundedCornerShape(8.dp)
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Default.Add,
|
|
||||||
contentDescription = null,
|
|
||||||
modifier = Modifier.size(24.dp)
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.width(8.dp))
|
|
||||||
Text(text = stringResource(R.string.add))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 尝试卸载内容组件
|
|
||||||
*/
|
|
||||||
@Composable
|
|
||||||
fun TryUmountContent(
|
|
||||||
tryUmounts: Set<String>,
|
|
||||||
umountForZygoteIsoService: Boolean,
|
|
||||||
isLoading: Boolean,
|
|
||||||
onAddUmount: () -> Unit,
|
|
||||||
onRemoveUmount: (String) -> Unit,
|
|
||||||
onEditUmount: ((String) -> Unit)? = null,
|
|
||||||
onToggleUmountForZygoteIsoService: (Boolean) -> Unit
|
|
||||||
) {
|
|
||||||
Box(modifier = Modifier.fillMaxSize()) {
|
|
||||||
LazyColumn(
|
|
||||||
modifier = Modifier.fillMaxSize(),
|
|
||||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
|
||||||
) {
|
|
||||||
if (isSusVersion158()) {
|
|
||||||
item {
|
|
||||||
Card(
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
colors = CardDefaults.cardColors(
|
|
||||||
containerColor = MaterialTheme.colorScheme.surface
|
|
||||||
),
|
|
||||||
shape = RoundedCornerShape(12.dp)
|
|
||||||
) {
|
|
||||||
Row(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(12.dp),
|
|
||||||
horizontalArrangement = Arrangement.SpaceBetween,
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
|
||||||
) {
|
|
||||||
Column(
|
|
||||||
modifier = Modifier.weight(1f)
|
|
||||||
) {
|
|
||||||
Row(
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Default.Security,
|
|
||||||
contentDescription = null,
|
|
||||||
tint = MaterialTheme.colorScheme.primary,
|
|
||||||
modifier = Modifier.size(18.dp)
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.width(8.dp))
|
|
||||||
Text(
|
|
||||||
text = stringResource(R.string.umount_zygote_iso_service),
|
|
||||||
style = MaterialTheme.typography.titleMedium,
|
|
||||||
fontWeight = FontWeight.Medium,
|
|
||||||
color = MaterialTheme.colorScheme.onSurface
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Spacer(modifier = Modifier.height(6.dp))
|
|
||||||
Text(
|
|
||||||
text = stringResource(R.string.umount_zygote_iso_service_description),
|
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant,
|
|
||||||
lineHeight = 14.sp
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Switch(
|
|
||||||
checked = umountForZygoteIsoService,
|
|
||||||
onCheckedChange = onToggleUmountForZygoteIsoService,
|
|
||||||
enabled = !isLoading
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tryUmounts.isEmpty()) {
|
|
||||||
item {
|
|
||||||
EmptyStateCard(
|
|
||||||
message = stringResource(R.string.susfs_no_umounts_configured)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
items(tryUmounts.toList()) { umountEntry ->
|
|
||||||
val parts = umountEntry.split("|")
|
|
||||||
val path = if (parts.isNotEmpty()) parts[0] else umountEntry
|
|
||||||
val mode = if (parts.size > 1) parts[1] else "0"
|
|
||||||
val modeText = if (mode == "0")
|
|
||||||
stringResource(R.string.susfs_umount_mode_normal_short)
|
|
||||||
else
|
|
||||||
stringResource(R.string.susfs_umount_mode_detach_short)
|
|
||||||
|
|
||||||
PathItemCard(
|
|
||||||
path = path,
|
|
||||||
icon = Icons.Default.Storage,
|
|
||||||
additionalInfo = stringResource(R.string.susfs_umount_mode_display, modeText, mode),
|
|
||||||
onDelete = { onRemoveUmount(umountEntry) },
|
|
||||||
onEdit = if (onEditUmount != null) { { onEditUmount(umountEntry) } } else null,
|
|
||||||
isLoading = isLoading
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
item {
|
|
||||||
Row(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(vertical = 16.dp),
|
|
||||||
horizontalArrangement = Arrangement.spacedBy(8.dp),
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
|
||||||
) {
|
|
||||||
Button(
|
|
||||||
onClick = onAddUmount,
|
|
||||||
modifier = Modifier
|
|
||||||
.weight(1f)
|
|
||||||
.height(48.dp),
|
|
||||||
shape = RoundedCornerShape(8.dp)
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
imageVector = Icons.Default.Add,
|
|
||||||
contentDescription = null,
|
|
||||||
modifier = Modifier.size(24.dp)
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.width(8.dp))
|
|
||||||
Text(text = stringResource(R.string.add))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Kstat配置内容组件
|
* Kstat配置内容组件
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ import com.sukisu.ultra.ui.util.getRootShell
|
|||||||
import com.sukisu.ultra.ui.util.getSuSFSVersion
|
import com.sukisu.ultra.ui.util.getSuSFSVersion
|
||||||
import com.sukisu.ultra.ui.util.getSuSFSFeatures
|
import com.sukisu.ultra.ui.util.getSuSFSFeatures
|
||||||
import com.sukisu.ultra.ui.viewmodel.SuperUserViewModel
|
import com.sukisu.ultra.ui.viewmodel.SuperUserViewModel
|
||||||
|
import com.topjohnwu.superuser.io.SuFile
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.async
|
import kotlinx.coroutines.async
|
||||||
import kotlinx.coroutines.awaitAll
|
import kotlinx.coroutines.awaitAll
|
||||||
@@ -44,8 +45,6 @@ object SuSFSManager {
|
|||||||
private const val KEY_SUS_LOOP_PATHS = "sus_loop_paths"
|
private const val KEY_SUS_LOOP_PATHS = "sus_loop_paths"
|
||||||
|
|
||||||
private const val KEY_SUS_MAPS = "sus_maps"
|
private const val KEY_SUS_MAPS = "sus_maps"
|
||||||
private const val KEY_SUS_MOUNTS = "sus_mounts"
|
|
||||||
private const val KEY_TRY_UMOUNTS = "try_umounts"
|
|
||||||
private const val KEY_ANDROID_DATA_PATH = "android_data_path"
|
private const val KEY_ANDROID_DATA_PATH = "android_data_path"
|
||||||
private const val KEY_SDCARD_PATH = "sdcard_path"
|
private const val KEY_SDCARD_PATH = "sdcard_path"
|
||||||
private const val KEY_ENABLE_LOG = "enable_log"
|
private const val KEY_ENABLE_LOG = "enable_log"
|
||||||
@@ -71,7 +70,7 @@ object SuSFSManager {
|
|||||||
const val MAX_SUSFS_VERSION = "2.0.0"
|
const val MAX_SUSFS_VERSION = "2.0.0"
|
||||||
private const val BACKUP_FILE_EXTENSION = ".susfs_backup"
|
private const val BACKUP_FILE_EXTENSION = ".susfs_backup"
|
||||||
private const val MEDIA_DATA_PATH = "/data/media/0/Android/data"
|
private const val MEDIA_DATA_PATH = "/data/media/0/Android/data"
|
||||||
private const val CGROUP_UID_PATH_PREFIX = "/sys/fs/cgroup/uid_"
|
private const val CGROUP_BASE_PATH = "/sys/fs/cgroup"
|
||||||
|
|
||||||
data class SlotInfo(val slotName: String, val uname: String, val buildTime: String)
|
data class SlotInfo(val slotName: String, val uname: String, val buildTime: String)
|
||||||
data class CommandResult(val isSuccess: Boolean, val output: String, val errorOutput: String = "")
|
data class CommandResult(val isSuccess: Boolean, val output: String, val errorOutput: String = "")
|
||||||
@@ -157,8 +156,6 @@ object SuSFSManager {
|
|||||||
val susPaths: Set<String>,
|
val susPaths: Set<String>,
|
||||||
val susLoopPaths: Set<String>,
|
val susLoopPaths: Set<String>,
|
||||||
val susMaps: Set<String>,
|
val susMaps: Set<String>,
|
||||||
val susMounts: Set<String>,
|
|
||||||
val tryUmounts: Set<String>,
|
|
||||||
val androidDataPath: String,
|
val androidDataPath: String,
|
||||||
val sdcardPath: String,
|
val sdcardPath: String,
|
||||||
val enableLog: Boolean,
|
val enableLog: Boolean,
|
||||||
@@ -180,8 +177,6 @@ object SuSFSManager {
|
|||||||
susPaths.isNotEmpty() ||
|
susPaths.isNotEmpty() ||
|
||||||
susLoopPaths.isNotEmpty() ||
|
susLoopPaths.isNotEmpty() ||
|
||||||
susMaps.isNotEmpty() ||
|
susMaps.isNotEmpty() ||
|
||||||
susMounts.isNotEmpty() ||
|
|
||||||
tryUmounts.isNotEmpty() ||
|
|
||||||
kstatConfigs.isNotEmpty() ||
|
kstatConfigs.isNotEmpty() ||
|
||||||
addKstatPaths.isNotEmpty()
|
addKstatPaths.isNotEmpty()
|
||||||
}
|
}
|
||||||
@@ -271,8 +266,6 @@ object SuSFSManager {
|
|||||||
susPaths = getSusPaths(context),
|
susPaths = getSusPaths(context),
|
||||||
susLoopPaths = getSusLoopPaths(context),
|
susLoopPaths = getSusLoopPaths(context),
|
||||||
susMaps = getSusMaps(context),
|
susMaps = getSusMaps(context),
|
||||||
susMounts = getSusMounts(context),
|
|
||||||
tryUmounts = getTryUmounts(context),
|
|
||||||
androidDataPath = getAndroidDataPath(context),
|
androidDataPath = getAndroidDataPath(context),
|
||||||
sdcardPath = getSdcardPath(context),
|
sdcardPath = getSdcardPath(context),
|
||||||
enableLog = getEnableLogState(context),
|
enableLog = getEnableLogState(context),
|
||||||
@@ -381,18 +374,6 @@ object SuSFSManager {
|
|||||||
fun getSusMaps(context: Context): Set<String> =
|
fun getSusMaps(context: Context): Set<String> =
|
||||||
getPrefs(context).getStringSet(KEY_SUS_MAPS, emptySet()) ?: emptySet()
|
getPrefs(context).getStringSet(KEY_SUS_MAPS, emptySet()) ?: emptySet()
|
||||||
|
|
||||||
fun saveSusMounts(context: Context, mounts: Set<String>) =
|
|
||||||
getPrefs(context).edit { putStringSet(KEY_SUS_MOUNTS, mounts) }
|
|
||||||
|
|
||||||
fun getSusMounts(context: Context): Set<String> =
|
|
||||||
getPrefs(context).getStringSet(KEY_SUS_MOUNTS, emptySet()) ?: emptySet()
|
|
||||||
|
|
||||||
fun saveTryUmounts(context: Context, umounts: Set<String>) =
|
|
||||||
getPrefs(context).edit { putStringSet(KEY_TRY_UMOUNTS, umounts) }
|
|
||||||
|
|
||||||
fun getTryUmounts(context: Context): Set<String> =
|
|
||||||
getPrefs(context).getStringSet(KEY_TRY_UMOUNTS, emptySet()) ?: emptySet()
|
|
||||||
|
|
||||||
fun saveKstatConfigs(context: Context, configs: Set<String>) =
|
fun saveKstatConfigs(context: Context, configs: Set<String>) =
|
||||||
getPrefs(context).edit { putStringSet(KEY_KSTAT_CONFIGS, configs) }
|
getPrefs(context).edit { putStringSet(KEY_KSTAT_CONFIGS, configs) }
|
||||||
|
|
||||||
@@ -494,7 +475,44 @@ object SuSFSManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun buildUidPath(uid: Int): String = "$CGROUP_UID_PATH_PREFIX$uid"
|
private fun checkPathExists(path: String): Boolean {
|
||||||
|
return try {
|
||||||
|
val shell = try {
|
||||||
|
getRootShell()
|
||||||
|
} catch (_: Exception) {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
val file = if (shell != null) {
|
||||||
|
SuFile(path).apply { setShell(shell) }
|
||||||
|
} else {
|
||||||
|
File(path)
|
||||||
|
}
|
||||||
|
|
||||||
|
file.exists() && file.isDirectory
|
||||||
|
} catch (_: Exception) {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun buildUidPath(uid: Int): String {
|
||||||
|
val possiblePaths = listOf(
|
||||||
|
"$CGROUP_BASE_PATH/uid_$uid",
|
||||||
|
"$CGROUP_BASE_PATH/apps/uid_$uid",
|
||||||
|
"$CGROUP_BASE_PATH/system/uid_$uid",
|
||||||
|
"$CGROUP_BASE_PATH/freezer/uid_$uid",
|
||||||
|
"$CGROUP_BASE_PATH/memory/uid_$uid",
|
||||||
|
"$CGROUP_BASE_PATH/cpuset/uid_$uid",
|
||||||
|
"$CGROUP_BASE_PATH/cpu/uid_$uid"
|
||||||
|
)
|
||||||
|
|
||||||
|
for (path in possiblePaths) {
|
||||||
|
if (checkPathExists(path)) {
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return possiblePaths[0]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// 快捷添加应用路径
|
// 快捷添加应用路径
|
||||||
@@ -547,8 +565,6 @@ object SuSFSManager {
|
|||||||
KEY_SUS_PATHS to getSusPaths(context),
|
KEY_SUS_PATHS to getSusPaths(context),
|
||||||
KEY_SUS_LOOP_PATHS to getSusLoopPaths(context),
|
KEY_SUS_LOOP_PATHS to getSusLoopPaths(context),
|
||||||
KEY_SUS_MAPS to getSusMaps(context),
|
KEY_SUS_MAPS to getSusMaps(context),
|
||||||
KEY_SUS_MOUNTS to getSusMounts(context),
|
|
||||||
KEY_TRY_UMOUNTS to getTryUmounts(context),
|
|
||||||
KEY_ANDROID_DATA_PATH to getAndroidDataPath(context),
|
KEY_ANDROID_DATA_PATH to getAndroidDataPath(context),
|
||||||
KEY_SDCARD_PATH to getSdcardPath(context),
|
KEY_SDCARD_PATH to getSdcardPath(context),
|
||||||
KEY_ENABLE_LOG to getEnableLogState(context),
|
KEY_ENABLE_LOG to getEnableLogState(context),
|
||||||
@@ -860,18 +876,12 @@ object SuSFSManager {
|
|||||||
|
|
||||||
val featureMap = mapOf(
|
val featureMap = mapOf(
|
||||||
"CONFIG_KSU_SUSFS_SUS_PATH" to context.getString(R.string.sus_path_feature_label),
|
"CONFIG_KSU_SUSFS_SUS_PATH" to context.getString(R.string.sus_path_feature_label),
|
||||||
"CONFIG_KSU_SUSFS_SUS_MOUNT" to context.getString(R.string.sus_mount_feature_label),
|
|
||||||
"CONFIG_KSU_SUSFS_TRY_UMOUNT" to context.getString(R.string.try_umount_feature_label),
|
|
||||||
"CONFIG_KSU_SUSFS_SPOOF_UNAME" to context.getString(R.string.spoof_uname_feature_label),
|
"CONFIG_KSU_SUSFS_SPOOF_UNAME" to context.getString(R.string.spoof_uname_feature_label),
|
||||||
"CONFIG_KSU_SUSFS_SPOOF_CMDLINE_OR_BOOTCONFIG" to context.getString(R.string.spoof_cmdline_feature_label),
|
"CONFIG_KSU_SUSFS_SPOOF_CMDLINE_OR_BOOTCONFIG" to context.getString(R.string.spoof_cmdline_feature_label),
|
||||||
"CONFIG_KSU_SUSFS_OPEN_REDIRECT" to context.getString(R.string.open_redirect_feature_label),
|
"CONFIG_KSU_SUSFS_OPEN_REDIRECT" to context.getString(R.string.open_redirect_feature_label),
|
||||||
"CONFIG_KSU_SUSFS_ENABLE_LOG" to context.getString(R.string.enable_log_feature_label),
|
"CONFIG_KSU_SUSFS_ENABLE_LOG" to context.getString(R.string.enable_log_feature_label),
|
||||||
"CONFIG_KSU_SUSFS_AUTO_ADD_SUS_KSU_DEFAULT_MOUNT" to context.getString(R.string.auto_default_mount_feature_label),
|
|
||||||
"CONFIG_KSU_SUSFS_AUTO_ADD_SUS_BIND_MOUNT" to context.getString(R.string.auto_bind_mount_feature_label),
|
|
||||||
"CONFIG_KSU_SUSFS_AUTO_ADD_TRY_UMOUNT_FOR_BIND_MOUNT" to context.getString(R.string.auto_try_umount_bind_feature_label),
|
|
||||||
"CONFIG_KSU_SUSFS_HIDE_KSU_SUSFS_SYMBOLS" to context.getString(R.string.hide_symbols_feature_label),
|
"CONFIG_KSU_SUSFS_HIDE_KSU_SUSFS_SYMBOLS" to context.getString(R.string.hide_symbols_feature_label),
|
||||||
"CONFIG_KSU_SUSFS_SUS_KSTAT" to context.getString(R.string.sus_kstat_feature_label),
|
"CONFIG_KSU_SUSFS_SUS_KSTAT" to context.getString(R.string.sus_kstat_feature_label),
|
||||||
"CONFIG_KSU_SUSFS_SUS_SU" to context.getString(R.string.sus_su_feature_label)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@@ -893,18 +903,12 @@ object SuSFSManager {
|
|||||||
private fun getDefaultDisabledFeatures(context: Context): List<EnabledFeature> {
|
private fun getDefaultDisabledFeatures(context: Context): List<EnabledFeature> {
|
||||||
val defaultFeatures = listOf(
|
val defaultFeatures = listOf(
|
||||||
"sus_path_feature_label" to context.getString(R.string.sus_path_feature_label),
|
"sus_path_feature_label" to context.getString(R.string.sus_path_feature_label),
|
||||||
"sus_mount_feature_label" to context.getString(R.string.sus_mount_feature_label),
|
|
||||||
"try_umount_feature_label" to context.getString(R.string.try_umount_feature_label),
|
|
||||||
"spoof_uname_feature_label" to context.getString(R.string.spoof_uname_feature_label),
|
"spoof_uname_feature_label" to context.getString(R.string.spoof_uname_feature_label),
|
||||||
"spoof_cmdline_feature_label" to context.getString(R.string.spoof_cmdline_feature_label),
|
"spoof_cmdline_feature_label" to context.getString(R.string.spoof_cmdline_feature_label),
|
||||||
"open_redirect_feature_label" to context.getString(R.string.open_redirect_feature_label),
|
"open_redirect_feature_label" to context.getString(R.string.open_redirect_feature_label),
|
||||||
"enable_log_feature_label" to context.getString(R.string.enable_log_feature_label),
|
"enable_log_feature_label" to context.getString(R.string.enable_log_feature_label),
|
||||||
"auto_default_mount_feature_label" to context.getString(R.string.auto_default_mount_feature_label),
|
|
||||||
"auto_bind_mount_feature_label" to context.getString(R.string.auto_bind_mount_feature_label),
|
|
||||||
"auto_try_umount_bind_feature_label" to context.getString(R.string.auto_try_umount_bind_feature_label),
|
|
||||||
"hide_symbols_feature_label" to context.getString(R.string.hide_symbols_feature_label),
|
"hide_symbols_feature_label" to context.getString(R.string.hide_symbols_feature_label),
|
||||||
"sus_kstat_feature_label" to context.getString(R.string.sus_kstat_feature_label),
|
"sus_kstat_feature_label" to context.getString(R.string.sus_kstat_feature_label),
|
||||||
"sus_su_feature_label" to context.getString(R.string.sus_su_feature_label)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
return defaultFeatures.map { (_, displayName) ->
|
return defaultFeatures.map { (_, displayName) ->
|
||||||
@@ -1188,107 +1192,6 @@ object SuSFSManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 添加SUS挂载
|
|
||||||
suspend fun addSusMount(context: Context, mount: String): Boolean {
|
|
||||||
val success = executeSusfsCommand(context, "add_sus_mount '$mount'")
|
|
||||||
if (success) {
|
|
||||||
saveSusMounts(context, getSusMounts(context) + mount)
|
|
||||||
if (isAutoStartEnabled(context)) updateMagiskModule(context)
|
|
||||||
}
|
|
||||||
return success
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun removeSusMount(context: Context, mount: String): Boolean {
|
|
||||||
saveSusMounts(context, getSusMounts(context) - mount)
|
|
||||||
if (isAutoStartEnabled(context)) updateMagiskModule(context)
|
|
||||||
showToast(context, "Removed SUS mount: $mount")
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 编辑SUS挂载
|
|
||||||
suspend fun editSusMount(context: Context, oldMount: String, newMount: String): Boolean {
|
|
||||||
return try {
|
|
||||||
val currentMounts = getSusMounts(context).toMutableSet()
|
|
||||||
if (!currentMounts.remove(oldMount)) {
|
|
||||||
showToast(context, "Original mount not found: $oldMount")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
saveSusMounts(context, currentMounts)
|
|
||||||
|
|
||||||
val success = addSusMount(context, newMount)
|
|
||||||
|
|
||||||
if (success) {
|
|
||||||
showToast(context, "SUS mount updated: $oldMount -> $newMount")
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
// 如果添加新挂载点失败,恢复旧挂载点
|
|
||||||
currentMounts.add(oldMount)
|
|
||||||
saveSusMounts(context, currentMounts)
|
|
||||||
if (isAutoStartEnabled(context)) updateMagiskModule(context)
|
|
||||||
showToast(context, "Failed to update mount, reverted to original")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
showToast(context, "Error updating SUS mount: ${e.message}")
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加尝试卸载
|
|
||||||
suspend fun addTryUmount(context: Context, path: String, mode: Int): Boolean {
|
|
||||||
val commandSuccess = executeSusfsCommand(context, "add_try_umount '$path' $mode")
|
|
||||||
saveTryUmounts(context, getTryUmounts(context) + "$path|$mode")
|
|
||||||
if (isAutoStartEnabled(context)) updateMagiskModule(context)
|
|
||||||
|
|
||||||
showToast(context, if (commandSuccess) {
|
|
||||||
context.getString(R.string.susfs_try_umount_added_success, path)
|
|
||||||
} else {
|
|
||||||
context.getString(R.string.susfs_try_umount_added_saved, path)
|
|
||||||
})
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
suspend fun removeTryUmount(context: Context, umountEntry: String): Boolean {
|
|
||||||
saveTryUmounts(context, getTryUmounts(context) - umountEntry)
|
|
||||||
if (isAutoStartEnabled(context)) updateMagiskModule(context)
|
|
||||||
val path = umountEntry.split("|").firstOrNull() ?: umountEntry
|
|
||||||
showToast(context, "Removed Try to uninstall: $path")
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
// 编辑尝试卸载
|
|
||||||
suspend fun editTryUmount(context: Context, oldEntry: String, newPath: String, newMode: Int): Boolean {
|
|
||||||
return try {
|
|
||||||
val currentUmounts = getTryUmounts(context).toMutableSet()
|
|
||||||
if (!currentUmounts.remove(oldEntry)) {
|
|
||||||
showToast(context, "Original umount entry not found: $oldEntry")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
saveTryUmounts(context, currentUmounts)
|
|
||||||
|
|
||||||
val success = addTryUmount(context, newPath, newMode)
|
|
||||||
|
|
||||||
if (success) {
|
|
||||||
showToast(context, "Try umount updated: $oldEntry -> $newPath|$newMode")
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
// 如果添加新条目失败,恢复旧条目
|
|
||||||
currentUmounts.add(oldEntry)
|
|
||||||
saveTryUmounts(context, currentUmounts)
|
|
||||||
if (isAutoStartEnabled(context)) updateMagiskModule(context)
|
|
||||||
showToast(context, "Failed to update umount entry, reverted to original")
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
} catch (e: Exception) {
|
|
||||||
e.printStackTrace()
|
|
||||||
showToast(context, "Error updating try umount: ${e.message}")
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Zygote隔离服务卸载控制
|
// Zygote隔离服务卸载控制
|
||||||
suspend fun setUmountForZygoteIsoService(context: Context, enabled: Boolean): Boolean {
|
suspend fun setUmountForZygoteIsoService(context: Context, enabled: Boolean): Boolean {
|
||||||
if (!isSusVersion158()) {
|
if (!isSusVersion158()) {
|
||||||
|
|||||||
@@ -26,13 +26,13 @@ object ScriptGenerator {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 日志相关的通用脚本片段
|
// 日志相关的通用脚本片段
|
||||||
private fun generateLogSetup(logFileName: String): String = """
|
private fun generateLogSetup(logFileName: String): String = $$"""
|
||||||
# 日志目录
|
# 日志目录
|
||||||
LOG_DIR="$LOG_DIR"
|
LOG_DIR="$$LOG_DIR"
|
||||||
LOG_FILE="${'$'}LOG_DIR/$logFileName"
|
LOG_FILE="$LOG_DIR/$$logFileName"
|
||||||
|
|
||||||
# 创建日志目录
|
# 创建日志目录
|
||||||
mkdir -p "${'$'}LOG_DIR"
|
mkdir -p "$LOG_DIR"
|
||||||
|
|
||||||
# 获取当前时间
|
# 获取当前时间
|
||||||
get_current_time() {
|
get_current_time() {
|
||||||
@@ -41,11 +41,11 @@ object ScriptGenerator {
|
|||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
|
|
||||||
// 二进制文件检查的通用脚本片段
|
// 二进制文件检查的通用脚本片段
|
||||||
private fun generateBinaryCheck(targetPath: String): String = """
|
private fun generateBinaryCheck(targetPath: String): String = $$"""
|
||||||
# 检查SuSFS二进制文件
|
# 检查SuSFS二进制文件
|
||||||
SUSFS_BIN="$targetPath"
|
SUSFS_BIN="$$targetPath"
|
||||||
if [ ! -f "${'$'}SUSFS_BIN" ]; then
|
if [ ! -f "$SUSFS_BIN" ]; then
|
||||||
echo "$(get_current_time): SuSFS二进制文件未找到: ${'$'}SUSFS_BIN" >> "${'$'}LOG_FILE"
|
echo "$(get_current_time): SuSFS二进制文件未找到: $SUSFS_BIN" >> "$LOG_FILE"
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
@@ -94,7 +94,7 @@ object ScriptGenerator {
|
|||||||
generateCleanupResidueSection()
|
generateCleanupResidueSection()
|
||||||
}
|
}
|
||||||
|
|
||||||
appendLine("echo \"$(get_current_time): Service脚本执行完成\" >> \"${'$'}LOG_FILE\"")
|
appendLine($$"echo \"$(get_current_time): Service脚本执行完成\" >> \"$LOG_FILE\"")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -112,16 +112,16 @@ object ScriptGenerator {
|
|||||||
private fun StringBuilder.generateLogSettingSection(enableLog: Boolean) {
|
private fun StringBuilder.generateLogSettingSection(enableLog: Boolean) {
|
||||||
appendLine("# 设置日志启用状态")
|
appendLine("# 设置日志启用状态")
|
||||||
val logValue = if (enableLog) 1 else 0
|
val logValue = if (enableLog) 1 else 0
|
||||||
appendLine("\"${'$'}SUSFS_BIN\" enable_log $logValue")
|
appendLine($$"\"$SUSFS_BIN\" enable_log $$logValue")
|
||||||
appendLine("echo \"$(get_current_time): 日志功能设置为: ${if (enableLog) "启用" else "禁用"}\" >> \"${'$'}LOG_FILE\"")
|
appendLine($$"echo \"$(get_current_time): 日志功能设置为: $${if (enableLog) "启用" else "禁用"}\" >> \"$LOG_FILE\"")
|
||||||
appendLine()
|
appendLine()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun StringBuilder.generateAvcLogSpoofingSection(enableAvcLogSpoofing: Boolean) {
|
private fun StringBuilder.generateAvcLogSpoofingSection(enableAvcLogSpoofing: Boolean) {
|
||||||
appendLine("# 设置AVC日志欺骗状态")
|
appendLine("# 设置AVC日志欺骗状态")
|
||||||
val avcLogValue = if (enableAvcLogSpoofing) 1 else 0
|
val avcLogValue = if (enableAvcLogSpoofing) 1 else 0
|
||||||
appendLine("\"${'$'}SUSFS_BIN\" enable_avc_log_spoofing $avcLogValue")
|
appendLine($$"\"$SUSFS_BIN\" enable_avc_log_spoofing $$avcLogValue")
|
||||||
appendLine("echo \"$(get_current_time): AVC日志欺骗功能设置为: ${if (enableAvcLogSpoofing) "启用" else "禁用"}\" >> \"${'$'}LOG_FILE\"")
|
appendLine($$"echo \"$(get_current_time): AVC日志欺骗功能设置为: $${if (enableAvcLogSpoofing) "启用" else "禁用"}\" >> \"$LOG_FILE\"")
|
||||||
appendLine()
|
appendLine()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,8 +129,8 @@ object ScriptGenerator {
|
|||||||
if (susPaths.isNotEmpty()) {
|
if (susPaths.isNotEmpty()) {
|
||||||
appendLine("# 添加SUS路径")
|
appendLine("# 添加SUS路径")
|
||||||
susPaths.forEach { path ->
|
susPaths.forEach { path ->
|
||||||
appendLine("\"${'$'}SUSFS_BIN\" add_sus_path '$path'")
|
appendLine($$"\"$SUSFS_BIN\" add_sus_path '$$path'")
|
||||||
appendLine("echo \"$(get_current_time): 添加SUS路径: $path\" >> \"${'$'}LOG_FILE\"")
|
appendLine($$"echo \"$(get_current_time): 添加SUS路径: $$path\" >> \"$LOG_FILE\"")
|
||||||
}
|
}
|
||||||
appendLine()
|
appendLine()
|
||||||
}
|
}
|
||||||
@@ -140,8 +140,8 @@ object ScriptGenerator {
|
|||||||
if (susLoopPaths.isNotEmpty()) {
|
if (susLoopPaths.isNotEmpty()) {
|
||||||
appendLine("# 添加SUS循环路径")
|
appendLine("# 添加SUS循环路径")
|
||||||
susLoopPaths.forEach { path ->
|
susLoopPaths.forEach { path ->
|
||||||
appendLine("\"${'$'}SUSFS_BIN\" add_sus_path_loop '$path'")
|
appendLine($$"\"$SUSFS_BIN\" add_sus_path_loop '$$path'")
|
||||||
appendLine("echo \"$(get_current_time): 添加SUS循环路径: $path\" >> \"${'$'}LOG_FILE\"")
|
appendLine($$"echo \"$(get_current_time): 添加SUS循环路径: $$path\" >> \"$LOG_FILE\"")
|
||||||
}
|
}
|
||||||
appendLine()
|
appendLine()
|
||||||
}
|
}
|
||||||
@@ -156,8 +156,8 @@ object ScriptGenerator {
|
|||||||
if (addKstatPaths.isNotEmpty()) {
|
if (addKstatPaths.isNotEmpty()) {
|
||||||
appendLine("# 添加Kstat路径")
|
appendLine("# 添加Kstat路径")
|
||||||
addKstatPaths.forEach { path ->
|
addKstatPaths.forEach { path ->
|
||||||
appendLine("\"${'$'}SUSFS_BIN\" add_sus_kstat '$path'")
|
appendLine($$"\"$SUSFS_BIN\" add_sus_kstat '$$path'")
|
||||||
appendLine("echo \"$(get_current_time): 添加Kstat路径: $path\" >> \"${'$'}LOG_FILE\"")
|
appendLine($$"echo \"$(get_current_time): 添加Kstat路径: $$path\" >> \"$LOG_FILE\"")
|
||||||
}
|
}
|
||||||
appendLine()
|
appendLine()
|
||||||
}
|
}
|
||||||
@@ -171,11 +171,11 @@ object ScriptGenerator {
|
|||||||
val path = parts[0]
|
val path = parts[0]
|
||||||
val params = parts.drop(1).joinToString("' '", "'", "'")
|
val params = parts.drop(1).joinToString("' '", "'", "'")
|
||||||
appendLine()
|
appendLine()
|
||||||
appendLine("\"${'$'}SUSFS_BIN\" add_sus_kstat_statically '$path' $params")
|
appendLine($$"\"$SUSFS_BIN\" add_sus_kstat_statically '$$path' $$params")
|
||||||
appendLine("echo \"$(get_current_time): 添加Kstat静态配置: $path\" >> \"${'$'}LOG_FILE\"")
|
appendLine($$"echo \"$(get_current_time): 添加Kstat静态配置: $$path\" >> \"$LOG_FILE\"")
|
||||||
appendLine()
|
appendLine()
|
||||||
appendLine("\"${'$'}SUSFS_BIN\" update_sus_kstat '$path'")
|
appendLine($$"\"$SUSFS_BIN\" update_sus_kstat '$$path'")
|
||||||
appendLine("echo \"$(get_current_time): 更新Kstat配置: $path\" >> \"${'$'}LOG_FILE\"")
|
appendLine($$"echo \"$(get_current_time): 更新Kstat配置: $$path\" >> \"$LOG_FILE\"")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
appendLine()
|
appendLine()
|
||||||
@@ -185,8 +185,8 @@ object ScriptGenerator {
|
|||||||
private fun StringBuilder.generateUnameSection(config: SuSFSManager.ModuleConfig) {
|
private fun StringBuilder.generateUnameSection(config: SuSFSManager.ModuleConfig) {
|
||||||
if (!config.executeInPostFsData && (config.unameValue != DEFAULT_UNAME || config.buildTimeValue != DEFAULT_BUILD_TIME)) {
|
if (!config.executeInPostFsData && (config.unameValue != DEFAULT_UNAME || config.buildTimeValue != DEFAULT_BUILD_TIME)) {
|
||||||
appendLine("# 设置uname和构建时间")
|
appendLine("# 设置uname和构建时间")
|
||||||
appendLine("\"${'$'}SUSFS_BIN\" set_uname '${config.unameValue}' '${config.buildTimeValue}'")
|
appendLine($$"\"$SUSFS_BIN\" set_uname '$${config.unameValue}' '$${config.buildTimeValue}'")
|
||||||
appendLine("echo \"$(get_current_time): 设置uname为: ${config.unameValue}, 构建时间为: ${config.buildTimeValue}\" >> \"${'$'}LOG_FILE\"")
|
appendLine($$"echo \"$(get_current_time): 设置uname为: $${config.unameValue}, 构建时间为: $${config.buildTimeValue}\" >> \"$LOG_FILE\"")
|
||||||
appendLine()
|
appendLine()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -194,44 +194,44 @@ object ScriptGenerator {
|
|||||||
private fun StringBuilder.generateHideBlSection() {
|
private fun StringBuilder.generateHideBlSection() {
|
||||||
appendLine("# 隐藏BL 来自 Shamiko 脚本")
|
appendLine("# 隐藏BL 来自 Shamiko 脚本")
|
||||||
appendLine(
|
appendLine(
|
||||||
"""
|
$$"""
|
||||||
RESETPROP_BIN="/data/adb/ksu/bin/resetprop"
|
RESETPROP_BIN="/data/adb/ksu/bin/resetprop"
|
||||||
|
|
||||||
check_reset_prop() {
|
check_reset_prop() {
|
||||||
local NAME=$1
|
local NAME=$1
|
||||||
local EXPECTED=$2
|
local EXPECTED=$2
|
||||||
local VALUE=$("${'$'}RESETPROP_BIN" ${'$'}NAME)
|
local VALUE=$("$RESETPROP_BIN" $NAME)
|
||||||
[ -z ${'$'}VALUE ] || [ ${'$'}VALUE = ${'$'}EXPECTED ] || "${'$'}RESETPROP_BIN" ${'$'}NAME ${'$'}EXPECTED
|
[ -z $VALUE ] || [ $VALUE = $EXPECTED ] || "$RESETPROP_BIN" $NAME $EXPECTED
|
||||||
}
|
}
|
||||||
|
|
||||||
check_missing_prop() {
|
check_missing_prop() {
|
||||||
local NAME=$1
|
local NAME=$1
|
||||||
local EXPECTED=$2
|
local EXPECTED=$2
|
||||||
local VALUE=$("${'$'}RESETPROP_BIN" ${'$'}NAME)
|
local VALUE=$("$RESETPROP_BIN" $NAME)
|
||||||
[ -z ${'$'}VALUE ] && "${'$'}RESETPROP_BIN" ${'$'}NAME ${'$'}EXPECTED
|
[ -z $VALUE ] && "$RESETPROP_BIN" $NAME $EXPECTED
|
||||||
}
|
}
|
||||||
|
|
||||||
check_missing_match_prop() {
|
check_missing_match_prop() {
|
||||||
local NAME=$1
|
local NAME=$1
|
||||||
local EXPECTED=$2
|
local EXPECTED=$2
|
||||||
local VALUE=$("${'$'}RESETPROP_BIN" ${'$'}NAME)
|
local VALUE=$("$RESETPROP_BIN" $NAME)
|
||||||
[ -z ${'$'}VALUE ] || [ ${'$'}VALUE = ${'$'}EXPECTED ] || "${'$'}RESETPROP_BIN" ${'$'}NAME ${'$'}EXPECTED
|
[ -z $VALUE ] || [ $VALUE = $EXPECTED ] || "$RESETPROP_BIN" $NAME $EXPECTED
|
||||||
[ -z ${'$'}VALUE ] && "${'$'}RESETPROP_BIN" ${'$'}NAME ${'$'}EXPECTED
|
[ -z $VALUE ] && "$RESETPROP_BIN" $NAME $EXPECTED
|
||||||
}
|
}
|
||||||
|
|
||||||
contains_reset_prop() {
|
contains_reset_prop() {
|
||||||
local NAME=$1
|
local NAME=$1
|
||||||
local CONTAINS=$2
|
local CONTAINS=$2
|
||||||
local NEWVAL=$3
|
local NEWVAL=$3
|
||||||
case "$("${'$'}RESETPROP_BIN" ${'$'}NAME)" in
|
case "$("$RESETPROP_BIN" $NAME)" in
|
||||||
*"${'$'}CONTAINS"*) "${'$'}RESETPROP_BIN" ${'$'}NAME ${'$'}NEWVAL ;;
|
*"$CONTAINS"*) "$RESETPROP_BIN" $NAME $NEWVAL ;;
|
||||||
esac
|
esac
|
||||||
}
|
}
|
||||||
""".trimIndent())
|
""".trimIndent())
|
||||||
appendLine()
|
appendLine()
|
||||||
appendLine("sleep 30")
|
appendLine("sleep 30")
|
||||||
appendLine()
|
appendLine()
|
||||||
appendLine("\"${'$'}RESETPROP_BIN\" -w sys.boot_completed 0")
|
appendLine($$"\"$RESETPROP_BIN\" -w sys.boot_completed 0")
|
||||||
|
|
||||||
// 添加所有系统属性重置
|
// 添加所有系统属性重置
|
||||||
val systemProps = listOf(
|
val systemProps = listOf(
|
||||||
@@ -292,27 +292,28 @@ object ScriptGenerator {
|
|||||||
// 清理残留脚本生成
|
// 清理残留脚本生成
|
||||||
private fun StringBuilder.generateCleanupResidueSection() {
|
private fun StringBuilder.generateCleanupResidueSection() {
|
||||||
appendLine("# 清理工具残留文件")
|
appendLine("# 清理工具残留文件")
|
||||||
appendLine("echo \"$(get_current_time): 开始清理工具残留\" >> \"${'$'}LOG_FILE\"")
|
appendLine($$"echo \"$(get_current_time): 开始清理工具残留\" >> \"$LOG_FILE\"")
|
||||||
appendLine()
|
appendLine()
|
||||||
|
|
||||||
// 定义清理函数
|
// 定义清理函数
|
||||||
appendLine("""
|
appendLine(
|
||||||
|
$$"""
|
||||||
cleanup_path() {
|
cleanup_path() {
|
||||||
local path="$1"
|
local path="$1"
|
||||||
local desc="$2"
|
local desc="$2"
|
||||||
local current="$3"
|
local current="$3"
|
||||||
local total="$4"
|
local total="$4"
|
||||||
|
|
||||||
if [ -n "${'$'}desc" ]; then
|
if [ -n "$desc" ]; then
|
||||||
echo "$(get_current_time): [${'$'}current/${'$'}total] 清理: ${'$'}path (${'$'}desc)" >> "${'$'}LOG_FILE"
|
echo "$(get_current_time): [$current/$total] 清理: $path ($desc)" >> "$LOG_FILE"
|
||||||
else
|
else
|
||||||
echo "$(get_current_time): [${'$'}current/${'$'}total] 清理: ${'$'}path" >> "${'$'}LOG_FILE"
|
echo "$(get_current_time): [$current/$total] 清理: $path" >> "$LOG_FILE"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if rm -rf "${'$'}path" 2>/dev/null; then
|
if rm -rf "$path" 2>/dev/null; then
|
||||||
echo "$(get_current_time): ✓ 成功清理: ${'$'}path" >> "${'$'}LOG_FILE"
|
echo "$(get_current_time): ✓ 成功清理: $path" >> "$LOG_FILE"
|
||||||
else
|
else
|
||||||
echo "$(get_current_time): ✗ 清理失败或不存在: ${'$'}path" >> "${'$'}LOG_FILE"
|
echo "$(get_current_time): ✗ 清理失败或不存在: $path" >> "$LOG_FILE"
|
||||||
fi
|
fi
|
||||||
}
|
}
|
||||||
""".trimIndent())
|
""".trimIndent())
|
||||||
@@ -360,11 +361,11 @@ object ScriptGenerator {
|
|||||||
|
|
||||||
cleanupPaths.forEachIndexed { index, (path, desc) ->
|
cleanupPaths.forEachIndexed { index, (path, desc) ->
|
||||||
val current = index + 1
|
val current = index + 1
|
||||||
appendLine("cleanup_path '$path' '$desc' $current \$TOTAL")
|
appendLine($$"cleanup_path '$$path' '$$desc' $$current $TOTAL")
|
||||||
}
|
}
|
||||||
|
|
||||||
appendLine()
|
appendLine()
|
||||||
appendLine("echo \"$(get_current_time): 工具残留清理完成\" >> \"${'$'}LOG_FILE\"")
|
appendLine($$"echo \"$(get_current_time): 工具残留清理完成\" >> \"$LOG_FILE\"")
|
||||||
appendLine()
|
appendLine()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -381,14 +382,14 @@ object ScriptGenerator {
|
|||||||
appendLine()
|
appendLine()
|
||||||
appendLine(generateBinaryCheck(config.targetPath))
|
appendLine(generateBinaryCheck(config.targetPath))
|
||||||
appendLine()
|
appendLine()
|
||||||
appendLine("echo \"$(get_current_time): Post-FS-Data脚本开始执行\" >> \"${'$'}LOG_FILE\"")
|
appendLine($$"echo \"$(get_current_time): Post-FS-Data脚本开始执行\" >> \"$LOG_FILE\"")
|
||||||
appendLine()
|
appendLine()
|
||||||
|
|
||||||
// 设置uname和构建时间 - 只有在选择在post-fs-data中执行时才执行
|
// 设置uname和构建时间 - 只有在选择在post-fs-data中执行时才执行
|
||||||
if (config.executeInPostFsData && (config.unameValue != DEFAULT_UNAME || config.buildTimeValue != DEFAULT_BUILD_TIME)) {
|
if (config.executeInPostFsData && (config.unameValue != DEFAULT_UNAME || config.buildTimeValue != DEFAULT_BUILD_TIME)) {
|
||||||
appendLine("# 设置uname和构建时间")
|
appendLine("# 设置uname和构建时间")
|
||||||
appendLine("\"${'$'}SUSFS_BIN\" set_uname '${config.unameValue}' '${config.buildTimeValue}'")
|
appendLine($$"\"$SUSFS_BIN\" set_uname '$${config.unameValue}' '$${config.buildTimeValue}'")
|
||||||
appendLine("echo \"$(get_current_time): 设置uname为: ${config.unameValue}, 构建时间为: ${config.buildTimeValue}\" >> \"${'$'}LOG_FILE\"")
|
appendLine($$"echo \"$(get_current_time): 设置uname为: $${config.unameValue}, 构建时间为: $${config.buildTimeValue}\" >> \"$LOG_FILE\"")
|
||||||
appendLine()
|
appendLine()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -397,7 +398,7 @@ object ScriptGenerator {
|
|||||||
// 添加AVC日志欺骗设置
|
// 添加AVC日志欺骗设置
|
||||||
generateAvcLogSpoofingSection(config.enableAvcLogSpoofing)
|
generateAvcLogSpoofingSection(config.enableAvcLogSpoofing)
|
||||||
|
|
||||||
appendLine("echo \"$(get_current_time): Post-FS-Data脚本执行完成\" >> \"${'$'}LOG_FILE\"")
|
appendLine($$"echo \"$(get_current_time): Post-FS-Data脚本执行完成\" >> \"$LOG_FILE\"")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -406,8 +407,8 @@ object ScriptGenerator {
|
|||||||
if (support158) {
|
if (support158) {
|
||||||
appendLine("# 设置Zygote隔离服务卸载状态")
|
appendLine("# 设置Zygote隔离服务卸载状态")
|
||||||
val umountValue = if (umountForZygoteIsoService) 1 else 0
|
val umountValue = if (umountForZygoteIsoService) 1 else 0
|
||||||
appendLine("\"${'$'}SUSFS_BIN\" umount_for_zygote_iso_service $umountValue")
|
appendLine($$"\"$SUSFS_BIN\" umount_for_zygote_iso_service $$umountValue")
|
||||||
appendLine("echo \"$(get_current_time): Zygote隔离服务卸载设置为: ${if (umountForZygoteIsoService) "启用" else "禁用"}\" >> \"${'$'}LOG_FILE\"")
|
appendLine($$"echo \"$(get_current_time): Zygote隔离服务卸载设置为: $${if (umountForZygoteIsoService) "启用" else "禁用"}\" >> \"$LOG_FILE\"")
|
||||||
appendLine()
|
appendLine()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -423,37 +424,12 @@ object ScriptGenerator {
|
|||||||
appendLine()
|
appendLine()
|
||||||
appendLine(generateLogSetup("susfs_post_mount.log"))
|
appendLine(generateLogSetup("susfs_post_mount.log"))
|
||||||
appendLine()
|
appendLine()
|
||||||
appendLine("echo \"$(get_current_time): Post-Mount脚本开始执行\" >> \"${'$'}LOG_FILE\"")
|
appendLine($$"echo \"$(get_current_time): Post-Mount脚本开始执行\" >> \"$LOG_FILE\"")
|
||||||
appendLine()
|
appendLine()
|
||||||
appendLine(generateBinaryCheck(config.targetPath))
|
appendLine(generateBinaryCheck(config.targetPath))
|
||||||
appendLine()
|
appendLine()
|
||||||
|
|
||||||
// 添加SUS挂载
|
appendLine($$"echo \"$(get_current_time): Post-Mount脚本执行完成\" >> \"$LOG_FILE\"")
|
||||||
if (config.susMounts.isNotEmpty()) {
|
|
||||||
appendLine("# 添加SUS挂载")
|
|
||||||
config.susMounts.forEach { mount ->
|
|
||||||
appendLine("\"${'$'}SUSFS_BIN\" add_sus_mount '$mount'")
|
|
||||||
appendLine("echo \"$(get_current_time): 添加SUS挂载: $mount\" >> \"${'$'}LOG_FILE\"")
|
|
||||||
}
|
|
||||||
appendLine()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加尝试卸载
|
|
||||||
if (config.tryUmounts.isNotEmpty()) {
|
|
||||||
appendLine("# 添加尝试卸载")
|
|
||||||
config.tryUmounts.forEach { umount ->
|
|
||||||
val parts = umount.split("|")
|
|
||||||
if (parts.size == 2) {
|
|
||||||
val path = parts[0]
|
|
||||||
val mode = parts[1]
|
|
||||||
appendLine("\"${'$'}SUSFS_BIN\" add_try_umount '$path' $mode")
|
|
||||||
appendLine("echo \"$(get_current_time): 添加尝试卸载: $path (模式: $mode)\" >> \"${'$'}LOG_FILE\"")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
appendLine()
|
|
||||||
}
|
|
||||||
|
|
||||||
appendLine("echo \"$(get_current_time): Post-Mount脚本执行完成\" >> \"${'$'}LOG_FILE\"")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -469,7 +445,7 @@ object ScriptGenerator {
|
|||||||
appendLine()
|
appendLine()
|
||||||
appendLine(generateLogSetup("susfs_boot_completed.log"))
|
appendLine(generateLogSetup("susfs_boot_completed.log"))
|
||||||
appendLine()
|
appendLine()
|
||||||
appendLine("echo \"$(get_current_time): Boot-Completed脚本开始执行\" >> \"${'$'}LOG_FILE\"")
|
appendLine($$"echo \"$(get_current_time): Boot-Completed脚本开始执行\" >> \"$LOG_FILE\"")
|
||||||
appendLine()
|
appendLine()
|
||||||
appendLine(generateBinaryCheck(config.targetPath))
|
appendLine(generateBinaryCheck(config.targetPath))
|
||||||
appendLine()
|
appendLine()
|
||||||
@@ -479,8 +455,8 @@ object ScriptGenerator {
|
|||||||
// SUS挂载隐藏控制
|
// SUS挂载隐藏控制
|
||||||
val hideValue = if (config.hideSusMountsForAllProcs) 1 else 0
|
val hideValue = if (config.hideSusMountsForAllProcs) 1 else 0
|
||||||
appendLine("# 设置SUS挂载隐藏控制")
|
appendLine("# 设置SUS挂载隐藏控制")
|
||||||
appendLine("\"${'$'}SUSFS_BIN\" hide_sus_mnts_for_all_procs $hideValue")
|
appendLine($$"\"$SUSFS_BIN\" hide_sus_mnts_for_all_procs $$hideValue")
|
||||||
appendLine("echo \"$(get_current_time): SUS挂载隐藏控制设置为: ${if (config.hideSusMountsForAllProcs) "对所有进程隐藏" else "仅对非KSU进程隐藏"}\" >> \"${'$'}LOG_FILE\"")
|
appendLine($$"echo \"$(get_current_time): SUS挂载隐藏控制设置为: $${if (config.hideSusMountsForAllProcs) "对所有进程隐藏" else "仅对非KSU进程隐藏"}\" >> \"$LOG_FILE\"")
|
||||||
appendLine()
|
appendLine()
|
||||||
|
|
||||||
// 路径设置和SUS路径设置
|
// 路径设置和SUS路径设置
|
||||||
@@ -504,7 +480,7 @@ object ScriptGenerator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
appendLine("echo \"$(get_current_time): Boot-Completed脚本执行完成\" >> \"${'$'}LOG_FILE\"")
|
appendLine($$"echo \"$(get_current_time): Boot-Completed脚本执行完成\" >> \"$LOG_FILE\"")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -512,8 +488,8 @@ object ScriptGenerator {
|
|||||||
if (susMaps.isNotEmpty()) {
|
if (susMaps.isNotEmpty()) {
|
||||||
appendLine("# 添加SUS映射")
|
appendLine("# 添加SUS映射")
|
||||||
susMaps.forEach { map ->
|
susMaps.forEach { map ->
|
||||||
appendLine("\"${'$'}SUSFS_BIN\" add_sus_map '$map'")
|
appendLine($$"\"$SUSFS_BIN\" add_sus_map '$$map'")
|
||||||
appendLine("echo \"$(get_current_time): 添加SUS映射: $map\" >> \"${'$'}LOG_FILE\"")
|
appendLine($$"echo \"$(get_current_time): 添加SUS映射: $$map\" >> \"$LOG_FILE\"")
|
||||||
}
|
}
|
||||||
appendLine()
|
appendLine()
|
||||||
}
|
}
|
||||||
@@ -526,12 +502,12 @@ object ScriptGenerator {
|
|||||||
appendLine("until [ -d \"/sdcard/Android\" ]; do sleep 1; done")
|
appendLine("until [ -d \"/sdcard/Android\" ]; do sleep 1; done")
|
||||||
appendLine("sleep 60")
|
appendLine("sleep 60")
|
||||||
appendLine()
|
appendLine()
|
||||||
appendLine("\"${'$'}SUSFS_BIN\" set_android_data_root_path '$androidDataPath'")
|
appendLine($$"\"$SUSFS_BIN\" set_android_data_root_path '$$androidDataPath'")
|
||||||
appendLine("echo \"$(get_current_time): Android Data路径设置为: $androidDataPath\" >> \"${'$'}LOG_FILE\"")
|
appendLine($$"echo \"$(get_current_time): Android Data路径设置为: $$androidDataPath\" >> \"$LOG_FILE\"")
|
||||||
appendLine()
|
appendLine()
|
||||||
appendLine("# 设置SD卡路径")
|
appendLine("# 设置SD卡路径")
|
||||||
appendLine("\"${'$'}SUSFS_BIN\" set_sdcard_root_path '$sdcardPath'")
|
appendLine($$"\"$SUSFS_BIN\" set_sdcard_root_path '$$sdcardPath'")
|
||||||
appendLine("echo \"$(get_current_time): SD卡路径设置为: $sdcardPath\" >> \"${'$'}LOG_FILE\"")
|
appendLine($$"echo \"$(get_current_time): SD卡路径设置为: $$sdcardPath\" >> \"$LOG_FILE\"")
|
||||||
appendLine()
|
appendLine()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,8 +19,10 @@ import kotlinx.parcelize.Parcelize
|
|||||||
import com.sukisu.ultra.BuildConfig
|
import com.sukisu.ultra.BuildConfig
|
||||||
import com.sukisu.ultra.Natives
|
import com.sukisu.ultra.Natives
|
||||||
import com.sukisu.ultra.ksuApp
|
import com.sukisu.ultra.ksuApp
|
||||||
|
import com.topjohnwu.superuser.io.SuFile
|
||||||
import org.json.JSONArray
|
import org.json.JSONArray
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
import java.util.Properties
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -97,6 +99,13 @@ fun execKsud(args: String, newShell: Boolean = false): Boolean {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
suspend fun getFeatureStatus(feature: String): String = withContext(Dispatchers.IO) {
|
||||||
|
val shell = getRootShell()
|
||||||
|
val out = shell.newJob()
|
||||||
|
.add("${getKsuDaemonPath()} feature check $feature").to(ArrayList<String>(), null).exec().out
|
||||||
|
out.firstOrNull()?.trim().orEmpty()
|
||||||
|
}
|
||||||
|
|
||||||
fun install() {
|
fun install() {
|
||||||
val start = SystemClock.elapsedRealtime()
|
val start = SystemClock.elapsedRealtime()
|
||||||
val magiskboot = File(ksuApp.applicationInfo.nativeLibraryDir, "libmagiskboot.so").absolutePath
|
val magiskboot = File(ksuApp.applicationInfo.nativeLibraryDir, "libmagiskboot.so").absolutePath
|
||||||
@@ -104,11 +113,15 @@ fun install() {
|
|||||||
Log.w(TAG, "install result: $result, cost: ${SystemClock.elapsedRealtime() - start}ms")
|
Log.w(TAG, "install result: $result, cost: ${SystemClock.elapsedRealtime() - start}ms")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun hasMetaModule(): Boolean {
|
||||||
|
return getMetaModuleImplement() != "None"
|
||||||
|
}
|
||||||
|
|
||||||
fun listModules(): String {
|
fun listModules(): String {
|
||||||
val shell = getRootShell()
|
val shell = getRootShell()
|
||||||
|
|
||||||
val out =
|
val out = shell.newJob()
|
||||||
shell.newJob().add("${getKsuDaemonPath()} module list").to(ArrayList(), null).exec().out
|
.add("${getKsuDaemonPath()} module list").to(ArrayList(), null).exec().out
|
||||||
return out.joinToString("\n").ifBlank { "[]" }
|
return out.joinToString("\n").ifBlank { "[]" }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -149,6 +162,13 @@ fun restoreModule(id: String): Boolean {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun undoUninstallModule(id: String): Boolean {
|
||||||
|
val cmd = "module undo-uninstall $id"
|
||||||
|
val result = execKsud(cmd, true)
|
||||||
|
Log.i(TAG, "undo uninstall module $id result: $result")
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
private fun flashWithIO(
|
private fun flashWithIO(
|
||||||
cmd: String,
|
cmd: String,
|
||||||
onStdout: (String) -> Unit,
|
onStdout: (String) -> Unit,
|
||||||
@@ -561,9 +581,27 @@ fun getSuSFSFeatures(): String {
|
|||||||
return runCmd(shell, cmd)
|
return runCmd(shell, cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getZygiskImplement(): String {
|
fun getMetaModuleImplement(): String {
|
||||||
val shell = getRootShell()
|
try {
|
||||||
|
val metaModuleProp = SuFile.open("/data/adb/metamodule/module.prop")
|
||||||
|
if (!metaModuleProp.isFile) {
|
||||||
|
Log.i(TAG, "Meta module implement: None")
|
||||||
|
return "None"
|
||||||
|
}
|
||||||
|
|
||||||
|
val prop = Properties()
|
||||||
|
prop.load(metaModuleProp.newInputStream())
|
||||||
|
|
||||||
|
val name = prop.getProperty("name")
|
||||||
|
Log.i(TAG, "Meta module implement: $name")
|
||||||
|
return name
|
||||||
|
} catch (t : Throwable) {
|
||||||
|
Log.i(TAG, "Meta module implement: None")
|
||||||
|
return "None"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getZygiskImplement(): String {
|
||||||
val zygiskModuleIds = listOf(
|
val zygiskModuleIds = listOf(
|
||||||
"zygisksu",
|
"zygisksu",
|
||||||
"rezygisk",
|
"rezygisk",
|
||||||
@@ -571,14 +609,19 @@ fun getZygiskImplement(): String {
|
|||||||
)
|
)
|
||||||
|
|
||||||
for (moduleId in zygiskModuleIds) {
|
for (moduleId in zygiskModuleIds) {
|
||||||
val modulePath = "/data/adb/modules/$moduleId"
|
// 忽略禁用/即将删除
|
||||||
when {
|
if (SuFile.open("/data/adb/modules/$moduleId/disable").isFile || SuFile.open("/data/adb/modules/$moduleId/remove").isFile) continue
|
||||||
ShellUtils.fastCmdResult(shell, "test -f $modulePath/module.prop && test ! -f $modulePath/disable") -> {
|
|
||||||
val result = ShellUtils.fastCmd(shell, "grep '^name=' $modulePath/module.prop | cut -d'=' -f2")
|
// 读取prop
|
||||||
Log.i(TAG, "Zygisk implement: $result")
|
val propFile = SuFile.open("/data/adb/modules/$moduleId/module.prop")
|
||||||
return result
|
if (!propFile.isFile) continue
|
||||||
}
|
|
||||||
}
|
val prop = Properties()
|
||||||
|
prop.load(propFile.newInputStream())
|
||||||
|
|
||||||
|
val name = prop.getProperty("name")
|
||||||
|
Log.i(TAG, "Zygisk implement: $name")
|
||||||
|
return name
|
||||||
}
|
}
|
||||||
|
|
||||||
Log.i(TAG, "Zygisk implement: None")
|
Log.i(TAG, "Zygisk implement: None")
|
||||||
|
|||||||
@@ -54,7 +54,8 @@ class HomeViewModel : ViewModel() {
|
|||||||
val kpmModuleCount: Int = 0,
|
val kpmModuleCount: Int = 0,
|
||||||
val managersList: Natives.ManagersList? = null,
|
val managersList: Natives.ManagersList? = null,
|
||||||
val isDynamicSignEnabled: Boolean = false,
|
val isDynamicSignEnabled: Boolean = false,
|
||||||
val zygiskImplement: String = ""
|
val zygiskImplement: String = "",
|
||||||
|
val metaModuleImplement: String = ""
|
||||||
)
|
)
|
||||||
|
|
||||||
// 状态变量
|
// 状态变量
|
||||||
@@ -79,6 +80,8 @@ class HomeViewModel : ViewModel() {
|
|||||||
private set
|
private set
|
||||||
var isHideZygiskImplement by mutableStateOf(false)
|
var isHideZygiskImplement by mutableStateOf(false)
|
||||||
private set
|
private set
|
||||||
|
var isHideMetaModuleImplement by mutableStateOf(false)
|
||||||
|
private set
|
||||||
var isHideLinkCard by mutableStateOf(false)
|
var isHideLinkCard by mutableStateOf(false)
|
||||||
private set
|
private set
|
||||||
var showKpmInfo by mutableStateOf(false)
|
var showKpmInfo by mutableStateOf(false)
|
||||||
@@ -109,6 +112,7 @@ class HomeViewModel : ViewModel() {
|
|||||||
isHideSusfsStatus = settingsPrefs.getBoolean("is_hide_susfs_status", false)
|
isHideSusfsStatus = settingsPrefs.getBoolean("is_hide_susfs_status", false)
|
||||||
isHideLinkCard = settingsPrefs.getBoolean("is_hide_link_card", false)
|
isHideLinkCard = settingsPrefs.getBoolean("is_hide_link_card", false)
|
||||||
isHideZygiskImplement = settingsPrefs.getBoolean("is_hide_zygisk_Implement", false)
|
isHideZygiskImplement = settingsPrefs.getBoolean("is_hide_zygisk_Implement", false)
|
||||||
|
isHideMetaModuleImplement = settingsPrefs.getBoolean("is_hide_meta_module_Implement", false)
|
||||||
showKpmInfo = settingsPrefs.getBoolean("show_kpm_info", false)
|
showKpmInfo = settingsPrefs.getBoolean("show_kpm_info", false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -222,7 +226,8 @@ class HomeViewModel : ViewModel() {
|
|||||||
superuserCount = moduleInfo.second,
|
superuserCount = moduleInfo.second,
|
||||||
moduleCount = moduleInfo.third,
|
moduleCount = moduleInfo.third,
|
||||||
kpmModuleCount = moduleInfo.fourth,
|
kpmModuleCount = moduleInfo.fourth,
|
||||||
zygiskImplement = moduleInfo.fifth
|
zygiskImplement = moduleInfo.fifth,
|
||||||
|
metaModuleImplement = moduleInfo.sixth
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -398,7 +403,7 @@ class HomeViewModel : ViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private suspend fun loadModuleInfo(): Tuple5<String, Int, Int, Int, String> {
|
private suspend fun loadModuleInfo(): Tuple6<String, Int, Int, Int, String, String> {
|
||||||
return withContext(Dispatchers.IO) {
|
return withContext(Dispatchers.IO) {
|
||||||
val kpmVersion = try {
|
val kpmVersion = try {
|
||||||
getKpmVersion()
|
getKpmVersion()
|
||||||
@@ -430,7 +435,13 @@ class HomeViewModel : ViewModel() {
|
|||||||
"None"
|
"None"
|
||||||
}
|
}
|
||||||
|
|
||||||
Tuple5(kpmVersion, superuserCount, moduleCount, kpmModuleCount, zygiskImplement)
|
val metaModuleImplement = try {
|
||||||
|
getMetaModuleImplement()
|
||||||
|
} catch (_: Exception) {
|
||||||
|
"None"
|
||||||
|
}
|
||||||
|
|
||||||
|
Tuple6(kpmVersion, superuserCount, moduleCount, kpmModuleCount, zygiskImplement, metaModuleImplement)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -567,6 +578,15 @@ class HomeViewModel : ViewModel() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
data class Tuple6<T1, T2, T3, T4, T5, T6>(
|
||||||
|
val first: T1,
|
||||||
|
val second: T2,
|
||||||
|
val third: T3,
|
||||||
|
val fourth: T4,
|
||||||
|
val fifth: T5,
|
||||||
|
val sixth: T6
|
||||||
|
)
|
||||||
|
|
||||||
data class Tuple5<T1, T2, T3, T4, T5>(
|
data class Tuple5<T1, T2, T3, T4, T5>(
|
||||||
val first: T1,
|
val first: T1,
|
||||||
val second: T2,
|
val second: T2,
|
||||||
|
|||||||
@@ -85,6 +85,7 @@ class ModuleViewModel : ViewModel() {
|
|||||||
val updateJson: String,
|
val updateJson: String,
|
||||||
val hasWebUi: Boolean,
|
val hasWebUi: Boolean,
|
||||||
val hasActionScript: Boolean,
|
val hasActionScript: Boolean,
|
||||||
|
val metamodule: Boolean,
|
||||||
val dirId: String, // real module id (dir name)
|
val dirId: String, // real module id (dir name)
|
||||||
var config: ModuleConfig? = null,
|
var config: ModuleConfig? = null,
|
||||||
var isVerified: Boolean = false, // 添加验证状态字段
|
var isVerified: Boolean = false, // 添加验证状态字段
|
||||||
@@ -143,15 +144,16 @@ class ModuleViewModel : ViewModel() {
|
|||||||
obj.optString("name"),
|
obj.optString("name"),
|
||||||
obj.optString("author", "Unknown"),
|
obj.optString("author", "Unknown"),
|
||||||
obj.optString("version", "Unknown"),
|
obj.optString("version", "Unknown"),
|
||||||
obj.optInt("versionCode", 0),
|
obj.getIntCompat("versionCode", 0),
|
||||||
obj.optString("description"),
|
obj.optString("description"),
|
||||||
obj.getBoolean("enabled"),
|
obj.getBooleanCompat("enabled"),
|
||||||
obj.getBoolean("update"),
|
obj.getBooleanCompat("update"),
|
||||||
obj.getBoolean("remove"),
|
obj.getBooleanCompat("remove"),
|
||||||
obj.optString("updateJson"),
|
obj.optString("updateJson"),
|
||||||
obj.optBoolean("web"),
|
obj.getBooleanCompat("web"),
|
||||||
obj.optBoolean("action"),
|
obj.getBooleanCompat("action"),
|
||||||
obj.getString("dir_id")
|
obj.getBooleanCompat("metamodule"),
|
||||||
|
obj.optString("dir_id", obj.getString("id"))
|
||||||
)
|
)
|
||||||
}.toList()
|
}.toList()
|
||||||
|
|
||||||
@@ -305,6 +307,7 @@ fun ModuleViewModel.ModuleInfo.copy(
|
|||||||
updateJson: String = this.updateJson,
|
updateJson: String = this.updateJson,
|
||||||
hasWebUi: Boolean = this.hasWebUi,
|
hasWebUi: Boolean = this.hasWebUi,
|
||||||
hasActionScript: Boolean = this.hasActionScript,
|
hasActionScript: Boolean = this.hasActionScript,
|
||||||
|
metamodule: Boolean = this.metamodule,
|
||||||
dirId: String = this.dirId,
|
dirId: String = this.dirId,
|
||||||
config: ModuleConfig? = this.config,
|
config: ModuleConfig? = this.config,
|
||||||
isVerified: Boolean = this.isVerified,
|
isVerified: Boolean = this.isVerified,
|
||||||
@@ -312,7 +315,7 @@ fun ModuleViewModel.ModuleInfo.copy(
|
|||||||
): ModuleViewModel.ModuleInfo {
|
): ModuleViewModel.ModuleInfo {
|
||||||
return ModuleViewModel.ModuleInfo(
|
return ModuleViewModel.ModuleInfo(
|
||||||
id, name, author, version, versionCode, description,
|
id, name, author, version, versionCode, description,
|
||||||
enabled, update, remove, updateJson, hasWebUi, hasActionScript,
|
enabled, update, remove, updateJson, hasWebUi, hasActionScript, metamodule,
|
||||||
dirId, config, isVerified, verificationTimestamp
|
dirId, config, isVerified, verificationTimestamp
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -469,6 +472,26 @@ class ModuleSizeCache(context: Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun JSONObject.getBooleanCompat(key: String, default: Boolean = false): Boolean {
|
||||||
|
if (!has(key)) return default
|
||||||
|
return when (val value = opt(key)) {
|
||||||
|
is Boolean -> value
|
||||||
|
is String -> value.equals("true", ignoreCase = true) || value == "1"
|
||||||
|
is Number -> value.toInt() != 0
|
||||||
|
else -> default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun JSONObject.getIntCompat(key: String, default: Int = 0): Int {
|
||||||
|
if (!has(key)) return default
|
||||||
|
return when (val value = opt(key)) {
|
||||||
|
is Int -> value
|
||||||
|
is Number -> value.toInt()
|
||||||
|
is String -> value.toIntOrNull() ?: default
|
||||||
|
else -> default
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 格式化文件大小的工具函数
|
* 格式化文件大小的工具函数
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -312,6 +312,16 @@ private fun HideOptionsSettings(
|
|||||||
onChange = handlers::handleHideZygiskImplementChange
|
onChange = handlers::handleHideZygiskImplementChange
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 元模块实现状态信息
|
||||||
|
SwitchSettingItem(
|
||||||
|
icon = Icons.Filled.VisibilityOff,
|
||||||
|
title = stringResource(R.string.hide_meta_module_implement),
|
||||||
|
summary = stringResource(R.string.hide_meta_module_implement_summary),
|
||||||
|
checked = state.isHideMetaModuleImplement,
|
||||||
|
onChange = handlers::handleHideMetaModuleImplementChange
|
||||||
|
)
|
||||||
|
|
||||||
|
// KPM 状态信息隐藏
|
||||||
if (Natives.version >= Natives.MINIMAL_SUPPORTED_KPM) {
|
if (Natives.version >= Natives.MINIMAL_SUPPORTED_KPM) {
|
||||||
SwitchSettingItem(
|
SwitchSettingItem(
|
||||||
icon = Icons.Filled.VisibilityOff,
|
icon = Icons.Filled.VisibilityOff,
|
||||||
|
|||||||
@@ -341,6 +341,14 @@ class MoreSettingsHandlers(
|
|||||||
state.isHideZygiskImplement = newValue
|
state.isHideZygiskImplement = newValue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理隐藏元模块实现变更
|
||||||
|
*/
|
||||||
|
fun handleHideMetaModuleImplementChange(newValue: Boolean) {
|
||||||
|
prefs.edit { putBoolean("is_hide_meta_module_Implement", newValue) }
|
||||||
|
state.isHideMetaModuleImplement = newValue
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理隐藏链接卡片变更
|
* 处理隐藏链接卡片变更
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ class MoreSettingsState(
|
|||||||
var isHideOtherInfo by mutableStateOf(prefs.getBoolean("is_hide_other_info", false))
|
var isHideOtherInfo by mutableStateOf(prefs.getBoolean("is_hide_other_info", false))
|
||||||
var isShowKpmInfo by mutableStateOf(prefs.getBoolean("show_kpm_info", false))
|
var isShowKpmInfo by mutableStateOf(prefs.getBoolean("show_kpm_info", false))
|
||||||
var isHideZygiskImplement by mutableStateOf(prefs.getBoolean("is_hide_zygisk_Implement", false))
|
var isHideZygiskImplement by mutableStateOf(prefs.getBoolean("is_hide_zygisk_Implement", false))
|
||||||
|
var isHideMetaModuleImplement by mutableStateOf(prefs.getBoolean("is_hide_meta_module_Implement", false))
|
||||||
var isHideSusfsStatus by mutableStateOf(prefs.getBoolean("is_hide_susfs_status", false))
|
var isHideSusfsStatus by mutableStateOf(prefs.getBoolean("is_hide_susfs_status", false))
|
||||||
var isHideLinkCard by mutableStateOf(prefs.getBoolean("is_hide_link_card", false))
|
var isHideLinkCard by mutableStateOf(prefs.getBoolean("is_hide_link_card", false))
|
||||||
var isHideTagRow by mutableStateOf(prefs.getBoolean("is_hide_tag_row", false))
|
var isHideTagRow by mutableStateOf(prefs.getBoolean("is_hide_tag_row", false))
|
||||||
|
|||||||
@@ -382,39 +382,20 @@ Tanamkan: Secara permanen memasang ke sistem</string>
|
|||||||
<!-- SuSFS Tab Titles -->
|
<!-- SuSFS Tab Titles -->
|
||||||
<string name="susfs_tab_basic_settings">Pengaturan Dasar</string>
|
<string name="susfs_tab_basic_settings">Pengaturan Dasar</string>
|
||||||
<string name="susfs_tab_sus_paths">Jalur SUS</string>
|
<string name="susfs_tab_sus_paths">Jalur SUS</string>
|
||||||
<string name="susfs_tab_sus_mounts">Kaitan SUS</string>
|
|
||||||
<string name="susfs_tab_try_umount">Coba Lepas Kait</string>
|
|
||||||
<string name="susfs_tab_path_settings">Pengaturan Jalur</string>
|
<string name="susfs_tab_path_settings">Pengaturan Jalur</string>
|
||||||
<string name="susfs_tab_enabled_features">Status Fitur Diaktifkan</string>
|
<string name="susfs_tab_enabled_features">Status Fitur Diaktifkan</string>
|
||||||
<!-- SuSFS Path Management -->
|
<!-- SuSFS Path Management -->
|
||||||
<string name="susfs_add_sus_path">Tambah Jalur SUS</string>
|
<string name="susfs_add_sus_path">Tambah Jalur SUS</string>
|
||||||
<string name="susfs_add_sus_mount">Tambah Kaitan SUS</string>
|
|
||||||
<string name="susfs_add_try_umount">Tambah Coba Lepas Kait</string>
|
|
||||||
<string name="susfs_sus_path_added_success">Jalur SUS berhasil ditambahkan</string>
|
<string name="susfs_sus_path_added_success">Jalur SUS berhasil ditambahkan</string>
|
||||||
<string name="susfs_path_not_found_error">Kesalahan: Jalur tidak ditemukan</string>
|
<string name="susfs_path_not_found_error">Kesalahan: Jalur tidak ditemukan</string>
|
||||||
<string name="susfs_path_label">Jalur</string>
|
<string name="susfs_path_label">Jalur</string>
|
||||||
<string name="susfs_mount_path_label">Jalur Kaitan</string>
|
|
||||||
<string name="susfs_path_placeholder">misalnya: /system/addon.d</string>
|
<string name="susfs_path_placeholder">misalnya: /system/addon.d</string>
|
||||||
<string name="susfs_no_paths_configured">Tidak ada jalur SUS yang dikonfigurasi</string>
|
<string name="susfs_no_paths_configured">Tidak ada jalur SUS yang dikonfigurasi</string>
|
||||||
<string name="susfs_no_mounts_configured">Tidak ada kaitan SUS yang dikonfigurasi</string>
|
|
||||||
<string name="susfs_no_umounts_configured">Tidak ada coba lepas kait yang dikonfigurasi</string>
|
|
||||||
<!-- SuSFS Umount Mode -->
|
<!-- SuSFS Umount Mode -->
|
||||||
<string name="susfs_umount_mode_label">Mode Lepas Kait</string>
|
|
||||||
<string name="susfs_umount_mode_normal">Lepas Kait Normal (0)</string>
|
|
||||||
<string name="susfs_umount_mode_detach">Lepas Kait Terpisah (1)</string>
|
|
||||||
<string name="susfs_umount_mode_normal_short">Normal</string>
|
|
||||||
<string name="susfs_umount_mode_detach_short">Terpisah</string>
|
|
||||||
<string name="susfs_umount_mode_display">Mode: %1$s (%2$s)</string>
|
|
||||||
<string name="susfs_try_umount_added_success">Jalur coba lepas kait berhasil ditambahkan: %s</string>
|
|
||||||
<string name="susfs_try_umount_added_saved">Berhasil menyimpan jalur coba lepas kait: %s</string>
|
|
||||||
<!-- SuSFS Run Umount -->
|
<!-- SuSFS Run Umount -->
|
||||||
<!-- SuSFS Reset Categories -->
|
<!-- SuSFS Reset Categories -->
|
||||||
<string name="susfs_reset_paths_title">Atur Ulang Jalur SUS</string>
|
<string name="susfs_reset_paths_title">Atur Ulang Jalur SUS</string>
|
||||||
<string name="susfs_reset_paths_message">Ini akan menghapus semua konfigurasi jalur SUS. Apakah Anda yakin ingin melanjutkan?</string>
|
<string name="susfs_reset_paths_message">Ini akan menghapus semua konfigurasi jalur SUS. Apakah Anda yakin ingin melanjutkan?</string>
|
||||||
<string name="susfs_reset_mounts_title">Atur Ulang Kaitan SUS</string>
|
|
||||||
<string name="susfs_reset_mounts_message">Ini akan menghapus semua konfigurasi kaitan SUS. Apakah Anda yakin ingin melanjutkan?</string>
|
|
||||||
<string name="susfs_reset_umounts_title">Atur Ulang Coba Lepas Kait</string>
|
|
||||||
<string name="susfs_reset_umounts_message">Ini akan menghapus semua konfigurasi coba lepas kait. Apakah Anda yakin ingin melanjutkan?</string>
|
|
||||||
<string name="susfs_reset_path_title">Atur Ulang Pengaturan Jalur</string>
|
<string name="susfs_reset_path_title">Atur Ulang Pengaturan Jalur</string>
|
||||||
<!-- SuSFS Path Settings -->
|
<!-- SuSFS Path Settings -->
|
||||||
<string name="susfs_android_data_path_label">Jalur Data Android</string>
|
<string name="susfs_android_data_path_label">Jalur Data Android</string>
|
||||||
@@ -428,18 +409,12 @@ Tanamkan: Secara permanen memasang ke sistem</string>
|
|||||||
<string name="susfs_feature_disabled">Dinonaktifkan</string>
|
<string name="susfs_feature_disabled">Dinonaktifkan</string>
|
||||||
<!-- Feature Labels -->
|
<!-- Feature Labels -->
|
||||||
<string name="sus_path_feature_label">Dukungan Jalur SUS</string>
|
<string name="sus_path_feature_label">Dukungan Jalur SUS</string>
|
||||||
<string name="sus_mount_feature_label">Dukungan Kaitan SUS</string>
|
|
||||||
<string name="try_umount_feature_label">Dukungan Coba Lepas Kait</string>
|
|
||||||
<string name="spoof_uname_feature_label">Dukungan Spoof Uname</string>
|
<string name="spoof_uname_feature_label">Dukungan Spoof Uname</string>
|
||||||
<string name="spoof_cmdline_feature_label">Spoof Cmdline/Bootconfig</string>
|
<string name="spoof_cmdline_feature_label">Spoof Cmdline/Bootconfig</string>
|
||||||
<string name="open_redirect_feature_label">Dukungan Open Redirect</string>
|
<string name="open_redirect_feature_label">Dukungan Open Redirect</string>
|
||||||
<string name="enable_log_feature_label">Dukungan Logging</string>
|
<string name="enable_log_feature_label">Dukungan Logging</string>
|
||||||
<string name="auto_default_mount_feature_label">Kaitan Bawaan Otomatis</string>
|
|
||||||
<string name="auto_bind_mount_feature_label">Kaitan Bind Otomatis</string>
|
|
||||||
<string name="auto_try_umount_bind_feature_label">Coba Lepas Kaitan Bind Otomatis</string>
|
|
||||||
<string name="hide_symbols_feature_label">Sembunyikan Simbol KSU SUSFS</string>
|
<string name="hide_symbols_feature_label">Sembunyikan Simbol KSU SUSFS</string>
|
||||||
<string name="sus_kstat_feature_label">Dukungan SUS Kstat</string>
|
<string name="sus_kstat_feature_label">Dukungan SUS Kstat</string>
|
||||||
<string name="sus_su_feature_label">Fitur Toggle Mode SUS SU</string>
|
|
||||||
<!-- 可切换状态 -->
|
<!-- 可切换状态 -->
|
||||||
<string name="susfs_feature_configurable">Fitur SuSFS yang Dapat Dikonfigurasi</string>
|
<string name="susfs_feature_configurable">Fitur SuSFS yang Dapat Dikonfigurasi</string>
|
||||||
<string name="susfs_enable_log_label">Aktifkan Log SuSFS</string>
|
<string name="susfs_enable_log_label">Aktifkan Log SuSFS</string>
|
||||||
|
|||||||
@@ -389,39 +389,20 @@
|
|||||||
<!-- SuSFS Tab Titles -->
|
<!-- SuSFS Tab Titles -->
|
||||||
<string name="susfs_tab_basic_settings">Pengaturan Dasar</string>
|
<string name="susfs_tab_basic_settings">Pengaturan Dasar</string>
|
||||||
<string name="susfs_tab_sus_paths">Jalur SUS</string>
|
<string name="susfs_tab_sus_paths">Jalur SUS</string>
|
||||||
<string name="susfs_tab_sus_mounts">Pemasangan SUS</string>
|
|
||||||
<string name="susfs_tab_try_umount">Coba Umount</string>
|
|
||||||
<string name="susfs_tab_path_settings">Pengaturan Path</string>
|
<string name="susfs_tab_path_settings">Pengaturan Path</string>
|
||||||
<string name="susfs_tab_enabled_features">Status Fitur yang Diaktifkan</string>
|
<string name="susfs_tab_enabled_features">Status Fitur yang Diaktifkan</string>
|
||||||
<!-- SuSFS Path Management -->
|
<!-- SuSFS Path Management -->
|
||||||
<string name="susfs_add_sus_path">Tambahkan Jalur SUS</string>
|
<string name="susfs_add_sus_path">Tambahkan Jalur SUS</string>
|
||||||
<string name="susfs_add_sus_mount">Tambahkan Pemasangan SUS</string>
|
|
||||||
<string name="susfs_add_try_umount">Tambahkan Coba Umount</string>
|
|
||||||
<string name="susfs_sus_path_added_success">Jalur SUS berhasil ditambahkan</string>
|
<string name="susfs_sus_path_added_success">Jalur SUS berhasil ditambahkan</string>
|
||||||
<string name="susfs_path_not_found_error">Kesalahan jalur tidak ditemukan</string>
|
<string name="susfs_path_not_found_error">Kesalahan jalur tidak ditemukan</string>
|
||||||
<string name="susfs_path_label">Jalur</string>
|
<string name="susfs_path_label">Jalur</string>
|
||||||
<string name="susfs_mount_path_label">Jalur Pemasangan</string>
|
|
||||||
<string name="susfs_path_placeholder">contoh: /system/addon.d</string>
|
<string name="susfs_path_placeholder">contoh: /system/addon.d</string>
|
||||||
<string name="susfs_no_paths_configured">Tidak ada jalur SUS yang dikonfigurasi</string>
|
<string name="susfs_no_paths_configured">Tidak ada jalur SUS yang dikonfigurasi</string>
|
||||||
<string name="susfs_no_mounts_configured">Tidak ada pemasangan SUS yang dikonfigurasi</string>
|
|
||||||
<string name="susfs_no_umounts_configured">Tidak ada coba umount yang dikonfigurasi</string>
|
|
||||||
<!-- SuSFS Umount Mode -->
|
<!-- SuSFS Umount Mode -->
|
||||||
<string name="susfs_umount_mode_label">Mode Umount</string>
|
|
||||||
<string name="susfs_umount_mode_normal">Umount Normal (0)</string>
|
|
||||||
<string name="susfs_umount_mode_detach">Umount Lepas (1)</string>
|
|
||||||
<string name="susfs_umount_mode_normal_short">Normal</string>
|
|
||||||
<string name="susfs_umount_mode_detach_short">Lepas</string>
|
|
||||||
<string name="susfs_umount_mode_display">Mode: %1$s (%2$s)</string>
|
|
||||||
<string name="susfs_try_umount_added_success">Jalur coba umount berhasil ditambahkan: %s</string>
|
|
||||||
<string name="susfs_try_umount_added_saved">Jalur coba umount berhasil disimpan: %s</string>
|
|
||||||
<!-- SuSFS Run Umount -->
|
<!-- SuSFS Run Umount -->
|
||||||
<!-- SuSFS Reset Categories -->
|
<!-- SuSFS Reset Categories -->
|
||||||
<string name="susfs_reset_paths_title">Setel Ulang Jalur SUS</string>
|
<string name="susfs_reset_paths_title">Setel Ulang Jalur SUS</string>
|
||||||
<string name="susfs_reset_paths_message">Ini akan menghapus semua konfigurasi jalur SUS. Apakah Anda yakin ingin melanjutkan?</string>
|
<string name="susfs_reset_paths_message">Ini akan menghapus semua konfigurasi jalur SUS. Apakah Anda yakin ingin melanjutkan?</string>
|
||||||
<string name="susfs_reset_mounts_title">Setel Ulang Pemasangan SUS</string>
|
|
||||||
<string name="susfs_reset_mounts_message">Ini akan menghapus semua konfigurasi mount SUS. Apakah Anda yakin ingin melanjutkan?</string>
|
|
||||||
<string name="susfs_reset_umounts_title">Setel Ulang Coba Umount</string>
|
|
||||||
<string name="susfs_reset_umounts_message">Ini akan menghapus semua konfigurasi umount. Apakah Anda yakin ingin melanjutkan?</string>
|
|
||||||
<string name="susfs_reset_path_title">Setel Ulang Pengaturan Jalur</string>
|
<string name="susfs_reset_path_title">Setel Ulang Pengaturan Jalur</string>
|
||||||
<!-- SuSFS Path Settings -->
|
<!-- SuSFS Path Settings -->
|
||||||
<string name="susfs_android_data_path_label">Jalur Data Android</string>
|
<string name="susfs_android_data_path_label">Jalur Data Android</string>
|
||||||
@@ -435,18 +416,12 @@
|
|||||||
<string name="susfs_feature_disabled">Dinonaktifkan</string>
|
<string name="susfs_feature_disabled">Dinonaktifkan</string>
|
||||||
<!-- Feature Labels -->
|
<!-- Feature Labels -->
|
||||||
<string name="sus_path_feature_label">Dukungan Jalur SUS</string>
|
<string name="sus_path_feature_label">Dukungan Jalur SUS</string>
|
||||||
<string name="sus_mount_feature_label">Dukungan Pemasangan SUS</string>
|
|
||||||
<string name="try_umount_feature_label">Dukungan Coba Umount</string>
|
|
||||||
<string name="spoof_uname_feature_label">Dukungan Spoof uname</string>
|
<string name="spoof_uname_feature_label">Dukungan Spoof uname</string>
|
||||||
<string name="spoof_cmdline_feature_label">Spoof Cmdline/Bootconfig</string>
|
<string name="spoof_cmdline_feature_label">Spoof Cmdline/Bootconfig</string>
|
||||||
<string name="open_redirect_feature_label">Dukungan Pengalihan Terbuka</string>
|
<string name="open_redirect_feature_label">Dukungan Pengalihan Terbuka</string>
|
||||||
<string name="enable_log_feature_label">Dukungan Logging</string>
|
<string name="enable_log_feature_label">Dukungan Logging</string>
|
||||||
<string name="auto_default_mount_feature_label">Pemasangan Default Otomatis</string>
|
|
||||||
<string name="auto_bind_mount_feature_label">Pemasangan Bind Otomatis</string>
|
|
||||||
<string name="auto_try_umount_bind_feature_label">Coba Umount Bind Mount Otomatis</string>
|
|
||||||
<string name="hide_symbols_feature_label">Sembunyikan Simbol KSU SUSFS</string>
|
<string name="hide_symbols_feature_label">Sembunyikan Simbol KSU SUSFS</string>
|
||||||
<string name="sus_kstat_feature_label">Dukungan SUS Kstat</string>
|
<string name="sus_kstat_feature_label">Dukungan SUS Kstat</string>
|
||||||
<string name="sus_su_feature_label">Fungsi pengalihan mode SUS SU</string>
|
|
||||||
<!-- 可切换状态 -->
|
<!-- 可切换状态 -->
|
||||||
<string name="susfs_feature_configurable">Fitur SuSFS yang Dapat Dikonfigurasi</string>
|
<string name="susfs_feature_configurable">Fitur SuSFS yang Dapat Dikonfigurasi</string>
|
||||||
<string name="susfs_enable_log_label">Aktifkan Log SuSFS</string>
|
<string name="susfs_enable_log_label">Aktifkan Log SuSFS</string>
|
||||||
@@ -542,8 +517,6 @@
|
|||||||
<string name="cleanup_residue">Bersihkan Residu</string>
|
<string name="cleanup_residue">Bersihkan Residu</string>
|
||||||
<string name="cleanup_residue_description">Bersihkan file dan direktori sisa dari berbagai modul dan alat (mungkin terhapus secara tidak sengaja, mengakibatkan kehilangan dan gagal memulai, gunakan dengan hati-hati)</string>
|
<string name="cleanup_residue_description">Bersihkan file dan direktori sisa dari berbagai modul dan alat (mungkin terhapus secara tidak sengaja, mengakibatkan kehilangan dan gagal memulai, gunakan dengan hati-hati)</string>
|
||||||
<string name="susfs_edit_sus_path">Edit Jalur SUS</string>
|
<string name="susfs_edit_sus_path">Edit Jalur SUS</string>
|
||||||
<string name="susfs_edit_sus_mount">Edit Pemasangan SUS</string>
|
|
||||||
<string name="susfs_edit_try_umount">Edit Coba Umount</string>
|
|
||||||
<string name="edit_kstat_statically_title">Edit Konfigurasi Statis Kstat</string>
|
<string name="edit_kstat_statically_title">Edit Konfigurasi Statis Kstat</string>
|
||||||
<string name="edit_kstat_path_title">Edit Jalur Kstat</string>
|
<string name="edit_kstat_path_title">Edit Jalur Kstat</string>
|
||||||
<string name="susfs_save">Simpan</string>
|
<string name="susfs_save">Simpan</string>
|
||||||
|
|||||||
@@ -380,39 +380,20 @@
|
|||||||
<!-- SuSFS Tab Titles -->
|
<!-- SuSFS Tab Titles -->
|
||||||
<string name="susfs_tab_basic_settings">基本設定</string>
|
<string name="susfs_tab_basic_settings">基本設定</string>
|
||||||
<string name="susfs_tab_sus_paths">SUS のパス</string>
|
<string name="susfs_tab_sus_paths">SUS のパス</string>
|
||||||
<string name="susfs_tab_sus_mounts">SUS マウント</string>
|
|
||||||
<string name="susfs_tab_try_umount">アンマウントを試す</string>
|
|
||||||
<string name="susfs_tab_path_settings">パスの設定</string>
|
<string name="susfs_tab_path_settings">パスの設定</string>
|
||||||
<string name="susfs_tab_enabled_features">有効な機能のステータス</string>
|
<string name="susfs_tab_enabled_features">有効な機能のステータス</string>
|
||||||
<!-- SuSFS Path Management -->
|
<!-- SuSFS Path Management -->
|
||||||
<string name="susfs_add_sus_path">SUS パスを追加</string>
|
<string name="susfs_add_sus_path">SUS パスを追加</string>
|
||||||
<string name="susfs_add_sus_mount">SUS マウントを追加</string>
|
|
||||||
<string name="susfs_add_try_umount">アンマウントを試すを追加</string>
|
|
||||||
<string name="susfs_sus_path_added_success">SUS パスが正常に追加されました</string>
|
<string name="susfs_sus_path_added_success">SUS パスが正常に追加されました</string>
|
||||||
<string name="susfs_path_not_found_error">パスが見つかりません</string>
|
<string name="susfs_path_not_found_error">パスが見つかりません</string>
|
||||||
<string name="susfs_path_label">パス</string>
|
<string name="susfs_path_label">パス</string>
|
||||||
<string name="susfs_mount_path_label">マウントのパス</string>
|
|
||||||
<string name="susfs_path_placeholder">例: /system/addon.d</string>
|
<string name="susfs_path_placeholder">例: /system/addon.d</string>
|
||||||
<string name="susfs_no_paths_configured">SUS パスが未構成です</string>
|
<string name="susfs_no_paths_configured">SUS パスが未構成です</string>
|
||||||
<string name="susfs_no_mounts_configured">SUS マウントが未構成です</string>
|
|
||||||
<string name="susfs_no_umounts_configured">アンマウントを試すが未構成です</string>
|
|
||||||
<!-- SuSFS Umount Mode -->
|
<!-- SuSFS Umount Mode -->
|
||||||
<string name="susfs_umount_mode_label">アンマウントモード</string>
|
|
||||||
<string name="susfs_umount_mode_normal">通常のアンマウント (0)</string>
|
|
||||||
<string name="susfs_umount_mode_detach">アンマウントを分離 (1)</string>
|
|
||||||
<string name="susfs_umount_mode_normal_short">通常</string>
|
|
||||||
<string name="susfs_umount_mode_detach_short">分離</string>
|
|
||||||
<string name="susfs_umount_mode_display">モード: %1$s (%2$s)</string>
|
|
||||||
<string name="susfs_try_umount_added_success">追加されたパスのアンマウントに成功しました: %s</string>
|
|
||||||
<string name="susfs_try_umount_added_saved">アンマウントのパスの保存に成功しました: %s</string>
|
|
||||||
<!-- SuSFS Run Umount -->
|
<!-- SuSFS Run Umount -->
|
||||||
<!-- SuSFS Reset Categories -->
|
<!-- SuSFS Reset Categories -->
|
||||||
<string name="susfs_reset_paths_title">SUS パスをリセット</string>
|
<string name="susfs_reset_paths_title">SUS パスをリセット</string>
|
||||||
<string name="susfs_reset_paths_message">すべての SUS パスの構成が消去されます。続行してもよろしいですか?</string>
|
<string name="susfs_reset_paths_message">すべての SUS パスの構成が消去されます。続行してもよろしいですか?</string>
|
||||||
<string name="susfs_reset_mounts_title">SUS マウントをリセット</string>
|
|
||||||
<string name="susfs_reset_mounts_message">すべての SUS マウントの構成が消去されます。続行してもよろしいですか?</string>
|
|
||||||
<string name="susfs_reset_umounts_title">リセットしてアンマウントを試す</string>
|
|
||||||
<string name="susfs_reset_umounts_message">すべてのアンマウント構成がリセットされます。続行してもよろしいですか?</string>
|
|
||||||
<string name="susfs_reset_path_title">パスの設定をリセット</string>
|
<string name="susfs_reset_path_title">パスの設定をリセット</string>
|
||||||
<!-- SuSFS Path Settings -->
|
<!-- SuSFS Path Settings -->
|
||||||
<string name="susfs_android_data_path_label">Android データパス</string>
|
<string name="susfs_android_data_path_label">Android データパス</string>
|
||||||
@@ -426,18 +407,12 @@
|
|||||||
<string name="susfs_feature_disabled">無効</string>
|
<string name="susfs_feature_disabled">無効</string>
|
||||||
<!-- Feature Labels -->
|
<!-- Feature Labels -->
|
||||||
<string name="sus_path_feature_label">SUS パスの対応</string>
|
<string name="sus_path_feature_label">SUS パスの対応</string>
|
||||||
<string name="sus_mount_feature_label">SUS マウントの対応</string>
|
|
||||||
<string name="try_umount_feature_label">アンマウントを試すの対応</string>
|
|
||||||
<string name="spoof_uname_feature_label">uname 偽装の対応</string>
|
<string name="spoof_uname_feature_label">uname 偽装の対応</string>
|
||||||
<string name="spoof_cmdline_feature_label">Cmdline/Bootconfig を偽装</string>
|
<string name="spoof_cmdline_feature_label">Cmdline/Bootconfig を偽装</string>
|
||||||
<string name="open_redirect_feature_label">オープンリダイレクトの対応</string>
|
<string name="open_redirect_feature_label">オープンリダイレクトの対応</string>
|
||||||
<string name="enable_log_feature_label">ログの対応</string>
|
<string name="enable_log_feature_label">ログの対応</string>
|
||||||
<string name="auto_default_mount_feature_label">自動でデフォルトのマウント</string>
|
|
||||||
<string name="auto_bind_mount_feature_label">自動でバインドマウント</string>
|
|
||||||
<string name="auto_try_umount_bind_feature_label">自動でバインドマウントのアンマウントを試す</string>
|
|
||||||
<string name="hide_symbols_feature_label">KSU SUSFS シンボルを非表示</string>
|
<string name="hide_symbols_feature_label">KSU SUSFS シンボルを非表示</string>
|
||||||
<string name="sus_kstat_feature_label">SUS Kstat の対応</string>
|
<string name="sus_kstat_feature_label">SUS Kstat の対応</string>
|
||||||
<string name="sus_su_feature_label">SUS SU モード切り替え機能</string>
|
|
||||||
<!-- 可切换状态 -->
|
<!-- 可切换状态 -->
|
||||||
<string name="susfs_feature_configurable">構成可能な SuSFS の機能</string>
|
<string name="susfs_feature_configurable">構成可能な SuSFS の機能</string>
|
||||||
<string name="susfs_enable_log_label">SuSFS のログ取得を有効化</string>
|
<string name="susfs_enable_log_label">SuSFS のログ取得を有効化</string>
|
||||||
@@ -533,8 +508,6 @@
|
|||||||
<string name="cleanup_residue">残骸をクリーンアップ</string>
|
<string name="cleanup_residue">残骸をクリーンアップ</string>
|
||||||
<string name="cleanup_residue_description">様々なモジュールや残骸となったツールのファイルとディレクトリをクリーンアップします (誤って削除すると損失や起動の失敗に繋がる可能性があるため、注意して使用してください)</string>
|
<string name="cleanup_residue_description">様々なモジュールや残骸となったツールのファイルとディレクトリをクリーンアップします (誤って削除すると損失や起動の失敗に繋がる可能性があるため、注意して使用してください)</string>
|
||||||
<string name="susfs_edit_sus_path">SUS のパスを編集</string>
|
<string name="susfs_edit_sus_path">SUS のパスを編集</string>
|
||||||
<string name="susfs_edit_sus_mount">SUS マウントを編集</string>
|
|
||||||
<string name="susfs_edit_try_umount">アンマウントを試すを編集</string>
|
|
||||||
<string name="edit_kstat_statically_title">Kstat 静的構成を編集</string>
|
<string name="edit_kstat_statically_title">Kstat 静的構成を編集</string>
|
||||||
<string name="edit_kstat_path_title">Kstat のパスを編集</string>
|
<string name="edit_kstat_path_title">Kstat のパスを編集</string>
|
||||||
<string name="susfs_save">保存</string>
|
<string name="susfs_save">保存</string>
|
||||||
|
|||||||
@@ -390,39 +390,20 @@
|
|||||||
<!-- SuSFS Tab Titles -->
|
<!-- SuSFS Tab Titles -->
|
||||||
<string name="susfs_tab_basic_settings">Базовые настройки</string>
|
<string name="susfs_tab_basic_settings">Базовые настройки</string>
|
||||||
<string name="susfs_tab_sus_paths">SUS пути</string>
|
<string name="susfs_tab_sus_paths">SUS пути</string>
|
||||||
<string name="susfs_tab_sus_mounts">SUS монтирование</string>
|
|
||||||
<string name="susfs_tab_try_umount">Попробовать размонтировать</string>
|
|
||||||
<string name="susfs_tab_path_settings">Настройки пути</string>
|
<string name="susfs_tab_path_settings">Настройки пути</string>
|
||||||
<string name="susfs_tab_enabled_features">Статус включённых функций</string>
|
<string name="susfs_tab_enabled_features">Статус включённых функций</string>
|
||||||
<!-- SuSFS Path Management -->
|
<!-- SuSFS Path Management -->
|
||||||
<string name="susfs_add_sus_path">Добавить SUS путь</string>
|
<string name="susfs_add_sus_path">Добавить SUS путь</string>
|
||||||
<string name="susfs_add_sus_mount">Добавить SUS монтирование</string>
|
|
||||||
<string name="susfs_add_try_umount">Добавить попробовать размонтировать</string>
|
|
||||||
<string name="susfs_sus_path_added_success">Путь SUS успешно добавлен</string>
|
<string name="susfs_sus_path_added_success">Путь SUS успешно добавлен</string>
|
||||||
<string name="susfs_path_not_found_error">Ошибка пути</string>
|
<string name="susfs_path_not_found_error">Ошибка пути</string>
|
||||||
<string name="susfs_path_label">Путь</string>
|
<string name="susfs_path_label">Путь</string>
|
||||||
<string name="susfs_mount_path_label">Путь монтирования</string>
|
|
||||||
<string name="susfs_path_placeholder">например: /system/addon.d</string>
|
<string name="susfs_path_placeholder">например: /system/addon.d</string>
|
||||||
<string name="susfs_no_paths_configured">SUS пути не настроены</string>
|
<string name="susfs_no_paths_configured">SUS пути не настроены</string>
|
||||||
<string name="susfs_no_mounts_configured">SUS монтирование не настроено</string>
|
|
||||||
<string name="susfs_no_umounts_configured">Попытка размонтировать не настроена</string>
|
|
||||||
<!-- SuSFS Umount Mode -->
|
<!-- SuSFS Umount Mode -->
|
||||||
<string name="susfs_umount_mode_label">Режим размонтирования</string>
|
|
||||||
<string name="susfs_umount_mode_normal">Обычное размонтирование (0)</string>
|
|
||||||
<string name="susfs_umount_mode_detach">Размонтирование отсоединением (1)</string>
|
|
||||||
<string name="susfs_umount_mode_normal_short">Нормальный</string>
|
|
||||||
<string name="susfs_umount_mode_detach_short">Отсоединить</string>
|
|
||||||
<string name="susfs_umount_mode_display">Режим: %1$s (%2$s)</string>
|
|
||||||
<string name="susfs_try_umount_added_success">Попытка размонтировать путь успешно добавлена: %s</string>
|
|
||||||
<string name="susfs_try_umount_added_saved">Попытка размонтировать путь успешно сохранена: %s</string>
|
|
||||||
<!-- SuSFS Run Umount -->
|
<!-- SuSFS Run Umount -->
|
||||||
<!-- SuSFS Reset Categories -->
|
<!-- SuSFS Reset Categories -->
|
||||||
<string name="susfs_reset_paths_title">Сбросить SUS пути</string>
|
<string name="susfs_reset_paths_title">Сбросить SUS пути</string>
|
||||||
<string name="susfs_reset_paths_message">Это очистит все конфигурации пути SUS. Вы уверены, что хотите продолжить?</string>
|
<string name="susfs_reset_paths_message">Это очистит все конфигурации пути SUS. Вы уверены, что хотите продолжить?</string>
|
||||||
<string name="susfs_reset_mounts_title">Сброс SUS монтирования</string>
|
|
||||||
<string name="susfs_reset_mounts_message">Это очистит все конфигурации SUS монтирования. Вы уверены, что хотите продолжить?</string>
|
|
||||||
<string name="susfs_reset_umounts_title">Сбросить Umount</string>
|
|
||||||
<string name="susfs_reset_umounts_message">Это очистит все конфигурации размонтирования. Вы уверены, что хотите продолжить?</string>
|
|
||||||
<string name="susfs_reset_path_title">Сбросить настройки пути</string>
|
<string name="susfs_reset_path_title">Сбросить настройки пути</string>
|
||||||
<!-- SuSFS Path Settings -->
|
<!-- SuSFS Path Settings -->
|
||||||
<string name="susfs_android_data_path_label">Путь к данным Android</string>
|
<string name="susfs_android_data_path_label">Путь к данным Android</string>
|
||||||
@@ -436,18 +417,12 @@
|
|||||||
<string name="susfs_feature_disabled">Выключено</string>
|
<string name="susfs_feature_disabled">Выключено</string>
|
||||||
<!-- Feature Labels -->
|
<!-- Feature Labels -->
|
||||||
<string name="sus_path_feature_label">Поддержка SUS пути</string>
|
<string name="sus_path_feature_label">Поддержка SUS пути</string>
|
||||||
<string name="sus_mount_feature_label">Поддержка SUS монтирования</string>
|
|
||||||
<string name="try_umount_feature_label">Поддержка размонтирования</string>
|
|
||||||
<string name="spoof_uname_feature_label">Поддержка подмены uname</string>
|
<string name="spoof_uname_feature_label">Поддержка подмены uname</string>
|
||||||
<string name="spoof_cmdline_feature_label">Подмена Cmdline/Bootconfig</string>
|
<string name="spoof_cmdline_feature_label">Подмена Cmdline/Bootconfig</string>
|
||||||
<string name="open_redirect_feature_label">Поддержка Open Redirect</string>
|
<string name="open_redirect_feature_label">Поддержка Open Redirect</string>
|
||||||
<string name="enable_log_feature_label">Поддержка логов</string>
|
<string name="enable_log_feature_label">Поддержка логов</string>
|
||||||
<string name="auto_default_mount_feature_label">Автомонтирование по умолчанию</string>
|
|
||||||
<string name="auto_bind_mount_feature_label">Автоматическое бинд монтирование</string>
|
|
||||||
<string name="auto_try_umount_bind_feature_label">Автоматически попробовать размонтировать привязать монтировать</string>
|
|
||||||
<string name="hide_symbols_feature_label">Скрытие KSU SUSFS Symbols</string>
|
<string name="hide_symbols_feature_label">Скрытие KSU SUSFS Symbols</string>
|
||||||
<string name="sus_kstat_feature_label">Поддержка SUS Kstat</string>
|
<string name="sus_kstat_feature_label">Поддержка SUS Kstat</string>
|
||||||
<string name="sus_su_feature_label">Функция переключения режима SUS SU</string>
|
|
||||||
<!-- 可切换状态 -->
|
<!-- 可切换状态 -->
|
||||||
<string name="susfs_feature_configurable">Настраиваемые функции SuSFS</string>
|
<string name="susfs_feature_configurable">Настраиваемые функции SuSFS</string>
|
||||||
<string name="susfs_enable_log_label">SuSFS включить лог</string>
|
<string name="susfs_enable_log_label">SuSFS включить лог</string>
|
||||||
@@ -543,8 +518,6 @@
|
|||||||
<string name="cleanup_residue">Очистка</string>
|
<string name="cleanup_residue">Очистка</string>
|
||||||
<string name="cleanup_residue_description">Очистка остаточных файлов и каталогов различных модулей и инструментов (может быть удален по ошибке, в результате потери и неспособности начаться, используйте с осторожностью)</string>
|
<string name="cleanup_residue_description">Очистка остаточных файлов и каталогов различных модулей и инструментов (может быть удален по ошибке, в результате потери и неспособности начаться, используйте с осторожностью)</string>
|
||||||
<string name="susfs_edit_sus_path">Редактировать путь SUS</string>
|
<string name="susfs_edit_sus_path">Редактировать путь SUS</string>
|
||||||
<string name="susfs_edit_sus_mount">Редактировать точку монтирования SUS</string>
|
|
||||||
<string name="susfs_edit_try_umount">Изменить попробовать размонтировать</string>
|
|
||||||
<string name="edit_kstat_statically_title">Изменить статическую конфигурацию Kstat</string>
|
<string name="edit_kstat_statically_title">Изменить статическую конфигурацию Kstat</string>
|
||||||
<string name="edit_kstat_path_title">Редактировать путь Kstat</string>
|
<string name="edit_kstat_path_title">Редактировать путь Kstat</string>
|
||||||
<string name="susfs_save">Сохранить</string>
|
<string name="susfs_save">Сохранить</string>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="home">Ana Sayfa</string>
|
<string name="home">Ana Sayfa</string>
|
||||||
|
<string name="learn_more">Daha fazla bilgi edinin</string>
|
||||||
<string name="home_not_installed">Yüklü değil</string>
|
<string name="home_not_installed">Yüklü değil</string>
|
||||||
<string name="home_click_to_install">Yüklemek için tıklayın</string>
|
<string name="home_click_to_install">Yüklemek için tıklayın</string>
|
||||||
<string name="home_working">Çalışıyor</string>
|
<string name="home_working">Çalışıyor</string>
|
||||||
@@ -34,6 +35,7 @@
|
|||||||
<string name="reboot_edl">EDL moduna yeniden başlat</string>
|
<string name="reboot_edl">EDL moduna yeniden başlat</string>
|
||||||
<string name="about">Hakkında</string>
|
<string name="about">Hakkında</string>
|
||||||
<string name="module_uninstall_confirm">%s modülünü kaldırmak istediğinizden emin misiniz?</string>
|
<string name="module_uninstall_confirm">%s modülünü kaldırmak istediğinizden emin misiniz?</string>
|
||||||
|
<string name="metamodule_uninstall_confirm">"%s modülünü kaldırmak istediğinizden emin misiniz? Bu eylem tüm modülleri etkileyecek ve meta modül tarafından sağlanan belirli özellikler (örneğin bağlama) artık çalışmayacaktır."</string>
|
||||||
<string name="module_uninstall_success">%s kaldırıldı</string>
|
<string name="module_uninstall_success">%s kaldırıldı</string>
|
||||||
<string name="module_uninstall_failed">Kaldırılamadı: %s</string>
|
<string name="module_uninstall_failed">Kaldırılamadı: %s</string>
|
||||||
<string name="module_version">Sürüm</string>
|
<string name="module_version">Sürüm</string>
|
||||||
@@ -109,6 +111,7 @@
|
|||||||
<string name="install_inactive_slot">Etkin olmayan yuvaya yükle (OTA sonrası)</string>
|
<string name="install_inactive_slot">Etkin olmayan yuvaya yükle (OTA sonrası)</string>
|
||||||
<string name="install_inactive_slot_warning">Cihazınız, yeniden başlatma sonrasında **ZORUNLU** olarak mevcut etkin olmayan yuvaya önyükleme yapacaktır!\nSadece OTA tamamlandıktan sonra bu seçeneği kullanın.\nDevam etmek istiyor musunuz?</string>
|
<string name="install_inactive_slot_warning">Cihazınız, yeniden başlatma sonrasında **ZORUNLU** olarak mevcut etkin olmayan yuvaya önyükleme yapacaktır!\nSadece OTA tamamlandıktan sonra bu seçeneği kullanın.\nDevam etmek istiyor musunuz?</string>
|
||||||
<string name="install_next">İleri</string>
|
<string name="install_next">İleri</string>
|
||||||
|
<string name="install_select_partition">Bölüm seçin</string>
|
||||||
<string name="install_upload_lkm_file">Yerel LKM dosyası kullan</string>
|
<string name="install_upload_lkm_file">Yerel LKM dosyası kullan</string>
|
||||||
<string name="install_only_support_ko_file">Yalnızca .ko dosyaları desteklenir</string>
|
<string name="install_only_support_ko_file">Yalnızca .ko dosyaları desteklenir</string>
|
||||||
<string name="select_file_tip">%1$s bölüm görüntüsü önerilir</string>
|
<string name="select_file_tip">%1$s bölüm görüntüsü önerilir</string>
|
||||||
@@ -167,6 +170,8 @@
|
|||||||
<string name="settings_disable_kernel_umount_summary">KernelSU tarafından kontrol edilen çekirdek seviyesindeki ayırma davranışını devre dışı bırakın.</string>
|
<string name="settings_disable_kernel_umount_summary">KernelSU tarafından kontrol edilen çekirdek seviyesindeki ayırma davranışını devre dışı bırakın.</string>
|
||||||
<string name="settings_enable_enhanced_security">Gelişmiş güvenliği etkinleştir</string>
|
<string name="settings_enable_enhanced_security">Gelişmiş güvenliği etkinleştir</string>
|
||||||
<string name="settings_enable_enhanced_security_summary">Daha katı güvenlik politikalarını etkinleştirin.</string>
|
<string name="settings_enable_enhanced_security_summary">Daha katı güvenlik politikalarını etkinleştirin.</string>
|
||||||
|
<string name="feature_status_unsupported_summary">Çekirdek bu özelliği desteklemiyor.</string>
|
||||||
|
<string name="feature_status_managed_summary">Bu özellik bir modül tarafından yönetiliyor.</string>
|
||||||
<string name="settings_mode_default">Varsayılan</string>
|
<string name="settings_mode_default">Varsayılan</string>
|
||||||
<string name="settings_mode_temp_enable">Geçici olarak etkinleştir</string>
|
<string name="settings_mode_temp_enable">Geçici olarak etkinleştir</string>
|
||||||
<string name="settings_mode_always_enable">Kalıcı olarak etkinleştir</string>
|
<string name="settings_mode_always_enable">Kalıcı olarak etkinleştir</string>
|
||||||
@@ -185,6 +190,8 @@
|
|||||||
<string name="hide_susfs_status_summary">Ana sayfadaki SuSFS durum bilgilerini gizle</string>
|
<string name="hide_susfs_status_summary">Ana sayfadaki SuSFS durum bilgilerini gizle</string>
|
||||||
<string name="hide_zygisk_implement">Zygisk durumunu gizle</string>
|
<string name="hide_zygisk_implement">Zygisk durumunu gizle</string>
|
||||||
<string name="hide_zygisk_implement_summary">Ana sayfada Zygisk uygulama bilgisini gizle</string>
|
<string name="hide_zygisk_implement_summary">Ana sayfada Zygisk uygulama bilgisini gizle</string>
|
||||||
|
<string name="hide_meta_module_implement">Meta Modül durumunu gizle</string>
|
||||||
|
<string name="hide_meta_module_implement_summary">Ana sayfadaki Meta Modül uygulama bilgilerini gizle</string>
|
||||||
<string name="hide_link_card">Bağlantı Kartı Durumunu Gizle</string>
|
<string name="hide_link_card">Bağlantı Kartı Durumunu Gizle</string>
|
||||||
<string name="hide_link_card_summary">Ana sayfadaki bağlantı kartı bilgilerini gizle</string>
|
<string name="hide_link_card_summary">Ana sayfadaki bağlantı kartı bilgilerini gizle</string>
|
||||||
<string name="hide_tag_card">Modül etiket satırlarını gizle</string>
|
<string name="hide_tag_card">Modül etiket satırlarını gizle</string>
|
||||||
@@ -194,6 +201,7 @@
|
|||||||
<string name="theme_light">Açık</string>
|
<string name="theme_light">Açık</string>
|
||||||
<string name="theme_dark">Koyu</string>
|
<string name="theme_dark">Koyu</string>
|
||||||
<string name="manual_hook">Manuel Kanca</string>
|
<string name="manual_hook">Manuel Kanca</string>
|
||||||
|
<string name="inline_hook">Satır İçi Kanca</string>
|
||||||
<string name="dynamic_color_title">Dinamik renkler</string>
|
<string name="dynamic_color_title">Dinamik renkler</string>
|
||||||
<string name="dynamic_color_summary">Sistem temaları kullanarak dinamik renkler</string>
|
<string name="dynamic_color_summary">Sistem temaları kullanarak dinamik renkler</string>
|
||||||
<string name="choose_theme_color">Bir tema rengi seçin</string>
|
<string name="choose_theme_color">Bir tema rengi seçin</string>
|
||||||
@@ -325,6 +333,8 @@
|
|||||||
<string name="module_failed_count">%d yeni modül yüklenemedi</string>
|
<string name="module_failed_count">%d yeni modül yüklenemedi</string>
|
||||||
<string name="module_download_error">Modül indirme başarısız</string>
|
<string name="module_download_error">Modül indirme başarısız</string>
|
||||||
<string name="kernel_flashing">Çekirdek Yükleniyor</string>
|
<string name="kernel_flashing">Çekirdek Yükleniyor</string>
|
||||||
|
<string name="warning_of_meta_module_title">Meta modül gerektirir</string>
|
||||||
|
<string name="warning_of_meta_module_summary">Bu modül /system bölümünü bağlamak istiyor, meta modül bunu halledecektir. Aksi takdirde, çalışmayabilir.</string>
|
||||||
<!-- 分类相关 -->
|
<!-- 分类相关 -->
|
||||||
<string name="category_all_apps">Tümü</string>
|
<string name="category_all_apps">Tümü</string>
|
||||||
<string name="category_root_apps">Root</string>
|
<string name="category_root_apps">Root</string>
|
||||||
@@ -387,39 +397,20 @@
|
|||||||
<!-- SuSFS Tab Titles -->
|
<!-- SuSFS Tab Titles -->
|
||||||
<string name="susfs_tab_basic_settings">Temel Ayarlar</string>
|
<string name="susfs_tab_basic_settings">Temel Ayarlar</string>
|
||||||
<string name="susfs_tab_sus_paths">SUS Yolları</string>
|
<string name="susfs_tab_sus_paths">SUS Yolları</string>
|
||||||
<string name="susfs_tab_sus_mounts">SUS Bağlama Noktaları</string>
|
|
||||||
<string name="susfs_tab_try_umount">Bağlamayı Kaldırmayı Dene</string>
|
|
||||||
<string name="susfs_tab_path_settings">Yol Ayarları</string>
|
<string name="susfs_tab_path_settings">Yol Ayarları</string>
|
||||||
<string name="susfs_tab_enabled_features">Etkinleştirilen Özellikler Durumu</string>
|
<string name="susfs_tab_enabled_features">Etkinleştirilen Özellikler Durumu</string>
|
||||||
<!-- SuSFS Path Management -->
|
<!-- SuSFS Path Management -->
|
||||||
<string name="susfs_add_sus_path">SUS Yolu Ekle</string>
|
<string name="susfs_add_sus_path">SUS Yolu Ekle</string>
|
||||||
<string name="susfs_add_sus_mount">SUS Bağlama Noktası Ekle</string>
|
|
||||||
<string name="susfs_add_try_umount">Bağlamayı Kaldırmayı Dene Ekle</string>
|
|
||||||
<string name="susfs_sus_path_added_success">SUS yolu başarıyla eklendi</string>
|
<string name="susfs_sus_path_added_success">SUS yolu başarıyla eklendi</string>
|
||||||
<string name="susfs_path_not_found_error">Yol bulunamadı hatası</string>
|
<string name="susfs_path_not_found_error">Yol bulunamadı hatası</string>
|
||||||
<string name="susfs_path_label">Yol</string>
|
<string name="susfs_path_label">Yol</string>
|
||||||
<string name="susfs_mount_path_label">Bağlama Yolu</string>
|
|
||||||
<string name="susfs_path_placeholder">örn.: /system/addon.d</string>
|
<string name="susfs_path_placeholder">örn.: /system/addon.d</string>
|
||||||
<string name="susfs_no_paths_configured">Yapılandırılmış SUS yolu yok</string>
|
<string name="susfs_no_paths_configured">Yapılandırılmış SUS yolu yok</string>
|
||||||
<string name="susfs_no_mounts_configured">Yapılandırılmış SUS bağlama noktası yok</string>
|
|
||||||
<string name="susfs_no_umounts_configured">Yapılandırılmış bağlamayı kaldırmayı dene yok</string>
|
|
||||||
<!-- SuSFS Umount Mode -->
|
<!-- SuSFS Umount Mode -->
|
||||||
<string name="susfs_umount_mode_label">Bağlamayı Kaldır Modu</string>
|
|
||||||
<string name="susfs_umount_mode_normal">Normal Bağlamayı Kaldır (0)</string>
|
|
||||||
<string name="susfs_umount_mode_detach">Ayrı Bağlamayı Kaldır (1)</string>
|
|
||||||
<string name="susfs_umount_mode_normal_short">Normal</string>
|
|
||||||
<string name="susfs_umount_mode_detach_short">Ayrı</string>
|
|
||||||
<string name="susfs_umount_mode_display">Mod: %1$s (%2$s)</string>
|
|
||||||
<string name="susfs_try_umount_added_success">Bağlamayı kaldırmayı dene yolu başarıyla eklendi: %s</string>
|
|
||||||
<string name="susfs_try_umount_added_saved">Bağlamayı kaldırmayı dene yolu kaydetme başarılı: %s</string>
|
|
||||||
<!-- SuSFS Run Umount -->
|
<!-- SuSFS Run Umount -->
|
||||||
<!-- SuSFS Reset Categories -->
|
<!-- SuSFS Reset Categories -->
|
||||||
<string name="susfs_reset_paths_title">SUS Yollarını Sıfırla</string>
|
<string name="susfs_reset_paths_title">SUS Yollarını Sıfırla</string>
|
||||||
<string name="susfs_reset_paths_message">Bu, tüm SUS yol yapılandırmalarını temizleyecektir. Devam etmek istiyor musunuz?</string>
|
<string name="susfs_reset_paths_message">Bu, tüm SUS yol yapılandırmalarını temizleyecektir. Devam etmek istiyor musunuz?</string>
|
||||||
<string name="susfs_reset_mounts_title">SUS Bağlama Noktalarını Sıfırla</string>
|
|
||||||
<string name="susfs_reset_mounts_message">Bu, tüm SUS bağlama noktası yapılandırmalarını temizleyecektir. Devam etmek istiyor musunuz?</string>
|
|
||||||
<string name="susfs_reset_umounts_title">Bağlamayı Kaldırmayı Dene Sıfırla</string>
|
|
||||||
<string name="susfs_reset_umounts_message">Bu, tüm bağlamayı kaldırmayı dene yapılandırmalarını temizleyecektir. Devam etmek istiyor musunuz?</string>
|
|
||||||
<string name="susfs_reset_path_title">Yol Ayarlarını Sıfırla</string>
|
<string name="susfs_reset_path_title">Yol Ayarlarını Sıfırla</string>
|
||||||
<!-- SuSFS Path Settings -->
|
<!-- SuSFS Path Settings -->
|
||||||
<string name="susfs_android_data_path_label">Android Veri Yolu</string>
|
<string name="susfs_android_data_path_label">Android Veri Yolu</string>
|
||||||
@@ -433,18 +424,12 @@
|
|||||||
<string name="susfs_feature_disabled">Devre Dışı Bırakıldı</string>
|
<string name="susfs_feature_disabled">Devre Dışı Bırakıldı</string>
|
||||||
<!-- Feature Labels -->
|
<!-- Feature Labels -->
|
||||||
<string name="sus_path_feature_label">SUS Yol Desteği</string>
|
<string name="sus_path_feature_label">SUS Yol Desteği</string>
|
||||||
<string name="sus_mount_feature_label">SUS Bağlama Noktası Desteği</string>
|
|
||||||
<string name="try_umount_feature_label">Bağlamayı Kaldırmayı Dene Desteği</string>
|
|
||||||
<string name="spoof_uname_feature_label">Uname Taklit Desteği</string>
|
<string name="spoof_uname_feature_label">Uname Taklit Desteği</string>
|
||||||
<string name="spoof_cmdline_feature_label">Cmdline/Bootconfig Taklit</string>
|
<string name="spoof_cmdline_feature_label">Cmdline/Bootconfig Taklit</string>
|
||||||
<string name="open_redirect_feature_label">Açık Yönlendirme Desteği</string>
|
<string name="open_redirect_feature_label">Açık Yönlendirme Desteği</string>
|
||||||
<string name="enable_log_feature_label">Günlük Kaydı Desteği</string>
|
<string name="enable_log_feature_label">Günlük Kaydı Desteği</string>
|
||||||
<string name="auto_default_mount_feature_label">Otomatik Varsayılan Bağlama</string>
|
|
||||||
<string name="auto_bind_mount_feature_label">Otomatik Bağlama Noktası Bağlama</string>
|
|
||||||
<string name="auto_try_umount_bind_feature_label">Otomatik Bağlamayı Kaldırmayı Dene Bağlama</string>
|
|
||||||
<string name="hide_symbols_feature_label">KSU SUSFS Sembollerini Gizle</string>
|
<string name="hide_symbols_feature_label">KSU SUSFS Sembollerini Gizle</string>
|
||||||
<string name="sus_kstat_feature_label">SUS Kstat Desteği</string>
|
<string name="sus_kstat_feature_label">SUS Kstat Desteği</string>
|
||||||
<string name="sus_su_feature_label">SUS SU mod değiştirme işlevi</string>
|
|
||||||
<!-- 可切换状态 -->
|
<!-- 可切换状态 -->
|
||||||
<string name="susfs_feature_configurable">Yapılandırılabilir SuSFS Özellikleri</string>
|
<string name="susfs_feature_configurable">Yapılandırılabilir SuSFS Özellikleri</string>
|
||||||
<string name="susfs_enable_log_label">SuSFS Günlük Kaydını Etkinleştir</string>
|
<string name="susfs_enable_log_label">SuSFS Günlük Kaydını Etkinleştir</string>
|
||||||
@@ -540,8 +525,6 @@
|
|||||||
<string name="cleanup_residue">Kalıntıları Temizle</string>
|
<string name="cleanup_residue">Kalıntıları Temizle</string>
|
||||||
<string name="cleanup_residue_description">Çeşitli modüllerin ve araçların kalıntı dosyalarını ve dizinlerini temizleyin (yanlışlıkla silinerek kayba ve başlatılamamaya neden olabilir, dikkatli kullanın)</string>
|
<string name="cleanup_residue_description">Çeşitli modüllerin ve araçların kalıntı dosyalarını ve dizinlerini temizleyin (yanlışlıkla silinerek kayba ve başlatılamamaya neden olabilir, dikkatli kullanın)</string>
|
||||||
<string name="susfs_edit_sus_path">SUS Yolunu Düzenle</string>
|
<string name="susfs_edit_sus_path">SUS Yolunu Düzenle</string>
|
||||||
<string name="susfs_edit_sus_mount">SUS Bağlama Noktasını Düzenle</string>
|
|
||||||
<string name="susfs_edit_try_umount">Ayırmayı Deneme Yolunu Düzenle</string>
|
|
||||||
<string name="edit_kstat_statically_title">Kstat Statik Yapılandırmasını Düzenle</string>
|
<string name="edit_kstat_statically_title">Kstat Statik Yapılandırmasını Düzenle</string>
|
||||||
<string name="edit_kstat_path_title">Kstat Yolunu Düzenle</string>
|
<string name="edit_kstat_path_title">Kstat Yolunu Düzenle</string>
|
||||||
<string name="susfs_save">Kaydet</string>
|
<string name="susfs_save">Kaydet</string>
|
||||||
@@ -585,6 +568,7 @@
|
|||||||
<string name="no_active_manager">Aktif yönetici yok</string>
|
<string name="no_active_manager">Aktif yönetici yok</string>
|
||||||
<string name="default_signature">SukiSU</string>
|
<string name="default_signature">SukiSU</string>
|
||||||
<string name="home_zygisk_implement">Zygisk uygulaması</string>
|
<string name="home_zygisk_implement">Zygisk uygulaması</string>
|
||||||
|
<string name="home_meta_module_implement">Meta Modül uygulaması</string>
|
||||||
<!-- 循环路径相关 -->
|
<!-- 循环路径相关 -->
|
||||||
<string name="susfs_tab_sus_loop_paths">SUS Döngü Yolları</string>
|
<string name="susfs_tab_sus_loop_paths">SUS Döngü Yolları</string>
|
||||||
<string name="susfs_add_sus_loop_path">SUS Döngü Yolu Ekle</string>
|
<string name="susfs_add_sus_loop_path">SUS Döngü Yolu Ekle</string>
|
||||||
@@ -718,4 +702,27 @@ etkin: Çekirdekteki AVC günlük kaydında, \'su\' komutuna ait tcontext\'i \'k
|
|||||||
<string name="miui_uninstall_title">SukiSU Yöneticisi Kaldırılsın mı?</string>
|
<string name="miui_uninstall_title">SukiSU Yöneticisi Kaldırılsın mı?</string>
|
||||||
<string name="miui_uninstall_content">Kaldırma işlemine devam etmek, root erişiminizin temel işlevselliğini etkilemeyecektir. Root, bu yöneticiden bağımsız olarak çalışacak şekilde tasarlanmıştır.</string>
|
<string name="miui_uninstall_content">Kaldırma işlemine devam etmek, root erişiminizin temel işlevselliğini etkilemeyecektir. Root, bu yöneticiden bağımsız olarak çalışacak şekilde tasarlanmıştır.</string>
|
||||||
<string name="incompatible_kernel_msg">Mevcut yönetici bu çekirdekle uyumsuz! Lütfen çekirdeği %2$d veya daha yüksek bir sürüme yükseltin (mevcut sürüm %1$d)</string>
|
<string name="incompatible_kernel_msg">Mevcut yönetici bu çekirdekle uyumsuz! Lütfen çekirdeği %2$d veya daha yüksek bir sürüme yükseltin (mevcut sürüm %1$d)</string>
|
||||||
|
<string name="umount_path_manager">Bağlantı Kaldırma Yolu Yönetimi</string>
|
||||||
|
<string name="umount_path_manager_summary">Çekirdek bağlantı kaldırma yollarını yönetin</string>
|
||||||
|
<string name="umount_path_restart_notice">Değişikliklerin etkili olması için yeniden başlatma gereklidir. Sistem, yeni yapılandırmayı bir sonraki önyüklemede uygulayacaktır.</string>
|
||||||
|
<string name="add_umount_path">Bağlantı Kaldırma Yolu Ekle</string>
|
||||||
|
<string name="mount_path">Bağlama Yolu</string>
|
||||||
|
<string name="umount_flags">Bağlantı Kaldırma Bayrakları</string>
|
||||||
|
<string name="umount_flags_hint">0=Normal, 8=MNT_DETACH, -1=Otomatik</string>
|
||||||
|
<string name="flags">Bayraklar</string>
|
||||||
|
<string name="confirm_delete">Silmeyi Onayla</string>
|
||||||
|
<string name="confirm_delete_umount_path">%s yolunu silmek istediğinizden emin misiniz?</string>
|
||||||
|
<string name="umount_path_added">Yol eklendi, yeniden başlattıktan sonra geçerli olacak</string>
|
||||||
|
<string name="umount_path_removed">Yol kaldırıldı, yeniden başlattıktan sonra geçerli olacak</string>
|
||||||
|
<string name="operation_failed">İşlem başarısız oldu</string>
|
||||||
|
<string name="confirm_action">Eylemi Onayla</string>
|
||||||
|
<string name="confirm_clear_custom_paths">Tüm özel yolları temizlemek istediğinizden emin misiniz? (Varsayılan yollar korunacaktır)</string>
|
||||||
|
<string name="custom_paths_cleared">Özel yollar temizlendi</string>
|
||||||
|
<string name="clear_custom_paths">Özel Yolları Temizle</string>
|
||||||
|
<string name="apply_config">Yapılandırmayı Uygula</string>
|
||||||
|
<string name="config_applied">Yapılandırma çekirdeğe uygulandı</string>
|
||||||
|
<string name="mnt_detach">MNT_DETACH</string>
|
||||||
|
<string name="group_contains_apps">%d uygulama içeriyor</string>
|
||||||
|
<string name="settings_disable_sulog">Süper kullanıcı günlük kaydını devre dışı bırak</string>
|
||||||
|
<string name="settings_disable_sulog_summary">KernelSU süper kullanıcı erişim günlük kaydını devre dışı bırakır</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -377,39 +377,20 @@
|
|||||||
<!-- SuSFS Tab Titles -->
|
<!-- SuSFS Tab Titles -->
|
||||||
<string name="susfs_tab_basic_settings">Основні налаштування</string>
|
<string name="susfs_tab_basic_settings">Основні налаштування</string>
|
||||||
<string name="susfs_tab_sus_paths">Шляхи SUS</string>
|
<string name="susfs_tab_sus_paths">Шляхи SUS</string>
|
||||||
<string name="susfs_tab_sus_mounts">Монтування SUS</string>
|
|
||||||
<string name="susfs_tab_try_umount">Спроба відмонтування</string>
|
|
||||||
<string name="susfs_tab_path_settings">Налаштування шляхів</string>
|
<string name="susfs_tab_path_settings">Налаштування шляхів</string>
|
||||||
<string name="susfs_tab_enabled_features">Статус увімкнених функцій</string>
|
<string name="susfs_tab_enabled_features">Статус увімкнених функцій</string>
|
||||||
<!-- SuSFS Path Management -->
|
<!-- SuSFS Path Management -->
|
||||||
<string name="susfs_add_sus_path">Додати шлях SUS</string>
|
<string name="susfs_add_sus_path">Додати шлях SUS</string>
|
||||||
<string name="susfs_add_sus_mount">Додати монтування SUS</string>
|
|
||||||
<string name="susfs_add_try_umount">Додати спробу відмонтування</string>
|
|
||||||
<string name="susfs_sus_path_added_success">Шлях SUS успішно додано</string>
|
<string name="susfs_sus_path_added_success">Шлях SUS успішно додано</string>
|
||||||
<string name="susfs_path_not_found_error">Помилка: шлях не знайдено</string>
|
<string name="susfs_path_not_found_error">Помилка: шлях не знайдено</string>
|
||||||
<string name="susfs_path_label">Шлях</string>
|
<string name="susfs_path_label">Шлях</string>
|
||||||
<string name="susfs_mount_path_label">Шлях монтування</string>
|
|
||||||
<string name="susfs_path_placeholder">напр.: /system/addon.d</string>
|
<string name="susfs_path_placeholder">напр.: /system/addon.d</string>
|
||||||
<string name="susfs_no_paths_configured">Немає налаштованих шляхів SUS</string>
|
<string name="susfs_no_paths_configured">Немає налаштованих шляхів SUS</string>
|
||||||
<string name="susfs_no_mounts_configured">Немає налаштованих монтувань SUS</string>
|
|
||||||
<string name="susfs_no_umounts_configured">Немає налаштованих спроб відмонтування</string>
|
|
||||||
<!-- SuSFS Umount Mode -->
|
<!-- SuSFS Umount Mode -->
|
||||||
<string name="susfs_umount_mode_label">Режим відмонтування</string>
|
|
||||||
<string name="susfs_umount_mode_normal">Звичайне відмонтування (0)</string>
|
|
||||||
<string name="susfs_umount_mode_detach">Від\'єднане відмонтування (1)</string>
|
|
||||||
<string name="susfs_umount_mode_normal_short">Звичайний</string>
|
|
||||||
<string name="susfs_umount_mode_detach_short">Від\'єднаний</string>
|
|
||||||
<string name="susfs_umount_mode_display">Режим: %1$s (%2$s)</string>
|
|
||||||
<string name="susfs_try_umount_added_success">Шлях для спроби відмонтування успішно додано: %s</string>
|
|
||||||
<string name="susfs_try_umount_added_saved">Спроба збереження шляху відмонтування успішна: %s</string>
|
|
||||||
<!-- SuSFS Run Umount -->
|
<!-- SuSFS Run Umount -->
|
||||||
<!-- SuSFS Reset Categories -->
|
<!-- SuSFS Reset Categories -->
|
||||||
<string name="susfs_reset_paths_title">Скинути шляхи SUS</string>
|
<string name="susfs_reset_paths_title">Скинути шляхи SUS</string>
|
||||||
<string name="susfs_reset_paths_message">Це видалить усі конфігурації шляхів SUS. Ви впевнені, що хочете продовжити?</string>
|
<string name="susfs_reset_paths_message">Це видалить усі конфігурації шляхів SUS. Ви впевнені, що хочете продовжити?</string>
|
||||||
<string name="susfs_reset_mounts_title">Скинути монтування SUS</string>
|
|
||||||
<string name="susfs_reset_mounts_message">Це видалить усі конфігурації монтувань SUS. Ви впевнені, що хочете продовжити?</string>
|
|
||||||
<string name="susfs_reset_umounts_title">Скинути спроби відмонтування</string>
|
|
||||||
<string name="susfs_reset_umounts_message">Це видалить усі конфігурації спроб відмонтування. Ви впевнені, що хочете продовжити?</string>
|
|
||||||
<string name="susfs_reset_path_title">Скинути налаштування шляхів</string>
|
<string name="susfs_reset_path_title">Скинути налаштування шляхів</string>
|
||||||
<!-- SuSFS Path Settings -->
|
<!-- SuSFS Path Settings -->
|
||||||
<string name="susfs_android_data_path_label">Шлях до Android Data</string>
|
<string name="susfs_android_data_path_label">Шлях до Android Data</string>
|
||||||
@@ -423,18 +404,12 @@
|
|||||||
<string name="susfs_feature_disabled">Вимкнено</string>
|
<string name="susfs_feature_disabled">Вимкнено</string>
|
||||||
<!-- Feature Labels -->
|
<!-- Feature Labels -->
|
||||||
<string name="sus_path_feature_label">Підтримка шляхів SUS</string>
|
<string name="sus_path_feature_label">Підтримка шляхів SUS</string>
|
||||||
<string name="sus_mount_feature_label">Підтримка монтувань SUS</string>
|
|
||||||
<string name="try_umount_feature_label">Підтримка спроб відмонтування</string>
|
|
||||||
<string name="spoof_uname_feature_label">Підтримка підміни uname</string>
|
<string name="spoof_uname_feature_label">Підтримка підміни uname</string>
|
||||||
<string name="spoof_cmdline_feature_label">Підміна Cmdline/Bootconfig</string>
|
<string name="spoof_cmdline_feature_label">Підміна Cmdline/Bootconfig</string>
|
||||||
<string name="open_redirect_feature_label">Підтримка Open Redirect</string>
|
<string name="open_redirect_feature_label">Підтримка Open Redirect</string>
|
||||||
<string name="enable_log_feature_label">Підтримка логування</string>
|
<string name="enable_log_feature_label">Підтримка логування</string>
|
||||||
<string name="auto_default_mount_feature_label">Автоматичне монтування за замовчуванням</string>
|
|
||||||
<string name="auto_bind_mount_feature_label">Автоматичне прив\'язане монтування</string>
|
|
||||||
<string name="auto_try_umount_bind_feature_label">Автоматична спроба відмонтування прив\'язаного монтування</string>
|
|
||||||
<string name="hide_symbols_feature_label">Приховати символи KSU SUSFS</string>
|
<string name="hide_symbols_feature_label">Приховати символи KSU SUSFS</string>
|
||||||
<string name="sus_kstat_feature_label">Підтримка SUS Kstat</string>
|
<string name="sus_kstat_feature_label">Підтримка SUS Kstat</string>
|
||||||
<string name="sus_su_feature_label">Функція перемикання режиму SUS SU</string>
|
|
||||||
<!-- 可切换状态 -->
|
<!-- 可切换状态 -->
|
||||||
<string name="susfs_feature_configurable">Настроювані функції SuSFS</string>
|
<string name="susfs_feature_configurable">Настроювані функції SuSFS</string>
|
||||||
<string name="susfs_enable_log_label">Увімкнути лог SuSFS</string>
|
<string name="susfs_enable_log_label">Увімкнути лог SuSFS</string>
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="home">Trang chủ</string>
|
<string name="home">Trang chủ</string>
|
||||||
|
<string name="learn_more">Tìm hiểu thêm</string>
|
||||||
<string name="home_not_installed">Chưa cài đặt</string>
|
<string name="home_not_installed">Chưa cài đặt</string>
|
||||||
<string name="home_click_to_install">Nhấn để cài đặt</string>
|
<string name="home_click_to_install">Nhấn để cài đặt</string>
|
||||||
<string name="home_working">Đang hoạt động</string>
|
<string name="home_working">Đang hoạt động</string>
|
||||||
@@ -30,10 +31,11 @@
|
|||||||
<string name="reboot_userspace">Khởi động lại mềm</string>
|
<string name="reboot_userspace">Khởi động lại mềm</string>
|
||||||
<string name="reboot_recovery">Khởi động lại vào Recovery</string>
|
<string name="reboot_recovery">Khởi động lại vào Recovery</string>
|
||||||
<string name="reboot_bootloader">Khởi động lại vào Bootloader</string>
|
<string name="reboot_bootloader">Khởi động lại vào Bootloader</string>
|
||||||
<string name="reboot_download">Khởi động lại vào Download Mode</string>
|
<string name="reboot_download">Khởi động lại vào Download</string>
|
||||||
<string name="reboot_edl">Khởi động lại vào EDL</string>
|
<string name="reboot_edl">Khởi động lại vào EDL</string>
|
||||||
<string name="about">Thông tin</string>
|
<string name="about">Thông tin</string>
|
||||||
<string name="module_uninstall_confirm">Bạn có THẬT SỰ muốn gỡ cài đặt module %s không?</string>
|
<string name="module_uninstall_confirm">Bạn có THẬT SỰ muốn gỡ cài đặt module %s không?</string>
|
||||||
|
<string name="metamodule_uninstall_confirm">"Bạn có chắc chắn muốn gỡ cài đặt module %s không? Thao tác này sẽ ảnh hưởng đến tất cả các module và một số tính năng do siêu module cung cấp (chẳng hạn như mount) sẽ không còn hoạt động nữa."</string>
|
||||||
<string name="module_uninstall_success">%s đã được gỡ cài đặt</string>
|
<string name="module_uninstall_success">%s đã được gỡ cài đặt</string>
|
||||||
<string name="module_uninstall_failed">Gỡ cài đặt thất bại: %s</string>
|
<string name="module_uninstall_failed">Gỡ cài đặt thất bại: %s</string>
|
||||||
<string name="module_version">Phiên bản</string>
|
<string name="module_version">Phiên bản</string>
|
||||||
@@ -109,6 +111,9 @@
|
|||||||
<string name="install_inactive_slot">Cài đặt vào phân vùng chưa được sử dụng (Sau OTA)</string>
|
<string name="install_inactive_slot">Cài đặt vào phân vùng chưa được sử dụng (Sau OTA)</string>
|
||||||
<string name="install_inactive_slot_warning">Thiết bị của bạn sẽ **BUỘC** phải khởi động vào phân vùng chưa được sử dụng!\nChỉ sử dụng tùy chọn này sau khi cập nhật OTA hoàn tất.\nTiếp tục?</string>
|
<string name="install_inactive_slot_warning">Thiết bị của bạn sẽ **BUỘC** phải khởi động vào phân vùng chưa được sử dụng!\nChỉ sử dụng tùy chọn này sau khi cập nhật OTA hoàn tất.\nTiếp tục?</string>
|
||||||
<string name="install_next">Kế tiếp</string>
|
<string name="install_next">Kế tiếp</string>
|
||||||
|
<string name="install_select_partition">Chọn phân vùng</string>
|
||||||
|
<string name="install_upload_lkm_file">Sử dụng file LKM cục bộ</string>
|
||||||
|
<string name="install_only_support_ko_file">Chỉ hỗ trợ các file .ko</string>
|
||||||
<string name="select_file_tip">Phân vùng %1$s được khuyến nghị</string>
|
<string name="select_file_tip">Phân vùng %1$s được khuyến nghị</string>
|
||||||
<string name="select_kmi">Chọn KMI</string>
|
<string name="select_kmi">Chọn KMI</string>
|
||||||
<string name="settings_uninstall">Gỡ cài đặt</string>
|
<string name="settings_uninstall">Gỡ cài đặt</string>
|
||||||
@@ -161,6 +166,15 @@
|
|||||||
<string name="su_not_allowed">Không thể cấp quyền Superuser cho %s</string>
|
<string name="su_not_allowed">Không thể cấp quyền Superuser cho %s</string>
|
||||||
<string name="settings_disable_su">Vô hiệu hoá lệnh SU</string>
|
<string name="settings_disable_su">Vô hiệu hoá lệnh SU</string>
|
||||||
<string name="settings_disable_su_summary">Vô hiệu hoá khả năng thực thi lệnh SU để lấy quyền root (Những app đã cấp trước đó không bị ảnh hưởng)</string>
|
<string name="settings_disable_su_summary">Vô hiệu hoá khả năng thực thi lệnh SU để lấy quyền root (Những app đã cấp trước đó không bị ảnh hưởng)</string>
|
||||||
|
<string name="settings_disable_kernel_umount">Vô hiệu hoá kernel umount</string>
|
||||||
|
<string name="settings_disable_kernel_umount_summary">Vô hiệu hoá umount kernel-level được kiểm soát bởi KernelSU.</string>
|
||||||
|
<string name="settings_enable_enhanced_security">Kích hoạt bảo mật nâng cao</string>
|
||||||
|
<string name="settings_enable_enhanced_security_summary">Cho phép chính sách bảo mật chặt chẽ hơn.</string>
|
||||||
|
<string name="feature_status_unsupported_summary">Kernel không hỗ trợ tính năng này.</string>
|
||||||
|
<string name="feature_status_managed_summary">Tính năng này được quản lý bởi một module.</string>
|
||||||
|
<string name="settings_mode_default">Mặc định</string>
|
||||||
|
<string name="settings_mode_temp_enable">Tạm thời kích hoạt</string>
|
||||||
|
<string name="settings_mode_always_enable">Luôn luôn kích hoạt</string>
|
||||||
<string name="module_install_multiple_confirm_with_names">Bạn có chắc muốn cài đặt các module %1$d không? \n\n%2$s</string>
|
<string name="module_install_multiple_confirm_with_names">Bạn có chắc muốn cài đặt các module %1$d không? \n\n%2$s</string>
|
||||||
<string name="more_settings">Nhiều cài đặt hơn</string>
|
<string name="more_settings">Nhiều cài đặt hơn</string>
|
||||||
<string name="selinux">SELinux</string>
|
<string name="selinux">SELinux</string>
|
||||||
@@ -176,6 +190,8 @@
|
|||||||
<string name="hide_susfs_status_summary">Ẩn thông tin trạng thái SuSFS ở trang chủ</string>
|
<string name="hide_susfs_status_summary">Ẩn thông tin trạng thái SuSFS ở trang chủ</string>
|
||||||
<string name="hide_zygisk_implement">Ẩn trạng thái Zygisk</string>
|
<string name="hide_zygisk_implement">Ẩn trạng thái Zygisk</string>
|
||||||
<string name="hide_zygisk_implement_summary">Ẩn thông tin triển khai Zygisk trên trang chủ</string>
|
<string name="hide_zygisk_implement_summary">Ẩn thông tin triển khai Zygisk trên trang chủ</string>
|
||||||
|
<string name="hide_meta_module_implement">Ẩn trạng thái Siêu Module</string>
|
||||||
|
<string name="hide_meta_module_implement_summary">Ẩn thông tin triển khai Siêu Module trên trang chủ.</string>
|
||||||
<string name="hide_link_card">Ẩn trạng thái thẻ liên kết</string>
|
<string name="hide_link_card">Ẩn trạng thái thẻ liên kết</string>
|
||||||
<string name="hide_link_card_summary">Ẩn thông tin thẻ liên kết ở trang chủ</string>
|
<string name="hide_link_card_summary">Ẩn thông tin thẻ liên kết ở trang chủ</string>
|
||||||
<string name="hide_tag_card">Ẩn các nhãn module</string>
|
<string name="hide_tag_card">Ẩn các nhãn module</string>
|
||||||
@@ -185,6 +201,7 @@
|
|||||||
<string name="theme_light">Sáng</string>
|
<string name="theme_light">Sáng</string>
|
||||||
<string name="theme_dark">Tối</string>
|
<string name="theme_dark">Tối</string>
|
||||||
<string name="manual_hook">Hook thủ công</string>
|
<string name="manual_hook">Hook thủ công</string>
|
||||||
|
<string name="inline_hook">Inline Hook</string>
|
||||||
<string name="dynamic_color_title">Màu sắc động</string>
|
<string name="dynamic_color_title">Màu sắc động</string>
|
||||||
<string name="dynamic_color_summary">Sử dụng màu sắc động làm chủ đề hệ thống</string>
|
<string name="dynamic_color_summary">Sử dụng màu sắc động làm chủ đề hệ thống</string>
|
||||||
<string name="choose_theme_color">Chọn màu chủ đề</string>
|
<string name="choose_theme_color">Chọn màu chủ đề</string>
|
||||||
@@ -316,6 +333,8 @@
|
|||||||
<string name="module_failed_count">Cài đặt module %d thất bại</string>
|
<string name="module_failed_count">Cài đặt module %d thất bại</string>
|
||||||
<string name="module_download_error">Tải xuống module thất bại</string>
|
<string name="module_download_error">Tải xuống module thất bại</string>
|
||||||
<string name="kernel_flashing">Kernel Flashing</string>
|
<string name="kernel_flashing">Kernel Flashing</string>
|
||||||
|
<string name="warning_of_meta_module_title">Yêu cầu Siêu Module</string>
|
||||||
|
<string name="warning_of_meta_module_summary">Module này muốn mount /system, Siêu Module sẽ xử lý việc đó. Nếu không, nó có thể không hoạt động.</string>
|
||||||
<!-- 分类相关 -->
|
<!-- 分类相关 -->
|
||||||
<string name="category_all_apps">Tất cả</string>
|
<string name="category_all_apps">Tất cả</string>
|
||||||
<string name="category_root_apps">Root</string>
|
<string name="category_root_apps">Root</string>
|
||||||
@@ -378,39 +397,20 @@
|
|||||||
<!-- SuSFS Tab Titles -->
|
<!-- SuSFS Tab Titles -->
|
||||||
<string name="susfs_tab_basic_settings">Cài đặt cơ bản</string>
|
<string name="susfs_tab_basic_settings">Cài đặt cơ bản</string>
|
||||||
<string name="susfs_tab_sus_paths">Đường dẫn SuS</string>
|
<string name="susfs_tab_sus_paths">Đường dẫn SuS</string>
|
||||||
<string name="susfs_tab_sus_mounts">SuS Mount</string>
|
|
||||||
<string name="susfs_tab_try_umount">Try Umount</string>
|
|
||||||
<string name="susfs_tab_path_settings">Cài đặt Đường dẫn</string>
|
<string name="susfs_tab_path_settings">Cài đặt Đường dẫn</string>
|
||||||
<string name="susfs_tab_enabled_features">Trạng thái tính năng</string>
|
<string name="susfs_tab_enabled_features">Trạng thái tính năng</string>
|
||||||
<!-- SuSFS Path Management -->
|
<!-- SuSFS Path Management -->
|
||||||
<string name="susfs_add_sus_path">Thêm Đường dẫn SuS</string>
|
<string name="susfs_add_sus_path">Thêm Đường dẫn SuS</string>
|
||||||
<string name="susfs_add_sus_mount">Thêm SuS Mount</string>
|
|
||||||
<string name="susfs_add_try_umount">Thêm Try Umount</string>
|
|
||||||
<string name="susfs_sus_path_added_success">Đường dẫn SuS đã được thêm thành công</string>
|
<string name="susfs_sus_path_added_success">Đường dẫn SuS đã được thêm thành công</string>
|
||||||
<string name="susfs_path_not_found_error">Lỗi không tìm thấy đường dẫn</string>
|
<string name="susfs_path_not_found_error">Lỗi không tìm thấy đường dẫn</string>
|
||||||
<string name="susfs_path_label">Đường dẫn</string>
|
<string name="susfs_path_label">Đường dẫn</string>
|
||||||
<string name="susfs_mount_path_label">Đường dẫn Mount</string>
|
|
||||||
<string name="susfs_path_placeholder">Ví dụ: /system/addon.d</string>
|
<string name="susfs_path_placeholder">Ví dụ: /system/addon.d</string>
|
||||||
<string name="susfs_no_paths_configured">Không có Đường dẫn SuS nào được cấu hình</string>
|
<string name="susfs_no_paths_configured">Không có Đường dẫn SuS nào được cấu hình</string>
|
||||||
<string name="susfs_no_mounts_configured">Không có SuS Mount nào được cấu hình</string>
|
|
||||||
<string name="susfs_no_umounts_configured">Không có Try Umount nào được cấu hình</string>
|
|
||||||
<!-- SuSFS Umount Mode -->
|
<!-- SuSFS Umount Mode -->
|
||||||
<string name="susfs_umount_mode_label">Chế độ Umount</string>
|
|
||||||
<string name="susfs_umount_mode_normal">Normal Umount (0)</string>
|
|
||||||
<string name="susfs_umount_mode_detach">Detach Umount (1)</string>
|
|
||||||
<string name="susfs_umount_mode_normal_short">Normal</string>
|
|
||||||
<string name="susfs_umount_mode_detach_short">Detach</string>
|
|
||||||
<string name="susfs_umount_mode_display">Chế độ: %1$s (%2$s)</string>
|
|
||||||
<string name="susfs_try_umount_added_success">Đường dẫn Try Umount đã thêm thành công: %s</string>
|
|
||||||
<string name="susfs_try_umount_added_saved">Đường dẫn Try Umount đã lưu thành công: %s</string>
|
|
||||||
<!-- SuSFS Run Umount -->
|
<!-- SuSFS Run Umount -->
|
||||||
<!-- SuSFS Reset Categories -->
|
<!-- SuSFS Reset Categories -->
|
||||||
<string name="susfs_reset_paths_title">Đặt lại Đường dẫn SuS</string>
|
<string name="susfs_reset_paths_title">Đặt lại Đường dẫn SuS</string>
|
||||||
<string name="susfs_reset_paths_message">Thao tác này sẽ xóa tất cả các cấu hình Đường dẫn SuS. Bạn có chắc chắn muốn tiếp tục không?</string>
|
<string name="susfs_reset_paths_message">Thao tác này sẽ xóa tất cả các cấu hình Đường dẫn SuS. Bạn có chắc chắn muốn tiếp tục không?</string>
|
||||||
<string name="susfs_reset_mounts_title">Đặt lại SuS Mount</string>
|
|
||||||
<string name="susfs_reset_mounts_message">Thao tác này sẽ xóa tất cả các cấu hình SuS Mount. Bạn có chắc chắn muốn tiếp tục không?</string>
|
|
||||||
<string name="susfs_reset_umounts_title">Đặt lại Try Umount</string>
|
|
||||||
<string name="susfs_reset_umounts_message">Thao tác này sẽ xóa tất cả các cấu hình Try Umount. Bạn có chắc chắn muốn tiếp tục không?</string>
|
|
||||||
<string name="susfs_reset_path_title">Reset Cài đặt Đường dẫn</string>
|
<string name="susfs_reset_path_title">Reset Cài đặt Đường dẫn</string>
|
||||||
<!-- SuSFS Path Settings -->
|
<!-- SuSFS Path Settings -->
|
||||||
<string name="susfs_android_data_path_label">Đường dẫn Android Data</string>
|
<string name="susfs_android_data_path_label">Đường dẫn Android Data</string>
|
||||||
@@ -424,18 +424,12 @@
|
|||||||
<string name="susfs_feature_disabled">Đã tắt</string>
|
<string name="susfs_feature_disabled">Đã tắt</string>
|
||||||
<!-- Feature Labels -->
|
<!-- Feature Labels -->
|
||||||
<string name="sus_path_feature_label">Hỗ trợ Đường dẫn SuS</string>
|
<string name="sus_path_feature_label">Hỗ trợ Đường dẫn SuS</string>
|
||||||
<string name="sus_mount_feature_label">Hỗ trợ SuS Mount</string>
|
|
||||||
<string name="try_umount_feature_label">Hỗ trợ Try Umount</string>
|
|
||||||
<string name="spoof_uname_feature_label">Hỗ trợ giả mạo Uname</string>
|
<string name="spoof_uname_feature_label">Hỗ trợ giả mạo Uname</string>
|
||||||
<string name="spoof_cmdline_feature_label">Giả mạo Cmdline/Bootconfig</string>
|
<string name="spoof_cmdline_feature_label">Giả mạo Cmdline/Bootconfig</string>
|
||||||
<string name="open_redirect_feature_label">Mở hỗ trợ chuyển hướng</string>
|
<string name="open_redirect_feature_label">Mở hỗ trợ chuyển hướng</string>
|
||||||
<string name="enable_log_feature_label">Hỗ trợ ghi nhật ký</string>
|
<string name="enable_log_feature_label">Hỗ trợ ghi nhật ký</string>
|
||||||
<string name="auto_default_mount_feature_label">Tự động Mount mặc định</string>
|
|
||||||
<string name="auto_bind_mount_feature_label">Tự động Bind Mount</string>
|
|
||||||
<string name="auto_try_umount_bind_feature_label">Tự động Try Umount Bind Mount</string>
|
|
||||||
<string name="hide_symbols_feature_label">Ẩn biểu tượng KSU SuSFS</string>
|
<string name="hide_symbols_feature_label">Ẩn biểu tượng KSU SuSFS</string>
|
||||||
<string name="sus_kstat_feature_label">Hỗ trợ SuS Kstat</string>
|
<string name="sus_kstat_feature_label">Hỗ trợ SuS Kstat</string>
|
||||||
<string name="sus_su_feature_label">Chức năng chuyển đổi chế độ SuS SU</string>
|
|
||||||
<!-- 可切换状态 -->
|
<!-- 可切换状态 -->
|
||||||
<string name="susfs_feature_configurable">Nhấn để bật/tắt ghi nhật ký</string>
|
<string name="susfs_feature_configurable">Nhấn để bật/tắt ghi nhật ký</string>
|
||||||
<string name="susfs_enable_log_label">Kích hoạt nhật ký SuSFS</string>
|
<string name="susfs_enable_log_label">Kích hoạt nhật ký SuSFS</string>
|
||||||
@@ -531,8 +525,6 @@
|
|||||||
<string name="cleanup_residue">Dọn rác</string>
|
<string name="cleanup_residue">Dọn rác</string>
|
||||||
<string name="cleanup_residue_description">Dọn dẹp các file và folder còn sót lại của các module và công cụ (Có thể bị xóa nhầm, dẫn đến mất dữ liệu và không khởi động được)</string>
|
<string name="cleanup_residue_description">Dọn dẹp các file và folder còn sót lại của các module và công cụ (Có thể bị xóa nhầm, dẫn đến mất dữ liệu và không khởi động được)</string>
|
||||||
<string name="susfs_edit_sus_path">Chỉnh sửa Đường dẫn SuS</string>
|
<string name="susfs_edit_sus_path">Chỉnh sửa Đường dẫn SuS</string>
|
||||||
<string name="susfs_edit_sus_mount">Chỉnh sửa SuS Mount</string>
|
|
||||||
<string name="susfs_edit_try_umount">Chỉnh sửa Try Umount</string>
|
|
||||||
<string name="edit_kstat_statically_title">Chỉnh sửa cấu hình Kstat tĩnh</string>
|
<string name="edit_kstat_statically_title">Chỉnh sửa cấu hình Kstat tĩnh</string>
|
||||||
<string name="edit_kstat_path_title">Chỉnh sửa Đường dẫn Kstat</string>
|
<string name="edit_kstat_path_title">Chỉnh sửa Đường dẫn Kstat</string>
|
||||||
<string name="susfs_save">Lưu</string>
|
<string name="susfs_save">Lưu</string>
|
||||||
@@ -576,6 +568,7 @@
|
|||||||
<string name="no_active_manager">Trình quản lý đang không hoạt động</string>
|
<string name="no_active_manager">Trình quản lý đang không hoạt động</string>
|
||||||
<string name="default_signature">SukiSU</string>
|
<string name="default_signature">SukiSU</string>
|
||||||
<string name="home_zygisk_implement">Triển khai Zygisk</string>
|
<string name="home_zygisk_implement">Triển khai Zygisk</string>
|
||||||
|
<string name="home_meta_module_implement">Triển khai Siêu Module</string>
|
||||||
<!-- 循环路径相关 -->
|
<!-- 循环路径相关 -->
|
||||||
<string name="susfs_tab_sus_loop_paths">Đường dẫn Vòng lặp SuS</string>
|
<string name="susfs_tab_sus_loop_paths">Đường dẫn Vòng lặp SuS</string>
|
||||||
<string name="susfs_add_sus_loop_path">Thêm Đường dẫn Vòng lặp SuS</string>
|
<string name="susfs_add_sus_loop_path">Thêm Đường dẫn Vòng lặp SuS</string>
|
||||||
@@ -708,4 +701,28 @@ Bật: Kích hoạt tính năng giả mạo sus tcontext của \'su\' thành \'k
|
|||||||
<!-- MiUI Uninstall Desc -->
|
<!-- MiUI Uninstall Desc -->
|
||||||
<string name="miui_uninstall_title">Bạn muốn xoá tôi đi sao 😭</string>
|
<string name="miui_uninstall_title">Bạn muốn xoá tôi đi sao 😭</string>
|
||||||
<string name="miui_uninstall_content">Hừm, được rồi, tôi sẽ bị bạn gỡ cài đặt. Chức năng root sẽ không ngừng hoạt động chỉ vì bạn mất một Trình quản lý. Đừng lo chỉ Gỡ cài đặt Trình quản lý thôi thì không thể mất quyền truy cập root được đâu, zako~❤️</string>
|
<string name="miui_uninstall_content">Hừm, được rồi, tôi sẽ bị bạn gỡ cài đặt. Chức năng root sẽ không ngừng hoạt động chỉ vì bạn mất một Trình quản lý. Đừng lo chỉ Gỡ cài đặt Trình quản lý thôi thì không thể mất quyền truy cập root được đâu, zako~❤️</string>
|
||||||
|
<string name="incompatible_kernel_msg">Trình quản lý hiện tại không tương thích với Kernel này! Vui lòng nâng cấp Kernel lên phiên bản %2$d hoặc cao hơn (hiện tại là %1$d)</string>
|
||||||
|
<string name="umount_path_manager">Quản lý Đường dẫn Umount</string>
|
||||||
|
<string name="umount_path_manager_summary">Quản lý Đường dẫn Kernel Umount.</string>
|
||||||
|
<string name="umount_path_restart_notice">Cần khởi động lại để các thay đổi có hiệu lực. Hệ thống sẽ áp dụng cấu hình mới vào lần khởi động tiếp theo</string>
|
||||||
|
<string name="add_umount_path">Thêm Đường dẫn Umount</string>
|
||||||
|
<string name="mount_path">Đường dẫn Mount</string>
|
||||||
|
<string name="umount_flags">Umount Flags</string>
|
||||||
|
<string name="umount_flags_hint">0=Normal Umount, 8=MNT_DETACH, -1=Auto</string>
|
||||||
|
<string name="flags">Flags</string>
|
||||||
|
<string name="confirm_delete">Xác nhận Xoá?</string>
|
||||||
|
<string name="confirm_delete_umount_path">Bạn có chắc chắn muốn xóa đường dẫn %s không?</string>
|
||||||
|
<string name="umount_path_added">Đã thêm đường dẫn, sẽ có hiệu lực sau khi khởi động lại</string>
|
||||||
|
<string name="umount_path_removed">Đường dẫn đã bị xóa, sẽ có hiệu lực sau khi khởi động lại</string>
|
||||||
|
<string name="operation_failed">Thao tác thất bại</string>
|
||||||
|
<string name="confirm_action">Xác nhận Khởi chạy?</string>
|
||||||
|
<string name="confirm_clear_custom_paths">Bạn có chắc chắn muốn xóa tất cả các đường dẫn tùy chỉnh không? (Các đường dẫn mặc định sẽ được giữ nguyên)</string>
|
||||||
|
<string name="custom_paths_cleared">Đường dẫn tùy chỉnh đã được xóa</string>
|
||||||
|
<string name="clear_custom_paths">Xóa Đường dẫn Tùy chỉnh</string>
|
||||||
|
<string name="apply_config">Áp dụng Cấu hình</string>
|
||||||
|
<string name="config_applied">Cấu hình đã được áp dụng cho Kernel</string>
|
||||||
|
<string name="mnt_detach">MNT_DETACH</string>
|
||||||
|
<string name="group_contains_apps">Chứa %d ứng dụng</string>
|
||||||
|
<string name="settings_disable_sulog">Vô hiệu hoá ghi nhật ký Superuser</string>
|
||||||
|
<string name="settings_disable_sulog_summary">Vô hiệu hoá ghi nhật ký truy cập Superuser.</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
|
<string name="learn_more">了解更多</string>
|
||||||
<string name="home">主页</string>
|
<string name="home">主页</string>
|
||||||
<string name="home_not_installed">未安装</string>
|
<string name="home_not_installed">未安装</string>
|
||||||
<string name="home_click_to_install">点击安装</string>
|
<string name="home_click_to_install">点击安装</string>
|
||||||
@@ -34,6 +35,7 @@
|
|||||||
<string name="reboot_edl">重启到 EDL</string>
|
<string name="reboot_edl">重启到 EDL</string>
|
||||||
<string name="about">关于</string>
|
<string name="about">关于</string>
|
||||||
<string name="module_uninstall_confirm">确定要卸载模块 %s 吗?</string>
|
<string name="module_uninstall_confirm">确定要卸载模块 %s 吗?</string>
|
||||||
|
<string name="metamodule_uninstall_confirm">"您确定要卸载模块 %s 吗?此操作将影响所有模块,并且元模块提供的某些功能(如挂载)将不再工作。 "</string>
|
||||||
<string name="module_uninstall_success">%s 已卸载</string>
|
<string name="module_uninstall_success">%s 已卸载</string>
|
||||||
<string name="module_uninstall_failed">卸载失败:%s</string>
|
<string name="module_uninstall_failed">卸载失败:%s</string>
|
||||||
<string name="module_version">版本</string>
|
<string name="module_version">版本</string>
|
||||||
@@ -168,6 +170,8 @@
|
|||||||
<string name="settings_disable_kernel_umount_summary">关闭 KernelSU 控制的内核级 umount 行为。</string>
|
<string name="settings_disable_kernel_umount_summary">关闭 KernelSU 控制的内核级 umount 行为。</string>
|
||||||
<string name="settings_enable_enhanced_security">增强安全性</string>
|
<string name="settings_enable_enhanced_security">增强安全性</string>
|
||||||
<string name="settings_enable_enhanced_security_summary">使用更严格的安全策略。</string>
|
<string name="settings_enable_enhanced_security_summary">使用更严格的安全策略。</string>
|
||||||
|
<string name="feature_status_unsupported_summary">内核不支持此功能。</string>
|
||||||
|
<string name="feature_status_managed_summary">此功能由模块管理。</string>
|
||||||
<string name="settings_mode_default">默认</string>
|
<string name="settings_mode_default">默认</string>
|
||||||
<string name="settings_mode_temp_enable">临时启用</string>
|
<string name="settings_mode_temp_enable">临时启用</string>
|
||||||
<string name="settings_mode_always_enable">始终启用</string>
|
<string name="settings_mode_always_enable">始终启用</string>
|
||||||
@@ -186,10 +190,12 @@
|
|||||||
<string name="hide_susfs_status_summary">隐藏主页上的 SuSFS 状态信息</string>
|
<string name="hide_susfs_status_summary">隐藏主页上的 SuSFS 状态信息</string>
|
||||||
<string name="hide_zygisk_implement">隐藏 Zygisk 状态信息</string>
|
<string name="hide_zygisk_implement">隐藏 Zygisk 状态信息</string>
|
||||||
<string name="hide_zygisk_implement_summary">隐藏主页上的 Zygisk 实现状态信息</string>
|
<string name="hide_zygisk_implement_summary">隐藏主页上的 Zygisk 实现状态信息</string>
|
||||||
|
<string name="hide_meta_module_implement">隐藏元模块状态信息</string>
|
||||||
|
<string name="hide_meta_module_implement_summary">隐藏主页上的元模块实现状态信息</string>
|
||||||
<string name="hide_link_card">隐藏链接卡片</string>
|
<string name="hide_link_card">隐藏链接卡片</string>
|
||||||
<string name="hide_link_card_summary">隐藏主页上的链接卡片信息</string>
|
<string name="hide_link_card_summary">隐藏主页上的链接卡片信息</string>
|
||||||
<string name="hide_tag_card">隐藏模块标签行</string>
|
<string name="hide_tag_card">隐藏模块标签行</string>
|
||||||
<string name="hide_tag_card_summary">隐藏模块卡片中的文件夹名称和大小标签</string>
|
<string name="hide_tag_card_summary">隐藏模块卡片中的文件夹名称、大小标签和是否为元模块</string>
|
||||||
<string name="theme_mode">主题模式</string>
|
<string name="theme_mode">主题模式</string>
|
||||||
<string name="theme_follow_system">跟随系统</string>
|
<string name="theme_follow_system">跟随系统</string>
|
||||||
<string name="theme_light">浅色</string>
|
<string name="theme_light">浅色</string>
|
||||||
@@ -326,6 +332,8 @@
|
|||||||
<string name="module_failed_count">%d 个模块安装失败</string>
|
<string name="module_failed_count">%d 个模块安装失败</string>
|
||||||
<string name="module_download_error">模块下载失败</string>
|
<string name="module_download_error">模块下载失败</string>
|
||||||
<string name="kernel_flashing">内核刷写</string>
|
<string name="kernel_flashing">内核刷写</string>
|
||||||
|
<string name="warning_of_meta_module_title">需要元模块</string>
|
||||||
|
<string name="warning_of_meta_module_summary">这个模块需要挂载一些文件。需要安装元模块,使它正常工作</string>
|
||||||
<!-- 分类相关 -->
|
<!-- 分类相关 -->
|
||||||
<string name="category_all_apps">全部</string>
|
<string name="category_all_apps">全部</string>
|
||||||
<string name="category_root_apps">Root</string>
|
<string name="category_root_apps">Root</string>
|
||||||
@@ -388,39 +396,20 @@
|
|||||||
<!-- SuSFS Tab Titles -->
|
<!-- SuSFS Tab Titles -->
|
||||||
<string name="susfs_tab_basic_settings">基本设置</string>
|
<string name="susfs_tab_basic_settings">基本设置</string>
|
||||||
<string name="susfs_tab_sus_paths">SuS 路径</string>
|
<string name="susfs_tab_sus_paths">SuS 路径</string>
|
||||||
<string name="susfs_tab_sus_mounts">SuS 挂载</string>
|
|
||||||
<string name="susfs_tab_try_umount">尝试卸载</string>
|
|
||||||
<string name="susfs_tab_path_settings">路径设置</string>
|
<string name="susfs_tab_path_settings">路径设置</string>
|
||||||
<string name="susfs_tab_enabled_features">启用功能状态</string>
|
<string name="susfs_tab_enabled_features">启用功能状态</string>
|
||||||
<!-- SuSFS Path Management -->
|
<!-- SuSFS Path Management -->
|
||||||
<string name="susfs_add_sus_path">添加 SuS 路径</string>
|
<string name="susfs_add_sus_path">添加 SuS 路径</string>
|
||||||
<string name="susfs_add_sus_mount">添加 SuS 挂载</string>
|
|
||||||
<string name="susfs_add_try_umount">添加尝试卸载</string>
|
|
||||||
<string name="susfs_sus_path_added_success">SuS 路径添加成功</string>
|
<string name="susfs_sus_path_added_success">SuS 路径添加成功</string>
|
||||||
<string name="susfs_path_not_found_error">路径未找到错误</string>
|
<string name="susfs_path_not_found_error">路径未找到错误</string>
|
||||||
<string name="susfs_path_label">路径</string>
|
<string name="susfs_path_label">路径</string>
|
||||||
<string name="susfs_mount_path_label">挂载路径</string>
|
|
||||||
<string name="susfs_path_placeholder">例如: /system/addon.d</string>
|
<string name="susfs_path_placeholder">例如: /system/addon.d</string>
|
||||||
<string name="susfs_no_paths_configured">暂无 SuS 路径配置</string>
|
<string name="susfs_no_paths_configured">暂无 SuS 路径配置</string>
|
||||||
<string name="susfs_no_mounts_configured">暂无 SuS 挂载配置</string>
|
|
||||||
<string name="susfs_no_umounts_configured">暂无尝试卸载配置</string>
|
|
||||||
<!-- SuSFS Umount Mode -->
|
<!-- SuSFS Umount Mode -->
|
||||||
<string name="susfs_umount_mode_label">卸载模式</string>
|
|
||||||
<string name="susfs_umount_mode_normal">普通卸载(0)</string>
|
|
||||||
<string name="susfs_umount_mode_detach">分离卸载(1)</string>
|
|
||||||
<string name="susfs_umount_mode_normal_short">普通</string>
|
|
||||||
<string name="susfs_umount_mode_detach_short">分离</string>
|
|
||||||
<string name="susfs_umount_mode_display">模式: %1$s (%2$s)</string>
|
|
||||||
<string name="susfs_try_umount_added_success">尝试 umount 路径添加成功: %s</string>
|
|
||||||
<string name="susfs_try_umount_added_saved">尝试 umount 路径保存成功: %s</string>
|
|
||||||
<!-- SuSFS Run Umount -->
|
<!-- SuSFS Run Umount -->
|
||||||
<!-- SuSFS Reset Categories -->
|
<!-- SuSFS Reset Categories -->
|
||||||
<string name="susfs_reset_paths_title">重置 SuS 路径</string>
|
<string name="susfs_reset_paths_title">重置 SuS 路径</string>
|
||||||
<string name="susfs_reset_paths_message">这将清除所有 SuS 路径配置,确定要继续吗?</string>
|
<string name="susfs_reset_paths_message">这将清除所有 SuS 路径配置,确定要继续吗?</string>
|
||||||
<string name="susfs_reset_mounts_title">重置 SuS 挂载</string>
|
|
||||||
<string name="susfs_reset_mounts_message">这将清除所有 SuS 挂载配置,确定要继续吗?</string>
|
|
||||||
<string name="susfs_reset_umounts_title">重置尝试卸载</string>
|
|
||||||
<string name="susfs_reset_umounts_message">这将清除所有尝试卸载配置,确定要继续吗?</string>
|
|
||||||
<string name="susfs_reset_path_title">重置路径设置</string>
|
<string name="susfs_reset_path_title">重置路径设置</string>
|
||||||
<!-- SuSFS Path Settings -->
|
<!-- SuSFS Path Settings -->
|
||||||
<string name="susfs_android_data_path_label">Android Data 路径</string>
|
<string name="susfs_android_data_path_label">Android Data 路径</string>
|
||||||
@@ -434,18 +423,12 @@
|
|||||||
<string name="susfs_feature_disabled">已禁用</string>
|
<string name="susfs_feature_disabled">已禁用</string>
|
||||||
<!-- Feature Labels -->
|
<!-- Feature Labels -->
|
||||||
<string name="sus_path_feature_label">SuS 路径支持</string>
|
<string name="sus_path_feature_label">SuS 路径支持</string>
|
||||||
<string name="sus_mount_feature_label">SuS 挂载支持</string>
|
|
||||||
<string name="try_umount_feature_label">尝试卸载支持</string>
|
|
||||||
<string name="spoof_uname_feature_label">欺骗 uname 支持</string>
|
<string name="spoof_uname_feature_label">欺骗 uname 支持</string>
|
||||||
<string name="spoof_cmdline_feature_label">欺骗 Cmdline/Bootconfig</string>
|
<string name="spoof_cmdline_feature_label">欺骗 Cmdline/Bootconfig</string>
|
||||||
<string name="open_redirect_feature_label">开放重定向支持</string>
|
<string name="open_redirect_feature_label">开放重定向支持</string>
|
||||||
<string name="enable_log_feature_label">日志记录支持</string>
|
<string name="enable_log_feature_label">日志记录支持</string>
|
||||||
<string name="auto_default_mount_feature_label">自动默认挂载</string>
|
|
||||||
<string name="auto_bind_mount_feature_label">自动绑定挂载</string>
|
|
||||||
<string name="auto_try_umount_bind_feature_label">自动尝试卸载绑定挂载</string>
|
|
||||||
<string name="hide_symbols_feature_label">隐藏 KSU SuSFS 符号</string>
|
<string name="hide_symbols_feature_label">隐藏 KSU SuSFS 符号</string>
|
||||||
<string name="sus_kstat_feature_label">SuS Kstat 支持</string>
|
<string name="sus_kstat_feature_label">SuS Kstat 支持</string>
|
||||||
<string name="sus_su_feature_label">SuS SU 模式切换功能</string>
|
|
||||||
<!-- 可切换状态 -->
|
<!-- 可切换状态 -->
|
||||||
<string name="susfs_feature_configurable">可配置的 SuSFS 功能</string>
|
<string name="susfs_feature_configurable">可配置的 SuSFS 功能</string>
|
||||||
<string name="susfs_enable_log_label">SuSFS 启用日志</string>
|
<string name="susfs_enable_log_label">SuSFS 启用日志</string>
|
||||||
@@ -541,8 +524,6 @@
|
|||||||
<string name="cleanup_residue">清理工具残留</string>
|
<string name="cleanup_residue">清理工具残留</string>
|
||||||
<string name="cleanup_residue_description">清理各种模块以及工具的残留文件和目录(可能会误删导致丢失以及无法启动,谨慎使用)</string>
|
<string name="cleanup_residue_description">清理各种模块以及工具的残留文件和目录(可能会误删导致丢失以及无法启动,谨慎使用)</string>
|
||||||
<string name="susfs_edit_sus_path">编辑 SuS 路径</string>
|
<string name="susfs_edit_sus_path">编辑 SuS 路径</string>
|
||||||
<string name="susfs_edit_sus_mount">编辑 SuS 挂载</string>
|
|
||||||
<string name="susfs_edit_try_umount">编辑尝试卸载</string>
|
|
||||||
<string name="edit_kstat_statically_title">编辑 Kstat 静态配置</string>
|
<string name="edit_kstat_statically_title">编辑 Kstat 静态配置</string>
|
||||||
<string name="edit_kstat_path_title">编辑 Kstat 路径</string>
|
<string name="edit_kstat_path_title">编辑 Kstat 路径</string>
|
||||||
<string name="susfs_save">保存</string>
|
<string name="susfs_save">保存</string>
|
||||||
@@ -586,6 +567,7 @@
|
|||||||
<string name="no_active_manager">无活跃管理器</string>
|
<string name="no_active_manager">无活跃管理器</string>
|
||||||
<string name="default_signature">SukiSU</string>
|
<string name="default_signature">SukiSU</string>
|
||||||
<string name="home_zygisk_implement">Zygisk 实现</string>
|
<string name="home_zygisk_implement">Zygisk 实现</string>
|
||||||
|
<string name="home_meta_module_implement">元模块实现</string>
|
||||||
<!-- 循环路径相关 -->
|
<!-- 循环路径相关 -->
|
||||||
<string name="susfs_tab_sus_loop_paths">SuS 循环路径</string>
|
<string name="susfs_tab_sus_loop_paths">SuS 循环路径</string>
|
||||||
<string name="susfs_add_sus_loop_path">添加 SuS 循环路径</string>
|
<string name="susfs_add_sus_loop_path">添加 SuS 循环路径</string>
|
||||||
@@ -727,7 +709,6 @@
|
|||||||
<string name="umount_flags">卸载标志</string>
|
<string name="umount_flags">卸载标志</string>
|
||||||
<string name="umount_flags_hint">0=正常卸载, 8=MNT_DETACH, -1=自动</string>
|
<string name="umount_flags_hint">0=正常卸载, 8=MNT_DETACH, -1=自动</string>
|
||||||
<string name="flags">标志</string>
|
<string name="flags">标志</string>
|
||||||
<string name="default_entry">默认条目</string>
|
|
||||||
<string name="confirm_delete">确认删除</string>
|
<string name="confirm_delete">确认删除</string>
|
||||||
<string name="confirm_delete_umount_path">确定要删除路径 %s 吗?</string>
|
<string name="confirm_delete_umount_path">确定要删除路径 %s 吗?</string>
|
||||||
<string name="umount_path_added">路径已添加,重启后生效</string>
|
<string name="umount_path_added">路径已添加,重启后生效</string>
|
||||||
|
|||||||
@@ -376,39 +376,20 @@
|
|||||||
<!-- SuSFS Tab Titles -->
|
<!-- SuSFS Tab Titles -->
|
||||||
<string name="susfs_tab_basic_settings">基本配置</string>
|
<string name="susfs_tab_basic_settings">基本配置</string>
|
||||||
<string name="susfs_tab_sus_paths">SuS 路徑</string>
|
<string name="susfs_tab_sus_paths">SuS 路徑</string>
|
||||||
<string name="susfs_tab_sus_mounts">SuS 掛載</string>
|
|
||||||
<string name="susfs_tab_try_umount">嘗試卸載</string>
|
|
||||||
<string name="susfs_tab_path_settings">路徑配置</string>
|
<string name="susfs_tab_path_settings">路徑配置</string>
|
||||||
<string name="susfs_tab_enabled_features">啟用功能狀態</string>
|
<string name="susfs_tab_enabled_features">啟用功能狀態</string>
|
||||||
<!-- SuSFS Path Management -->
|
<!-- SuSFS Path Management -->
|
||||||
<string name="susfs_add_sus_path">添加 SuS 路徑</string>
|
<string name="susfs_add_sus_path">添加 SuS 路徑</string>
|
||||||
<string name="susfs_add_sus_mount">添加 SuS 掛載</string>
|
|
||||||
<string name="susfs_add_try_umount">嘗試添加卸載</string>
|
|
||||||
<string name="susfs_sus_path_added_success">SuS 路徑添加成功</string>
|
<string name="susfs_sus_path_added_success">SuS 路徑添加成功</string>
|
||||||
<string name="susfs_path_not_found_error">錯誤冇此找到路徑</string>
|
<string name="susfs_path_not_found_error">錯誤冇此找到路徑</string>
|
||||||
<string name="susfs_path_label">路徑</string>
|
<string name="susfs_path_label">路徑</string>
|
||||||
<string name="susfs_mount_path_label">掛載路徑</string>
|
|
||||||
<string name="susfs_path_placeholder">例如: /system/addon.d</string>
|
<string name="susfs_path_placeholder">例如: /system/addon.d</string>
|
||||||
<string name="susfs_no_paths_configured">暫冇 SuS 路徑配置</string>
|
<string name="susfs_no_paths_configured">暫冇 SuS 路徑配置</string>
|
||||||
<string name="susfs_no_mounts_configured">暫冇 SuS 掛載配置</string>
|
|
||||||
<string name="susfs_no_umounts_configured">暫冇嘗試卸載配置</string>
|
|
||||||
<!-- SuSFS Umount Mode -->
|
<!-- SuSFS Umount Mode -->
|
||||||
<string name="susfs_umount_mode_label">卸載模式</string>
|
|
||||||
<string name="susfs_umount_mode_normal">普通卸載 (0)</string>
|
|
||||||
<string name="susfs_umount_mode_detach">分離卸載 (1)</string>
|
|
||||||
<string name="susfs_umount_mode_normal_short">普通</string>
|
|
||||||
<string name="susfs_umount_mode_detach_short">分離</string>
|
|
||||||
<string name="susfs_umount_mode_display">模式: %1$s (%2$s)</string>
|
|
||||||
<string name="susfs_try_umount_added_success">嘗試 umount 路徑添加成功: %s</string>
|
|
||||||
<string name="susfs_try_umount_added_saved">嘗試 umount 路徑存儲成功: %s</string>
|
|
||||||
<!-- SuSFS Run Umount -->
|
<!-- SuSFS Run Umount -->
|
||||||
<!-- SuSFS Reset Categories -->
|
<!-- SuSFS Reset Categories -->
|
||||||
<string name="susfs_reset_paths_title">重置 SuS 路徑</string>
|
<string name="susfs_reset_paths_title">重置 SuS 路徑</string>
|
||||||
<string name="susfs_reset_paths_message">這將清除所有 SuS 路徑配置,確定要繼續嗎?</string>
|
<string name="susfs_reset_paths_message">這將清除所有 SuS 路徑配置,確定要繼續嗎?</string>
|
||||||
<string name="susfs_reset_mounts_title">重置 SuS 掛載</string>
|
|
||||||
<string name="susfs_reset_mounts_message">這將清除所有 SuS 掛載配置,確定要繼續嗎?</string>
|
|
||||||
<string name="susfs_reset_umounts_title">重置嘗試卸載</string>
|
|
||||||
<string name="susfs_reset_umounts_message">這將清除所有嘗試卸載配置,確定要繼續嗎?</string>
|
|
||||||
<string name="susfs_reset_path_title">重置路徑配置</string>
|
<string name="susfs_reset_path_title">重置路徑配置</string>
|
||||||
<!-- SuSFS Path Settings -->
|
<!-- SuSFS Path Settings -->
|
||||||
<string name="susfs_android_data_path_label">Android Data 路徑</string>
|
<string name="susfs_android_data_path_label">Android Data 路徑</string>
|
||||||
@@ -422,18 +403,12 @@
|
|||||||
<string name="susfs_feature_disabled">已禁用</string>
|
<string name="susfs_feature_disabled">已禁用</string>
|
||||||
<!-- Feature Labels -->
|
<!-- Feature Labels -->
|
||||||
<string name="sus_path_feature_label">SuS 路徑支援</string>
|
<string name="sus_path_feature_label">SuS 路徑支援</string>
|
||||||
<string name="sus_mount_feature_label">SuS 掛載支援</string>
|
|
||||||
<string name="try_umount_feature_label">嘗試卸載支援</string>
|
|
||||||
<string name="spoof_uname_feature_label">欺騙 uname 支援</string>
|
<string name="spoof_uname_feature_label">欺騙 uname 支援</string>
|
||||||
<string name="spoof_cmdline_feature_label">欺騙 Cmdline/Bootconfig</string>
|
<string name="spoof_cmdline_feature_label">欺騙 Cmdline/Bootconfig</string>
|
||||||
<string name="open_redirect_feature_label">開放重定向支援</string>
|
<string name="open_redirect_feature_label">開放重定向支援</string>
|
||||||
<string name="enable_log_feature_label">日誌記錄支援</string>
|
<string name="enable_log_feature_label">日誌記錄支援</string>
|
||||||
<string name="auto_default_mount_feature_label">自動默認掛載</string>
|
|
||||||
<string name="auto_bind_mount_feature_label">自動綁定掛載</string>
|
|
||||||
<string name="auto_try_umount_bind_feature_label">自動嘗試卸載綁定掛載</string>
|
|
||||||
<string name="hide_symbols_feature_label">隱藏 KSU SuSFS 符號</string>
|
<string name="hide_symbols_feature_label">隱藏 KSU SuSFS 符號</string>
|
||||||
<string name="sus_kstat_feature_label">SuS Kstat 支援</string>
|
<string name="sus_kstat_feature_label">SuS Kstat 支援</string>
|
||||||
<string name="sus_su_feature_label">SuS SU 模式切換功能</string>
|
|
||||||
<!-- 可切换状态 -->
|
<!-- 可切换状态 -->
|
||||||
<string name="susfs_feature_configurable">可配置嘅 SuSFS 功能</string>
|
<string name="susfs_feature_configurable">可配置嘅 SuSFS 功能</string>
|
||||||
<string name="susfs_enable_log_label">SuSFS 啟用日誌</string>
|
<string name="susfs_enable_log_label">SuSFS 啟用日誌</string>
|
||||||
@@ -529,8 +504,6 @@
|
|||||||
<string name="cleanup_residue">清理工具殘留</string>
|
<string name="cleanup_residue">清理工具殘留</string>
|
||||||
<string name="cleanup_residue_description">清理各種模組以及工具嘅殘留檔案同目錄(可能會誤刪導致丟失以及無法啟動,謹慎使用)</string>
|
<string name="cleanup_residue_description">清理各種模組以及工具嘅殘留檔案同目錄(可能會誤刪導致丟失以及無法啟動,謹慎使用)</string>
|
||||||
<string name="susfs_edit_sus_path">編輯 SuS 路徑</string>
|
<string name="susfs_edit_sus_path">編輯 SuS 路徑</string>
|
||||||
<string name="susfs_edit_sus_mount">編輯 SuS 掛載</string>
|
|
||||||
<string name="susfs_edit_try_umount">編輯嘗試解除安裝</string>
|
|
||||||
<string name="edit_kstat_statically_title">編輯 Kstat 靜態配置</string>
|
<string name="edit_kstat_statically_title">編輯 Kstat 靜態配置</string>
|
||||||
<string name="edit_kstat_path_title">編輯 Kstat 路徑</string>
|
<string name="edit_kstat_path_title">編輯 Kstat 路徑</string>
|
||||||
<string name="susfs_save">儲存</string>
|
<string name="susfs_save">儲存</string>
|
||||||
|
|||||||
@@ -378,39 +378,20 @@
|
|||||||
<!-- SuSFS Tab Titles -->
|
<!-- SuSFS Tab Titles -->
|
||||||
<string name="susfs_tab_basic_settings">基本設定</string>
|
<string name="susfs_tab_basic_settings">基本設定</string>
|
||||||
<string name="susfs_tab_sus_paths">SuS 路徑</string>
|
<string name="susfs_tab_sus_paths">SuS 路徑</string>
|
||||||
<string name="susfs_tab_sus_mounts">SuS 掛載</string>
|
|
||||||
<string name="susfs_tab_try_umount">嘗試卸載</string>
|
|
||||||
<string name="susfs_tab_path_settings">路徑設定</string>
|
<string name="susfs_tab_path_settings">路徑設定</string>
|
||||||
<string name="susfs_tab_enabled_features">啟用功能狀態</string>
|
<string name="susfs_tab_enabled_features">啟用功能狀態</string>
|
||||||
<!-- SuSFS Path Management -->
|
<!-- SuSFS Path Management -->
|
||||||
<string name="susfs_add_sus_path">新增 SuS 路徑</string>
|
<string name="susfs_add_sus_path">新增 SuS 路徑</string>
|
||||||
<string name="susfs_add_sus_mount">新增 SuS 掛載</string>
|
|
||||||
<string name="susfs_add_try_umount">新增嘗試卸載</string>
|
|
||||||
<string name="susfs_sus_path_added_success">成功添加 SuS 路径</string>
|
<string name="susfs_sus_path_added_success">成功添加 SuS 路径</string>
|
||||||
<string name="susfs_path_not_found_error">未找到路径</string>
|
<string name="susfs_path_not_found_error">未找到路径</string>
|
||||||
<string name="susfs_path_label">路徑</string>
|
<string name="susfs_path_label">路徑</string>
|
||||||
<string name="susfs_mount_path_label">掛載路徑</string>
|
|
||||||
<string name="susfs_path_placeholder">例如:/system/addon.d</string>
|
<string name="susfs_path_placeholder">例如:/system/addon.d</string>
|
||||||
<string name="susfs_no_paths_configured">暫無 SuS 路徑設定</string>
|
<string name="susfs_no_paths_configured">暫無 SuS 路徑設定</string>
|
||||||
<string name="susfs_no_mounts_configured">暫無 SuS 掛載設定</string>
|
|
||||||
<string name="susfs_no_umounts_configured">暫無嘗試卸載設定</string>
|
|
||||||
<!-- SuSFS Umount Mode -->
|
<!-- SuSFS Umount Mode -->
|
||||||
<string name="susfs_umount_mode_label">卸載模式</string>
|
|
||||||
<string name="susfs_umount_mode_normal">一般卸載 (0)</string>
|
|
||||||
<string name="susfs_umount_mode_detach">分離卸載 (1)</string>
|
|
||||||
<string name="susfs_umount_mode_normal_short">一般</string>
|
|
||||||
<string name="susfs_umount_mode_detach_short">分離</string>
|
|
||||||
<string name="susfs_umount_mode_display">模式:%1$s (%2$s)</string>
|
|
||||||
<string name="susfs_try_umount_added_success">嘗試 umount 路徑新增成功: %s</string>
|
|
||||||
<string name="susfs_try_umount_added_saved">嘗試 umount 路徑儲存成功: %s</string>
|
|
||||||
<!-- SuSFS Run Umount -->
|
<!-- SuSFS Run Umount -->
|
||||||
<!-- SuSFS Reset Categories -->
|
<!-- SuSFS Reset Categories -->
|
||||||
<string name="susfs_reset_paths_title">重設 SuS 路徑</string>
|
<string name="susfs_reset_paths_title">重設 SuS 路徑</string>
|
||||||
<string name="susfs_reset_paths_message">這將清除所有 SuS 路徑設定,確定要繼續嗎?</string>
|
<string name="susfs_reset_paths_message">這將清除所有 SuS 路徑設定,確定要繼續嗎?</string>
|
||||||
<string name="susfs_reset_mounts_title">重設 SuS 掛載</string>
|
|
||||||
<string name="susfs_reset_mounts_message">這將清除所有 SuS 掛載設定,確定要繼續嗎?</string>
|
|
||||||
<string name="susfs_reset_umounts_title">重設嘗試卸載</string>
|
|
||||||
<string name="susfs_reset_umounts_message">這將清除所有嘗試卸載設定,確定要繼續嗎?</string>
|
|
||||||
<string name="susfs_reset_path_title">重置路徑設定</string>
|
<string name="susfs_reset_path_title">重置路徑設定</string>
|
||||||
<!-- SuSFS Path Settings -->
|
<!-- SuSFS Path Settings -->
|
||||||
<string name="susfs_android_data_path_label">Android Data 路徑</string>
|
<string name="susfs_android_data_path_label">Android Data 路徑</string>
|
||||||
@@ -424,18 +405,12 @@
|
|||||||
<string name="susfs_feature_disabled">已停用</string>
|
<string name="susfs_feature_disabled">已停用</string>
|
||||||
<!-- Feature Labels -->
|
<!-- Feature Labels -->
|
||||||
<string name="sus_path_feature_label">SuS 路徑支援</string>
|
<string name="sus_path_feature_label">SuS 路徑支援</string>
|
||||||
<string name="sus_mount_feature_label">SuS 掛載支援</string>
|
|
||||||
<string name="try_umount_feature_label">嘗試卸載支援</string>
|
|
||||||
<string name="spoof_uname_feature_label">偽裝 uname 支援</string>
|
<string name="spoof_uname_feature_label">偽裝 uname 支援</string>
|
||||||
<string name="spoof_cmdline_feature_label">偽裝 Cmdline/Bootconfig</string>
|
<string name="spoof_cmdline_feature_label">偽裝 Cmdline/Bootconfig</string>
|
||||||
<string name="open_redirect_feature_label">開放重定向支援</string>
|
<string name="open_redirect_feature_label">開放重定向支援</string>
|
||||||
<string name="enable_log_feature_label">日誌記錄支援</string>
|
<string name="enable_log_feature_label">日誌記錄支援</string>
|
||||||
<string name="auto_default_mount_feature_label">自動預設掛載</string>
|
|
||||||
<string name="auto_bind_mount_feature_label">自動綁定掛載</string>
|
|
||||||
<string name="auto_try_umount_bind_feature_label">自動嘗試卸載綁定掛載</string>
|
|
||||||
<string name="hide_symbols_feature_label">隱藏 KSU SuSFS 符號</string>
|
<string name="hide_symbols_feature_label">隱藏 KSU SuSFS 符號</string>
|
||||||
<string name="sus_kstat_feature_label">SuS 內核統計支援</string>
|
<string name="sus_kstat_feature_label">SuS 內核統計支援</string>
|
||||||
<string name="sus_su_feature_label">SuS SU 模式切換功能</string>
|
|
||||||
<!-- 可切换状态 -->
|
<!-- 可切换状态 -->
|
||||||
<string name="susfs_feature_configurable">可設定的 SuSFS 功能</string>
|
<string name="susfs_feature_configurable">可設定的 SuSFS 功能</string>
|
||||||
<string name="susfs_enable_log_label">SuSFS 啟用日誌</string>
|
<string name="susfs_enable_log_label">SuSFS 啟用日誌</string>
|
||||||
@@ -531,8 +506,6 @@
|
|||||||
<string name="cleanup_residue">清理工具殘留</string>
|
<string name="cleanup_residue">清理工具殘留</string>
|
||||||
<string name="cleanup_residue_description">清理各種模組以及工具的殘留檔案和目錄(可能會誤刪導致丟失以及無法啟動,謹慎使用)</string>
|
<string name="cleanup_residue_description">清理各種模組以及工具的殘留檔案和目錄(可能會誤刪導致丟失以及無法啟動,謹慎使用)</string>
|
||||||
<string name="susfs_edit_sus_path">編輯 SuS 路徑</string>
|
<string name="susfs_edit_sus_path">編輯 SuS 路徑</string>
|
||||||
<string name="susfs_edit_sus_mount">編輯 SuS 掛載</string>
|
|
||||||
<string name="susfs_edit_try_umount">編輯嘗試解除安裝</string>
|
|
||||||
<string name="edit_kstat_statically_title">編輯 Kstat 靜態配置</string>
|
<string name="edit_kstat_statically_title">編輯 Kstat 靜態配置</string>
|
||||||
<string name="edit_kstat_path_title">編輯 Kstat 路徑</string>
|
<string name="edit_kstat_path_title">編輯 Kstat 路徑</string>
|
||||||
<string name="susfs_save">儲存</string>
|
<string name="susfs_save">儲存</string>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<string name="app_name" translatable="false">SukiSU Ultra</string>
|
<string name="app_name" translatable="false">SukiSU Ultra</string>
|
||||||
<string name="home">Home</string>
|
<string name="home">Home</string>
|
||||||
|
<string name="learn_more">Learn more</string>
|
||||||
<string name="home_not_installed">Not installed</string>
|
<string name="home_not_installed">Not installed</string>
|
||||||
<string name="home_click_to_install">Click to install</string>
|
<string name="home_click_to_install">Click to install</string>
|
||||||
<string name="home_working">Working</string>
|
<string name="home_working">Working</string>
|
||||||
@@ -35,6 +36,7 @@
|
|||||||
<string name="reboot_edl">Reboot to EDL</string>
|
<string name="reboot_edl">Reboot to EDL</string>
|
||||||
<string name="about">About</string>
|
<string name="about">About</string>
|
||||||
<string name="module_uninstall_confirm">Are you sure you want to uninstall module %s?</string>
|
<string name="module_uninstall_confirm">Are you sure you want to uninstall module %s?</string>
|
||||||
|
<string name="metamodule_uninstall_confirm">"Are you sure you want to uninstall module %s? This action will affect all modules, and certain features provided by the metamodule (such as mounting) will no longer work. "</string>
|
||||||
<string name="module_uninstall_success">%s uninstalled</string>
|
<string name="module_uninstall_success">%s uninstalled</string>
|
||||||
<string name="module_uninstall_failed">Failed to uninstall: %s</string>
|
<string name="module_uninstall_failed">Failed to uninstall: %s</string>
|
||||||
<string name="module_version">Version</string>
|
<string name="module_version">Version</string>
|
||||||
@@ -170,6 +172,8 @@
|
|||||||
<string name="settings_disable_kernel_umount_summary">Disable kernel-level umount behavior controlled by KernelSU.</string>
|
<string name="settings_disable_kernel_umount_summary">Disable kernel-level umount behavior controlled by KernelSU.</string>
|
||||||
<string name="settings_enable_enhanced_security">Enable enhanced security</string>
|
<string name="settings_enable_enhanced_security">Enable enhanced security</string>
|
||||||
<string name="settings_enable_enhanced_security_summary">Enable stricter security policies.</string>
|
<string name="settings_enable_enhanced_security_summary">Enable stricter security policies.</string>
|
||||||
|
<string name="feature_status_unsupported_summary">Kernel does not support this feature.</string>
|
||||||
|
<string name="feature_status_managed_summary">This feature is managed by a module.</string>
|
||||||
<string name="settings_mode_default">Default</string>
|
<string name="settings_mode_default">Default</string>
|
||||||
<string name="settings_mode_temp_enable">Temporarily enable</string>
|
<string name="settings_mode_temp_enable">Temporarily enable</string>
|
||||||
<string name="settings_mode_always_enable">Permanently enable</string>
|
<string name="settings_mode_always_enable">Permanently enable</string>
|
||||||
@@ -188,10 +192,12 @@
|
|||||||
<string name="hide_susfs_status_summary">Hide SuSFS status information on the home page</string>
|
<string name="hide_susfs_status_summary">Hide SuSFS status information on the home page</string>
|
||||||
<string name="hide_zygisk_implement">Hide Zygisk status</string>
|
<string name="hide_zygisk_implement">Hide Zygisk status</string>
|
||||||
<string name="hide_zygisk_implement_summary">Hide Zygisk implementation information on the home page</string>
|
<string name="hide_zygisk_implement_summary">Hide Zygisk implementation information on the home page</string>
|
||||||
|
<string name="hide_meta_module_implement">Hide Meta Module status</string>
|
||||||
|
<string name="hide_meta_module_implement_summary">Hide Meta Module implementation information on the home page</string>
|
||||||
<string name="hide_link_card">Hide Link Card Status</string>
|
<string name="hide_link_card">Hide Link Card Status</string>
|
||||||
<string name="hide_link_card_summary">Hide link card information on the home page</string>
|
<string name="hide_link_card_summary">Hide link card information on the home page</string>
|
||||||
<string name="hide_tag_card">Hide module label rows</string>
|
<string name="hide_tag_card">Hide module label rows</string>
|
||||||
<string name="hide_tag_card_summary">Hide folder name and size labels in module cards</string>
|
<string name="hide_tag_card_summary">Hide folder name,size,metamodule notice labels in module cards</string>
|
||||||
<string name="theme_mode">Theme</string>
|
<string name="theme_mode">Theme</string>
|
||||||
<string name="theme_follow_system">Follow system</string>
|
<string name="theme_follow_system">Follow system</string>
|
||||||
<string name="theme_light">Light</string>
|
<string name="theme_light">Light</string>
|
||||||
@@ -329,6 +335,8 @@
|
|||||||
<string name="module_failed_count">%d Failed to install a new module</string>
|
<string name="module_failed_count">%d Failed to install a new module</string>
|
||||||
<string name="module_download_error">Module download failed</string>
|
<string name="module_download_error">Module download failed</string>
|
||||||
<string name="kernel_flashing">Kernel Flashing</string>
|
<string name="kernel_flashing">Kernel Flashing</string>
|
||||||
|
<string name="warning_of_meta_module_title">Require Meta module</string>
|
||||||
|
<string name="warning_of_meta_module_summary">This module want to mount /system, meta module will handle that. Otherwise, it might not work.</string>
|
||||||
<!-- 分类相关 -->
|
<!-- 分类相关 -->
|
||||||
<string name="category_all_apps">All</string>
|
<string name="category_all_apps">All</string>
|
||||||
<string name="category_root_apps">Root</string>
|
<string name="category_root_apps">Root</string>
|
||||||
@@ -391,39 +399,20 @@
|
|||||||
<!-- SuSFS Tab Titles -->
|
<!-- SuSFS Tab Titles -->
|
||||||
<string name="susfs_tab_basic_settings">Basic Settings</string>
|
<string name="susfs_tab_basic_settings">Basic Settings</string>
|
||||||
<string name="susfs_tab_sus_paths">SUS Paths</string>
|
<string name="susfs_tab_sus_paths">SUS Paths</string>
|
||||||
<string name="susfs_tab_sus_mounts">SUS Mounts</string>
|
|
||||||
<string name="susfs_tab_try_umount">Try Umount</string>
|
|
||||||
<string name="susfs_tab_path_settings">Path Settings</string>
|
<string name="susfs_tab_path_settings">Path Settings</string>
|
||||||
<string name="susfs_tab_enabled_features">Enabled Features Status</string>
|
<string name="susfs_tab_enabled_features">Enabled Features Status</string>
|
||||||
<!-- SuSFS Path Management -->
|
<!-- SuSFS Path Management -->
|
||||||
<string name="susfs_add_sus_path">Add SUS Path</string>
|
<string name="susfs_add_sus_path">Add SUS Path</string>
|
||||||
<string name="susfs_add_sus_mount">Add SUS Mount</string>
|
|
||||||
<string name="susfs_add_try_umount">Add Try Umount</string>
|
|
||||||
<string name="susfs_sus_path_added_success">SUS path added successfully</string>
|
<string name="susfs_sus_path_added_success">SUS path added successfully</string>
|
||||||
<string name="susfs_path_not_found_error">Path not found error</string>
|
<string name="susfs_path_not_found_error">Path not found error</string>
|
||||||
<string name="susfs_path_label">Path</string>
|
<string name="susfs_path_label">Path</string>
|
||||||
<string name="susfs_mount_path_label">Mount Path</string>
|
|
||||||
<string name="susfs_path_placeholder">e.g.: /system/addon.d</string>
|
<string name="susfs_path_placeholder">e.g.: /system/addon.d</string>
|
||||||
<string name="susfs_no_paths_configured">No SUS paths configured</string>
|
<string name="susfs_no_paths_configured">No SUS paths configured</string>
|
||||||
<string name="susfs_no_mounts_configured">No SUS mounts configured</string>
|
|
||||||
<string name="susfs_no_umounts_configured">No try umount configured</string>
|
|
||||||
<!-- SuSFS Umount Mode -->
|
<!-- SuSFS Umount Mode -->
|
||||||
<string name="susfs_umount_mode_label">Umount Mode</string>
|
|
||||||
<string name="susfs_umount_mode_normal">Normal Umount (0)</string>
|
|
||||||
<string name="susfs_umount_mode_detach">Detach Umount (1)</string>
|
|
||||||
<string name="susfs_umount_mode_normal_short">Normal</string>
|
|
||||||
<string name="susfs_umount_mode_detach_short">Detach</string>
|
|
||||||
<string name="susfs_umount_mode_display">Mode: %1$s (%2$s)</string>
|
|
||||||
<string name="susfs_try_umount_added_success">Try to umount path added successfully: %s</string>
|
|
||||||
<string name="susfs_try_umount_added_saved">Attempted umount path save succeeded: %s</string>
|
|
||||||
<!-- SuSFS Run Umount -->
|
<!-- SuSFS Run Umount -->
|
||||||
<!-- SuSFS Reset Categories -->
|
<!-- SuSFS Reset Categories -->
|
||||||
<string name="susfs_reset_paths_title">Reset SUS Paths</string>
|
<string name="susfs_reset_paths_title">Reset SUS Paths</string>
|
||||||
<string name="susfs_reset_paths_message">This will clear all SUS path configurations. Are you sure you want to continue?</string>
|
<string name="susfs_reset_paths_message">This will clear all SUS path configurations. Are you sure you want to continue?</string>
|
||||||
<string name="susfs_reset_mounts_title">Reset SUS Mounts</string>
|
|
||||||
<string name="susfs_reset_mounts_message">This will clear all SUS mount configurations. Are you sure you want to continue?</string>
|
|
||||||
<string name="susfs_reset_umounts_title">Reset Try Umount</string>
|
|
||||||
<string name="susfs_reset_umounts_message">This will clear all try umount configurations. Are you sure you want to continue?</string>
|
|
||||||
<string name="susfs_reset_path_title">Reset Path Settings</string>
|
<string name="susfs_reset_path_title">Reset Path Settings</string>
|
||||||
<!-- SuSFS Path Settings -->
|
<!-- SuSFS Path Settings -->
|
||||||
<string name="susfs_android_data_path_label">Android Data Path</string>
|
<string name="susfs_android_data_path_label">Android Data Path</string>
|
||||||
@@ -437,18 +426,12 @@
|
|||||||
<string name="susfs_feature_disabled">Disabled</string>
|
<string name="susfs_feature_disabled">Disabled</string>
|
||||||
<!-- Feature Labels -->
|
<!-- Feature Labels -->
|
||||||
<string name="sus_path_feature_label">SUS Path Support</string>
|
<string name="sus_path_feature_label">SUS Path Support</string>
|
||||||
<string name="sus_mount_feature_label">SUS Mount Support</string>
|
|
||||||
<string name="try_umount_feature_label">Try Umount Support</string>
|
|
||||||
<string name="spoof_uname_feature_label">Spoof uname Support</string>
|
<string name="spoof_uname_feature_label">Spoof uname Support</string>
|
||||||
<string name="spoof_cmdline_feature_label">Spoof Cmdline/Bootconfig</string>
|
<string name="spoof_cmdline_feature_label">Spoof Cmdline/Bootconfig</string>
|
||||||
<string name="open_redirect_feature_label">Open Redirect Support</string>
|
<string name="open_redirect_feature_label">Open Redirect Support</string>
|
||||||
<string name="enable_log_feature_label">Logging Support</string>
|
<string name="enable_log_feature_label">Logging Support</string>
|
||||||
<string name="auto_default_mount_feature_label">Auto Default Mount</string>
|
|
||||||
<string name="auto_bind_mount_feature_label">Auto Bind Mount</string>
|
|
||||||
<string name="auto_try_umount_bind_feature_label">Auto Try Umount Bind Mount</string>
|
|
||||||
<string name="hide_symbols_feature_label">Hide KSU SUSFS Symbols</string>
|
<string name="hide_symbols_feature_label">Hide KSU SUSFS Symbols</string>
|
||||||
<string name="sus_kstat_feature_label">SUS Kstat Support</string>
|
<string name="sus_kstat_feature_label">SUS Kstat Support</string>
|
||||||
<string name="sus_su_feature_label">SUS SU mode switching function</string>
|
|
||||||
<!-- 可切换状态 -->
|
<!-- 可切换状态 -->
|
||||||
<string name="susfs_feature_configurable">Configurable SuSFS Features</string>
|
<string name="susfs_feature_configurable">Configurable SuSFS Features</string>
|
||||||
<string name="susfs_enable_log_label">SuSFS Enable Log</string>
|
<string name="susfs_enable_log_label">SuSFS Enable Log</string>
|
||||||
@@ -544,8 +527,6 @@
|
|||||||
<string name="cleanup_residue">Cleanup Residue</string>
|
<string name="cleanup_residue">Cleanup Residue</string>
|
||||||
<string name="cleanup_residue_description">Clean up the residual files and directories of various modules and tools (May be deleted by mistake, resulting in loss and failure to start, use with caution)</string>
|
<string name="cleanup_residue_description">Clean up the residual files and directories of various modules and tools (May be deleted by mistake, resulting in loss and failure to start, use with caution)</string>
|
||||||
<string name="susfs_edit_sus_path">Edit SUS Path</string>
|
<string name="susfs_edit_sus_path">Edit SUS Path</string>
|
||||||
<string name="susfs_edit_sus_mount">Edit SUS Mount</string>
|
|
||||||
<string name="susfs_edit_try_umount">Edit Try Umount</string>
|
|
||||||
<string name="edit_kstat_statically_title">Edit Kstat Static Configuration</string>
|
<string name="edit_kstat_statically_title">Edit Kstat Static Configuration</string>
|
||||||
<string name="edit_kstat_path_title">Edit Kstat Path</string>
|
<string name="edit_kstat_path_title">Edit Kstat Path</string>
|
||||||
<string name="susfs_save">Save</string>
|
<string name="susfs_save">Save</string>
|
||||||
@@ -589,6 +570,7 @@
|
|||||||
<string name="no_active_manager">No active manager</string>
|
<string name="no_active_manager">No active manager</string>
|
||||||
<string name="default_signature">SukiSU</string>
|
<string name="default_signature">SukiSU</string>
|
||||||
<string name="home_zygisk_implement">Zygisk implement</string>
|
<string name="home_zygisk_implement">Zygisk implement</string>
|
||||||
|
<string name="home_meta_module_implement">Meta Module implement</string>
|
||||||
<!-- 循环路径相关 -->
|
<!-- 循环路径相关 -->
|
||||||
<string name="susfs_tab_sus_loop_paths">SUS Loop Paths</string>
|
<string name="susfs_tab_sus_loop_paths">SUS Loop Paths</string>
|
||||||
<string name="susfs_add_sus_loop_path">Add SUS Loop Path</string>
|
<string name="susfs_add_sus_loop_path">Add SUS Loop Path</string>
|
||||||
@@ -736,7 +718,6 @@ Important Note:\n
|
|||||||
<string name="umount_flags">Unmount Flags</string>
|
<string name="umount_flags">Unmount Flags</string>
|
||||||
<string name="umount_flags_hint">0=Normal unmount, 8=MNT_DETACH, -1=Auto</string>
|
<string name="umount_flags_hint">0=Normal unmount, 8=MNT_DETACH, -1=Auto</string>
|
||||||
<string name="flags">Flags</string>
|
<string name="flags">Flags</string>
|
||||||
<string name="default_entry">Default Entry</string>
|
|
||||||
<string name="confirm_delete">Confirm Delete</string>
|
<string name="confirm_delete">Confirm Delete</string>
|
||||||
<string name="confirm_delete_umount_path">Are you sure you want to delete the path %s?</string>
|
<string name="confirm_delete_umount_path">Are you sure you want to delete the path %s?</string>
|
||||||
<string name="umount_path_added">Path added, will take effect after reboot</string>
|
<string name="umount_path_added">Path added, will take effect after reboot</string>
|
||||||
|
|||||||
34
ts/.gitignore
vendored
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
# dependencies (bun install)
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
# output
|
||||||
|
out
|
||||||
|
dist
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# code coverage
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# logs
|
||||||
|
logs
|
||||||
|
_.log
|
||||||
|
report.[0-9]_.[0-9]_.[0-9]_.[0-9]_.json
|
||||||
|
|
||||||
|
# dotenv environment variable files
|
||||||
|
.env
|
||||||
|
.env.development.local
|
||||||
|
.env.test.local
|
||||||
|
.env.production.local
|
||||||
|
.env.local
|
||||||
|
|
||||||
|
# caches
|
||||||
|
.eslintcache
|
||||||
|
.cache
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# IntelliJ based IDEs
|
||||||
|
.idea
|
||||||
|
|
||||||
|
# Finder (MacOS) folder config
|
||||||
|
.DS_Store
|
||||||
13
ts/README.md
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
# ts
|
||||||
|
|
||||||
|
To install dependencies:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun install
|
||||||
|
```
|
||||||
|
|
||||||
|
To run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bun run send.ts
|
||||||
|
```
|
||||||
52
ts/bun.lock
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
{
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"configVersion": 1,
|
||||||
|
"workspaces": {
|
||||||
|
"": {
|
||||||
|
"name": "ts",
|
||||||
|
"dependencies": {
|
||||||
|
"grammy": "^1.38.4",
|
||||||
|
"zod": "^4.1.12",
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/bun": "latest",
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": "^5",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"packages": {
|
||||||
|
"@grammyjs/types": ["@grammyjs/types@3.22.2", "", {}, "sha512-uu7DX2ezhnBPozL3bXHmwhLvaFsh59E4QyviNH4Cij7EdVekYrs6mCzeXsa2pDk30l3uXo7DBahlZLzTPtpYZg=="],
|
||||||
|
|
||||||
|
"@types/bun": ["@types/bun@1.3.3", "", { "dependencies": { "bun-types": "1.3.3" } }, "sha512-ogrKbJ2X5N0kWLLFKeytG0eHDleBYtngtlbu9cyBKFtNL3cnpDZkNdQj8flVf6WTZUX5ulI9AY1oa7ljhSrp+g=="],
|
||||||
|
|
||||||
|
"@types/node": ["@types/node@24.10.1", "", { "dependencies": { "undici-types": "~7.16.0" } }, "sha512-GNWcUTRBgIRJD5zj+Tq0fKOJ5XZajIiBroOF0yvj2bSU1WvNdYS/dn9UxwsujGW4JX06dnHyjV2y9rRaybH0iQ=="],
|
||||||
|
|
||||||
|
"abort-controller": ["abort-controller@3.0.0", "", { "dependencies": { "event-target-shim": "^5.0.0" } }, "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg=="],
|
||||||
|
|
||||||
|
"bun-types": ["bun-types@1.3.3", "", { "dependencies": { "@types/node": "*" } }, "sha512-z3Xwlg7j2l9JY27x5Qn3Wlyos8YAp0kKRlrePAOjgjMGS5IG6E7Jnlx736vH9UVI4wUICwwhC9anYL++XeOgTQ=="],
|
||||||
|
|
||||||
|
"debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="],
|
||||||
|
|
||||||
|
"event-target-shim": ["event-target-shim@5.0.1", "", {}, "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ=="],
|
||||||
|
|
||||||
|
"grammy": ["grammy@1.38.4", "", { "dependencies": { "@grammyjs/types": "3.22.2", "abort-controller": "^3.0.0", "debug": "^4.4.3", "node-fetch": "^2.7.0" } }, "sha512-z07Kin3HgRwMdy40KUs+c9fmNBvGlSxGwcqY8NAH0a8KULGFYEMQaFAo3ge0V5tvmgr02Jgubkf54KjHLAMCbw=="],
|
||||||
|
|
||||||
|
"ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="],
|
||||||
|
|
||||||
|
"node-fetch": ["node-fetch@2.7.0", "", { "dependencies": { "whatwg-url": "^5.0.0" }, "peerDependencies": { "encoding": "^0.1.0" }, "optionalPeers": ["encoding"] }, "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A=="],
|
||||||
|
|
||||||
|
"tr46": ["tr46@0.0.3", "", {}, "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw=="],
|
||||||
|
|
||||||
|
"typescript": ["typescript@5.9.3", "", { "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" } }, "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw=="],
|
||||||
|
|
||||||
|
"undici-types": ["undici-types@7.16.0", "", {}, "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw=="],
|
||||||
|
|
||||||
|
"webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],
|
||||||
|
|
||||||
|
"whatwg-url": ["whatwg-url@5.0.0", "", { "dependencies": { "tr46": "~0.0.3", "webidl-conversions": "^3.0.0" } }, "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw=="],
|
||||||
|
|
||||||
|
"zod": ["zod@4.1.12", "", {}, "sha512-JInaHOamG8pt5+Ey8kGmdcAcg3OL9reK8ltczgHTAwNhMys/6ThXHityHxVV2p3fkw/c+MAvBHFVYHFZDmjMCQ=="],
|
||||||
|
}
|
||||||
|
}
|
||||||
17
ts/package.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"name": "ts",
|
||||||
|
"module": "send.ts",
|
||||||
|
"type": "module",
|
||||||
|
"private": true,
|
||||||
|
"author": "TypeFlu <TypeFlu@gmail.com> (https://Typeflu.me/)",
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/bun": "latest"
|
||||||
|
},
|
||||||
|
"peerDependencies": {
|
||||||
|
"typescript": "^5"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"grammy": "^1.38.4",
|
||||||
|
"zod": "^4.1.12"
|
||||||
|
}
|
||||||
|
}
|
||||||
197
ts/send.ts
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
import { Bot, InlineKeyboard } from "grammy";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
const BOT_TOKEN = process.env.BOT_TOKEN!;
|
||||||
|
const GROUP_ID = Number(process.env.TELEGRAM_GROUP_ID!);
|
||||||
|
const TOPIC_COMMITS = Number(process.env.TELEGRAM_TOPIC_COMMITS!);
|
||||||
|
const TOPIC_PRS = Number(process.env.TELEGRAM_TOPIC_PRS!);
|
||||||
|
const GITHUB_TOKEN = process.env.GITHUB_TOKEN!;
|
||||||
|
const EVENT_PATH = process.env.GITHUB_EVENT_PATH!;
|
||||||
|
|
||||||
|
const bot = new Bot(BOT_TOKEN);
|
||||||
|
|
||||||
|
const FileNode = z.object({ path: z.string() });
|
||||||
|
|
||||||
|
const PullRequestSchema = z.object({
|
||||||
|
action: z.string(),
|
||||||
|
number: z.number(),
|
||||||
|
repository: z.object({
|
||||||
|
full_name: z.string(),
|
||||||
|
html_url: z.url(),
|
||||||
|
}),
|
||||||
|
pull_request: z.object({
|
||||||
|
html_url: z.url().optional(),
|
||||||
|
url: z.url().optional(),
|
||||||
|
title: z.string(),
|
||||||
|
body: z.string().nullable(),
|
||||||
|
user: z.object({ login: z.string(), html_url: z.url() }),
|
||||||
|
head: z.object({ ref: z.string() }),
|
||||||
|
base: z.object({ ref: z.string() }),
|
||||||
|
changed_files: z.number().optional().default(0),
|
||||||
|
additions: z.number().optional().default(0),
|
||||||
|
deletions: z.number().optional().default(0),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const PushSchema = z.object({
|
||||||
|
ref: z.string(),
|
||||||
|
repository: z.object({
|
||||||
|
full_name: z.string(),
|
||||||
|
html_url: z.url(),
|
||||||
|
}),
|
||||||
|
head_commit: z
|
||||||
|
.object({
|
||||||
|
id: z.string(),
|
||||||
|
url: z.url(),
|
||||||
|
message: z.string(),
|
||||||
|
author: z.object({ name: z.string(), email: z.email() }),
|
||||||
|
added: z.array(z.string()).optional().default([]),
|
||||||
|
modified: z.array(z.string()).optional().default([]),
|
||||||
|
removed: z.array(z.string()).optional().default([]),
|
||||||
|
})
|
||||||
|
.nullable(),
|
||||||
|
});
|
||||||
|
|
||||||
|
function detectLanguage(files: string[]): string {
|
||||||
|
const ext = files.map((f) => (f.split(".").pop() || "").toLowerCase());
|
||||||
|
if (ext.some((e) => e === "kt" || e === "kts")) return "Kotlin";
|
||||||
|
if (ext.some((e) => e === "rs")) return "Rust";
|
||||||
|
if (ext.some((e) => e === "c")) return "C";
|
||||||
|
if (ext.some((e) => e === "sh")) return "Shell";
|
||||||
|
if (ext.some((e) => e === "ts" || e === "tsx")) return "TypeScript";
|
||||||
|
return "Other";
|
||||||
|
}
|
||||||
|
|
||||||
|
async function fetchPrFiles(
|
||||||
|
repoFullName: string,
|
||||||
|
prNumber: number,
|
||||||
|
): Promise<string[]> {
|
||||||
|
const query = `
|
||||||
|
query($owner:String!, $name:String!, $number:Int!) {
|
||||||
|
repository(owner:$owner, name:$name) {
|
||||||
|
pullRequest(number:$number) {
|
||||||
|
files(first:100) {
|
||||||
|
nodes {
|
||||||
|
path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}`;
|
||||||
|
const [owner, name] = repoFullName.split("/");
|
||||||
|
const resp = await fetch("https://api.github.com/graphql", {
|
||||||
|
method: "POST",
|
||||||
|
headers: {
|
||||||
|
Authorization: `bearer ${GITHUB_TOKEN}`,
|
||||||
|
"Content-Type": "application/json",
|
||||||
|
},
|
||||||
|
body: JSON.stringify({
|
||||||
|
query,
|
||||||
|
variables: { owner, name, number: prNumber },
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
if (!resp.ok) {
|
||||||
|
const body = await resp.text();
|
||||||
|
throw new Error(`GitHub GraphQL API error: ${resp.status} ${body}`);
|
||||||
|
}
|
||||||
|
const json = (await resp.json()) as any;
|
||||||
|
const nodes = json?.data?.repository?.pullRequest?.files?.nodes as
|
||||||
|
| { path: string }[]
|
||||||
|
| undefined;
|
||||||
|
if (!nodes || !Array.isArray(nodes)) return [];
|
||||||
|
return nodes.map((n) => n.path);
|
||||||
|
}
|
||||||
|
|
||||||
|
function prUrlOf(pr: { html_url?: string; url?: string }) {
|
||||||
|
return pr.html_url ?? pr.url ?? "";
|
||||||
|
}
|
||||||
|
|
||||||
|
async function formatPrMessage(
|
||||||
|
evt: z.infer<typeof PullRequestSchema>,
|
||||||
|
): Promise<{ text: string; fileLink: string }> {
|
||||||
|
const pr = evt.pull_request;
|
||||||
|
const repo = evt.repository;
|
||||||
|
const files = await fetchPrFiles(repo.full_name, evt.number);
|
||||||
|
const lang = detectLanguage(files);
|
||||||
|
const prUrl = prUrlOf(pr);
|
||||||
|
const fileLink = prUrl ? `${prUrl}/files` : repo.html_url;
|
||||||
|
const bodyText = pr.body ? pr.body : "_No description provided_";
|
||||||
|
const text =
|
||||||
|
`### Repository\n[${repo.full_name}](${repo.html_url})\n\n` +
|
||||||
|
`**Pull Request #${evt.number}:** [${pr.title}](${prUrl || repo.html_url})\n\n` +
|
||||||
|
`**Author:** [${pr.user.login}](${pr.user.html_url})\n` +
|
||||||
|
`**Files Changed:** ${pr.changed_files}\n` +
|
||||||
|
`**Additions / Deletions:** +${pr.additions} / -${pr.deletions}\n` +
|
||||||
|
`**Language:** ${lang}\n\n` +
|
||||||
|
`**Description:**\n\`\`\`\n${bodyText}\n\`\`\``;
|
||||||
|
return { text, fileLink };
|
||||||
|
}
|
||||||
|
|
||||||
|
function formatPushMessage(evt: z.infer<typeof PushSchema>): {
|
||||||
|
text: string;
|
||||||
|
fileLink: string;
|
||||||
|
} {
|
||||||
|
const repo = evt.repository;
|
||||||
|
const c = evt.head_commit;
|
||||||
|
if (!c) {
|
||||||
|
const text = `### Repository\n[${repo.full_name}](${repo.html_url})\n\nPush event detected, but no head commit data available.`;
|
||||||
|
return { text, fileLink: repo.html_url };
|
||||||
|
}
|
||||||
|
const added = c.added ?? [];
|
||||||
|
const modified = c.modified ?? [];
|
||||||
|
const removed = c.removed ?? [];
|
||||||
|
const details = [
|
||||||
|
added.length ? `➕ Added: ${added.join(", ")}` : "",
|
||||||
|
modified.length ? `✏️ Modified: ${modified.join(", ")}` : "",
|
||||||
|
removed.length ? `❌ Removed: ${removed.join(", ")}` : "",
|
||||||
|
]
|
||||||
|
.filter(Boolean)
|
||||||
|
.join("\n");
|
||||||
|
const lang = detectLanguage([...added, ...modified, ...removed]);
|
||||||
|
const fileLink = c.url;
|
||||||
|
const text =
|
||||||
|
`### Repository\n[${repo.full_name}](${repo.html_url})\n\n` +
|
||||||
|
`**Commit:** [${c.id}](${c.url})\n` +
|
||||||
|
`**Author:** ${c.author.name} <${c.author.email}>\n` +
|
||||||
|
`**Message:**\n\`\`\`\n${c.message}\n\`\`\`\n` +
|
||||||
|
(details ? `**Changes:**\n${details}\n` : "") +
|
||||||
|
`**Language:** ${lang}`;
|
||||||
|
return { text, fileLink };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main(): Promise<void> {
|
||||||
|
const raw = await (
|
||||||
|
await import("node:fs/promises")
|
||||||
|
).readFile(EVENT_PATH, "utf-8");
|
||||||
|
const parsed = JSON.parse(raw);
|
||||||
|
let messageObj: { text: string; fileLink: string };
|
||||||
|
let topic: number;
|
||||||
|
if ("pull_request" in parsed) {
|
||||||
|
const prEvt = PullRequestSchema.parse(parsed);
|
||||||
|
messageObj = await formatPrMessage(prEvt);
|
||||||
|
topic = TOPIC_PRS;
|
||||||
|
} else {
|
||||||
|
const pushEvt = PushSchema.parse(parsed);
|
||||||
|
messageObj = formatPushMessage(pushEvt);
|
||||||
|
topic = TOPIC_COMMITS;
|
||||||
|
}
|
||||||
|
const repoUrl =
|
||||||
|
(parsed.repository &&
|
||||||
|
(parsed.repository.html_url ?? parsed.repository.url)) ||
|
||||||
|
"";
|
||||||
|
const keyboard = new InlineKeyboard()
|
||||||
|
.url("View on GitHub", repoUrl)
|
||||||
|
.row()
|
||||||
|
.url("View Files", messageObj.fileLink);
|
||||||
|
await bot.api.sendMessage(GROUP_ID, messageObj.text, {
|
||||||
|
parse_mode: "Markdown",
|
||||||
|
message_thread_id: topic,
|
||||||
|
reply_markup: keyboard,
|
||||||
|
});
|
||||||
|
process.exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch((err) => {
|
||||||
|
console.error(err);
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
29
ts/tsconfig.json
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
// Environment setup & latest features
|
||||||
|
"lib": ["ESNext"],
|
||||||
|
"target": "ESNext",
|
||||||
|
"module": "Preserve",
|
||||||
|
"moduleDetection": "force",
|
||||||
|
"jsx": "react-jsx",
|
||||||
|
"allowJs": true,
|
||||||
|
|
||||||
|
// Bundler mode
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowImportingTsExtensions": true,
|
||||||
|
"verbatimModuleSyntax": true,
|
||||||
|
"noEmit": true,
|
||||||
|
|
||||||
|
// Best practices
|
||||||
|
"strict": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"noFallthroughCasesInSwitch": true,
|
||||||
|
"noUncheckedIndexedAccess": true,
|
||||||
|
"noImplicitOverride": true,
|
||||||
|
|
||||||
|
// Some stricter flags (disabled by default)
|
||||||
|
"noUnusedLocals": false,
|
||||||
|
"noUnusedParameters": false,
|
||||||
|
"noPropertyAccessFromIndexSignature": false
|
||||||
|
}
|
||||||
|
}
|
||||||
872
userspace/ksud/Cargo.lock
generated
@@ -6,11 +6,11 @@ edition = "2024"
|
|||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
notify = "8.2"
|
notify = "6.1"
|
||||||
anyhow = "1"
|
anyhow = "1"
|
||||||
clap = { version = "4", features = ["derive"] }
|
clap = { version = "4", features = ["derive"] }
|
||||||
const_format = "0.2"
|
const_format = "0.2"
|
||||||
zip = { version = "6", features = [
|
zip = { version = "3", features = [
|
||||||
"deflate",
|
"deflate",
|
||||||
"deflate64",
|
"deflate64",
|
||||||
"time",
|
"time",
|
||||||
@@ -38,7 +38,7 @@ rust-embed = { version = "8", features = [
|
|||||||
"debug-embed",
|
"debug-embed",
|
||||||
"compression", # must clean build after updating binaries
|
"compression", # must clean build after updating binaries
|
||||||
] }
|
] }
|
||||||
which = "8"
|
which = "7"
|
||||||
getopts = "0.2"
|
getopts = "0.2"
|
||||||
sha256 = "1"
|
sha256 = "1"
|
||||||
sha1 = "0.10"
|
sha1 = "0.10"
|
||||||
@@ -48,14 +48,14 @@ regex-lite = "0.1"
|
|||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
|
|
||||||
[target.'cfg(any(target_os = "android", target_os = "linux"))'.dependencies]
|
[target.'cfg(any(target_os = "android", target_os = "linux"))'.dependencies]
|
||||||
rustix = { git = "https://github.com/Kernel-SU/rustix.git", rev = "4a53fbc7cb7a07cabe87125cc21dbc27db316259", features = [
|
rustix = { git = "https://github.com/Kernel-SU/rustix.git", branch = "main", features = [
|
||||||
"all-apis",
|
"all-apis",
|
||||||
] }
|
] }
|
||||||
# some android specific dependencies which compiles under unix are also listed here for convenience of coding
|
# some android specific dependencies which compiles under unix are also listed here for convenience of coding
|
||||||
android-properties = { version = "0.2", features = ["bionic-deprecated"] }
|
android-properties = { version = "0.2", features = ["bionic-deprecated"] }
|
||||||
|
|
||||||
[target.'cfg(target_os = "android")'.dependencies]
|
[target.'cfg(target_os = "android")'.dependencies]
|
||||||
android_logger = { version = "0.15", default-features = false }
|
android_logger = { version = "0.14", default-features = false }
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
overflow-checks = false
|
overflow-checks = false
|
||||||
|
|||||||
@@ -84,7 +84,7 @@ pub fn get_apk_signature(apk: &str) -> Result<(u32, String)> {
|
|||||||
return Err(anyhow::anyhow!("Unexpected v3 signature found!",));
|
return Err(anyhow::anyhow!("Unexpected v3 signature found!",));
|
||||||
}
|
}
|
||||||
|
|
||||||
v2_signing.ok_or(anyhow::anyhow!("No signature found!"))
|
v2_signing.ok_or_else(|| anyhow::anyhow!("No signature found!"))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calc_cert_sha256(
|
fn calc_cert_sha256(
|
||||||
|
|||||||
@@ -31,19 +31,19 @@ pub fn ensure_binaries(ignore_if_exist: bool) -> Result<()> {
|
|||||||
// don't extract ksuinit and kernel modules
|
// don't extract ksuinit and kernel modules
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let asset = Asset::get(&file).ok_or(anyhow::anyhow!("asset not found: {}", file))?;
|
let asset = Asset::get(&file).ok_or_else(|| anyhow::anyhow!("asset not found: {file}"))?;
|
||||||
utils::ensure_binary(format!("{BINARY_DIR}{file}"), &asset.data, ignore_if_exist)?
|
utils::ensure_binary(format!("{BINARY_DIR}{file}"), &asset.data, ignore_if_exist)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn copy_assets_to_file(name: &str, dst: impl AsRef<Path>) -> Result<()> {
|
pub fn copy_assets_to_file(name: &str, dst: impl AsRef<Path>) -> Result<()> {
|
||||||
let asset = Asset::get(name).ok_or(anyhow::anyhow!("asset not found: {}", name))?;
|
let asset = Asset::get(name).ok_or_else(|| anyhow::anyhow!("asset not found: {name}"))?;
|
||||||
std::fs::write(dst, asset.data)?;
|
std::fs::write(dst, asset.data)?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn list_supported_kmi() -> Result<Vec<String>> {
|
pub fn list_supported_kmi() -> std::vec::Vec<std::string::String> {
|
||||||
let mut list = Vec::new();
|
let mut list = Vec::new();
|
||||||
for file in Asset::iter() {
|
for file in Asset::iter() {
|
||||||
// kmi_name = "xxx_kernelsu.ko"
|
// kmi_name = "xxx_kernelsu.ko"
|
||||||
@@ -51,5 +51,5 @@ pub fn list_supported_kmi() -> Result<Vec<String>> {
|
|||||||
list.push(kmi.to_string());
|
list.push(kmi.to_string());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(list)
|
list
|
||||||
}
|
}
|
||||||
|
|||||||