Browse Source

博士安全App提交

bjb 2 weeks ago
commit
eccbe15231
59 changed files with 1717 additions and 0 deletions
  1. 15 0
      .gitignore
  2. 3 0
      .idea/.gitignore
  3. 6 0
      .idea/AndroidProjectSystem.xml
  4. 6 0
      .idea/compiler.xml
  5. 10 0
      .idea/deploymentTargetSelector.xml
  6. 19 0
      .idea/gradle.xml
  7. 61 0
      .idea/inspectionProfiles/Project_Default.xml
  8. 10 0
      .idea/migrations.xml
  9. 9 0
      .idea/misc.xml
  10. 17 0
      .idea/runConfigurations.xml
  11. 1 0
      app/.gitignore
  12. 71 0
      app/build.gradle.kts
  13. 21 0
      app/proguard-rules.pro
  14. 24 0
      app/src/androidTest/java/com/iscs/bozzys/ExampleInstrumentedTest.kt
  15. 36 0
      app/src/main/AndroidManifest.xml
  16. 30 0
      app/src/main/java/com/iscs/bozzys/Entry.kt
  17. 15 0
      app/src/main/java/com/iscs/bozzys/api/ApiBean.kt
  18. 1 0
      app/src/main/java/com/iscs/bozzys/api/ApiModel.kt
  19. 85 0
      app/src/main/java/com/iscs/bozzys/api/ApiRequest.kt
  20. 21 0
      app/src/main/java/com/iscs/bozzys/api/ApiService.kt
  21. 129 0
      app/src/main/java/com/iscs/bozzys/ui/base/PageBase.kt
  22. 15 0
      app/src/main/java/com/iscs/bozzys/ui/base/VMBase.kt
  23. 42 0
      app/src/main/java/com/iscs/bozzys/ui/pages/PageHome.kt
  24. 90 0
      app/src/main/java/com/iscs/bozzys/ui/pages/PageSplash.kt
  25. 30 0
      app/src/main/java/com/iscs/bozzys/ui/pages/vm/VMHome.kt
  26. 11 0
      app/src/main/java/com/iscs/bozzys/ui/theme/Color.kt
  27. 49 0
      app/src/main/java/com/iscs/bozzys/ui/theme/Theme.kt
  28. 34 0
      app/src/main/java/com/iscs/bozzys/ui/theme/Type.kt
  29. 35 0
      app/src/main/java/com/iscs/bozzys/utils/Storage.kt
  30. 73 0
      app/src/main/java/com/iscs/bozzys/utils/network/Request.kt
  31. 170 0
      app/src/main/res/drawable/ic_launcher_background.xml
  32. 30 0
      app/src/main/res/drawable/ic_launcher_foreground.xml
  33. 6 0
      app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml
  34. 6 0
      app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml
  35. BIN
      app/src/main/res/mipmap-hdpi/ic_launcher.webp
  36. BIN
      app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
  37. BIN
      app/src/main/res/mipmap-mdpi/ic_launcher.webp
  38. BIN
      app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
  39. BIN
      app/src/main/res/mipmap-xhdpi/ic_launcher.webp
  40. BIN
      app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
  41. BIN
      app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
  42. BIN
      app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
  43. BIN
      app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
  44. BIN
      app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
  45. 4 0
      app/src/main/res/values-zh-rCN/strings.xml
  46. 10 0
      app/src/main/res/values/colors.xml
  47. 3 0
      app/src/main/res/values/strings.xml
  48. 14 0
      app/src/main/res/values/themes.xml
  49. 13 0
      app/src/main/res/xml/backup_rules.xml
  50. 19 0
      app/src/main/res/xml/data_extraction_rules.xml
  51. 17 0
      app/src/test/java/com/iscs/bozzys/ExampleUnitTest.kt
  52. 6 0
      build.gradle.kts
  53. 23 0
      gradle.properties
  54. 46 0
      gradle/libs.versions.toml
  55. BIN
      gradle/wrapper/gradle-wrapper.jar
  56. 8 0
      gradle/wrapper/gradle-wrapper.properties
  57. 251 0
      gradlew
  58. 94 0
      gradlew.bat
  59. 28 0
      settings.gradle.kts

+ 15 - 0
.gitignore

@@ -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

+ 3 - 0
.idea/.gitignore

@@ -0,0 +1,3 @@
+# Default ignored files
+/shelf/
+/workspace.xml

+ 6 - 0
.idea/AndroidProjectSystem.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="AndroidProjectSystem">
+    <option name="providerId" value="com.android.tools.idea.GradleProjectSystem" />
+  </component>
+</project>

+ 6 - 0
.idea/compiler.xml

@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="CompilerConfiguration">
+    <bytecodeTargetLevel target="21" />
+  </component>
+</project>

+ 10 - 0
.idea/deploymentTargetSelector.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="deploymentTargetSelector">
+    <selectionStates>
+      <SelectionState runConfigName="app">
+        <option name="selectionMode" value="DROPDOWN" />
+      </SelectionState>
+    </selectionStates>
+  </component>
+</project>

+ 19 - 0
.idea/gradle.xml

@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="GradleMigrationSettings" migrationVersion="1" />
+  <component name="GradleSettings">
+    <option name="linkedExternalProjectsSettings">
+      <GradleProjectSettings>
+        <option name="testRunner" value="CHOOSE_PER_TEST" />
+        <option name="externalProjectPath" value="$PROJECT_DIR$" />
+        <option name="gradleJvm" value="#GRADLE_LOCAL_JAVA_HOME" />
+        <option name="modules">
+          <set>
+            <option value="$PROJECT_DIR$" />
+            <option value="$PROJECT_DIR$/app" />
+          </set>
+        </option>
+      </GradleProjectSettings>
+    </option>
+  </component>
+</project>

