Compare commits

...

27 Commits

Author SHA1 Message Date
71119ac4c4 Fix(OxygenTopAppBar): Fix the bug that cannot hide completely when scrolling 2024-11-06 14:12:06 +08:00
c91eaaf1a5 Build(gradle): Upgrade dependencies 2024-11-05 17:57:41 +08:00
77802d3dc9 Refactor(WebView): Change background color to transparent 2024-11-05 17:45:25 +08:00
8669a2c9ef Feat(Tool): Support theme 2024-11-05 11:45:56 +08:00
d589df860e Refactor(ToolView): Remove safeDrawingPadding 2024-10-15 12:12:19 +08:00
db25ab4e5f Feat(App): Support sliding down the notification bar to exit full screen 2024-10-15 12:01:30 +08:00
e855a414a4 Feat(App): Support full screen 2024-10-14 18:07:03 +08:00
2823788765 Feat(ToolView): Support alert, confirm and prompt 2024-10-14 16:49:10 +08:00
fc4baca21d Build(gradle): Upgrade dependencies 2024-10-14 16:48:06 +08:00
e188d743f0 Refactor(.gitignore): Ignore key store 2024-10-12 15:59:37 +08:00
f7dd806885 Refactor(FileUtils): Optimize code 2024-10-12 15:59:07 +08:00
326e777b7f Refactor(TopAppBar): Optimize size of TopAppBar 2024-10-12 14:48:52 +08:00
0eb0667d2a Perf(ToolView): Optimize webview loading performance 2024-10-12 11:55:15 +08:00
cd97f6156f Refactor(minSdk): Upgrade minSdk from 21 to 24 2024-10-12 09:49:39 +08:00
3979027471 Refactor(NativeWebApi): Change data transfer type from ByteArray to Base64 String 2024-10-11 09:42:43 +08:00
d3f3fba413 Feat(ToolView): Support filltering file types 2024-10-11 09:42:40 +08:00
7b77069744 Refactor(SplashScreen): Optimize the splash screen of lower version 2024-10-11 09:42:35 +08:00
585a261bb8 Feat(ToolView): Support upload and download file 2024-09-30 15:06:28 +08:00
2631c22e52 Refactor(BottomAppBar): Adapt virtual button navigation bar 2024-09-30 12:09:43 +08:00
af3edb1d30 Refactor(ToolViewScreenViewModel): Save tool detail in cache 2024-09-29 15:52:12 +08:00
ba72ef2759 Refactor(Any): Optimize codes 2024-09-27 09:21:13 +08:00
2639d88492 Refactor(ClickableText): Replace ClickableText with Text 2024-09-26 18:44:59 +08:00
04812cf56b Build(gradle): Upgrade dependencies 2024-09-26 18:44:03 +08:00
112e30804e Refactor(Icon): Update APP icon and add startup animation 2024-09-24 17:55:14 +08:00
7e5554cfcd Refactor(Gradle): Optimize build script
Optimize version number rule. Optimize generated file name.
2024-09-02 16:36:46 +08:00
7e91f267e3 Refactor(AboutScreen): Optimize version info 2024-09-02 16:35:11 +08:00
21bb9a729f Build(Gradle): Auto sign release package 2024-09-01 23:02:07 +08:00
76 changed files with 2928 additions and 955 deletions

4
.gitignore vendored
View File

@@ -17,6 +17,10 @@ build/
# Local configuration file (sdk path, etc)
local.properties
# Key Store
keystore.properties
*.jks
# Eclipse project files
.classpath
.project

View File

