Compare commits
35 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8304c54b8c | ||
|
|
8ddca5f310 | ||
|
|
d4d42cae0d | ||
|
|
cd12337f2a | ||
|
|
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"
|
|
||||||
@@ -15,6 +15,7 @@ A kernel-based root solution for Android devices, forked from [`tiann/KernelSU`]
|
|||||||
|
|
||||||
1. Kernel-based `su` and root access management
|
1. Kernel-based `su` and root access management
|
||||||
2. Module system based on [Magic Mount](https://github.com/5ec1cff/KernelSU)
|
2. Module system based on [Magic Mount](https://github.com/5ec1cff/KernelSU)
|
||||||
|
> **Note:** SukiSU now delegates all module mounting to the installed *metamodule*; the core no longer handles mount operations.
|
||||||
3. [App Profile](https://kernelsu.org/guide/app-profile.html): Lock up the root power in a cage
|
3. [App Profile](https://kernelsu.org/guide/app-profile.html): Lock up the root power in a cage
|
||||||
4. Support non-GKI and GKI 1.0
|
4. Support non-GKI and GKI 1.0
|
||||||
5. KPM Support
|
5. KPM Support
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ fun getBugreportFile(context: Context): File {
|
|||||||
val bugreportDir = File(context.cacheDir, "bugreport")
|
val bugreportDir = File(context.cacheDir, "bugreport")
|
||||||
bugreportDir.mkdirs()
|
bugreportDir.mkdirs()
|
||||||
|
|
||||||
|
val processFile = File(bugreportDir, "process.txt")
|
||||||
val dmesgFile = File(bugreportDir, "dmesg.txt")
|
val dmesgFile = File(bugreportDir, "dmesg.txt")
|
||||||
val logcatFile = File(bugreportDir, "logcat.txt")
|
val logcatFile = File(bugreportDir, "logcat.txt")
|
||||||
val tombstonesFile = File(bugreportDir, "tombstones.tar.gz")
|
val tombstonesFile = File(bugreportDir, "tombstones.tar.gz")
|
||||||
@@ -40,8 +41,10 @@ fun getBugreportFile(context: Context): File {
|
|||||||
|
|
||||||
val shell = getRootShell(true)
|
val shell = getRootShell(true)
|
||||||
|
|
||||||
shell.newJob().add("dmesg > ${dmesgFile.absolutePath}").exec()
|
// busybox ps has very few features for embed devices
|
||||||
shell.newJob().add("logcat -d > ${logcatFile.absolutePath}").exec()
|
shell.newJob().add("toybox ps -T -A -w -o PID,TID,UID,COMM,CMDLINE,CMD,LABEL,STAT,WCHAN > ${processFile.absolutePath}").exec()
|
||||||
|
shell.newJob().add("dmesg -r > ${dmesgFile.absolutePath}").exec()
|
||||||
|
shell.newJob().add("logcat -b all -v uid -d > ${logcatFile.absolutePath}").exec()
|
||||||
shell.newJob().add("tar -czf ${tombstonesFile.absolutePath} -C /data/tombstones .").exec()
|
shell.newJob().add("tar -czf ${tombstonesFile.absolutePath} -C /data/tombstones .").exec()
|
||||||
shell.newJob().add("tar -czf ${dropboxFile.absolutePath} -C /data/system/dropbox .").exec()
|
shell.newJob().add("tar -czf ${dropboxFile.absolutePath} -C /data/system/dropbox .").exec()
|
||||||
shell.newJob().add("tar -czf ${pstoreFile.absolutePath} -C /sys/fs/pstore .").exec()
|
shell.newJob().add("tar -czf ${pstoreFile.absolutePath} -C /sys/fs/pstore .").exec()
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -387,39 +387,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 +414,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 +515,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>
|
||||||
|
|||||||
@@ -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>
|
||||||
|
|||||||
@@ -378,39 +378,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 +405,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 +506,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>
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||