+ 61 - 0
.idea/inspectionProfiles/Project_Default.xml

@@ -0,0 +1,61 @@
+<component name="InspectionProjectProfileManager">
+  <profile version="1.0">
+    <option name="myName" value="Project Default" />
+    <inspection_tool class="ComposePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
+      <option name="composableFile" value="true" />
+      <option name="previewFile" value="true" />
+    </inspection_tool>
+    <inspection_tool class="ComposePreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
+      <option name="composableFile" value="true" />
+      <option name="previewFile" value="true" />
+    </inspection_tool>
+    <inspection_tool class="ComposePreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
+      <option name="composableFile" value="true" />
+      <option name="previewFile" value="true" />
+    </inspection_tool>
+    <inspection_tool class="ComposePreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
+      <option name="composableFile" value="true" />
+      <option name="previewFile" value="true" />
+    </inspection_tool>
+    <inspection_tool class="GlancePreviewDimensionRespectsLimit" enabled="true" level="WARNING" enabled_by_default="true">
+      <option name="composableFile" value="true" />
+    </inspection_tool>
+    <inspection_tool class="GlancePreviewMustBeTopLevelFunction" enabled="true" level="ERROR" enabled_by_default="true">
+      <option name="composableFile" value="true" />
+    </inspection_tool>
+    <inspection_tool class="GlancePreviewNeedsComposableAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
+      <option name="composableFile" value="true" />
+    </inspection_tool>
+    <inspection_tool class="GlancePreviewNotSupportedInUnitTestFiles" enabled="true" level="ERROR" enabled_by_default="true">
+      <option name="composableFile" value="true" />
+    </inspection_tool>
+    <inspection_tool class="PreviewAnnotationInFunctionWithParameters" enabled="true" level="ERROR" enabled_by_default="true">
+      <option name="composableFile" value="true" />
+      <option name="previewFile" value="true" />
+    </inspection_tool>
+    <inspection_tool class="PreviewApiLevelMustBeValid" enabled="true" level="ERROR" enabled_by_default="true">
+      <option name="composableFile" value="true" />
+      <option name="previewFile" value="true" />
+    </inspection_tool>
+    <inspection_tool class="PreviewDeviceShouldUseNewSpec" enabled="true" level="WEAK WARNING" enabled_by_default="true">
+      <option name="composableFile" value="true" />
+      <option name="previewFile" value="true" />
+    </inspection_tool>
+    <inspection_tool class="PreviewFontScaleMustBeGreaterThanZero" enabled="true" level="ERROR" enabled_by_default="true">
+      <option name="composableFile" value="true" />
+      <option name="previewFile" value="true" />
+    </inspection_tool>
+    <inspection_tool class="PreviewMultipleParameterProviders" enabled="true" level="ERROR" enabled_by_default="true">
+      <option name="composableFile" value="true" />
+      <option name="previewFile" value="true" />
+    </inspection_tool>
+    <inspection_tool class="PreviewParameterProviderOnFirstParameter" enabled="true" level="ERROR" enabled_by_default="true">
+      <option name="composableFile" value="true" />
+      <option name="previewFile" value="true" />
+    </inspection_tool>
+    <inspection_tool class="PreviewPickerAnnotation" enabled="true" level="ERROR" enabled_by_default="true">
+      <option name="composableFile" value="true" />
+      <option name="previewFile" value="true" />
+    </inspection_tool>
+  </profile>
+</component>

+ 10 - 0
.idea/migrations.xml

@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="ProjectMigrations">
+    <option name="MigrateToGradleLocalJavaHome">
+      <set>
+        <option value="$PROJECT_DIR$" />
+      </set>
+    </option>
+  </component>
+</project>

+ 9 - 0
.idea/misc.xml

@@ -0,0 +1,9 @@
+<project version="4">
+  <component name="ExternalStorageConfigurationManager" enabled="true" />
+  <component name="ProjectRootManager" version="2" languageLevel="JDK_21" default="true" project-jdk-name="jbr-21" project-jdk-type="JavaSDK">
+    <output url="file://$PROJECT_DIR$/build/classes" />
+  </component>
+  <component name="ProjectType">
+    <option name="id" value="Android" />
+  </component>
+</project>

+ 17 - 0
.idea/runConfigurations.xml

@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project version="4">
+  <component name="RunConfigurationProducerService">
+    <option name="ignoredProducers">
+      <set>
+        <option value="com.intellij.execution.junit.AbstractAllInDirectoryConfigurationProducer" />
+        <option value="com.intellij.execution.junit.AllInPackageConfigurationProducer" />
+        <option value="com.intellij.execution.junit.PatternConfigurationProducer" />
+        <option value="com.intellij.execution.junit.TestInClassConfigurationProducer" />
+        <option value="com.intellij.execution.junit.UniqueIdConfigurationProducer" />
+        <option value="com.intellij.execution.junit.testDiscovery.JUnitTestDiscoveryConfigurationProducer" />
+        <option value="org.jetbrains.kotlin.idea.junit.KotlinJUnitRunConfigurationProducer" />
+        <option value="org.jetbrains.kotlin.idea.junit.KotlinPatternConfigurationProducer" />
+      </set>
+    </option>
+  </component>
+</project>

+ 1 - 0
app/.gitignore

@@ -0,0 +1 @@
+/build

+ 71 - 0
app/build.gradle.kts