@@ -1,5 +1,23 @@
import com.android.build.gradle.internal.api.BaseVariantOutputImpl
import com.mikepenz.aboutlibraries.plugin.AboutLibrariesTask
import java.io.FileInputStream
import java.time.LocalDateTime
import java.time.ZoneOffset
import java.time.format.DateTimeFormatter
import java.util.Properties
val localDateTime: LocalDateTime = LocalDateTime.now(ZoneOffset.UTC)
val baseVersionCode = 1
val baseVersionName = "0.0.0"
val keystoreProperties = rootProject.file("keystore.properties").run {
if (!exists()) {
null
} else {
Properties().apply {
load(FileInputStream(this@run))
}
}
}
plugins {
alias(libs.plugins.androidApplication)
@@ -17,19 +35,38 @@ plugins {
android {
namespace = "top.fatweb.oxygen.toolbox"
compileSdk = 34
compileSdk = 35
defaultConfig {
applicationId = "top.fatweb.oxygen.toolbox"
minSdk = 21
targetSdk = 34
versionCode = 1
versionName = "1.0.0-SNAPSHOT"
minSdk = 24
targetSdk = 35
versionCode = baseVersionCode
versionName = "$baseVersionName${
if (baseVersionCode % 100 != 0) ".${
localDateTime.format(
DateTimeFormatter.ofPattern("yyMMdd")
)
}" else ""
}"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary = true
}
setProperty("archivesBaseName", "$applicationId-v$versionName($versionCode)")
}
signingConfigs {
keystoreProperties?.let {
create("release") {
storeFile = rootProject.file(it["storeFile"] as String)
storePassword = it["storePassword"] as String
keyAlias = it["keyAlias"] as String
keyPassword = it["keyPassword"] as String
}
}
}
buildTypes {
@@ -40,13 +77,7 @@ android {
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
android.applicationVariants.all {
outputs.all {
(this as BaseVariantOutputImpl).outputFileName =
"${project.name}_${defaultConfig.versionName}-${defaultConfig.versionCode}_${buildType.name}.apk"
signingConfig = signingConfigs.findByName("release")
}
}
@@ -64,9 +95,6 @@ android {
compose = true
buildConfig = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.5.12"
}
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
@@ -155,6 +183,7 @@ dependencies {
implementation(libs.material.icons.core)
implementation(libs.material.icons.extended)
implementation(libs.material3.window.size)
implementation(libs.animation.graphics)
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.appcompat)

View File

@@ -4,6 +4,9 @@
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="32"
tools:ignore="ScopedStorage" />
<application
android:name=".OxygenApplication"

View File

@@ -0,0 +1,365 @@
:root {
--blue: #1677FF;
--blue1: #111a2c;
--blue2: #112545;
--blue3: #15325b;
--blue4: #15417e;
--blue5: #1554ad;
--blue6: #1668dc;
--blue7: #3c89e8;
--blue8: #65a9f3;
--blue9: #8dc5f8;
--blue10: #b7dcfa;
--purple: #722ED1;
--purple1: #1a1325;
--purple2: #24163a;
--purple3: #301c4d;
--purple4: #3e2069;
--purple5: #51258f;
--purple6: #642ab5;
--purple7: #854eca;
--purple8: #ab7ae0;
--purple9: #cda8f0;
--purple10: #ebd7fa;
--cyan: #13C2C2;
--cyan1: #112123;
--cyan2: #113536;
--cyan3: #144848;
--cyan4: #146262;
--cyan5: #138585;
--cyan6: #13a8a8;
--cyan7: #33bcb7;
--cyan8: #58d1c9;
--cyan9: #84e2d8;
--cyan10: #b2f1e8;
--green: #52C41A;
--green1: #162312;
--green2: #1d3712;
--green3: #274916;
--green4: #306317;
--green5: #3c8618;
--green6: #49aa19;
--green7: #6abe39;
--green8: #8fd460;
--green9: #b2e58b;
--green10: #d5f2bb;
--magenta: #EB2F96;
--magenta1: #291321;
--magenta2: #40162f;
--magenta3: #551c3b;
--magenta4: #75204f;
--magenta5: #a02669;
--magenta6: #cb2b83;
--magenta7: #e0529c;
--magenta8: #f37fb7;
--magenta9: #f8a8cc;
--magenta10: #fad2e3;
--pink: #EB2F96;
--pink1: #291321;
--pink2: #40162f;
--pink3: #551c3b;
--pink4: #75204f;
--pink5: #a02669;
--pink6: #cb2b83;
--pink7: #e0529c;
--pink8: #f37fb7;
--pink9: #f8a8cc;
--pink10: #fad2e3;
--red: #F5222D;
--red1: #2a1215;
--red2: #431418;
--red3: #58181c;
--red4: #791a1f;
--red5: #a61d24;
--red6: #d32029;
--red7: #e84749;
--red8: #f37370;
--red9: #f89f9a;
--red10: #fac8c3;
--orange: #FA8C16;
--orange1: #2b1d11;
--orange2: #442a11;
--orange3: #593815;
--orange4: #7c4a15;
--orange5: #aa6215;
--orange6: #d87a16;
--orange7: #e89a3c;
--orange8: #f3b765;
--orange9: #f8cf8d;
--orange10: #fae3b7;
--yellow: #FADB14;
--yellow1: #2b2611;
--yellow2: #443b11;
--yellow3: #595014;
--yellow4: #7c6e14;
--yellow5: #aa9514;
--yellow6: #d8bd14;
--yellow7: #e8d639;
--yellow8: #f3ea62;
--yellow9: #f8f48b;
--yellow10: #fafab5;
--volcano: #FA541C;
--volcano1: #2b1611;
--volcano2: #441d12;
--volcano3: #592716;
--volcano4: #7c3118;
--volcano5: #aa3e19;
--volcano6: #d84a1b;
--volcano7: #e87040;
--volcano8: #f3956a;
--volcano9: #f8b692;
--volcano10: #fad4bc;
--geekblue: #2F54EB;
--geekblue1: #131629;
--geekblue2: #161d40;
--geekblue3: #1c2755;
--geekblue4: #203175;
--geekblue5: #263ea0;
--geekblue6: #2b4acb;
--geekblue7: #5273e0;
--geekblue8: #7f9ef3;
--geekblue9: #a8c1f8;
--geekblue10: #d2e0fa;
--gold: #FAAD14;
--gold1: #2b2111;
--gold2: #443111;
--gold3: #594214;
--gold4: #7c5914;
--gold5: #aa7714;
--gold6: #d89614;
--gold7: #e8b339;
--gold8: #f3cc62;
--gold9: #f8df8b;
--gold10: #faedb5;
--lime: #A0D911;
--lime1: #1f2611;
--lime2: #2e3c10;
--lime3: #3e4f13;
--lime4: #536d13;
--lime5: #6f9412;
--lime6: #8bbb11;
--lime7: #a9d134;
--lime8: #c9e75d;
--lime9: #e4f88b;
--lime10: #f0fab5;
--colorPrimary: #453fa2;
--colorSuccess: #49aa19;
--colorWarning: #d89614;
--colorError: #dc4446;
--colorInfo: #1668dc;
--colorLink: #1668dc;
--colorTextBase: #fff;
--colorBgBase: #000;
--fontFamily: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,
'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
'Noto Color Emoji';
--fontFamilyCode: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace;
--fontSize: 14;
--lineWidth: 1;
--lineType: solid;
--motionUnit: 0.1;
--motionBase: 0;
--motionEaseOutCirc: cubic-bezier(0.08, 0.82, 0.17, 1);
--motionEaseInOutCirc: cubic-bezier(0.78, 0.14, 0.15, 0.86);
--motionEaseOut: cubic-bezier(0.215, 0.61, 0.355, 1);
--motionEaseInOut: cubic-bezier(0.645, 0.045, 0.355, 1);
--motionEaseOutBack: cubic-bezier(0.12, 0.4, 0.29, 1.46);
--motionEaseInBack: cubic-bezier(0.71, -0.46, 0.88, 0.6);
--motionEaseInQuint: cubic-bezier(0.755, 0.05, 0.855, 0.06);
--motionEaseOutQuint: cubic-bezier(0.23, 1, 0.32, 1);
--borderRadius: 6;
--sizeUnit: 4;
--sizeStep: 4;
--sizePopupArrow: 16;
--controlHeight: 32;
--zIndexBase: 0;
--zIndexPopupBase: 1000;
--opacityImage: 1;
--colorLinkHover: #4E47BB;
--colorText: rgba(255, 255, 255, 0.85);
--colorTextSecondary: rgba(255, 255, 255, 0.65);
--colorTextTertiary: rgba(255, 255, 255, 0.45);
--colorTextQuaternary: rgba(255, 255, 255, 0.25);
--colorFill: rgba(255, 255, 255, 0.18);
--colorFillSecondary: rgba(255, 255, 255, 0.12);
--colorFillTertiary: rgba(255, 255, 255, 0.08);
--colorFillQuaternary: rgba(255, 255, 255, 0.04);
--colorBgSolid: rgba(255, 255, 255, 0.95);
--colorBgSolidHover: rgb(255, 255, 255);
--colorBgSolidActive: rgba(255, 255, 255, 0.9);
--colorBgLayout: #000000;
--colorBgContainer: #141414;
--colorBgElevated: #1f1f1f;
--colorBgSpotlight: #424242;
--colorBgBlur: rgba(255, 255, 255, 0.04);
--colorBorder: #424242;
--colorBorderSecondary: #303030;
--colorPrimaryBg: #161622;
--colorPrimaryBgHover: #1c1b34;
--colorPrimaryBorder: #252346;
--colorPrimaryBorderHover: #2e2b5f;
--colorPrimaryHover: #6b62b5;
--colorPrimaryActive: #3a3581;
--colorPrimaryTextHover: #6b62b5;
--colorPrimaryText: #453fa2;
--colorPrimaryTextActive: #3a3581;
--colorSuccessBg: #162312;
--colorSuccessBgHover: #1d3712;
--colorSuccessBorder: #274916;
--colorSuccessBorderHover: #306317;
--colorSuccessHover: #306317;
--colorSuccessActive: #3c8618;
--colorSuccessTextHover: #6abe39;
--colorSuccessText: #49aa19;
--colorSuccessTextActive: #3c8618;
--colorErrorBg: #2c1618;
--colorErrorBgHover: #451d1f;
--colorErrorBgFilledHover: #441e1f;
--colorErrorBgActive: #5b2526;
--colorErrorBorder: #5b2526;
--colorErrorBorderHover: #7e2e2f;
--colorErrorHover: #e86e6b;
--colorErrorActive: #ad393a;
--colorErrorTextHover: #e86e6b;
--colorErrorText: #dc4446;
--colorErrorTextActive: #ad393a;
--colorWarningBg: #2b2111;
--colorWarningBgHover: #443111;
--colorWarningBorder: #594214;
--colorWarningBorderHover: #7c5914;
--colorWarningHover: #7c5914;
--colorWarningActive: #aa7714;
--colorWarningTextHover: #e8b339;
--colorWarningText: #d89614;
--colorWarningTextActive: #aa7714;
--colorInfoBg: #111a2c;
--colorInfoBgHover: #112545;
--colorInfoBorder: #15325b;
--colorInfoBorderHover: #15417e;
--colorInfoHover: #15417e;
--colorInfoActive: #1554ad;
--colorInfoTextHover: #3c89e8;
--colorInfoText: #1668dc;
--colorInfoTextActive: #1554ad;
--colorLinkActive: #1554ad;
--colorBgMask: rgba(0, 0, 0, 0.45);
--colorWhite: #fff;
--fontSizeSM: 12;
--fontSizeLG: 16;
--fontSizeXL: 20;
--fontSizeHeading1: 38;
--fontSizeHeading2: 30;
--fontSizeHeading3: 24;
--fontSizeHeading4: 20;
--fontSizeHeading5: 16;
--lineHeight: 1.5714285714285714;
--lineHeightLG: 1.5;
--lineHeightSM: 1.6666666666666667;
--lineHeightHeading1: 1.2105263157894737;
--lineHeightHeading2: 1.2666666666666666;
--lineHeightHeading3: 1.3333333333333333;
--lineHeightHeading4: 1.4;
--lineHeightHeading5: 1.5;
--sizeXXL: 48;
--sizeXL: 32;
--sizeLG: 24;
--sizeMD: 20;
--sizeMS: 16;
--size: 16;
--sizeSM: 12;
--sizeXS: 8;
--sizeXXS: 4;
--controlHeightSM: 24;
--controlHeightXS: 16;
--controlHeightLG: 40;
--motionDurationFast: 0.1s;
--motionDurationMid: 0.2s;
--motionDurationSlow: 0.3s;
--lineWidthBold: 2;
--borderRadiusXS: 2;
--borderRadiusSM: 4;
--borderRadiusLG: 8;
--borderRadiusOuter: 4;
--colorFillContent: rgba(255, 255, 255, 0.12);
--colorFillContentHover: rgba(255, 255, 255, 0.18);
--colorFillAlter: rgba(255, 255, 255, 0.04);
--colorBgContainerDisabled: rgba(255, 255, 255, 0.08);
--colorBorderBg: #141414;
--colorSplit: rgba(253, 253, 253, 0.12);
--colorTextPlaceholder: rgba(255, 255, 255, 0.25);
--colorTextDisabled: rgba(255, 255, 255, 0.25);
--colorTextHeading: rgba(255, 255, 255, 0.85);
--colorTextLabel: rgba(255, 255, 255, 0.65);
--colorTextDescription: rgba(255, 255, 255, 0.45);
--colorTextLightSolid: #fff;
--colorHighlight: #dc4446;
--colorBgTextHover: rgba(255, 255, 255, 0.12);
--colorBgTextActive: rgba(255, 255, 255, 0.18);
--colorIcon: rgba(255, 255, 255, 0.45);
--colorIconHover: rgba(255, 255, 255, 0.85);
--colorErrorOutline: rgba(238, 38, 56, 0.11);
--colorWarningOutline: rgba(173, 107, 0, 0.15);
--fontSizeIcon: 12;
--lineWidthFocus: 3;
--controlOutlineWidth: 2;
--controlInteractiveSize: 16;
--controlItemBgHover: rgba(255, 255, 255, 0.08);
--controlItemBgActive: #161622;
--controlItemBgActiveHover: #1c1b34;
--controlItemBgActiveDisabled: rgba(255, 255, 255, 0.18);
--controlOutline: rgba(53, 53, 253, 0.06);
--fontWeightStrong: 600;
--opacityLoading: 0.65;
--linkDecoration: none;
--linkHoverDecoration: none;
--linkFocusDecoration: none;
--controlPaddingHorizontal: 12;
--controlPaddingHorizontalSM: 8;
--paddingXXS: 4;
--paddingXS: 8;
--paddingSM: 12;
--padding: 16;
--paddingMD: 20;
--paddingLG: 24;
--paddingXL: 32;
--paddingContentHorizontalLG: 24;
--paddingContentVerticalLG: 16;
--paddingContentHorizontal: 16;
--paddingContentVertical: 12;
--paddingContentHorizontalSM: 16;
--paddingContentVerticalSM: 8;
--marginXXS: 4;
--marginXS: 8;
--marginSM: 12;
--margin: 16;
--marginMD: 20;
--marginLG: 24;
--marginXL: 32;
--marginXXL: 48;
--boxShadow: 0 6px 16px 0 rgba(0, 0, 0, 0.08),
0 3px 6px -4px rgba(0, 0, 0, 0.12),
0 9px 28px 8px rgba(0, 0, 0, 0.05);
--boxShadowSecondary: 0 6px 16px 0 rgba(0, 0, 0, 0.08),
0 3px 6px -4px rgba(0, 0, 0, 0.12),
0 9px 28px 8px rgba(0, 0, 0, 0.05);
--boxShadowTertiary: 0 1px 2px 0 rgba(0, 0, 0, 0.03),
0 1px 6px -1px rgba(0, 0, 0, 0.02),
0 2px 4px 0 rgba(0, 0, 0, 0.02);
--screenXS: 480;
--screenXSMin: 480;
--screenXSMax: 575;
--screenSM: 576;
--screenSMMin: 576;
--screenSMMax: 767;
--screenMD: 768;
--screenMDMin: 768;
--screenMDMax: 991;
--screenLG: 992;
--screenLGMin: 992;
--screenLGMax: 1199;
--screenXL: 1200;
--screenXLMin: 1200;
--screenXLMax: 1599;
--screenXXL: 1600;
--screenXXLMin: 1600;
}

View File

@@ -0,0 +1,517 @@
const globalVariables = {
OxygenTheme: {
blue: '#1677FF',
purple: '#722ED1',
cyan: '#13C2C2',
green: '#52C41A',
magenta: '#EB2F96',
pink: '#EB2F96',
red: '#F5222D',
orange: '#FA8C16',
yellow: '#FADB14',
volcano: '#FA541C',
geekblue: '#2F54EB',
gold: '#FAAD14',
lime: '#A0D911',
colorPrimary: '#453fa2',
colorSuccess: '#49aa19',
colorWarning: '#d89614',
colorError: '#dc4446',
colorInfo: '#1668dc',
colorLink: '#1668dc',
colorTextBase: '#fff',
colorBgBase: '#000',
fontFamily:
"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,\n'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',\n'Noto Color Emoji'",
fontFamilyCode: "'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace",
fontSize: 14,
lineWidth: 1,
lineType: 'solid',
motionUnit: 0.1,
motionBase: 0,
motionEaseOutCirc: 'cubic-bezier(0.08, 0.82, 0.17, 1)',
motionEaseInOutCirc: 'cubic-bezier(0.78, 0.14, 0.15, 0.86)',
motionEaseOut: 'cubic-bezier(0.215, 0.61, 0.355, 1)',
motionEaseInOut: 'cubic-bezier(0.645, 0.045, 0.355, 1)',
motionEaseOutBack: 'cubic-bezier(0.12, 0.4, 0.29, 1.46)',
motionEaseInBack: 'cubic-bezier(0.71, -0.46, 0.88, 0.6)',
motionEaseInQuint: 'cubic-bezier(0.755, 0.05, 0.855, 0.06)',
motionEaseOutQuint: 'cubic-bezier(0.23, 1, 0.32, 1)',
borderRadius: 6,
sizeUnit: 4,
sizeStep: 4,
sizePopupArrow: 16,
controlHeight: 32,
zIndexBase: 0,
zIndexPopupBase: 1000,
opacityImage: 1,
motion: true,
colorLinkHover: '#4E47BB',
'blue-1': '#111a2c',
blue1: '#111a2c',
'blue-2': '#112545',
blue2: '#112545',
'blue-3': '#15325b',
blue3: '#15325b',
'blue-4': '#15417e',
blue4: '#15417e',
'blue-5': '#1554ad',
blue5: '#1554ad',
'blue-6': '#1668dc',
blue6: '#1668dc',
'blue-7': '#3c89e8',
blue7: '#3c89e8',
'blue-8': '#65a9f3',
blue8: '#65a9f3',
'blue-9': '#8dc5f8',
blue9: '#8dc5f8',
'blue-10': '#b7dcfa',
blue10: '#b7dcfa',
'purple-1': '#1a1325',
purple1: '#1a1325',
'purple-2': '#24163a',
purple2: '#24163a',
'purple-3': '#301c4d',
purple3: '#301c4d',
'purple-4': '#3e2069',
purple4: '#3e2069',
'purple-5': '#51258f',
purple5: '#51258f',
'purple-6': '#642ab5',
purple6: '#642ab5',
'purple-7': '#854eca',
purple7: '#854eca',
'purple-8': '#ab7ae0',
purple8: '#ab7ae0',
'purple-9': '#cda8f0',
purple9: '#cda8f0',
'purple-10': '#ebd7fa',
purple10: '#ebd7fa',
'cyan-1': '#112123',
cyan1: '#112123',
'cyan-2': '#113536',
cyan2: '#113536',
'cyan-3': '#144848',
cyan3: '#144848',
'cyan-4': '#146262',
cyan4: '#146262',
'cyan-5': '#138585',
cyan5: '#138585',
'cyan-6': '#13a8a8',
cyan6: '#13a8a8',
'cyan-7': '#33bcb7',
cyan7: '#33bcb7',
'cyan-8': '#58d1c9',
cyan8: '#58d1c9',
'cyan-9': '#84e2d8',
cyan9: '#84e2d8',
'cyan-10': '#b2f1e8',
cyan10: '#b2f1e8',
'green-1': '#162312',
green1: '#162312',
'green-2': '#1d3712',
green2: '#1d3712',
'green-3': '#274916',
green3: '#274916',
'green-4': '#306317',
green4: '#306317',
'green-5': '#3c8618',
green5: '#3c8618',
'green-6': '#49aa19',
green6: '#49aa19',
'green-7': '#6abe39',
green7: '#6abe39',
'green-8': '#8fd460',
green8: '#8fd460',
'green-9': '#b2e58b',
green9: '#b2e58b',
'green-10': '#d5f2bb',
green10: '#d5f2bb',
'magenta-1': '#291321',
magenta1: '#291321',
'magenta-2': '#40162f',
magenta2: '#40162f',
'magenta-3': '#551c3b',
magenta3: '#551c3b',
'magenta-4': '#75204f',
magenta4: '#75204f',
'magenta-5': '#a02669',
magenta5: '#a02669',
'magenta-6': '#cb2b83',
magenta6: '#cb2b83',
'magenta-7': '#e0529c',
magenta7: '#e0529c',
'magenta-8': '#f37fb7',
magenta8: '#f37fb7',
'magenta-9': '#f8a8cc',
magenta9: '#f8a8cc',
'magenta-10': '#fad2e3',
magenta10: '#fad2e3',
'pink-1': '#291321',
pink1: '#291321',
'pink-2': '#40162f',
pink2: '#40162f',
'pink-3': '#551c3b',
pink3: '#551c3b',
'pink-4': '#75204f',
pink4: '#75204f',
'pink-5': '#a02669',
pink5: '#a02669',
'pink-6': '#cb2b83',
pink6: '#cb2b83',
'pink-7': '#e0529c',
pink7: '#e0529c',
'pink-8': '#f37fb7',
pink8: '#f37fb7',
'pink-9': '#f8a8cc',
pink9: '#f8a8cc',
'pink-10': '#fad2e3',
pink10: '#fad2e3',
'red-1': '#2a1215',
red1: '#2a1215',
'red-2': '#431418',
red2: '#431418',
'red-3': '#58181c',
red3: '#58181c',
'red-4': '#791a1f',
red4: '#791a1f',
'red-5': '#a61d24',
red5: '#a61d24',
'red-6': '#d32029',
red6: '#d32029',
'red-7': '#e84749',
red7: '#e84749',
'red-8': '#f37370',
red8: '#f37370',
'red-9': '#f89f9a',
red9: '#f89f9a',
'red-10': '#fac8c3',
red10: '#fac8c3',
'orange-1': '#2b1d11',
orange1: '#2b1d11',
'orange-2': '#442a11',
orange2: '#442a11',
'orange-3': '#593815',
orange3: '#593815',
'orange-4': '#7c4a15',
orange4: '#7c4a15',
'orange-5': '#aa6215',
orange5: '#aa6215',
'orange-6': '#d87a16',
orange6: '#d87a16',
'orange-7': '#e89a3c',
orange7: '#e89a3c',
'orange-8': '#f3b765',
orange8: '#f3b765',
'orange-9': '#f8cf8d',
orange9: '#f8cf8d',
'orange-10': '#fae3b7',
orange10: '#fae3b7',
'yellow-1': '#2b2611',
yellow1: '#2b2611',
'yellow-2': '#443b11',
yellow2: '#443b11',
'yellow-3': '#595014',
yellow3: '#595014',
'yellow-4': '#7c6e14',
yellow4: '#7c6e14',
'yellow-5': '#aa9514',
yellow5: '#aa9514',
'yellow-6': '#d8bd14',
yellow6: '#d8bd14',
'yellow-7': '#e8d639',
yellow7: '#e8d639',
'yellow-8': '#f3ea62',
yellow8: '#f3ea62',
'yellow-9': '#f8f48b',
yellow9: '#f8f48b',
'yellow-10': '#fafab5',
yellow10: '#fafab5',
'volcano-1': '#2b1611',
volcano1: '#2b1611',
'volcano-2': '#441d12',
volcano2: '#441d12',
'volcano-3': '#592716',
volcano3: '#592716',
'volcano-4': '#7c3118',
volcano4: '#7c3118',
'volcano-5': '#aa3e19',
volcano5: '#aa3e19',
'volcano-6': '#d84a1b',
volcano6: '#d84a1b',
'volcano-7': '#e87040',
volcano7: '#e87040',
'volcano-8': '#f3956a',
volcano8: '#f3956a',
'volcano-9': '#f8b692',
volcano9: '#f8b692',
'volcano-10': '#fad4bc',
volcano10: '#fad4bc',
'geekblue-1': '#131629',
geekblue1: '#131629',
'geekblue-2': '#161d40',
geekblue2: '#161d40',
'geekblue-3': '#1c2755',
geekblue3: '#1c2755',
'geekblue-4': '#203175',
geekblue4: '#203175',
'geekblue-5': '#263ea0',
geekblue5: '#263ea0',
'geekblue-6': '#2b4acb',
geekblue6: '#2b4acb',
'geekblue-7': '#5273e0',
geekblue7: '#5273e0',
'geekblue-8': '#7f9ef3',
geekblue8: '#7f9ef3',
'geekblue-9': '#a8c1f8',
geekblue9: '#a8c1f8',
'geekblue-10': '#d2e0fa',
geekblue10: '#d2e0fa',
'gold-1': '#2b2111',
gold1: '#2b2111',
'gold-2': '#443111',
gold2: '#443111',
'gold-3': '#594214',
gold3: '#594214',
'gold-4': '#7c5914',
gold4: '#7c5914',
'gold-5': '#aa7714',
gold5: '#aa7714',
'gold-6': '#d89614',
gold6: '#d89614',
'gold-7': '#e8b339',
gold7: '#e8b339',
'gold-8': '#f3cc62',
gold8: '#f3cc62',
'gold-9': '#f8df8b',
gold9: '#f8df8b',
'gold-10': '#faedb5',
gold10: '#faedb5',
'lime-1': '#1f2611',
lime1: '#1f2611',
'lime-2': '#2e3c10',
lime2: '#2e3c10',
'lime-3': '#3e4f13',
lime3: '#3e4f13',
'lime-4': '#536d13',
lime4: '#536d13',
'lime-5': '#6f9412',
lime5: '#6f9412',
'lime-6': '#8bbb11',
lime6: '#8bbb11',
'lime-7': '#a9d134',
lime7: '#a9d134',
'lime-8': '#c9e75d',
lime8: '#c9e75d',
'lime-9': '#e4f88b',
lime9: '#e4f88b',
'lime-10': '#f0fab5',
lime10: '#f0fab5',
colorText: 'rgba(255, 255, 255, 0.85)',
colorTextSecondary: 'rgba(255, 255, 255, 0.65)',
colorTextTertiary: 'rgba(255, 255, 255, 0.45)',
colorTextQuaternary: 'rgba(255, 255, 255, 0.25)',
colorFill: 'rgba(255, 255, 255, 0.18)',
colorFillSecondary: 'rgba(255, 255, 255, 0.12)',
colorFillTertiary: 'rgba(255, 255, 255, 0.08)',
colorFillQuaternary: 'rgba(255, 255, 255, 0.04)',
colorBgSolid: 'rgba(255, 255, 255, 0.95)',
colorBgSolidHover: 'rgb(255, 255, 255)',
colorBgSolidActive: 'rgba(255, 255, 255, 0.9)',
colorBgLayout: '#000000',
colorBgContainer: '#141414',
colorBgElevated: '#1f1f1f',
colorBgSpotlight: '#424242',
colorBgBlur: 'rgba(255, 255, 255, 0.04)',
colorBorder: '#424242',
colorBorderSecondary: '#303030',
colorPrimaryBg: '#161622',
colorPrimaryBgHover: '#1c1b34',
colorPrimaryBorder: '#252346',
colorPrimaryBorderHover: '#2e2b5f',
colorPrimaryHover: '#6b62b5',
colorPrimaryActive: '#3a3581',
colorPrimaryTextHover: '#6b62b5',
colorPrimaryText: '#453fa2',
colorPrimaryTextActive: '#3a3581',
colorSuccessBg: '#162312',
colorSuccessBgHover: '#1d3712',
colorSuccessBorder: '#274916',
colorSuccessBorderHover: '#306317',
colorSuccessHover: '#306317',
colorSuccessActive: '#3c8618',
colorSuccessTextHover: '#6abe39',
colorSuccessText: '#49aa19',
colorSuccessTextActive: '#3c8618',
colorErrorBg: '#2c1618',
colorErrorBgHover: '#451d1f',
colorErrorBgFilledHover: '#441e1f',
colorErrorBgActive: '#5b2526',
colorErrorBorder: '#5b2526',
colorErrorBorderHover: '#7e2e2f',
colorErrorHover: '#e86e6b',
colorErrorActive: '#ad393a',
colorErrorTextHover: '#e86e6b',
colorErrorText: '#dc4446',
colorErrorTextActive: '#ad393a',
colorWarningBg: '#2b2111',
colorWarningBgHover: '#443111',
colorWarningBorder: '#594214',
colorWarningBorderHover: '#7c5914',
colorWarningHover: '#7c5914',
colorWarningActive: '#aa7714',
colorWarningTextHover: '#e8b339',
colorWarningText: '#d89614',
colorWarningTextActive: '#aa7714',
colorInfoBg: '#111a2c',
colorInfoBgHover: '#112545',
colorInfoBorder: '#15325b',
colorInfoBorderHover: '#15417e',
colorInfoHover: '#15417e',
colorInfoActive: '#1554ad',
colorInfoTextHover: '#3c89e8',
colorInfoText: '#1668dc',
colorInfoTextActive: '#1554ad',
colorLinkActive: '#1554ad',
colorBgMask: 'rgba(0, 0, 0, 0.45)',
colorWhite: '#fff',
fontSizeSM: 12,
fontSizeLG: 16,
fontSizeXL: 20,
fontSizeHeading1: 38,
fontSizeHeading2: 30,
fontSizeHeading3: 24,
fontSizeHeading4: 20,
fontSizeHeading5: 16,
lineHeight: 1.5714285714285714,
lineHeightLG: 1.5,
lineHeightSM: 1.6666666666666667,
fontHeight: 22,
fontHeightLG: 24,
fontHeightSM: 20,
lineHeightHeading1: 1.2105263157894737,
lineHeightHeading2: 1.2666666666666666,
lineHeightHeading3: 1.3333333333333333,
lineHeightHeading4: 1.4,
lineHeightHeading5: 1.5,
sizeXXL: 48,
sizeXL: 32,
sizeLG: 24,
sizeMD: 20,
sizeMS: 16,
size: 16,
sizeSM: 12,
sizeXS: 8,
sizeXXS: 4,
controlHeightSM: 24,
controlHeightXS: 16,
controlHeightLG: 40,
motionDurationFast: '0.1s',
motionDurationMid: '0.2s',
motionDurationSlow: '0.3s',
lineWidthBold: 2,
borderRadiusXS: 2,
borderRadiusSM: 4,
borderRadiusLG: 8,
borderRadiusOuter: 4,
colorFillContent: 'rgba(255, 255, 255, 0.12)',
colorFillContentHover: 'rgba(255, 255, 255, 0.18)',
colorFillAlter: 'rgba(255, 255, 255, 0.04)',
colorBgContainerDisabled: 'rgba(255, 255, 255, 0.08)',
colorBorderBg: '#141414',
colorSplit: 'rgba(253, 253, 253, 0.12)',
colorTextPlaceholder: 'rgba(255, 255, 255, 0.25)',
colorTextDisabled: 'rgba(255, 255, 255, 0.25)',
colorTextHeading: 'rgba(255, 255, 255, 0.85)',
colorTextLabel: 'rgba(255, 255, 255, 0.65)',
colorTextDescription: 'rgba(255, 255, 255, 0.45)',
colorTextLightSolid: '#fff',
colorHighlight: '#dc4446',
colorBgTextHover: 'rgba(255, 255, 255, 0.12)',
colorBgTextActive: 'rgba(255, 255, 255, 0.18)',
colorIcon: 'rgba(255, 255, 255, 0.45)',
colorIconHover: 'rgba(255, 255, 255, 0.85)',
colorErrorOutline: 'rgba(238, 38, 56, 0.11)',
colorWarningOutline: 'rgba(173, 107, 0, 0.15)',
fontSizeIcon: 12,
lineWidthFocus: 3,
controlOutlineWidth: 2,
controlInteractiveSize: 16,
controlItemBgHover: 'rgba(255, 255, 255, 0.08)',
controlItemBgActive: '#161622',
controlItemBgActiveHover: '#1c1b34',
controlItemBgActiveDisabled: 'rgba(255, 255, 255, 0.18)',
controlTmpOutline: 'rgba(255, 255, 255, 0.04)',
controlOutline: 'rgba(53, 53, 253, 0.06)',
fontWeightStrong: 600,
opacityLoading: 0.65,
linkDecoration: 'none',
linkHoverDecoration: 'none',
linkFocusDecoration: 'none',
controlPaddingHorizontal: 12,
controlPaddingHorizontalSM: 8,
paddingXXS: 4,
paddingXS: 8,
paddingSM: 12,
padding: 16,
paddingMD: 20,
paddingLG: 24,
paddingXL: 32,
paddingContentHorizontalLG: 24,
paddingContentVerticalLG: 16,
paddingContentHorizontal: 16,
paddingContentVertical: 12,
paddingContentHorizontalSM: 16,
paddingContentVerticalSM: 8,
marginXXS: 4,
marginXS: 8,
marginSM: 12,
margin: 16,
marginMD: 20,
marginLG: 24,
marginXL: 32,
marginXXL: 48,
boxShadow:
'\n 0 6px 16px 0 rgba(0, 0, 0, 0.08),\n 0 3px 6px -4px rgba(0, 0, 0, 0.12),\n 0 9px 28px 8px rgba(0, 0, 0, 0.05)\n ',
boxShadowSecondary:
'\n 0 6px 16px 0 rgba(0, 0, 0, 0.08),\n 0 3px 6px -4px rgba(0, 0, 0, 0.12),\n 0 9px 28px 8px rgba(0, 0, 0, 0.05)\n ',
boxShadowTertiary:
'\n 0 1px 2px 0 rgba(0, 0, 0, 0.03),\n 0 1px 6px -1px rgba(0, 0, 0, 0.02),\n 0 2px 4px 0 rgba(0, 0, 0, 0.02)\n ',
screenXS: 480,
screenXSMin: 480,
screenXSMax: 575,
screenSM: 576,
screenSMMin: 576,
screenSMMax: 767,
screenMD: 768,
screenMDMin: 768,
screenMDMax: 991,
screenLG: 992,
screenLGMin: 992,
screenLGMax: 1199,
screenXL: 1200,
screenXLMin: 1200,
screenXLMax: 1599,
screenXXL: 1600,
screenXXLMin: 1600,
boxShadowPopoverArrow: '2px 2px 5px rgba(0, 0, 0, 0.05)',
boxShadowCard:
'\n 0 1px 2px -2px rgba(0, 0, 0, 0.16),\n 0 3px 6px 0 rgba(0, 0, 0, 0.12),\n 0 5px 12px 4px rgba(0, 0, 0, 0.09)\n ',
boxShadowDrawerRight:
'\n -6px 0 16px 0 rgba(0, 0, 0, 0.08),\n -3px 0 6px -4px rgba(0, 0, 0, 0.12),\n -9px 0 28px 8px rgba(0, 0, 0, 0.05)\n ',
boxShadowDrawerLeft:
'\n 6px 0 16px 0 rgba(0, 0, 0, 0.08),\n 3px 0 6px -4px rgba(0, 0, 0, 0.12),\n 9px 0 28px 8px rgba(0, 0, 0, 0.05)\n ',
boxShadowDrawerUp:
'\n 0 6px 16px 0 rgba(0, 0, 0, 0.08),\n 0 3px 6px -4px rgba(0, 0, 0, 0.12),\n 0 9px 28px 8px rgba(0, 0, 0, 0.05)\n ',
boxShadowDrawerDown:
'\n 0 -6px 16px 0 rgba(0, 0, 0, 0.08),\n 0 -3px 6px -4px rgba(0, 0, 0, 0.12),\n 0 -9px 28px 8px rgba(0, 0, 0, 0.05)\n ',
boxShadowTabsOverflowLeft: 'inset 10px 0 8px -8px rgba(0, 0, 0, 0.08)',
boxShadowTabsOverflowRight: 'inset -10px 0 8px -8px rgba(0, 0, 0, 0.08)',
boxShadowTabsOverflowTop: 'inset 0 10px 8px -8px rgba(0, 0, 0, 0.08)',
boxShadowTabsOverflowBottom: 'inset 0 -10px 8px -8px rgba(0, 0, 0, 0.08)'
}
}
for (const key in globalVariables) {
globalThis[key] = globalVariables[key]
}

View File

@@ -0,0 +1,365 @@
:root {
--blue: #1677FF;
--blue1: #e6f4ff;
--blue2: #bae0ff;
--blue3: #91caff;
--blue4: #69b1ff;
--blue5: #4096ff;
--blue6: #1677ff;
--blue7: #0958d9;
--blue8: #003eb3;
--blue9: #002c8c;
--blue10: #001d66;
--purple: #722ED1;
--purple1: #f9f0ff;
--purple2: #efdbff;
--purple3: #d3adf7;
--purple4: #b37feb;
--purple5: #9254de;
--purple6: #722ed1;
--purple7: #531dab;
--purple8: #391085;
--purple9: #22075e;
--purple10: #120338;
--cyan: #13C2C2;
--cyan1: #e6fffb;
--cyan2: #b5f5ec;
--cyan3: #87e8de;
--cyan4: #5cdbd3;
--cyan5: #36cfc9;
--cyan6: #13c2c2;
--cyan7: #08979c;
--cyan8: #006d75;
--cyan9: #00474f;
--cyan10: #002329;
--green: #52C41A;
--green1: #f6ffed;
--green2: #d9f7be;
--green3: #b7eb8f;
--green4: #95de64;
--green5: #73d13d;
--green6: #52c41a;
--green7: #389e0d;
--green8: #237804;
--green9: #135200;
--green10: #092b00;
--magenta: #EB2F96;
--magenta1: #fff0f6;
--magenta2: #ffd6e7;
--magenta3: #ffadd2;
--magenta4: #ff85c0;
--magenta5: #f759ab;
--magenta6: #eb2f96;
--magenta7: #c41d7f;
--magenta8: #9e1068;
--magenta9: #780650;
--magenta10: #520339;
--pink: #EB2F96;
--pink1: #fff0f6;
--pink2: #ffd6e7;
--pink3: #ffadd2;
--pink4: #ff85c0;
--pink5: #f759ab;
--pink6: #eb2f96;
--pink7: #c41d7f;
--pink8: #9e1068;
--pink9: #780650;
--pink10: #520339;
--red: #F5222D;
--red1: #fff1f0;
--red2: #ffccc7;
--red3: #ffa39e;
--red4: #ff7875;
--red5: #ff4d4f;
--red6: #f5222d;
--red7: #cf1322;
--red8: #a8071a;
--red9: #820014;
--red10: #5c0011;
--orange: #FA8C16;
--orange1: #fff7e6;
--orange2: #ffe7ba;
--orange3: #ffd591;
--orange4: #ffc069;
--orange5: #ffa940;
--orange6: #fa8c16;
--orange7: #d46b08;
--orange8: #ad4e00;
--orange9: #873800;
--orange10: #612500;
--yellow: #FADB14;
--yellow1: #feffe6;
--yellow2: #ffffb8;
--yellow3: #fffb8f;
--yellow4: #fff566;
--yellow5: #ffec3d;
--yellow6: #fadb14;
--yellow7: #d4b106;
--yellow8: #ad8b00;
--yellow9: #876800;
--yellow10: #614700;
--volcano: #FA541C;
--volcano1: #fff2e8;
--volcano2: #ffd8bf;
--volcano3: #ffbb96;
--volcano4: #ff9c6e;
--volcano5: #ff7a45;
--volcano6: #fa541c;
--volcano7: #d4380d;
--volcano8: #ad2102;
--volcano9: #871400;
--volcano10: #610b00;
--geekblue: #2F54EB;
--geekblue1: #f0f5ff;
--geekblue2: #d6e4ff;
--geekblue3: #adc6ff;
--geekblue4: #85a5ff;
--geekblue5: #597ef7;
--geekblue6: #2f54eb;
--geekblue7: #1d39c4;
--geekblue8: #10239e;
--geekblue9: #061178;
--geekblue10: #030852;
--gold: #FAAD14;
--gold1: #fffbe6;
--gold2: #fff1b8;
--gold3: #ffe58f;
--gold4: #ffd666;
--gold5: #ffc53d;
--gold6: #faad14;
--gold7: #d48806;
--gold8: #ad6800;
--gold9: #874d00;
--gold10: #613400;
--lime: #A0D911;
--lime1: #fcffe6;
--lime2: #f4ffb8;
--lime3: #eaff8f;
--lime4: #d3f261;
--lime5: #bae637;
--lime6: #a0d911;
--lime7: #7cb305;
--lime8: #5b8c00;
--lime9: #3f6600;
--lime10: #254000;
--colorPrimary: #4e47bb;
--colorSuccess: #52c41a;
--colorWarning: #faad14;
--colorError: #ff4d4f;
--colorInfo: #1677ff;
--colorLink: #1677ff;
--colorTextBase: #000;
--colorBgBase: #fff;
--fontFamily: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,
'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',
'Noto Color Emoji';
--fontFamilyCode: 'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace;
--fontSize: 14;
--lineWidth: 1;
--lineType: solid;
--motionUnit: 0.1;
--motionBase: 0;
--motionEaseOutCirc: cubic-bezier(0.08, 0.82, 0.17, 1);
--motionEaseInOutCirc: cubic-bezier(0.78, 0.14, 0.15, 0.86);
--motionEaseOut: cubic-bezier(0.215, 0.61, 0.355, 1);
--motionEaseInOut: cubic-bezier(0.645, 0.045, 0.355, 1);
--motionEaseOutBack: cubic-bezier(0.12, 0.4, 0.29, 1.46);
--motionEaseInBack: cubic-bezier(0.71, -0.46, 0.88, 0.6);
--motionEaseInQuint: cubic-bezier(0.755, 0.05, 0.855, 0.06);
--motionEaseOutQuint: cubic-bezier(0.23, 1, 0.32, 1);
--borderRadius: 6;
--sizeUnit: 4;
--sizeStep: 4;
--sizePopupArrow: 16;
--controlHeight: 32;
--zIndexBase: 0;
--zIndexPopupBase: 1000;
--opacityImage: 1;
--colorLinkHover: #4E47BB;
--colorText: rgba(0, 0, 0, 0.88);
--colorTextSecondary: rgba(0, 0, 0, 0.65);
--colorTextTertiary: rgba(0, 0, 0, 0.45);
--colorTextQuaternary: rgba(0, 0, 0, 0.25);
--colorFill: rgba(0, 0, 0, 0.15);
--colorFillSecondary: rgba(0, 0, 0, 0.06);
--colorFillTertiary: rgba(0, 0, 0, 0.04);
--colorFillQuaternary: rgba(0, 0, 0, 0.02);
--colorBgSolid: rgb(0, 0, 0);
--colorBgSolidHover: rgba(0, 0, 0, 0.75);
--colorBgSolidActive: rgba(0, 0, 0, 0.95);
--colorBgLayout: #f5f5f5;
--colorBgContainer: #ffffff;
--colorBgElevated: #ffffff;
--colorBgSpotlight: rgba(0, 0, 0, 0.85);
--colorBgBlur: transparent;
--colorBorder: #d9d9d9;
--colorBorderSecondary: #f0f0f0;
--colorPrimaryBg: #eeebfa;
--colorPrimaryBgHover: #e2dfed;
--colorPrimaryBorder: #c6c1e0;
--colorPrimaryBorderHover: #9d94d4;
--colorPrimaryHover: #756bc7;
--colorPrimaryActive: #343194;
--colorPrimaryTextHover: #756bc7;
--colorPrimaryText: #4e47bb;
--colorPrimaryTextActive: #343194;
--colorSuccessBg: #f6ffed;
--colorSuccessBgHover: #d9f7be;
--colorSuccessBorder: #b7eb8f;
--colorSuccessBorderHover: #95de64;
--colorSuccessHover: #95de64;
--colorSuccessActive: #389e0d;
--colorSuccessTextHover: #73d13d;
--colorSuccessText: #52c41a;
--colorSuccessTextActive: #389e0d;
--colorErrorBg: #fff2f0;
--colorErrorBgHover: #fff1f0;
--colorErrorBgFilledHover: #ffdfdc;
--colorErrorBgActive: #ffccc7;
--colorErrorBorder: #ffccc7;
--colorErrorBorderHover: #ffa39e;
--colorErrorHover: #ff7875;
--colorErrorActive: #d9363e;
--colorErrorTextHover: #ff7875;
--colorErrorText: #ff4d4f;
--colorErrorTextActive: #d9363e;
--colorWarningBg: #fffbe6;
--colorWarningBgHover: #fff1b8;
--colorWarningBorder: #ffe58f;
--colorWarningBorderHover: #ffd666;
--colorWarningHover: #ffd666;
--colorWarningActive: #d48806;
--colorWarningTextHover: #ffc53d;
--colorWarningText: #faad14;
--colorWarningTextActive: #d48806;
--colorInfoBg: #e6f4ff;
--colorInfoBgHover: #bae0ff;
--colorInfoBorder: #91caff;
--colorInfoBorderHover: #69b1ff;
--colorInfoHover: #69b1ff;
--colorInfoActive: #0958d9;
--colorInfoTextHover: #4096ff;
--colorInfoText: #1677ff;
--colorInfoTextActive: #0958d9;
--colorLinkActive: #0958d9;
--colorBgMask: rgba(0, 0, 0, 0.45);
--colorWhite: #fff;
--fontSizeSM: 12;
--fontSizeLG: 16;
--fontSizeXL: 20;
--fontSizeHeading1: 38;
--fontSizeHeading2: 30;
--fontSizeHeading3: 24;
--fontSizeHeading4: 20;
--fontSizeHeading5: 16;
--lineHeight: 1.5714285714285714;
--lineHeightLG: 1.5;
--lineHeightSM: 1.6666666666666667;
--lineHeightHeading1: 1.2105263157894737;
--lineHeightHeading2: 1.2666666666666666;
--lineHeightHeading3: 1.3333333333333333;
--lineHeightHeading4: 1.4;
--lineHeightHeading5: 1.5;
--sizeXXL: 48;
--sizeXL: 32;
--sizeLG: 24;
--sizeMD: 20;
--sizeMS: 16;
--size: 16;
--sizeSM: 12;
--sizeXS: 8;
--sizeXXS: 4;
--controlHeightSM: 24;
--controlHeightXS: 16;
--controlHeightLG: 40;
--motionDurationFast: 0.1s;
--motionDurationMid: 0.2s;
--motionDurationSlow: 0.3s;
--lineWidthBold: 2;
--borderRadiusXS: 2;
--borderRadiusSM: 4;
--borderRadiusLG: 8;
--borderRadiusOuter: 4;
--colorFillContent: rgba(0, 0, 0, 0.06);
--colorFillContentHover: rgba(0, 0, 0, 0.15);
--colorFillAlter: rgba(0, 0, 0, 0.02);
--colorBgContainerDisabled: rgba(0, 0, 0, 0.04);
--colorBorderBg: #ffffff;
--colorSplit: rgba(5, 5, 5, 0.06);
--colorTextPlaceholder: rgba(0, 0, 0, 0.25);
--colorTextDisabled: rgba(0, 0, 0, 0.25);
--colorTextHeading: rgba(0, 0, 0, 0.88);
--colorTextLabel: rgba(0, 0, 0, 0.65);
--colorTextDescription: rgba(0, 0, 0, 0.45);
--colorTextLightSolid: #fff;
--colorHighlight: #ff4d4f;
--colorBgTextHover: rgba(0, 0, 0, 0.06);
--colorBgTextActive: rgba(0, 0, 0, 0.15);
--colorIcon: rgba(0, 0, 0, 0.45);
--colorIconHover: rgba(0, 0, 0, 0.88);
--colorErrorOutline: rgba(255, 38, 5, 0.06);
--colorWarningOutline: rgba(255, 215, 5, 0.1);
--fontSizeIcon: 12;
--lineWidthFocus: 3;
--controlOutlineWidth: 2;
--controlInteractiveSize: 16;
--controlItemBgHover: rgba(0, 0, 0, 0.04);
--controlItemBgActive: #eeebfa;
--controlItemBgActiveHover: #e2dfed;
--controlItemBgActiveDisabled: rgba(0, 0, 0, 0.15);
--controlOutline: rgba(42, 5, 192, 0.08);
--fontWeightStrong: 600;
--opacityLoading: 0.65;
--linkDecoration: none;
--linkHoverDecoration: none;
--linkFocusDecoration: none;
--controlPaddingHorizontal: 12;
--controlPaddingHorizontalSM: 8;
--paddingXXS: 4;
--paddingXS: 8;
--paddingSM: 12;
--padding: 16;
--paddingMD: 20;
--paddingLG: 24;
--paddingXL: 32;
--paddingContentHorizontalLG: 24;
--paddingContentVerticalLG: 16;
--paddingContentHorizontal: 16;
--paddingContentVertical: 12;
--paddingContentHorizontalSM: 16;
--paddingContentVerticalSM: 8;
--marginXXS: 4;
--marginXS: 8;
--marginSM: 12;
--margin: 16;
--marginMD: 20;
--marginLG: 24;
--marginXL: 32;
--marginXXL: 48;
--boxShadow: 0 6px 16px 0 rgba(0, 0, 0, 0.08),
0 3px 6px -4px rgba(0, 0, 0, 0.12),
0 9px 28px 8px rgba(0, 0, 0, 0.05);
--boxShadowSecondary: 0 6px 16px 0 rgba(0, 0, 0, 0.08),
0 3px 6px -4px rgba(0, 0, 0, 0.12),
0 9px 28px 8px rgba(0, 0, 0, 0.05);
--boxShadowTertiary: 0 1px 2px 0 rgba(0, 0, 0, 0.03),
0 1px 6px -1px rgba(0, 0, 0, 0.02),
0 2px 4px 0 rgba(0, 0, 0, 0.02);
--screenXS: 480;
--screenXSMin: 480;
--screenXSMax: 575;
--screenSM: 576;
--screenSMMin: 576;
--screenSMMax: 767;
--screenMD: 768;
--screenMDMin: 768;
--screenMDMax: 991;
--screenLG: 992;
--screenLGMin: 992;
--screenLGMax: 1199;
--screenXL: 1200;
--screenXLMin: 1200;
--screenXLMax: 1599;
--screenXXL: 1600;
--screenXXLMin: 1600;
}

View File

@@ -0,0 +1,518 @@
const globalVariables = {
OxygenTheme: {
blue: '#1677FF',
purple: '#722ED1',
cyan: '#13C2C2',
green: '#52C41A',
magenta: '#EB2F96',
pink: '#EB2F96',
red: '#F5222D',
orange: '#FA8C16',
yellow: '#FADB14',
volcano: '#FA541C',
geekblue: '#2F54EB',
gold: '#FAAD14',
lime: '#A0D911',
colorPrimary: '#4e47bb',
colorSuccess: '#52c41a',
colorWarning: '#faad14',
colorError: '#ff4d4f',
colorInfo: '#1677ff',
colorLink: '#1677ff',
colorTextBase: '#000',
colorBgBase: '#fff',
fontFamily:
"-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial,\n'Noto Sans', sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji', 'Segoe UI Symbol',\n'Noto Color Emoji'",
fontFamilyCode: "'SFMono-Regular', Consolas, 'Liberation Mono', Menlo, Courier, monospace",
fontSize: 14,
lineWidth: 1,
lineType: 'solid',
motionUnit: 0.1,
motionBase: 0,
motionEaseOutCirc: 'cubic-bezier(0.08, 0.82, 0.17, 1)',
motionEaseInOutCirc: 'cubic-bezier(0.78, 0.14, 0.15, 0.86)',
motionEaseOut: 'cubic-bezier(0.215, 0.61, 0.355, 1)',
motionEaseInOut: 'cubic-bezier(0.645, 0.045, 0.355, 1)',
motionEaseOutBack: 'cubic-bezier(0.12, 0.4, 0.29, 1.46)',
motionEaseInBack: 'cubic-bezier(0.71, -0.46, 0.88, 0.6)',
motionEaseInQuint: 'cubic-bezier(0.755, 0.05, 0.855, 0.06)',
motionEaseOutQuint: 'cubic-bezier(0.23, 1, 0.32, 1)',
borderRadius: 6,
sizeUnit: 4,
sizeStep: 4,
sizePopupArrow: 16,
controlHeight: 32,
zIndexBase: 0,
zIndexPopupBase: 1000,
opacityImage: 1,
motion: true,
colorLinkHover: '#4E47BB',
'blue-1': '#e6f4ff',
blue1: '#e6f4ff',
'blue-2': '#bae0ff',
blue2: '#bae0ff',
'blue-3': '#91caff',
blue3: '#91caff',
'blue-4': '#69b1ff',
blue4: '#69b1ff',
'blue-5': '#4096ff',
blue5: '#4096ff',
'blue-6': '#1677ff',
blue6: '#1677ff',
'blue-7': '#0958d9',
blue7: '#0958d9',
'blue-8': '#003eb3',
blue8: '#003eb3',
'blue-9': '#002c8c',
blue9: '#002c8c',
'blue-10': '#001d66',
blue10: '#001d66',
'purple-1': '#f9f0ff',
purple1: '#f9f0ff',
'purple-2': '#efdbff',
purple2: '#efdbff',
'purple-3': '#d3adf7',
purple3: '#d3adf7',
'purple-4': '#b37feb',
purple4: '#b37feb',
'purple-5': '#9254de',
purple5: '#9254de',
'purple-6': '#722ed1',
purple6: '#722ed1',
'purple-7': '#531dab',
purple7: '#531dab',
'purple-8': '#391085',
purple8: '#391085',
'purple-9': '#22075e',
purple9: '#22075e',
'purple-10': '#120338',
purple10: '#120338',
'cyan-1': '#e6fffb',
cyan1: '#e6fffb',
'cyan-2': '#b5f5ec',
cyan2: '#b5f5ec',
'cyan-3': '#87e8de',
cyan3: '#87e8de',
'cyan-4': '#5cdbd3',
cyan4: '#5cdbd3',
'cyan-5': '#36cfc9',
cyan5: '#36cfc9',
'cyan-6': '#13c2c2',
cyan6: '#13c2c2',
'cyan-7': '#08979c',
cyan7: '#08979c',
'cyan-8': '#006d75',
cyan8: '#006d75',
'cyan-9': '#00474f',
cyan9: '#00474f',
'cyan-10': '#002329',
cyan10: '#002329',
'green-1': '#f6ffed',
green1: '#f6ffed',
'green-2': '#d9f7be',
green2: '#d9f7be',
'green-3': '#b7eb8f',
green3: '#b7eb8f',
'green-4': '#95de64',
green4: '#95de64',
'green-5': '#73d13d',
green5: '#73d13d',
'green-6': '#52c41a',
green6: '#52c41a',
'green-7': '#389e0d',
green7: '#389e0d',
'green-8': '#237804',
green8: '#237804',
'green-9': '#135200',
green9: '#135200',
'green-10': '#092b00',
green10: '#092b00',
'magenta-1': '#fff0f6',
magenta1: '#fff0f6',
'magenta-2': '#ffd6e7',
magenta2: '#ffd6e7',
'magenta-3': '#ffadd2',
magenta3: '#ffadd2',
'magenta-4': '#ff85c0',
magenta4: '#ff85c0',
'magenta-5': '#f759ab',
magenta5: '#f759ab',
'magenta-6': '#eb2f96',
magenta6: '#eb2f96',
'magenta-7': '#c41d7f',
magenta7: '#c41d7f',
'magenta-8': '#9e1068',
magenta8: '#9e1068',
'magenta-9': '#780650',
magenta9: '#780650',
'magenta-10': '#520339',
magenta10: '#520339',
'pink-1': '#fff0f6',
pink1: '#fff0f6',
'pink-2': '#ffd6e7',
pink2: '#ffd6e7',
'pink-3': '#ffadd2',
pink3: '#ffadd2',
'pink-4': '#ff85c0',
pink4: '#ff85c0',
'pink-5': '#f759ab',
pink5: '#f759ab',
'pink-6': '#eb2f96',
pink6: '#eb2f96',
'pink-7': '#c41d7f',
pink7: '#c41d7f',
'pink-8': '#9e1068',
pink8: '#9e1068',
'pink-9': '#780650',
pink9: '#780650',
'pink-10': '#520339',
pink10: '#520339',
'red-1': '#fff1f0',
red1: '#fff1f0',
'red-2': '#ffccc7',
red2: '#ffccc7',
'red-3': '#ffa39e',
red3: '#ffa39e',
'red-4': '#ff7875',
red4: '#ff7875',
'red-5': '#ff4d4f',
red5: '#ff4d4f',
'red-6': '#f5222d',
red6: '#f5222d',
'red-7': '#cf1322',
red7: '#cf1322',
'red-8': '#a8071a',
red8: '#a8071a',
'red-9': '#820014',
red9: '#820014',
'red-10': '#5c0011',
red10: '#5c0011',
'orange-1': '#fff7e6',
orange1: '#fff7e6',
'orange-2': '#ffe7ba',
orange2: '#ffe7ba',
'orange-3': '#ffd591',
orange3: '#ffd591',
'orange-4': '#ffc069',
orange4: '#ffc069',
'orange-5': '#ffa940',
orange5: '#ffa940',
'orange-6': '#fa8c16',
orange6: '#fa8c16',
'orange-7': '#d46b08',
orange7: '#d46b08',
'orange-8': '#ad4e00',
orange8: '#ad4e00',
'orange-9': '#873800',
orange9: '#873800',
'orange-10': '#612500',
orange10: '#612500',
'yellow-1': '#feffe6',
yellow1: '#feffe6',
'yellow-2': '#ffffb8',
yellow2: '#ffffb8',
'yellow-3': '#fffb8f',
yellow3: '#fffb8f',
'yellow-4': '#fff566',
yellow4: '#fff566',
'yellow-5': '#ffec3d',
yellow5: '#ffec3d',
'yellow-6': '#fadb14',
yellow6: '#fadb14',
'yellow-7': '#d4b106',
yellow7: '#d4b106',
'yellow-8': '#ad8b00',
yellow8: '#ad8b00',
'yellow-9': '#876800',
yellow9: '#876800',
'yellow-10': '#614700',
yellow10: '#614700',
'volcano-1': '#fff2e8',
volcano1: '#fff2e8',
'volcano-2': '#ffd8bf',
volcano2: '#ffd8bf',
'volcano-3': '#ffbb96',
volcano3: '#ffbb96',
'volcano-4': '#ff9c6e',
volcano4: '#ff9c6e',
'volcano-5': '#ff7a45',
volcano5: '#ff7a45',
'volcano-6': '#fa541c',
volcano6: '#fa541c',
'volcano-7': '#d4380d',
volcano7: '#d4380d',
'volcano-8': '#ad2102',
volcano8: '#ad2102',
'volcano-9': '#871400',
volcano9: '#871400',
'volcano-10': '#610b00',
volcano10: '#610b00',
'geekblue-1': '#f0f5ff',
geekblue1: '#f0f5ff',
'geekblue-2': '#d6e4ff',
geekblue2: '#d6e4ff',
'geekblue-3': '#adc6ff',
geekblue3: '#adc6ff',
'geekblue-4': '#85a5ff',
geekblue4: '#85a5ff',
'geekblue-5': '#597ef7',
geekblue5: '#597ef7',
'geekblue-6': '#2f54eb',
geekblue6: '#2f54eb',
'geekblue-7': '#1d39c4',
geekblue7: '#1d39c4',
'geekblue-8': '#10239e',
geekblue8: '#10239e',
'geekblue-9': '#061178',
geekblue9: '#061178',
'geekblue-10': '#030852',
geekblue10: '#030852',
'gold-1': '#fffbe6',
gold1: '#fffbe6',
'gold-2': '#fff1b8',
gold2: '#fff1b8',
'gold-3': '#ffe58f',
gold3: '#ffe58f',
'gold-4': '#ffd666',
gold4: '#ffd666',
'gold-5': '#ffc53d',
gold5: '#ffc53d',
'gold-6': '#faad14',
gold6: '#faad14',
'gold-7': '#d48806',
gold7: '#d48806',
'gold-8': '#ad6800',
gold8: '#ad6800',
'gold-9': '#874d00',
gold9: '#874d00',
'gold-10': '#613400',
gold10: '#613400',
'lime-1': '#fcffe6',
lime1: '#fcffe6',
'lime-2': '#f4ffb8',
lime2: '#f4ffb8',
'lime-3': '#eaff8f',
lime3: '#eaff8f',
'lime-4': '#d3f261',
lime4: '#d3f261',
'lime-5': '#bae637',
lime5: '#bae637',
'lime-6': '#a0d911',
lime6: '#a0d911',
'lime-7': '#7cb305',
lime7: '#7cb305',
'lime-8': '#5b8c00',
lime8: '#5b8c00',
'lime-9': '#3f6600',
lime9: '#3f6600',
'lime-10': '#254000',
lime10: '#254000',
colorText: 'rgba(0, 0, 0, 0.88)',
colorTextSecondary: 'rgba(0, 0, 0, 0.65)',
colorTextTertiary: 'rgba(0, 0, 0, 0.45)',
colorTextQuaternary: 'rgba(0, 0, 0, 0.25)',
colorFill: 'rgba(0, 0, 0, 0.15)',
colorFillSecondary: 'rgba(0, 0, 0, 0.06)',
colorFillTertiary: 'rgba(0, 0, 0, 0.04)',
colorFillQuaternary: 'rgba(0, 0, 0, 0.02)',
colorBgSolid: 'rgb(0, 0, 0)',
colorBgSolidHover: 'rgba(0, 0, 0, 0.75)',
colorBgSolidActive: 'rgba(0, 0, 0, 0.95)',
colorBgLayout: '#f5f5f5',
colorBgContainer: '#ffffff',
colorBgElevated: '#ffffff',
colorBgSpotlight: 'rgba(0, 0, 0, 0.85)',
colorBgBlur: 'transparent',
colorBorder: '#d9d9d9',
colorBorderSecondary: '#f0f0f0',
colorPrimaryBg: '#eeebfa',
colorPrimaryBgHover: '#e2dfed',
colorPrimaryBorder: '#c6c1e0',
colorPrimaryBorderHover: '#9d94d4',
colorPrimaryHover: '#756bc7',
colorPrimaryActive: '#343194',
colorPrimaryTextHover: '#756bc7',
colorPrimaryText: '#4e47bb',
colorPrimaryTextActive: '#343194',
colorSuccessBg: '#f6ffed',
colorSuccessBgHover: '#d9f7be',
colorSuccessBorder: '#b7eb8f',
colorSuccessBorderHover: '#95de64',
colorSuccessHover: '#95de64',
colorSuccessActive: '#389e0d',
colorSuccessTextHover: '#73d13d',
colorSuccessText: '#52c41a',
colorSuccessTextActive: '#389e0d',
colorErrorBg: '#fff2f0',
colorErrorBgHover: '#fff1f0',
colorErrorBgFilledHover: '#ffdfdc',
colorErrorBgActive: '#ffccc7',
colorErrorBorder: '#ffccc7',
colorErrorBorderHover: '#ffa39e',
colorErrorHover: '#ff7875',
colorErrorActive: '#d9363e',
colorErrorTextHover: '#ff7875',
colorErrorText: '#ff4d4f',
colorErrorTextActive: '#d9363e',
colorWarningBg: '#fffbe6',
colorWarningBgHover: '#fff1b8',
colorWarningBorder: '#ffe58f',
colorWarningBorderHover: '#ffd666',
colorWarningHover: '#ffd666',
colorWarningActive: '#d48806',
colorWarningTextHover: '#ffc53d',
colorWarningText: '#faad14',
colorWarningTextActive: '#d48806',
colorInfoBg: '#e6f4ff',
colorInfoBgHover: '#bae0ff',
colorInfoBorder: '#91caff',
colorInfoBorderHover: '#69b1ff',
colorInfoHover: '#69b1ff',
colorInfoActive: '#0958d9',
colorInfoTextHover: '#4096ff',
colorInfoText: '#1677ff',
colorInfoTextActive: '#0958d9',
colorLinkActive: '#0958d9',
colorBgMask: 'rgba(0, 0, 0, 0.45)',
colorWhite: '#fff',
fontSizeSM: 12,
fontSizeLG: 16,
fontSizeXL: 20,
fontSizeHeading1: 38,
fontSizeHeading2: 30,
fontSizeHeading3: 24,
fontSizeHeading4: 20,
fontSizeHeading5: 16,
lineHeight: 1.5714285714285714,
lineHeightLG: 1.5,
lineHeightSM: 1.6666666666666667,
fontHeight: 22,
fontHeightLG: 24,
fontHeightSM: 20,
lineHeightHeading1: 1.2105263157894737,
lineHeightHeading2: 1.2666666666666666,
lineHeightHeading3: 1.3333333333333333,
lineHeightHeading4: 1.4,
lineHeightHeading5: 1.5,
sizeXXL: 48,
sizeXL: 32,
sizeLG: 24,
sizeMD: 20,
sizeMS: 16,
size: 16,
sizeSM: 12,
sizeXS: 8,
sizeXXS: 4,
controlHeightSM: 24,
controlHeightXS: 16,
controlHeightLG: 40,
motionDurationFast: '0.1s',
motionDurationMid: '0.2s',
motionDurationSlow: '0.3s',
lineWidthBold: 2,
borderRadiusXS: 2,
borderRadiusSM: 4,
borderRadiusLG: 8,
borderRadiusOuter: 4,
colorFillContent: 'rgba(0, 0, 0, 0.06)',
colorFillContentHover: 'rgba(0, 0, 0, 0.15)',
colorFillAlter: 'rgba(0, 0, 0, 0.02)',
colorBgContainerDisabled: 'rgba(0, 0, 0, 0.04)',
colorBorderBg: '#ffffff',
colorSplit: 'rgba(5, 5, 5, 0.06)',
colorTextPlaceholder: 'rgba(0, 0, 0, 0.25)',
colorTextDisabled: 'rgba(0, 0, 0, 0.25)',
colorTextHeading: 'rgba(0, 0, 0, 0.88)',
colorTextLabel: 'rgba(0, 0, 0, 0.65)',
colorTextDescription: 'rgba(0, 0, 0, 0.45)',
colorTextLightSolid: '#fff',
colorHighlight: '#ff4d4f',
colorBgTextHover: 'rgba(0, 0, 0, 0.06)',
colorBgTextActive: 'rgba(0, 0, 0, 0.15)',
colorIcon: 'rgba(0, 0, 0, 0.45)',
colorIconHover: 'rgba(0, 0, 0, 0.88)',
colorErrorOutline: 'rgba(255, 38, 5, 0.06)',
colorWarningOutline: 'rgba(255, 215, 5, 0.1)',
fontSizeIcon: 12,
lineWidthFocus: 3,
controlOutlineWidth: 2,
controlInteractiveSize: 16,
controlItemBgHover: 'rgba(0, 0, 0, 0.04)',
controlItemBgActive: '#eeebfa',
controlItemBgActiveHover: '#e2dfed',
controlItemBgActiveDisabled: 'rgba(0, 0, 0, 0.15)',
controlTmpOutline: 'rgba(0, 0, 0, 0.02)',
controlOutline: 'rgba(42, 5, 192, 0.08)',
fontWeightStrong: 600,
opacityLoading: 0.65,
linkDecoration: 'none',
linkHoverDecoration: 'none',
linkFocusDecoration: 'none',
controlPaddingHorizontal: 12,
controlPaddingHorizontalSM: 8,
paddingXXS: 4,
paddingXS: 8,
paddingSM: 12,
padding: 16,
paddingMD: 20,
paddingLG: 24,
paddingXL: 32,
paddingContentHorizontalLG: 24,
paddingContentVerticalLG: 16,
paddingContentHorizontal: 16,
paddingContentVertical: 12,
paddingContentHorizontalSM: 16,
paddingContentVerticalSM: 8,
marginXXS: 4,
marginXS: 8,
marginSM: 12,
margin: 16,
marginMD: 20,
marginLG: 24,
marginXL: 32,
marginXXL: 48,
boxShadow:
'\n 0 6px 16px 0 rgba(0, 0, 0, 0.08),\n 0 3px 6px -4px rgba(0, 0, 0, 0.12),\n 0 9px 28px 8px rgba(0, 0, 0, 0.05)\n ',
boxShadowSecondary:
'\n 0 6px 16px 0 rgba(0, 0, 0, 0.08),\n 0 3px 6px -4px rgba(0, 0, 0, 0.12),\n 0 9px 28px 8px rgba(0, 0, 0, 0.05)\n ',
boxShadowTertiary:
'\n 0 1px 2px 0 rgba(0, 0, 0, 0.03),\n 0 1px 6px -1px rgba(0, 0, 0, 0.02),\n 0 2px 4px 0 rgba(0, 0, 0, 0.02)\n ',
screenXS: 480,
screenXSMin: 480,
screenXSMax: 575,
screenSM: 576,
screenSMMin: 576,
screenSMMax: 767,
screenMD: 768,
screenMDMin: 768,
screenMDMax: 991,
screenLG: 992,
screenLGMin: 992,
screenLGMax: 1199,
screenXL: 1200,
screenXLMin: 1200,
screenXLMax: 1599,
screenXXL: 1600,
screenXXLMin: 1600,
boxShadowPopoverArrow: '2px 2px 5px rgba(0, 0, 0, 0.05)',
boxShadowCard:
'\n 0 1px 2px -2px rgba(0, 0, 0, 0.16),\n 0 3px 6px 0 rgba(0, 0, 0, 0.12),\n 0 5px 12px 4px rgba(0, 0, 0, 0.09)\n ',
boxShadowDrawerRight:
'\n -6px 0 16px 0 rgba(0, 0, 0, 0.08),\n -3px 0 6px -4px rgba(0, 0, 0, 0.12),\n -9px 0 28px 8px rgba(0, 0, 0, 0.05)\n ',
boxShadowDrawerLeft:
'\n 6px 0 16px 0 rgba(0, 0, 0, 0.08),\n 3px 0 6px -4px rgba(0, 0, 0, 0.12),\n 9px 0 28px 8px rgba(0, 0, 0, 0.05)\n ',
boxShadowDrawerUp:
'\n 0 6px 16px 0 rgba(0, 0, 0, 0.08),\n 0 3px 6px -4px rgba(0, 0, 0, 0.12),\n 0 9px 28px 8px rgba(0, 0, 0, 0.05)\n ',
boxShadowDrawerDown:
'\n 0 -6px 16px 0 rgba(0, 0, 0, 0.08),\n 0 -3px 6px -4px rgba(0, 0, 0, 0.12),\n 0 -9px 28px 8px rgba(0, 0, 0, 0.05)\n ',
boxShadowTabsOverflowLeft: 'inset 10px 0 8px -8px rgba(0, 0, 0, 0.08)',
boxShadowTabsOverflowRight: 'inset -10px 0 8px -8px rgba(0, 0, 0, 0.08)',
boxShadowTabsOverflowTop: 'inset 0 10px 8px -8px rgba(0, 0, 0, 0.08)',
boxShadowTabsOverflowBottom: 'inset 0 -10px 8px -8px rgba(0, 0, 0, 0.08)',
isDarkMode: false
}
}
for (const key in globalVariables) {
globalThis[key] = globalVariables[key]
}

View File

@@ -1,18 +1,23 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Preview</title>
<!-- es-module-shims -->
</head>
<body>
<script type="module" id="appSrc">{{replace_code}}</script>
<div id="root">
<div
style="position:absolute;top: 0;left:0;width:100%;height:100%;display: flex;justify-content: center;align-items: center;">
Loading...
</div>
</div>
</body>
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Preview</title>
<script id="global-js-variables">
{{replace_global_js_variables}}
</script>
<style id="global-css-variables">
{{replace_global_css_variables}}
</style>
</head>
<body>
<script type="module" id="appDictSrc">{{replace_dict_code}}</script>
<script type="module" id="appBaseSrc">{{replace_base_code}}</script>
<div id="root">
<div style="position:absolute;top: 0;left:0;width:100%;height:100%;display: flex;justify-content: center;align-items: center;color: #777;">
Loading...
</div>
</div>
</body>
</html>

View File

@@ -22,4 +22,30 @@ class ToolDataSource @Inject constructor(
}
)
}.flowOn(ioDispatcher)
fun getGlobalJsVariables(isDarkMode: Boolean) = flow {
emit(
context.assets.open(
if (isDarkMode) "template/global-variables-dark.js"
else "template/global-variables-light.js"
)
.bufferedReader()
.use {
it.readText()
}
)
}.flowOn(ioDispatcher)
fun getGlobalCssVariables(isDarkMode: Boolean) = flow {
emit(
context.assets.open(
if (isDarkMode) "template/global-variables-dark.css"
else "template/global-variables-light.css"
)
.bufferedReader()
.use {
it.readText()
}
)
}.flowOn(ioDispatcher)
}

