完成UI界面设计
|
@ -0,0 +1,15 @@
|
|||
*.iml
|
||||
.gradle
|
||||
/local.properties
|
||||
/.idea/caches
|
||||
/.idea/libraries
|
||||
/.idea/modules.xml
|
||||
/.idea/workspace.xml
|
||||
/.idea/navEditor.xml
|
||||
/.idea/assetWizardSettings.xml
|
||||
.DS_Store
|
||||
/build
|
||||
/captures
|
||||
.externalNativeBuild
|
||||
.cxx
|
||||
local.properties
|
|
@ -0,0 +1 @@
|
|||
/build
|
|
@ -0,0 +1,98 @@
|
|||
plugins {
|
||||
id("com.android.application")
|
||||
id("org.jetbrains.kotlin.android")
|
||||
kotlin("plugin.serialization")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "moe.fuqiuluo.shamrock"
|
||||
compileSdk = 33
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "moe.fuqiuluo.shamrock"
|
||||
minSdk = 24
|
||||
targetSdk = 33
|
||||
versionCode = 2
|
||||
versionName = "1.0.0"
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
vectorDrawables {
|
||||
useSupportLibrary = true
|
||||
}
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
buildFeatures {
|
||||
compose = true
|
||||
}
|
||||
composeOptions {
|
||||
kotlinCompilerExtensionVersion = "1.4.3"
|
||||
}
|
||||
packaging {
|
||||
resources {
|
||||
excludes += "/META-INF/{AL2.0,LGPL2.1}"
|
||||
excludes += "/META-INF/*"
|
||||
excludes += "/META-INF/NOTICE.txt"
|
||||
excludes += "/META-INF/DEPENDENCIES.txt"
|
||||
excludes += "/META-INF/NOTICE"
|
||||
excludes += "/META-INF/LICENSE"
|
||||
excludes += "/META-INF/DEPENDENCIES"
|
||||
excludes += "/META-INF/notice.txt"
|
||||
excludes += "/META-INF/dependencies.txt"
|
||||
excludes += "/META-INF/LGPL2.1"
|
||||
excludes += "/META-INF/ASL2.0"
|
||||
excludes += "/META-INF/INDEX.LIST"
|
||||
excludes += "/META-INF/io.netty.versions.properties"
|
||||
excludes += "/META-INF/INDEX.LIST"
|
||||
excludes += "/META-INF/LICENSE.txt"
|
||||
excludes += "/META-INF/license.txt"
|
||||
excludes += "/META-INF/*.kotlin_module"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("androidx.core:core-ktx:1.9.0")
|
||||
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1")
|
||||
implementation("androidx.activity:activity-compose:1.7.2")
|
||||
implementation(platform("androidx.compose:compose-bom:2023.06.01"))
|
||||
implementation("androidx.compose.ui:ui")
|
||||
implementation("androidx.compose.ui:ui-graphics")
|
||||
implementation("androidx.compose.ui:ui-tooling-preview")
|
||||
implementation("androidx.compose.material3:material3")
|
||||
//noinspection GradleDynamicVersion
|
||||
implementation("com.google.accompanist:accompanist-pager:0.31.5+")
|
||||
//noinspection GradleDynamicVersion
|
||||
implementation("com.google.accompanist:accompanist-systemuicontroller:0.31.5+")
|
||||
//noinspection GradleDynamicVersion useless
|
||||
// implementation("androidx.constraintlayout:constraintlayout-compose:1.1.0+")
|
||||
implementation("io.coil-kt:coil:2.4.0")
|
||||
implementation("io.coil-kt:coil-compose:2.4.0")
|
||||
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")
|
||||
|
||||
implementation(project(":xposed"))
|
||||
|
||||
testImplementation("junit:junit:4.13.2")
|
||||
androidTestImplementation("androidx.test.ext:junit:1.1.5")
|
||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
|
||||
androidTestImplementation(platform("androidx.compose:compose-bom:2023.06.01"))
|
||||
androidTestImplementation("androidx.compose.ui:ui-test-junit4")
|
||||
debugImplementation("androidx.compose.ui:ui-tooling")
|
||||
debugImplementation("androidx.compose.ui:ui-test-manifest")
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
|
@ -0,0 +1,20 @@
|
|||
{
|
||||
"version": 3,
|
||||
"artifactType": {
|
||||
"type": "APK",
|
||||
"kind": "Directory"
|
||||
},
|
||||
"applicationId": "moe.fuqiuluo.shamrock",
|
||||
"variantName": "release",
|
||||
"elements": [
|
||||
{
|
||||
"type": "SINGLE",
|
||||
"filters": [],
|
||||
"attributes": [],
|
||||
"versionCode": 2,
|
||||
"versionName": "1.0.0",
|
||||
"outputFile": "app-release.apk"
|
||||
}
|
||||
],
|
||||
"elementType": "File"
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package moe.fuqiuluo.shamrock
|
||||
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Instrumented test, which will execute on an Android device.
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class ExampleInstrumentedTest {
|
||||
@Test
|
||||
fun useAppContext() {
|
||||
// Context of the app under test.
|
||||
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||
assertEquals("moe.fuqiuluo.shamrock", appContext.packageName)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
|
||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
|
||||
<application
|
||||
android:name=".app.MyApplication"
|
||||
android:zygotePreloadName="@string/app_name"
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
android:supportsRtl="true"
|
||||
android:theme="@style/Theme.Shamrock"
|
||||
tools:targetApi="31">
|
||||
<activity
|
||||
android:name=".MainActivity"
|
||||
android:exported="true"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/Theme.Shamrock"
|
||||
tools:ignore="RedundantLabel">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
<category android:name="android.intent.category.LAUNCHER" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
|
||||
<provider
|
||||
android:authorities="moe.fuqiuluo.xqbot.provider"
|
||||
android:name=".ui.service.MultifunctionalProvider"
|
||||
android:exported="true"
|
||||
android:grantUriPermissions="true"
|
||||
tools:ignore="ExportedContentProvider" />
|
||||
|
||||
<meta-data
|
||||
android:name="xposedmodule"
|
||||
android:value="true" />
|
||||
<meta-data
|
||||
android:name="xposeddescription"
|
||||
android:value="A Bot Framework" />
|
||||
<meta-data
|
||||
android:name="xposedminversion"
|
||||
android:value="23" />
|
||||
|
||||
<!--适配华为(huawei)刘海屏-->
|
||||
<meta-data
|
||||
android:name="android.notch_support"
|
||||
android:value="true"/>
|
||||
<!--适配小米(xiaomi)刘海屏-->
|
||||
<meta-data
|
||||
android:name="notch.config"
|
||||
android:value="portrait|landscape" />
|
||||
</application>
|
||||
|
||||
</manifest>
|
|
@ -0,0 +1,376 @@
|
|||
@file:OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class)
|
||||
package moe.fuqiuluo.shamrock
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.annotation.ColorRes
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.core.FastOutLinearInEasing
|
||||
import androidx.compose.animation.core.FastOutSlowInEasing
|
||||
import androidx.compose.animation.core.MutableTransitionState
|
||||
import androidx.compose.animation.core.TweenSpec
|
||||
import androidx.compose.animation.scaleIn
|
||||
import androidx.compose.animation.scaleOut
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
import androidx.compose.foundation.LocalIndication
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.indication
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.absolutePadding
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.pager.HorizontalPager
|
||||
import androidx.compose.foundation.pager.PagerState
|
||||
import androidx.compose.foundation.pager.rememberPagerState
|
||||
import androidx.compose.material.ripple.rememberRipple
|
||||
import androidx.compose.material3.Divider
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.TabRow
|
||||
import androidx.compose.material3.TabRowDefaults
|
||||
import androidx.compose.material3.TabRowDefaults.tabIndicatorOffset
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.material3.TopAppBar
|
||||
import androidx.compose.material3.TopAppBarDefaults.topAppBarColors
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.core.view.WindowCompat
|
||||
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import moe.fuqiuluo.shamrock.ui.app.AppRuntime
|
||||
import moe.fuqiuluo.shamrock.ui.app.Level
|
||||
import moe.fuqiuluo.shamrock.ui.app.Logger
|
||||
import moe.fuqiuluo.shamrock.ui.app.RuntimeState
|
||||
import moe.fuqiuluo.shamrock.ui.fragment.DashboardFragment
|
||||
import moe.fuqiuluo.shamrock.ui.fragment.HomeFragment
|
||||
import moe.fuqiuluo.shamrock.ui.fragment.LabFragment
|
||||
import moe.fuqiuluo.shamrock.ui.fragment.LogFragment
|
||||
import moe.fuqiuluo.shamrock.ui.theme.LocalString
|
||||
import moe.fuqiuluo.shamrock.ui.theme.ShamrockTheme
|
||||
import moe.fuqiuluo.shamrock.ui.theme.TabDividerColor
|
||||
import moe.fuqiuluo.shamrock.ui.theme.TabSelectedColor
|
||||
import moe.fuqiuluo.shamrock.ui.theme.TabUnSelectedColor
|
||||
import moe.fuqiuluo.shamrock.ui.theme.ToolbarColor
|
||||
import moe.fuqiuluo.shamrock.ui.tools.NoIndication
|
||||
import moe.fuqiuluo.shamrock.ui.tools.ShamrockTab
|
||||
import java.lang.StringBuilder
|
||||
|
||||
class MainActivity : ComponentActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContent {
|
||||
CompositionLocalProvider(
|
||||
LocalIndication provides NoIndication
|
||||
) {
|
||||
AppMainView()
|
||||
}
|
||||
WindowCompat.getInsetsController(window, LocalView.current).apply {
|
||||
isAppearanceLightStatusBars = true
|
||||
}
|
||||
WindowCompat.setDecorFitsSystemWindows(window, true)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AppMainView() {
|
||||
val scope = rememberCoroutineScope()
|
||||
val ctx = LocalContext.current
|
||||
|
||||
val systemUiController = rememberSystemUiController()
|
||||
val statusBarDarkIcons by remember { mutableStateOf(true) }
|
||||
val navigationBarDarkIcons by remember { mutableStateOf(true) }
|
||||
|
||||
LaunchedEffect(systemUiController, statusBarDarkIcons, navigationBarDarkIcons) {
|
||||
systemUiController.statusBarDarkContentEnabled = statusBarDarkIcons
|
||||
systemUiController.navigationBarDarkContentEnabled = navigationBarDarkIcons
|
||||
systemUiController.setStatusBarColor(Color.White)
|
||||
}
|
||||
|
||||
// Home
|
||||
val isFined = remember { mutableStateOf(false) }
|
||||
val coreVersion = remember { mutableStateOf("1.0.0") }
|
||||
val coreName = remember { mutableStateOf("Xposed") }
|
||||
val coreCode = remember { mutableIntStateOf(1000) }
|
||||
|
||||
AppRuntime.state = remember {
|
||||
RuntimeState(isFined, coreVersion, coreCode, coreName)
|
||||
}
|
||||
|
||||
AppRuntime.logger = remember {
|
||||
Logger(mutableStateOf(StringBuilder()), mutableListOf())
|
||||
}
|
||||
|
||||
AppRuntime.AccountInfo.also {
|
||||
it.uin = remember {
|
||||
mutableStateOf("unknown")
|
||||
}
|
||||
it.nick = remember {
|
||||
mutableStateOf("unknown")
|
||||
}
|
||||
}
|
||||
|
||||
@Suppress("LocalVariableName") val LocalString = LocalString
|
||||
LaunchedEffect(isFined.value) {
|
||||
if (isFined.value) {
|
||||
AppRuntime.log("日志框架激活成功,开放操作许可。")
|
||||
Toast.makeText(ctx, LocalString.frameworkYes, Toast.LENGTH_SHORT).show()
|
||||
} else {
|
||||
AppRuntime.log("日志框架处于未激活状态,请检查。")
|
||||
Toast.makeText(ctx, LocalString.frameworkNo, Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
//模拟激活事件测试
|
||||
//LaunchedEffect(systemUiController) {
|
||||
// delay(5000)
|
||||
// isFined.value = true
|
||||
// AppRuntime.AccountInfo.also {
|
||||
// it.uin.value = "1372362033"
|
||||
// it.nick.value = "伏秋洛啊~"
|
||||
// }
|
||||
//}
|
||||
|
||||
ShamrockTheme {
|
||||
Surface(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
color = Color.White
|
||||
) {
|
||||
@Suppress("LocalVariableName") val TitlesWithIcon = LocalString.TitlesWithIcon
|
||||
val state = rememberPagerState(
|
||||
pageCount = { TitlesWithIcon.size },
|
||||
initialPage = 0,
|
||||
initialPageOffsetFraction = 0f,
|
||||
)
|
||||
|
||||
Scaffold(
|
||||
topBar = { ToolBar(
|
||||
title = arrayOf(
|
||||
"Clover", "CuteKitty", "Shamrock",
|
||||
"Threeleaf", "CuteCat", "FuckingCat",
|
||||
"XVideos", "Onlyfans", "Pornhub",
|
||||
"Xposed", "LittleFox", "Springboot",
|
||||
"Kotlin", "Rust & Android", "Dashabi",
|
||||
"YYDS", "Amd Yes", "Gayhub",
|
||||
"Yuzukkity", "HongKongDoll", "Xinrao"
|
||||
).random()
|
||||
) }
|
||||
) {
|
||||
val topPadding = it.calculateTopPadding()
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(top = topPadding)
|
||||
) {
|
||||
TabRow(
|
||||
modifier = Modifier
|
||||
.width(1080.dp)
|
||||
.background(Color.White)
|
||||
.absolutePadding(left = 70.dp, right = 70.dp)
|
||||
,
|
||||
selectedTabIndex = state.currentPage,
|
||||
divider = { },
|
||||
containerColor = Color.White,
|
||||
indicator = {
|
||||
TabRowDefaults.Indicator(
|
||||
Modifier.tabIndicatorOffset(it[state.currentPage]),
|
||||
color = TabSelectedColor,
|
||||
height = (3.3).dp
|
||||
)
|
||||
},
|
||||
) {
|
||||
TitlesWithIcon.forEachIndexed { index, titleWithIcon ->
|
||||
AnimatedTab(scope, state, index, titleWithIcon)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Divider(
|
||||
modifier = Modifier
|
||||
.shadow(8.dp),
|
||||
color = Color(0xBFDADADA),
|
||||
thickness = 0.2.dp
|
||||
)
|
||||
|
||||
HorizontalPager(
|
||||
modifier = Modifier
|
||||
.padding(top = 8.dp)
|
||||
,
|
||||
state = state,
|
||||
) { page ->
|
||||
when(page) {
|
||||
0 -> HomeFragment(AppRuntime.state)
|
||||
1 -> AppRuntime.AccountInfo.let {
|
||||
DashboardFragment(it.nick.value, it.uin.value)
|
||||
}
|
||||
2 -> LogFragment(AppRuntime.logger)
|
||||
3 -> LabFragment()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ToolBar(title: String) {
|
||||
TopAppBar(
|
||||
title = { Column {
|
||||
Text(
|
||||
text = title,
|
||||
color = ToolbarColor,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
Text(
|
||||
text = arrayOf(
|
||||
"A Framework Base On Xposed",
|
||||
"今天吃什么好呢?",
|
||||
"遇事不决,量子力学!",
|
||||
"Just kkb?",
|
||||
"いいよ,こいよ",
|
||||
"伊已逝 吾亦逝",
|
||||
"忆久意久 把义领",
|
||||
"喵帕斯!",
|
||||
"Creeper?",
|
||||
"Make American Great Again!",
|
||||
"TXHookPro",
|
||||
"曾经有人失去了那个她",
|
||||
"欲买桂花同载酒,终不似,少年游。",
|
||||
"抚千窟为佑 看长安落花",
|
||||
"どこにもない",
|
||||
"春日和 かかってらしゃい"
|
||||
).random(),
|
||||
color = ToolbarColor,
|
||||
fontSize = 14.sp
|
||||
)
|
||||
} },
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
colors = topAppBarColors(
|
||||
titleContentColor = Color.Black,
|
||||
containerColor = Color.White,
|
||||
scrolledContainerColor = Color.White,
|
||||
navigationIconContentColor = Color.White
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
private val SELECTED_TABLE = arrayOf(
|
||||
1, 2,
|
||||
4, 8,
|
||||
16, 32,
|
||||
64, 128,
|
||||
256, 512,
|
||||
1024, 2048
|
||||
)
|
||||
|
||||
@Composable
|
||||
@SuppressLint("AutoboxingStateValueProperty")
|
||||
private fun AnimatedTab(scope: CoroutineScope, state: PagerState, index: Int, titleWithIcon: Pair<String, Int>) {
|
||||
val curSelected = index == state.currentPage
|
||||
val lastSelectedState = remember {
|
||||
mutableIntStateOf(0)
|
||||
}
|
||||
val enter = remember {
|
||||
scaleIn(animationSpec = TweenSpec(150, easing = FastOutLinearInEasing))
|
||||
}
|
||||
val exit = remember {
|
||||
scaleOut(animationSpec = TweenSpec(150, easing = FastOutSlowInEasing))
|
||||
}
|
||||
|
||||
val defaultConst = SELECTED_TABLE[index * 2]
|
||||
val selectedConst = SELECTED_TABLE[(index * 2) + 1]
|
||||
val isFirst: Boolean = (lastSelectedState.value and defaultConst) != defaultConst
|
||||
|
||||
var icon: @Composable (() -> Unit)? = null
|
||||
var text: @Composable (() -> Unit)? = null
|
||||
|
||||
if (curSelected) {
|
||||
text = {
|
||||
AnimatedVisibility(visibleState = MutableTransitionState(false).also {
|
||||
it.targetState = isFirst || lastSelectedState.value and selectedConst == selectedConst
|
||||
}, enter = enter, exit = exit, modifier = Modifier) {
|
||||
Text(
|
||||
text = titleWithIcon.first,
|
||||
color = TabSelectedColor,
|
||||
fontSize = 15.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
modifier = Modifier
|
||||
.padding(bottom = 5.dp)
|
||||
.indication(
|
||||
remember { MutableInteractionSource() },
|
||||
rememberRipple(color = Color.Transparent)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
icon = {
|
||||
Icon(
|
||||
painter = painterResource(id = titleWithIcon.second),
|
||||
contentDescription = titleWithIcon.first,
|
||||
tint = Color.Unspecified,
|
||||
modifier = Modifier
|
||||
.height(24.dp)
|
||||
.width(24.dp)
|
||||
.padding(bottom = 5.dp)
|
||||
.indication(
|
||||
remember { MutableInteractionSource() },
|
||||
rememberRipple(color = Color.Transparent)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
ShamrockTab(
|
||||
selected = curSelected,
|
||||
onClick = { scope.launch {
|
||||
state.scrollToPage(index, 0f)
|
||||
} },
|
||||
text = text,
|
||||
icon = icon,
|
||||
selectedContentColor = Color.Transparent,
|
||||
unselectedContentColor = Color.Transparent,
|
||||
indication = null
|
||||
)
|
||||
lastSelectedState.value.let {
|
||||
var tmp = it
|
||||
if (isFirst) {
|
||||
tmp = it or defaultConst
|
||||
}
|
||||
lastSelectedState.value = if (curSelected) tmp or selectedConst else tmp xor selectedConst
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun MainPreview() {
|
||||
AppMainView()
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package moe.fuqiuluo.shamrock.app
|
||||
|
||||
import android.app.Application
|
||||
|
||||
class MyApplication: Application() {
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
package moe.fuqiuluo.shamrock.ui.app
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.icu.text.SimpleDateFormat
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.MutableIntState
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import java.util.Date
|
||||
|
||||
object AppRuntime {
|
||||
@SuppressLint("SimpleDateFormat")
|
||||
private val format = SimpleDateFormat("[HH:mm:ss] ")
|
||||
|
||||
lateinit var state: RuntimeState
|
||||
|
||||
lateinit var logger: Logger
|
||||
|
||||
object AccountInfo {
|
||||
lateinit var uin: MutableState<String>
|
||||
|
||||
lateinit var nick: MutableState<String>
|
||||
}
|
||||
|
||||
fun log(msg: String, level: Level = Level.INFO) {
|
||||
if (::logger.isInitialized) {
|
||||
val format = "%s%s %s".format(format.format(Date()), level.name, msg)
|
||||
|
||||
val builder = logger.logCache.value
|
||||
val start = builder.length
|
||||
val end = start + format.length
|
||||
|
||||
builder.append(format)
|
||||
logger.logRanges.add(Logger.LogRange(start, end, level))
|
||||
} else {
|
||||
Log.e("AppRuntime", "logger is not initialized")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class RuntimeState(
|
||||
val isFined: MutableState<Boolean>,
|
||||
val coreVersion: MutableState<String>,
|
||||
val coreCode: MutableIntState,
|
||||
val coreName: MutableState<String>
|
||||
) {
|
||||
val attrMap = mutableMapOf<String, String>()
|
||||
}
|
||||
|
||||
enum class Level(
|
||||
val color: Color
|
||||
) {
|
||||
DEBUG(Color(0xFF4CAF50)),
|
||||
INFO(Color(0xff6c6c6c)),
|
||||
WARN(Color(0xFFFF9800)),
|
||||
ERROR(Color(0xFFE91E63)),
|
||||
}
|
||||
|
||||
class Logger(
|
||||
val logCache: MutableState<StringBuilder>,
|
||||
val logRanges: MutableList<LogRange>,
|
||||
) {
|
||||
data class LogRange(
|
||||
val start: Int,
|
||||
val end: Int,
|
||||
val level: Level
|
||||
)
|
||||
}
|
||||
|
|
@ -0,0 +1,181 @@
|
|||
package moe.fuqiuluo.shamrock.ui.fragment
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.indication
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import moe.fuqiuluo.shamrock.R
|
||||
import moe.fuqiuluo.shamrock.ui.tools.IosSwitch
|
||||
|
||||
@Composable
|
||||
fun NoticeBox(
|
||||
modifier: Modifier = Modifier,
|
||||
text: String,
|
||||
onClick: (() -> Unit)? = null
|
||||
) {
|
||||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.indication(remember { MutableInteractionSource() }, null)
|
||||
.background(
|
||||
color = Color(0xFFf4f4f4),
|
||||
shape = RoundedCornerShape(12.dp)
|
||||
)
|
||||
.clickable(enabled = onClick != null) {
|
||||
onClick?.invoke()
|
||||
}
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
top = 10.dp,
|
||||
bottom = 10.dp
|
||||
)
|
||||
.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.padding(10.dp)
|
||||
.width(20.dp)
|
||||
.height(20.dp),
|
||||
painter = painterResource(id = R.drawable.round_warning_24),
|
||||
contentDescription = "WarningIcon",
|
||||
tint = Color(0xFF838383)
|
||||
)
|
||||
Text(
|
||||
text = text,
|
||||
fontSize = 11.sp,
|
||||
color = Color(0xff6c6c6c)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ActionBox(
|
||||
modifier: Modifier = Modifier,
|
||||
painter: Painter,
|
||||
title: String,
|
||||
content: @Composable (textColor: Color) -> Unit
|
||||
) {
|
||||
val textColor = Color(0xff6c6c6c)
|
||||
Box(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.indication(remember { MutableInteractionSource() }, null)
|
||||
.background(
|
||||
color = Color(0xFFf4f4f4),
|
||||
shape = RoundedCornerShape(12.dp)
|
||||
)
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
top = 10.dp,
|
||||
bottom = 10.dp
|
||||
)
|
||||
.fillMaxWidth(),
|
||||
) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.padding(start = 10.dp)
|
||||
.width(20.dp)
|
||||
.height(20.dp),
|
||||
painter = painter,
|
||||
contentDescription = "ActionIcon",
|
||||
tint = Color.Unspecified
|
||||
)
|
||||
Text(
|
||||
modifier = Modifier.padding(start = 6.dp),
|
||||
text = title,
|
||||
fontSize = 12.sp,
|
||||
color = textColor
|
||||
)
|
||||
}
|
||||
|
||||
Row(
|
||||
modifier = Modifier.padding(8.dp)
|
||||
) {
|
||||
content.invoke(textColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ActionSwitch(
|
||||
text: String,
|
||||
isSwitch: Boolean,
|
||||
onValueChanged: (Boolean) -> Unit,
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(2.dp)
|
||||
.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier,
|
||||
text = text,
|
||||
fontSize = 13.sp
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.End
|
||||
) {
|
||||
IosSwitch(
|
||||
switchValue = isSwitch,
|
||||
buttonHeight = 22.dp,
|
||||
onValueChanged = onValueChanged
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun NoticeBoxPreview() {
|
||||
NoticeBox(
|
||||
text = "该模块仅适用于QQ版本8.9.68及以上的版本。\n" +
|
||||
"同时声明本项目仅用于学习与交流,请于24小时内删除。\n" +
|
||||
"同时开源贡献者均享受免责条例。"
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun ActionBoxPreview() {
|
||||
ActionBox(
|
||||
painter = painterResource(id = R.drawable.ic_help_512),
|
||||
title = "使用帮助 & 教程"
|
||||
) {
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,318 @@
|
|||
package moe.fuqiuluo.shamrock.ui.fragment
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.os.Build
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.absolutePadding
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Divider
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.modifier.modifierLocalMapOf
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.core.content.edit
|
||||
import coil.compose.AsyncImagePainter
|
||||
import coil.compose.rememberAsyncImagePainter
|
||||
import coil.request.ImageRequest
|
||||
import coil.size.Size
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.launch
|
||||
import moe.fuqiuluo.shamrock.R
|
||||
import moe.fuqiuluo.shamrock.ui.app.AppRuntime
|
||||
import moe.fuqiuluo.shamrock.ui.app.Level
|
||||
import moe.fuqiuluo.shamrock.ui.theme.TabSelectedColor
|
||||
import moe.fuqiuluo.shamrock.ui.theme.TabUnSelectedColor
|
||||
|
||||
@Composable
|
||||
fun DashboardFragment(
|
||||
nick: String,
|
||||
uin: String
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
val ctx = LocalContext.current
|
||||
val preferences = ctx.getSharedPreferences("config", 0)
|
||||
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.fillMaxSize(),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(16.dp)
|
||||
) {
|
||||
AccountCard(nick, uin)
|
||||
InformationCard()
|
||||
FunctionCard(scope, ctx, preferences, "功能设置")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun FunctionCard(
|
||||
scope: CoroutineScope,
|
||||
ctx: Context,
|
||||
preferences: SharedPreferences,
|
||||
title: String
|
||||
) {
|
||||
ActionBox(
|
||||
modifier = Modifier.padding(top = 12.dp),
|
||||
painter = painterResource(id = R.drawable.round_api_24),
|
||||
title = title
|
||||
) {
|
||||
Column {
|
||||
Divider(
|
||||
modifier = Modifier,
|
||||
color = TabUnSelectedColor,
|
||||
thickness = 0.2.dp
|
||||
)
|
||||
|
||||
Function(
|
||||
title = "强制平板模式",
|
||||
desc = "强制QQ使用平板模式,实现共存登录。",
|
||||
descColor = TabSelectedColor,
|
||||
isSwitch = preferences.getBoolean("tablet", false)
|
||||
) {
|
||||
preferences.edit { putBoolean("tablet", it) }
|
||||
scope.launch { Toast.makeText(ctx, "重启QQ生效", Toast.LENGTH_SHORT).show() }
|
||||
}
|
||||
|
||||
Function(
|
||||
title = "HTTP回调",
|
||||
desc = "OneBot标准的HTTPAPI回调,Shamrock作为Client。",
|
||||
descColor = TabSelectedColor,
|
||||
isSwitch = !preferences.getString("webhook", "").isNullOrBlank()
|
||||
) {
|
||||
|
||||
//preferences.edit { putBoolean("webhook", it) }
|
||||
scope.launch { Toast.makeText(ctx, "重启QQ生效", Toast.LENGTH_SHORT).show() }
|
||||
|
||||
}
|
||||
|
||||
Function(
|
||||
title = "主动WebSocket",
|
||||
desc = "OneBot标准WebSocket,Shamrock作为Server。",
|
||||
descColor = TabSelectedColor,
|
||||
isSwitch = preferences.getBoolean("ws", false)
|
||||
) {
|
||||
preferences.edit { putBoolean("ws", it) }
|
||||
scope.launch { Toast.makeText(ctx, "重启QQ生效", Toast.LENGTH_SHORT).show() }
|
||||
|
||||
}
|
||||
|
||||
Function(
|
||||
title = "被动WebSocket",
|
||||
desc = "OneBot标准WebSocket,Shamrock作为Client。",
|
||||
descColor = TabSelectedColor,
|
||||
isSwitch = !preferences.getString("ws_api", "").isNullOrBlank()
|
||||
) {
|
||||
|
||||
scope.launch { Toast.makeText(ctx, "重启QQ生效", Toast.LENGTH_SHORT).show() }
|
||||
|
||||
}
|
||||
|
||||
Function(
|
||||
title = "专业级接口",
|
||||
desc = "如果你不知道你在做什么,请不要开启本功能。",
|
||||
descColor = Color.Red,
|
||||
isSwitch = preferences.getBoolean("pro_api", false)
|
||||
) {
|
||||
preferences.edit { putBoolean("pro_api", it) }
|
||||
scope.launch { Toast.makeText(ctx, "重启QQ生效", Toast.LENGTH_SHORT).show() }
|
||||
AppRuntime.log("专业级API = $it", Level.WARN)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Function(
|
||||
title: String,
|
||||
desc: String? = null,
|
||||
descColor: Color? = null,
|
||||
isSwitch: Boolean,
|
||||
onClick: (Boolean) -> Unit
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.absolutePadding(left = 8.dp, right = 8.dp, top = 12.dp, bottom = 0.dp)
|
||||
) {
|
||||
if (desc != null && descColor != null) {
|
||||
Text(
|
||||
modifier = Modifier.padding(2.dp),
|
||||
text = desc,
|
||||
color = descColor,
|
||||
fontSize = 11.sp
|
||||
)
|
||||
}
|
||||
ActionSwitch(
|
||||
text = title,
|
||||
isSwitch = isSwitch
|
||||
) {
|
||||
onClick(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun InformationCard() {
|
||||
ActionBox(
|
||||
modifier = Modifier.padding(top = 12.dp),
|
||||
painter = painterResource(id = R.drawable.round_info_24),
|
||||
title = "数据信息"
|
||||
) {
|
||||
Column {
|
||||
Divider(
|
||||
modifier = Modifier,
|
||||
color = TabUnSelectedColor,
|
||||
thickness = 0.2.dp
|
||||
)
|
||||
|
||||
InfoItem(
|
||||
title = "系统版本",
|
||||
content = "${Build.VERSION.RELEASE} (API ${Build.VERSION.SDK_INT})"
|
||||
)
|
||||
|
||||
InfoItem(title = "设备", content = "${Build.BRAND} ${Build.MODEL}")
|
||||
|
||||
InfoItem(title = "系统架构", content = Build.SUPPORTED_ABIS.joinToString())
|
||||
|
||||
InfoItem(
|
||||
title = "累计调用次数",
|
||||
content = "114514"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun InfoItem(
|
||||
modifier: Modifier = Modifier,
|
||||
titleColor: Color = Color(0xFF5A5A5A),
|
||||
contentColor: Color = Color(0xFF5A5A5A),
|
||||
title: String,
|
||||
content: String
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.absolutePadding(left = 8.dp, right = 8.dp, top = 12.dp, bottom = 0.dp)
|
||||
.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
text = title,
|
||||
fontSize = 14.sp,
|
||||
color = titleColor
|
||||
)
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.End
|
||||
) {
|
||||
Text(
|
||||
text = content,
|
||||
fontSize = 14.sp,
|
||||
color = contentColor
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun AccountCard(
|
||||
nick: String,
|
||||
uin: String
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(
|
||||
Brush.linearGradient(
|
||||
listOf(Color(0xFF2196F3), Color(0xFF00BCD4))
|
||||
), shape = RoundedCornerShape(12.dp)
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
val painter = kotlin.runCatching {
|
||||
rememberAsyncImagePainter(model = ImageRequest.Builder(LocalContext.current)
|
||||
.data("https://q.qlogo.cn/g?b=qq&nk=$uin&s=100")
|
||||
.crossfade(true)
|
||||
.size(Size.ORIGINAL)
|
||||
.build())
|
||||
}.onSuccess {
|
||||
when(it.state){
|
||||
is AsyncImagePainter.State.Success ->{
|
||||
|
||||
}
|
||||
is AsyncImagePainter.State.Loading ->{
|
||||
}
|
||||
is AsyncImagePainter.State.Error ->{
|
||||
AppRuntime.log("头像拉取失败,请检查网络连接。", Level.ERROR)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}.getOrDefault(painterResource(id = R.drawable.ic_letter_q))
|
||||
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
start = 15.dp,
|
||||
end = 5.dp
|
||||
)
|
||||
.width(45.dp)
|
||||
.height(45.dp)
|
||||
.clip(RoundedCornerShape(36.dp))
|
||||
,
|
||||
painter = painter,
|
||||
contentDescription = "HeadLogo",
|
||||
tint = Color.Unspecified
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(20.dp),
|
||||
verticalArrangement = Arrangement.Top,
|
||||
horizontalAlignment = Alignment.Start,
|
||||
) {
|
||||
Text(
|
||||
text = nick,
|
||||
color = Color.White,
|
||||
fontSize = 14.sp
|
||||
)
|
||||
Text(
|
||||
text = "QQ号:$uin",
|
||||
color = Color.White,
|
||||
fontSize = 14.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun PreViewDashBoard() {
|
||||
DashboardFragment("测试昵称", "100001")
|
||||
}
|
|
@ -0,0 +1,163 @@
|
|||
package moe.fuqiuluo.shamrock.ui.fragment
|
||||
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableIntState
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import moe.fuqiuluo.shamrock.R
|
||||
import moe.fuqiuluo.shamrock.ui.app.RuntimeState
|
||||
import moe.fuqiuluo.shamrock.ui.theme.LocalString
|
||||
|
||||
@Composable
|
||||
fun HomeFragment(
|
||||
runtime: RuntimeState
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
val ctx = LocalContext.current
|
||||
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.fillMaxSize(),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(16.dp)
|
||||
) {
|
||||
StatusCardBoard(runtime.isFined, runtime.coreVersion, runtime.coreCode, runtime.coreName)
|
||||
|
||||
NoticeBox(
|
||||
modifier = Modifier
|
||||
.padding(top = 12.dp),
|
||||
text = LocalString.legalWarning,
|
||||
) {
|
||||
Toast.makeText(ctx, arrayOf(
|
||||
"请严格遵守哦!", "点我又不能下崽...", "家人们谁懂啊!", "别点啦,记得遵守规则啊!", "CRC:06f77ca1"
|
||||
).random(), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
|
||||
ActionBox(
|
||||
modifier = Modifier
|
||||
.padding(top = 12.dp),
|
||||
painter = painterResource(id = R.drawable.ic_help_512),
|
||||
title = "使用教程 & 注意事项"
|
||||
) { textColor ->
|
||||
Text(text = """
|
||||
Q:如何使用呢?
|
||||
A:在Xposed/Lsposed中激活模块,选中目标应用重新后强行停止目标应用并重新启动即可。
|
||||
Q:冻结封号是怎么回事?
|
||||
A:也许未来的某个时刻,会加强检测吧。亦或许你发了什么不干净的东西呢。
|
||||
Q:Shamrock有模块冲突?
|
||||
A:目前没有发现模块冲突。
|
||||
Q:Shamrock需求权限?
|
||||
A:Shamrock使用到了Net权限,去实现HTTPAPI和WebSocket的一些请求,请确保你所设置的端口未被占用。当你使用来自一些Android机器人框架的插件,请确保本APP的存活,否则将无法运行;当你使用来自OneBot标准的相关接口时,无需保证APP的存活。
|
||||
Q:Shamrock运行原理?
|
||||
A:使用Xposed框架,将代码插入到QQ运行时,并在QQ内使用Ktor框架放出外部可用的API接口,采用Provider/DynamicBroadcast与实现模块与目标进程数据交互,无需额外的储存权限。
|
||||
Q:兼容性框架支持有哪些?
|
||||
A:
|
||||
1,支持载入一些Android机器人框架的插件(如QR词库插件)。
|
||||
2,支持OneBot12标准,但不会更新支持之后的OneBot标准(特殊性)。
|
||||
3,原始支持来自Native/Jvm的插件。
|
||||
""".trimIndent(),
|
||||
color = textColor,
|
||||
fontSize = 12.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun StatusCardBoard(
|
||||
isRight: MutableState<Boolean>,
|
||||
version: MutableState<String>,
|
||||
code: MutableIntState,
|
||||
core: MutableState<String>
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(
|
||||
Brush.linearGradient(
|
||||
listOf(Color(0xFF03AA9A), Color(0xFF4DB8AD))
|
||||
), shape = RoundedCornerShape(12.dp)
|
||||
),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Icon(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
start = 25.dp,
|
||||
end = 5.dp
|
||||
)
|
||||
.width(20.dp)
|
||||
.height(20.dp),
|
||||
painter = painterResource(id = if (isRight.value) R.drawable.round_near_me_24 else
|
||||
R.drawable.round_near_me_disabled_24),
|
||||
contentDescription = "StatusIcon",
|
||||
tint = Color.White
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(20.dp)
|
||||
.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.Top,
|
||||
horizontalAlignment = Alignment.Start,
|
||||
) {
|
||||
Text(
|
||||
text = if (isRight.value) LocalString.frameworkYesLite else LocalString.frameworkNoLite,
|
||||
color = Color.White,
|
||||
fontSize = 14.sp
|
||||
)
|
||||
Text(
|
||||
text = "${version.value} (${code.intValue}) - ${core.value}",
|
||||
color = Color.White,
|
||||
fontSize = 14.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun MainPreview() {
|
||||
val isFined = remember { mutableStateOf(false) }
|
||||
val coreVersion = remember { mutableStateOf("1.0.0") }
|
||||
val coreName = remember { mutableStateOf("Xposed") }
|
||||
val coreCode = remember { mutableIntStateOf(1000) }
|
||||
|
||||
val runtime = remember {
|
||||
RuntimeState(isFined, coreVersion, coreCode, coreName)
|
||||
}
|
||||
|
||||
HomeFragment(runtime)
|
||||
}
|
|
@ -0,0 +1,139 @@
|
|||
package moe.fuqiuluo.shamrock.ui.fragment
|
||||
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.absolutePadding
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Divider
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import androidx.core.content.edit
|
||||
import kotlinx.coroutines.launch
|
||||
import moe.fuqiuluo.shamrock.R
|
||||
import moe.fuqiuluo.shamrock.ui.theme.LocalString
|
||||
import moe.fuqiuluo.shamrock.ui.theme.TabUnSelectedColor
|
||||
import moe.fuqiuluo.shamrock.ui.tools.NoticeTextDialog
|
||||
|
||||
@Composable
|
||||
fun LabFragment() {
|
||||
val scope = rememberCoroutineScope()
|
||||
val ctx = LocalContext.current
|
||||
val preferences = ctx.getSharedPreferences("config", 0)
|
||||
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.fillMaxSize(),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.verticalScroll(rememberScrollState())
|
||||
.padding(16.dp)
|
||||
) {
|
||||
val showNoticeDialog = remember { mutableStateOf(false) }
|
||||
NoticeBox(text = LocalString.labWarning) {
|
||||
showNoticeDialog.value = true
|
||||
}
|
||||
if (showNoticeDialog.value) {
|
||||
NoticeTextDialog(
|
||||
openDialog = showNoticeDialog,
|
||||
title = "温馨提示",
|
||||
text = "实验室功能会导致一些奇怪的问题,请谨慎使用!"
|
||||
)
|
||||
}
|
||||
|
||||
ActionBox(
|
||||
modifier = Modifier.padding(top = 12.dp),
|
||||
painter = painterResource(id = R.drawable.baseline_preview_24),
|
||||
title = "显示设置"
|
||||
) {
|
||||
Column {
|
||||
Divider(
|
||||
modifier = Modifier,
|
||||
color = TabUnSelectedColor,
|
||||
thickness = 0.2.dp
|
||||
)
|
||||
|
||||
Function(
|
||||
title = "中二病模式",
|
||||
desc = "也许会导致奇怪的问题,大抵就是你看不懂罢了。",
|
||||
descColor = it,
|
||||
isSwitch = preferences.getBoolean("2B", false)
|
||||
) {
|
||||
preferences.edit { putBoolean("2B", it) }
|
||||
scope.launch { Toast.makeText(ctx, "重启生效哦~", Toast.LENGTH_SHORT).show() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ActionBox(
|
||||
modifier = Modifier.padding(top = 12.dp),
|
||||
painter = painterResource(id = R.drawable.round_logo_dev_24),
|
||||
title = "实验功能"
|
||||
) {
|
||||
Column {
|
||||
Divider(
|
||||
modifier = Modifier,
|
||||
color = TabUnSelectedColor,
|
||||
thickness = 0.2.dp
|
||||
)
|
||||
|
||||
Function(
|
||||
title = "自动清理QQ垃圾",
|
||||
desc = "也许会导致奇怪的问题。",
|
||||
descColor = it,
|
||||
isSwitch = preferences.getBoolean("auto_clear", false)
|
||||
) {
|
||||
preferences.edit { putBoolean("auto_clear", it) }
|
||||
scope.launch { Toast.makeText(ctx, "重启QQ生效", Toast.LENGTH_SHORT).show() }
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Function(
|
||||
title: String,
|
||||
desc: String,
|
||||
descColor: Color,
|
||||
isSwitch: Boolean,
|
||||
onClick: (Boolean) -> Unit
|
||||
) {
|
||||
Column(modifier = Modifier
|
||||
.absolutePadding(left = 8.dp, right = 8.dp, top = 12.dp, bottom = 0.dp)
|
||||
) {
|
||||
Text(
|
||||
modifier = Modifier.padding(2.dp),
|
||||
text = desc,
|
||||
color = descColor,
|
||||
fontSize = 11.sp
|
||||
)
|
||||
ActionSwitch(text = title, isSwitch = isSwitch) {
|
||||
onClick(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun LabPreView() {
|
||||
LabFragment()
|
||||
}
|
|
@ -0,0 +1,126 @@
|
|||
package moe.fuqiuluo.shamrock.ui.fragment
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.indication
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material3.Surface
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.ParagraphStyle
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import moe.fuqiuluo.shamrock.ui.app.Logger
|
||||
import java.lang.StringBuilder
|
||||
import java.util.Collections.EMPTY_LIST
|
||||
|
||||
@Composable
|
||||
fun LogFragment(
|
||||
logger: Logger
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
.fillMaxSize(),
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(16.dp)
|
||||
) {
|
||||
NoticeBox(text = "日志仅保留最新的${Short.MAX_VALUE}条,超出部分会自动删除,如有需要请做好保留。")
|
||||
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.padding(top = 12.dp)
|
||||
.fillMaxSize()
|
||||
.indication(remember { MutableInteractionSource() }, null)
|
||||
.background(
|
||||
color = Color(0xFFf4f4f4),
|
||||
shape = RoundedCornerShape(12.dp)
|
||||
)
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.padding(
|
||||
start = 10.dp,
|
||||
end = 10.dp,
|
||||
top = 10.dp,
|
||||
bottom = 10.dp
|
||||
),
|
||||
horizontalArrangement = Arrangement.Start,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
@Suppress("UNCHECKED_CAST") val text = remember {
|
||||
mutableStateOf(
|
||||
AnnotatedString(
|
||||
text = "",
|
||||
spanStyles = EMPTY_LIST as List<AnnotatedString.Range<SpanStyle>>,
|
||||
paragraphStyles = EMPTY_LIST as List<AnnotatedString.Range<ParagraphStyle>>
|
||||
)
|
||||
)
|
||||
}
|
||||
LaunchedEffect(logger.logCache.value.length) {
|
||||
val spanStyles = mutableListOf<AnnotatedString.Range<SpanStyle>>()
|
||||
val paragraphStyles = mutableListOf<AnnotatedString.Range<ParagraphStyle>>()
|
||||
logger.logRanges.forEach {
|
||||
spanStyles.add(AnnotatedString.Range(
|
||||
SpanStyle(
|
||||
color = it.level.color
|
||||
), it.start, it.end
|
||||
))
|
||||
paragraphStyles.add(AnnotatedString.Range(
|
||||
ParagraphStyle(
|
||||
textAlign = TextAlign.Start
|
||||
), it.start, it.end
|
||||
))
|
||||
}
|
||||
text.value = AnnotatedString(
|
||||
text = logger.logCache.value.toString(),
|
||||
spanStyles = spanStyles,
|
||||
paragraphStyles = paragraphStyles
|
||||
)
|
||||
}
|
||||
|
||||
Text(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
text = text.value,
|
||||
fontSize = 12.sp,
|
||||
color = Color(0xff6c6c6c),
|
||||
)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("UnrememberedMutableState")
|
||||
@Preview
|
||||
@Composable
|
||||
private fun LogPreview() {
|
||||
LogFragment(
|
||||
Logger(mutableStateOf(StringBuilder()), mutableListOf())
|
||||
)
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package moe.fuqiuluo.shamrock.ui.service
|
||||
|
||||
import android.content.ContentProvider
|
||||
import android.content.ContentValues
|
||||
import android.database.Cursor
|
||||
import android.net.Uri
|
||||
|
||||
class MultifunctionalProvider: ContentProvider() {
|
||||
override fun insert(uri: Uri, content: ContentValues?): Uri {
|
||||
|
||||
|
||||
return uri
|
||||
}
|
||||
|
||||
override fun onCreate(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun query(
|
||||
p0: Uri,
|
||||
p1: Array<out String>?,
|
||||
p2: String?,
|
||||
p3: Array<out String>?,
|
||||
p4: String?
|
||||
): Cursor? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun getType(p0: Uri): String? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun delete(p0: Uri, p1: String?, p2: Array<out String>?): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
override fun update(p0: Uri, p1: ContentValues?, p2: String?, p3: Array<out String>?): Int {
|
||||
return 0
|
||||
}
|
||||
}
|
|
@ -0,0 +1,17 @@
|
|||
package moe.fuqiuluo.shamrock.ui.theme
|
||||
|
||||
import androidx.compose.ui.graphics.Color
|
||||
|
||||
val Purple80 = Color(0xFFD0BCFF)
|
||||
val PurpleGrey80 = Color(0xFFCCC2DC)
|
||||
val Pink80 = Color(0xFFEFB8C8)
|
||||
|
||||
val Purple40 = Color(0xFF6650a4)
|
||||
val PurpleGrey40 = Color(0xFF625b71)
|
||||
val Pink40 = Color(0xFF7D5260)
|
||||
|
||||
val ToolbarColor = Color(0xFF6f6f6f)
|
||||
|
||||
val TabSelectedColor = Color(0xFF5A5A5A)
|
||||
val TabUnSelectedColor = Color(0xFF6f6f6f)
|
||||
val TabDividerColor = Color(0xFFBEBEBE)
|
|
@ -0,0 +1,75 @@
|
|||
@file:Suppress(
|
||||
"SpellCheckingInspection", "unused", "PropertyName",
|
||||
"ClassName", "NonAsciiCharacters"
|
||||
)
|
||||
package moe.fuqiuluo.shamrock.ui.theme
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.ReadOnlyComposable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import moe.fuqiuluo.shamrock.R
|
||||
|
||||
private val LocalStringDefault = Default()
|
||||
private val LocalString2B = Chūnibyō()
|
||||
|
||||
val LocalString: VarString
|
||||
@ReadOnlyComposable
|
||||
@Composable
|
||||
get() {
|
||||
val ctx = LocalContext.current
|
||||
val sharedPreferences = ctx.getSharedPreferences("config", 0)
|
||||
return if (!sharedPreferences.getBoolean("2B", false)) {
|
||||
LocalStringDefault
|
||||
} else {
|
||||
LocalString2B
|
||||
}
|
||||
}
|
||||
|
||||
private open class Chūnibyō: Default() {
|
||||
init {
|
||||
TitlesWithIcon = arrayOf(
|
||||
"玄天" to R.drawable.round_home_24,
|
||||
"天穹" to R.drawable.round_dashboard_24,
|
||||
"无极" to R.drawable.round_monitor_heart_24,
|
||||
"飘渺" to R.drawable.round_logo_dev_24
|
||||
)
|
||||
frameworkYes = "仙路已通"
|
||||
frameworkNo = "鬼怪横行"
|
||||
frameworkYesLite = "五行已备"
|
||||
frameworkNoLite = "需待东风"
|
||||
legalWarning = "白榆,北辰,曜魄,应星,云川当方位不乱,即可作于无极之域。\n" +
|
||||
"执明起,至除免于灾祸。\n" +
|
||||
"元冥浩浩,非凡不可动之。"
|
||||
labWarning = "寒酥降矣,梅熟日久,莫不可测。"
|
||||
}
|
||||
}
|
||||
|
||||
private open class Default: VarString(
|
||||
TitlesWithIcon = arrayOf(
|
||||
"主页" to R.drawable.round_home_24,
|
||||
"状态" to R.drawable.round_dashboard_24,
|
||||
"日志" to R.drawable.round_monitor_heart_24,
|
||||
"Lab" to R.drawable.round_logo_dev_24
|
||||
), "框架已激活", "框架未激活",
|
||||
"已激活", "未激活",
|
||||
legalWarning = "该模块仅适用于目标版本8.9.68及以上的版本。\n" +
|
||||
"同时声明本项目仅用于学习与交流,请于24小时内删除。\n" +
|
||||
"同时开源贡献者均享受免责条例。",
|
||||
labWarning = "实验室功能,可能会导致出乎意料的BUG!",
|
||||
"日志"
|
||||
)
|
||||
|
||||
open class VarString(
|
||||
var TitlesWithIcon: Array<Pair<String, Int>>,
|
||||
var frameworkYes: String,
|
||||
var frameworkNo: String,
|
||||
|
||||
var frameworkYesLite: String,
|
||||
var frameworkNoLite: String,
|
||||
|
||||
var legalWarning: String,
|
||||
|
||||
var labWarning: String,
|
||||
|
||||
var logTitle: String
|
||||
)
|
|
@ -0,0 +1,71 @@
|
|||
package moe.fuqiuluo.shamrock.ui.theme
|
||||
|
||||
import android.app.Activity
|
||||
import android.os.Build
|
||||
import androidx.compose.foundation.isSystemInDarkTheme
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.darkColorScheme
|
||||
import androidx.compose.material3.dynamicDarkColorScheme
|
||||
import androidx.compose.material3.dynamicLightColorScheme
|
||||
import androidx.compose.material3.lightColorScheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.SideEffect
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.core.view.WindowCompat
|
||||
|
||||
private val DarkColorScheme = darkColorScheme(
|
||||
primary = Color(0xFFD0BCFF),
|
||||
secondary = Color(0xFFCCC2DC),
|
||||
tertiary = Color(0xFFEFB8C8)
|
||||
)
|
||||
|
||||
private val LightColorScheme = lightColorScheme(
|
||||
primary = Color.White,
|
||||
secondary = Color.White,
|
||||
tertiary = Color(0xFF7D5260),
|
||||
surface = Color(0xFFFFFBFE),
|
||||
|
||||
/* Other default colors to override
|
||||
background = Color(0xFFFFFBFE),
|
||||
onPrimary = Color.White,
|
||||
onSecondary = Color.White,
|
||||
onTertiary = Color.White,
|
||||
onBackground = Color(0xFF1C1B1F),
|
||||
onSurface = Color(0xFF1C1B1F),
|
||||
*/
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun ShamrockTheme(
|
||||
darkTheme: Boolean = isSystemInDarkTheme(),
|
||||
// Dynamic color is available on Android 12+
|
||||
dynamicColor: Boolean = true,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
val colorScheme = when {
|
||||
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
|
||||
val context = LocalContext.current
|
||||
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
|
||||
}
|
||||
|
||||
darkTheme -> DarkColorScheme
|
||||
else -> LightColorScheme
|
||||
}
|
||||
val view = LocalView.current
|
||||
if (!view.isInEditMode) {
|
||||
SideEffect {
|
||||
val window = (view.context as Activity).window
|
||||
window.statusBarColor = colorScheme.primary.toArgb()
|
||||
WindowCompat.getInsetsController(window, view).isAppearanceLightStatusBars = darkTheme
|
||||
}
|
||||
}
|
||||
|
||||
MaterialTheme(
|
||||
colorScheme = colorScheme,
|
||||
typography = Typography,
|
||||
content = content
|
||||
)
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package moe.fuqiuluo.shamrock.ui.theme
|
||||
|
||||
import androidx.compose.material3.Typography
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.sp
|
||||
|
||||
// Set of Material typography styles to start with
|
||||
val Typography = Typography(
|
||||
bodyLarge = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 16.sp,
|
||||
lineHeight = 24.sp,
|
||||
letterSpacing = 0.5.sp
|
||||
)
|
||||
/* Other default text styles to override
|
||||
titleLarge = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontSize = 22.sp,
|
||||
lineHeight = 28.sp,
|
||||
letterSpacing = 0.sp
|
||||
),
|
||||
labelSmall = TextStyle(
|
||||
fontFamily = FontFamily.Default,
|
||||
fontWeight = FontWeight.Medium,
|
||||
fontSize = 11.sp,
|
||||
lineHeight = 16.sp,
|
||||
letterSpacing = 0.5.sp
|
||||
)
|
||||
*/
|
||||
)
|
|
@ -0,0 +1,73 @@
|
|||
package moe.fuqiuluo.shamrock.ui.tools
|
||||
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.AlertDialog
|
||||
import androidx.compose.material3.Button
|
||||
import androidx.compose.material3.ButtonDefaults
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import moe.fuqiuluo.shamrock.ui.theme.ShamrockTheme
|
||||
import moe.fuqiuluo.shamrock.ui.theme.TabSelectedColor
|
||||
import moe.fuqiuluo.shamrock.ui.theme.TabUnSelectedColor
|
||||
|
||||
@Composable
|
||||
fun NoticeTextDialog(
|
||||
openDialog: MutableState<Boolean>,
|
||||
title: String,
|
||||
text: String
|
||||
) {
|
||||
AlertDialog(
|
||||
shape = RoundedCornerShape(12.dp),
|
||||
onDismissRequest = {
|
||||
openDialog.value = false
|
||||
},
|
||||
title = {
|
||||
Text(
|
||||
text = title,
|
||||
fontSize = 16.sp,
|
||||
color = TabSelectedColor
|
||||
)
|
||||
},
|
||||
text = {
|
||||
Text(
|
||||
text,
|
||||
fontSize = 14.sp,
|
||||
color = TabUnSelectedColor
|
||||
)
|
||||
},
|
||||
confirmButton = {
|
||||
Button(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth(),
|
||||
onClick = {
|
||||
openDialog.value = false
|
||||
},
|
||||
colors = ButtonDefaults.buttonColors(
|
||||
containerColor = Color(0xFF2196F3),
|
||||
contentColor = Color.White
|
||||
)
|
||||
) {
|
||||
Text("我知道了")
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun NoticeDialogPreView() {
|
||||
ShamrockTheme {
|
||||
NoticeTextDialog(openDialog = remember {
|
||||
mutableStateOf(true)
|
||||
}, "Notice", "Text")
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package moe.fuqiuluo.shamrock.ui.tools
|
||||
|
||||
operator fun <T> List<T>.get(range: IntRange): List<T> {
|
||||
return this.subList(range.first, range.last)
|
||||
}
|
||||
|
||||
val <T> List<T>.last: T
|
||||
get() = this.last()
|
|
@ -0,0 +1,47 @@
|
|||
package moe.fuqiuluo.shamrock.ui.tools
|
||||
|
||||
import androidx.compose.animation.animateColorAsState
|
||||
import androidx.compose.animation.core.animateFloatAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.gestures.detectTapGestures
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.derivedStateOf
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.geometry.CornerRadius
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.geometry.lerp
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.gestures.DraggableState
|
||||
import androidx.compose.foundation.gestures.Orientation
|
||||
import androidx.compose.foundation.gestures.detectTapGestures
|
||||
import androidx.compose.foundation.gestures.draggable
|
||||
import androidx.compose.foundation.gestures.rememberDraggableState
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.composed
|
||||
import androidx.compose.ui.graphics.drawscope.scale
|
||||
import androidx.compose.ui.graphics.lerp
|
||||
import androidx.compose.ui.input.pointer.pointerInput
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.dp
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlin.math.min
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
package moe.fuqiuluo.shamrock.ui.tools
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.compose.foundation.Indication
|
||||
import androidx.compose.foundation.IndicationInstance
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.InteractionSource
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.drawscope.ContentDrawScope
|
||||
import androidx.compose.ui.semantics.Role
|
||||
|
||||
@SuppressLint("ComposableModifierFactory")
|
||||
@Composable
|
||||
fun Modifier.noRippleClickable(
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
indication: Indication? = null,
|
||||
enabled: Boolean = true,
|
||||
onClickLabel: String? = null,
|
||||
role: Role? = null,
|
||||
onClick: () -> Unit,
|
||||
) = clickable(
|
||||
interactionSource = interactionSource,
|
||||
indication = indication,
|
||||
enabled = enabled,
|
||||
onClickLabel = onClickLabel,
|
||||
role = role,
|
||||
onClick = onClick,
|
||||
)
|
||||
|
||||
object NoIndication : Indication {
|
||||
private object NoIndicationInstance : IndicationInstance {
|
||||
override fun ContentDrawScope.drawIndication() {
|
||||
drawContent()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
override fun rememberUpdatedInstance(interactionSource: InteractionSource): IndicationInstance {
|
||||
return NoIndicationInstance
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,209 @@
|
|||
package moe.fuqiuluo.shamrock.ui.tools
|
||||
|
||||
import android.os.Build
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Card
|
||||
import androidx.compose.material3.CardDefaults
|
||||
import androidx.compose.material3.CardElevation
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.Stable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.drawBehind
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Paint
|
||||
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
|
||||
import androidx.compose.ui.res.colorResource
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
|
||||
/**
|
||||
* 绘制阴影范围
|
||||
* [top] 顶部范围
|
||||
* [start] 开始范围
|
||||
* [bottom] 底部范围
|
||||
* [end] 结束范围
|
||||
* Create empty Shadow elevation
|
||||
*/
|
||||
open class ShadowElevation(
|
||||
val top: Dp = 0.dp,
|
||||
val start: Dp = 0.dp,
|
||||
val bottom: Dp = 0.dp,
|
||||
val end: Dp = 0.dp
|
||||
){
|
||||
companion object : ShadowElevation()
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制阴影内侧间距
|
||||
* @param horizontal 横向
|
||||
* @param vertical 纵向
|
||||
* @return ShadowElevation
|
||||
*/
|
||||
@Stable
|
||||
fun ShadowElevation.padding(
|
||||
horizontal: Dp = 0.dp,
|
||||
vertical: Dp = 0.dp
|
||||
): ShadowElevation {
|
||||
return ShadowElevation(
|
||||
top = horizontal,
|
||||
start = vertical,
|
||||
bottom = horizontal,
|
||||
end = vertical,
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 绘制阴影所有范围
|
||||
* @param elevation 圆角
|
||||
* @return ShadowElevation
|
||||
*/
|
||||
fun ShadowElevation.all(
|
||||
elevation: Dp = 0.dp,
|
||||
): ShadowElevation {
|
||||
return ShadowElevation(
|
||||
top = elevation,
|
||||
start = elevation,
|
||||
bottom = elevation,
|
||||
end = elevation,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
|
||||
class ShadowShape(
|
||||
val topStart: Dp = 0.dp,
|
||||
val bottomStart: Dp = 0.dp,
|
||||
val topEnd: Dp = 0.dp,
|
||||
val bottomEnd: Dp = 0.dp
|
||||
)
|
||||
|
||||
fun ShadowShape.padding(
|
||||
topAll: Dp = 0.dp,
|
||||
bottomAll: Dp = 0.dp
|
||||
): ShadowShape {
|
||||
return ShadowShape(
|
||||
topStart = topAll,
|
||||
bottomStart = bottomAll,
|
||||
topEnd = topAll,
|
||||
bottomEnd = bottomAll,
|
||||
)
|
||||
}
|
||||
|
||||
fun ShadowShape.all(
|
||||
elevation: Dp = 0.dp,
|
||||
): ShadowShape {
|
||||
return ShadowShape(
|
||||
topStart = elevation,
|
||||
bottomStart = elevation,
|
||||
topEnd = elevation,
|
||||
bottomEnd = elevation,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 阴影Layout
|
||||
* 不支持圆角,在Modifier中设置圆角会导致无作用
|
||||
* 以子控件的大小为测试,请在子控件中设置padding及长宽
|
||||
* @param shadowColor [Color] 绘制阴影颜色
|
||||
* @param elevation [ShadowElevation] 绘制阴影范围
|
||||
* @param shape [Dp] 绘制圆角
|
||||
* @param offsetX [Dp] 偏移X轴
|
||||
* @param offsetY [Dp] 偏移Y轴
|
||||
* @param content (slot) 填充内容
|
||||
* @receiver
|
||||
*/
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@Composable
|
||||
fun ShadowLayout(
|
||||
modifier: Modifier = Modifier,
|
||||
shadowColor : Color = Color(0xffD3DBF9),
|
||||
elevation: ShadowElevation = ShadowElevation(),
|
||||
shape: Dp = 10.dp,
|
||||
offsetX: Dp = 0.dp,
|
||||
offsetY: Dp = 0.dp,
|
||||
alpha: Float = 0.5f,
|
||||
content: @Composable ColumnScope.() -> Unit,
|
||||
) {
|
||||
Card(
|
||||
modifier = modifier
|
||||
.padding(
|
||||
top = elevation.top,
|
||||
bottom = elevation.bottom,
|
||||
start = elevation.start,
|
||||
end = elevation.end
|
||||
)
|
||||
.drawColoredShadow(
|
||||
shadowColor,
|
||||
alpha,
|
||||
borderRadius = shape,
|
||||
shadowRadius = shape,
|
||||
offsetX = offsetX,
|
||||
offsetY = offsetY,
|
||||
roundedRect = true
|
||||
)
|
||||
.background(Color.Transparent)
|
||||
,
|
||||
elevation = CardDefaults.cardElevation(
|
||||
0.dp, 0.dp, 0.dp, 0.dp, 0.dp, 0.dp
|
||||
),
|
||||
shape = RoundedCornerShape(0.dp) ,
|
||||
content = content,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 绘制基础阴影
|
||||
* @param color 颜色
|
||||
* @param alpha 颜色透明度
|
||||
* @param borderRadius 阴影便捷圆角
|
||||
* @param shadowRadius 阴影圆角
|
||||
* @param offsetX 偏移X轴
|
||||
* @param offsetY 偏移Y轴
|
||||
* @param roundedRect 是否绘制圆角就行
|
||||
*/
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
fun Modifier.drawColoredShadow(
|
||||
color: Color,
|
||||
alpha: Float = 0.2f,
|
||||
borderRadius: Dp = 0.dp,
|
||||
shadowRadius: Dp = 0.dp,
|
||||
offsetX: Dp = 0.dp,
|
||||
offsetY: Dp = 0.dp,
|
||||
roundedRect: Boolean = true
|
||||
) = this.drawBehind {
|
||||
/**将颜色转换为Argb的Int类型*/
|
||||
val transparentColor = android.graphics.Color.toArgb(color.copy(alpha = .0f).value.toLong())
|
||||
val shadowColor = android.graphics.Color.toArgb(color.copy(alpha = alpha).value.toLong())
|
||||
/**调用Canvas绘制*/
|
||||
this.drawIntoCanvas {
|
||||
val paint = Paint()
|
||||
paint.color = Color.Transparent
|
||||
/**调用底层fragment Paint绘制*/
|
||||
val frameworkPaint = paint.asFrameworkPaint()
|
||||
frameworkPaint.color = transparentColor
|
||||
/**绘制阴影*/
|
||||
frameworkPaint.setShadowLayer(
|
||||
shadowRadius.toPx(),
|
||||
offsetX.toPx(),
|
||||
offsetY.toPx(),
|
||||
shadowColor
|
||||
)
|
||||
/**形状绘制*/
|
||||
it.drawRoundRect(
|
||||
0f,
|
||||
0f,
|
||||
this.size.width,
|
||||
this.size.height,
|
||||
if (roundedRect) this.size.height / 2 else borderRadius.toPx(),
|
||||
if (roundedRect) this.size.height / 2 else borderRadius.toPx(),
|
||||
paint
|
||||
)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
package moe.fuqiuluo.shamrock.ui.tools
|
||||
|
||||
import androidx.compose.animation.animateColorAsState
|
||||
import androidx.compose.animation.core.FastOutSlowInEasing
|
||||
import androidx.compose.animation.core.LinearOutSlowInEasing
|
||||
import androidx.compose.animation.core.animateDpAsState
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.defaultMinSize
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.shadow
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.onGloballyPositioned
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
@Composable
|
||||
fun IosSwitch(
|
||||
modifier: Modifier = Modifier,
|
||||
buttonHeight: Dp = 40.dp,
|
||||
innerPadding: Dp = 3.5.dp,
|
||||
shape: RoundedCornerShape = RoundedCornerShape(45.dp),
|
||||
switchValue: Boolean,
|
||||
positiveColor: Color = Color(0xFF35C759),
|
||||
negativeColor: Color = Color(0xFFE9E9EA),
|
||||
onValueChanged: (Boolean) -> Unit,
|
||||
) {
|
||||
var width by remember { (mutableStateOf(0.dp)) }
|
||||
|
||||
val interactionSource = remember {
|
||||
MutableInteractionSource()
|
||||
}
|
||||
|
||||
var switchClicked by remember {
|
||||
mutableStateOf(switchValue)
|
||||
}
|
||||
|
||||
var padding by remember {
|
||||
mutableStateOf(0.dp)
|
||||
}
|
||||
|
||||
padding = if (!switchClicked) 0.dp else width - (width / 2)
|
||||
|
||||
val animateSize by animateDpAsState(
|
||||
targetValue = if (!switchClicked) 0.dp else padding,
|
||||
tween(
|
||||
durationMillis = 300,
|
||||
delayMillis = 0,
|
||||
easing = LinearOutSlowInEasing
|
||||
), label = "DpAnimation"
|
||||
)
|
||||
|
||||
val animateBgColor by animateColorAsState(
|
||||
targetValue = if (switchClicked) positiveColor else negativeColor,
|
||||
tween(
|
||||
durationMillis = 300,
|
||||
delayMillis = 0,
|
||||
easing = FastOutSlowInEasing
|
||||
), label = "ColorAnimation"
|
||||
)
|
||||
|
||||
val localDensity = LocalDensity.current
|
||||
Box(
|
||||
modifier = modifier
|
||||
.defaultMinSize(
|
||||
minWidth = buttonHeight * 2,
|
||||
minHeight = buttonHeight
|
||||
)
|
||||
.onGloballyPositioned {
|
||||
width = with(localDensity) {
|
||||
it.size.width.toDp()
|
||||
}
|
||||
}
|
||||
.height(buttonHeight)
|
||||
.clip(shape = shape)
|
||||
.background(animateBgColor)
|
||||
.clickable(
|
||||
interactionSource = interactionSource,
|
||||
indication = null
|
||||
) {
|
||||
switchClicked = !switchClicked
|
||||
onValueChanged(switchClicked)
|
||||
}
|
||||
) {
|
||||
Row {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.width(animateSize)
|
||||
.background(Color.Transparent)
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(buttonHeight)
|
||||
.padding(innerPadding)
|
||||
.shadow(elevation = 5.dp, shape)
|
||||
.clip(shape = shape)
|
||||
.background(Color.White)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun SwitchPreView() {
|
||||
IosSwitch(
|
||||
switchValue = false,
|
||||
buttonHeight = 22.dp,
|
||||
onValueChanged = {
|
||||
|
||||
}
|
||||
)
|
||||
}
|
|
@ -0,0 +1,392 @@
|
|||
package moe.fuqiuluo.shamrock.ui.tools
|
||||
|
||||
import androidx.compose.animation.animateColor
|
||||
import androidx.compose.animation.core.LinearEasing
|
||||
import androidx.compose.animation.core.tween
|
||||
import androidx.compose.animation.core.updateTransition
|
||||
import androidx.compose.foundation.Indication
|
||||
import androidx.compose.foundation.interaction.MutableInteractionSource
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.selection.selectable
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.ripple.rememberRipple
|
||||
import androidx.compose.material3.LocalContentColor
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.ProvideTextStyle
|
||||
import androidx.compose.material3.Typography
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.FirstBaseline
|
||||
import androidx.compose.ui.layout.LastBaseline
|
||||
import androidx.compose.ui.layout.Layout
|
||||
import androidx.compose.ui.layout.Placeable
|
||||
import androidx.compose.ui.layout.layoutId
|
||||
import androidx.compose.ui.semantics.Role
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.unit.Density
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import kotlin.math.max
|
||||
|
||||
private enum class ColorSchemeKeyTokens {
|
||||
Background,
|
||||
Error,
|
||||
ErrorContainer,
|
||||
InverseOnSurface,
|
||||
InversePrimary,
|
||||
InverseSurface,
|
||||
OnBackground,
|
||||
OnError,
|
||||
OnErrorContainer,
|
||||
OnPrimary,
|
||||
OnPrimaryContainer,
|
||||
OnSecondary,
|
||||
OnSecondaryContainer,
|
||||
OnSurface,
|
||||
OnSurfaceVariant,
|
||||
OnTertiary,
|
||||
OnTertiaryContainer,
|
||||
Outline,
|
||||
OutlineVariant,
|
||||
Primary,
|
||||
PrimaryContainer,
|
||||
Scrim,
|
||||
Secondary,
|
||||
SecondaryContainer,
|
||||
Surface,
|
||||
SurfaceTint,
|
||||
SurfaceVariant,
|
||||
Tertiary,
|
||||
TertiaryContainer,
|
||||
}
|
||||
|
||||
private enum class ShapeKeyTokens {
|
||||
CornerExtraLarge,
|
||||
CornerExtraLargeTop,
|
||||
CornerExtraSmall,
|
||||
CornerExtraSmallTop,
|
||||
CornerFull,
|
||||
CornerLarge,
|
||||
CornerLargeEnd,
|
||||
CornerLargeTop,
|
||||
CornerMedium,
|
||||
CornerNone,
|
||||
CornerSmall,
|
||||
}
|
||||
|
||||
private enum class TypographyKeyTokens {
|
||||
BodyLarge,
|
||||
BodyMedium,
|
||||
BodySmall,
|
||||
DisplayLarge,
|
||||
DisplayMedium,
|
||||
DisplaySmall,
|
||||
HeadlineLarge,
|
||||
HeadlineMedium,
|
||||
HeadlineSmall,
|
||||
LabelLarge,
|
||||
LabelMedium,
|
||||
LabelSmall,
|
||||
TitleLarge,
|
||||
TitleMedium,
|
||||
TitleSmall,
|
||||
}
|
||||
|
||||
private object ElevationTokens {
|
||||
val Level0 = 0.0.dp
|
||||
val Level1 = 1.0.dp
|
||||
val Level2 = 3.0.dp
|
||||
val Level3 = 6.0.dp
|
||||
val Level4 = 8.0.dp
|
||||
val Level5 = 12.0.dp
|
||||
}
|
||||
|
||||
private object PrimaryNavigationTabTokens {
|
||||
val ActiveIndicatorColor = ColorSchemeKeyTokens.Primary
|
||||
val ActiveIndicatorHeight = 3.0.dp
|
||||
val ActiveIndicatorShape = RoundedCornerShape(3.0.dp)
|
||||
val ContainerColor = ColorSchemeKeyTokens.Surface
|
||||
val ContainerElevation = ElevationTokens.Level0
|
||||
val ContainerHeight = 48.0.dp
|
||||
val ContainerShape = ShapeKeyTokens.CornerNone
|
||||
val DividerColor = ColorSchemeKeyTokens.SurfaceVariant
|
||||
val DividerHeight = 1.0.dp
|
||||
val ActiveFocusIconColor = ColorSchemeKeyTokens.Primary
|
||||
val ActiveHoverIconColor = ColorSchemeKeyTokens.Primary
|
||||
val ActiveIconColor = ColorSchemeKeyTokens.Primary
|
||||
val ActivePressedIconColor = ColorSchemeKeyTokens.Primary
|
||||
val IconAndLabelTextContainerHeight = 64.0.dp
|
||||
val IconSize = 24.0.dp
|
||||
val InactiveFocusIconColor = ColorSchemeKeyTokens.OnSurface
|
||||
val InactiveHoverIconColor = ColorSchemeKeyTokens.OnSurface
|
||||
val InactiveIconColor = ColorSchemeKeyTokens.OnSurfaceVariant
|
||||
val InactivePressedIconColor = ColorSchemeKeyTokens.OnSurface
|
||||
val ActiveFocusLabelTextColor = ColorSchemeKeyTokens.Primary
|
||||
val ActiveHoverLabelTextColor = ColorSchemeKeyTokens.Primary
|
||||
val ActiveLabelTextColor = ColorSchemeKeyTokens.Primary
|
||||
val ActivePressedLabelTextColor = ColorSchemeKeyTokens.Primary
|
||||
val InactiveFocusLabelTextColor = ColorSchemeKeyTokens.OnSurface
|
||||
val InactiveHoverLabelTextColor = ColorSchemeKeyTokens.OnSurface
|
||||
val InactiveLabelTextColor = ColorSchemeKeyTokens.OnSurfaceVariant
|
||||
val InactivePressedLabelTextColor = ColorSchemeKeyTokens.OnSurface
|
||||
val LabelTextFont = TypographyKeyTokens.TitleSmall
|
||||
}
|
||||
|
||||
private val HorizontalTextPadding = 16.dp
|
||||
private val LargeTabHeight = 72.dp
|
||||
private val SmallTabHeight = PrimaryNavigationTabTokens.ContainerHeight
|
||||
private val IconDistanceFromBaseline = 20.sp
|
||||
// Distance from the top of the indicator to the text baseline when there is one line of text and an
|
||||
// icon
|
||||
private val SingleLineTextBaselineWithIcon = 14.dp
|
||||
// Distance from the top of the indicator to the last text baseline when there are two lines of text
|
||||
// and an icon
|
||||
private val DoubleLineTextBaselineWithIcon = 6.dp
|
||||
|
||||
@Composable
|
||||
private fun TabBaselineLayout(
|
||||
text: @Composable (() -> Unit)?,
|
||||
icon: @Composable (() -> Unit)?
|
||||
) {
|
||||
Layout(
|
||||
{
|
||||
if (text != null) {
|
||||
Box(
|
||||
Modifier
|
||||
.layoutId("text")
|
||||
.padding(horizontal = HorizontalTextPadding)
|
||||
) { text() }
|
||||
}
|
||||
if (icon != null) {
|
||||
Box(Modifier.layoutId("icon")) { icon() }
|
||||
}
|
||||
}
|
||||
) { measurables, constraints ->
|
||||
val textPlaceable = text?.let {
|
||||
measurables.first { it.layoutId == "text" }.measure(
|
||||
// Measure with loose constraints for height as we don't want the text to take up more
|
||||
// space than it needs
|
||||
constraints.copy(minHeight = 0)
|
||||
)
|
||||
}
|
||||
|
||||
val iconPlaceable = icon?.let {
|
||||
measurables.first { it.layoutId == "icon" }.measure(constraints)
|
||||
}
|
||||
|
||||
val tabWidth = max(textPlaceable?.width ?: 0, iconPlaceable?.width ?: 0)
|
||||
|
||||
val specHeight = if (textPlaceable != null && iconPlaceable != null) {
|
||||
LargeTabHeight
|
||||
} else {
|
||||
SmallTabHeight
|
||||
}.roundToPx()
|
||||
|
||||
val tabHeight = max(
|
||||
specHeight,
|
||||
(iconPlaceable?.height ?: 0) + (textPlaceable?.height ?: 0) +
|
||||
IconDistanceFromBaseline.roundToPx()
|
||||
)
|
||||
|
||||
val firstBaseline = textPlaceable?.get(FirstBaseline)
|
||||
val lastBaseline = textPlaceable?.get(LastBaseline)
|
||||
|
||||
layout(tabWidth, tabHeight) {
|
||||
when {
|
||||
textPlaceable != null && iconPlaceable != null -> placeTextAndIcon(
|
||||
density = this@Layout,
|
||||
textPlaceable = textPlaceable,
|
||||
iconPlaceable = iconPlaceable,
|
||||
tabWidth = tabWidth,
|
||||
tabHeight = tabHeight,
|
||||
firstBaseline = firstBaseline!!,
|
||||
lastBaseline = lastBaseline!!
|
||||
)
|
||||
textPlaceable != null -> placeTextOrIcon(textPlaceable, tabHeight)
|
||||
iconPlaceable != null -> placeTextOrIcon(iconPlaceable, tabHeight)
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun Placeable.PlacementScope.placeTextOrIcon(
|
||||
textOrIconPlaceable: Placeable,
|
||||
tabHeight: Int
|
||||
) {
|
||||
val contentY = (tabHeight - textOrIconPlaceable.height) / 2
|
||||
textOrIconPlaceable.placeRelative(0, contentY)
|
||||
}
|
||||
|
||||
private fun Placeable.PlacementScope.placeTextAndIcon(
|
||||
density: Density,
|
||||
textPlaceable: Placeable,
|
||||
iconPlaceable: Placeable,
|
||||
tabWidth: Int,
|
||||
tabHeight: Int,
|
||||
firstBaseline: Int,
|
||||
lastBaseline: Int
|
||||
) {
|
||||
val baselineOffset = if (firstBaseline == lastBaseline) {
|
||||
SingleLineTextBaselineWithIcon
|
||||
} else {
|
||||
DoubleLineTextBaselineWithIcon
|
||||
}
|
||||
|
||||
// Total offset between the last text baseline and the bottom of the Tab layout
|
||||
val textOffset = with(density) {
|
||||
baselineOffset.roundToPx() + PrimaryNavigationTabTokens.ActiveIndicatorHeight.roundToPx()
|
||||
}
|
||||
|
||||
// How much space there is between the top of the icon (essentially the top of this layout)
|
||||
// and the top of the text layout's bounding box (not baseline)
|
||||
val iconOffset = with(density) {
|
||||
iconPlaceable.height + IconDistanceFromBaseline.roundToPx() - firstBaseline
|
||||
}
|
||||
|
||||
val textPlaceableX = (tabWidth - textPlaceable.width) / 2
|
||||
val textPlaceableY = tabHeight - lastBaseline - textOffset
|
||||
textPlaceable.placeRelative(textPlaceableX, textPlaceableY)
|
||||
|
||||
val iconPlaceableX = (tabWidth - iconPlaceable.width) / 2
|
||||
val iconPlaceableY = textPlaceableY - iconOffset
|
||||
iconPlaceable.placeRelative(iconPlaceableX, iconPlaceableY)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ShamrockTab(
|
||||
selected: Boolean,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
text: @Composable (() -> Unit)? = null,
|
||||
icon: @Composable (() -> Unit)? = null,
|
||||
selectedContentColor: Color = LocalContentColor.current,
|
||||
unselectedContentColor: Color = selectedContentColor,
|
||||
indication: Indication? = rememberRipple(bounded = true, color = selectedContentColor),
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() }
|
||||
) {
|
||||
val styledText: @Composable (() -> Unit)? = text?.let {
|
||||
@Composable {
|
||||
val style =
|
||||
MaterialTheme.typography.fromToken(PrimaryNavigationTabTokens.LabelTextFont)
|
||||
.copy(textAlign = TextAlign.Center)
|
||||
ProvideTextStyle(style, content = text)
|
||||
}
|
||||
}
|
||||
ShamrockTab(
|
||||
selected,
|
||||
onClick,
|
||||
modifier,
|
||||
enabled,
|
||||
selectedContentColor,
|
||||
unselectedContentColor,
|
||||
interactionSource,
|
||||
indication
|
||||
) {
|
||||
TabBaselineLayout(icon = icon, text = styledText)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ShamrockTab(
|
||||
selected: Boolean,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
enabled: Boolean = true,
|
||||
selectedContentColor: Color = LocalContentColor.current,
|
||||
unselectedContentColor: Color = selectedContentColor,
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
indication: Indication? = rememberRipple(bounded = true, color = selectedContentColor),
|
||||
content: @Composable ColumnScope.() -> Unit
|
||||
) {
|
||||
// The color of the Ripple should always the selected color, as we want to show the color
|
||||
// before the item is considered selected, and hence before the new contentColor is
|
||||
// provided by TabTransition.
|
||||
|
||||
TabTransition(selectedContentColor, unselectedContentColor, selected) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.selectable(
|
||||
selected = selected,
|
||||
onClick = onClick,
|
||||
enabled = enabled,
|
||||
role = Role.Tab,
|
||||
interactionSource = interactionSource,
|
||||
indication = indication
|
||||
)
|
||||
.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private const val TabFadeInAnimationDuration = 150
|
||||
private const val TabFadeInAnimationDelay = 100
|
||||
private const val TabFadeOutAnimationDuration = 100
|
||||
|
||||
@Composable
|
||||
private fun TabTransition(
|
||||
activeColor: Color,
|
||||
inactiveColor: Color,
|
||||
selected: Boolean,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
val transition = updateTransition(selected, label = "")
|
||||
val color by transition.animateColor(
|
||||
transitionSpec = {
|
||||
if (false isTransitioningTo true) {
|
||||
tween(
|
||||
durationMillis = TabFadeInAnimationDuration,
|
||||
delayMillis = TabFadeInAnimationDelay,
|
||||
easing = LinearEasing
|
||||
)
|
||||
} else {
|
||||
tween(
|
||||
durationMillis = TabFadeOutAnimationDuration,
|
||||
easing = LinearEasing
|
||||
)
|
||||
}
|
||||
}, label = ""
|
||||
) {
|
||||
if (it) activeColor else inactiveColor
|
||||
}
|
||||
CompositionLocalProvider(
|
||||
LocalContentColor provides color,
|
||||
content = content
|
||||
)
|
||||
}
|
||||
|
||||
private fun Typography.fromToken(value: TypographyKeyTokens): TextStyle {
|
||||
return when (value) {
|
||||
TypographyKeyTokens.DisplayLarge -> displayLarge
|
||||
TypographyKeyTokens.DisplayMedium -> displayMedium
|
||||
TypographyKeyTokens.DisplaySmall -> displaySmall
|
||||
TypographyKeyTokens.HeadlineLarge -> headlineLarge
|
||||
TypographyKeyTokens.HeadlineMedium -> headlineMedium
|
||||
TypographyKeyTokens.HeadlineSmall -> headlineSmall
|
||||
TypographyKeyTokens.TitleLarge -> titleLarge
|
||||
TypographyKeyTokens.TitleMedium -> titleMedium
|
||||
TypographyKeyTokens.TitleSmall -> titleSmall
|
||||
TypographyKeyTokens.BodyLarge -> bodyLarge
|
||||
TypographyKeyTokens.BodyMedium -> bodyMedium
|
||||
TypographyKeyTokens.BodySmall -> bodySmall
|
||||
TypographyKeyTokens.LabelLarge -> labelLarge
|
||||
TypographyKeyTokens.LabelMedium -> labelMedium
|
||||
TypographyKeyTokens.LabelSmall -> labelSmall
|
||||
}
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
<vector android:height="24dp" android:tint="#6C6C6C"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M19,3H5C3.89,3 3,3.9 3,5v14c0,1.1 0.89,2 2,2h14c1.1,0 2,-0.9 2,-2V5C21,3.9 20.11,3 19,3zM19,19H5V7h14V19zM13.5,13c0,0.83 -0.67,1.5 -1.5,1.5s-1.5,-0.67 -1.5,-1.5c0,-0.83 0.67,-1.5 1.5,-1.5S13.5,12.17 13.5,13zM12,9c-2.73,0 -5.06,1.66 -6,4c0.94,2.34 3.27,4 6,4s5.06,-1.66 6,-4C17.06,10.66 14.73,9 12,9zM12,15.5c-1.38,0 -2.5,-1.12 -2.5,-2.5c0,-1.38 1.12,-2.5 2.5,-2.5c1.38,0 2.5,1.12 2.5,2.5C14.5,14.38 13.38,15.5 12,15.5z"/>
|
||||
</vector>
|
After Width: | Height: | Size: 14 KiB |
|
@ -0,0 +1,170 @@
|
|||
<?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" />
|
||||
</vector>
|
|
@ -0,0 +1,30 @@
|
|||
<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>
|
After Width: | Height: | Size: 34 KiB |
|
@ -0,0 +1,5 @@
|
|||
<vector android:height="24dp" android:tint="#6F6F6F"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M13,13L13,13c-0.56,0.56 -1.45,0.56 -2,0.01L11,13c-0.55,-0.55 -0.55,-1.44 0,-1.99L11,11c0.55,-0.55 1.44,-0.55 1.99,0L13,11C13.55,11.55 13.55,12.45 13,13zM12,6l2.12,2.12l2.5,-2.5l-3.2,-3.2c-0.78,-0.78 -2.05,-0.78 -2.83,0l-3.2,3.2l2.5,2.5L12,6zM6,12l2.12,-2.12l-2.5,-2.5l-3.2,3.2c-0.78,0.78 -0.78,2.05 0,2.83l3.2,3.2l2.5,-2.5L6,12zM18,12l-2.12,2.12l2.5,2.5l3.2,-3.2c0.78,-0.78 0.78,-2.05 0,-2.83l-3.2,-3.2l-2.5,2.5L18,12zM12,18l-2.12,-2.12l-2.5,2.5l3.2,3.2c0.78,0.78 2.05,0.78 2.83,0l3.2,-3.2l-2.5,-2.5L12,18z"/>
|
||||
</vector>
|
|
@ -0,0 +1,5 @@
|
|||
<vector android:height="24dp" android:tint="#6F6F6F"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M4,13h6c0.55,0 1,-0.45 1,-1L11,4c0,-0.55 -0.45,-1 -1,-1L4,3c-0.55,0 -1,0.45 -1,1v8c0,0.55 0.45,1 1,1zM4,21h6c0.55,0 1,-0.45 1,-1v-4c0,-0.55 -0.45,-1 -1,-1L4,15c-0.55,0 -1,0.45 -1,1v4c0,0.55 0.45,1 1,1zM14,21h6c0.55,0 1,-0.45 1,-1v-8c0,-0.55 -0.45,-1 -1,-1h-6c-0.55,0 -1,0.45 -1,1v8c0,0.55 0.45,1 1,1zM13,4v4c0,0.55 0.45,1 1,1h6c0.55,0 1,-0.45 1,-1L21,4c0,-0.55 -0.45,-1 -1,-1h-6c-0.55,0 -1,0.45 -1,1z"/>
|
||||
</vector>
|
|
@ -0,0 +1,5 @@
|
|||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M9,16.2l-3.5,-3.5c-0.39,-0.39 -1.01,-0.39 -1.4,0 -0.39,0.39 -0.39,1.01 0,1.4l4.19,4.19c0.39,0.39 1.02,0.39 1.41,0L20.3,7.7c0.39,-0.39 0.39,-1.01 0,-1.4 -0.39,-0.39 -1.01,-0.39 -1.4,0L9,16.2z"/>
|
||||
</vector>
|
|
@ -0,0 +1,5 @@
|
|||
<vector android:height="24dp" android:tint="#6F6F6F"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M10,19v-5h4v5c0,0.55 0.45,1 1,1h3c0.55,0 1,-0.45 1,-1v-7h1.7c0.46,0 0.68,-0.57 0.33,-0.87L12.67,3.6c-0.38,-0.34 -0.96,-0.34 -1.34,0l-8.36,7.53c-0.34,0.3 -0.13,0.87 0.33,0.87H5v7c0,0.55 0.45,1 1,1h3c0.55,0 1,-0.45 1,-1z"/>
|
||||
</vector>
|
|
@ -0,0 +1,5 @@
|
|||
<vector android:height="24dp" android:tint="#6C6C6C"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,17c-0.55,0 -1,-0.45 -1,-1v-4c0,-0.55 0.45,-1 1,-1s1,0.45 1,1v4c0,0.55 -0.45,1 -1,1zM13,9h-2L11,7h2v2z"/>
|
||||
</vector>
|
|
@ -0,0 +1,6 @@
|
|||
<vector android:height="24dp" android:tint="#6F6F6F"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M19,3H5C3.9,3 3,3.9 3,5v14c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2V5C21,3.9 20.1,3 19,3zM7.68,14.98H6V9h1.71c1.28,0 1.71,1.03 1.71,1.71l0,2.56C9.42,13.95 9,14.98 7.68,14.98zM12.38,11.46v1.07h-1.18v1.39h1.93v1.07h-2.25c-0.4,0.01 -0.74,-0.31 -0.75,-0.71V9.75c-0.01,-0.4 0.31,-0.74 0.71,-0.75h2.28l0,1.07h-1.92v1.39H12.38zM16.88,14.23c-0.48,1.11 -1.33,0.89 -1.71,0L13.77,9h1.18l1.07,4.11L17.09,9h1.18L16.88,14.23z"/>
|
||||
<path android:fillColor="@android:color/white" android:pathData="M7.77,10.12H7.14v3.77h0.63c0.14,0 0.28,-0.05 0.42,-0.16c0.14,-0.1 0.21,-0.26 0.21,-0.47v-2.52c0,-0.21 -0.07,-0.37 -0.21,-0.47C8.05,10.17 7.91,10.12 7.77,10.12z"/>
|
||||
</vector>
|
|
@ -0,0 +1,6 @@
|
|||
<vector android:height="24dp" android:tint="#6F6F6F"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M15.11,12.45L14,10.24l-3.11,6.21C10.73,16.79 10.38,17 10,17s-0.73,-0.21 -0.89,-0.55L7.38,13H2v5c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2v-5h-6C15.62,13 15.27,12.79 15.11,12.45z"/>
|
||||
<path android:fillColor="@android:color/white" android:pathData="M20,4H4C2.9,4 2,4.9 2,6v5h6c0.38,0 0.73,0.21 0.89,0.55L10,13.76l3.11,-6.21c0.34,-0.68 1.45,-0.68 1.79,0L16.62,11H22V6C22,4.9 21.1,4 20,4z"/>
|
||||
</vector>
|
|
@ -0,0 +1,5 @@
|
|||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M18.75,3.94L4.07,10.08c-0.83,0.35 -0.81,1.53 0.02,1.85L9.43,14c0.26,0.1 0.47,0.31 0.57,0.57l2.06,5.33c0.32,0.84 1.51,0.86 1.86,0.03l6.15,-14.67c0.33,-0.83 -0.5,-1.66 -1.32,-1.32z"/>
|
||||
</vector>
|
|
@ -0,0 +1,5 @@
|
|||
<vector android:height="24dp" android:tint="#FFFFFF"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M12,6.34l6.95,-2.58c0.8,-0.3 1.58,0.48 1.29,1.29L17.66,12L12,6.34zM21.9,19.07L4.93,2.1c-0.39,-0.39 -1.02,-0.39 -1.41,0c-0.39,0.39 -0.39,1.02 0,1.41l4.36,4.36l-4.2,1.56C3.27,9.59 3,9.97 3,10.4c0,0.42 0.26,0.8 0.65,0.96l6.42,2.57l2.57,6.42C12.8,20.74 13.18,21 13.6,21c0.43,0 0.82,-0.27 0.97,-0.67l1.56,-4.2l4.36,4.36c0.39,0.39 1.02,0.39 1.41,0C22.29,20.09 22.29,19.46 21.9,19.07z"/>
|
||||
</vector>
|
|
@ -0,0 +1,5 @@
|
|||
<vector android:height="24dp" android:tint="#6C6C6C"
|
||||
android:viewportHeight="24" android:viewportWidth="24"
|
||||
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<path android:fillColor="@android:color/white" android:pathData="M4.47,21h15.06c1.54,0 2.5,-1.67 1.73,-3L13.73,4.99c-0.77,-1.33 -2.69,-1.33 -3.46,0L2.74,18c-0.77,1.33 0.19,3 1.73,3zM12,14c-0.55,0 -1,-0.45 -1,-1v-2c0,-0.55 0.45,-1 1,-1s1,0.45 1,1v2c0,0.55 -0.45,1 -1,1zM13,18h-2v-2h2v2z"/>
|
||||
</vector>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
|
@ -0,0 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<background android:drawable="@drawable/ic_launcher_background" />
|
||||
<foreground android:drawable="@drawable/ic_launcher_foreground" />
|
||||
<monochrome android:drawable="@drawable/ic_launcher_foreground" />
|
||||
</adaptive-icon>
|
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 982 B |
After Width: | Height: | Size: 1.7 KiB |
After Width: | Height: | Size: 1.9 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 2.8 KiB |
After Width: | Height: | Size: 5.8 KiB |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 7.6 KiB |
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="purple_200">#FFBB86FC</color>
|
||||
<color name="purple_500">#FF6200EE</color>
|
||||
<color name="purple_700">#FF3700B3</color>
|
||||
<color name="teal_200">#FF03DAC5</color>
|
||||
<color name="teal_700">#FF018786</color>
|
||||
<color name="black">#FF000000</color>
|
||||
<color name="white">#FFFFFFFF</color>
|
||||
</resources>
|
|
@ -0,0 +1,3 @@
|
|||
<resources>
|
||||
<string name="app_name">Shamrock</string>
|
||||
</resources>
|
|
@ -0,0 +1,5 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
<style name="Theme.Shamrock" parent="android:Theme.Material.Light.NoActionBar" />
|
||||
</resources>
|
|
@ -0,0 +1,13 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Sample backup rules file; uncomment and customize as necessary.
|
||||
See https://developer.android.com/guide/topics/data/autobackup
|
||||
for details.
|
||||
Note: This file is ignored for devices older that API 31
|
||||
See https://developer.android.com/about/versions/12/backup-restore
|
||||
-->
|
||||
<full-backup-content>
|
||||
<!--
|
||||
<include domain="sharedpref" path="."/>
|
||||
<exclude domain="sharedpref" path="device.xml"/>
|
||||
-->
|
||||
</full-backup-content>
|
|
@ -0,0 +1,19 @@
|
|||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Sample data extraction rules file; uncomment and customize as necessary.
|
||||
See https://developer.android.com/about/versions/12/backup-restore#xml-changes
|
||||
for details.
|
||||
-->
|
||||
<data-extraction-rules>
|
||||
<cloud-backup>
|
||||
<!-- TODO: Use <include> and <exclude> to control what is backed up.
|
||||
<include .../>
|
||||
<exclude .../>
|
||||
-->
|
||||
</cloud-backup>
|
||||
<!--
|
||||
<device-transfer>
|
||||
<include .../>
|
||||
<exclude .../>
|
||||
</device-transfer>
|
||||
-->
|
||||
</data-extraction-rules>
|
|
@ -0,0 +1,17 @@
|
|||
package moe.fuqiuluo.shamrock
|
||||
|
||||
import org.junit.Test
|
||||
|
||||
import org.junit.Assert.*
|
||||
|
||||
/**
|
||||
* Example local unit test, which will execute on the development machine (host).
|
||||
*
|
||||
* See [testing documentation](http://d.android.com/tools/testing).
|
||||
*/
|
||||
class ExampleUnitTest {
|
||||
@Test
|
||||
fun addition_isCorrect() {
|
||||
assertEquals(4, 2 + 2)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
plugins {
|
||||
id("com.android.application") version "8.1.0" apply false
|
||||
id("org.jetbrains.kotlin.android") version "1.8.10" apply false
|
||||
id("org.jetbrains.kotlin.plugin.serialization") version "1.8.10" apply false
|
||||
id("com.android.library") version "8.1.0" apply false
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
# Project-wide Gradle settings.
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
# AndroidX package structure to make it clearer which packages are bundled with the
|
||||
# Android operating system, and which are packaged with your app's APK
|
||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||
android.useAndroidX=true
|
||||
# Kotlin code style for this project: "official" or "obsolete":
|
||||
kotlin.code.style=official
|
||||
# Enables namespacing of each library's R class so that its R class includes only the
|
||||
# resources declared in the library itself and none from the library's dependencies,
|
||||
# thereby reducing the size of the R class for that library
|
||||
android.nonTransitiveRClass=true
|
|
@ -0,0 +1,6 @@
|
|||
#Thu Jul 27 20:53:51 CST 2023
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.0-bin.zip
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
|
@ -0,0 +1,185 @@
|
|||
#!/usr/bin/env sh
|
||||
|
||||
#
|
||||
# Copyright 2015 the original author or authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# https://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
##############################################################################
|
||||
##
|
||||
## Gradle start up script for UN*X
|
||||
##
|
||||
##############################################################################
|
||||
|
||||
# Attempt to set APP_HOME
|
||||
# Resolve links: $0 may be a link
|
||||
PRG="$0"
|
||||
# Need this for relative symlinks.
|
||||
while [ -h "$PRG" ] ; do
|
||||
ls=`ls -ld "$PRG"`
|
||||
link=`expr "$ls" : '.*-> \(.*\)$'`
|
||||
if expr "$link" : '/.*' > /dev/null; then
|
||||
PRG="$link"
|
||||
else
|
||||
PRG=`dirname "$PRG"`"/$link"
|
||||
fi
|
||||
done
|
||||
SAVED="`pwd`"
|
||||
cd "`dirname \"$PRG\"`/" >/dev/null
|
||||
APP_HOME="`pwd -P`"
|
||||
cd "$SAVED" >/dev/null
|
||||
|
||||
APP_NAME="Gradle"
|
||||
APP_BASE_NAME=`basename "$0"`
|
||||
|
||||
# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
|
||||
|
||||
# Use the maximum available, or set MAX_FD != -1 to use that value.
|
||||
MAX_FD="maximum"
|
||||
|
||||
warn () {
|
||||
echo "$*"
|
||||
}
|
||||
|
||||
die () {
|
||||
echo
|
||||
echo "$*"
|
||||
echo
|
||||
exit 1
|
||||
}
|
||||
|
||||
# OS specific support (must be 'true' or 'false').
|
||||
cygwin=false
|
||||
msys=false
|
||||
darwin=false
|
||||
nonstop=false
|
||||
case "`uname`" in
|
||||
CYGWIN* )
|
||||
cygwin=true
|
||||
;;
|
||||
Darwin* )
|
||||
darwin=true
|
||||
;;
|
||||
MINGW* )
|
||||
msys=true
|
||||
;;
|
||||
NONSTOP* )
|
||||
nonstop=true
|
||||
;;
|
||||
esac
|
||||
|
||||
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
|
||||
|
||||
|
||||
# Determine the Java command to use to start the JVM.
|
||||
if [ -n "$JAVA_HOME" ] ; then
|
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
|
||||
# IBM's JDK on AIX uses strange locations for the executables
|
||||
JAVACMD="$JAVA_HOME/jre/sh/java"
|
||||
else
|
||||
JAVACMD="$JAVA_HOME/bin/java"
|
||||
fi
|
||||
if [ ! -x "$JAVACMD" ] ; then
|
||||
die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
else
|
||||
JAVACMD="java"
|
||||
which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
|
||||
Please set the JAVA_HOME variable in your environment to match the
|
||||
location of your Java installation."
|
||||
fi
|
||||
|
||||
# Increase the maximum file descriptors if we can.
|
||||
if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then
|
||||
MAX_FD_LIMIT=`ulimit -H -n`
|
||||
if [ $? -eq 0 ] ; then
|
||||
if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
|
||||
MAX_FD="$MAX_FD_LIMIT"
|
||||
fi
|
||||
ulimit -n $MAX_FD
|
||||
if [ $? -ne 0 ] ; then
|
||||
warn "Could not set maximum file descriptor limit: $MAX_FD"
|
||||
fi
|
||||
else
|
||||
warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
|
||||
fi
|
||||
fi
|
||||
|
||||
# For Darwin, add options to specify how the application appears in the dock
|
||||
if $darwin; then
|
||||
GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
|
||||
fi
|
||||
|
||||
# For Cygwin or MSYS, switch paths to Windows format before running java
|
||||
if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then
|
||||
APP_HOME=`cygpath --path --mixed "$APP_HOME"`
|
||||
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
|
||||
|
||||
JAVACMD=`cygpath --unix "$JAVACMD"`
|
||||
|
||||
# We build the pattern for arguments to be converted via cygpath
|
||||
ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
|
||||
SEP=""
|
||||
for dir in $ROOTDIRSRAW ; do
|
||||
ROOTDIRS="$ROOTDIRS$SEP$dir"
|
||||
SEP="|"
|
||||
done
|
||||
OURCYGPATTERN="(^($ROOTDIRS))"
|
||||
# Add a user-defined pattern to the cygpath arguments
|
||||
if [ "$GRADLE_CYGPATTERN" != "" ] ; then
|
||||
OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
|
||||
fi
|
||||
# Now convert the arguments - kludge to limit ourselves to /bin/sh
|
||||
i=0
|
||||
for arg in "$@" ; do
|
||||
CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
|
||||
CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
|
||||
|
||||
if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
|
||||
eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
|
||||
else
|
||||
eval `echo args$i`="\"$arg\""
|
||||
fi
|
||||
i=`expr $i + 1`
|
||||
done
|
||||
case $i in
|
||||
0) set -- ;;
|
||||
1) set -- "$args0" ;;
|
||||
2) set -- "$args0" "$args1" ;;
|
||||
3) set -- "$args0" "$args1" "$args2" ;;
|
||||
4) set -- "$args0" "$args1" "$args2" "$args3" ;;
|
||||
5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
|
||||
6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
|
||||
7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
|
||||
8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
|
||||
9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
# Escape application args
|
||||
save () {
|
||||
for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done
|
||||
echo " "
|
||||
}
|
||||
APP_ARGS=`save "$@"`
|
||||
|
||||
# Collect all arguments for the java command, following the shell quoting and substitution rules
|
||||
eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS"
|
||||
|
||||
exec "$JAVACMD" "$@"
|
|
@ -0,0 +1,89 @@
|
|||
@rem
|
||||
@rem Copyright 2015 the original author or authors.
|
||||
@rem
|
||||
@rem Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@rem you may not use this file except in compliance with the License.
|
||||
@rem You may obtain a copy of the License at
|
||||
@rem
|
||||
@rem https://www.apache.org/licenses/LICENSE-2.0
|
||||
@rem
|
||||
@rem Unless required by applicable law or agreed to in writing, software
|
||||
@rem distributed under the License is distributed on an "AS IS" BASIS,
|
||||
@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
@rem See the License for the specific language governing permissions and
|
||||
@rem limitations under the License.
|
||||
@rem
|
||||
|
||||
@if "%DEBUG%" == "" @echo off
|
||||
@rem ##########################################################################
|
||||
@rem
|
||||
@rem Gradle startup script for Windows
|
||||
@rem
|
||||
@rem ##########################################################################
|
||||
|
||||
@rem Set local scope for the variables with windows NT shell
|
||||
if "%OS%"=="Windows_NT" setlocal
|
||||
|
||||
set DIRNAME=%~dp0
|
||||
if "%DIRNAME%" == "" set DIRNAME=.
|
||||
set APP_BASE_NAME=%~n0
|
||||
set APP_HOME=%DIRNAME%
|
||||
|
||||
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
|
||||
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
|
||||
|
||||
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
|
||||
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
|
||||
|
||||
@rem Find java.exe
|
||||
if defined JAVA_HOME goto findJavaFromJavaHome
|
||||
|
||||
set JAVA_EXE=java.exe
|
||||
%JAVA_EXE% -version >NUL 2>&1
|
||||
if "%ERRORLEVEL%" == "0" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:findJavaFromJavaHome
|
||||
set JAVA_HOME=%JAVA_HOME:"=%
|
||||
set JAVA_EXE=%JAVA_HOME%/bin/java.exe
|
||||
|
||||
if exist "%JAVA_EXE%" goto execute
|
||||
|
||||
echo.
|
||||
echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
|
||||
echo.
|
||||
echo Please set the JAVA_HOME variable in your environment to match the
|
||||
echo location of your Java installation.
|
||||
|
||||
goto fail
|
||||
|
||||
:execute
|
||||
@rem Setup the command line
|
||||
|
||||
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
|
||||
|
||||
|
||||
@rem Execute Gradle
|
||||
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
|
||||
|
||||
:end
|
||||
@rem End local scope for the variables with windows NT shell
|
||||
if "%ERRORLEVEL%"=="0" goto mainEnd
|
||||
|
||||
:fail
|
||||
rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
|
||||
rem the _cmd.exe /c_ return code!
|
||||
if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
|
||||
exit /b 1
|
||||
|
||||
:mainEnd
|
||||
if "%OS%"=="Windows_NT" endlocal
|
||||
|
||||
:omega
|
|
@ -0,0 +1 @@
|
|||
/build
|
|
@ -0,0 +1,42 @@
|
|||
plugins {
|
||||
id("com.android.library")
|
||||
id("org.jetbrains.kotlin.android")
|
||||
}
|
||||
|
||||
android {
|
||||
namespace = "moe.fuqiuluo.qqinterface"
|
||||
compileSdk = 33
|
||||
|
||||
defaultConfig {
|
||||
minSdk = 24
|
||||
|
||||
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
|
||||
consumerProguardFiles("consumer-rules.pro")
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
release {
|
||||
isMinifyEnabled = false
|
||||
proguardFiles(
|
||||
getDefaultProguardFile("proguard-android-optimize.txt"),
|
||||
"proguard-rules.pro"
|
||||
)
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_1_8
|
||||
targetCompatibility = JavaVersion.VERSION_1_8
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("androidx.core:core-ktx:1.9.0")
|
||||
implementation("androidx.appcompat:appcompat:1.6.1")
|
||||
implementation("com.google.android.material:material:1.9.0")
|
||||
testImplementation("junit:junit:4.13.2")
|
||||
androidTestImplementation("androidx.test.ext:junit:1.1.5")
|
||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
|
||||
}
|
|
@ -0,0 +1,21 @@
|
|||
# Add project specific ProGuard rules here.
|
||||
# You can control the set of applied configuration files using the
|
||||
# proguardFiles setting in build.gradle.
|
||||
#
|
||||
# For more details, see
|
||||
# http://developer.android.com/guide/developing/tools/proguard.html
|
||||
|
||||
# If your project uses WebView with JS, uncomment the following
|
||||
# and specify the fully qualified class name to the JavaScript interface
|
||||
# class:
|
||||
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
|
||||
# public *;
|
||||
#}
|
||||
|
||||
# Uncomment this to preserve the line number information for
|
||||
# debugging stack traces.
|
||||
#-keepattributes SourceFile,LineNumberTable
|
||||
|
||||
# If you keep the line number information, uncomment this to
|
||||
# hide the original source file name.
|
||||
#-renamesourcefileattribute SourceFile
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
</manifest>
|
|
@ -0,0 +1,9 @@
|
|||
package com.tencent.common.app;
|
||||
|
||||
import mqq.app.AppRuntime;
|
||||
|
||||
public abstract class AppInterface extends AppRuntime {
|
||||
public String getCurrentNickname() {
|
||||
return "";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package com.tencent.common.app.business;
|
||||
|
||||
import com.tencent.common.app.AppInterface;
|
||||
|
||||
public abstract class BaseQQAppInterface extends AppInterface {
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package com.tencent.common.config.pad;
|
||||
|
||||
public enum DeviceType {
|
||||
PHONE,
|
||||
TABLET,
|
||||
FOLD
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
package com.tencent.image;
|
||||
|
||||
public class URLDrawableHandler {
|
||||
}
|
|
@ -0,0 +1,76 @@
|
|||
package com.tencent.mobileqq.activity.photo;
|
||||
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public class PhotoSendParams implements Parcelable, Serializable {
|
||||
public static final Creator<PhotoSendParams> CREATOR = new Creator<PhotoSendParams>() {
|
||||
|
||||
@Override
|
||||
public PhotoSendParams createFromParcel(Parcel parcel) {
|
||||
return new PhotoSendParams(parcel);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PhotoSendParams[] newArray(int i) {
|
||||
return new PhotoSendParams[i];
|
||||
}
|
||||
};
|
||||
public static final int SEND_PIC_NORMAL = 0;
|
||||
public static final int SEND_PIC_QZONE = 1;
|
||||
public long fileSize;
|
||||
public int picType;
|
||||
public String rawDownloadUrl;
|
||||
public int rawHeight;
|
||||
public String rawMd5;
|
||||
public String rawPicPath;
|
||||
public int rawWidth;
|
||||
public String thumbPath;
|
||||
|
||||
public PhotoSendParams(String str, String str2, String str3, long j2, int i2, int i3, String str4, int i4) {
|
||||
this.picType = 0;
|
||||
this.thumbPath = str;
|
||||
this.rawMd5 = str2;
|
||||
this.rawPicPath = str3;
|
||||
this.fileSize = j2;
|
||||
this.rawHeight = i2;
|
||||
this.rawWidth = i3;
|
||||
this.rawDownloadUrl = str4;
|
||||
this.picType = i4;
|
||||
}
|
||||
|
||||
@Override // android.os.Parcelable
|
||||
public int describeContents() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "PhotoSendParams:&thumbPath:" + this.thumbPath + " &rawMd5:" + this.rawMd5 + " &rawPicPath:" + this.rawPicPath + " &rawHeight:" + this.rawHeight + " &rawWidth:" + this.rawWidth + " &rawDownloadUrl:" + this.rawDownloadUrl + " &picType:" + this.picType;
|
||||
}
|
||||
|
||||
@Override // android.os.Parcelable
|
||||
public void writeToParcel(Parcel parcel, int i2) {
|
||||
parcel.writeString(this.thumbPath);
|
||||
parcel.writeString(this.rawMd5);
|
||||
parcel.writeString(this.rawPicPath);
|
||||
parcel.writeLong(this.fileSize);
|
||||
parcel.writeInt(this.rawHeight);
|
||||
parcel.writeInt(this.rawWidth);
|
||||
parcel.writeString(this.rawDownloadUrl);
|
||||
parcel.writeInt(this.picType);
|
||||
}
|
||||
|
||||
public PhotoSendParams(Parcel parcel) {
|
||||
this.picType = 0;
|
||||
this.thumbPath = parcel.readString();
|
||||
this.rawMd5 = parcel.readString();
|
||||
this.rawPicPath = parcel.readString();
|
||||
this.fileSize = parcel.readLong();
|
||||
this.rawHeight = parcel.readInt();
|
||||
this.rawWidth = parcel.readInt();
|
||||
this.rawDownloadUrl = parcel.readString();
|
||||
this.picType = parcel.readInt();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
package com.tencent.mobileqq.app;
|
||||
|
||||
import com.tencent.common.app.business.BaseQQAppInterface;
|
||||
|
||||
public class QQAppInterface extends BaseQQAppInterface {
|
||||
@Override
|
||||
public String getCurrentAccountUin() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void execute(Runnable runnable) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCurrentNickname() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public void syncOnlineFriend() {
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
package com.tencent.mobileqq.data;
|
||||
|
||||
public class MessageRecord {
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
package com.tencent.mobileqq.openapi;
|
||||
|
||||
import android.content.ContentProvider;
|
||||
import android.content.ContentValues;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
|
||||
public class OpenApiProvider extends ContentProvider {
|
||||
@Override
|
||||
public boolean onCreate() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor query(Uri uri, String[] strings, String s, String[] strings1, String s1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getType(Uri uri) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Uri insert(Uri uri, ContentValues contentValues) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int delete(Uri uri, String s, String[] strings) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int update(Uri uri, ContentValues contentValues, String s, String[] strings) {
|
||||
return 0;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
package com.tencent.mobileqq.qfix;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
public class ApplicationDelegate extends Application {
|
||||
}
|
|
@ -0,0 +1,29 @@
|
|||
package com.tencent.mobileqq.transfile;
|
||||
|
||||
import com.tencent.mobileqq.transfile.api.ITransFileController;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import mqq.app.AppRuntime;
|
||||
|
||||
public class BaseTransFileController implements ITransFileController {
|
||||
@Override
|
||||
public AtomicBoolean isWorking() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public synchronized boolean transferAsync(TransferRequest transferRequest) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onCreate(AppRuntime appRuntime) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroy() {
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
package com.tencent.mobileqq.transfile;
|
||||
|
||||
public class GroupPicUploadProcessor {
|
||||
}
|
|
@ -0,0 +1,208 @@
|
|||
package com.tencent.mobileqq.transfile;
|
||||
|
||||
import com.tencent.image.URLDrawableHandler;
|
||||
import com.tencent.mobileqq.activity.photo.PhotoSendParams;
|
||||
import com.tencent.mobileqq.data.MessageRecord;
|
||||
|
||||
import java.io.OutputStream;
|
||||
|
||||
public class TransferRequest {
|
||||
public int chatType;
|
||||
public int delayShowProgressTimeInMs;
|
||||
public Object extraObject;
|
||||
public boolean isShareImageByServer;
|
||||
public int mBusiType;
|
||||
public boolean mCanSendMsg;
|
||||
public int mCommandId;
|
||||
public String mDisplayOutFilePath;
|
||||
//public c mDownCallBack;
|
||||
public int mDownMode;
|
||||
public byte[] mExtentionInfo;
|
||||
public Object mExtraObj;
|
||||
public long mFastForwardFileSize;
|
||||
public int mFastForwardHeight;
|
||||
public int mFastForwardWidth;
|
||||
public int mFileType;
|
||||
public long mGroupFileID;
|
||||
public String mGroupFileKeyStr;
|
||||
public boolean mIsPresend;
|
||||
public boolean mIsPttPreSend;
|
||||
public boolean mIsSecSnapChatPic;
|
||||
public boolean mIsUp;
|
||||
public String mLocalPath;
|
||||
public String mMd5;
|
||||
public long mMsgTime;
|
||||
public OutputStream mOut;
|
||||
public String mOutFilePath;
|
||||
public String mPeerUin;
|
||||
public int mPicSendSource;
|
||||
public int mPttUploadPanel;
|
||||
public boolean mReqVideoSubtitle;
|
||||
public int mRequestDisplayLength;
|
||||
public int mRequestLength;
|
||||
public int mRequestOffset;
|
||||
public TransferResult mResult;
|
||||
public long mSecMsgId;
|
||||
public String mSecondId;
|
||||
public String mSelfUin;
|
||||
public String mServerPath;
|
||||
public int mSourceVideoCodecFormat;
|
||||
public long mSubMsgId;
|
||||
public int mTargetVideoCodecFormat;
|
||||
public String mThumbMd5;
|
||||
public String mThumbPath;
|
||||
public int mUinType;
|
||||
public long mUniseq;
|
||||
public int multiMsgType;
|
||||
public int pcmForVadNum;
|
||||
public String pcmForVadPath;
|
||||
public int pcmForVadPos;
|
||||
public PhotoSendParams photoSendParams;
|
||||
public String resIdStr;
|
||||
public byte[] toSendData;
|
||||
public int upMsgBusiType;
|
||||
//public WXVadSeg vadSeg;
|
||||
public String mRichTag = "";
|
||||
public boolean isJubaoMsgType = false;
|
||||
public boolean mIsSelfSend = false;
|
||||
public boolean useOutputstream = true;
|
||||
public boolean mSupportRangeBreakDown = false;
|
||||
public int mDbRecVersion = 5;
|
||||
public boolean needSendMsg = true;
|
||||
public int mPrioty = 1;
|
||||
public MessageRecord mRec = null;
|
||||
public boolean mNeedReport = true;
|
||||
public boolean mIsOnlyGetUrl = false;
|
||||
public boolean mIsFastForward = false;
|
||||
public boolean myPresendInvalid = false;
|
||||
public boolean mPttCompressFinish = false;
|
||||
public boolean bEnableEnc = false;
|
||||
public boolean isQzonePic = false;
|
||||
private String mKey = null;
|
||||
|
||||
/* compiled from: P */
|
||||
/* loaded from: classes18.dex */
|
||||
public static class AppInfo {
|
||||
public String packName;
|
||||
public String sourceIconBig;
|
||||
public String sourceIconSmall;
|
||||
public String sourceName;
|
||||
public String sourceUrl;
|
||||
public int status;
|
||||
}
|
||||
|
||||
/* compiled from: P */
|
||||
/* loaded from: classes18.dex */
|
||||
public static class AppShare {
|
||||
public long mAppShareID;
|
||||
public String mShareUrl;
|
||||
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("mAppShareID:" + this.mAppShareID);
|
||||
sb.append(",mShareUrl:" + this.mShareUrl);
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/* compiled from: P */
|
||||
/* loaded from: classes18.dex */
|
||||
public static class PicDownExtraInfo {
|
||||
public URLDrawableHandler mHandler;
|
||||
public int mStartDownOffset;
|
||||
public String mUrlFromMsg;
|
||||
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("mUrlFromMsg:" + this.mUrlFromMsg);
|
||||
sb.append(",mStartDownOffset:" + this.mStartDownOffset);
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/* compiled from: P */
|
||||
/* loaded from: classes18.dex */
|
||||
public static class PicUpExtraInfo {
|
||||
public boolean mIsRaw;
|
||||
public boolean mIsShareAppPic;
|
||||
public AppShare mShareAppInfo;
|
||||
public int mUinType;
|
||||
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("mUinType:" + this.mUinType);
|
||||
sb.append(",mIsRaw:" + this.mIsRaw);
|
||||
sb.append(",mIsShareAppPic:" + this.mIsShareAppPic);
|
||||
sb.append(",mShareAppInfo:{" + this.mShareAppInfo + "}");
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
/* compiled from: P */
|
||||
/* loaded from: classes18.dex */
|
||||
public static class PttDownExtraInfo {
|
||||
public int mFromType;
|
||||
public int mLayer;
|
||||
|
||||
public PttDownExtraInfo(int i2, int i3) {
|
||||
this.mFromType = i2;
|
||||
this.mLayer = i3;
|
||||
}
|
||||
}
|
||||
|
||||
/* compiled from: P */
|
||||
/* loaded from: classes18.dex */
|
||||
public static class ShareExtraInfo {
|
||||
public long appId;
|
||||
public AppInfo appInfo;
|
||||
public String audioUrl;
|
||||
public boolean enableServerSendMsg;
|
||||
public int forwardType;
|
||||
public String imageUrl;
|
||||
public int imageUrlStatus;
|
||||
public String pkgName;
|
||||
public int serviceType;
|
||||
public int shortUrlStatus;
|
||||
public String summary;
|
||||
public String targetUrl;
|
||||
public String title;
|
||||
}
|
||||
|
||||
public String getKey() {
|
||||
String str = this.mKey;
|
||||
if (str == null) {
|
||||
return this.mPeerUin + "_" + this.mFileType + "_" + this.mUniseq + "_" + this.mSubMsgId;
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
public String getKeyForTransfer() {
|
||||
return this.mPeerUin + this.mUniseq;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder("TransferRequest\n");
|
||||
sb.append("mUniseq=" + this.mUniseq);
|
||||
sb.append(",mMd5=" + this.mMd5);
|
||||
sb.append(",mIsIp=" + this.mIsUp);
|
||||
sb.append(",mUinType=" + this.mUinType);
|
||||
sb.append(",mFileType=" + this.mFileType);
|
||||
sb.append(",mSelfUin=" + this.mSelfUin);
|
||||
sb.append(",mPeerUin=" + this.mPeerUin);
|
||||
sb.append(",mSecondId=" + this.mSecondId);
|
||||
sb.append(",mServerPath=" + this.mServerPath);
|
||||
sb.append(",mLocalPath=" + this.mLocalPath);
|
||||
sb.append(",mBusiType=" + this.mBusiType);
|
||||
sb.append(",mGroupFileID=" + this.mGroupFileID);
|
||||
sb.append(",mExtraObj={" + this.mExtraObj + "}");
|
||||
StringBuilder sb2 = new StringBuilder();
|
||||
sb2.append(",mPrioty=");
|
||||
sb2.append(this.mPrioty);
|
||||
sb.append(sb2.toString());
|
||||
//sb.append(",mLogicCallBack=" + this.mUpCallBack);
|
||||
sb.append(",bEnableEnc=" + this.bEnableEnc);
|
||||
sb.append(",isQzonePic=" + this.isQzonePic);
|
||||
sb.append(",pcmForVadPath=" + this.pcmForVadPath);
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,11 @@
|
|||
package com.tencent.mobileqq.transfile;
|
||||
|
||||
public class TransferResult {
|
||||
public static final int RESULT_FAIL = -1;
|
||||
public static final int RESULT_NOT_SET = -2;
|
||||
public static final int RESULT_OK = 0;
|
||||
public long mErrCode;
|
||||
public String mErrDesc;
|
||||
public TransferRequest mOrigReq;
|
||||
public int mResult = -2;
|
||||
}
|
|
@ -0,0 +1,37 @@
|
|||
package com.tencent.mobileqq.transfile.api;
|
||||
|
||||
import com.tencent.mobileqq.transfile.TransferRequest;
|
||||
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import mqq.app.api.IRuntimeService;
|
||||
|
||||
public interface ITransFileController extends IRuntimeService {
|
||||
// void addHandle(TransProcessorHandler transProcessorHandler);
|
||||
|
||||
//@Deprecated
|
||||
//void addProcessor(String str, IHttpCommunicatorListener iHttpCommunicatorListener);
|
||||
|
||||
//boolean containsProcessor(String str, long j2);
|
||||
|
||||
//IHttpCommunicatorListener findProcessor(String str);
|
||||
|
||||
//IHttpCommunicatorListener findProcessor(String str, long j2);
|
||||
|
||||
//TransFileControllerBusHelper getBusHelper();
|
||||
|
||||
//ConcurrentHashMap<String, IHttpCommunicatorListener> getProcessMap();
|
||||
|
||||
AtomicBoolean isWorking();
|
||||
|
||||
// void removeHandle(TransProcessorHandler transProcessorHandler);
|
||||
|
||||
// boolean removeProcessor(String str);
|
||||
|
||||
// void removeProcessorByPeerUin(String str, int i2);
|
||||
|
||||
// void stop(TransferRequest transferRequest);
|
||||
|
||||
boolean transferAsync(TransferRequest transferRequest);
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package com.tencent.mobileqq.transfile.api.impl;
|
||||
|
||||
import com.tencent.mobileqq.transfile.BaseTransFileController;
|
||||
import com.tencent.mobileqq.transfile.api.ITransFileController;
|
||||
|
||||
public class TransFileControllerImpl extends BaseTransFileController implements ITransFileController {
|
||||
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
package com.tencent.mobileqq.transfile.protohandler;
|
||||
|
||||
public class GroupPicUpHandler {
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
package com.tencent.qphone.base.remote;
|
||||
|
||||
import java.util.HashMap;
|
||||
|
||||
public class SimpleAccount {
|
||||
public static final String _ISLOGINED = "_isLogined";
|
||||
public static final String _LOGINPROCESS = "_loginedProcess";
|
||||
public static final String _LOGINTIME = "_loginTime";
|
||||
public static final String _UIN = "_uin";
|
||||
|
||||
public boolean containsKey(String str) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public boolean equals(Object obj) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public String getAttribute(String str, String str2) {
|
||||
return str2;
|
||||
}
|
||||
|
||||
public HashMap<String, String> getAttributes() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getLoginProcess() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public String getUin() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public boolean isLogined() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public String removeAttribute(String str) {
|
||||
return "";
|
||||
}
|
||||
|
||||
public void setAttribute(String str, String str2) {
|
||||
throw new RuntimeException("key found space ");
|
||||
}
|
||||
|
||||
public void setLoginProcess(String str) {
|
||||
}
|
||||
|
||||
public void setUin(String str) {
|
||||
}
|
||||
|
||||
public String toStoreString() {
|
||||
return "";
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "";
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package com.tencent.qphone.base.util;
|
||||
|
||||
import com.tencent.mobileqq.qfix.ApplicationDelegate;
|
||||
|
||||
public abstract class BaseApplication extends ApplicationDelegate {
|
||||
public static BaseApplication getContext() {
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
public abstract int getAppId();
|
||||
|
||||
public abstract int getNTCoreVersion();
|
||||
|
||||
public abstract String getQua();
|
||||
}
|
|
@ -0,0 +1,7 @@
|
|||
package com.tencent.qqnt.kernel.api;
|
||||
|
||||
import mqq.app.api.IRuntimeService;
|
||||
|
||||
public interface IKernelService extends IRuntimeService {
|
||||
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package com.tencent.qqnt.kernel.api.impl;
|
||||
|
||||
import com.tencent.qqnt.kernel.nativeinterface.IKernelMsgListener;
|
||||
|
||||
public class MsgService {
|
||||
public void addMsgListener(IKernelMsgListener listener) {
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package com.tencent.qqnt.kernel.nativeinterface;
|
||||
|
||||
/* loaded from: classes2.dex */
|
||||
public enum ATTYPE {
|
||||
UNKNOWN_AT_TYPE,
|
||||
AT_EXPLICIT_USER,
|
||||
AT_ROLE_GROUP,
|
||||
AT_GUILD
|
||||
}
|
|
@ -0,0 +1,52 @@
|
|||
package com.tencent.qqnt.kernel.nativeinterface;
|
||||
|
||||
public final class AVRecordElement {
|
||||
Integer extraType;
|
||||
boolean hasRead;
|
||||
int mainType;
|
||||
String text;
|
||||
long time;
|
||||
int type;
|
||||
|
||||
public AVRecordElement() {
|
||||
this.text = "";
|
||||
}
|
||||
|
||||
public Integer getExtraType() {
|
||||
return this.extraType;
|
||||
}
|
||||
|
||||
public boolean getHasRead() {
|
||||
return this.hasRead;
|
||||
}
|
||||
|
||||
public int getMainType() {
|
||||
return this.mainType;
|
||||
}
|
||||
|
||||
public String getText() {
|
||||
return this.text;
|
||||
}
|
||||
|
||||
public long getTime() {
|
||||
return this.time;
|
||||
}
|
||||
|
||||
public int getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "AVRecordElement{type=" + this.type + ",time=" + this.time + ",text=" + this.text + ",mainType=" + this.mainType + ",hasRead=" + this.hasRead + ",extraType=" + this.extraType + ",}";
|
||||
}
|
||||
|
||||
public AVRecordElement(int i2, long j2, String str, int i3, boolean z, Integer num) {
|
||||
this.text = "";
|
||||
this.type = i2;
|
||||
this.time = j2;
|
||||
this.text = str;
|
||||
this.mainType = i3;
|
||||
this.hasRead = z;
|
||||
this.extraType = num;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,41 @@
|
|||
package com.tencent.qqnt.kernel.nativeinterface;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public final class AioOperateGrayTipElement implements Serializable {
|
||||
String fromGrpCodeOfTmpChat;
|
||||
int operateType;
|
||||
String peerUid;
|
||||
long serialVersionUID;
|
||||
|
||||
public AioOperateGrayTipElement() {
|
||||
this.serialVersionUID = 1L;
|
||||
this.peerUid = "";
|
||||
this.fromGrpCodeOfTmpChat = "";
|
||||
}
|
||||
|
||||
public String getFromGrpCodeOfTmpChat() {
|
||||
return this.fromGrpCodeOfTmpChat;
|
||||
}
|
||||
|
||||
public int getOperateType() {
|
||||
return this.operateType;
|
||||
}
|
||||
|
||||
public String getPeerUid() {
|
||||
return this.peerUid;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "AioOperateGrayTipElement{operateType=" + this.operateType + ",peerUid=" + this.peerUid + ",fromGrpCodeOfTmpChat=" + this.fromGrpCodeOfTmpChat + ",}";
|
||||
}
|
||||
|
||||
public AioOperateGrayTipElement(int i2, String str, String str2) {
|
||||
this.serialVersionUID = 1L;
|
||||
this.peerUid = "";
|
||||
this.fromGrpCodeOfTmpChat = "";
|
||||
this.operateType = i2;
|
||||
this.peerUid = str;
|
||||
this.fromGrpCodeOfTmpChat = str2;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
package com.tencent.qqnt.kernel.nativeinterface;
|
||||
|
||||
public final class AnonymousExtInfo {
|
||||
int anonymousFlag;
|
||||
byte[] anonymousId;
|
||||
String anonymousNick;
|
||||
long bubbleId;
|
||||
long expireTime;
|
||||
long headPicIndex;
|
||||
String rankColor;
|
||||
|
||||
public AnonymousExtInfo() {
|
||||
this.anonymousId = new byte[0];
|
||||
this.anonymousNick = "";
|
||||
this.rankColor = "";
|
||||
}
|
||||
|
||||
public int getAnonymousFlag() {
|
||||
return this.anonymousFlag;
|
||||
}
|
||||
|
||||
public byte[] getAnonymousId() {
|
||||
return this.anonymousId;
|
||||
}
|
||||
|
||||
public String getAnonymousNick() {
|
||||
return this.anonymousNick;
|
||||
}
|
||||
|
||||
public long getBubbleId() {
|
||||
return this.bubbleId;
|
||||
}
|
||||
|
||||
public long getExpireTime() {
|
||||
return this.expireTime;
|
||||
}
|
||||
|
||||
public long getHeadPicIndex() {
|
||||
return this.headPicIndex;
|
||||
}
|
||||
|
||||
public String getRankColor() {
|
||||
return this.rankColor;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "AnonymousExtInfo{anonymousFlag=" + this.anonymousFlag + ",anonymousId=" + this.anonymousId + ",anonymousNick=" + this.anonymousNick + ",headPicIndex=" + this.headPicIndex + ",expireTime=" + this.expireTime + ",bubbleId=" + this.bubbleId + ",rankColor=" + this.rankColor + ",}";
|
||||
}
|
||||
|
||||
public AnonymousExtInfo(int i2, byte[] bArr, String str, long j2, long j3, long j4, String str2) {
|
||||
this.anonymousId = new byte[0];
|
||||
this.anonymousNick = "";
|
||||
this.rankColor = "";
|
||||
this.anonymousFlag = i2;
|
||||
this.anonymousId = bArr;
|
||||
this.anonymousNick = str;
|
||||
this.headPicIndex = j2;
|
||||
this.expireTime = j3;
|
||||
this.bubbleId = j4;
|
||||
this.rankColor = str2;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
package com.tencent.qqnt.kernel.nativeinterface;
|
||||
|
||||
public final class ArkElement {
|
||||
String bytesData;
|
||||
LinkInfo linkInfo;
|
||||
Integer subElementType;
|
||||
|
||||
public ArkElement() {
|
||||
this.bytesData = "";
|
||||
}
|
||||
|
||||
public String getBytesData() {
|
||||
return this.bytesData;
|
||||
}
|
||||
|
||||
public LinkInfo getLinkInfo() {
|
||||
return this.linkInfo;
|
||||
}
|
||||
|
||||
public Integer getSubElementType() {
|
||||
return this.subElementType;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "ArkElement{bytesData=" + this.bytesData + ",linkInfo=" + this.linkInfo + ",subElementType=" + this.subElementType + ",}";
|
||||
}
|
||||
|
||||
public ArkElement(String str, LinkInfo linkInfo, Integer num) {
|
||||
this.bytesData = "";
|
||||
this.bytesData = str;
|
||||
this.linkInfo = linkInfo;
|
||||
this.subElementType = num;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
package com.tencent.qqnt.kernel.nativeinterface;
|
||||
|
||||
/* loaded from: classes2.dex */
|
||||
public final class BasicPointInfo {
|
||||
byte[] headUrl;
|
||||
long readSeqNo;
|
||||
long seqNo;
|
||||
|
||||
public BasicPointInfo() {
|
||||
this.headUrl = new byte[0];
|
||||
}
|
||||
|
||||
public byte[] getHeadUrl() {
|
||||
return this.headUrl;
|
||||
}
|
||||
|
||||
public long getReadSeqNo() {
|
||||
return this.readSeqNo;
|
||||
}
|
||||
|
||||
public long getSeqNo() {
|
||||
return this.seqNo;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "BasicPointInfo{headUrl=" + this.headUrl + ",seqNo=" + this.seqNo + ",readSeqNo=" + this.readSeqNo + ",}";
|
||||
}
|
||||
|
||||
public BasicPointInfo(byte[] bArr, long j2, long j3) {
|
||||
this.headUrl = new byte[0];
|
||||
this.headUrl = bArr;
|
||||
this.seqNo = j2;
|
||||
this.readSeqNo = j3;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,9 @@
|
|||
package com.tencent.qqnt.kernel.nativeinterface;
|
||||
|
||||
/* compiled from: P */
|
||||
/* loaded from: classes2.dex */
|
||||
public enum BeatTypeEnum {
|
||||
KRECOVERY,
|
||||
KFOLDNOTSPREAD,
|
||||
KFOLDCANSPREAD
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
package com.tencent.qqnt.kernel.nativeinterface;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
public final class BlockGrayTipElement implements Serializable {
|
||||
boolean block;
|
||||
boolean isBuddy;
|
||||
String peerUid;
|
||||
long serialVersionUID;
|
||||
|
||||
public BlockGrayTipElement() {
|
||||
this.serialVersionUID = 1L;
|
||||
this.peerUid = "";
|
||||
}
|
||||
|
||||
public boolean getBlock() {
|
||||
return this.block;
|
||||
}
|
||||
|
||||
public boolean getIsBuddy() {
|
||||
return this.isBuddy;
|
||||
}
|
||||
|
||||
public String getPeerUid() {
|
||||
return this.peerUid;
|
||||
}
|
||||
|
||||
public String toString() {
|
||||
return "BlockGrayTipElement{peerUid=" + this.peerUid + ",block=" + this.block + ",isBuddy=" + this.isBuddy + ",}";
|
||||
}
|
||||
|
||||
public BlockGrayTipElement(String str, boolean z, boolean z2) {
|
||||
this.serialVersionUID = 1L;
|
||||
this.peerUid = "";
|
||||
this.peerUid = str;
|
||||
this.block = z;
|
||||
this.isBuddy = z2;
|
||||
}
|
||||
}
|