@@ -0,0 +1,71 @@
+plugins {
+    alias(libs.plugins.android.application)
+    alias(libs.plugins.kotlin.android)
+    alias(libs.plugins.kotlin.compose)
+}
+
+android {
+    namespace = "com.iscs.bozzys"
+    compileSdk {
+        version = release(36)
+    }
+
+    defaultConfig {
+        applicationId = "com.iscs.bozzys"
+        minSdk = 24
+        targetSdk = 36
+        versionCode = 1
+        versionName = "1.0"
+
+        testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+    }
+
+    buildTypes {
+        release {
+            isMinifyEnabled = false
+            proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
+        }
+    }
+    compileOptions {
+        sourceCompatibility = JavaVersion.VERSION_11
+        targetCompatibility = JavaVersion.VERSION_11
+    }
+    kotlinOptions {
+        jvmTarget = "11"
+    }
+    buildFeatures {
+        compose = true
+    }
+}
+
+dependencies {
+    implementation(libs.androidx.core.ktx)
+    implementation(libs.androidx.lifecycle.runtime.ktx)
+    implementation(libs.androidx.lifecycle.viewmodel.compose)
+    implementation(libs.androidx.activity.compose)
+    implementation(platform(libs.androidx.compose.bom))
+    implementation(libs.androidx.compose.ui)
+    implementation(libs.androidx.compose.ui.graphics)
+    implementation(libs.androidx.compose.ui.tooling.preview)
+    implementation(libs.androidx.compose.material3)
+
+    // 携程相关包的导入
+    implementation(libs.coroutines.core)
+    implementation(libs.coroutines.android)
+    // 网络请求相关
+    implementation(libs.retrofit.core)
+    implementation(libs.retrofit.converter.gson)
+    implementation(libs.retrofit.interceptor.logging)
+    // 轻量级键值对数据存储
+    implementation(libs.tencent.mmkv)
+    // 阿里云消息推送
+    implementation(libs.push)
+
+    testImplementation(libs.junit)
+    androidTestImplementation(libs.androidx.junit)
+    androidTestImplementation(libs.androidx.espresso.core)
+    androidTestImplementation(platform(libs.androidx.compose.bom))
+    androidTestImplementation(libs.androidx.compose.ui.test.junit4)
+    debugImplementation(libs.androidx.compose.ui.tooling)
+    debugImplementation(libs.androidx.compose.ui.test.manifest)
+}

+ 21 - 0
app/proguard-rules.pro

@@ -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

+ 24 - 0
app/src/androidTest/java/com/iscs/bozzys/ExampleInstrumentedTest.kt

@@ -0,0 +1,24 @@
+package com.iscs.bozzys
+
+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("com.iscs.bozzys", appContext.packageName)
+    }
+}

+ 36 - 0
app/src/main/AndroidManifest.xml

@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android">
+
+    <!--  网络请求  -->
+    <uses-permission android:name="android.permission.INTERNET" />
+
+    <application
+        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.Bozzys"
+        android:usesCleartextTraffic="true">
+        <!--  闪屏页面  -->
+        <activity
+            android:name=".ui.pages.PageSplash"
+            android:exported="true"
+            android:label="@string/app_name"
+            android:theme="@style/Theme.BozzysSplash">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.LAUNCHER" />
+            </intent-filter>
+        </activity>
+        <!--  主页面  -->
+        <activity
+            android:name=".ui.pages.PageHome"
+            android:exported="true"
+            android:launchMode="singleTask"
+            android:theme="@style/Theme.Bozzys" />
+    </application>
+
+</manifest>

+ 30 - 0
app/src/main/java/com/iscs/bozzys/Entry.kt

@@ -0,0 +1,30 @@
+package com.iscs.bozzys
+
+import android.app.Application
+import com.alibaba.sdk.android.push.noonesdk.PushInitConfig
+import com.alibaba.sdk.android.push.noonesdk.PushServiceFactory
+import com.iscs.bozzys.utils.Storage
+
+/**
+ * App主入口
+ */
+class Entry : Application() {
+
+    override fun onCreate() {
+        super.onCreate()
+        // 初始化轻量级存储相关
+        Storage.init(this)
+        // 初始化消息推送
+        initPush()
+    }
+
+    private fun initPush() {
+        val config = PushInitConfig.Builder()
+            .application(this)
+            .appKey("335634709")
+            .appSecret("5b88d533afe84513881a9c2f8502265f")
+            .build()
+        PushServiceFactory.init(config)
+    }
+
+}

+ 15 - 0
app/src/main/java/com/iscs/bozzys/api/ApiBean.kt

@@ -0,0 +1,15 @@
+package com.iscs.bozzys.api
+
+
+/**
+ * 请求基础响应对象
+ */
+open class Response(var code: Int = 0, var msg: String = "")
+
+/**
+ * 账号登录接口响应数据
+ *
+ * @param nickName  用户昵称
+ * @param token     token
+ */
+class LoginRsp(val nickName: String, val token: String) : Response()

+ 1 - 0
app/src/main/java/com/iscs/bozzys/api/ApiModel.kt

@@ -0,0 +1 @@
+package com.iscs.bozzys.api

+ 85 - 0
app/src/main/java/com/iscs/bozzys/api/ApiRequest.kt