View File

@@ -10,6 +10,8 @@ import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.filled.Code
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Download
import androidx.compose.material.icons.filled.Fullscreen
import androidx.compose.material.icons.filled.FullscreenExit
import androidx.compose.material.icons.filled.Inbox
import androidx.compose.material.icons.filled.MoreVert
import androidx.compose.material.icons.filled.Reorder
@@ -43,6 +45,8 @@ object OxygenIcons {
val Delete = Icons.Default.Delete
val Download = Icons.Default.Download
val Error = Icons.Default.Cancel
val FullScreen = Icons.Default.Fullscreen
val FullScreenExit = Icons.Default.FullscreenExit
val Home = Icons.Rounded.Home
val HomeBorder = Icons.Outlined.Home
val Info = Icons.Outlined.Info

View File

@@ -57,14 +57,8 @@ internal class ConnectivityManagerNetworkMonitor @Inject constructor(
}
.conflate()
@Suppress("DEPRECATION")
private fun ConnectivityManager.isCurrentlyConnected() =
when {
VERSION.SDK_INT >= VERSION_CODES.M ->
activeNetwork
?.let(::getNetworkCapabilities)
?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
else -> activeNetworkInfo?.isConnected
} ?: false
activeNetwork
?.let(::getNetworkCapabilities)
?.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) == true
}