@@ -0,0 +1,85 @@
+package com.iscs.bozzys.api
+
+import com.google.gson.Gson
+import com.google.gson.JsonObject
+import com.iscs.bozzys.utils.network.Request
+import java.net.UnknownHostException
+
+object ApiRequest {
+
+    // 创建请求对象
+    private val api: ApiService by lazy { Request.getRetrofit().create(ApiService::class.java) }
+
+    /**
+     * 处理请求头参数
+     *
+     * @param headers 用于自定义请求头参数
+     * @return 默认请求头参数,如果用户携带请求头参数与默认一致,优先使用用户携带
+     */
+    private fun getUserHeaders(headers: Map<String, String>): Map<String, String> {
+        val map = HashMap<String, String>()
+        map.putAll(headers)
+        return map
+    }
+
+    /**
+     * 处理各种异常
+     *
+     * @param e 异常总类
+     *
+     * @return 封装对外出去的异常总响应
+     */
+    private fun <T : Response> dealException(e: Exception, rsp: T): Exception {
+        if (rsp.code == 0) {
+            rsp.code = 404
+            rsp.msg = "Network error"
+            when {
+                e is UnknownHostException -> {
+                    rsp.code = 500
+                    rsp.msg = e.message ?: "Network error"
+                }
+            }
+        }
+        return Exception(Gson().toJson(rsp))
+    }
+
+    /**
+     * 基础请求封装
+     */
+    private suspend fun <T : Response> requestApi(apiFun: suspend () -> T): Result<T> {
+        return try {
+            val rsp = apiFun()
+            if (rsp.code == 200) {
+                Result.success(rsp)
+            } else {
+                Result.failure(dealException(Exception(rsp.msg), rsp))
+            }
+        } catch (e: Exception) {
+            Result.failure(dealException(e, Response()))
+        }
+    }
+
+    /**
+     * 获取响应数据结构
+     */
+    fun Throwable.getResponse(): Response {
+        val msg = this.message ?: "{\"code\":404,\"msg\":\"Network error\"}"
+        val json = if (msg.startsWith("{") && msg.endsWith("}")) msg else "{\"code\":404,\"msg\":\"Network error\"}"
+        val t = Response()
+        return Gson().fromJson(json, t::class.java)
+    }
+
+    /**
+     * 登录
+     *
+     * @param username  用户名
+     * @param pwd       密码
+     */
+    suspend fun login(username: String, pwd: String): Result<LoginRsp> {
+        val params = JsonObject()
+        params.addProperty("username", username)
+        params.addProperty("password", pwd)
+        return requestApi { api.login(getUserHeaders(emptyMap()), params) }
+    }
+
+}

+ 21 - 0
app/src/main/java/com/iscs/bozzys/api/ApiService.kt

@@ -0,0 +1,21 @@
+package com.iscs.bozzys.api
+
+import com.google.gson.JsonObject
+import retrofit2.http.Body
+import retrofit2.http.HeaderMap
+import retrofit2.http.Headers
+import retrofit2.http.POST
+
+/**
+ * 接口定义层
+ */
+interface ApiService {
+
+    /**
+     * 登录
+     */
+    @Headers("Content-Type: application/json")
+    @POST("/login")
+    suspend fun login(@HeaderMap headers: Map<String, String>, @Body body: JsonObject): LoginRsp
+
+}

+ 129 - 0
app/src/main/java/com/iscs/bozzys/ui/base/PageBase.kt

@@ -0,0 +1,129 @@
+package com.iscs.bozzys.ui.base
+
+import android.os.Bundle
+import android.widget.Toast
+import androidx.activity.ComponentActivity
+import androidx.activity.SystemBarStyle
+import androidx.activity.compose.setContent
+import androidx.activity.enableEdgeToEdge
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.material3.Scaffold
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.toArgb
+import androidx.core.view.WindowCompat
+import androidx.core.view.WindowInsetsCompat
+import androidx.core.view.WindowInsetsControllerCompat
+import androidx.lifecycle.lifecycleScope
+import com.iscs.bozzys.ui.theme.BozzysTheme
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.delay
+import kotlinx.coroutines.flow.MutableSharedFlow
+import kotlinx.coroutines.launch
+
+/**
+ * 基础活动,需所有活动继承
+ */
+abstract class PageBase(
+    // 是否处理布局边界
+    val needEdge: Boolean = true,
+    // 是否隐藏底部导航按钮
+    val hideNavigationBar: Boolean = false
+) : ComponentActivity() {
+
+    /**
+     * 页面创建
+     *
+     * @param savedInstanceState 页面实例
+     */
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+        if (hideNavigationBar) hideNavigationBar()
+        if (needEdge) enableEdgeToEdge(
+            navigationBarStyle = SystemBarStyle.auto(
+                lightScrim = Color.Transparent.toArgb(),
+                darkScrim = Color.Transparent.toArgb()
+            )
+        )
+        setContent {
+            BozzysTheme {
+                Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
+                    GetViews(innerPadding)
+                }
+            }
+        }
+    }
+
+    /**
+     * 提供外部 用于构建页面视图使用
+     *
+     * @param pv 页面顶部预留间隙
+     */
+    @Composable
+    abstract fun GetViews(pv: PaddingValues)
+
+    /**
+     * 页面显示后的处理逻辑
+     */
+    override fun onResume() {
+        super.onResume()
+        if (hideNavigationBar) hideNavigationBar()
+    }
+
+    /**
+     * 销毁当前活动
+     */
+    fun destroy() {
+        finish()
+    }
+
+    /**
+     * 销毁当前活动,延时{value}ms后
+     *
+     * @param value 延时时间 单位:ms
+     */
+    fun destroyDelay(value: Long) {
+        lifecycleScope.launch {
+            delay(value)
+            finish()
+        }
+    }
+
+    /**
+     * 隐藏底部导航栏
+     */
+    fun hideNavigationBar() {
+        WindowCompat.setDecorFitsSystemWindows(window, false)
+        val wic = WindowInsetsControllerCompat(window, window.decorView)
+        wic.systemBarsBehavior = WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
+        wic.hide(WindowInsetsCompat.Type.navigationBars())
+    }
+
+    /**
+     * 显示Toast,通过字符串
+     */
+    fun Any.showToast() {
+        lifecycleScope.launch(Dispatchers.Main) {
+            when (this@showToast) {
+                // 显示字符串Toast
+                is String -> {
+                    Toast.makeText(this@PageBase, this@showToast, Toast.LENGTH_LONG).show()
+                }
+                // 显示资源id Toast
+                is Int -> {
+                    Toast.makeText(this@PageBase, this@showToast, Toast.LENGTH_LONG).show()
+                }
+            }
+        }
+    }
+
+    /**
+     * 显示toast,Flow方式
+     */
+    suspend fun MutableSharedFlow<Any>.showToast() {
+        this.collect { msg -> msg.showToast() }
+    }
+
+}

+ 15 - 0
app/src/main/java/com/iscs/bozzys/ui/base/VMBase.kt

@@ -0,0 +1,15 @@
+package com.iscs.bozzys.ui.base
+
+import androidx.lifecycle.ViewModel
+import kotlinx.coroutines.flow.MutableSharedFlow
+
+/**
+ * 基类Model需要其他继承
+ */
+open class VMBase : ViewModel() {
+
+    /**
+     * Toast 事件
+     */
+    val toastEvent = MutableSharedFlow<Any>()
+}

+ 42 - 0
app/src/main/java/com/iscs/bozzys/ui/pages/PageHome.kt

@@ -0,0 +1,42 @@
+package com.iscs.bozzys.ui.pages
+
+import android.content.Context
+import android.content.Intent
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.ui.Modifier
+import androidx.lifecycle.viewmodel.compose.viewModel
+import com.iscs.bozzys.ui.base.PageBase
+import com.iscs.bozzys.ui.pages.vm.VMHome
+
+/**
+ * 打开主页面
+ */
+fun Context.openPageHome() {
+    startActivity(Intent(this, PageHome::class.java))
+}
+
+/**
+ * App主页面
+ */
+class PageHome : PageBase() {
+
+    @Composable
+    override fun GetViews(pv: PaddingValues) {
+        Home(pv)
+    }
+
+    @Composable
+    fun Home(pv: PaddingValues, vm: VMHome = viewModel()) {
+        val state = vm.state
+        Text(state.username, modifier = Modifier.padding(pv))
+
+        LaunchedEffect(Unit) {
+
+        }
+    }
+
+}

+ 90 - 0
app/src/main/java/com/iscs/bozzys/ui/pages/PageSplash.kt

@@ -0,0 +1,90 @@
+package com.iscs.bozzys.ui.pages
+
+import android.app.NotificationChannel
+import android.app.NotificationManager
+import android.graphics.Color
+import android.util.Log
+import androidx.compose.foundation.layout.PaddingValues
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import com.alibaba.sdk.android.push.CommonCallback
+import com.alibaba.sdk.android.push.noonesdk.PushServiceFactory
+import com.iscs.bozzys.ui.base.PageBase
+import kotlinx.coroutines.delay
+
+/**
+ * 配置App启动页面
+ */
+class PageSplash : PageBase() {
+
+    @Composable
+    override fun GetViews(pv: PaddingValues) {
+        // 配置页面显示控件
+//        Image(
+//            painter = painterResource(R.mipmap.ic_launcher_round),
+//            contentDescription = "",
+//            modifier = Modifier.fillMaxSize(),
+//            contentScale = ContentScale.FillBounds
+//        )
+        // 协程处理跳转事件
+        LaunchedEffect(Unit) {
+            // 做SDK的初始化操作,有一些没必要在Application中初始化操作的,在这里进行初始化操作
+            initSDK()
+            delay(3000)
+            openPageHome()
+            destroyDelay(1000)
+        }
+    }
+
+    /**
+     * 这里做一些SDK初始化的操作
+     */
+    private fun initSDK() {
+        // 初始化消息推送
+        initPush()
+    }
+
+    /**
+     * 消息推送初始化操作
+     */
+    private fun initPush() {
+        // 初始化消息推送
+        PushServiceFactory.getCloudPushService().register(this, object : CommonCallback {
+
+            override fun onSuccess(msg: String?) {
+                // 消息推送初始化成功
+                Log.d("xiaoming", "Push init successful $msg")
+            }
+
+            override fun onFailed(errCode: String?, errMsg: String?) {
+                // 消息推送初始化失败
+                Log.d("xiaoming", "Push init failed code $errCode msg $errMsg")
+            }
+
+        })
+        // 建立通知通道
+        val nm = getSystemService(NOTIFICATION_SERVICE) as NotificationManager
+        // 通知渠道的id。这个id值需要给后端开发和运维人员,推送的时候对应 AndroidNotificationChannel 参数。
+        val channelId = "normal"
+        // 用户可以看到的通知渠道的名字。
+        val name: CharSequence = "我的测试通道"
+        // 用户可以看到的通知渠道的描述。
+        val description = "我的测试通道"
+        val importance = NotificationManager.IMPORTANCE_HIGH
+        val channel = NotificationChannel(channelId, name, importance)
+
+        // 配置通知渠道的属性。
+        channel.description = description
+        // 设置通知出现时的闪灯(如果Android设备支持的话)。
+        channel.enableLights(true)
+        channel.lightColor = Color.RED
+        // 设置通知出现时的震动(如果Android设备支持的话)。
+        channel.enableVibration(true)
+        // 自定义铃声
+        // channel.setSound(Uri.parse("android.resource://${packageName}/${R.raw.push_hongbao}"), Notification.AUDIO_ATTRIBUTES_DEFAULT)
+        channel.vibrationPattern = longArrayOf(100, 200, 300, 400, 500, 400, 300, 200, 400)
+        // 最后在NotificationManager中创建该通知渠道。
+        nm.createNotificationChannel(channel)
+    }
+
+}

+ 30 - 0
app/src/main/java/com/iscs/bozzys/ui/pages/vm/VMHome.kt