View File

@@ -1,9 +1,14 @@
package top.fatweb.oxygen.toolbox.navigation
import android.os.Bundle
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Modifier
import androidx.navigation.NavController
import androidx.navigation.NavDestination
import androidx.navigation.compose.NavHost
import top.fatweb.oxygen.toolbox.ui.OxygenAppState
import top.fatweb.oxygen.toolbox.ui.util.LocalFullScreen
@Composable
fun OxygenNavHost(
@@ -16,6 +21,21 @@ fun OxygenNavHost(
onShowSnackbar: suspend (message: String, action: String?) -> Boolean
) {
val navController = appState.navController
val fullScreen = LocalFullScreen.current
LaunchedEffect(navController) {
navController.addOnDestinationChangedListener(object :
NavController.OnDestinationChangedListener {
override fun onDestinationChanged(
controller: NavController,
destination: NavDestination,
arguments: Bundle?
) {
fullScreen.onStateChange.invoke(false)
}
})
}
NavHost(
modifier = modifier,
navController = navController,

View File

@@ -6,6 +6,10 @@ import top.fatweb.oxygen.toolbox.model.tool.ToolEntity
interface ToolRepository {
val toolViewTemplate: Flow<String>
fun getGlobalJsVariables(isDarkMode: Boolean): Flow<String>
fun getGlobalCssVariables(isDarkMode: Boolean): Flow<String>
fun getAllToolsStream(searchValue: String): Flow<List<ToolEntity>>
fun getStarToolsStream(searchValue: String): Flow<List<ToolEntity>>

View File

@@ -14,6 +14,12 @@ class OfflineToolRepository @Inject constructor(
override val toolViewTemplate: Flow<String>
get() = toolDataSource.toolViewTemplate
override fun getGlobalJsVariables(isDarkMode: Boolean): Flow<String> =
toolDataSource.getGlobalJsVariables(isDarkMode)
override fun getGlobalCssVariables(isDarkMode: Boolean): Flow<String> =
toolDataSource.getGlobalCssVariables(isDarkMode)
override fun getAllToolsStream(searchValue: String): Flow<List<ToolEntity>> =
toolDao.selectAllTools(searchValue)

View File

@@ -1,18 +1,14 @@
package top.fatweb.oxygen.toolbox.ui
import androidx.activity.ComponentActivity
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.material3.BottomAppBar
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
@@ -24,6 +20,8 @@ import androidx.compose.material3.SnackbarResult
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
@@ -34,7 +32,11 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.WindowInsetsControllerCompat
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavDestination
import androidx.navigation.NavDestination.Companion.hierarchy
@@ -57,6 +59,8 @@ import top.fatweb.oxygen.toolbox.ui.component.SearchButtonPosition
import top.fatweb.oxygen.toolbox.ui.settings.SettingsDialog
import top.fatweb.oxygen.toolbox.ui.theme.GradientColors
import top.fatweb.oxygen.toolbox.ui.theme.LocalGradientColors
import top.fatweb.oxygen.toolbox.ui.util.FullScreen
import top.fatweb.oxygen.toolbox.ui.util.LocalFullScreen
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@@ -67,70 +71,100 @@ fun OxygenApp(appState: OxygenAppState) {
mutableStateOf(false)
}
OxygenBackground {
OxygenGradientBackground(
gradientColors = if (shouldShowGradientBackground) LocalGradientColors.current else GradientColors()
) {
val destination = appState.currentTopLevelDestination
val context = LocalContext.current
val window = (context as ComponentActivity).window
val windowInsetsController = WindowInsetsControllerCompat(window, window.decorView)
var isFullScreen by remember { mutableStateOf(false) }
val snackbarHostState = remember { SnackbarHostState() }
val fullScreen = FullScreen(
enable = isFullScreen,
onStateChange = {
isFullScreen = it
}
)
val isOffline by appState.isOffline.collectAsStateWithLifecycle()
DisposableEffect(Unit) {
windowInsetsController.systemBarsBehavior =
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
val listener = WindowInsetsControllerCompat.OnControllableInsetsChangedListener { _, _ ->
isFullScreen = false
}
windowInsetsController.addOnControllableInsetsChangedListener(listener)
onDispose {
windowInsetsController.removeOnControllableInsetsChangedListener(listener)
}
}
val noConnectMessage = stringResource(R.string.core_no_connect)
LaunchedEffect(isFullScreen) {
if (isFullScreen) {
windowInsetsController.hide(WindowInsetsCompat.Type.systemBars())
} else {
windowInsetsController.show(WindowInsetsCompat.Type.systemBars())
}
}
var canScroll by remember { mutableStateOf(true) }
val topAppBarScrollBehavior =
if (canScroll) TopAppBarDefaults.enterAlwaysScrollBehavior() else TopAppBarDefaults.pinnedScrollBehavior()
CompositionLocalProvider(LocalFullScreen provides fullScreen) {
OxygenBackground {
OxygenGradientBackground(
gradientColors = if (shouldShowGradientBackground) LocalGradientColors.current else GradientColors()
) {
val destination = appState.currentTopLevelDestination
var activeSearch by remember { mutableStateOf(false) }
var searchValue by remember { mutableStateOf("") }
var searchCount by remember { mutableIntStateOf(0) }
val snackbarHostState = remember { SnackbarHostState() }
LaunchedEffect(activeSearch) {
canScroll = !activeSearch
}
val isOffline by appState.isOffline.collectAsStateWithLifecycle()
LaunchedEffect(destination) {
activeSearch = false
searchValue = ""
if (searchCount == 0) {
searchCount++
} else {
searchCount = 0
val noConnectMessage = stringResource(R.string.core_no_connect)
var canScroll by remember { mutableStateOf(true) }
val topAppBarScrollBehavior =
if (canScroll) TopAppBarDefaults.enterAlwaysScrollBehavior() else TopAppBarDefaults.pinnedScrollBehavior()
var activeSearch by remember { mutableStateOf(false) }
var searchValue by remember { mutableStateOf("") }
var searchCount by remember { mutableIntStateOf(0) }
LaunchedEffect(activeSearch) {
canScroll = !activeSearch
}
}
LaunchedEffect(isOffline) {
if (isOffline) {
snackbarHostState.showSnackbar(
message = noConnectMessage,
duration = SnackbarDuration.Indefinite
LaunchedEffect(destination) {
activeSearch = false
searchValue = ""
if (searchCount == 0) {
searchCount++
} else {
searchCount = 0
}
}
LaunchedEffect(isOffline) {
if (isOffline) {
snackbarHostState.showSnackbar(
message = noConnectMessage,
duration = SnackbarDuration.Indefinite
)
}
}
if (showSettingsDialog) {
SettingsDialog(
onDismiss = { showSettingsDialog = false },
onNavigateToLibraries = appState::navigateToLibraries,
onNavigateToAbout = appState::navigateToAbout
)
}
}
if (showSettingsDialog) {
SettingsDialog(
onDismiss = { showSettingsDialog = false },
onNavigateToLibraries = appState::navigateToLibraries,
onNavigateToAbout = appState::navigateToAbout
)
}
Scaffold(
modifier = Modifier
.nestedScroll(connection = topAppBarScrollBehavior.nestedScrollConnection),
containerColor = Color.Transparent,
contentColor = MaterialTheme.colorScheme.onBackground,
contentWindowInsets = WindowInsets(left = 0, top = 0, right = 0, bottom = 0),
snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
bottomBar = {
AnimatedVisibility(
visible = appState.shouldShowBottomBar && destination != null
) {
BottomAppBar(
windowInsets = WindowInsets(0)
Scaffold(
modifier = Modifier
.nestedScroll(connection = topAppBarScrollBehavior.nestedScrollConnection),
containerColor = Color.Transparent,
contentColor = MaterialTheme.colorScheme.onBackground,
contentWindowInsets = WindowInsets(left = 0, top = 0, right = 0, bottom = 0),
snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
bottomBar = {
AnimatedVisibility(
visible = appState.shouldShowBottomBar && destination != null
) {
OxygenBottomBar(
destinations = appState.topLevelDestinations,
@@ -139,85 +173,87 @@ fun OxygenApp(appState: OxygenAppState) {
)
}
}
}
) { padding ->
Row(
Modifier
.fillMaxSize()
.padding(padding)
.consumeWindowInsets(padding)
.windowInsetsPadding(
WindowInsets.safeDrawing.only(
WindowInsetsSides.Horizontal
)
)
) {
AnimatedVisibility(
visible = appState.shouldShowNavRail && destination != null
) {
OxygenNavRail(
modifier = Modifier.safeDrawingPadding(),
destinations = appState.topLevelDestinations,
currentDestination = appState.currentDestination,
onNavigateToDestination = appState::navigateToTopLevelDestination
)
}
Column(
Modifier.fillMaxSize()
) { padding ->
Row(
Modifier
.fillMaxSize()
) {
AnimatedVisibility(
visible = destination != null
visible = appState.shouldShowNavRail && destination != null
) {
OxygenTopAppBar(
scrollBehavior = topAppBarScrollBehavior,
title = {
destination?.let { Text(text = stringResource(destination.titleTextId)) }
},
navigationIcon = OxygenIcons.Search,
navigationIconContentDescription = stringResource(R.string.feature_settings_top_app_bar_navigation_icon_description),
actionIcon = OxygenIcons.MoreVert,
actionIconContentDescription = stringResource(R.string.feature_settings_top_app_bar_action_icon_description),
activeSearch = activeSearch,
searchButtonPosition = SearchButtonPosition.Navigation,
query = searchValue,
colors = TopAppBarDefaults.centerAlignedTopAppBarColors(
containerColor = Color.Transparent,
scrolledContainerColor = Color.Transparent
),
onNavigationClick = { activeSearch = true },
onActionClick = { showSettingsDialog = true },
onQueryChange = {
searchValue = it
},
onSearch = {
searchCount++
},
onCancelSearch = {
searchValue = ""
activeSearch = false
searchCount = 0
}
OxygenNavRail(
modifier = Modifier
.padding(padding)
.consumeWindowInsets(padding)
.safeDrawingPadding(),
destinations = appState.topLevelDestinations,
currentDestination = appState.currentDestination,
onNavigateToDestination = appState::navigateToTopLevelDestination
)
}
OxygenNavHost(
appState = appState,
startDestination = when (appState.launchPageConfig) {
LaunchPageConfig.Tools -> TOOLS_ROUTE
LaunchPageConfig.Star -> STAR_ROUTE
},
isVertical = appState.shouldShowBottomBar,
searchValue = searchValue,
searchCount = searchCount,
onShowSnackbar = { message, action ->
snackbarHostState.showSnackbar(
message = message,
actionLabel = action,
duration = SnackbarDuration.Short
) == SnackbarResult.ActionPerformed
Column(
Modifier.fillMaxSize()
) {
AnimatedVisibility(
visible = destination != null
) {
OxygenTopAppBar(
scrollBehavior = topAppBarScrollBehavior,
title = {
destination?.let {
Text(
text = stringResource(destination.titleTextId),
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
},
navigationIcon = OxygenIcons.Search,
navigationIconContentDescription = stringResource(R.string.feature_settings_top_app_bar_navigation_icon_description),
actionIcon = OxygenIcons.MoreVert,
actionIconContentDescription = stringResource(R.string.feature_settings_top_app_bar_action_icon_description),
activeSearch = activeSearch,
searchButtonPosition = SearchButtonPosition.Navigation,
query = searchValue,
colors = TopAppBarDefaults.centerAlignedTopAppBarColors(
containerColor = Color.Transparent,
scrolledContainerColor = Color.Transparent
),
onNavigationClick = { activeSearch = true },
onActionClick = { showSettingsDialog = true },
onQueryChange = {
searchValue = it
},
onSearch = {
searchCount++
},
onCancelSearch = {
searchValue = ""
activeSearch = false
searchCount = 0
}
)
}
)
OxygenNavHost(
appState = appState,
startDestination = when (appState.launchPageConfig) {
LaunchPageConfig.Tools -> TOOLS_ROUTE
LaunchPageConfig.Star -> STAR_ROUTE
},
isVertical = appState.shouldShowBottomBar,
searchValue = searchValue,
searchCount = searchCount,
onShowSnackbar = { message, action ->
snackbarHostState.showSnackbar(
message = message,
actionLabel = action,
duration = SnackbarDuration.Short
) == SnackbarResult.ActionPerformed
}
)
}
}
}
}
@@ -295,5 +331,5 @@ private fun OxygenNavRail(
private fun NavDestination?.isTopLevelDestinationInHierarchy(destination: TopLevelDestination) =
this?.hierarchy?.any {
it.route?.equals(destination.route) ?: false
} ?: false
it.route?.equals(destination.route) == true
} == true

View File

@@ -1,5 +1,10 @@
package top.fatweb.oxygen.toolbox.ui.about
import androidx.compose.animation.core.AnimationConstants
import androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi
import androidx.compose.animation.graphics.res.animatedVectorResource
import androidx.compose.animation.graphics.res.rememberAnimatedVectorPainter
import androidx.compose.animation.graphics.vector.AnimatedImageVector
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
@@ -13,6 +18,7 @@ import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.layout.safeDrawingPadding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
@@ -23,16 +29,21 @@ import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.res.vectorResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.delay
import top.fatweb.oxygen.toolbox.R
import top.fatweb.oxygen.toolbox.icon.OxygenIcons
import top.fatweb.oxygen.toolbox.ui.component.OxygenTopAppBar
@@ -86,7 +97,11 @@ internal fun AboutScreen(
OxygenTopAppBar(
scrollBehavior = topAppBarScrollBehavior,
title = {
Text(text = stringResource(R.string.feature_settings_more_about))
Text(
text = stringResource(R.string.feature_settings_more_about),
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
},
navigationIcon = OxygenIcons.Back,
navigationIconContentDescription = stringResource(R.string.core_back),
@@ -106,16 +121,26 @@ internal fun AboutScreen(
}
}
@OptIn(ExperimentalAnimationGraphicsApi::class)
@Composable
private fun AboutAppInfo(
modifier: Modifier = Modifier
) {
val logo = AnimatedImageVector.animatedVectorResource(R.drawable.ic_launcher)
var atEnd by remember { mutableStateOf(false) }
LaunchedEffect(Unit) {
delay(AnimationConstants.DefaultDurationMillis.toLong())
atEnd = true
}
Column(
modifier = modifier,
horizontalAlignment = Alignment.CenterHorizontally
) {
Image(
imageVector = ImageVector.vectorResource(R.drawable.ic_oxygen),
modifier = Modifier
.width(160.dp),
painter = rememberAnimatedVectorPainter(animatedImageVector = logo, atEnd = atEnd),
contentDescription = stringResource(R.string.app_full_name)
)
Spacer(Modifier.height(16.dp))
@@ -135,9 +160,12 @@ private fun AboutAppInfo(
Text(
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.outline,
text = "${ResourcesUtils.getAppVersionName(LocalContext.current)}(${
ResourcesUtils.getAppVersionCode(
LocalContext.current
text = "${ResourcesUtils.getAppVersionName(LocalContext.current)} (${
stringResource(
if (ResourcesUtils.getAppVersionCode(LocalContext.current) % 100 == 0L)
R.string.core_ga_version
else
R.string.core_beta_version
)
})"
)

View File

@@ -47,6 +47,7 @@ import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
@@ -132,7 +133,11 @@ internal fun LibrariesScreen(
OxygenTopAppBar(
scrollBehavior = topAppBarScrollBehavior,
title = {
Text(text = stringResource(R.string.feature_settings_open_source_license))
Text(
text = stringResource(R.string.feature_settings_open_source_license),
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
},
navigationIcon = OxygenIcons.Back,
navigationIconContentDescription = stringResource(R.string.core_back),
@@ -159,7 +164,7 @@ internal fun LibrariesScreen(
onSearch("")
}
)
Box(modifier = Modifier) {
Box {
when (librariesScreenUiState) {
LibrariesScreenUiState.Loading -> {
Indicator()
@@ -247,10 +252,7 @@ internal fun LibrariesScreen(
Column(
modifier = Modifier.verticalScroll(state = rememberScrollState())
) {
Text(
modifier = Modifier,
text = dialogContent
)
Text(text = dialogContent)
}
},
confirmButton = {

View File

@@ -1,10 +1,11 @@
package top.fatweb.oxygen.toolbox.ui.component
import androidx.annotation.StringRes
import androidx.compose.foundation.text.ClickableText
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.LinkAnnotation
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.text.withStyle
@@ -14,8 +15,8 @@ import top.fatweb.oxygen.toolbox.ui.util.ResourcesUtils
fun ClickableText(
@StringRes text: Int,
@StringRes replaceText: Int,
onClick: (Int) -> Unit
) {
onClick: () -> Unit
) {
val context = LocalContext.current
val primaryColor = MaterialTheme.colorScheme.primary
@@ -30,7 +31,10 @@ fun ClickableText(
clickablePart
)
append(mainText.substringBefore(clickablePart))
pushStringAnnotation(tag = "Click", annotation = clickablePart)
pushLink(LinkAnnotation.Clickable(
tag = "Click",
linkInteractionListener = { onClick() }
))
withStyle(style = SpanStyle(color = primaryColor)) {
append(clickablePart)
}
@@ -38,5 +42,5 @@ fun ClickableText(
append(mainText.substringAfter(clickablePart))
}
ClickableText(text = annotatedString, onClick = onClick)
Text(text = annotatedString)
}

View File

@@ -33,6 +33,8 @@ import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import top.fatweb.oxygen.toolbox.R
import top.fatweb.oxygen.toolbox.icon.OxygenIcons
@@ -44,6 +46,7 @@ import android.R as androidR
@Composable
fun OxygenTopAppBar(
modifier: Modifier = Modifier,
expandedHeight: Dp = 48.dp,
scrollBehavior: TopAppBarScrollBehavior? = null,
title: @Composable () -> Unit = {},
navigationIcon: ImageVector? = null,
@@ -61,8 +64,12 @@ fun OxygenTopAppBar(
onCancelSearch: () -> Unit = {}
) {
val topInset by animateIntAsState(
if (scrollBehavior != null && -scrollBehavior.state.heightOffset >= with(LocalDensity.current) { 64.0.dp.toPx() }) 0
else TopAppBarDefaults.windowInsets.getTop(LocalDensity.current), label = ""
targetValue = if (scrollBehavior != null && -scrollBehavior.state.heightOffset >= with(
LocalDensity.current
) { expandedHeight.toPx() }
) 0
else TopAppBarDefaults.windowInsets.getTop(LocalDensity.current),
label = ""
)
val keyboardController = LocalSoftwareKeyboardController.current
val focusRequester = remember { FocusRequester() }
@@ -81,6 +88,7 @@ fun OxygenTopAppBar(
CenterAlignedTopAppBar(
modifier = modifier,
scrollBehavior = scrollBehavior,
expandedHeight = expandedHeight,
title = {
if (activeSearch) TextField(
modifier = Modifier
@@ -116,6 +124,7 @@ fun OxygenTopAppBar(
},
maxLines = 1,
singleLine = true,
textStyle = MaterialTheme.typography.titleSmall,
onValueChange = {
if ("\n" !in it) onQueryChange(it)
}
@@ -175,7 +184,13 @@ enum class SearchButtonPosition {
private fun OxygenTopAppBarPreview() {
OxygenTheme {
OxygenTopAppBar(
title = { Text(text = stringResource(androidR.string.untitled)) },
title = {
Text(
text = stringResource(androidR.string.untitled),
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
},
navigationIcon = OxygenIcons.Search,
navigationIconContentDescription = "Navigation icon",
actionIcon = OxygenIcons.MoreVert,

View File

@@ -167,7 +167,6 @@ private fun ToolAction(
.padding(horizontal = 6.dp, vertical = 6.dp)
) {
Icon(
modifier = Modifier,
imageVector = actionIcon,
contentDescription = actionIconContentDescription
)

View File

@@ -221,7 +221,7 @@ fun Scrollbar(
withTimeout(viewConfiguration.longPressTimeoutMillis) {
tryAwaitRelease()
}
} catch (e: TimeoutCancellationException) {
} catch (_: TimeoutCancellationException) {
// Start the press triggered scroll
val initialPress = PressInteraction.Press(offset)
interactionSource?.tryEmit(initialPress)

View File

@@ -4,7 +4,6 @@ import android.widget.Toast
import androidx.activity.compose.ReportDrawnWhen
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row
@@ -33,7 +32,7 @@ import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.pulltorefresh.PullToRefreshContainer
import androidx.compose.material3.pulltorefresh.PullToRefreshBox
import androidx.compose.material3.pulltorefresh.rememberPullToRefreshState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
@@ -44,8 +43,6 @@ import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
@@ -114,15 +111,11 @@ internal fun ToolStoreScreen(
ReportDrawnWhen { !isToolLoading }
val pullToRefreshState = rememberPullToRefreshState()
LaunchedEffect(pullToRefreshState.isRefreshing) {
if (pullToRefreshState.isRefreshing) {
toolStorePagingItems.refresh()
}
}
var isRefreshing by remember { mutableStateOf(false) }
LaunchedEffect(toolStorePagingItems.loadState.refresh) {
if (toolStorePagingItems.loadState.refresh != LoadState.Loading) {
pullToRefreshState.endRefresh()
if (toolStorePagingItems.loadState.refresh != LoadState.Loading && isRefreshing) {
isRefreshing = false
}
if (toolStorePagingItems.loadState.refresh is LoadState.Error) {
Toast.makeText(
@@ -136,6 +129,12 @@ internal fun ToolStoreScreen(
}
}
LaunchedEffect(isRefreshing) {
if (isRefreshing) {
toolStorePagingItems.refresh()
}
}
val itemsAvailable = toolStorePagingItems.itemCount
val state = rememberLazyStaggeredGridState()
@@ -151,11 +150,14 @@ internal fun ToolStoreScreen(
)
}
Box(
modifier
.fillMaxSize()
.clipToBounds()
.nestedScroll(pullToRefreshState.nestedScrollConnection)
PullToRefreshBox(
modifier = modifier
.fillMaxSize(),
state = rememberPullToRefreshState(),
isRefreshing = isRefreshing,
onRefresh = {
isRefreshing = true
}
) {
if (itemsAvailable > 0 || (toolStorePagingItems.loadState.refresh == LoadState.Loading && itemsAvailable == 0)) {
LazyVerticalStaggeredGrid(
@@ -232,12 +234,6 @@ internal fun ToolStoreScreen(
}
}
PullToRefreshContainer(
modifier = Modifier
.align(Alignment.TopCenter),
state = pullToRefreshState,
)
state.DraggableScrollbar(
modifier = Modifier
.fillMaxHeight()

View File

@@ -204,7 +204,7 @@ fun supportsDynamicTheming() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
uiMode = Configuration.UI_MODE_NIGHT_YES or Configuration.UI_MODE_TYPE_NORMAL
)
@Preview(
name = "Api 21 Light", group = "ApiLevelPreviews", apiLevel = 21,
name = "Api 24 Light", group = "ApiLevelPreviews", apiLevel = 24,
uiMode = Configuration.UI_MODE_NIGHT_NO or Configuration.UI_MODE_TYPE_NORMAL
)
annotation class OxygenPreviews

View File

@@ -0,0 +1,10 @@
package top.fatweb.oxygen.toolbox.ui.util
import androidx.compose.runtime.compositionLocalOf
data class FullScreen(
val enable: Boolean = false,
val onStateChange: (Boolean) -> Unit = {}
)
val LocalFullScreen = compositionLocalOf { FullScreen() }

View File

@@ -2,9 +2,7 @@ package top.fatweb.oxygen.toolbox.ui.util
import android.app.Activity
import android.content.Context
import android.os.Build
import android.os.LocaleList
import androidx.annotation.RequiresApi
import top.fatweb.oxygen.toolbox.model.userdata.LanguageConfig
import java.util.Locale
@@ -24,11 +22,7 @@ object LocaleUtils {
fun attachBaseContext(context: Context, languageConfig: LanguageConfig): Context {
val locale: Locale = getLocaleFromLanguageConfig(languageConfig)
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
createConfigurationContext(context, locale)
} else {
updateConfiguration(context, locale)
}
return createConfigurationContext(context, locale)
}
private fun getLocaleFromLanguageConfig(languageConfig: LanguageConfig): Locale =
@@ -38,21 +32,10 @@ object LocaleUtils {
LanguageConfig.English -> Locale("en")
}
@RequiresApi(Build.VERSION_CODES.N)
private fun createConfigurationContext(context: Context, locale: Locale): Context {
val configuration = context.resources.configuration
configuration.setLocales(LocaleList(locale))
return context.createConfigurationContext(configuration)
}
@Suppress("DEPRECATION")
private fun updateConfiguration(context: Context, locale: Locale): Context {
val resources = context.resources
val configuration = resources.configuration
configuration.locale = locale
resources.updateConfiguration(configuration, resources.displayMetrics)
return context
}
}

View File

@@ -2,6 +2,7 @@ package top.fatweb.oxygen.toolbox.ui.util
import android.content.Context
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.content.res.Resources
import android.os.Build
import androidx.annotation.StringRes
@@ -10,14 +11,9 @@ import androidx.core.os.LocaleListCompat
import java.util.Locale
object ResourcesUtils {
fun getConfiguration(context: Context) = context.resources.configuration
fun getConfiguration(context: Context): Configuration = context.resources.configuration
fun getDisplayMetrics(context: Context) = context.resources.displayMetrics
@Suppress("DEPRECATION")
fun getAppLocale(context: Context): Locale =
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) getConfiguration(context).locales.get(0)
else getConfiguration(context).locale
fun getAppLocale(context: Context): Locale = getConfiguration(context).locales.get(0)
fun getSystemLocale(): LocaleListCompat =
ConfigurationCompat.getLocales(Resources.getSystem().configuration)
@@ -25,7 +21,7 @@ object ResourcesUtils {
fun getAppVersionName(context: Context): String =
try {
context.packageManager.getPackageInfo(context.packageName, 0)?.versionName ?: "Unknown"
} catch (e: PackageManager.NameNotFoundException) {
} catch (_: PackageManager.NameNotFoundException) {
"Unknown"
}
@@ -38,7 +34,7 @@ object ResourcesUtils {
context.packageName,
0
)?.versionCode?.toLong() ?: -1
} catch (e: PackageManager.NameNotFoundException) {
} catch (_: PackageManager.NameNotFoundException) {
-1
}

View File

@@ -1,32 +1,56 @@
package top.fatweb.oxygen.toolbox.ui.view
import android.annotation.SuppressLint
import android.app.Activity
import android.app.DownloadManager
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.util.Log
import android.webkit.ConsoleMessage
import android.webkit.JsPromptResult
import android.webkit.JsResult
import android.webkit.ValueCallback
import android.webkit.WebView
import androidx.activity.compose.ManagedActivityResultLauncher
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.ActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.WindowInsetsSides
import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.imePadding
import androidx.compose.foundation.layout.only
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.safeDrawing
import androidx.compose.foundation.layout.windowInsetsPadding
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Scaffold
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TextField
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.kevinnzou.web.AccompanistWebChromeClient
@@ -37,7 +61,11 @@ import top.fatweb.oxygen.toolbox.R
import top.fatweb.oxygen.toolbox.icon.OxygenIcons
import top.fatweb.oxygen.toolbox.ui.component.Indicator
import top.fatweb.oxygen.toolbox.ui.component.OxygenTopAppBar
import top.fatweb.oxygen.toolbox.ui.util.LocalFullScreen
import top.fatweb.oxygen.toolbox.ui.util.ResourcesUtils
import top.fatweb.oxygen.toolbox.util.NativeWebApi
import top.fatweb.oxygen.toolbox.util.Permissions
import kotlin.coroutines.resume
@Composable
internal fun ToolViewRoute(
@@ -45,12 +73,14 @@ internal fun ToolViewRoute(
viewModel: ToolViewScreenViewModel = hiltViewModel(),
onBackClick: () -> Unit
) {
val isPreview by viewModel.isPreview.collectAsStateWithLifecycle()
val toolViewUiState by viewModel.toolViewUiState.collectAsStateWithLifecycle()
val webViewInstanceState by viewModel.webviewInstance.collectAsStateWithLifecycle()
val isPreview by viewModel.isPreview.collectAsStateWithLifecycle()
ToolViewScreen(
modifier = modifier,
toolViewUiState = toolViewUiState,
webViewInstanceState = webViewInstanceState,
isPreview = isPreview,
onBackClick = onBackClick
)
@@ -62,48 +92,92 @@ internal fun ToolViewRoute(
internal fun ToolViewScreen(
modifier: Modifier = Modifier,
toolViewUiState: ToolViewUiState,
webViewInstanceState: WebViewInstanceState,
isPreview: Boolean,
onBackClick: () -> Unit
) {
val (isFullScreen, onFullScreenStateChange) = LocalFullScreen.current
Column(modifier.fillMaxWidth()) {
AnimatedVisibility(!isFullScreen) {
TopBar(
toolViewUiState = toolViewUiState,
isPreview = isPreview,
isFullScreen = isFullScreen,
onBackClick = onBackClick,
onFullScreenChange = onFullScreenStateChange
)
}
Content(
toolViewUiState = toolViewUiState,
webViewInstanceState = webViewInstanceState
)
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
private fun TopBar(
toolViewUiState: ToolViewUiState,
isPreview: Boolean,
isFullScreen: Boolean,
onBackClick: () -> Unit,
onFullScreenChange: (Boolean) -> Unit
) = OxygenTopAppBar(
title = {
Text(
text = when (toolViewUiState) {
ToolViewUiState.Loading -> stringResource(R.string.core_loading)
ToolViewUiState.Error -> stringResource(R.string.feature_tools_can_not_open)
is ToolViewUiState.Success -> if (isPreview) stringResource(
R.string.feature_tool_view_preview_suffix,
toolViewUiState.toolName
) else toolViewUiState.toolName
},
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
},
navigationIcon = OxygenIcons.Back,
navigationIconContentDescription = stringResource(R.string.core_back),
actionIcon = if (isFullScreen) OxygenIcons.FullScreenExit else OxygenIcons.FullScreen,
actionIconContentDescription = stringResource(if (isFullScreen) R.string.core_exit_full_screen else R.string.core_full_screen),
colors = TopAppBarDefaults.centerAlignedTopAppBarColors(
containerColor = Color.Transparent,
scrolledContainerColor = Color.Transparent
),
onNavigationClick = onBackClick,
onActionClick = {
onFullScreenChange(!isFullScreen)
}
)
@Composable
private fun Content(
toolViewUiState: ToolViewUiState,
webViewInstanceState: WebViewInstanceState
) {
val configuration = LocalConfiguration.current
val context = LocalContext.current
Scaffold(
modifier = Modifier,
containerColor = Color.Transparent,
contentWindowInsets = WindowInsets(left = 0, top = 0, right = 0, bottom = 0)
) { padding ->
Column(
modifier
.fillMaxWidth()
.padding(padding)
.consumeWindowInsets(padding)
.windowInsetsPadding(
WindowInsets.safeDrawing.only(
WindowInsetsSides.Horizontal
)
)
) {
OxygenTopAppBar(
title = {
Text(
text = when (toolViewUiState) {
ToolViewUiState.Loading -> stringResource(R.string.core_loading)
ToolViewUiState.Error -> stringResource(R.string.feature_tools_can_not_open)
is ToolViewUiState.Success -> if (isPreview) stringResource(
R.string.feature_tool_view_preview_suffix,
toolViewUiState.toolName
) else toolViewUiState.toolName
}
)
},
navigationIcon = OxygenIcons.Back,
navigationIconContentDescription = stringResource(R.string.core_back),
colors = TopAppBarDefaults.centerAlignedTopAppBarColors(
containerColor = Color.Transparent,
scrolledContainerColor = Color.Transparent
),
onNavigationClick = onBackClick
)
var fileChooserCallback by remember { mutableStateOf<ValueCallback<Array<Uri>>?>(null) }
val fileChooserLauncher = rememberFileChooserLauncher(fileChooserCallback)
val permissionLauncher = rememberPermissionLauncher()
var isShowDialog = remember { mutableStateOf(false) }
var dialogType = remember { mutableStateOf(DialogType.Alert) }
var dialogTitle by remember { mutableStateOf("") }
var dialogText = remember { mutableStateOf("") }
var dialogInputValue = remember { mutableStateOf("") }
var onDialogConfirm = remember { mutableStateOf<((String) -> Unit)?>(null) }
var onDialogCancel = remember { mutableStateOf<(() -> Unit)?>(null) }
when (webViewInstanceState) {
WebViewInstanceState.Loading -> {
Indicator()
}
is WebViewInstanceState.Success -> {
when (toolViewUiState) {
ToolViewUiState.Loading -> {
Indicator()
@@ -120,6 +194,7 @@ internal fun ToolViewScreen(
}
is ToolViewUiState.Success -> {
dialogTitle = toolViewUiState.toolName
val webViewState = rememberWebViewStateWithHTMLData(
data = toolViewUiState.htmlData,
)
@@ -128,37 +203,255 @@ internal fun ToolViewScreen(
.fillMaxSize()
.imePadding(),
state = webViewState,
chromeClient = remember {
object : AccompanistWebChromeClient() {
override fun onConsoleMessage(consoleMessage: ConsoleMessage?): Boolean {
consoleMessage?.let {
Timber.tag("WebView").log(
priority = when (it.messageLevel()) {
ConsoleMessage.MessageLevel.TIP -> Log.VERBOSE
ConsoleMessage.MessageLevel.LOG -> Log.INFO
ConsoleMessage.MessageLevel.WARNING -> Log.WARN
ConsoleMessage.MessageLevel.ERROR -> Log.ERROR
else -> Log.DEBUG
},
message = "${it.message()} (${it.lineNumber()})"
)
return true
}
return false
}
}
chromeClient = rememberChromeClient(
context = context,
fileChooserLauncher = fileChooserLauncher,
isShowDialog = isShowDialog,
dialogType = dialogType,
dialogText = dialogText,
dialogInputValue = dialogInputValue,
onDialogConfirm = onDialogConfirm,
onDialogCancel = onDialogCancel
) {
fileChooserCallback = it
},
onCreated = {
it.settings.javaScriptEnabled = true
it.settings.domStorageEnabled = true
it.addJavascriptInterface(
NativeWebApi(context = context, webView = it),
"NativeApi"
)
}
onCreated = initWebView(
context = context,
permissionLauncher = permissionLauncher
),
factory = {
webViewInstanceState.webView
},
captureBackPresses = false
)
}
}
}
}
if (isShowDialog.value) {
AlertDialog(
modifier = Modifier
.widthIn(max = configuration.screenWidthDp.dp - 80.dp)
.heightIn(max = configuration.screenHeightDp.dp - 40.dp),
onDismissRequest = {},
title = {
Text(
text = dialogTitle,
style = MaterialTheme.typography.titleLarge
)
},
text = {
Column {
Column(
modifier = Modifier.verticalScroll(state = rememberScrollState())
) {
Text(text = dialogText.value)
}
if (dialogType.value == DialogType.Prompt) {
Spacer(modifier = Modifier.height(8.dp))
TextField(
value = dialogInputValue.value,
onValueChange = {
dialogInputValue.value = it
},
textStyle = MaterialTheme.typography.bodyMedium
)
}
}
},
confirmButton = {
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp)
) {
TextButton(onClick = {
isShowDialog.value = false
onDialogConfirm.value?.invoke(dialogInputValue.value)
}) {
Text(text = stringResource(R.string.core_ok))
}
if (dialogType.value != DialogType.Alert) {
TextButton(onClick = {
isShowDialog.value = false
onDialogCancel.value?.invoke()
}) {
Text(text = stringResource(R.string.core_cancel))
}
}
}
}
)
}
}
@Composable
private fun rememberFileChooserLauncher(fileChooserCallback: ValueCallback<Array<Uri>>?) =
rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == Activity.RESULT_OK) {
it.data?.data?.let { uri ->
fileChooserCallback?.onReceiveValue(arrayOf(uri))
} ?: {
fileChooserCallback?.onReceiveValue(emptyArray())
}
} else {
fileChooserCallback?.onReceiveValue(emptyArray())
}
}
@Composable
private fun rememberPermissionLauncher() =
rememberLauncherForActivityResult(ActivityResultContracts.RequestPermission()) {
Permissions.continuation?.resume(it)
}
@SuppressLint("SetJavaScriptEnabled")
private fun initWebView(
context: Context,
permissionLauncher: ManagedActivityResultLauncher<String, Boolean>
) = { webview: WebView ->
webview.setBackgroundColor(Color.Transparent.toArgb())
webview.settings.javaScriptEnabled = true
webview.settings.domStorageEnabled = true
webview.addJavascriptInterface(
NativeWebApi(context = context, permissionLauncher = permissionLauncher),
"NativeApi"
)
webview.setDownloadListener { url, userAgent, _, mimetype, _ ->
if (!listOf("http://", "https://").any(url::startsWith)) {
webview.evaluateJavascript(
"alert('${
ResourcesUtils.getString(
context = context,
resId = R.string.core_can_only_download_http_https,
url
)
}')"
) {}
return@setDownloadListener
}
val request = DownloadManager.Request(Uri.parse(url)).apply {
addRequestHeader("User-Agent", userAgent)
setMimeType(mimetype)
setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
}
val downloadManager: DownloadManager =
context.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
downloadManager.enqueue(request)
}
}
@Composable
private fun rememberChromeClient(
context: Context,
fileChooserLauncher: ManagedActivityResultLauncher<Intent, ActivityResult>,
isShowDialog: MutableState<Boolean>,
dialogType: MutableState<DialogType>,
dialogText: MutableState<String>,
dialogInputValue: MutableState<String>,
onDialogConfirm: MutableState<((String) -> Unit)?>,
onDialogCancel: MutableState<(() -> Unit)?>,
processCallback: (ValueCallback<Array<Uri>>?) -> Unit
) = remember {
object : AccompanistWebChromeClient() {
override fun onConsoleMessage(consoleMessage: ConsoleMessage?): Boolean {
consoleMessage?.let {
Timber.tag("WebView").log(
priority = when (it.messageLevel()) {
ConsoleMessage.MessageLevel.TIP -> Log.VERBOSE
ConsoleMessage.MessageLevel.LOG -> Log.INFO
ConsoleMessage.MessageLevel.WARNING -> Log.WARN
ConsoleMessage.MessageLevel.ERROR -> Log.ERROR
else -> Log.DEBUG
},
message = "${it.message()} (${it.lineNumber()})"
)
return true
}
return false
}
override fun onShowFileChooser(
webView: WebView?,
filePathCallback: ValueCallback<Array<Uri>>?,
fileChooserParams: FileChooserParams?
): Boolean {
processCallback(filePathCallback)
val intent = fileChooserParams?.createIntent()
?: Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = "*/*"
}
fileChooserLauncher.launch(
Intent.createChooser(
intent,
fileChooserParams?.title ?: ResourcesUtils.getString(
context = context,
resId = R.string.core_file_select_one_text
)
)
)
return true
}
override fun onJsAlert(
view: WebView?,
url: String?,
message: String?,
result: JsResult?
): Boolean {
isShowDialog.value = true
dialogType.value = DialogType.Alert
dialogText.value = message ?: ""
onDialogConfirm.value = {
result?.confirm()
}
return true
}
override fun onJsConfirm(
view: WebView?,
url: String?,
message: String?,
result: JsResult?
): Boolean {
isShowDialog.value = true
dialogType.value = DialogType.Confirm
dialogText.value = message ?: ""
onDialogConfirm.value = {
result?.confirm()
}
onDialogCancel.value = {
result?.cancel()
}
return true
}
override fun onJsPrompt(
view: WebView?,
url: String?,
message: String?,
defaultValue: String?,
result: JsPromptResult?
): Boolean {
isShowDialog.value = true
dialogType.value = DialogType.Prompt
dialogText.value = message ?: ""
dialogInputValue.value = defaultValue ?: ""
onDialogConfirm.value = {
result?.confirm(dialogInputValue.value)
}
onDialogCancel.value = {
result?.cancel()
}
return true
}
}
}
private enum class DialogType {
Alert, Confirm, Prompt
}

View File

@@ -1,21 +1,38 @@
package top.fatweb.oxygen.toolbox.ui.view
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.res.Configuration
import android.webkit.WebView
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.FlowCollector
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.stateIn
import timber.log.Timber
import top.fatweb.oxygen.toolbox.model.Result
import top.fatweb.oxygen.toolbox.model.tool.ToolEntity
import top.fatweb.oxygen.toolbox.model.userdata.DarkThemeConfig
import top.fatweb.oxygen.toolbox.navigation.ToolViewArgs
import top.fatweb.oxygen.toolbox.repository.tool.StoreRepository
import top.fatweb.oxygen.toolbox.repository.tool.ToolRepository
import top.fatweb.oxygen.toolbox.repository.userdata.UserDataRepository
import top.fatweb.oxygen.toolbox.ui.util.ResourcesUtils
import top.fatweb.oxygen.toolbox.util.decodeToStringWithZip
import javax.inject.Inject
import kotlin.io.encoding.Base64
@@ -24,6 +41,8 @@ import kotlin.time.Duration.Companion.seconds
@HiltViewModel
class ToolViewScreenViewModel @Inject constructor(
@ApplicationContext context: Context,
userDataRepository: UserDataRepository,
storeRepository: StoreRepository,
toolRepository: ToolRepository,
savedStateHandle: SavedStateHandle
@@ -35,82 +54,159 @@ class ToolViewScreenViewModel @Inject constructor(
private val toolId = toolViewArgs.toolId
private val preview = toolViewArgs.preview
private val storeDetailCache = MutableStateFlow<Result<ToolEntity>?>(null)
val toolViewUiState: StateFlow<ToolViewUiState> = toolViewUiState(
context = context,
savedStateHandle = savedStateHandle,
username = username,
toolId = toolId,
preview = preview,
userDataRepository = userDataRepository,
storeRepository = storeRepository,
toolRepository = toolRepository
toolRepository = toolRepository,
storeDetailCache = storeDetailCache
)
.stateIn(
scope = viewModelScope,
initialValue = ToolViewUiState.Loading,
started = SharingStarted.WhileSubscribed(stopTimeoutMillis = 5.seconds.inWholeMilliseconds)
)
val webviewInstance = flow<WebViewInstanceState> {
val webviewInstance = WebView(context)
emit(WebViewInstanceState.Success(webviewInstance))
}.stateIn(
viewModelScope,
initialValue = WebViewInstanceState.Loading,
started = SharingStarted.WhileSubscribed(stopTimeoutMillis = 5.seconds.inWholeMilliseconds)
)
}
@OptIn(ExperimentalCoroutinesApi::class)
private fun toolViewUiState(
context: Context,
savedStateHandle: SavedStateHandle,
username: String,
toolId: String,
preview: Boolean,
userDataRepository: UserDataRepository,
storeRepository: StoreRepository,
toolRepository: ToolRepository
toolRepository: ToolRepository,
storeDetailCache: MutableStateFlow<Result<ToolEntity>?>
): Flow<ToolViewUiState> {
val toolViewTemplate = toolRepository.toolViewTemplate
val entityFlow =
if (!preview) toolRepository.getToolByUsernameAndToolId(username, toolId) else flowOf(null)
return flow {
combine(entityFlow, toolViewTemplate, ::Pair).collect { (entityFlow, toolViewTemplate) ->
if (entityFlow == null) {
savedStateHandle[IS_PREVIEW] = true
storeRepository.detail(username, toolId).collect {
emit(
when (it) {
is Result.Success -> {
val dist = it.data.dist!!
val base = it.data.base!!
val isSystemDarkModeFlow = callbackFlow<Boolean> {
val receiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, instent: Intent?) {
context?.let(ResourcesUtils::getConfiguration)?.run {
trySend((uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES)
}
}
}
val filter = IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED)
context.registerReceiver(receiver, filter)
trySend((ResourcesUtils.getConfiguration(context).uiMode and Configuration.UI_MODE_NIGHT_MASK) == Configuration.UI_MODE_NIGHT_YES)
awaitClose { context.unregisterReceiver(receiver) }
}
return isSystemDarkModeFlow.flatMapLatest { isSystemDarkMode ->
flow {
userDataRepository.userData.collect { userData ->
val isDarkMode: Boolean = when (userData.darkThemeConfig) {
DarkThemeConfig.FollowSystem -> isSystemDarkMode
DarkThemeConfig.Light -> false
DarkThemeConfig.Dark -> true
}
val globalJsVariables = toolRepository.getGlobalJsVariables(isDarkMode)
val globalCssVariables = toolRepository.getGlobalCssVariables(isDarkMode)
combine(entityFlow, toolViewTemplate, globalJsVariables, ::Triple)
.combine(globalCssVariables) { triple, globalCssVariables ->
Quadruple(triple.first, triple.second, triple.third, globalCssVariables)
}
.collect { (entityFlow, toolViewTemplate, globalJsVariables, globalCssVariables) ->
if (entityFlow == null) {
savedStateHandle[IS_PREVIEW] = true
val cachedDetail = storeDetailCache.value
if (cachedDetail != null) {
emitResult(
result = cachedDetail,
toolViewTemplate = toolViewTemplate,
globalJsVariables = globalJsVariables,
globalCssVariables = globalCssVariables
)
} else {
storeRepository.detail(username, toolId).collect { result ->
storeDetailCache.value = result
emitResult(
result = result,
toolViewTemplate = toolViewTemplate,
globalJsVariables = globalJsVariables,
globalCssVariables = globalCssVariables
)
}
}
} else {
savedStateHandle[IS_PREVIEW] = false
emit(
ToolViewUiState.Success(
it.data.name,
entityFlow.name,
processHtml(
toolViewTemplate = toolViewTemplate,
distBase64 = dist,
baseBase64 = base
globalJsVariables = globalJsVariables,
globalCssVariables = globalCssVariables,
distBase64 = entityFlow.dist!!,
baseBase64 = entityFlow.base!!
)
)
}
is Result.Loading -> ToolViewUiState.Loading
is Result.Error -> {
Timber.e("Can not load tool", it.exception)
ToolViewUiState.Error
}
is Result.Fail -> ToolViewUiState.Error
)
}
)
}
} else {
savedStateHandle[IS_PREVIEW] = false
emit(
ToolViewUiState.Success(
entityFlow.name,
processHtml(
toolViewTemplate = toolViewTemplate,
distBase64 = entityFlow.dist!!,
baseBase64 = entityFlow.base!!
)
)
)
}
}
}
}
}
private suspend fun FlowCollector<ToolViewUiState>.emitResult(
result: Result<ToolEntity>,
toolViewTemplate: String,
globalJsVariables: String,
globalCssVariables: String
) {
emit(
when (result) {
is Result.Success -> {
val dist = result.data.dist!!
val base = result.data.base!!
ToolViewUiState.Success(
result.data.name,
processHtml(
toolViewTemplate = toolViewTemplate,
globalJsVariables = globalJsVariables,
globalCssVariables = globalCssVariables,
distBase64 = dist,
baseBase64 = base
)
)
}
is Result.Loading -> ToolViewUiState.Loading
is Result.Error -> {
Timber.e(result.exception, "Can not load tool")
ToolViewUiState.Error
}
is Result.Fail -> ToolViewUiState.Error
}
)
}
sealed interface ToolViewUiState {
data class Success(
val toolName: String,
@@ -122,12 +218,32 @@ sealed interface ToolViewUiState {
data object Loading : ToolViewUiState
}
sealed interface WebViewInstanceState {
data class Success(
val webView: WebView
) : WebViewInstanceState
data object Loading : WebViewInstanceState
}
@OptIn(ExperimentalEncodingApi::class)
private fun processHtml(toolViewTemplate: String, distBase64: String, baseBase64: String): String {
private fun processHtml(
toolViewTemplate: String,
globalJsVariables: String,
globalCssVariables: String,
distBase64: String,
baseBase64: String
): String {
val dist = Base64.decodeToStringWithZip(distBase64)
val base = Base64.decodeToStringWithZip(baseBase64)
return toolViewTemplate.replace("{{replace_code}}", "$dist\n$base")
return toolViewTemplate
.replace(oldValue = "{{replace_global_js_variables}}", newValue = globalJsVariables)
.replace(oldValue = "{{replace_global_css_variables}}", newValue = globalCssVariables)
.replace(oldValue = "{{replace_dict_code}}", newValue = dist)
.replace(oldValue = "{{replace_base_code}}", newValue = base)
}
data class Quadruple<A, B, C, D>(val first: A, val second: B, val third: C, val fourth: D)
private const val IS_PREVIEW = "IS_PREVIEW"

View File

@@ -0,0 +1,95 @@
package top.fatweb.oxygen.toolbox.util
import android.content.ContentValues
import android.content.Context
import android.os.Build
import android.os.Environment
import android.provider.MediaStore
import androidx.activity.compose.ManagedActivityResultLauncher
import androidx.annotation.RequiresApi
import kotlinx.coroutines.runBlocking
import timber.log.Timber
import java.io.File
import java.io.FileOutputStream
import java.io.IOException
import kotlin.let
fun saveToDownloads(
context: Context,
permissionLauncher: ManagedActivityResultLauncher<String, Boolean>,
data: ByteArray,
fileName: String
) = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
saveFileToDownloads(context = context, data = data, fileName = fileName)
} else {
saveFileToDownloads(
context = context,
permissionLauncher = permissionLauncher,
data = data,
fileName = fileName
)
}
@RequiresApi(Build.VERSION_CODES.Q)
private fun saveFileToDownloads(
context: Context,
data: ByteArray,
fileName: String
): Boolean {
val resolver = context.contentResolver
val contentValues = ContentValues().apply {
put(MediaStore.Downloads.DISPLAY_NAME, fileName)
put(MediaStore.Downloads.MIME_TYPE, "application/octet-stream")
put(MediaStore.Downloads.IS_PENDING, 1)
}
val collection = MediaStore.Downloads.getContentUri(MediaStore.VOLUME_EXTERNAL_PRIMARY)
val fileUri = resolver.insert(collection, contentValues)
return fileUri?.let { uri ->
resolver.openOutputStream(uri)?.use { outputStream ->
outputStream.write(data)
outputStream.flush()
}
contentValues.clear()
contentValues.put(MediaStore.Downloads.IS_PENDING, 0)
resolver.update(uri, contentValues, null, null)
true
} ?: run {
Timber.e("Could not save file $fileName to Downloads")
false
}
}
private fun saveFileToDownloads(
context: Context,
permissionLauncher: ManagedActivityResultLauncher<String, Boolean>,
data: ByteArray,
fileName: String
): Boolean {
if (!runBlocking {
Permissions.requestWriteExternalStoragePermission(
context,
permissionLauncher
)
}) {
return false
}
val downloadsDir =
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
val file = File(downloadsDir, fileName)
return try {
FileOutputStream(file).apply {
write(data)
close()
}
true
} catch (e: IOException) {
Timber.e(e, "Could not save file $fileName to ${file.absolutePath}")
false
}
}

View File

@@ -4,12 +4,13 @@ import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.content.Context.CLIPBOARD_SERVICE
import android.util.Base64
import android.webkit.JavascriptInterface
import android.webkit.WebView
import androidx.activity.compose.ManagedActivityResultLauncher
class NativeWebApi(
private val context: Context,
private val webView: WebView
private val permissionLauncher: ManagedActivityResultLauncher<String, Boolean>
) {
@JavascriptInterface
fun copyToClipboard(text: String): Boolean {
@@ -17,7 +18,7 @@ class NativeWebApi(
return clipboardManager?.let {
it.setPrimaryClip(ClipData.newPlainText("copy", text))
true
} ?: false
} == true
}
@JavascriptInterface
@@ -26,9 +27,15 @@ class NativeWebApi(
return clipboardManager?.primaryClip?.getItemAt(0)?.text?.toString() ?: ""
}
private fun callback(callback: String, vararg args: Any) {
val jsCode =
"$callback(${args.map { if (it is String) "'$it'" else it }.joinToString(", ")})"
webView.evaluateJavascript(jsCode, null)
@JavascriptInterface
fun saveToDownloads(dataBase64: String, fileName: String): Boolean {
val data = Base64.decode(dataBase64, Base64.DEFAULT)
return saveToDownloads(
context = context,
permissionLauncher = permissionLauncher,
data = data,
fileName = fileName
)
}
}

View File

@@ -0,0 +1,41 @@
package top.fatweb.oxygen.toolbox.util
import android.Manifest
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import androidx.activity.compose.ManagedActivityResultLauncher
import androidx.core.content.ContextCompat
import kotlinx.coroutines.suspendCancellableCoroutine
import kotlin.coroutines.Continuation
import kotlin.coroutines.resume
object Permissions {
var continuation: Continuation<Boolean>? = null
suspend fun requestWriteExternalStoragePermission(
context: Context,
permissionLauncher: ManagedActivityResultLauncher<String, Boolean>
): Boolean {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
return true
}
return suspendCancellableCoroutine { continuation ->
if (ContextCompat.checkSelfPermission(
context,
Manifest.permission.WRITE_EXTERNAL_STORAGE
) == PackageManager.PERMISSION_GRANTED
) {
continuation.resume(true)
} else {
this.continuation = continuation
permissionLauncher.launch(Manifest.permission.WRITE_EXTERNAL_STORAGE)
}
continuation.invokeOnCancellation {
continuation.resume(false)
}
}
}
}

View File

@@ -1,30 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

View File

@@ -1,194 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:aapt="http://schemas.android.com/aapt"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z">
<aapt:attr name="android:fillColor">
<gradient
android:endX="85.84757"
android:endY="92.4963"
android:startX="42.9492"
android:startY="49.59793"
android:type="linear">
<item
android:color="#44000000"
android:offset="0.0" />
<item
android:color="#00000000"
android:offset="1.0" />
</gradient>
</aapt:attr>
</path>
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

File diff suppressed because one or more lines are too long

View File

@@ -1,170 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
android:width="200dp"
android:height="200dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
android:fillColor="#4E47BB"
android:pathData="M0,0h1024v1024h-1024z" />
</vector>

View File

@@ -1,12 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
xmlns:tools="http://schemas.android.com/tools"
android:width="200dp"
android:height="200dp"
android:viewportWidth="1024"
android:viewportHeight="1024">
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
android:pathData="M307.1,499.6c0,-70.5 86.8,-42.6 58.5,-33.2 -31,10.3 2.1,73.6 11.1,51.3c19.7,-48.9 27,27.9 50.1,27.9c23.1,0 69.4,-119.3 82.4,-88.7c46.9,109.9 66.2,-4.6 86.7,59.3c20.7,64.4 41,-58.9 50.6,-24.7c7.5,26.9 18.8,5.8 28.3,-19.5c10.7,-28.4 41.8,-37.4 41.8,27.5c0,111.5 -88.7,204.7 -204.7,204.7 -116.1,0 -204.7,-93.5 -204.7,-204.7ZM426.7,504c7,0 12.2,6.2 12.2,12.2s-4.9,12.2 -12.2,12.2c-7.3,0 -12.2,-6 -12.2,-12.2c0,-6.2 5.2,-12.2 12.2,-12.2ZM414.5,382.8c5.3,0 9.2,4.7 9.2,9.2s-3.7,9.2 -9.2,9.2 -9.2,-4.5 -9.2,-9.2c0,-4.7 3.9,-9.2 9.2,-9.2ZM377.8,433.6c4.3,0 7.6,3.8 7.6,7.6c0,3.7 -3,7.6 -7.6,7.6 -4.5,0 -7.6,-3.7 -7.6,-7.6c0,-3.8 3.2,-7.6 7.6,-7.6ZM466.2,408.8c11.6,0 20.3,10.3 20.3,20.3s-8.1,20.3 -20.3,20.3 -20.3,-10 -20.3,-20.3s8.7,-20.3 20.3,-20.3ZM614.9,433.6c9.4,0 16.4,8.3 16.4,16.4c0,8.1 -6.6,16.4 -16.4,16.4 -9.8,0 -16.4,-8.1 -16.4,-16.4c0,-8.3 7,-16.4 16.4,-16.4ZM405.9,440c3,0 5.3,2.7 5.3,5.3s-2.1,5.3 -5.3,5.3c-3.2,0 -5.3,-2.6 -5.3,-5.3c0,-2.7 2.3,-5.3 5.3,-5.3ZM528.1,437.2c5.6,0 9.8,5 9.8,9.8c0,4.8 -3.9,9.8 -9.8,9.8s-9.8,-4.9 -9.8,-9.8c0,-5 4.2,-9.8 9.8,-9.8ZM560.5,467.9c8.4,0 14.7,7.5 14.7,14.7s-5.9,14.7 -14.7,14.7 -14.7,-7.3 -14.7,-14.7c0,-7.4 6.3,-14.7 14.7,-14.7ZM459.7,350.8c5.2,0 9,4.6 9,9c0,4.4 -3.6,9 -9,9 -5.4,0 -9,-4.4 -9,-9c0,-4.6 3.8,-9 9,-9ZM352.3,556.4c0,12.9 10.2,25.4 25.4,25.4c15.2,0 25.4,-12.9 25.4,-25.4s-10.9,-25.4 -25.4,-25.4c-14.6,0 -25.4,12.6 -25.4,25.4ZM377.8,535.4c12,0 21,10.7 21,21s-8.4,21 -21,21c-12.6,0 -21,-10.4 -21,-21c0,-10.6 9,-21 21,-21ZM560.1,547.6c0,10.2 8.1,20.1 20.1,20.1s20.1,-10.2 20.1,-20.1 -8.6,-20.1 -20.1,-20.1 -20.1,9.9 -20.1,20.1ZM580.2,532c8.9,0 15.6,7.9 15.6,15.6s-6.3,15.6 -15.6,15.6 -15.6,-7.7 -15.6,-15.6c0,-7.9 6.7,-15.6 15.6,-15.6ZM403.2,615.3c0,4.5 3.6,8.9 8.9,8.9s8.9,-4.5 8.9,-8.9 -3.8,-8.9 -8.9,-8.9c-5.1,0 -8.9,4.4 -8.9,8.9ZM412.2,609.1c3.6,0 6.2,3.2 6.2,6.2s-2.5,6.2 -6.2,6.2c-3.7,0 -6.2,-3.1 -6.2,-6.2s2.7,-6.2 6.2,-6.2ZM441,566.5c0,3 2.4,5.9 5.9,5.9s5.9,-3 5.9,-5.9c0,-2.9 -2.5,-5.9 -5.9,-5.9 -3.4,0 -5.9,2.9 -5.9,5.9ZM446.9,562.4c2.4,0 4.1,2.1 4.1,4.1c0,2 -1.7,4.1 -4.1,4.1s-4.1,-2 -4.1,-4.1c0,-2.1 1.8,-4.1 4.1,-4.1ZM587.6,592.6c0,3 2.4,5.9 5.9,5.9s5.9,-3 5.9,-5.9 -2.5,-5.9 -5.9,-5.9 -5.9,2.9 -5.9,5.9ZM593.5,588.5c2.4,0 4.1,2.1 4.1,4.1c0,2 -1.7,4.1 -4.1,4.1s-4.1,-2 -4.1,-4.1c0,-2.1 1.8,-4.1 4.1,-4.1ZM634.6,592.6c0,3 2.4,5.9 5.9,5.9s5.9,-3 5.9,-5.9 -2.5,-5.9 -5.9,-5.9c-3.4,0 -5.9,2.9 -5.9,5.9ZM640.5,588.5c2.4,0 4.1,2.1 4.1,4.1c0,2 -1.7,4.1 -4.1,4.1s-4.1,-2 -4.1,-4.1c0,-2.1 1.8,-4.1 4.1,-4.1ZM566.1,582.8c0,3 2.4,5.9 5.9,5.9s5.9,-3 5.9,-5.9c0,-2.9 -2.5,-5.9 -5.9,-5.9s-5.9,2.9 -5.9,5.9ZM572,578.7c2.4,0 4.1,2.1 4.1,4.1c0,2 -1.7,4.1 -4.1,4.1s-4.1,-2 -4.1,-4.1c0,-2.1 1.8,-4.1 4.1,-4.1ZM480.7,624.3c0,3 2.4,5.9 5.9,5.9s5.9,-3 5.9,-5.9c0,-2.9 -2.5,-5.9 -5.9,-5.9s-5.9,2.9 -5.9,5.9ZM486.6,620.1c2.4,0 4.1,2.1 4.1,4.1c0,2 -1.7,4.1 -4.1,4.1s-4.1,-2 -4.1,-4.1c0,-2.1 1.8,-4.1 4.1,-4.1ZM652.3,553.7c0,6.5 5.2,12.9 12.9,12.9s12.9,-6.5 12.9,-12.9 -5.5,-12.9 -12.9,-12.9c-7.4,0 -12.9,6.4 -12.9,12.9ZM665.2,544.1c5.5,0 9.5,4.8 9.5,9.5s-3.8,9.5 -9.5,9.5 -9.5,-4.7 -9.5,-9.5s4.1,-9.5 9.5,-9.5ZM492.4,495.5c0,6.5 5.2,12.9 12.9,12.9c7.7,0 12.9,-6.5 12.9,-12.9s-5.5,-12.9 -12.9,-12.9c-7.4,0 -12.9,6.4 -12.9,12.9ZM505.3,485.9c5.5,0 9.5,4.8 9.5,9.5s-3.8,9.5 -9.5,9.5 -9.5,-4.7 -9.5,-9.5s4.1,-9.5 9.5,-9.5ZM492.5,588.8c0,5 3.9,9.8 9.8,9.8s9.8,-5 9.8,-9.8c0,-4.8 -4.2,-9.8 -9.8,-9.8s-9.8,4.9 -9.8,9.8ZM502.3,581.9c3.9,0 6.9,3.5 6.9,6.9c0,3.4 -2.8,6.9 -6.9,6.9s-6.9,-3.4 -6.9,-6.9c0,-3.5 2.9,-6.9 6.9,-6.9ZM600.5,625.1c0,5 3.9,9.8 9.8,9.8s9.8,-5 9.8,-9.8c0,-4.8 -4.2,-9.8 -9.8,-9.8s-9.8,4.9 -9.8,9.8ZM610.4,618.3c3.9,0 6.9,3.5 6.9,6.9c0,3.4 -2.7,6.9 -6.9,6.9s-6.9,-3.4 -6.9,-6.9c0,-3.5 2.9,-6.9 6.9,-6.9ZM518.3,527.5c0,5 3.9,9.8 9.8,9.8s9.8,-5 9.8,-9.8 -4.2,-9.8 -9.8,-9.8 -9.8,4.9 -9.8,9.8ZM528.1,520.6c3.9,0 6.9,3.5 6.9,6.9s-2.7,6.9 -6.9,6.9 -6.9,-3.4 -6.9,-6.9s2.9,-6.9 6.9,-6.9ZM466.2,531c0,5 3.9,9.8 9.8,9.8s9.8,-5 9.8,-9.8 -4.2,-9.8 -9.8,-9.8 -9.8,4.9 -9.8,9.8ZM476,524.1c3.9,0 6.9,3.5 6.9,6.9s-2.8,6.9 -6.9,6.9c-4.1,0 -6.9,-3.4 -6.9,-6.9s2.9,-6.9 6.9,-6.9ZM550.4,318.3c27.5,0 45.7,20.8 45.7,45.7c0,24.9 -19.8,45.7 -45.7,45.7s-45.7,-20.8 -45.7,-45.7c0,-24.9 18.2,-45.7 45.7,-45.7ZM550.4,318.3"
tools:ignore="VectorPath" />
</vector>

View File

@@ -1,176 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="108dp"
android:height="108dp"
android:viewportWidth="108"
android:viewportHeight="108">
<path
android:fillColor="#3DDC84"
android:pathData="M0,0h108v108h-108z" />
<path
android:fillColor="#00000000"
android:pathData="M9,0L9,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,0L19,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,0L29,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,0L39,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,0L49,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,0L59,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,0L69,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,0L79,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M89,0L89,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M99,0L99,108"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,9L108,9"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,19L108,19"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,29L108,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,39L108,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,49L108,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,59L108,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,69L108,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,79L108,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,89L108,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M0,99L108,99"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,29L89,29"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,39L89,39"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,49L89,49"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,59L89,59"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,69L89,69"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M19,79L89,79"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M29,19L29,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M39,19L39,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M49,19L49,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M59,19L59,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M69,19L69,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#00000000"
android:pathData="M79,19L79,89"
android:strokeWidth="0.8"
android:strokeColor="#33FFFFFF" />
<path
android:fillColor="#FFFFFF"
android:fillType="nonZero"
android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z"
android:strokeWidth="1"
android:strokeColor="#00000000" />
</vector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 857 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 982 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 463 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

View File

@@ -4,6 +4,8 @@
<string name="app_full_name">氧工具</string>
<string name="app_description">All in One</string>
<string name="core_beta_version">测试版</string>
<string name="core_ga_version">正式版</string>
<string name="core_ok">完成</string>
<string name="core_back">返回</string>
<string name="core_close">关闭</string>
@@ -12,6 +14,8 @@
<string name="core_search">搜索</string>
<string name="core_loading">加载中…</string>
<string name="core_no_connect">⚠️ 无法连接至互联网</string>
<string name="core_full_screen">全屏</string>
<string name="core_exit_full_screen">退出全屏</string>
<string name="core_install">安装</string>
<string name="core_installing">安装中……</string>
<string name="core_upgrade">更新</string>
@@ -24,6 +28,11 @@
<string name="core_undo">撤消</string>
<string name="core_nothing">空空如也</string>
<string name="core_nothing_found">未找到相关内容</string>
<string name="core_file_select_one">选择一个文件</string>
<string name="core_file_select_multi">选择文件</string>
<string name="core_file_select_one_text">选择一个文本文件</string>
<string name="core_file_select_multi_text">选择文本文件</string>
<string name="core_can_only_download_http_https">只能下载 HTTP/HTTPS URI%1$s</string>
<string name="feature_store_title">商店</string>
<string name="feature_store_retry">重试</string>

View File

@@ -3,6 +3,8 @@
<string name="app_full_name">Oxygen Toolbox</string>
<string name="app_description">All in One</string>
<string name="core_beta_version">Beta</string>
<string name="core_ga_version">GA</string>
<string name="core_ok">OK</string>
<string name="core_back">Back</string>
<string name="core_close">Close</string>
@@ -11,6 +13,8 @@
<string name="core_search">Search</string>
<string name="core_loading">Loading…</string>
<string name="core_no_connect">⚠️ Unable to connect to the internet</string>
<string name="core_full_screen">Full Screen</string>
<string name="core_exit_full_screen">Exit Full Screen</string>
<string name="core_install">Install</string>
<string name="core_installing">Installing…</string>
<string name="core_upgrade">Upgrade</string>
@@ -23,6 +27,11 @@
<string name="core_undo">Undo</string>
<string name="core_nothing">Nothing</string>
<string name="core_nothing_found">Nothing found</string>
<string name="core_file_select_one">Select a file</string>
<string name="core_file_select_multi">Select files</string>
<string name="core_file_select_one_text">Select a text file</string>
<string name="core_file_select_multi_text">Select text files</string>
<string name="core_can_only_download_http_https">Can only download HTTP/HTTPS URIs: %1$s</string>
<string name="feature_store_title">Store</string>
<string name="feature_store_retry">try again</string>

View File

@@ -11,7 +11,7 @@
</style>
<style name="Theme.Oxygen.Splash" parent="NightAdjusted.Theme.Splash">
<item name="windowSplashScreenAnimatedIcon">@drawable/ic_oxygen</item>
<item name="windowSplashScreenAnimatedIcon">@drawable/ic_launcher</item>
<item name="postSplashScreenTheme">@style/Theme.Oxygen</item>
</style>
</resources>

View File

@@ -1,31 +1,32 @@
[versions]
agp = "8.5.2"
kotlin = "2.0.10"
ksp = "2.0.10-1.0.24"
aboutlibraries = "11.2.2"
agp = "8.7.2"
kotlin = "2.0.20"
ksp = "2.0.20-1.0.25"
aboutlibraries = "11.2.3"
protobufPlugin = "0.9.4"
kotlinxSerialization = "2.0.0"
kotlinxSerialization = "2.0.20"
secrets = "2.0.1"
paging = "3.3.2"
desugarJdkLibs = "2.1.0"
composeBom = "2024.08.00"
desugarJdkLibs = "2.1.2"
composeBom = "2024.10.01"
junit = "4.13.2"
coreKtx = "1.13.1"
coreKtx = "1.15.0"
junitVersion = "1.2.1"
espressoCore = "3.6.1"
activityCompose = "1.9.1"
uiTestManifest = "1.7.5"
activityCompose = "1.9.3"
appcompat = "1.7.0"
androidxLifecycle = "2.8.4"
androidxLifecycle = "2.8.7"
androidxCoreSplashscreen = "1.0.1"
hilt = "2.52"
coil = "2.7.0"
kotlinxDatetime = "0.6.0"
kotlinxDatetime = "0.6.1"
androidxDataStore = "1.1.1"
protobuf = "4.27.3"
androidxNavigation = "2.7.7"
protobuf = "4.28.3"
androidxNavigation = "2.8.3"
androidxHiltNavigationCompose = "1.2.0"
kotlinxSerializationJson = "1.7.1"
kotlinxSerializationJson = "1.7.3"
retrofit = "2.11.0"
retrofitKotlinxSerializationJson = "1.0.0"
okhttp = "4.12.0"
@@ -59,7 +60,7 @@ androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "j
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
androidx-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest", version.ref = "uiTestManifest" }
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
androidx-ui = { group = "androidx.compose.ui", name = "ui" }
@@ -69,6 +70,7 @@ androidx-material3 = { group = "androidx.compose.material3", name = "material3"
material-icons-core = { group = "androidx.compose.material", name = "material-icons-core" }
material-icons-extended = { group = "androidx.compose.material", name = "material-icons-extended" }
material3-window-size = { group = "androidx.compose.material3", name = "material3-window-size-class" }
animation-graphics = { group = "androidx.compose.animation", name = "animation-graphics" }
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
androidx-appcompat = { group = "androidx.appcompat", name = "appcompat", version.ref = "appcompat" }