@@ -0,0 +1,30 @@
+package com.iscs.bozzys.ui.pages.vm
+
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.lifecycle.viewModelScope
+import com.iscs.bozzys.api.ApiRequest
+import com.iscs.bozzys.api.ApiRequest.getResponse
+import com.iscs.bozzys.ui.base.VMBase
+import kotlinx.coroutines.launch
+
+class VMHome : VMBase() {
+
+    var state by mutableStateOf(StateHome())
+        private set
+
+    fun login() {
+        viewModelScope.launch {
+            ApiRequest.login("zhangsan", "1234567").onSuccess {
+                state = state.copy(username = it.nickName)
+                toastEvent.emit("登录成功")
+            }.onFailure {
+                toastEvent.emit(it.getResponse().msg)
+            }
+        }
+    }
+
+}
+
+data class StateHome(val username: String = "")

+ 11 - 0
app/src/main/java/com/iscs/bozzys/ui/theme/Color.kt

@@ -0,0 +1,11 @@
+package com.iscs.bozzys.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)

+ 49 - 0
app/src/main/java/com/iscs/bozzys/ui/theme/Theme.kt

@@ -0,0 +1,49 @@
+package com.iscs.bozzys.ui.theme
+
+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.ui.platform.LocalContext
+
+// 暗黑主题配置
+private val DarkColorScheme = darkColorScheme(
+    primary = Purple80,
+    secondary = PurpleGrey80,
+    tertiary = Pink80
+)
+
+// 标准主题配置
+private val LightColorScheme = lightColorScheme(
+    primary = Purple40,
+    secondary = PurpleGrey40,
+    tertiary = Pink40
+)
+
+@Composable
+fun BozzysTheme(
+    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
+    }
+
+    MaterialTheme(
+        colorScheme = colorScheme,
+        typography = Typography,
+        content = content
+    )
+}

+ 34 - 0
app/src/main/java/com/iscs/bozzys/ui/theme/Type.kt

@@ -0,0 +1,34 @@
+package com.iscs.bozzys.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
+
+// 定义字体规范
+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
+    )
+    */
+)

+ 35 - 0
app/src/main/java/com/iscs/bozzys/utils/Storage.kt

@@ -0,0 +1,35 @@
+package com.iscs.bozzys.utils
+
+import android.app.Application
+import com.tencent.mmkv.MMKV
+
+object Storage {
+
+    /**
+     * 操作对象
+     */
+    private lateinit var mmkv: MMKV
+
+    /**
+     * 初始化
+     */
+    fun init(app: Application) {
+        MMKV.initialize(app)
+        mmkv = MMKV.defaultMMKV()
+    }
+
+    /**
+     * 存储token
+     */
+    fun String?.saveToken() {
+        mmkv.encode("user_token", this ?: "")
+    }
+
+    /**
+     * 获取token
+     */
+    fun readToken(): String {
+        return mmkv.decodeString("user_token", "") ?: ""
+    }
+
+}

+ 73 - 0
app/src/main/java/com/iscs/bozzys/utils/network/Request.kt

@@ -0,0 +1,73 @@
+package com.iscs.bozzys.utils.network
+
+import okhttp3.OkHttpClient
+import okhttp3.logging.HttpLoggingInterceptor
+import retrofit2.Retrofit
+import retrofit2.converter.gson.GsonConverterFactory
+import java.security.SecureRandom
+import java.security.cert.X509Certificate
+import javax.net.ssl.SSLContext
+import javax.net.ssl.SSLSocketFactory
+import javax.net.ssl.TrustManager
+import javax.net.ssl.X509TrustManager
+
+/**
+ * 基础请求封装层
+ */
+object Request {
+
+    // 网络请求的基地址
+    private const val BASE_URL = "http://120.27.232.27:9190"
+
+    // 构建请求客户端
+    private val okClient = OkHttpClient.Builder()
+        .addInterceptor(HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
+        .sslSocketFactory(getSocketFactory(), createTrustManager())
+        .hostnameVerifier { _, _ -> true }
+        .build()
+
+    // 构建接口请求工具
+    private val retrofit = Retrofit.Builder()
+        .baseUrl(BASE_URL)
+        .client(okClient)
+        .addConverterFactory(GsonConverterFactory.create())
+        .build()
+
+
+    /**
+     * 创建一个证书工厂
+     */
+    private fun getSocketFactory(): SSLSocketFactory {
+        // 创建一个不验证证书链的 TrustManager
+        val trustAllCerts = arrayOf<TrustManager>(
+            object : X509TrustManager {
+                override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) {}
+                override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String) {}
+                override fun getAcceptedIssuers(): Array<X509Certificate> = arrayOf()
+            }
+        )
+
+        val sslContext = SSLContext.getInstance("SSL")
+        sslContext.init(null, trustAllCerts, SecureRandom())
+
+        return sslContext.socketFactory
+    }
+
+    /**
+     * 创建一个不验证证书链的 TrustManager
+     */
+    private fun createTrustManager(): X509TrustManager {
+        val trustAllCerts = arrayOf<TrustManager>(
+            object : X509TrustManager {
+                override fun checkClientTrusted(chain: Array<X509Certificate>, authType: String) {}
+                override fun checkServerTrusted(chain: Array<X509Certificate>, authType: String) {}
+                override fun getAcceptedIssuers(): Array<X509Certificate> = arrayOf()
+            }
+        )
+        return trustAllCerts[0] as X509TrustManager
+    }
+
+    fun getRetrofit(): Retrofit {
+        return retrofit
+    }
+}

+ 170 - 0
app/src/main/res/drawable/ic_launcher_background.xml

@@ -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>

+ 30 - 0
app/src/main/res/drawable/ic_launcher_foreground.xml

@@ -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>

+ 6 - 0
app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml

@@ -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>

+ 6 - 0
app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml

@@ -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>

BIN
app/src/main/res/mipmap-hdpi/ic_launcher.webp


BIN
app/src/main/res/mipmap-hdpi/ic_launcher_round.webp


BIN
app/src/main/res/mipmap-mdpi/ic_launcher.webp


BIN
app/src/main/res/mipmap-mdpi/ic_launcher_round.webp


BIN
app/src/main/res/mipmap-xhdpi/ic_launcher.webp


BIN
app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp


BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher.webp


BIN
app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp


BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp


BIN
app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp


+ 4 - 0
app/src/main/res/values-zh-rCN/strings.xml

@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="app_name">博士安全</string>
+</resources>

+ 10 - 0
app/src/main/res/values/colors.xml

@@ -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>

+ 3 - 0
app/src/main/res/values/strings.xml

@@ -0,0 +1,3 @@
+<resources>
+    <string name="app_name">Bozzys</string>
+</resources>

+ 14 - 0
app/src/main/res/values/themes.xml

@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+
+    <style name="Theme.Bozzys" parent="android:Theme.Material.Light.NoActionBar" />
+
+    <style name="Theme.BozzysSplash" parent="android:Theme.Light.NoTitleBar.Fullscreen">
+        <item name="android:windowTranslucentStatus">true</item>
+        <item name="android:windowTranslucentNavigation">true</item>
+        <item name="android:statusBarColor">@android:color/transparent</item>
+        <item name="android:windowFullscreen">true</item>
+        <item name="android:background">@mipmap/ic_launcher</item>
+    </style>
+
+</resources>

+ 13 - 0
app/src/main/res/xml/backup_rules.xml

@@ -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 than 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>

+ 19 - 0
app/src/main/res/xml/data_extraction_rules.xml

@@ -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>

+ 17 - 0
app/src/test/java/com/iscs/bozzys/ExampleUnitTest.kt

@@ -0,0 +1,17 @@
+package com.iscs.bozzys
+
+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)
+    }
+}

+ 6 - 0
build.gradle.kts

@@ -0,0 +1,6 @@
+// Top-level build file where you can add configuration options common to all sub-projects/modules.
+plugins {
+    alias(libs.plugins.android.application) apply false
+    alias(libs.plugins.kotlin.android) apply false
+    alias(libs.plugins.kotlin.compose) apply false
+}

+ 23 - 0
gradle.properties

@@ -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. For more details, visit
+# https://developer.android.com/r/tools/gradle-multi-project-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

+ 46 - 0
gradle/libs.versions.toml

@@ -0,0 +1,46 @@
+[versions]
+agp = "8.13.1"
+kotlin = "2.0.21"
+coreKtx = "1.10.1"
+junit = "4.13.2"
+junitVersion = "1.1.5"
+espressoCore = "3.5.1"
+lifecycleRuntimeKtx = "2.6.1"
+activityCompose = "1.8.0"
+composeBom = "2024.09.00"
+coroutines = "1.6.4"
+
+[libraries]
+androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
+junit = { group = "junit", name = "junit", version.ref = "junit" }
+androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
+androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
+androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
+androidx-lifecycle-viewmodel-compose = { group = "androidx.lifecycle", name = "lifecycle-viewmodel-compose", version = "2.8.0" }
+androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
+androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
+androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" }
+androidx-compose-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
+androidx-compose-ui-tooling = { group = "androidx.compose.ui", name = "ui-tooling" }
+androidx-compose-ui-tooling-preview = { group = "androidx.compose.ui", name = "ui-tooling-preview" }
+androidx-compose-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-manifest" }
+androidx-compose-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
+androidx-compose-material3 = { group = "androidx.compose.material3", name = "material3" }
+
+# 网络请求
+retrofit-core = { group = "com.squareup.retrofit2", name = "retrofit", version = "3.0.0" }
+retrofit-converter-gson = { group = "com.squareup.retrofit2", name = "converter-gson", version = "3.0.0" }
+retrofit-interceptor-logging = { group = "com.squareup.okhttp3", name = "logging-interceptor", version = "4.12.0" }
+# 携程相关
+coroutines-core = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-core", version.ref = "coroutines" }
+coroutines-android = { group = "org.jetbrains.kotlinx", name = "kotlinx-coroutines-android", version.ref = "coroutines" }
+# MMKV
+tencent-mmkv = { group = "com.tencent", name = "mmkv", version = "2.2.3" }
+# 阿里消息推送
+push = { group = "com.aliyun.ams", name = "alicloud-android-push", version = "3.10.1" }
+
+[plugins]
+android-application = { id = "com.android.application", version.ref = "agp" }
+kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }
+kotlin-compose = { id = "org.jetbrains.kotlin.plugin.compose", version.ref = "kotlin" }
+

BIN
gradle/wrapper/gradle-wrapper.jar


+ 8 - 0
gradle/wrapper/gradle-wrapper.properties

@@ -0,0 +1,8 @@
+#Thu Dec 04 13:43:16 CST 2025
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.13-bin.zip
+networkTimeout=10000
+validateDistributionUrl=true
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists

+ 251 - 0
gradlew

@@ -0,0 +1,251 @@
+#!/bin/sh
+
+#
+# Copyright © 2015 the original 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.
+#
+# SPDX-License-Identifier: Apache-2.0
+#
+
+##############################################################################
+#
+#   Gradle start up script for POSIX generated by Gradle.
+#
+#   Important for running:
+#
+#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+#       noncompliant, but you have some other compliant shell such as ksh or
+#       bash, then to run this script, type that shell name before the whole
+#       command line, like:
+#
+#           ksh Gradle
+#
+#       Busybox and similar reduced shells will NOT work, because this script
+#       requires all of these POSIX shell features:
+#         * functions;
+#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+#         * compound commands having a testable exit status, especially «case»;
+#         * various built-in commands including «command», «set», and «ulimit».
+#
+#   Important for patching:
+#
+#   (2) This script targets any POSIX shell, so it avoids extensions provided
+#       by Bash, Ksh, etc; in particular arrays are avoided.
+#
+#       The "traditional" practice of packing multiple parameters into a
+#       space-separated string is a well documented source of bugs and security
+#       problems, so this is (mostly) avoided, by progressively accumulating
+#       options in "$@", and eventually passing that to Java.
+#
+#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+#       see the in-line comments for details.
+#
+#       There are tweaks for specific operating systems such as AIX, CygWin,
+#       Darwin, MinGW, and NonStop.
+#
+#   (3) This script is generated from the Groovy template
+#       https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+#       within the Gradle project.
+#
+#       You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+    APP_HOME=${app_path%"${app_path##*/}"}  # leaves a trailing /; empty if no leading path
+    [ -h "$app_path" ]
+do
+    ls=$( ls -ld "$app_path" )
+    link=${ls#*' -> '}
+    case $link in             #(
+      /*)   app_path=$link ;; #(
+      *)    app_path=$APP_HOME$link ;;
+    esac
+done
+
+# This is normally unused
+# shellcheck disable=SC2034
+APP_BASE_NAME=${0##*/}
+# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
+APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+    echo "$*"
+} >&2
+
+die () {
+    echo
+    echo "$*"
+    echo
+    exit 1
+} >&2
+
+# 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  ;; #(
+  MSYS* | MINGW* )  msys=true    ;; #(
+  NONSTOP* )        nonstop=true ;;
+esac
+
+CLASSPATH="\\\"\\\""
+
+
+# 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
+    if ! command -v java >/dev/null 2>&1
+    then
+        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
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+    case $MAX_FD in #(
+      max*)
+        # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked.
+        # shellcheck disable=SC2039,SC3045
+        MAX_FD=$( ulimit -H -n ) ||
+            warn "Could not query maximum file descriptor limit"
+    esac
+    case $MAX_FD in  #(
+      '' | soft) :;; #(
+      *)
+        # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked.
+        # shellcheck disable=SC2039,SC3045
+        ulimit -n "$MAX_FD" ||
+            warn "Could not set maximum file descriptor limit to $MAX_FD"
+    esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+#   * args from the command line
+#   * the main class name
+#   * -classpath
+#   * -D...appname settings
+#   * --module-path (only if needed)
+#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+    APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+    CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+    JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    for arg do
+        if
+            case $arg in                                #(
+              -*)   false ;;                            # don't mess with options #(
+              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath
+                    [ -e "$t" ] ;;                      #(
+              *)    false ;;
+            esac
+        then
+            arg=$( cygpath --path --ignore --mixed "$arg" )
+        fi
+        # Roll the args list around exactly as many times as the number of
+        # args, so each arg winds up back in the position where it started, but
+        # possibly modified.
+        #
+        # NB: a `for` loop captures its iteration list before it begins, so
+        # changing the positional parameters here affects neither the number of
+        # iterations, nor the values presented in `arg`.
+        shift                   # remove old arg
+        set -- "$@" "$arg"      # push replacement arg
+    done
+fi
+
+
+# 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"'
+
+# Collect all arguments for the java command:
+#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
+#     and any embedded shellness will be escaped.
+#   * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
+#     treated as '${Hostname}' itself on the command line.
+
+set -- \
+        "-Dorg.gradle.appname=$APP_BASE_NAME" \
+        -classpath "$CLASSPATH" \
+        -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
+        "$@"
+
+# Stop when "xargs" is not available.
+if ! command -v xargs >/dev/null 2>&1
+then
+    die "xargs is not available"
+fi
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+#   readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+#   set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+        printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+        xargs -n1 |
+        sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+        tr '\n' ' '
+    )" '"$@"'
+
+exec "$JAVACMD" "$@"

+ 94 - 0
gradlew.bat

@@ -0,0 +1,94 @@
+@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
+@rem SPDX-License-Identifier: Apache-2.0
+@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=.
+@rem This is normally unused
+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% equ 0 goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto execute
+
+echo. 1>&2
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2
+echo. 1>&2
+echo Please set the JAVA_HOME variable in your environment to match the 1>&2
+echo location of your Java installation. 1>&2
+
+goto fail
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=
+
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
+
+:end
+@rem End local scope for the variables with windows NT shell
+if %ERRORLEVEL% equ 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!
+set EXIT_CODE=%ERRORLEVEL%
+if %EXIT_CODE% equ 0 set EXIT_CODE=1
+if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE%
+exit /b %EXIT_CODE%
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega

+ 28 - 0
settings.gradle.kts

@@ -0,0 +1,28 @@
+pluginManagement {
+    repositories {
+        google {
+            content {
+                includeGroupByRegex("com\\.android.*")
+                includeGroupByRegex("com\\.google.*")
+                includeGroupByRegex("androidx.*")
+            }
+        }
+        mavenCentral()
+        gradlePluginPortal()
+    }
+}
+dependencyResolutionManagement {
+    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
+    repositories {
+        google()
+        mavenCentral()
+        // 配置阿里云仓库
+        maven("https://maven.aliyun.com/nexus/content/repositories/releases/")
+        // 配置HMS Core SDK的Maven仓地址,集成华为通道需要。
+        maven("https://developer.huawei.com/repo/")
+    }
+}
+
+rootProject.name = "Bozzys"
+include(":app")
+