├── App
├── .gitignore
├── .idea
│ ├── .gitignore
│ ├── compiler.xml
│ ├── deploymentTargetDropDown.xml
│ ├── gradle.xml
│ ├── inspectionProfiles
│ │ └── Project_Default.xml
│ ├── misc.xml
│ └── vcs.xml
├── app
│ ├── .gitignore
│ ├── build.gradle
│ ├── proguard-rules.pro
│ └── src
│ │ ├── androidTest
│ │ └── java
│ │ │ └── icu
│ │ │ └── bughub
│ │ │ └── app
│ │ │ └── app
│ │ │ └── ExampleInstrumentedTest.kt
│ │ ├── main
│ │ ├── AndroidManifest.xml
│ │ ├── java
│ │ │ └── icu
│ │ │ │ └── bughub
│ │ │ │ └── app
│ │ │ │ └── app
│ │ │ │ ├── MainActivity.kt
│ │ │ │ ├── compositionLocal
│ │ │ │ └── CompositionLocal.kt
│ │ │ │ ├── extension
│ │ │ │ └── LazyListStateExtension.kt
│ │ │ │ ├── model
│ │ │ │ ├── Network.kt
│ │ │ │ ├── entity
│ │ │ │ │ ├── ArticleEntity.kt
│ │ │ │ │ ├── BaseResponse.kt
│ │ │ │ │ ├── Category.kt
│ │ │ │ │ ├── DataType.kt
│ │ │ │ │ ├── NavigationItem.kt
│ │ │ │ │ ├── SwiperEntity.kt
│ │ │ │ │ ├── UserInfoEntity.kt
│ │ │ │ │ └── VideoEntity.kt
│ │ │ │ └── service
│ │ │ │ │ ├── ArticleService.kt
│ │ │ │ │ ├── HomeService.kt
│ │ │ │ │ ├── UserInfoManager.kt
│ │ │ │ │ ├── UserService.kt
│ │ │ │ │ └── VideoService.kt
│ │ │ │ ├── ui
│ │ │ │ ├── components
│ │ │ │ │ ├── ArticleItem.kt
│ │ │ │ │ ├── ChartView.kt
│ │ │ │ │ ├── CircleRing.kt
│ │ │ │ │ ├── DailyTaskContent.kt
│ │ │ │ │ ├── NavHostApp.kt
│ │ │ │ │ ├── NotificationContent.kt
│ │ │ │ │ ├── SwiperContent.kt
│ │ │ │ │ ├── TopAppBar.kt
│ │ │ │ │ ├── VideoItem.kt
│ │ │ │ │ └── video
│ │ │ │ │ │ ├── PlayerValue.kt
│ │ │ │ │ │ ├── VideoPlayer.kt
│ │ │ │ │ │ ├── VideoView.kt
│ │ │ │ │ │ └── VodController.kt
│ │ │ │ ├── navigation
│ │ │ │ │ └── Destinations.kt
│ │ │ │ ├── screens
│ │ │ │ │ ├── ArticleDetailScreen.kt
│ │ │ │ │ ├── LoginScreen.kt
│ │ │ │ │ ├── MainFrame.kt
│ │ │ │ │ ├── MineScreen.kt
│ │ │ │ │ ├── StudyScreen.kt
│ │ │ │ │ ├── TaskScreen.kt
│ │ │ │ │ └── VideoDetailScreen.kt
│ │ │ │ └── theme
│ │ │ │ │ ├── Color.kt
│ │ │ │ │ ├── Shape.kt
│ │ │ │ │ ├── Theme.kt
│ │ │ │ │ └── Type.kt
│ │ │ │ └── viewmodel
│ │ │ │ ├── ArticleViewModel.kt
│ │ │ │ ├── MainViewModel.kt
│ │ │ │ ├── TaskViewModel.kt
│ │ │ │ ├── UserViewModel.kt
│ │ │ │ └── VideoViewModel.kt
│ │ └── res
│ │ │ ├── drawable-v24
│ │ │ └── ic_launcher_foreground.xml
│ │ │ ├── drawable
│ │ │ ├── archives.png
│ │ │ ├── bg.jpg
│ │ │ ├── browser_history.png
│ │ │ ├── ic_launcher_background.xml
│ │ │ ├── newbanner3.png
│ │ │ ├── points.png
│ │ │ ├── questions.png
│ │ │ ├── settings.png
│ │ │ └── version.png
│ │ │ ├── layout
│ │ │ └── video.xml
│ │ │ ├── mipmap-anydpi-v26
│ │ │ ├── ic_launcher.xml
│ │ │ └── ic_launcher_round.xml
│ │ │ ├── mipmap-hdpi
│ │ │ ├── ic_launcher.webp
│ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-mdpi
│ │ │ ├── ic_launcher.webp
│ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-xhdpi
│ │ │ ├── ic_launcher.webp
│ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-xxhdpi
│ │ │ ├── ic_launcher.webp
│ │ │ └── ic_launcher_round.webp
│ │ │ ├── mipmap-xxxhdpi
│ │ │ ├── ic_launcher.webp
│ │ │ └── ic_launcher_round.webp
│ │ │ └── values
│ │ │ ├── colors.xml
│ │ │ ├── strings.xml
│ │ │ └── themes.xml
│ │ └── test
│ │ └── java
│ │ └── icu
│ │ └── bughub
│ │ └── app
│ │ └── app
│ │ └── ExampleUnitTest.kt
├── build.gradle
├── gradle.properties
├── gradle
│ └── wrapper
│ │ ├── gradle-wrapper.jar
│ │ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── settings.gradle
└── webview
│ ├── .gitignore
│ ├── build.gradle
│ ├── consumer-rules.pro
│ ├── proguard-rules.pro
│ └── src
│ ├── androidTest
│ └── java
│ │ └── icu
│ │ └── bughub
│ │ └── app
│ │ └── module
│ │ └── webview
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ └── java
│ │ └── icu
│ │ └── bughub
│ │ └── app
│ │ └── module
│ │ └── webview
│ │ └── WebView.kt
│ └── test
│ └── java
│ └── icu
│ └── bughub
│ └── app
│ └── module
│ └── webview
│ └── ExampleUnitTest.kt
└── ComposeBasic
├── .gitignore
├── .idea
├── .gitignore
├── compiler.xml
├── gradle.xml
├── inspectionProfiles
│ └── Project_Default.xml
├── misc.xml
└── vcs.xml
├── app
├── .gitignore
├── build.gradle
├── proguard-rules.pro
└── src
│ ├── androidTest
│ └── java
│ │ └── icu
│ │ └── bughub
│ │ └── app
│ │ └── composebasic
│ │ └── ExampleInstrumentedTest.kt
│ ├── main
│ ├── AndroidManifest.xml
│ ├── java
│ │ └── icu
│ │ │ └── bughub
│ │ │ └── app
│ │ │ └── composebasic
│ │ │ ├── MainActivity.kt
│ │ │ ├── components
│ │ │ ├── AnimationCanvasSample.kt
│ │ │ ├── AnimationSample.kt
│ │ │ ├── BoxSample.kt
│ │ │ ├── ButtonSample.kt
│ │ │ ├── CanvasSample.kt
│ │ │ ├── CardSample.kt
│ │ │ ├── CheckBoxSample.kt
│ │ │ ├── ColumnSample.kt
│ │ │ ├── CompositionLocalSample.kt
│ │ │ ├── ConstaintLayoutSample.kt
│ │ │ ├── ConstraintsSample.kt
│ │ │ ├── DialogSample.kt
│ │ │ ├── DividerSample.kt
│ │ │ ├── DropdownMenuSample.kt
│ │ │ ├── IconSample.kt
│ │ │ ├── ImageSample.kt
│ │ │ ├── LazyColumnSample.kt
│ │ │ ├── LazyRowSample.kt
│ │ │ ├── LazyVerticalGridSample.kt
│ │ │ ├── LifecycleSample.kt
│ │ │ ├── ListItemSample.kt
│ │ │ ├── ModifierSample.kt
│ │ │ ├── NavSample.kt
│ │ │ ├── PermissionSample.kt
│ │ │ ├── ProgressIndicatorSample.kt
│ │ │ ├── RadioButtonSample.kt
│ │ │ ├── RowSample.kt
│ │ │ ├── ScaffoldSample.kt
│ │ │ ├── SliderSample.kt
│ │ │ ├── SpacerSample.kt
│ │ │ ├── StateSample.kt
│ │ │ ├── Stepper.kt
│ │ │ ├── SurfaceSample.kt
│ │ │ ├── SwipeToDismissSample.kt
│ │ │ ├── SwitchSample.kt
│ │ │ ├── TabSample.kt
│ │ │ ├── TextFieldSample.kt
│ │ │ └── TextSample.kt
│ │ │ └── ui
│ │ │ └── theme
│ │ │ ├── Color.kt
│ │ │ ├── Shape.kt
│ │ │ ├── Theme.kt
│ │ │ └── Type.kt
│ └── res
│ │ ├── drawable-v24
│ │ └── ic_launcher_foreground.xml
│ │ ├── drawable
│ │ ├── ic_android_black_24dp.xml
│ │ ├── ic_launcher_background.xml
│ │ └── newbanner4.png
│ │ ├── layout
│ │ └── main.xml
│ │ ├── mipmap-anydpi-v26
│ │ ├── ic_launcher.xml
│ │ └── ic_launcher_round.xml
│ │ ├── mipmap-hdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-mdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ ├── mipmap-xxxhdpi
│ │ ├── ic_launcher.webp
│ │ └── ic_launcher_round.webp
│ │ └── values
│ │ ├── colors.xml
│ │ ├── strings.xml
│ │ └── themes.xml
│ └── test
│ └── java
│ └── icu
│ └── bughub
│ └── app
│ └── composebasic
│ └── ExampleUnitTest.kt
├── build.gradle
├── gradle.properties
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── settings.gradle
/App/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 | local.properties
16 |
--------------------------------------------------------------------------------
/App/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/App/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/App/.idea/deploymentTargetDropDown.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/App/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
20 |
21 |
--------------------------------------------------------------------------------
/App/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/App/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
--------------------------------------------------------------------------------
/App/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/App/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/App/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | id 'org.jetbrains.kotlin.android'
4 | id 'org.jetbrains.kotlin.plugin.parcelize'
5 | }
6 |
7 | android {
8 | compileSdk 32
9 |
10 | defaultConfig {
11 | applicationId "icu.bughub.app.app"
12 | minSdk 23
13 | targetSdk 32
14 | versionCode 1
15 | versionName "1.0"
16 |
17 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
18 | vectorDrawables {
19 | useSupportLibrary true
20 | }
21 |
22 | ndk {
23 | abiFilters "arm64-v8a"
24 | }
25 | }
26 |
27 | buildTypes {
28 | release {
29 | minifyEnabled false
30 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
31 | }
32 | }
33 | compileOptions {
34 | sourceCompatibility JavaVersion.VERSION_1_8
35 | targetCompatibility JavaVersion.VERSION_1_8
36 | }
37 | kotlinOptions {
38 | jvmTarget = '1.8'
39 | }
40 | buildFeatures {
41 | compose true
42 | }
43 | composeOptions {
44 | kotlinCompilerExtensionVersion compose_version
45 | }
46 | packagingOptions {
47 | resources {
48 | excludes += '/META-INF/{AL2.0,LGPL2.1}'
49 | }
50 |
51 | pickFirst '**/libc++_shared.so'
52 | doNotStrip "*/arm64-v8a/libYTCommon.so"
53 | }
54 | }
55 |
56 | dependencies {
57 |
58 | implementation 'androidx.core:core-ktx:1.7.0'
59 | implementation "androidx.compose.ui:ui:$compose_version"
60 | implementation "androidx.compose.material:material:$compose_version"
61 | implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
62 |
63 |
64 | def lifecycle_version = "2.4.1"
65 |
66 | implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycle_version"
67 | implementation "androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version"
68 | implementation 'androidx.activity:activity-compose:1.4.0'
69 |
70 | //accompanist
71 | def accompanist_version = "0.24.3-alpha"
72 | implementation "com.google.accompanist:accompanist-systemuicontroller:$accompanist_version"
73 | implementation "com.google.accompanist:accompanist-insets:$accompanist_version"
74 | implementation "com.google.accompanist:accompanist-pager:$accompanist_version"
75 | implementation "com.google.accompanist:accompanist-navigation-animation:$accompanist_version"
76 | implementation "com.google.accompanist:accompanist-placeholder-material:$accompanist_version"
77 | implementation "com.google.accompanist:accompanist-swiperefresh:$accompanist_version"
78 |
79 | //图标扩展
80 | implementation "androidx.compose.material:material-icons-extended:$compose_version"
81 |
82 | implementation "androidx.constraintlayout:constraintlayout-compose:1.0.0"
83 |
84 |
85 | //图片加载
86 | implementation("io.coil-kt:coil-compose:2.0.0-rc01")
87 |
88 | //webview
89 | implementation project(path: ':webview')
90 |
91 |
92 | //播放器
93 | implementation 'com.tencent.liteav:LiteAVSDK_Player:latest.release'
94 |
95 | //datastore
96 | implementation "androidx.datastore:datastore-preferences:1.0.0"
97 |
98 | def retrofit = "2.9.0"
99 | //网络请求框架
100 | implementation "com.squareup.retrofit2:retrofit:$retrofit"
101 | //GSON
102 | // implementation "com.squareup.retrofit2:converter-gson:$retrofit"
103 | //moshi
104 | implementation "com.squareup.retrofit2:converter-moshi:$retrofit"
105 |
106 | def moshi_version = "1.13.0"
107 | implementation "com.squareup.moshi:moshi-kotlin:$moshi_version"
108 |
109 |
110 | testImplementation 'junit:junit:4.13.2'
111 | androidTestImplementation 'androidx.test.ext:junit:1.1.3'
112 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
113 | androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
114 | debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
115 | }
--------------------------------------------------------------------------------
/App/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
23 | -keep class com.tencent.** { *; }
--------------------------------------------------------------------------------
/App/app/src/androidTest/java/icu/bughub/app/app/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.app
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("icu.bughub.app.app", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/App/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
16 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
--------------------------------------------------------------------------------
/App/app/src/main/java/icu/bughub/app/app/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.app
2 |
3 | import android.os.Build
4 | import android.os.Bundle
5 | import android.util.Log
6 | import android.view.View
7 | import androidx.activity.ComponentActivity
8 | import androidx.activity.compose.setContent
9 | import androidx.compose.foundation.layout.fillMaxSize
10 | import androidx.compose.material.MaterialTheme
11 | import androidx.compose.material.Surface
12 | import androidx.compose.material.Text
13 | import androidx.compose.runtime.Composable
14 | import androidx.compose.ui.Modifier
15 | import androidx.compose.ui.graphics.Color
16 | import androidx.compose.ui.graphics.toArgb
17 | import androidx.compose.ui.tooling.preview.Preview
18 | import androidx.core.view.WindowCompat
19 | import icu.bughub.app.app.ui.components.NavHostApp
20 | import icu.bughub.app.app.ui.screens.MainFrame
21 | import icu.bughub.app.app.ui.theme.AppTheme
22 |
23 | class MainActivity : ComponentActivity() {
24 | override fun onCreate(savedInstanceState: Bundle?) {
25 | super.onCreate(savedInstanceState)
26 |
27 | //获取状态栏高度
28 | // var statusBarHeight = 0
29 | // val resourceId = resources.getIdentifier("status_bar_height", "dimen", "android")
30 | // if (resourceId > 0) {
31 | // statusBarHeight = resources.getDimensionPixelSize(resourceId)
32 | // }
33 |
34 | //处理不同机型,状态栏不透明问题
35 | // window.statusBarColor = Color.Transparent.value.toInt()
36 | // //处理不同机型,导航栏遮盖内容问题
37 | // window.decorView.systemUiVisibility =
38 | // View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
39 |
40 | //让内容,显示在状态栏和系统导航栏后面:状态栏和导航栏会遮盖部分内容
41 | WindowCompat.setDecorFitsSystemWindows(window, false)
42 |
43 | setContent {
44 | AppTheme {
45 | // A surface container using the 'background' color from the theme
46 | Surface(
47 | modifier = Modifier.fillMaxSize(),
48 | color = MaterialTheme.colors.background
49 | ) {
50 | NavHostApp()
51 | }
52 | }
53 | }
54 | }
55 | }
56 |
57 | @Composable
58 | fun Greeting(name: String) {
59 | Text(text = "Hello $name!")
60 | }
61 |
62 | @Preview(showBackground = true)
63 | @Composable
64 | fun DefaultPreview() {
65 | AppTheme {
66 | Greeting("Android")
67 | }
68 | }
--------------------------------------------------------------------------------
/App/app/src/main/java/icu/bughub/app/app/compositionLocal/CompositionLocal.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.app.compositionLocal
2 |
3 | import androidx.compose.runtime.compositionLocalOf
4 | import icu.bughub.app.app.viewmodel.UserViewModel
5 |
6 | val LocalUserViewModel =
7 | compositionLocalOf { error("User View Model Context Not Found") }
--------------------------------------------------------------------------------
/App/app/src/main/java/icu/bughub/app/app/extension/LazyListStateExtension.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.app.extension
2 |
3 | import android.util.Log
4 | import androidx.compose.foundation.lazy.LazyListState
5 | import androidx.compose.runtime.*
6 |
7 | @Composable
8 | fun LazyListState.OnBottomReached(buffer: Int = 0, loadMore: () -> Unit) {
9 |
10 | require(buffer >= 0) { "buffer 值必须是正整数" }
11 |
12 | //是否应该加载更多的状态
13 | val shouldLoadMore = remember {
14 | //由另一个状态计算派生
15 | derivedStateOf {
16 |
17 | //获取最后显示的 item
18 | val lastVisibleItem =
19 | layoutInfo.visibleItemsInfo.lastOrNull() ?: return@derivedStateOf true
20 |
21 | //如果最后显示的 item 是最后一个 item 的话,说明已经触底,需要加载更多
22 | lastVisibleItem.index == layoutInfo.totalItemsCount - 1 - buffer
23 |
24 | }
25 | }
26 |
27 | LaunchedEffect(shouldLoadMore) {
28 | //详见文档:https://developer.android.com/jetpack/compose/side-effects#snapshotFlow
29 | snapshotFlow { shouldLoadMore.value }
30 | .collect {
31 | if (it) {
32 | loadMore()
33 | }
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/App/app/src/main/java/icu/bughub/app/app/model/Network.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.app.model
2 |
3 | import com.squareup.moshi.Moshi
4 | import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
5 | import retrofit2.Retrofit
6 | import retrofit2.converter.moshi.MoshiConverterFactory
7 |
8 | object Network {
9 |
10 | //文档地址:https://docs.apipost.cn/preview/1a28e17fa3c8f473/16838456ae6dc5c7
11 | //mock 数据请求 url
12 | private const val baseUrl =
13 | "https://mock.apipost.cn/app/mock/project/ced69cf2-9206-4a42-895e-dd7442a888df/"
14 |
15 | private val retrofit = Retrofit.Builder()
16 | .baseUrl(baseUrl)
17 | .addConverterFactory(
18 | MoshiConverterFactory.create(
19 | Moshi.Builder()
20 | .add(KotlinJsonAdapterFactory())
21 | .build()
22 | )
23 | ).build()
24 |
25 | fun createService(clazz: Class): T {
26 | return retrofit.create(clazz)
27 | }
28 | }
--------------------------------------------------------------------------------
/App/app/src/main/java/icu/bughub/app/app/model/entity/ArticleEntity.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.app.model.entity
2 |
3 | import com.squareup.moshi.Json
4 | import com.squareup.moshi.JsonClass
5 |
6 | @JsonClass(generateAdapter = true)
7 | data class ArticleEntity(
8 | val title: String,
9 | var source: String,
10 | @Json(name = "time")
11 | var timestamp: String,
12 | var content: String? = ""
13 | )
14 |
15 | data class ArticleListResponse(val data: List?) : BaseResponse()
16 |
17 | data class ArticleInfoResponse(val data: ArticleEntity?) : BaseResponse()
--------------------------------------------------------------------------------
/App/app/src/main/java/icu/bughub/app/app/model/entity/BaseResponse.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.app.model.entity
2 |
3 | open class BaseResponse {
4 | var code: Int = -1
5 | var message: String = ""
6 | }
--------------------------------------------------------------------------------
/App/app/src/main/java/icu/bughub/app/app/model/entity/Category.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.app.model.entity
2 |
3 | /**
4 | * 分类
5 | *
6 | * @property title
7 | */
8 | data class Category(
9 | val title: String,
10 | val id: String
11 | )
12 |
13 | /**
14 | * Category Response
15 | *
16 | * @property data
17 | */
18 | data class CategoryResponse(var data: List) : BaseResponse() {}
19 |
20 |
21 |
--------------------------------------------------------------------------------
/App/app/src/main/java/icu/bughub/app/app/model/entity/DataType.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.app.model.entity
2 |
3 | import androidx.compose.ui.graphics.vector.ImageVector
4 |
5 | /**
6 | * 数据类型
7 | *
8 | * @property title
9 | * @property icon
10 | */
11 | data class DataType(
12 | var title: String,
13 | var icon: ImageVector
14 | )
15 |
--------------------------------------------------------------------------------
/App/app/src/main/java/icu/bughub/app/app/model/entity/NavigationItem.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.app.model.entity
2 |
3 | import androidx.compose.ui.graphics.vector.ImageVector
4 |
5 | /**
6 | * 导航栏对象
7 | *
8 | * @property title 导航栏的标题
9 | * @property icon 导航栏图标
10 | */
11 | data class NavigationItem(
12 | val title: String, //底部导航栏的标题
13 | val icon: ImageVector//底部导航栏图标
14 | )
15 |
--------------------------------------------------------------------------------
/App/app/src/main/java/icu/bughub/app/app/model/entity/SwiperEntity.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.app.model.entity
2 |
3 | import com.squareup.moshi.Json
4 | import com.squareup.moshi.JsonClass
5 |
6 | @JsonClass(generateAdapter = true)
7 | data class SwiperEntity(
8 | @Json(name = "imgUrl") val imageUrl: String,
9 | val title: String? = ""
10 | )
11 |
12 |
13 | data class SwiperResponse(val data: List?) : BaseResponse()
--------------------------------------------------------------------------------
/App/app/src/main/java/icu/bughub/app/app/model/entity/UserInfoEntity.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.app.model.entity
2 |
3 | data class UserInfoEntity(val userName: String)
4 |
5 | data class UserInfoResponse(val data: UserInfoEntity?) : BaseResponse()
6 |
--------------------------------------------------------------------------------
/App/app/src/main/java/icu/bughub/app/app/model/entity/VideoEntity.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.app.model.entity
2 |
3 | import com.squareup.moshi.Json
4 | import com.squareup.moshi.JsonClass
5 |
6 | @JsonClass(generateAdapter = true)
7 | data class VideoEntity(
8 | val title: String,
9 | val type: String? = "",
10 | val duration: String,
11 | @Json(name = "cover")
12 | val imageUrl: String,
13 | val video: String? = "",
14 | val desc: String? = ""
15 | )
16 |
17 | data class VideoListResponse(val data: List?) : BaseResponse()
18 |
19 | data class VideoInfoResponse(val data: VideoEntity?) : BaseResponse()
--------------------------------------------------------------------------------
/App/app/src/main/java/icu/bughub/app/app/model/service/ArticleService.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.app.model.service
2 |
3 | import icu.bughub.app.app.model.Network
4 | import icu.bughub.app.app.model.entity.ArticleInfoResponse
5 | import icu.bughub.app.app.model.entity.ArticleListResponse
6 | import retrofit2.http.GET
7 | import retrofit2.http.Query
8 |
9 | interface ArticleService {
10 |
11 | @GET("article/list")
12 | suspend fun list(
13 | @Query("pageOffset") pageOffset: Int,
14 | @Query("pageSize") pageSize: Int
15 | ): ArticleListResponse
16 |
17 | @GET("article/info")
18 | suspend fun info(@Query("id") id: String): ArticleInfoResponse
19 |
20 | companion object {
21 | fun instance(): ArticleService {
22 | return Network.createService(ArticleService::class.java)
23 | }
24 | }
25 | }
--------------------------------------------------------------------------------
/App/app/src/main/java/icu/bughub/app/app/model/service/HomeService.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.app.model.service
2 |
3 | import icu.bughub.app.app.model.Network
4 | import icu.bughub.app.app.model.entity.CategoryResponse
5 | import icu.bughub.app.app.model.entity.SwiperResponse
6 | import retrofit2.http.GET
7 |
8 | interface HomeService {
9 |
10 | @GET("category/list")
11 | suspend fun category(): CategoryResponse
12 |
13 | @GET("recommand/banner")
14 | suspend fun banner(): SwiperResponse
15 |
16 | companion object {
17 | fun instance(): HomeService {
18 | return Network.createService(HomeService::class.java)
19 | }
20 | }
21 |
22 |
23 | }
--------------------------------------------------------------------------------
/App/app/src/main/java/icu/bughub/app/app/model/service/UserInfoManager.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.app.model.service
2 |
3 | import android.content.Context
4 | import androidx.datastore.core.DataStore
5 | import androidx.datastore.preferences.core.Preferences
6 | import androidx.datastore.preferences.core.booleanPreferencesKey
7 | import androidx.datastore.preferences.core.edit
8 | import androidx.datastore.preferences.core.stringPreferencesKey
9 | import androidx.datastore.preferences.preferencesDataStore
10 | import kotlinx.coroutines.flow.Flow
11 | import kotlinx.coroutines.flow.map
12 |
13 | class UserInfoManager(private val context: Context) {
14 |
15 | companion object {
16 | private val Context.userStore: DataStore by preferencesDataStore("user_store")
17 |
18 | val LOGGED = booleanPreferencesKey("LOGGED")
19 | val USERNAME = stringPreferencesKey("USERNAME")
20 | }
21 |
22 | val logged: Flow = context.userStore.data.map { it[LOGGED] ?: false }
23 | val userName: Flow = context.userStore.data.map { it[USERNAME] ?: "" }
24 |
25 | /**
26 | * 存储用户信息
27 | *
28 | * @param userName
29 | */
30 | suspend fun save(userName: String) {
31 | context.userStore.edit {
32 | it[LOGGED] = userName.isNotEmpty()
33 | it[USERNAME] = userName
34 | }
35 | }
36 |
37 | /**
38 | * 清空用户登录数据
39 | *
40 | */
41 | suspend fun clear() {
42 | context.userStore.edit {
43 | it[LOGGED] = false
44 | it[USERNAME] = ""
45 | }
46 | }
47 |
48 | }
--------------------------------------------------------------------------------
/App/app/src/main/java/icu/bughub/app/app/model/service/UserService.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.app.model.service
2 |
3 | import icu.bughub.app.app.model.Network
4 | import icu.bughub.app.app.model.entity.UserInfoResponse
5 | import retrofit2.http.*
6 |
7 | interface UserService {
8 | @FormUrlEncoded
9 | @POST("user/signIn")
10 | suspend fun signIn(
11 | @Field("userName") useName: String,
12 | @Field("password") password: String,
13 | ): UserInfoResponse
14 |
15 | companion object {
16 | fun instance(): UserService {
17 | return Network.createService(UserService::class.java)
18 | }
19 | }
20 |
21 | }
--------------------------------------------------------------------------------
/App/app/src/main/java/icu/bughub/app/app/model/service/VideoService.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.app.model.service
2 |
3 | import icu.bughub.app.app.model.Network
4 | import icu.bughub.app.app.model.entity.VideoInfoResponse
5 | import icu.bughub.app.app.model.entity.VideoListResponse
6 | import retrofit2.http.GET
7 | import retrofit2.http.Query
8 |
9 | interface VideoService {
10 |
11 | @GET("video/list")
12 | suspend fun list(
13 | @Query("pageOffset") pageOffset: Int,
14 | @Query("pageSize") pageSize: Int
15 | ): VideoListResponse
16 |
17 | @GET("video/info")
18 | suspend fun info(
19 | @Query("id") id: String
20 | ): VideoInfoResponse
21 |
22 | companion object {
23 | fun instance(): VideoService {
24 | return Network.createService(VideoService::class.java)
25 | }
26 | }
27 |
28 | }
--------------------------------------------------------------------------------
/App/app/src/main/java/icu/bughub/app/app/ui/components/ArticleItem.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.app.ui.components
2 |
3 |
4 | import androidx.compose.foundation.layout.*
5 | import androidx.compose.material.Divider
6 | import androidx.compose.material.Text
7 | import androidx.compose.runtime.Composable
8 | import androidx.compose.ui.Modifier
9 | import androidx.compose.ui.graphics.Color
10 | import androidx.compose.ui.text.style.TextOverflow
11 | import androidx.compose.ui.tooling.preview.Preview
12 | import androidx.compose.ui.unit.dp
13 | import androidx.compose.ui.unit.sp
14 | import com.google.accompanist.placeholder.PlaceholderHighlight
15 | import com.google.accompanist.placeholder.material.placeholder
16 | import com.google.accompanist.placeholder.material.shimmer
17 | import icu.bughub.app.app.model.entity.ArticleEntity
18 |
19 |
20 | /**
21 | * 文章列表 item
22 | *
23 | * @param article
24 | * @param modifier
25 | */
26 | @Composable
27 | fun ArticleItem(article: ArticleEntity, loaded: Boolean, modifier: Modifier = Modifier) {
28 |
29 | Column(modifier = modifier.padding(8.dp)) {
30 | Text(
31 | text = article.title,
32 | color = Color(0xFF333333),
33 | fontSize = 16.sp,
34 | maxLines = 2,
35 | overflow = TextOverflow.Ellipsis,
36 | modifier = Modifier
37 | .padding(bottom = 8.dp)
38 | .placeholder(visible = !loaded, highlight = PlaceholderHighlight.shimmer())
39 | )
40 |
41 | Row(horizontalArrangement = Arrangement.SpaceBetween, modifier = Modifier.fillMaxWidth()) {
42 | Text(
43 | "来源:${article.source}",
44 | color = Color(0xFF999999),
45 | fontSize = 10.sp,
46 | maxLines = 1,
47 | overflow = TextOverflow.Ellipsis,
48 | modifier = Modifier
49 | .placeholder(visible = !loaded, highlight = PlaceholderHighlight.shimmer())
50 | )
51 |
52 | Text(
53 | article.timestamp,
54 | color = Color(0xFF999999),
55 | fontSize = 10.sp,
56 | maxLines = 1,
57 | overflow = TextOverflow.Ellipsis,
58 | modifier = Modifier
59 | .placeholder(visible = !loaded, highlight = PlaceholderHighlight.shimmer())
60 | )
61 | }
62 |
63 | Spacer(Modifier.height(8.dp))
64 |
65 | Divider()
66 | }
67 |
68 | }
69 |
70 |
--------------------------------------------------------------------------------
/App/app/src/main/java/icu/bughub/app/app/ui/components/ChartView.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.app.ui.components
2 |
3 |
4 | import androidx.compose.foundation.Canvas
5 | import androidx.compose.foundation.layout.size
6 | import androidx.compose.runtime.Composable
7 | import androidx.compose.ui.Modifier
8 | import androidx.compose.ui.geometry.Offset
9 | import androidx.compose.ui.graphics.Color
10 | import androidx.compose.ui.graphics.drawscope.Stroke
11 | import androidx.compose.ui.platform.LocalConfiguration
12 | import androidx.compose.ui.tooling.preview.Preview
13 | import androidx.compose.ui.unit.dp
14 |
15 |
16 | @Composable
17 | fun ChartView(points: List, modifier: Modifier = Modifier) {
18 |
19 | //每一行的高度DP
20 | val heightForRow = 24
21 |
22 | //总行数
23 | val countForRow = 5
24 |
25 | //小圆圈半径DP
26 | val circleRadius = 2.5
27 |
28 | val perY = 8 // 24 * 5 / 15 每8dp代表1积分,也就是每一行3积分
29 |
30 | //画布宽度 = 屏幕宽度 - padding * 2
31 | val canvasWidth = LocalConfiguration.current.screenWidthDp - 8 * 2
32 |
33 | //画布高度 = 行高 * 总行数 + 小圆圈直径
34 | val canvasHeight = heightForRow * countForRow + circleRadius * 2
35 |
36 | //7平分的宽度
37 | val averageOfWidth = canvasWidth / 7
38 |
39 | Canvas(
40 | modifier = modifier.size(
41 | width = canvasWidth.dp,
42 | height = canvasHeight.dp
43 | )
44 | ) {
45 | //画背景横线
46 | for (index in 0..countForRow) {
47 | //行高 * index + 小圆圈半径
48 | val y = (heightForRow * index + circleRadius).dp.toPx()
49 | drawLine(
50 | Color(0xFFEEEEEE),
51 | start = Offset(0f, y),
52 | end = Offset(size.width, y),
53 | strokeWidth = 2.5f
54 | )
55 | }
56 |
57 | //画圆圈、拆线
58 | for (index in 0 until points.count()) {
59 | //x: 7等分宽度 * index + 7等分宽度 / 2
60 | //y: 行高 * 行数 - 积分 * 每一积分代表的 dp 值 - 小圆圈半径
61 | val circleCenter = Offset(
62 | (averageOfWidth * index + averageOfWidth / 2).dp.toPx(),
63 | (heightForRow * countForRow - points[index] * perY + circleRadius).dp.toPx()
64 | )
65 | drawCircle(
66 | Color(0xFF149EE7),
67 | radius = circleRadius.dp.toPx(),
68 | center = circleCenter,
69 | style = Stroke(width = 5f)
70 | )
71 |
72 | if (index < points.count() - 1) {
73 | //下一个点的坐标:也就是circleCenter的计算方法中 index + 1 即可
74 | val nextPointOffset = Offset(
75 | (averageOfWidth * (index + 1) + averageOfWidth / 2).dp.toPx(),
76 | (heightForRow * countForRow - points[(index + 1)] * perY + circleRadius).dp.toPx()
77 | )
78 |
79 | drawLine(
80 | Color(0xFF149EE7),
81 | start = circleCenter,
82 | end = nextPointOffset,
83 | strokeWidth = 5f
84 | )
85 | }
86 | }
87 | }
88 |
89 | }
90 |
91 |
--------------------------------------------------------------------------------
/App/app/src/main/java/icu/bughub/app/app/ui/components/CircleRing.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.app.ui.components
2 |
3 |
4 | import androidx.compose.foundation.Canvas
5 | import androidx.compose.foundation.layout.size
6 | import androidx.compose.runtime.Composable
7 | import androidx.compose.ui.Modifier
8 | import androidx.compose.ui.graphics.Color
9 | import androidx.compose.ui.graphics.StrokeCap
10 | import androidx.compose.ui.graphics.drawscope.Stroke
11 | import androidx.compose.ui.graphics.drawscope.rotate
12 | import androidx.compose.ui.tooling.preview.Preview
13 | import androidx.compose.ui.unit.dp
14 | import icu.bughub.app.app.viewmodel.TaskViewModel
15 |
16 |
17 | @Composable
18 | fun CircleRing(boxWidthDp: Int, vm: TaskViewModel) {
19 |
20 | val strokeWidth = 30f
21 |
22 | Canvas(modifier = Modifier.size(boxWidthDp.dp)) {
23 | //两个方案
24 | //1、使用startAngle = -10f 和 sweepAngle = 220f 并使用 rotate(180f)进行翻转
25 | //2、startAngle = 160f,sweepAngle = 220f,
26 | drawArc(
27 | Color(0, 0, 0, 15),
28 | startAngle = 160f,
29 | sweepAngle = 220f,
30 | useCenter = false,
31 | style = Stroke(strokeWidth, cap = StrokeCap.Round)
32 | )
33 |
34 | drawArc(
35 | Color.White,
36 | startAngle = 160f,
37 | sweepAngle = vm.pointOfYearPercent,
38 | useCenter = false,
39 | style = Stroke(strokeWidth, cap = StrokeCap.Round)
40 | )
41 | }
42 |
43 | }
44 |
45 |
46 |
--------------------------------------------------------------------------------
/App/app/src/main/java/icu/bughub/app/app/ui/components/DailyTaskContent.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.app.ui.components
2 |
3 |
4 | import android.util.Log
5 | import androidx.compose.foundation.BorderStroke
6 | import androidx.compose.foundation.clickable
7 | import androidx.compose.foundation.layout.*
8 | import androidx.compose.foundation.shape.CircleShape
9 | import androidx.compose.foundation.text.InlineTextContent
10 | import androidx.compose.foundation.text.appendInlineContent
11 | import androidx.compose.material.*
12 | import androidx.compose.material.icons.Icons
13 | import androidx.compose.material.icons.filled.HelpOutline
14 | import androidx.compose.runtime.Composable
15 | import androidx.compose.ui.Alignment
16 | import androidx.compose.ui.Modifier
17 | import androidx.compose.ui.graphics.Color
18 | import androidx.compose.ui.text.Placeholder
19 | import androidx.compose.ui.text.PlaceholderVerticalAlign
20 | import androidx.compose.ui.text.buildAnnotatedString
21 | import androidx.compose.ui.tooling.preview.Preview
22 | import androidx.compose.ui.unit.dp
23 | import androidx.compose.ui.unit.sp
24 |
25 |
26 | @Composable
27 | fun DailyTaskContent() {
28 |
29 | DailyTaskItem(
30 | "登录",
31 | "5积分/每日首次登录",
32 | "已获5积分 / 每日上限5积分",
33 | 1f
34 | )
35 |
36 | DailyTaskItem(
37 | "文章学习",
38 | "10积分/每有效阅读1篇文章",
39 | "已获50积分/每日上限100积分",
40 | 0.7f
41 | )
42 |
43 | DailyTaskItem(
44 | "视听学习",
45 | "10积分/每有效观看视频或收听音频累计",
46 | "已获50积分/每日上限100积分",
47 | 0.5f
48 | )
49 |
50 | }
51 |
52 | @Composable
53 | fun DailyTaskItem(title: String, secondaryText: String, desc: String, percent: Float) {
54 |
55 | val inlineContentId = "inlineContentId"
56 |
57 | val secondaryAnnotatedText = buildAnnotatedString {
58 | append(secondaryText)
59 | appendInlineContent(inlineContentId, "[icon]")
60 | }
61 |
62 | val inlineContent = mapOf(
63 | Pair(inlineContentId,
64 | InlineTextContent(
65 | Placeholder(
66 | width = 14.sp,
67 | height = 14.sp,
68 | placeholderVerticalAlign = PlaceholderVerticalAlign.AboveBaseline
69 | )
70 | ) {
71 | Icon(
72 | imageVector = Icons.Default.HelpOutline,
73 | contentDescription = null,
74 | modifier = Modifier.clickable {
75 | Log.i("===", "点击了问号")
76 | })
77 | })
78 | )
79 |
80 | Row(
81 | modifier = Modifier
82 | .fillMaxWidth()
83 | .padding(top = 12.dp),
84 | horizontalArrangement = Arrangement.SpaceBetween,
85 | verticalAlignment = Alignment.CenterVertically
86 | ) {
87 |
88 | Column(modifier = Modifier.weight(7.5f)) {
89 | Text(title, fontSize = 16.sp, color = Color(0xFF333333))
90 | Text(
91 | secondaryAnnotatedText,
92 | inlineContent = inlineContent,
93 | fontSize = 14.sp,
94 | color = Color(0xFF333333)
95 | )
96 | Row(verticalAlignment = Alignment.CenterVertically) {
97 | LinearProgressIndicator(progress = percent, modifier = Modifier.weight(3f))
98 | Text(
99 | text = desc,
100 | fontSize = 10.sp,
101 | color = Color(0xFF333333),
102 | modifier = Modifier
103 | .weight(7f, fill = false)
104 | .padding(horizontal = 8.dp)
105 | )
106 | }
107 | }
108 |
109 | OutlinedButton(
110 | onClick = { },
111 | border = if (percent >= 1f) ButtonDefaults.outlinedBorder else BorderStroke(
112 | 1.dp,
113 | Color(0xFFFF5900)
114 | ),
115 | shape = CircleShape,
116 | colors = ButtonDefaults.outlinedButtonColors(contentColor = Color(0xFFFF5900)),
117 | modifier = Modifier
118 | .weight(2.5f),
119 | enabled = (percent < 1f)
120 | ) {
121 | Text(text = "去学习")
122 | }
123 |
124 | }
125 |
126 | }
127 |
128 |
--------------------------------------------------------------------------------
/App/app/src/main/java/icu/bughub/app/app/ui/components/NavHostApp.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.app.ui.components
2 |
3 |
4 | import android.util.Log
5 | import androidx.compose.animation.AnimatedContentScope
6 | import androidx.compose.animation.ExperimentalAnimationApi
7 | import androidx.compose.animation.core.tween
8 | import androidx.compose.runtime.Composable
9 | import androidx.compose.runtime.CompositionLocalProvider
10 | import androidx.compose.ui.platform.LocalContext
11 | import androidx.compose.ui.tooling.preview.Preview
12 | import com.google.accompanist.insets.ProvideWindowInsets
13 | import com.google.accompanist.navigation.animation.AnimatedNavHost
14 | import com.google.accompanist.navigation.animation.composable
15 | import com.google.accompanist.navigation.animation.rememberAnimatedNavController
16 | import icu.bughub.app.app.compositionLocal.LocalUserViewModel
17 | import icu.bughub.app.app.ui.navigation.Destinations
18 | import icu.bughub.app.app.ui.screens.ArticleDetailScreen
19 | import icu.bughub.app.app.ui.screens.LoginScreen
20 | import icu.bughub.app.app.ui.screens.MainFrame
21 | import icu.bughub.app.app.ui.screens.VideoDetailScreen
22 | import icu.bughub.app.app.viewmodel.UserViewModel
23 |
24 |
25 | /**
26 | * 导航控制器
27 | *
28 | */
29 | @OptIn(ExperimentalAnimationApi::class)
30 | @Composable
31 | fun NavHostApp() {
32 |
33 | val navController = rememberAnimatedNavController()
34 | ProvideWindowInsets {
35 |
36 | CompositionLocalProvider(LocalUserViewModel provides UserViewModel(LocalContext.current)) {
37 |
38 | val userViewModel = LocalUserViewModel.current
39 |
40 | AnimatedNavHost(
41 | navController = navController,
42 | startDestination = Destinations.HomeFrame.route
43 | ) {
44 |
45 | //首页大框架
46 | composable(
47 | Destinations.HomeFrame.route,
48 | enterTransition = {
49 | slideIntoContainer(
50 | AnimatedContentScope.SlideDirection.Right
51 | )
52 | },
53 | exitTransition = {
54 | slideOutOfContainer(
55 | AnimatedContentScope.SlideDirection.Left
56 | )
57 | },
58 | ) {
59 | MainFrame(onNavigateToArticle = {
60 | Log.i("===", "onNavigateToArticle")
61 | navController.navigate(Destinations.ArticleDetail.route)
62 | }, onNavigateToVideo = {
63 | navController.navigate(Destinations.VideoDetail.route)
64 | }, onNavigateToStudyHistory = {
65 | if (userViewModel.logged) {
66 | //已登录
67 | } else {
68 | //未登录
69 | navController.navigate(Destinations.Login.route)
70 | }
71 | })
72 | }
73 |
74 | //文章详情页
75 | composable(
76 | Destinations.ArticleDetail.route,
77 | enterTransition = {
78 | slideIntoContainer(AnimatedContentScope.SlideDirection.Left)
79 | },
80 | exitTransition = {
81 | slideOutOfContainer(AnimatedContentScope.SlideDirection.Right)
82 | },
83 | ) {
84 | ArticleDetailScreen(onBack = {
85 | navController.popBackStack()
86 | })
87 | }
88 |
89 | //视频详情页
90 | composable(
91 | Destinations.VideoDetail.route,
92 | enterTransition = {
93 | slideIntoContainer(AnimatedContentScope.SlideDirection.Left)
94 | },
95 | exitTransition = {
96 | slideOutOfContainer(AnimatedContentScope.SlideDirection.Right)
97 | },
98 | ) {
99 | VideoDetailScreen(onBack = {
100 | navController.popBackStack()
101 | })
102 | }
103 |
104 | composable(
105 | Destinations.Login.route,
106 | enterTransition = {
107 | slideIntoContainer(AnimatedContentScope.SlideDirection.Left)
108 | },
109 | exitTransition = {
110 | slideOutOfContainer(AnimatedContentScope.SlideDirection.Right)
111 | },
112 | ) {
113 | LoginScreen {
114 | navController.popBackStack()
115 | }
116 | }
117 |
118 | }
119 | }
120 | }
121 | }
122 |
123 | @Preview
124 | @Composable
125 | fun NavHostAppPreview() {
126 | NavHostApp()
127 | }
128 |
129 |
--------------------------------------------------------------------------------
/App/app/src/main/java/icu/bughub/app/app/ui/components/NotificationContent.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.app.ui.components
2 |
3 |
4 | import androidx.compose.foundation.background
5 | import androidx.compose.foundation.layout.*
6 | import androidx.compose.foundation.shape.RoundedCornerShape
7 | import androidx.compose.material.Text
8 | import androidx.compose.runtime.Composable
9 | import androidx.compose.runtime.DisposableEffect
10 | import androidx.compose.runtime.rememberCoroutineScope
11 | import androidx.compose.ui.Alignment
12 | import androidx.compose.ui.Modifier
13 | import androidx.compose.ui.draw.clip
14 | import androidx.compose.ui.graphics.Color
15 | import androidx.compose.ui.text.style.TextOverflow
16 | import androidx.compose.ui.tooling.preview.Preview
17 | import androidx.compose.ui.unit.dp
18 | import androidx.compose.ui.unit.sp
19 | import com.google.accompanist.pager.ExperimentalPagerApi
20 | import com.google.accompanist.pager.VerticalPager
21 | import com.google.accompanist.pager.rememberPagerState
22 | import icu.bughub.app.app.viewmodel.MainViewModel
23 | import kotlinx.coroutines.launch
24 | import java.util.*
25 |
26 |
27 | @OptIn(ExperimentalPagerApi::class)
28 | @Composable
29 | fun NotificationContent(vm: MainViewModel) {
30 |
31 | //虚拟页数
32 | val virtualCount = Int.MAX_VALUE
33 |
34 | //实际页数
35 | val actualCount = vm.notifications.size
36 |
37 | //初始图片下标
38 | val initialIndex = virtualCount / 2
39 |
40 | val pagerState = rememberPagerState(initialPage = initialIndex)
41 |
42 | val coroutineScope = rememberCoroutineScope()
43 |
44 | DisposableEffect(Unit) {
45 | val timer = Timer()
46 |
47 | timer.schedule(object : TimerTask() {
48 | override fun run() {
49 | coroutineScope.launch { pagerState.animateScrollToPage(pagerState.currentPage + 1) }
50 | }
51 | }, 2000, 3000)
52 |
53 | onDispose {
54 | timer.cancel()
55 | }
56 | }
57 |
58 | Row(
59 | modifier = Modifier
60 | .padding(8.dp)
61 | .clip(RoundedCornerShape(8.dp))
62 | .background(Color(0x22149EE7))
63 | .height(45.dp)
64 | .padding(horizontal = 8.dp),
65 | verticalAlignment = Alignment.CenterVertically,
66 | horizontalArrangement = Arrangement.SpaceBetween
67 | ) {
68 | Text(
69 | text = "最新活动",
70 | color = Color(0xFF149EE7),
71 | fontSize = 14.sp
72 | )
73 |
74 | Spacer(modifier = Modifier.width(8.dp))
75 |
76 | VerticalPager(
77 | count = virtualCount,
78 | state = pagerState,
79 | modifier = Modifier.weight(1f),
80 | horizontalAlignment = Alignment.Start
81 | ) { index ->
82 |
83 | val actualIndex =
84 | (index - initialIndex).floorMod(actualCount)
85 |
86 | Text(
87 | text = vm.notifications[actualIndex],
88 | color = Color(0xFF333333),
89 | fontSize = 14.sp,
90 | maxLines = 1,
91 | overflow = TextOverflow.Ellipsis,
92 | )
93 | }
94 |
95 | Spacer(modifier = Modifier.width(8.dp))
96 |
97 | Text(
98 | text = "更多",
99 | color = Color(0xFF666666),
100 | fontSize = 14.sp,
101 | maxLines = 1,
102 | )
103 | }
104 |
105 | }
106 |
107 |
108 |
--------------------------------------------------------------------------------
/App/app/src/main/java/icu/bughub/app/app/ui/components/SwiperContent.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.app.ui.components
2 |
3 |
4 | import androidx.compose.foundation.layout.aspectRatio
5 | import androidx.compose.foundation.layout.fillMaxWidth
6 | import androidx.compose.foundation.layout.padding
7 | import androidx.compose.foundation.shape.RoundedCornerShape
8 | import androidx.compose.runtime.Composable
9 | import androidx.compose.runtime.DisposableEffect
10 | import androidx.compose.runtime.LaunchedEffect
11 | import androidx.compose.runtime.rememberCoroutineScope
12 | import androidx.compose.ui.Modifier
13 | import androidx.compose.ui.draw.clip
14 | import androidx.compose.ui.layout.ContentScale
15 | import androidx.compose.ui.tooling.preview.Preview
16 | import androidx.compose.ui.unit.dp
17 | import coil.compose.AsyncImage
18 | import com.google.accompanist.pager.ExperimentalPagerApi
19 | import com.google.accompanist.pager.HorizontalPager
20 | import com.google.accompanist.pager.rememberPagerState
21 | import com.google.accompanist.placeholder.PlaceholderHighlight
22 | import com.google.accompanist.placeholder.material.placeholder
23 | import com.google.accompanist.placeholder.material.shimmer
24 | import com.google.accompanist.placeholder.placeholder
25 | import icu.bughub.app.app.viewmodel.MainViewModel
26 | import kotlinx.coroutines.launch
27 | import java.util.*
28 |
29 |
30 | @OptIn(ExperimentalPagerApi::class)
31 | @Composable
32 | fun SwiperContent(vm: MainViewModel) {
33 |
34 | //虚拟页数
35 | val virtualCount = Int.MAX_VALUE
36 |
37 | //实际页数
38 | val actualCount = vm.swiperData.size
39 |
40 | //初始图片下标
41 | val initialIndex = virtualCount / 2
42 |
43 | val pagerState = rememberPagerState(initialPage = initialIndex)
44 |
45 | val coroutineScope = rememberCoroutineScope()
46 |
47 | DisposableEffect(Unit) {
48 |
49 | coroutineScope.launch {
50 | vm.swiperData()
51 | }
52 |
53 | val timer = Timer()
54 |
55 | timer.schedule(object : TimerTask() {
56 | override fun run() {
57 | if (vm.swiperLoaded) {
58 | coroutineScope.launch { pagerState.animateScrollToPage(pagerState.currentPage + 1) }
59 | }
60 | }
61 | }, 3000, 3000)
62 |
63 | onDispose {
64 | timer.cancel()
65 | }
66 | }
67 |
68 | HorizontalPager(
69 | count = virtualCount,
70 | state = pagerState,
71 | modifier = Modifier
72 | .padding(horizontal = 8.dp)
73 | .clip(RoundedCornerShape(8.dp)),
74 | userScrollEnabled = vm.swiperLoaded
75 | ) { index ->
76 | val actualIndex =
77 | (index - initialIndex).floorMod(actualCount) //index - (index.floorDiv(actualCount)) * actualCount
78 |
79 | AsyncImage(
80 | model = vm.swiperData[actualIndex].imageUrl,
81 | contentDescription = null,
82 | modifier = Modifier
83 | .fillMaxWidth()
84 | .aspectRatio(7 / 3f)
85 | .placeholder(
86 | visible = !vm.swiperLoaded,
87 | highlight = PlaceholderHighlight.shimmer()
88 | ),
89 | contentScale = ContentScale.Crop
90 | )
91 | }
92 | }
93 |
94 | fun Int.floorMod(other: Int): Int = when (other) {
95 | 0 -> this
96 | //将虚拟数据按照实际数据总数分为 N 组
97 | //当前虚拟下标是在这虚拟数据中的哪一组:虚拟下标floorDiv实际数据总数(虚拟下标/实际数据总数)。向下取整
98 | //虚拟下标 - (虚拟下标/实际数据总数) * 实际数据总数
99 | else -> this - floorDiv(other) * other
100 | }
101 |
102 |
--------------------------------------------------------------------------------
/App/app/src/main/java/icu/bughub/app/app/ui/components/TopAppBar.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.app.ui.components
2 |
3 |
4 | import androidx.compose.foundation.background
5 | import androidx.compose.foundation.layout.*
6 | import androidx.compose.material.MaterialTheme
7 | import androidx.compose.material.Text
8 | import androidx.compose.runtime.Composable
9 | import androidx.compose.runtime.LaunchedEffect
10 | import androidx.compose.ui.Alignment
11 | import androidx.compose.ui.Modifier
12 | import androidx.compose.ui.graphics.Brush
13 | import androidx.compose.ui.graphics.Color
14 | import androidx.compose.ui.platform.LocalDensity
15 | import androidx.compose.ui.tooling.preview.Preview
16 | import androidx.compose.ui.unit.dp
17 | import com.google.accompanist.insets.LocalWindowInsets
18 | import com.google.accompanist.insets.statusBarsPadding
19 | import com.google.accompanist.systemuicontroller.rememberSystemUiController
20 | import icu.bughub.app.app.ui.theme.Blue200
21 | import icu.bughub.app.app.ui.theme.Blue700
22 |
23 | //标题栏高度
24 | val appBarHeight = 56.dp
25 |
26 | /**
27 | * 统一标题栏
28 | *
29 | * @param modifier
30 | * @param content 标题栏内容
31 | */
32 | @Composable
33 | fun TopAppBar(modifier: Modifier = Modifier, content: @Composable () -> Unit) {
34 |
35 | val systemUiController = rememberSystemUiController()
36 |
37 | LaunchedEffect(key1 = Unit) {
38 | systemUiController.setStatusBarColor(Color.Transparent)
39 | }
40 |
41 | //转换状态栏高度 px 为 dp
42 | val statusBarHeightDp = with(LocalDensity.current) {
43 | LocalWindowInsets.current.statusBars.top.toDp()
44 | }
45 |
46 | Row(
47 | modifier = Modifier
48 | .fillMaxWidth()
49 | .background(
50 | Brush.linearGradient(
51 | listOf(
52 | Blue700,
53 | Blue200
54 | )
55 | )
56 | )
57 | .height(appBarHeight + statusBarHeightDp)
58 | .padding(top = statusBarHeightDp)
59 | .then(modifier),
60 | horizontalArrangement = Arrangement.Center,
61 | verticalAlignment = Alignment.CenterVertically
62 | ) {
63 | content()
64 | }
65 |
66 | }
67 |
68 | @Preview
69 | @Composable
70 | fun TopAppBarPreview() {
71 | TopAppBar() {
72 | Text("标题")
73 | }
74 | }
75 |
76 |
--------------------------------------------------------------------------------
/App/app/src/main/java/icu/bughub/app/app/ui/components/VideoItem.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.app.ui.components
2 |
3 |
4 | import androidx.compose.foundation.layout.aspectRatio
5 | import androidx.compose.foundation.layout.fillMaxWidth
6 | import androidx.compose.foundation.layout.padding
7 | import androidx.compose.foundation.shape.RoundedCornerShape
8 | import androidx.compose.material.Divider
9 | import androidx.compose.material.Text
10 | import androidx.compose.runtime.Composable
11 | import androidx.compose.ui.Modifier
12 | import androidx.compose.ui.draw.clip
13 | import androidx.compose.ui.graphics.Color
14 | import androidx.compose.ui.layout.ContentScale
15 | import androidx.compose.ui.layout.layout
16 | import androidx.compose.ui.layout.layoutId
17 | import androidx.compose.ui.text.style.TextOverflow
18 | import androidx.compose.ui.tooling.preview.Preview
19 | import androidx.compose.ui.unit.dp
20 | import androidx.compose.ui.unit.sp
21 | import androidx.constraintlayout.compose.ConstraintLayout
22 | import androidx.constraintlayout.compose.ConstraintSet
23 | import androidx.constraintlayout.compose.Dimension
24 | import coil.compose.AsyncImage
25 | import com.google.accompanist.placeholder.PlaceholderHighlight
26 | import com.google.accompanist.placeholder.material.placeholder
27 | import com.google.accompanist.placeholder.material.shimmer
28 | import icu.bughub.app.app.model.entity.VideoEntity
29 |
30 |
31 | @Composable
32 | fun VideoItem(modifier: Modifier = Modifier, videoEntity: VideoEntity, loaded: Boolean) {
33 |
34 | val constraintSet = ConstraintSet {
35 | val title = createRefFor("title")
36 | val cover = createRefFor("cover")
37 | val type = createRefFor("type")
38 | val duration = createRefFor("duration")
39 | val divider = createRefFor("divider")
40 |
41 | constrain(cover) {
42 | start.linkTo(parent.start)
43 | centerVerticallyTo(parent)
44 |
45 | width = Dimension.value(115.5.dp)
46 | }
47 |
48 | constrain(title) {
49 | start.linkTo(cover.end, margin = 8.dp)
50 | end.linkTo(parent.end)
51 | width = Dimension.fillToConstraints
52 | }
53 |
54 | constrain(type) {
55 | start.linkTo(title.start)
56 | bottom.linkTo(parent.bottom)
57 | }
58 |
59 | constrain(duration) {
60 | end.linkTo(parent.end)
61 | bottom.linkTo(parent.bottom)
62 | }
63 |
64 | constrain(divider) {
65 | bottom.linkTo(cover.bottom, margin = (-8).dp)
66 | }
67 | }
68 |
69 | ConstraintLayout(
70 | constraintSet, modifier = modifier
71 | .fillMaxWidth()
72 | .padding(8.dp)
73 | ) {
74 | AsyncImage(
75 | model = videoEntity.imageUrl, contentDescription = null,
76 | contentScale = ContentScale.Crop,
77 | modifier = Modifier
78 | .layoutId("cover")
79 | .aspectRatio(16 / 9f)
80 | .clip(RoundedCornerShape(8.dp))
81 | .placeholder(
82 | visible = !loaded, highlight = PlaceholderHighlight.shimmer()
83 | )
84 | )
85 |
86 | Text(
87 | text = videoEntity.title,
88 | fontSize = 16.sp,
89 | color = Color(0xFF666666),
90 | maxLines = 2,
91 | overflow = TextOverflow.Ellipsis,
92 | modifier = Modifier
93 | .layoutId("title")
94 | .placeholder(
95 | visible = !loaded, highlight = PlaceholderHighlight.shimmer()
96 | )
97 | )
98 |
99 | Text(
100 | text = videoEntity.type ?: "",
101 | fontSize = 10.sp,
102 | color = Color(0xFF999999),
103 | maxLines = 1,
104 | overflow = TextOverflow.Ellipsis,
105 | modifier = Modifier
106 | .layoutId("type")
107 | .placeholder(
108 | visible = !loaded, highlight = PlaceholderHighlight.shimmer()
109 | )
110 | )
111 |
112 | Text(
113 | text = "时长:${videoEntity.duration}",
114 | fontSize = 10.sp,
115 | color = Color(0xFF999999),
116 | maxLines = 1,
117 | overflow = TextOverflow.Ellipsis,
118 | modifier = Modifier
119 | .layoutId("duration")
120 | .placeholder(
121 | visible = !loaded, highlight = PlaceholderHighlight.shimmer()
122 | )
123 | )
124 |
125 | Divider(
126 | modifier = Modifier
127 | .layoutId("divider")
128 | )
129 | }
130 |
131 |
132 | }
133 |
134 |
--------------------------------------------------------------------------------
/App/app/src/main/java/icu/bughub/app/app/ui/components/video/PlayerValue.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.app.ui.components.video
2 |
3 | import android.os.Parcelable
4 | import androidx.compose.runtime.getValue
5 | import androidx.compose.runtime.mutableStateOf
6 | import androidx.compose.runtime.setValue
7 | import kotlinx.parcelize.Parcelize
8 |
9 | /**
10 | * 播放器相关数据类
11 | *
12 | */
13 | @Parcelize
14 | class PlayerValue : Parcelable {
15 |
16 | var coverUrl: String = ""
17 |
18 | var title: String = ""
19 |
20 | //存储 url 目的是为了横竖屏切换等重绘的场景
21 | var videoUrl: String = ""
22 |
23 | //视频总时长
24 | var duration by mutableStateOf(0L)
25 |
26 | //当前播放进度
27 | var currentPosition by mutableStateOf(0L)
28 |
29 | //当前状态
30 | var state by mutableStateOf(PlayState.None)
31 |
32 | }
33 |
34 | /**
35 | * 播放状态
36 | *
37 | */
38 | enum class PlayState {
39 | None, //未播放
40 | Loading,//加载中
41 | Playing,//播放中
42 | Pause,//已暂停
43 | }
--------------------------------------------------------------------------------
/App/app/src/main/java/icu/bughub/app/app/ui/components/video/VideoView.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.app.ui.components.video
2 |
3 |
4 | import android.view.LayoutInflater
5 | import androidx.compose.runtime.Composable
6 | import androidx.compose.ui.tooling.preview.Preview
7 | import androidx.compose.ui.viewinterop.AndroidView
8 | import com.tencent.rtmp.TXVodPlayer
9 | import com.tencent.rtmp.ui.TXCloudVideoView
10 | import icu.bughub.app.app.R
11 |
12 |
13 | @Composable
14 | fun VideoView(vodPlayer: TXVodPlayer) {
15 |
16 | AndroidView(factory = { context ->
17 | (LayoutInflater.from(context).inflate(R.layout.video, null, false)
18 | .findViewById(R.id.videoView) as TXCloudVideoView).apply {
19 | vodPlayer.setPlayerView(this)
20 | }
21 | })
22 |
23 | }
24 |
25 |
--------------------------------------------------------------------------------
/App/app/src/main/java/icu/bughub/app/app/ui/components/video/VodController.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.app.ui.components.video
2 |
3 | import android.content.Context
4 | import android.os.Bundle
5 | import android.util.Log
6 | import androidx.compose.runtime.Composable
7 | import androidx.compose.runtime.remember
8 | import androidx.compose.runtime.saveable.Saver
9 | import androidx.compose.runtime.saveable.SaverScope
10 | import androidx.compose.runtime.saveable.rememberSaveable
11 | import androidx.compose.ui.platform.LocalContext
12 | import com.tencent.rtmp.ITXVodPlayListener
13 | import com.tencent.rtmp.TXLiveConstants
14 | import com.tencent.rtmp.TXVodPlayer
15 |
16 | class VodController(
17 | context: Context,
18 | val playerValue: PlayerValue
19 | ) {
20 |
21 | val vodPlayer = TXVodPlayer(context).apply {
22 | setVodListener(object : ITXVodPlayListener {
23 | override fun onPlayEvent(player: TXVodPlayer?, event: Int, param: Bundle?) {
24 | when (event) {
25 | TXLiveConstants.PLAY_EVT_PLAY_LOADING -> {
26 | playerValue.state = PlayState.Loading
27 | }
28 | TXLiveConstants.PLAY_EVT_VOD_PLAY_PREPARED,
29 | TXLiveConstants.PLAY_EVT_RCV_FIRST_I_FRAME,
30 | TXLiveConstants.PLAY_EVT_VOD_LOADING_END,
31 | TXLiveConstants.PLAY_EVT_PLAY_BEGIN
32 | -> {
33 | playerValue.state = PlayState.Playing
34 | }
35 | //获取视频时长和进度
36 | TXLiveConstants.PLAY_EVT_PLAY_PROGRESS -> {
37 | playerValue.duration =
38 | param?.getInt(TXLiveConstants.EVT_PLAY_DURATION_MS)?.toLong() ?: 0L
39 |
40 | playerValue.currentPosition =
41 | param?.getInt(TXLiveConstants.EVT_PLAY_PROGRESS_MS)?.toLong() ?: 0L
42 | }
43 | }
44 | }
45 |
46 | override fun onNetStatus(player: TXVodPlayer?, param: Bundle?) {
47 |
48 | }
49 | })
50 | }
51 |
52 | /**
53 | * 开始播放
54 | *
55 | * @param url
56 | */
57 | fun startPlay() {
58 | playerValue.videoUrl = playerValue.videoUrl
59 | vodPlayer.startPlay(playerValue.videoUrl)
60 | }
61 |
62 | /**
63 | * 配置改变后重新播放
64 | *
65 | */
66 | fun restore() {
67 | vodPlayer.setStartTime(playerValue.currentPosition / 1000f)
68 | startPlay()
69 | }
70 |
71 | /**
72 | * 停止播放
73 | *
74 | */
75 | fun stopPlay() {
76 | vodPlayer.stopPlay(true)
77 | }
78 |
79 | /**
80 | * 暂停播放
81 | *
82 | */
83 | fun pause() {
84 | vodPlayer.pause()
85 | playerValue.state = PlayState.Pause
86 | }
87 |
88 | /**
89 | * 恢复播放
90 | *
91 | */
92 | fun resume() {
93 | vodPlayer.resume()
94 | }
95 |
96 | /**
97 | * 设置播放进度
98 | *
99 | * @param millSeconds
100 | */
101 | fun seekTo(millSeconds: Long) {
102 | vodPlayer.seek((millSeconds / 1000).toInt())
103 | }
104 | }
105 |
106 | @Composable
107 | fun rememberVodController(
108 | context: Context = LocalContext.current,
109 | videoUrl: String,
110 | coverUrl: String? = "",
111 | title: String? = ""
112 | ) =
113 | rememberSaveable(saver = object : Saver {
114 | override fun restore(value: PlayerValue): VodController? {
115 | return VodController(context, value)
116 | }
117 |
118 | override fun SaverScope.save(value: VodController): PlayerValue {
119 | return value.playerValue
120 | }
121 | }) {
122 | val playerValue = PlayerValue()
123 | playerValue.videoUrl = videoUrl
124 | playerValue.coverUrl = coverUrl ?: ""
125 | playerValue.title = title ?: ""
126 | return@rememberSaveable VodController(context, playerValue)
127 | }
128 | // remember {
129 | // VodController(context, videoUrl, coverUrl)
130 | // }
--------------------------------------------------------------------------------
/App/app/src/main/java/icu/bughub/app/app/ui/navigation/Destinations.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.app.ui.navigation
2 |
3 | sealed class Destinations(val route: String) {
4 | //首页大框架
5 | object HomeFrame : Destinations("HomeFrame")
6 |
7 | //文章详情页
8 | object ArticleDetail : Destinations("ArticleDetail")
9 |
10 | //视频详情页
11 | object VideoDetail : Destinations("VideoDetail")
12 |
13 | //登录页
14 | object Login : Destinations("Login")
15 | }
16 |
--------------------------------------------------------------------------------
/App/app/src/main/java/icu/bughub/app/app/ui/screens/MainFrame.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.app.ui.screens
2 |
3 |
4 | import androidx.compose.foundation.layout.Box
5 | import androidx.compose.foundation.layout.padding
6 | import androidx.compose.material.*
7 | import androidx.compose.material.icons.Icons
8 | import androidx.compose.material.icons.filled.DateRange
9 | import androidx.compose.material.icons.filled.Home
10 | import androidx.compose.material.icons.filled.Person
11 | import androidx.compose.runtime.*
12 | import androidx.compose.ui.Modifier
13 | import androidx.compose.ui.graphics.Color
14 | import androidx.compose.ui.tooling.preview.Preview
15 | import com.google.accompanist.insets.ProvideWindowInsets
16 | import com.google.accompanist.insets.navigationBarsPadding
17 | import icu.bughub.app.app.model.entity.NavigationItem
18 |
19 |
20 | @Composable
21 | fun MainFrame(
22 | onNavigateToArticle: () -> Unit = {},
23 | onNavigateToVideo: () -> Unit = {},
24 | onNavigateToStudyHistory: () -> Unit = {}
25 | ) {
26 |
27 | val navigationItems = listOf(
28 | NavigationItem(title = "学习", icon = Icons.Filled.Home),
29 | NavigationItem(title = "任务", icon = Icons.Filled.DateRange),
30 | NavigationItem(title = "我的", icon = Icons.Filled.Person),
31 | )
32 |
33 | var currentNavigationIndex by remember {
34 | mutableStateOf(0)
35 | }
36 |
37 | Scaffold(bottomBar = {
38 | BottomNavigation(
39 | backgroundColor = MaterialTheme.colors.surface,
40 | modifier = Modifier.navigationBarsPadding(bottom = true)
41 | ) {
42 | navigationItems.forEachIndexed { index, navigationItem ->
43 | BottomNavigationItem(
44 | selected = currentNavigationIndex == index,
45 | onClick = {
46 | currentNavigationIndex = index
47 | },
48 | //直接考试结果页面,进入查看页面,返回直接回到列表?
49 | icon = {
50 | Icon(
51 | imageVector = navigationItem.icon,
52 | contentDescription = null
53 | )
54 | },
55 | label = {
56 | Text(text = navigationItem.title)
57 | },
58 | selectedContentColor = Color(0xFF149EE7),
59 | unselectedContentColor = Color(0xFF999999)
60 | )
61 | }
62 | }
63 | }
64 | ) {
65 | Box(modifier = Modifier.padding(it)) {
66 | when (currentNavigationIndex) {
67 | 0 -> StudyScreen(
68 | onNavigateToArticle = onNavigateToArticle,
69 | onNavigateToVideo = onNavigateToVideo,
70 | onNavigateToStudyHistory = onNavigateToStudyHistory
71 | )
72 | 1 -> TaskScreen()
73 | 2 -> MineScreen()
74 | }
75 | }
76 | }
77 |
78 |
79 | }
80 |
81 | @Preview
82 | @Composable
83 | fun MainFramePreview() {
84 | MainFrame()
85 | }
86 |
87 |
--------------------------------------------------------------------------------
/App/app/src/main/java/icu/bughub/app/app/ui/screens/VideoDetailScreen.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.app.ui.screens
2 |
3 |
4 | import android.content.res.Configuration
5 | import androidx.compose.foundation.background
6 | import androidx.compose.foundation.clickable
7 | import androidx.compose.foundation.layout.*
8 | import androidx.compose.material.*
9 | import androidx.compose.material.icons.Icons
10 | import androidx.compose.material.icons.filled.NavigateBefore
11 | import androidx.compose.runtime.*
12 | import androidx.compose.ui.Alignment
13 | import androidx.compose.ui.Modifier
14 | import androidx.compose.ui.draw.alpha
15 | import androidx.compose.ui.graphics.Color
16 | import androidx.compose.ui.platform.LocalConfiguration
17 | import androidx.compose.ui.platform.LocalContext
18 | import androidx.compose.ui.text.style.TextAlign
19 | import androidx.compose.ui.tooling.preview.Preview
20 | import androidx.compose.ui.unit.dp
21 | import androidx.compose.ui.unit.sp
22 | import androidx.lifecycle.viewmodel.compose.viewModel
23 | import com.google.accompanist.insets.statusBarsPadding
24 | import com.google.accompanist.systemuicontroller.rememberSystemUiController
25 | import com.tencent.rtmp.TXVodPlayer
26 | import icu.bughub.app.app.ui.components.video.VideoPlayer
27 | import icu.bughub.app.app.ui.components.video.VideoView
28 | import icu.bughub.app.app.ui.components.video.rememberVodController
29 | import icu.bughub.app.app.ui.theme.Blue700
30 | import icu.bughub.app.app.viewmodel.VideoViewModel
31 | import icu.bughub.app.module.webview.WebView
32 | import icu.bughub.app.module.webview.rememberWebViewState
33 |
34 |
35 | @Composable
36 | fun VideoDetailScreen(videoViewModel: VideoViewModel = viewModel(), onBack: () -> Unit) {
37 |
38 | val systemUiController = rememberSystemUiController()
39 |
40 | LaunchedEffect(Unit) {
41 | videoViewModel.fetchInfo()
42 | }
43 |
44 | val webViewState = rememberWebViewState(data = videoViewModel.videoDesc)
45 |
46 |
47 | val configuration = LocalConfiguration.current
48 |
49 | var scaffoldModifier by remember {
50 | mutableStateOf(Modifier.alpha(1f))
51 | }
52 |
53 | var videoBoxModifier by remember {
54 | mutableStateOf(
55 | Modifier.aspectRatio(16 / 9f)
56 | )
57 | }
58 |
59 |
60 | Scaffold(
61 | topBar = {
62 | if (configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
63 | TopAppBar(
64 | title = {
65 | Text(
66 | text = "视频详情",
67 | fontSize = 18.sp
68 | )
69 | },
70 | navigationIcon = {
71 | Icon(
72 | imageVector = Icons.Default.NavigateBefore,
73 | contentDescription = null,
74 | modifier = Modifier
75 | .clickable {
76 | onBack()
77 | }
78 | .padding(8.dp)
79 | )
80 | },
81 | )
82 | }
83 | },
84 | modifier = scaffoldModifier,
85 | ) {
86 | Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
87 | if (videoViewModel.infoLoaded) {
88 | Column(modifier = Modifier.fillMaxSize()) {
89 | val vodController =
90 | rememberVodController(
91 | videoUrl = videoViewModel.videoUrl,
92 | coverUrl = videoViewModel.coverUrl
93 | )
94 | //TODO 横屏后,点击屏幕状态栏即显示出来,而且不会再隐藏,如何处理这个问题?
95 | LaunchedEffect(configuration.orientation) {
96 | vodController.restore()
97 | if (configuration.orientation == Configuration.ORIENTATION_PORTRAIT) {
98 | videoBoxModifier = Modifier
99 | .fillMaxWidth()
100 | .aspectRatio(16 / 9f)
101 | systemUiController.isSystemBarsVisible = true
102 |
103 | scaffoldModifier = Modifier
104 | .background(Blue700)
105 | .statusBarsPadding()
106 |
107 | } else {
108 | videoBoxModifier = Modifier
109 | .fillMaxSize()
110 | systemUiController.isSystemBarsVisible = false
111 |
112 | scaffoldModifier = Modifier
113 | }
114 | }
115 |
116 | //视频区域
117 | Box(modifier = videoBoxModifier) {
118 | VideoPlayer(vodController = vodController)
119 | }
120 |
121 | //想让标题一起滚动,有两个方案
122 | //方案一:把标题放到视频简介的 html 文本中去
123 | //方案二:计算 视频简介在 webview 中的高度,然后动态设置 webview 的高度
124 | WebView(state = webViewState)
125 | }
126 | } else {
127 | CircularProgressIndicator()
128 | }
129 | }
130 | }
131 |
132 | }
133 |
134 |
135 |
--------------------------------------------------------------------------------
/App/app/src/main/java/icu/bughub/app/app/ui/theme/Color.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.app.ui.theme
2 |
3 | import androidx.compose.ui.graphics.Color
4 |
5 |
6 | val Blue700 = Color(0xFF149EE7)
7 | val Blue200 = Color(0xFF2DCDF5)
--------------------------------------------------------------------------------
/App/app/src/main/java/icu/bughub/app/app/ui/theme/Shape.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.app.ui.theme
2 |
3 | import androidx.compose.foundation.shape.RoundedCornerShape
4 | import androidx.compose.material.Shapes
5 | import androidx.compose.ui.unit.dp
6 |
7 | val Shapes = Shapes(
8 | small = RoundedCornerShape(4.dp),
9 | medium = RoundedCornerShape(4.dp),
10 | large = RoundedCornerShape(0.dp)
11 | )
--------------------------------------------------------------------------------
/App/app/src/main/java/icu/bughub/app/app/ui/theme/Theme.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.app.ui.theme
2 |
3 | import androidx.compose.foundation.isSystemInDarkTheme
4 | import androidx.compose.material.MaterialTheme
5 | import androidx.compose.material.darkColors
6 | import androidx.compose.material.lightColors
7 | import androidx.compose.runtime.Composable
8 |
9 | private val DarkColorPalette = darkColors(
10 | primary = Blue700,
11 | primaryVariant = Blue700,
12 | secondary = Blue200
13 | )
14 |
15 | private val LightColorPalette = lightColors(
16 | primary = Blue700,
17 | primaryVariant = Blue700,
18 | secondary = Blue200
19 |
20 | /* Other default colors to override
21 | background = Color.White,
22 | surface = Color.White,
23 | onPrimary = Color.White,
24 | onSecondary = Color.Black,
25 | onBackground = Color.Black,
26 | onSurface = Color.Black,
27 | */
28 | )
29 |
30 | @Composable
31 | fun AppTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) {
32 | // val colors = if (darkTheme) {
33 | // DarkColorPalette
34 | // } else {
35 | // LightColorPalette
36 | // }
37 |
38 | MaterialTheme(
39 | colors = LightColorPalette,
40 | typography = Typography,
41 | shapes = Shapes,
42 | content = content
43 | )
44 | }
--------------------------------------------------------------------------------
/App/app/src/main/java/icu/bughub/app/app/ui/theme/Type.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.app.ui.theme
2 |
3 | import androidx.compose.material.Typography
4 | import androidx.compose.ui.text.TextStyle
5 | import androidx.compose.ui.text.font.FontFamily
6 | import androidx.compose.ui.text.font.FontWeight
7 | import androidx.compose.ui.unit.sp
8 |
9 | // Set of Material typography styles to start with
10 | val Typography = Typography(
11 | body1 = TextStyle(
12 | fontFamily = FontFamily.Default,
13 | fontWeight = FontWeight.Normal,
14 | fontSize = 16.sp
15 | )
16 | /* Other default text styles to override
17 | button = TextStyle(
18 | fontFamily = FontFamily.Default,
19 | fontWeight = FontWeight.W500,
20 | fontSize = 14.sp
21 | ),
22 | caption = TextStyle(
23 | fontFamily = FontFamily.Default,
24 | fontWeight = FontWeight.Normal,
25 | fontSize = 12.sp
26 | )
27 | */
28 | )
--------------------------------------------------------------------------------
/App/app/src/main/java/icu/bughub/app/app/viewmodel/ArticleViewModel.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.app.viewmodel
2 |
3 | import android.util.Log
4 | import androidx.compose.runtime.getValue
5 | import androidx.compose.runtime.mutableStateListOf
6 | import androidx.compose.runtime.mutableStateOf
7 | import androidx.compose.runtime.setValue
8 | import androidx.lifecycle.ViewModel
9 | import icu.bughub.app.app.model.entity.ArticleEntity
10 | import icu.bughub.app.app.model.service.ArticleService
11 | import kotlinx.coroutines.delay
12 |
13 | class ArticleViewModel : ViewModel() {
14 |
15 | private val articleService = ArticleService.instance()
16 |
17 | private val pageSize = 10
18 | private var pageOffset = 1
19 |
20 | //文章列表数据
21 | var list by mutableStateOf(
22 | listOf(
23 | ArticleEntity(
24 | title = "人社部向疫情防控期参与复工复产的劳动者表",
25 | source = "“学习强国”学习平台",
26 | timestamp = "2020-02-10"
27 | ),
28 | ArticleEntity(
29 | title = "人社部向疫情防控期参与复工复产的劳动者表人社部向疫情防控期参与复工复产的劳动者表",
30 | source = "“学习强国”学习平台",
31 | timestamp = "2020-02-10"
32 | ), ArticleEntity(
33 | title = "人社部向疫情防控期参与复工复产的劳动者表",
34 | source = "“学习强国”学习平台",
35 | timestamp = "2020-02-10"
36 | ), ArticleEntity(
37 | title = "人社部向疫情防控期参与复工复产的劳动者表",
38 | source = "“学习强国”学习平台",
39 | timestamp = "2020-02-10"
40 | ), ArticleEntity(
41 | title = "人社部向疫情防控期参与复工复产的劳动者表",
42 | source = "“学习强国”学习平台",
43 | timestamp = "2020-02-10"
44 | ), ArticleEntity(
45 | title = "人社部向疫情防控期参与复工复产的劳动者表",
46 | source = "“学习强国”学习平台",
47 | timestamp = "2020-02-10"
48 | ), ArticleEntity(
49 | title = "人社部向疫情防控期参与复工复产的劳动者表",
50 | source = "“学习强国”学习平台",
51 | timestamp = "2020-02-10"
52 | ), ArticleEntity(
53 | title = "人社部向疫情防控期参与复工复产的劳动者表",
54 | source = "“学习强国”学习平台",
55 | timestamp = "2020-02-10"
56 | ), ArticleEntity(
57 | title = "人社部向疫情防控期参与复工复产的劳动者表",
58 | source = "“学习强国”学习平台",
59 | timestamp = "2020-02-10"
60 | ), ArticleEntity(
61 | title = "人社部向疫情防控期参与复工复产的劳动者表",
62 | source = "“学习强国”学习平台",
63 | timestamp = "2020-02-10"
64 | )
65 | )
66 | )
67 | private set
68 |
69 | //第一页文章列表数据是否加载完成
70 | var listLoaded by mutableStateOf(false)
71 | private set
72 |
73 | //是否正在刷新
74 | var refreshing by mutableStateOf(false)
75 | private set
76 |
77 | //是否还有更多
78 | private var hasMore = false
79 |
80 | suspend fun fetchArticleList() {
81 | val res = articleService.list(pageOffset = pageOffset, pageSize = pageSize)
82 | if (res.code == 0 && res.data != null) {
83 | val tmpList = mutableListOf()
84 | if (pageOffset != 1) {
85 | tmpList.addAll(list)
86 | }
87 | tmpList.addAll(res.data)
88 | //是否还有更多数据
89 | hasMore = res.data.size == pageSize
90 | list = tmpList
91 | listLoaded = true
92 | refreshing = false
93 | } else {
94 | pageOffset--
95 | if (pageOffset <= 1) {
96 | pageOffset = 1
97 | }
98 | }
99 | }
100 |
101 | /**
102 | * 下拉刷新
103 | *
104 | */
105 | suspend fun refresh() {
106 | pageOffset = 1
107 | // listLoaded = false
108 | refreshing = true
109 | fetchArticleList()
110 | }
111 |
112 | suspend fun loadMore() {
113 | if (hasMore) {
114 | pageOffset++
115 | fetchArticleList()
116 | }
117 | }
118 |
119 | //HTML 头部
120 | private val htmlHeader = """
121 |
122 |
123 |
124 |
125 |
126 |
127 |
132 |
133 |
134 | """.trimIndent()
135 |
136 | //html尾部
137 | private val htmlFooter = """
138 |
139 |
140 | """.trimIndent()
141 |
142 |
143 | private var articleEntity: ArticleEntity? = null
144 |
145 | var content by mutableStateOf(
146 | """$htmlHeader
147 | ${articleEntity?.content ?: ""}
148 | $htmlFooter
149 | """.trimIndent()
150 | )
151 |
152 | var infoLoaded by mutableStateOf(false)
153 | private set
154 |
155 | suspend fun fetchInfo() {
156 | val res = articleService.info("")
157 | if (res.code == 0 && res.data != null) {
158 | articleEntity = res.data
159 | content = """$htmlHeader
160 | ${articleEntity?.content ?: ""}
161 | $htmlFooter
162 | """.trimIndent()
163 | infoLoaded = true
164 | }
165 | }
166 | }
--------------------------------------------------------------------------------
/App/app/src/main/java/icu/bughub/app/app/viewmodel/MainViewModel.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.app.viewmodel
2 |
3 | import androidx.compose.material.icons.Icons
4 | import androidx.compose.material.icons.filled.Description
5 | import androidx.compose.material.icons.filled.SmartDisplay
6 | import androidx.compose.runtime.getValue
7 | import androidx.compose.runtime.mutableStateOf
8 | import androidx.compose.runtime.setValue
9 | import androidx.lifecycle.ViewModel
10 | import icu.bughub.app.app.model.entity.Category
11 | import icu.bughub.app.app.model.entity.DataType
12 | import icu.bughub.app.app.model.entity.SwiperEntity
13 | import icu.bughub.app.app.model.service.HomeService
14 |
15 | class MainViewModel : ViewModel() {
16 |
17 | private val homeService = HomeService.instance()
18 |
19 | //分类数据是否加载成功
20 | var categoryLoaded by mutableStateOf(false)
21 | private set
22 |
23 | //分类数据
24 | var categories by mutableStateOf(
25 | listOf(
26 | Category("思想政治1", "1"),
27 | Category("法律法规2", "2"),
28 | Category("职业道德3", "3"),
29 | Category("诚信自律4", "4")
30 | )
31 | )
32 | private set
33 |
34 | suspend fun categoryData() {
35 | val categoryRes = homeService.category()
36 | if (categoryRes.code == 0) {
37 | categories = categoryRes.data
38 | categoryLoaded = true
39 | } else {
40 | //不成功的情况下,读取 message
41 | val message = categoryRes.message
42 | }
43 | }
44 |
45 | //当前分类下标
46 | var categoryIndex by mutableStateOf(0)
47 | private set
48 |
49 | /**
50 | * 更新分类下标
51 | *
52 | * @param index
53 | */
54 | fun updateCategoryIndex(index: Int) {
55 | categoryIndex = index
56 | }
57 |
58 |
59 | //类型数据
60 | val types by mutableStateOf(
61 | listOf(
62 | DataType("相关资讯", Icons.Default.Description),
63 | DataType("视频课程", Icons.Default.SmartDisplay)
64 | )
65 | )
66 |
67 | //当前类型下标
68 | var currentTypeIndex by mutableStateOf(0)
69 | private set
70 |
71 | //是否文章列表
72 | var showArticleList by mutableStateOf(true)
73 | private set
74 |
75 | /**
76 | * 更新类型下标
77 | *
78 | * @param index
79 | */
80 | fun updateTypeIndex(index: Int) {
81 | currentTypeIndex = index
82 | showArticleList = currentTypeIndex == 0
83 | }
84 |
85 | //轮播图数据
86 | var swiperData by mutableStateOf(
87 | listOf(
88 | SwiperEntity("https://docs.bughub.icu/compose/assets/banner5.jpg")
89 | )
90 | )
91 | private set
92 |
93 | var swiperLoaded by mutableStateOf(false)
94 | private set
95 |
96 | suspend fun swiperData() {
97 | val swiperRes = homeService.banner()
98 | if (swiperRes.code == 0 && swiperRes.data != null) {
99 | swiperData = swiperRes.data
100 | swiperLoaded = true
101 | } else {
102 | val message = swiperRes.message
103 | }
104 | }
105 |
106 | //通知数据
107 | val notifications =
108 | listOf("人社部向疫情防控期", "湖北黄冈新冠肺炎患者治愈病例破千连续5治愈病例破千连续5", "安徽单日新增确诊病例首次降至个位数累计")
109 |
110 |
111 | }
--------------------------------------------------------------------------------
/App/app/src/main/java/icu/bughub/app/app/viewmodel/TaskViewModel.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.app.viewmodel
2 |
3 | import androidx.compose.runtime.getValue
4 | import androidx.compose.runtime.mutableStateOf
5 | import androidx.compose.runtime.setValue
6 | import androidx.lifecycle.ViewModel
7 |
8 | class TaskViewModel : ViewModel() {
9 |
10 | var taskDate by mutableStateOf("学习周期:2022.01.01-2022.12.31")
11 | private set
12 |
13 | var totalPointOfYear = 13500
14 |
15 | //学年积分
16 | var pointOfYear by mutableStateOf(10000)
17 | private set
18 |
19 | //学年积分进度 = 220f * pointOfYear / 学年总积分
20 | var pointOfYearPercent by mutableStateOf(0f)
21 | private set
22 |
23 | /**
24 | * 更新学年积分进度
25 | *
26 | */
27 | fun updatePointPercent() {
28 | pointOfYearPercent = 220f * pointOfYear / totalPointOfYear
29 | }
30 |
31 | //一周积分情况
32 | var pointsOfWeek by mutableStateOf(listOf(0.0, 2.0, 6.0, 9.5, 10.0, 15.0, 5.0))
33 | private set
34 |
35 | //日期
36 | val weeks = listOf("02.05", "02.06", "02.07", "02.08", "02.09", "02.10", "今日")
37 |
38 |
39 | //今日积分
40 | private var todayPoint = 13
41 |
42 | //今日提醒文字
43 | var tips by mutableStateOf("今日获得0积分,快去完成下面任务吧")
44 | private set
45 |
46 | /**
47 | * 更新任务提醒文字
48 | *
49 | */
50 | fun updateTips() {
51 | tips = when (todayPoint) {
52 | 0 -> {
53 | "今日获得0积分,快去完成下面任务吧"
54 | }
55 | in 1..14 -> {
56 | "今日获得${todayPoint}积分,快去完成下面任务吧"
57 | }
58 | else -> {
59 | "今日获得${todayPoint}积分,已经完成任务"
60 | }
61 | }
62 | }
63 |
64 | }
--------------------------------------------------------------------------------
/App/app/src/main/java/icu/bughub/app/app/viewmodel/UserViewModel.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.app.viewmodel
2 |
3 | import android.content.Context
4 | import android.util.Log
5 | import androidx.compose.runtime.getValue
6 | import androidx.compose.runtime.mutableStateOf
7 | import androidx.compose.runtime.setValue
8 | import androidx.lifecycle.ViewModel
9 | import androidx.lifecycle.viewModelScope
10 | import icu.bughub.app.app.model.entity.UserInfoEntity
11 | import icu.bughub.app.app.model.service.UserInfoManager
12 | import icu.bughub.app.app.model.service.UserService
13 | import kotlinx.coroutines.delay
14 | import kotlinx.coroutines.flow.firstOrNull
15 | import kotlinx.coroutines.launch
16 | import okhttp3.internal.and
17 | import java.security.MessageDigest
18 | import java.security.NoSuchAlgorithmException
19 |
20 | class UserViewModel(context: Context) : ViewModel() {
21 |
22 | private val userInfoManager = UserInfoManager(context)
23 | private val userService = UserService.instance()
24 |
25 | var userName by mutableStateOf("")
26 |
27 | var password by mutableStateOf("")
28 |
29 | var userInfo: UserInfoEntity? = null
30 | private set
31 |
32 | init {
33 | //其实这里可以使用 DataStore 的对象存储,直接存储整个对象。
34 | viewModelScope.launch {
35 | val userName = userInfoManager.userName.firstOrNull()
36 | userInfo = if (userName?.isNotEmpty() == true) {
37 | UserInfoEntity(userName)
38 | } else {
39 | null
40 | }
41 | }
42 | }
43 |
44 | //是否已登录
45 | val logged: Boolean
46 | get() {
47 | return userInfo != null
48 | }
49 |
50 | //是否正在登录
51 | var loading by mutableStateOf(false)
52 | private set
53 |
54 | var error by mutableStateOf("")
55 | private set
56 |
57 | /**
58 | * 登录操作
59 | *
60 | */
61 | suspend fun login(onClose: () -> Unit) {
62 | error = ""
63 | loading = true
64 | val res = userService.signIn(userName, md5(password))
65 | if (res.code == 0 && res.data != null) {
66 | userInfo = res.data
67 | userInfoManager.save(userName)
68 | onClose()
69 | } else {
70 | //失败
71 | error = res.message
72 | }
73 | loading = false
74 | }
75 |
76 | fun md5(content: String): String {
77 | val hash = MessageDigest.getInstance("MD5").digest(content.toByteArray())
78 | val hex = StringBuilder(hash.size * 2)
79 | for (b in hash) {
80 | var str = Integer.toHexString(b.toInt())
81 | if (b < 0x10) {
82 | str = "0$str"
83 | }
84 | hex.append(str.substring(str.length - 2))
85 | }
86 | return hex.toString()
87 | }
88 |
89 |
90 | fun clear() {
91 | viewModelScope.launch {
92 | userInfoManager.clear() //清除本地数据存储
93 | userInfo = null //置空内存数据
94 | }
95 | }
96 | }
--------------------------------------------------------------------------------
/App/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/App/app/src/main/res/drawable/archives.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/JetpackComposeCase/192eb509c12ea41eb8b9fef4ee69806a3a39afd7/App/app/src/main/res/drawable/archives.png
--------------------------------------------------------------------------------
/App/app/src/main/res/drawable/bg.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/JetpackComposeCase/192eb509c12ea41eb8b9fef4ee69806a3a39afd7/App/app/src/main/res/drawable/bg.jpg
--------------------------------------------------------------------------------
/App/app/src/main/res/drawable/browser_history.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/JetpackComposeCase/192eb509c12ea41eb8b9fef4ee69806a3a39afd7/App/app/src/main/res/drawable/browser_history.png
--------------------------------------------------------------------------------
/App/app/src/main/res/drawable/newbanner3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/JetpackComposeCase/192eb509c12ea41eb8b9fef4ee69806a3a39afd7/App/app/src/main/res/drawable/newbanner3.png
--------------------------------------------------------------------------------
/App/app/src/main/res/drawable/points.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/JetpackComposeCase/192eb509c12ea41eb8b9fef4ee69806a3a39afd7/App/app/src/main/res/drawable/points.png
--------------------------------------------------------------------------------
/App/app/src/main/res/drawable/questions.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/JetpackComposeCase/192eb509c12ea41eb8b9fef4ee69806a3a39afd7/App/app/src/main/res/drawable/questions.png
--------------------------------------------------------------------------------
/App/app/src/main/res/drawable/settings.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/JetpackComposeCase/192eb509c12ea41eb8b9fef4ee69806a3a39afd7/App/app/src/main/res/drawable/settings.png
--------------------------------------------------------------------------------
/App/app/src/main/res/drawable/version.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/JetpackComposeCase/192eb509c12ea41eb8b9fef4ee69806a3a39afd7/App/app/src/main/res/drawable/version.png
--------------------------------------------------------------------------------
/App/app/src/main/res/layout/video.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
--------------------------------------------------------------------------------
/App/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/App/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/App/app/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/JetpackComposeCase/192eb509c12ea41eb8b9fef4ee69806a3a39afd7/App/app/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/App/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/JetpackComposeCase/192eb509c12ea41eb8b9fef4ee69806a3a39afd7/App/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/App/app/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/JetpackComposeCase/192eb509c12ea41eb8b9fef4ee69806a3a39afd7/App/app/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/App/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/JetpackComposeCase/192eb509c12ea41eb8b9fef4ee69806a3a39afd7/App/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/App/app/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/JetpackComposeCase/192eb509c12ea41eb8b9fef4ee69806a3a39afd7/App/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/App/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/JetpackComposeCase/192eb509c12ea41eb8b9fef4ee69806a3a39afd7/App/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/App/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/JetpackComposeCase/192eb509c12ea41eb8b9fef4ee69806a3a39afd7/App/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/App/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/JetpackComposeCase/192eb509c12ea41eb8b9fef4ee69806a3a39afd7/App/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/App/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/JetpackComposeCase/192eb509c12ea41eb8b9fef4ee69806a3a39afd7/App/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/App/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/JetpackComposeCase/192eb509c12ea41eb8b9fef4ee69806a3a39afd7/App/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/App/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FF149EE7
4 |
--------------------------------------------------------------------------------
/App/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | App
3 |
--------------------------------------------------------------------------------
/App/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
--------------------------------------------------------------------------------
/App/app/src/test/java/icu/bughub/app/app/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.app
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------
/App/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext {
3 | compose_version = '1.1.1'
4 | }
5 | }// Top-level build file where you can add configuration options common to all sub-projects/modules.
6 | plugins {
7 | id 'com.android.application' version '7.1.0-rc01' apply false
8 | id 'com.android.library' version '7.1.0-rc01' apply false
9 | id 'org.jetbrains.kotlin.android' version '1.6.10' apply false
10 | id 'org.jetbrains.kotlin.plugin.parcelize' version '1.6.10' apply false
11 | }
12 |
13 | task clean(type: Delete) {
14 | delete rootProject.buildDir
15 | }
--------------------------------------------------------------------------------
/App/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Kotlin code style for this project: "official" or "obsolete":
19 | kotlin.code.style=official
20 | # Enables namespacing of each library's R class so that its R class includes only the
21 | # resources declared in the library itself and none from the library's dependencies,
22 | # thereby reducing the size of the R class for that library
23 | android.nonTransitiveRClass=true
--------------------------------------------------------------------------------
/App/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/JetpackComposeCase/192eb509c12ea41eb8b9fef4ee69806a3a39afd7/App/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/App/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Thu Mar 03 06:15:00 CST 2022
2 | distributionBase=GRADLE_USER_HOME
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
7 |
--------------------------------------------------------------------------------
/App/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/App/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | gradlePluginPortal()
4 | google()
5 | mavenCentral()
6 | maven { url "https://maven.aliyun.com/repository/central" }
7 | }
8 | }
9 | dependencyResolutionManagement {
10 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
11 | repositories {
12 | google()
13 | mavenCentral()
14 | maven { url "https://maven.aliyun.com/repository/central" }
15 | }
16 | }
17 | rootProject.name = "App"
18 | include ':app'
19 | include ':webview'
20 |
--------------------------------------------------------------------------------
/App/webview/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/App/webview/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.library'
3 | id 'org.jetbrains.kotlin.android'
4 | }
5 |
6 | android {
7 | compileSdk 32
8 |
9 | defaultConfig {
10 | minSdk 23
11 | targetSdk 32
12 |
13 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
14 | consumerProguardFiles "consumer-rules.pro"
15 | }
16 |
17 | buildTypes {
18 | release {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
21 | }
22 | }
23 | compileOptions {
24 | sourceCompatibility JavaVersion.VERSION_1_8
25 | targetCompatibility JavaVersion.VERSION_1_8
26 | }
27 | buildFeatures {
28 | compose true
29 | }
30 | composeOptions {
31 | kotlinCompilerExtensionVersion compose_version
32 | }
33 | kotlinOptions {
34 | jvmTarget = '1.8'
35 | }
36 | }
37 |
38 | dependencies {
39 |
40 | implementation 'androidx.core:core-ktx:1.7.0'
41 |
42 | implementation "androidx.compose.ui:ui:$compose_version"
43 | implementation "androidx.compose.material:material:$compose_version"
44 | implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
45 |
46 | testImplementation 'junit:junit:4.13.2'
47 | androidTestImplementation 'androidx.test.ext:junit:1.1.3'
48 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
49 | }
--------------------------------------------------------------------------------
/App/webview/consumer-rules.pro:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/JetpackComposeCase/192eb509c12ea41eb8b9fef4ee69806a3a39afd7/App/webview/consumer-rules.pro
--------------------------------------------------------------------------------
/App/webview/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/App/webview/src/androidTest/java/icu/bughub/app/module/webview/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.module.webview
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("icu.bughub.app.module.webview.test", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/App/webview/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
--------------------------------------------------------------------------------
/App/webview/src/main/java/icu/bughub/app/module/webview/WebView.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.module.webview
2 |
3 |
4 | import android.graphics.Bitmap
5 | import android.webkit.WebChromeClient
6 | import android.webkit.WebView
7 | import android.webkit.WebViewClient
8 | import androidx.compose.runtime.*
9 | import androidx.compose.ui.tooling.preview.Preview
10 | import androidx.compose.ui.viewinterop.AndroidView
11 | import kotlinx.coroutines.*
12 | import kotlinx.coroutines.flow.MutableSharedFlow
13 | import kotlinx.coroutines.flow.collect
14 |
15 |
16 | @Composable
17 | fun WebView(state: WebViewState) {
18 |
19 | var webView by remember {
20 | mutableStateOf(null)
21 | }
22 |
23 | //webview 变化或 state变化时重新订阅流数据
24 | LaunchedEffect(webView, state) {
25 | with(state) {
26 | //订阅流
27 | webView?.handleEvents()
28 | }
29 | }
30 |
31 | AndroidView(factory = { context ->
32 | WebView(context).apply {
33 | webChromeClient = object : WebChromeClient() {
34 | override fun onReceivedTitle(view: WebView?, title: String?) {
35 | super.onReceivedTitle(view, title)
36 | state.pageTitle = title
37 | }
38 | }
39 |
40 | webViewClient = object : WebViewClient() {
41 | override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
42 | super.onPageStarted(view, url, favicon)
43 | state.pageTitle = null
44 | }
45 | }
46 |
47 | with(settings) {
48 | javaScriptEnabled = true
49 | }
50 | }.also { webView = it }
51 | }) { view ->
52 | when (val content = state.content) {
53 | is WebContent.Url -> {
54 | val url = content.url
55 | //url 不空或者当前的 webview 加载的 url 不相同
56 | if (url.isNotEmpty() && url != view.url) {
57 | view.loadUrl(content.url)
58 | }
59 | }
60 | is WebContent.Data -> {
61 | view.loadDataWithBaseURL(
62 | content.baseUrl,
63 | content.data,
64 | null,
65 | "utf-8",
66 | null
67 | )
68 | }
69 | }
70 | }
71 |
72 | }
73 |
74 | sealed class WebContent() {
75 | data class Url(val url: String) : WebContent()
76 | data class Data(val data: String, val baseUrl: String? = null) : WebContent()
77 | }
78 |
79 | class WebViewState(private val coroutineScope: CoroutineScope, webContent: WebContent) {
80 | //网页内容:url 或者 data(html 内容)
81 | var content by mutableStateOf(webContent)
82 |
83 | var pageTitle: String? by mutableStateOf(null)
84 | internal set
85 |
86 | //事件类型
87 | private enum class EventType {
88 | EVALUATE_JAVASCRIPT //执行 JS 方法
89 | }
90 |
91 | //共享流的数据类型
92 | private class Event(val type: EventType, val args: String, val callback: ((String) -> Unit)?)
93 |
94 | //共享流
95 | private val events: MutableSharedFlow = MutableSharedFlow()
96 |
97 | internal suspend fun WebView.handleEvents(): Unit = withContext(Dispatchers.Main) {
98 | events.collect { event ->
99 | when (event.type) {
100 | EventType.EVALUATE_JAVASCRIPT -> evaluateJavascript(event.args, event.callback)
101 | }
102 | }
103 | }
104 |
105 | //执行 js 方法
106 | fun evaluateJavascript(script: String, resultCallback: ((String) -> Unit)? = {}) {
107 | val event = Event(EventType.EVALUATE_JAVASCRIPT, script, resultCallback)
108 | coroutineScope.launch { events.emit(event) } //推送流
109 | }
110 | }
111 |
112 | @Composable
113 | fun rememberWebViewState(coroutineScope: CoroutineScope = rememberCoroutineScope(), url: String) =
114 | remember(key1 = url) {
115 | WebViewState(coroutineScope, WebContent.Url(url))
116 | }
117 |
118 | @Composable
119 | fun rememberWebViewState(
120 | coroutineScope: CoroutineScope = rememberCoroutineScope(),
121 | data: String,
122 | baseUrl: String? = null
123 | ) = remember(
124 | key1 = data,
125 | key2 = baseUrl
126 | ) {
127 | WebViewState(coroutineScope, WebContent.Data(data, baseUrl))
128 | }
129 |
130 | @Preview
131 | @Composable
132 | fun WebViewPreview() {
133 | WebView(rememberWebViewState(data = "Header
"))
134 | }
135 |
136 |
--------------------------------------------------------------------------------
/App/webview/src/test/java/icu/bughub/app/module/webview/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.module.webview
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------
/ComposeBasic/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/caches
5 | /.idea/libraries
6 | /.idea/modules.xml
7 | /.idea/workspace.xml
8 | /.idea/navEditor.xml
9 | /.idea/assetWizardSettings.xml
10 | .DS_Store
11 | /build
12 | /captures
13 | .externalNativeBuild
14 | .cxx
15 | local.properties
16 |
--------------------------------------------------------------------------------
/ComposeBasic/.idea/.gitignore:
--------------------------------------------------------------------------------
1 | # Default ignored files
2 | /shelf/
3 | /workspace.xml
4 |
--------------------------------------------------------------------------------
/ComposeBasic/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/ComposeBasic/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/ComposeBasic/.idea/inspectionProfiles/Project_Default.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/ComposeBasic/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/ComposeBasic/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
--------------------------------------------------------------------------------
/ComposeBasic/app/build.gradle:
--------------------------------------------------------------------------------
1 | plugins {
2 | id 'com.android.application'
3 | id 'org.jetbrains.kotlin.android'
4 | }
5 |
6 | android {
7 | compileSdk 32
8 |
9 | defaultConfig {
10 | applicationId "icu.bughub.app.composebasic"
11 | minSdk 23
12 | targetSdk 32
13 | versionCode 1
14 | versionName "1.0"
15 |
16 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
17 | vectorDrawables {
18 | useSupportLibrary true
19 | }
20 | }
21 |
22 | buildTypes {
23 | release {
24 | minifyEnabled false
25 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
26 | }
27 | }
28 | compileOptions {
29 | sourceCompatibility JavaVersion.VERSION_1_8
30 | targetCompatibility JavaVersion.VERSION_1_8
31 | }
32 | kotlinOptions {
33 | jvmTarget = '1.8'
34 | }
35 | buildFeatures {
36 | compose true
37 | }
38 | composeOptions {
39 | kotlinCompilerExtensionVersion compose_version
40 | }
41 | packagingOptions {
42 | resources {
43 | excludes += '/META-INF/{AL2.0,LGPL2.1}'
44 | }
45 | }
46 | }
47 |
48 | dependencies {
49 |
50 | implementation 'androidx.core:core-ktx:1.7.0'
51 | implementation "androidx.compose.ui:ui:$compose_version"
52 | implementation "androidx.compose.material:material:$compose_version"
53 | implementation "androidx.compose.ui:ui-tooling-preview:$compose_version"
54 | implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.4.0'
55 | implementation 'androidx.activity:activity-compose:1.4.0'
56 |
57 | implementation "androidx.compose.material:material-icons-extended:$compose_version"
58 |
59 | implementation "androidx.constraintlayout:constraintlayout-compose:1.0.0"
60 |
61 | implementation "androidx.navigation:navigation-compose:2.4.0-rc01"
62 |
63 | implementation "com.google.accompanist:accompanist-permissions:0.24.2-alpha"
64 |
65 |
66 | testImplementation 'junit:junit:4.13.2'
67 | androidTestImplementation 'androidx.test.ext:junit:1.1.3'
68 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
69 | androidTestImplementation "androidx.compose.ui:ui-test-junit4:$compose_version"
70 | debugImplementation "androidx.compose.ui:ui-tooling:$compose_version"
71 |
72 |
73 | }
--------------------------------------------------------------------------------
/ComposeBasic/app/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
--------------------------------------------------------------------------------
/ComposeBasic/app/src/androidTest/java/icu/bughub/app/composebasic/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.composebasic
2 |
3 | import androidx.test.platform.app.InstrumentationRegistry
4 | import androidx.test.ext.junit.runners.AndroidJUnit4
5 |
6 | import org.junit.Test
7 | import org.junit.runner.RunWith
8 |
9 | import org.junit.Assert.*
10 |
11 | /**
12 | * Instrumented test, which will execute on an Android device.
13 | *
14 | * See [testing documentation](http://d.android.com/tools/testing).
15 | */
16 | @RunWith(AndroidJUnit4::class)
17 | class ExampleInstrumentedTest {
18 | @Test
19 | fun useAppContext() {
20 | // Context of the app under test.
21 | val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22 | assertEquals("icu.bughub.app.composebasic", appContext.packageName)
23 | }
24 | }
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
18 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/java/icu/bughub/app/composebasic/MainActivity.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.composebasic
2 |
3 | import android.graphics.Color
4 | import android.os.Bundle
5 | import android.view.LayoutInflater
6 | import android.widget.TextView
7 | import androidx.activity.ComponentActivity
8 | import androidx.activity.compose.setContent
9 | import androidx.compose.foundation.layout.Column
10 | import androidx.compose.foundation.layout.fillMaxSize
11 | import androidx.compose.material.MaterialTheme
12 | import androidx.compose.material.Surface
13 | import androidx.compose.material.Text
14 | import androidx.compose.runtime.Composable
15 | import androidx.compose.ui.Modifier
16 | import androidx.compose.ui.platform.ComposeView
17 | import androidx.compose.ui.tooling.preview.Devices
18 | import androidx.compose.ui.tooling.preview.Preview
19 | import androidx.compose.ui.viewinterop.AndroidView
20 | import icu.bughub.app.composebasic.ui.theme.ComposeBasicTheme
21 |
22 | class MainActivity : ComponentActivity() {
23 | override fun onCreate(savedInstanceState: Bundle?) {
24 | super.onCreate(savedInstanceState)
25 |
26 | setContentView(R.layout.main)
27 |
28 | val composeView = findViewById(R.id.composeView)
29 |
30 | composeView.setContent {
31 | Column() {
32 | Greeting("Android")
33 |
34 | AndroidView(factory = {
35 | TextView(it)
36 | }){
37 | it.apply {
38 | text = "这是 Compose View 里面的 TextView"
39 | }
40 | }
41 |
42 | }
43 | }
44 |
45 |
46 | // setContent {
47 | // ComposeBasicTheme {
48 | // // A surface container using the 'background' color from the theme
49 | // Surface(
50 | // modifier = Modifier.fillMaxSize(),
51 | // color = MaterialTheme.colors.background
52 | // ) {
53 | // Greeting("Android")
54 | // }
55 | // }
56 | // }
57 | }
58 | }
59 |
60 | @Composable
61 | fun Greeting(name: String) {
62 | Text(text = "Hello $name!")
63 | }
64 |
65 | @Preview(
66 | showSystemUi = true,
67 | name = "Preview Name"
68 | )
69 | @Composable
70 | fun DefaultPreview() {
71 | ComposeBasicTheme {
72 | Greeting("Android")
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/java/icu/bughub/app/composebasic/components/AnimationCanvasSample.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.composebasic.components
2 |
3 |
4 | import androidx.compose.animation.core.animateOffsetAsState
5 | import androidx.compose.animation.core.tween
6 | import androidx.compose.foundation.Canvas
7 | import androidx.compose.foundation.background
8 | import androidx.compose.foundation.layout.*
9 | import androidx.compose.material.Button
10 | import androidx.compose.material.Surface
11 | import androidx.compose.material.Text
12 | import androidx.compose.runtime.Composable
13 | import androidx.compose.runtime.LaunchedEffect
14 | import androidx.compose.runtime.getValue
15 | import androidx.compose.runtime.rememberCoroutineScope
16 | import androidx.compose.ui.Modifier
17 | import androidx.compose.ui.geometry.Offset
18 | import androidx.compose.ui.graphics.Color
19 | import androidx.compose.ui.graphics.drawscope.translate
20 | import androidx.compose.ui.tooling.preview.Preview
21 | import androidx.compose.ui.unit.dp
22 | import kotlinx.coroutines.delay
23 | import kotlinx.coroutines.launch
24 |
25 |
26 | @Composable
27 | fun AnimationCanvasSample() {
28 |
29 | var endOffset = Offset(300f, 300f)
30 |
31 | val offsetAnimation by animateOffsetAsState(
32 | targetValue = endOffset,
33 | tween(durationMillis = 500, delayMillis = 500)
34 | )
35 |
36 | val coroutineScope = rememberCoroutineScope()
37 |
38 | Column(
39 | modifier = Modifier
40 | .fillMaxSize()
41 | .background(Color(0xffeeeeee))
42 | ) {
43 |
44 | Surface(
45 | color = Color.White,
46 | elevation = 1.dp,
47 | modifier = Modifier
48 | .fillMaxWidth()
49 | .height(65.dp)
50 | ) {
51 | Text(text = "TOP")
52 | }
53 |
54 | Surface(
55 | color = Color.White,
56 | elevation = 1.dp,
57 | modifier = Modifier
58 | .fillMaxWidth()
59 | .height(65.dp)
60 | .offset(y = 8.dp)
61 | ) {
62 | Text(text = "BOTTOM")
63 | }
64 |
65 |
66 | // Canvas(
67 | // modifier = Modifier
68 | // .size(300.dp)
69 | // .background(Color.Gray)
70 | // ) {
71 | // translate(offsetAnimation) {
72 | // drawLine(Color.Red, start = Offset.Zero, end = offsetAnimation)
73 | // }
74 | //
75 | //
76 | // }
77 | }
78 |
79 | }
80 |
81 | @Preview
82 | @Composable
83 | fun AnimationCanvasSamplePreview() {
84 | AnimationCanvasSample()
85 | }
86 |
87 |
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/java/icu/bughub/app/composebasic/components/BoxSample.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.composebasic.components
2 |
3 |
4 | import androidx.compose.foundation.background
5 | import androidx.compose.foundation.layout.Box
6 | import androidx.compose.foundation.layout.BoxWithConstraints
7 | import androidx.compose.foundation.layout.size
8 | import androidx.compose.material.Text
9 | import androidx.compose.runtime.Composable
10 | import androidx.compose.ui.Alignment
11 | import androidx.compose.ui.Modifier
12 | import androidx.compose.ui.graphics.Color
13 | import androidx.compose.ui.tooling.preview.Preview
14 | import androidx.compose.ui.unit.dp
15 |
16 |
17 | @Composable
18 | fun BoxSample() {
19 |
20 | // Box() {
21 | // Box(
22 | // modifier = Modifier
23 | // .size(200.dp)
24 | // .background(Color.Red)
25 | // )
26 | //
27 | // Box(
28 | // modifier = Modifier
29 | // .align(Alignment.Center)
30 | // .size(100.dp)
31 | // .background(Color.Green)
32 | // )
33 | // }
34 |
35 | BoxWithConstraints() {
36 |
37 | if (maxWidth < maxHeight){
38 | Box(
39 | modifier = Modifier
40 | .size(200.dp)
41 | .background(Color.Red)
42 | )
43 | } else {
44 | Box(
45 | modifier = Modifier
46 | .align(Alignment.Center)
47 | .size(100.dp)
48 | .background(Color.Green)
49 | )
50 | }
51 |
52 | }
53 | }
54 |
55 | @Preview
56 | @Composable
57 | fun BoxSamplePreview() {
58 | BoxSample()
59 | }
60 |
61 |
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/java/icu/bughub/app/composebasic/components/ButtonSample.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.composebasic.components
2 |
3 |
4 | import android.util.Log
5 | import androidx.compose.material.*
6 | import androidx.compose.runtime.Composable
7 | import androidx.compose.ui.graphics.Color
8 | import androidx.compose.ui.tooling.preview.Preview
9 |
10 |
11 | @Composable
12 | fun ButtonSample() {
13 | // Button(onClick = {
14 | // Log.d("====", "我是中国人")
15 | // }, colors = ButtonDefaults.buttonColors(backgroundColor = Color.Yellow)) {
16 | // Text("不点不是中国人")
17 | // }
18 |
19 | // TextButton(onClick = { }) {
20 | // Text("不点不是中国人")
21 | // }
22 |
23 | OutlinedButton(onClick = { }) {
24 | Text("不点不是中国人")
25 | }
26 | }
27 |
28 | @Preview
29 | @Composable
30 | fun ButtonSamplePreview() {
31 | ButtonSample()
32 | }
33 |
34 |
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/java/icu/bughub/app/composebasic/components/CanvasSample.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.composebasic.components
2 |
3 |
4 | import androidx.compose.foundation.Canvas
5 | import androidx.compose.foundation.layout.size
6 | import androidx.compose.runtime.Composable
7 | import androidx.compose.ui.Modifier
8 | import androidx.compose.ui.geometry.CornerRadius
9 | import androidx.compose.ui.geometry.Offset
10 | import androidx.compose.ui.geometry.Size
11 | import androidx.compose.ui.graphics.Color
12 | import androidx.compose.ui.graphics.ImageBitmap
13 | import androidx.compose.ui.graphics.PointMode
14 | import androidx.compose.ui.graphics.StrokeCap
15 | import androidx.compose.ui.graphics.drawscope.Stroke
16 | import androidx.compose.ui.platform.LocalContext
17 | import androidx.compose.ui.res.imageResource
18 | import androidx.compose.ui.tooling.preview.Preview
19 | import androidx.compose.ui.unit.dp
20 | import icu.bughub.app.composebasic.R
21 |
22 |
23 | @Composable
24 | fun CanvasSample() {
25 |
26 | var imageBitmap: ImageBitmap? = null
27 | with(LocalContext.current) {
28 | imageBitmap = ImageBitmap.imageResource(id = R.drawable.newbanner4)
29 | }
30 |
31 | Canvas(modifier = Modifier.size(400.dp)) {
32 |
33 | // drawLine(
34 | // Color.Yellow,
35 | // start = Offset(0f, 10f),
36 | // end = Offset(100f, 200f),
37 | // strokeWidth = 100f,
38 | // cap = StrokeCap.Round
39 | // )
40 | //
41 | // drawRect(
42 | // Color.Green,
43 | // topLeft = Offset(100f, 200f),
44 | // size = Size(100f, 100f)
45 | // )
46 | //
47 | //// imageBitmap?.let {
48 | //// drawImage(it)
49 | //// }
50 | //
51 | // drawRoundRect(
52 | // Color.Green,
53 | // topLeft = Offset(100f, 200f),
54 | // size = Size(200f, 200f),
55 | // cornerRadius = CornerRadius(50f, 50f)
56 | // )
57 | //
58 | // drawCircle(Color.Blue, style = Stroke(width = 10f))
59 | //
60 | // drawOval(Color.Red, size = Size(300f, 200f))
61 | //
62 | // drawArc(
63 | // Color.Magenta,
64 | // startAngle = 0f,
65 | // sweepAngle = 30f,
66 | // useCenter = true,
67 | // style = Stroke(10f)
68 | // )
69 |
70 | drawPoints(
71 | listOf(
72 | Offset(10f, 10f),
73 | Offset(20f, 50f),
74 | Offset(40f, 60f),
75 | Offset(60f, 100f)
76 | ),
77 | pointMode = PointMode.Points,
78 | color = Color.Magenta
79 | )
80 |
81 | }
82 |
83 | }
84 |
85 | @Preview
86 | @Composable
87 | fun CanvasSamplePreview() {
88 | CanvasSample()
89 | }
90 |
91 |
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/java/icu/bughub/app/composebasic/components/CardSample.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.composebasic.components
2 |
3 |
4 | import androidx.compose.foundation.BorderStroke
5 | import androidx.compose.foundation.layout.padding
6 | import androidx.compose.material.Card
7 | import androidx.compose.material.Text
8 | import androidx.compose.runtime.Composable
9 | import androidx.compose.ui.Modifier
10 | import androidx.compose.ui.graphics.Color
11 | import androidx.compose.ui.tooling.preview.Preview
12 | import androidx.compose.ui.unit.dp
13 |
14 |
15 | @Composable
16 | fun CardSample() {
17 |
18 | Card(
19 | backgroundColor = Color.Green,
20 | contentColor = Color.Red,
21 | border = BorderStroke(1.dp, Color.Red),
22 | elevation = 10.dp
23 | ) {
24 | Text(
25 | "Card Sample",
26 | modifier = Modifier.padding(8.dp),
27 | )
28 | }
29 |
30 | }
31 |
32 | @Preview
33 | @Composable
34 | fun CardSamplePreview() {
35 | CardSample()
36 | }
37 |
38 |
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/java/icu/bughub/app/composebasic/components/CheckBoxSample.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.composebasic.components
2 |
3 |
4 | import androidx.compose.foundation.layout.Column
5 | import androidx.compose.material.Checkbox
6 | import androidx.compose.runtime.*
7 | import androidx.compose.ui.tooling.preview.Preview
8 |
9 |
10 | @Composable
11 | fun CheckBoxSample() {
12 |
13 | // var checked by remember {
14 | // mutableStateOf(false)
15 | // }
16 | //
17 | // Checkbox(checked = checked, onCheckedChange = {
18 | // checked = it
19 | // })
20 |
21 | var checkedList by remember {
22 | mutableStateOf(listOf(false, false))
23 | }
24 |
25 |
26 | Column {
27 |
28 | checkedList.forEachIndexed { i, item ->
29 |
30 | Checkbox(checked = item, onCheckedChange = {
31 |
32 | checkedList = checkedList.mapIndexed { j, b ->
33 |
34 | if (i == j) {
35 | !b
36 | } else {
37 | b
38 | }
39 |
40 | }
41 |
42 | })
43 |
44 | }
45 |
46 | }
47 |
48 |
49 | }
50 |
51 | @Preview
52 | @Composable
53 | fun CheckBoxSamplePreview() {
54 | CheckBoxSample()
55 | }
56 |
57 |
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/java/icu/bughub/app/composebasic/components/ColumnSample.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.composebasic.components
2 |
3 |
4 | import androidx.compose.foundation.background
5 | import androidx.compose.foundation.layout.Arrangement
6 | import androidx.compose.foundation.layout.Column
7 | import androidx.compose.foundation.layout.size
8 | import androidx.compose.material.Text
9 | import androidx.compose.runtime.Composable
10 | import androidx.compose.ui.Modifier
11 | import androidx.compose.ui.graphics.Color
12 | import androidx.compose.ui.tooling.preview.Preview
13 | import androidx.compose.ui.unit.dp
14 |
15 |
16 | @Composable
17 | fun ColumnSample() {
18 |
19 | Column(
20 | modifier = Modifier
21 | .size(200.dp)
22 | .background(Color.Green),
23 | ) {
24 | Text(
25 | text = "Column First Item",
26 | modifier = Modifier.weight(1f)
27 | .background(Color.Red)
28 | )
29 | Text(
30 | text = "Column Second Item",
31 | modifier = Modifier.weight(1f)
32 | )
33 | }
34 |
35 | }
36 |
37 | @Preview
38 | @Composable
39 | fun ColumnSamplePreview() {
40 | ColumnSample()
41 | }
42 |
43 |
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/java/icu/bughub/app/composebasic/components/CompositionLocalSample.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.composebasic.components
2 |
3 |
4 | import android.net.wifi.hotspot2.pps.HomeSp
5 | import androidx.compose.foundation.layout.Column
6 | import androidx.compose.material.Button
7 | import androidx.compose.material.Text
8 | import androidx.compose.runtime.Composable
9 | import androidx.compose.runtime.CompositionLocalProvider
10 | import androidx.compose.runtime.compositionLocalOf
11 | import androidx.compose.ui.tooling.preview.Preview
12 | import androidx.navigation.compose.NavHost
13 | import androidx.navigation.compose.composable
14 | import androidx.navigation.compose.rememberNavController
15 |
16 |
17 | @Composable
18 | fun CompositionLocalSample() {
19 |
20 | val navController = rememberNavController()
21 | val user = User("Test")
22 |
23 | CompositionLocalProvider(LocalActiveUser provides user) {
24 | NavHost(navController = navController, startDestination = "Home") {
25 | composable("Home") {
26 | HomeScreen {
27 | navController.navigate("Detail")
28 | }
29 | }
30 |
31 | composable("Detail") {
32 | DetailScreen()
33 | }
34 | }
35 | }
36 | }
37 |
38 | @Composable
39 | fun HomeScreen(onTap: () -> Unit) {
40 | Column {
41 | Text(text = "HomeScreen:${LocalActiveUser.current.name}")
42 |
43 | Button(onClick = { onTap() }) {
44 | Text(text = "Navigate to Detail")
45 | }
46 | }
47 | }
48 |
49 | @Composable
50 | fun DetailScreen() {
51 | Text(text = "DetailScreen:${LocalActiveUser.current.name}")
52 | }
53 |
54 | val LocalActiveUser = compositionLocalOf { error("user is null") }
55 |
56 | data class User(val name: String)
57 |
58 | @Preview
59 | @Composable
60 | fun CompositionLocalSamplePreview() {
61 | CompositionLocalSample()
62 | }
63 |
64 |
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/java/icu/bughub/app/composebasic/components/ConstaintLayoutSample.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.composebasic.components
2 |
3 |
4 | import androidx.compose.foundation.background
5 | import androidx.compose.foundation.layout.fillMaxWidth
6 | import androidx.compose.foundation.layout.height
7 | import androidx.compose.material.Checkbox
8 | import androidx.compose.material.Icon
9 | import androidx.compose.material.Text
10 | import androidx.compose.material.icons.Icons
11 | import androidx.compose.material.icons.filled.AccountBox
12 | import androidx.compose.runtime.*
13 | import androidx.compose.ui.Modifier
14 | import androidx.compose.ui.graphics.Color
15 | import androidx.compose.ui.layout.layoutId
16 | import androidx.compose.ui.text.font.FontWeight
17 | import androidx.compose.ui.tooling.preview.Preview
18 | import androidx.compose.ui.unit.dp
19 | import androidx.compose.ui.unit.sp
20 | import androidx.constraintlayout.compose.ConstraintLayout
21 | import androidx.constraintlayout.compose.ConstraintSet
22 |
23 |
24 | @Composable
25 | fun ConstraintLayoutSample() {
26 |
27 | var checked by remember {
28 | mutableStateOf(
29 | false
30 | )
31 | }
32 |
33 | val constraints = ConstraintSet {
34 | val icon = createRefFor("icon")
35 | val primaryText = createRefFor("primaryText")
36 | val secondlyText = createRefFor("secondlyText")
37 | val checkBox = createRefFor("checkBox")
38 |
39 | constrain(icon) {
40 | centerVerticallyTo(parent)
41 | start.linkTo(parent.start, margin = 8.dp)
42 | }
43 |
44 | constrain(primaryText) {
45 | start.linkTo(icon.end, margin = 8.dp)
46 | top.linkTo(parent.top, margin = 8.dp)
47 | }
48 |
49 | constrain(secondlyText) {
50 | start.linkTo(primaryText.start)
51 | top.linkTo(primaryText.bottom, margin = 8.dp)
52 | bottom.linkTo(parent.bottom, margin = 8.dp)
53 | }
54 |
55 | constrain(checkBox) {
56 | centerVerticallyTo(parent)
57 | end.linkTo(parent.end, margin = 8.dp)
58 | }
59 | }
60 |
61 | ConstraintLayout(
62 | modifier = Modifier
63 | .fillMaxWidth()
64 | .height(100.dp)
65 | .background(Color.Yellow),
66 | constraintSet = constraints
67 | ) {
68 |
69 | Icon(
70 | imageVector = Icons.Default.AccountBox,
71 | contentDescription = null,
72 | modifier = Modifier.layoutId("icon")
73 | )
74 |
75 | Text(
76 | text = "Primary Text",
77 | fontWeight = FontWeight.Bold,
78 | fontSize = 16.sp,
79 | modifier = Modifier.layoutId("primaryText")
80 | )
81 |
82 | Text(
83 | text = "Secondly Text", color = Color.Gray,
84 | modifier = Modifier.layoutId("secondlyText")
85 | )
86 |
87 | Checkbox(
88 | checked = checked,
89 | onCheckedChange = { checked = it },
90 | modifier = Modifier.layoutId("checkBox")
91 | )
92 | }
93 |
94 | }
95 |
96 | @Preview
97 | @Composable
98 | fun ConstraintLayoutSamplePreview() {
99 | ConstraintLayoutSample()
100 | }
101 |
102 |
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/java/icu/bughub/app/composebasic/components/ConstraintsSample.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.composebasic.components
2 |
3 |
4 | import androidx.compose.foundation.background
5 | import androidx.compose.foundation.layout.fillMaxWidth
6 | import androidx.compose.foundation.layout.height
7 | import androidx.compose.material.Button
8 | import androidx.compose.material.Checkbox
9 | import androidx.compose.material.Icon
10 | import androidx.compose.material.Text
11 | import androidx.compose.material.icons.Icons
12 | import androidx.compose.material.icons.filled.AccountBox
13 | import androidx.compose.runtime.*
14 | import androidx.compose.ui.Modifier
15 | import androidx.compose.ui.graphics.Color
16 | import androidx.compose.ui.text.font.FontWeight
17 | import androidx.compose.ui.tooling.preview.Preview
18 | import androidx.compose.ui.unit.dp
19 | import androidx.compose.ui.unit.sp
20 | import androidx.constraintlayout.compose.ConstraintLayout
21 |
22 |
23 | @Composable
24 | fun ConstraintsSample() {
25 |
26 | var checked by remember {
27 | mutableStateOf(
28 | false
29 | )
30 | }
31 |
32 | ConstraintLayout(
33 | modifier = Modifier
34 | .fillMaxWidth()
35 | .height(100.dp)
36 | .background(Color.Yellow)
37 | ) {
38 | val (icon, primaryText, secondlyText, checkBox) = createRefs()
39 |
40 | Icon(
41 | imageVector = Icons.Default.AccountBox,
42 | contentDescription = null,
43 | modifier = Modifier.constrainAs(icon) {
44 | top.linkTo(parent.top)
45 | bottom.linkTo(parent.bottom)
46 | start.linkTo(parent.start, 8.dp)
47 | }
48 | )
49 |
50 | Text(
51 | text = "Primary Text",
52 | fontWeight = FontWeight.Bold,
53 | fontSize = 16.sp,
54 | modifier = Modifier.constrainAs(primaryText) {
55 | start.linkTo(icon.end, margin = 8.dp)
56 | top.linkTo(parent.top)
57 | }
58 | )
59 |
60 | Text(text = "Secondly Text", color = Color.Gray,
61 | modifier = Modifier.constrainAs(secondlyText) {
62 | start.linkTo(primaryText.start)
63 | top.linkTo(primaryText.bottom, margin = 8.dp)
64 | bottom.linkTo(parent.bottom)
65 | })
66 |
67 | Checkbox(
68 | checked = checked,
69 | onCheckedChange = { checked = it },
70 | modifier = Modifier.constrainAs(checkBox) {
71 | centerVerticallyTo(parent)
72 | end.linkTo(parent.end, margin = 8.dp)
73 | }
74 | )
75 | }
76 |
77 | }
78 |
79 | @Composable
80 | fun ConstraintLayoutSample1() {
81 | ConstraintLayout(
82 | modifier = Modifier
83 | .fillMaxWidth()
84 | .height(100.dp)
85 | .background(Color.Yellow)
86 | ) {
87 |
88 | val (button, text) = createRefs()
89 |
90 | Button(onClick = { }, modifier = Modifier.constrainAs(button) {
91 | centerTo(parent)
92 | }) {
93 | Text(text = "Button")
94 | }
95 |
96 | Text(text = "Text", modifier = Modifier.constrainAs(text) {
97 | top.linkTo(button.bottom, 8.dp)
98 | start.linkTo(button.start)
99 | })
100 | }
101 | }
102 |
103 | @Preview
104 | @Composable
105 | fun ConstraintsSamplePreview() {
106 | ConstraintLayoutSample1()
107 | }
108 |
109 |
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/java/icu/bughub/app/composebasic/components/DialogSample.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.composebasic.components
2 |
3 |
4 | import androidx.compose.foundation.layout.Column
5 | import androidx.compose.foundation.layout.size
6 | import androidx.compose.material.*
7 | import androidx.compose.runtime.*
8 | import androidx.compose.ui.Modifier
9 | import androidx.compose.ui.graphics.Color
10 | import androidx.compose.ui.text.input.KeyboardType.Companion.Text
11 | import androidx.compose.ui.tooling.preview.Preview
12 | import androidx.compose.ui.unit.dp
13 | import androidx.compose.ui.window.Dialog
14 |
15 |
16 | @Composable
17 | fun DialogSample() {
18 |
19 | var showDialog by remember {
20 | mutableStateOf(false)
21 | }
22 |
23 | Column() {
24 | Button(onClick = {
25 | showDialog = true
26 | }) {
27 | Text("show dialog")
28 | }
29 | if (showDialog) {
30 | // Dialog(
31 | // onDismissRequest = { showDialog = false }
32 | // ) {
33 | // Surface(
34 | // color = Color.White,
35 | // modifier = Modifier.size(
36 | // 200.dp, 100.dp
37 | // )
38 | // ) {
39 | // Column() {
40 | // Text("Dialog Content")
41 | // }
42 | // }
43 | // }
44 |
45 | AlertDialog(
46 | onDismissRequest = { showDialog = false },
47 | title = {
48 | Text("Title")
49 | },
50 | confirmButton = {
51 | TextButton(onClick = { showDialog = false }) {
52 | Text(text = "Confirm")
53 | }
54 | },
55 | dismissButton = {
56 | TextButton(onClick = { showDialog = false }) {
57 | Text(text = "Cancel")
58 | }
59 | },
60 | text = {
61 | Text("这是 Dialog 的内容")
62 | }
63 | )
64 |
65 | }
66 |
67 | }
68 |
69 | }
70 |
71 | @Preview
72 | @Composable
73 | fun DialogSamplePreview() {
74 | DialogSample()
75 | }
76 |
77 |
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/java/icu/bughub/app/composebasic/components/DividerSample.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.composebasic.components
2 |
3 |
4 | import androidx.compose.foundation.background
5 | import androidx.compose.foundation.layout.Column
6 | import androidx.compose.foundation.layout.Spacer
7 | import androidx.compose.foundation.layout.height
8 | import androidx.compose.foundation.layout.size
9 | import androidx.compose.material.Divider
10 | import androidx.compose.material.Text
11 | import androidx.compose.runtime.Composable
12 | import androidx.compose.ui.Modifier
13 | import androidx.compose.ui.graphics.Color
14 | import androidx.compose.ui.tooling.preview.Preview
15 | import androidx.compose.ui.unit.dp
16 |
17 |
18 | @Composable
19 | fun DividerSample() {
20 |
21 | Column(
22 | modifier = Modifier
23 | .size(200.dp)
24 | .background(Color.Green),
25 | ) {
26 | Text(
27 | text = "Column First Item",
28 | modifier = Modifier
29 | .background(Color.Red)
30 | )
31 |
32 | Divider(
33 | startIndent = 4.dp,
34 | thickness = 10.dp,
35 | color = Color.Magenta
36 | )
37 |
38 | Text(
39 | text = "Column Second Item",
40 | modifier = Modifier
41 | .background(Color.Yellow)
42 | )
43 | }
44 |
45 | }
46 |
47 | @Preview
48 | @Composable
49 | fun DividerSamplePreview() {
50 | DividerSample()
51 | }
52 |
53 |
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/java/icu/bughub/app/composebasic/components/DropdownMenuSample.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.composebasic.components
2 |
3 |
4 | import androidx.compose.foundation.layout.Column
5 | import androidx.compose.material.Button
6 | import androidx.compose.material.DropdownMenu
7 | import androidx.compose.material.DropdownMenuItem
8 | import androidx.compose.material.Text
9 | import androidx.compose.runtime.*
10 | import androidx.compose.ui.tooling.preview.Preview
11 |
12 |
13 | @Composable
14 | fun DropdownMenuSample() {
15 |
16 | var expanded by remember {
17 | mutableStateOf(false)
18 | }
19 |
20 | Column() {
21 | Button(onClick = {
22 | expanded = true
23 | }) {
24 | Text("快快点我吧")
25 | }
26 |
27 | DropdownMenu(
28 | expanded = expanded,
29 | onDismissRequest = {
30 | expanded = false
31 | }
32 | ) {
33 | DropdownMenuItem(onClick = {
34 | expanded = false
35 | }) {
36 | Text("Menu 0")
37 | }
38 |
39 | DropdownMenuItem(onClick = {
40 | expanded = false
41 | }) {
42 | Text("Menu 1")
43 | }
44 |
45 | DropdownMenuItem(onClick = {
46 | expanded = false
47 | }) {
48 | Text("Menu 2")
49 | }
50 |
51 | }
52 | }
53 |
54 | }
55 |
56 | @Preview
57 | @Composable
58 | fun DropdownMenuSamplePreview() {
59 | DropdownMenuSample()
60 | }
61 |
62 |
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/java/icu/bughub/app/composebasic/components/IconSample.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.composebasic.components
2 |
3 |
4 | import androidx.compose.material.Icon
5 | import androidx.compose.material.IconButton
6 | import androidx.compose.material.icons.Icons
7 | import androidx.compose.material.icons.filled.AccountBox
8 | import androidx.compose.material.icons.filled.Translate
9 | import androidx.compose.runtime.Composable
10 | import androidx.compose.ui.graphics.Color
11 | import androidx.compose.ui.res.painterResource
12 | import androidx.compose.ui.tooling.preview.Preview
13 | import icu.bughub.app.composebasic.R
14 |
15 |
16 | @Composable
17 | fun IconSample() {
18 | // Icon(imageVector = Icons.Default.Translate, contentDescription = null, tint = Color.Red)
19 |
20 | IconButton(onClick = { }) {
21 | Icon(
22 | painter = painterResource(id = R.drawable.ic_android_black_24dp),
23 | contentDescription = null
24 | )
25 | }
26 | }
27 |
28 | @Preview
29 | @Composable
30 | fun IconSamplePreview() {
31 | IconSample()
32 | }
33 |
34 |
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/java/icu/bughub/app/composebasic/components/ImageSample.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.composebasic.components
2 |
3 |
4 | import androidx.compose.foundation.Image
5 | import androidx.compose.foundation.layout.size
6 | import androidx.compose.runtime.Composable
7 | import androidx.compose.ui.Modifier
8 | import androidx.compose.ui.graphics.BlendMode
9 | import androidx.compose.ui.graphics.Color
10 | import androidx.compose.ui.graphics.ColorFilter
11 | import androidx.compose.ui.layout.ContentScale
12 | import androidx.compose.ui.res.painterResource
13 | import androidx.compose.ui.tooling.preview.Preview
14 | import androidx.compose.ui.unit.dp
15 | import icu.bughub.app.composebasic.R
16 |
17 |
18 | @Composable
19 | fun ImageSample() {
20 | Image(
21 | painter = painterResource(id = R.drawable.newbanner4),
22 | contentDescription = null,
23 | modifier = Modifier.size(50.dp),
24 | colorFilter = ColorFilter.tint(Color.Red, blendMode = BlendMode.Color)
25 | )
26 | }
27 |
28 | @Preview
29 | @Composable
30 | fun ImageSamplePreview() {
31 | ImageSample()
32 | }
33 |
34 |
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/java/icu/bughub/app/composebasic/components/LazyColumnSample.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.composebasic.components
2 |
3 |
4 | import android.util.Log
5 | import androidx.compose.foundation.background
6 | import androidx.compose.foundation.clickable
7 | import androidx.compose.foundation.gestures.scrollBy
8 | import androidx.compose.foundation.gestures.scrollable
9 | import androidx.compose.foundation.layout.Column
10 | import androidx.compose.foundation.layout.fillMaxWidth
11 | import androidx.compose.foundation.layout.padding
12 | import androidx.compose.foundation.lazy.LazyColumn
13 | import androidx.compose.foundation.lazy.items
14 | import androidx.compose.foundation.lazy.rememberLazyListState
15 | import androidx.compose.foundation.rememberScrollState
16 | import androidx.compose.foundation.verticalScroll
17 | import androidx.compose.material.ExperimentalMaterialApi
18 | import androidx.compose.material.Icon
19 | import androidx.compose.material.ListItem
20 | import androidx.compose.material.Text
21 | import androidx.compose.material.icons.Icons
22 | import androidx.compose.material.icons.filled.AccountBox
23 | import androidx.compose.runtime.Composable
24 | import androidx.compose.runtime.DisposableEffect
25 | import androidx.compose.runtime.rememberCoroutineScope
26 | import androidx.compose.ui.Modifier
27 | import androidx.compose.ui.graphics.Color
28 | import androidx.compose.ui.tooling.preview.Preview
29 | import androidx.compose.ui.unit.dp
30 | import kotlinx.coroutines.launch
31 |
32 |
33 | @OptIn(ExperimentalMaterialApi::class)
34 | @Composable
35 | fun LazyColumnSample() {
36 |
37 | val data = listOf(
38 | 1, 2, 3, 4, 5,
39 | 6, 7, 8, 9, 10,
40 | 11, 12, 13, 14, 15,
41 | 16, 17, 18, 19, 20
42 | )
43 |
44 | val scrollState = rememberScrollState()
45 |
46 | val coroutineScope = rememberCoroutineScope()
47 |
48 | Column(
49 | modifier = Modifier.verticalScroll(scrollState)
50 | ) {
51 | data.forEach {
52 | ListItem(icon = {
53 | Icon(
54 | imageVector = Icons.Default.AccountBox,
55 | contentDescription = null
56 | )
57 | }, text = {
58 | Text("Title $it")
59 | }, secondaryText = {
60 | Text("secondaryText")
61 | }, modifier = Modifier.clickable {
62 | coroutineScope.launch {
63 | scrollState.scrollBy(100f)
64 | }
65 | })
66 | DisposableEffect(Unit) {
67 | Log.d("====", "effect:$it")
68 | onDispose {
69 | Log.d("====", "onDispose:$it")
70 | }
71 | }
72 | }
73 | }
74 |
75 | }
76 |
77 | @OptIn(ExperimentalMaterialApi::class, androidx.compose.foundation.ExperimentalFoundationApi::class)
78 | @Composable
79 | fun LazyColumnSample1() {
80 | val data = listOf(
81 | 1, 2, 3, 4, 5,
82 | 6, 7, 8, 9, 10,
83 | 11, 12, 13, 14, 15,
84 | 16, 17, 18, 19, 20
85 | )
86 |
87 | val lazyListState = rememberLazyListState()
88 | val coroutineScope = rememberCoroutineScope()
89 |
90 | LazyColumn(state = lazyListState) {
91 | stickyHeader {
92 | Text(
93 | "Sticky Header",
94 | modifier = Modifier
95 | .background(Color.Yellow)
96 | .fillMaxWidth()
97 | .padding(8.dp)
98 | )
99 | }
100 | items(data) {
101 | ListItem(icon = {
102 | Icon(
103 | imageVector = Icons.Default.AccountBox,
104 | contentDescription = null
105 | )
106 | }, text = {
107 | Text("Title $it")
108 | }, secondaryText = {
109 | Text("secondaryText")
110 | }, modifier = Modifier.clickable {
111 | coroutineScope.launch {
112 | lazyListState.animateScrollToItem(data.size - 1)
113 | }
114 | })
115 | DisposableEffect(Unit) {
116 | Log.d("====", "effect:$it")
117 | onDispose {
118 | Log.d("====", "onDispose:$it")
119 | }
120 | }
121 | }
122 | }
123 | }
124 |
125 | @Preview
126 | @Composable
127 | fun LazyColumnSamplePreview() {
128 | LazyColumnSample1()
129 | }
130 |
131 |
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/java/icu/bughub/app/composebasic/components/LazyRowSample.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.composebasic.components
2 |
3 |
4 | import androidx.compose.foundation.ExperimentalFoundationApi
5 | import androidx.compose.foundation.background
6 | import androidx.compose.foundation.layout.padding
7 | import androidx.compose.foundation.lazy.LazyRow
8 | import androidx.compose.foundation.lazy.items
9 | import androidx.compose.material.Text
10 | import androidx.compose.runtime.Composable
11 | import androidx.compose.ui.Modifier
12 | import androidx.compose.ui.graphics.Color
13 | import androidx.compose.ui.tooling.preview.Preview
14 | import androidx.compose.ui.unit.dp
15 |
16 |
17 | @OptIn(ExperimentalFoundationApi::class)
18 | @Composable
19 | fun LazyRowSample() {
20 |
21 | val data = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9)
22 |
23 |
24 |
25 | LazyRow() {
26 |
27 | stickyHeader {
28 | Text(
29 | text = "Header",
30 | modifier = Modifier.background(Color.Yellow)
31 | )
32 | }
33 |
34 | items(data) {
35 |
36 | Text(
37 | "Row Item $it",
38 | modifier = Modifier.padding(8.dp)
39 | )
40 |
41 | }
42 |
43 | }
44 |
45 | }
46 |
47 | @Preview
48 | @Composable
49 | fun LazyRowSamplePreview() {
50 | LazyRowSample()
51 | }
52 |
53 |
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/java/icu/bughub/app/composebasic/components/LazyVerticalGridSample.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.composebasic.components
2 |
3 |
4 | import androidx.compose.foundation.ExperimentalFoundationApi
5 | import androidx.compose.foundation.layout.padding
6 | import androidx.compose.foundation.lazy.GridCells
7 | import androidx.compose.foundation.lazy.LazyVerticalGrid
8 | import androidx.compose.foundation.lazy.items
9 | import androidx.compose.material.Card
10 | import androidx.compose.material.Text
11 | import androidx.compose.runtime.Composable
12 | import androidx.compose.ui.Modifier
13 | import androidx.compose.ui.tooling.preview.Preview
14 | import androidx.compose.ui.unit.dp
15 |
16 |
17 | @OptIn(ExperimentalFoundationApi::class)
18 | @Composable
19 | fun LazyVerticalGridSample() {
20 |
21 | val data = listOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
22 |
23 | LazyVerticalGrid(cells = GridCells.Adaptive(100.dp)) {
24 | items(data) {
25 | Card() {
26 | Text(
27 | "Grid Item $it",
28 | modifier = Modifier.padding(8.dp)
29 | )
30 | }
31 | }
32 | }
33 |
34 | }
35 |
36 | @Preview
37 | @Composable
38 | fun LazyVerticalGridSamplePreview() {
39 | LazyVerticalGridSample()
40 | }
41 |
42 |
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/java/icu/bughub/app/composebasic/components/LifecycleSample.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.composebasic.components
2 |
3 |
4 | import android.util.Log
5 | import androidx.compose.foundation.layout.Column
6 | import androidx.compose.material.Button
7 | import androidx.compose.material.Text
8 | import androidx.compose.runtime.*
9 | import androidx.compose.ui.platform.LocalLifecycleOwner
10 | import androidx.compose.ui.tooling.preview.Preview
11 | import androidx.lifecycle.Lifecycle
12 | import androidx.lifecycle.LifecycleEventObserver
13 | import kotlinx.coroutines.coroutineScope
14 |
15 |
16 | @Composable
17 | fun LifecycleSample() {
18 |
19 | var count by remember {
20 | mutableStateOf(0)
21 | }
22 |
23 | // LaunchedEffect(Unit) {
24 | // Log.i("======", "LaunchedEffect")
25 | // }
26 | //
27 | // Log.i("======", "LifecycleSample")
28 |
29 | val lifecycleOwner = LocalLifecycleOwner.current
30 |
31 | DisposableEffect(lifecycleOwner) {
32 |
33 | val lifecycleEventObserver = LifecycleEventObserver { _, event ->
34 | when (event) {
35 | Lifecycle.Event.ON_PAUSE -> {
36 | //进入后台,调用暂停方法
37 | }
38 | Lifecycle.Event.ON_RESUME -> {
39 | //回到前台 ,调用播放
40 | }
41 | else -> {
42 |
43 | }
44 | }
45 | }
46 |
47 | lifecycleOwner.lifecycle.addObserver(lifecycleEventObserver)
48 |
49 | onDispose {
50 | lifecycleOwner.lifecycle.removeObserver(lifecycleEventObserver)
51 | }
52 | }
53 |
54 | Column() {
55 | Text("我叫了${count}个小姐姐")
56 |
57 | Button(onClick = {
58 | count++
59 | }) {
60 | Text("Button")
61 | }
62 |
63 | if (count == 3) {
64 | SubScreen(count)
65 | }
66 | }
67 |
68 | }
69 |
70 | @Composable
71 | fun SubScreen(count: Int) {
72 |
73 | DisposableEffect(Unit) {
74 |
75 | Log.i("======", "DisposableEffect")
76 |
77 | onDispose {
78 | Log.i("======", "DisposableEffect:onDispose")
79 | }
80 | }
81 |
82 | Text(text = "我儿子也叫了${count}个小姐姐")
83 | }
84 |
85 | @Preview
86 | @Composable
87 | fun LifecycleSamplePreview() {
88 | LifecycleSample()
89 | }
90 |
91 |
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/java/icu/bughub/app/composebasic/components/ListItemSample.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.composebasic.components
2 |
3 |
4 | import android.util.Log
5 | import androidx.compose.foundation.layout.Column
6 | import androidx.compose.material.*
7 | import androidx.compose.material.icons.Icons
8 | import androidx.compose.material.icons.filled.AccountBox
9 | import androidx.compose.runtime.*
10 | import androidx.compose.ui.tooling.preview.Preview
11 |
12 |
13 | @OptIn(ExperimentalMaterialApi::class)
14 | @Composable
15 | fun ListItemSample() {
16 |
17 | var list by remember {
18 | mutableStateOf(
19 | listOf(
20 | ListItem("Item 0", true),
21 | ListItem("Item 1", false),
22 | ListItem("Item 2", false),
23 | ListItem("Item 3", false),
24 | ListItem("Item 4", false)
25 | )
26 | )
27 | }
28 |
29 | Log.i("======1","list:${list}")
30 |
31 | Column() {
32 | list.forEachIndexed { rawIndex, listItem ->
33 |
34 | ListItem(icon = {
35 | Icon(
36 | imageVector = Icons.Default.AccountBox,
37 | contentDescription = null
38 | )
39 | }, text = {
40 | Text(text = listItem.title)
41 | }, secondaryText = {
42 | Text("Secondary Text")
43 | }, trailing = {
44 | Checkbox(checked = listItem.checked, onCheckedChange = {
45 |
46 | list = list.mapIndexed { newIndex, listItem ->
47 | val newItem = listItem.copy()
48 | if (rawIndex == newIndex) {
49 | newItem.checked = !listItem.checked
50 | } else {
51 | newItem.checked = listItem.checked
52 | }
53 | return@mapIndexed newItem
54 | }
55 | Log.i("======2","list:${list}")
56 | })
57 | })
58 |
59 | }
60 | }
61 |
62 | }
63 |
64 | data class ListItem(val title: String, var checked: Boolean)
65 |
66 | @Preview
67 | @Composable
68 | fun ListItemSamplePreview() {
69 | ListItemSample()
70 | }
71 |
72 |
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/java/icu/bughub/app/composebasic/components/ModifierSample.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.composebasic.components
2 |
3 |
4 | import android.util.Log
5 | import androidx.compose.foundation.background
6 | import androidx.compose.foundation.border
7 | import androidx.compose.foundation.clickable
8 | import androidx.compose.foundation.layout.padding
9 | import androidx.compose.material.Text
10 | import androidx.compose.runtime.Composable
11 | import androidx.compose.ui.Modifier
12 | import androidx.compose.ui.graphics.Color
13 | import androidx.compose.ui.text.TextStyle
14 | import androidx.compose.ui.tooling.preview.Preview
15 | import androidx.compose.ui.unit.dp
16 |
17 |
18 | @Composable
19 | fun ModifierSample() {
20 | Text(
21 | text = "中国人不骗中国人",
22 | modifier = Modifier
23 | .border(1.dp, Color.Red)
24 | .background(Color.Yellow)
25 | .padding(8.dp)
26 | .clickable {
27 | Log.i("====","你点击到我了")
28 | }
29 | )
30 | }
31 |
32 | @Preview
33 | @Composable
34 | fun ModifierSamplePreview() {
35 | ModifierSample()
36 | }
37 |
38 |
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/java/icu/bughub/app/composebasic/components/NavSample.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.composebasic.components
2 |
3 |
4 | import androidx.compose.foundation.background
5 | import androidx.compose.foundation.layout.Arrangement
6 | import androidx.compose.foundation.layout.Column
7 | import androidx.compose.foundation.layout.fillMaxSize
8 | import androidx.compose.material.Button
9 | import androidx.compose.material.Text
10 | import androidx.compose.runtime.Composable
11 | import androidx.compose.ui.Alignment
12 | import androidx.compose.ui.Modifier
13 | import androidx.compose.ui.graphics.Color
14 | import androidx.compose.ui.tooling.preview.Preview
15 | import androidx.navigation.NavHostController
16 | import androidx.navigation.NavType
17 | import androidx.navigation.compose.NavHost
18 | import androidx.navigation.compose.composable
19 | import androidx.navigation.compose.rememberNavController
20 | import androidx.navigation.navArgument
21 |
22 |
23 | @Composable
24 | fun NavSample() {
25 |
26 | val navController = rememberNavController()
27 |
28 | NavHost(
29 | navController = navController,
30 | startDestination = Screen.First.route
31 | ) {
32 |
33 | composable(route = Screen.First.route) {
34 | FirstScreen() {
35 | navController.navigate("${Screen.Second.route}/韦爵爷/19")
36 | }
37 | }
38 |
39 | composable(
40 | route = "${Screen.Second.route}/{name}/{age}", arguments = listOf(
41 | navArgument("age") {
42 | type = NavType.IntType
43 | }
44 | )
45 | ) {
46 | val name = it.arguments?.getString("name")
47 | val age = it.arguments?.getInt("age") ?: 18
48 | SecondScreen(name, age) {
49 | navController.navigate("Third?carName=五菱宏光 Mini")
50 | }
51 | }
52 |
53 | composable(
54 | route = "${Screen.Third.route}?carName={carName}", arguments = listOf(
55 | navArgument("carName") {
56 | nullable = true
57 | defaultValue = "卡宴"
58 | }
59 | )
60 | ) {
61 | val carName = it.arguments?.getString("carName")
62 |
63 | ThirdScreen(carName) {
64 | navController.popBackStack(
65 | Screen.Second.route,
66 | inclusive = true
67 | )
68 | }
69 | }
70 |
71 | }
72 |
73 | }
74 |
75 | sealed class Screen(val route: String) {
76 | object First : Screen("First")
77 | object Second : Screen("Second")
78 | object Third : Screen("Third")
79 | }
80 |
81 | @Composable
82 | fun FirstScreen(onNavigateToSecond: () -> Unit) {
83 | Column(
84 | modifier = Modifier
85 | .fillMaxSize()
86 | .background(Color.Red),
87 | horizontalAlignment = Alignment.CenterHorizontally,
88 | verticalArrangement = Arrangement.Center,
89 | ) {
90 | Text(text = "First Screen")
91 |
92 | Button(onClick = {
93 | onNavigateToSecond()
94 | }) {
95 | Text(text = "Navigate to second screen")
96 | }
97 | }
98 | }
99 |
100 | @Composable
101 | fun SecondScreen(name: String?, age: Int, onNavigateToThird: () -> Unit) {
102 | Column(
103 | modifier = Modifier
104 | .fillMaxSize()
105 | .background(Color.Green),
106 | horizontalAlignment = Alignment.CenterHorizontally,
107 | verticalArrangement = Arrangement.Center,
108 | ) {
109 | Text(text = "Second Screen:$name 今年${age}岁")
110 |
111 | Button(onClick = {
112 | onNavigateToThird()
113 | }) {
114 | Text(text = "Navigate to third screen")
115 | }
116 | }
117 | }
118 |
119 | @Composable
120 | fun ThirdScreen(carName: String?, onBackToRoot: () -> Unit) {
121 | Column(
122 | modifier = Modifier
123 | .fillMaxSize()
124 | .background(Color.Blue),
125 | horizontalAlignment = Alignment.CenterHorizontally,
126 | verticalArrangement = Arrangement.Center,
127 | ) {
128 | Text(text = "Third Screen:$carName")
129 |
130 | Button(onClick = {
131 | onBackToRoot()
132 | }) {
133 | Text(text = "Navigate back to root screen")
134 | }
135 | }
136 | }
137 |
138 | @Preview
139 | @Composable
140 | fun NavSamplePreview() {
141 | NavSample()
142 | }
143 |
144 |
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/java/icu/bughub/app/composebasic/components/ProgressIndicatorSample.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.composebasic.components
2 |
3 |
4 | import androidx.compose.material.CircularProgressIndicator
5 | import androidx.compose.material.LinearProgressIndicator
6 | import androidx.compose.runtime.Composable
7 | import androidx.compose.ui.graphics.Color
8 | import androidx.compose.ui.tooling.preview.Preview
9 |
10 |
11 | @Composable
12 | fun ProgressIndicatorSample() {
13 |
14 | // CircularProgressIndicator(
15 | // color = Color.Red,
16 | // progress = 0.5f
17 | // )
18 |
19 | LinearProgressIndicator(
20 | color = Color.Red, backgroundColor = Color.Green, progress = 0.5f
21 | )
22 |
23 | }
24 |
25 | @Preview
26 | @Composable
27 | fun ProgressIndicatorSamplePreview() {
28 | ProgressIndicatorSample()
29 | }
30 |
31 |
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/java/icu/bughub/app/composebasic/components/RadioButtonSample.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.composebasic.components
2 |
3 |
4 | import androidx.compose.foundation.layout.Column
5 | import androidx.compose.material.RadioButton
6 | import androidx.compose.runtime.*
7 | import androidx.compose.ui.tooling.preview.Preview
8 |
9 |
10 | @Composable
11 | fun RadioButtonSample() {
12 |
13 | //单个按钮
14 | // var selected by remember {
15 | // mutableStateOf(false)
16 | // }
17 | //
18 | // RadioButton(selected = selected, onClick = {
19 | // selected = !selected
20 | // })
21 |
22 | //多个按钮
23 |
24 | var checkedList by remember {
25 |
26 | mutableStateOf(
27 | listOf(
28 | false, false
29 | )
30 | )
31 | }
32 |
33 | Column() {
34 |
35 | checkedList.forEachIndexed { i, item ->
36 | RadioButton(selected = item, onClick = {
37 | checkedList = checkedList.mapIndexed { j, _ ->
38 | i == j
39 | }
40 |
41 | })
42 | }
43 |
44 | }
45 |
46 | }
47 |
48 | @Preview
49 | @Composable
50 | fun RadioButtonSamplePreview() {
51 | RadioButtonSample()
52 | }
53 |
54 |
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/java/icu/bughub/app/composebasic/components/RowSample.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.composebasic.components
2 |
3 |
4 | import androidx.compose.foundation.background
5 | import androidx.compose.foundation.layout.Arrangement
6 | import androidx.compose.foundation.layout.Row
7 | import androidx.compose.foundation.layout.size
8 | import androidx.compose.material.Text
9 | import androidx.compose.runtime.Composable
10 | import androidx.compose.ui.Modifier
11 | import androidx.compose.ui.graphics.Color
12 | import androidx.compose.ui.tooling.preview.Preview
13 | import androidx.compose.ui.unit.dp
14 |
15 |
16 | @Composable
17 | fun RowSample() {
18 |
19 | Row(
20 | modifier = Modifier
21 | .size(400.dp)
22 | .background(Color.Green), horizontalArrangement = Arrangement.SpaceAround
23 | ) {
24 | Text(text = "Column First Item" )
25 | Text(text = "Column Second Item")
26 | }
27 |
28 | }
29 |
30 | @Preview
31 | @Composable
32 | fun RowSamplePreview() {
33 | RowSample()
34 | }
35 |
36 |
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/java/icu/bughub/app/composebasic/components/SliderSample.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.composebasic.components
2 |
3 |
4 | import androidx.compose.material.ExperimentalMaterialApi
5 | import androidx.compose.material.RangeSlider
6 | import androidx.compose.material.Slider
7 | import androidx.compose.runtime.*
8 | import androidx.compose.ui.tooling.preview.Preview
9 |
10 |
11 | @OptIn(ExperimentalMaterialApi::class)
12 | @Composable
13 | fun SliderSample() {
14 |
15 | // var value by remember {
16 | // mutableStateOf(0f)
17 | // }
18 | //
19 | // Slider(value = value, onValueChange = {
20 | // value = it
21 | // }, valueRange = 0f..100f, steps = 4)
22 |
23 | var values by remember {
24 | mutableStateOf(0.2f..0.5f)
25 | }
26 |
27 | RangeSlider(values = values, onValueChange = { values = it })
28 |
29 | }
30 |
31 | @Preview
32 | @Composable
33 | fun SliderSamplePreview() {
34 | SliderSample()
35 | }
36 |
37 |
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/java/icu/bughub/app/composebasic/components/SpacerSample.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.composebasic.components
2 |
3 |
4 | import androidx.compose.foundation.background
5 | import androidx.compose.foundation.layout.*
6 | import androidx.compose.material.Text
7 | import androidx.compose.runtime.Composable
8 | import androidx.compose.ui.Modifier
9 | import androidx.compose.ui.graphics.Color
10 | import androidx.compose.ui.tooling.preview.Preview
11 | import androidx.compose.ui.unit.dp
12 |
13 |
14 | @Composable
15 | fun SpacerSample() {
16 |
17 | Column(
18 | modifier = Modifier
19 | .size(200.dp)
20 | .background(Color.Green),
21 | ) {
22 | Text(
23 | text = "Column First Item",
24 | modifier = Modifier
25 | .background(Color.Red)
26 | )
27 |
28 | Spacer(modifier = Modifier.height(50.dp))
29 |
30 | Text(
31 | text = "Column Second Item",
32 | modifier = Modifier
33 | .background(Color.Yellow)
34 | )
35 | }
36 |
37 | }
38 |
39 | @Preview
40 | @Composable
41 | fun SpacerSamplePreview() {
42 | SpacerSample()
43 | }
44 |
45 |
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/java/icu/bughub/app/composebasic/components/StateSample.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.composebasic.components
2 |
3 |
4 | import android.annotation.SuppressLint
5 | import android.util.Log
6 | import androidx.compose.foundation.clickable
7 | import androidx.compose.foundation.layout.padding
8 | import androidx.compose.material.Text
9 | import androidx.compose.runtime.*
10 | import androidx.compose.ui.Modifier
11 | import androidx.compose.ui.tooling.preview.Preview
12 | import androidx.compose.ui.unit.dp
13 |
14 |
15 | @Composable
16 | fun StateSample() {
17 |
18 | var count by remember {
19 | mutableStateOf(1)
20 | }
21 |
22 | Log.d("====", "外面的值:${count}")
23 |
24 | Text(text = "我今天想叫${count}个小姐姐", modifier = Modifier
25 | .padding(8.dp)
26 | .clickable {
27 | count++
28 | Log.d("====", "我进来了")
29 | })
30 |
31 |
32 | }
33 |
34 | @Preview
35 | @Composable
36 | fun StateSamplePreview() {
37 | StateSample()
38 | }
39 |
40 |
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/java/icu/bughub/app/composebasic/components/Stepper.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.composebasic.components
2 |
3 |
4 | import androidx.compose.foundation.background
5 | import androidx.compose.foundation.border
6 | import androidx.compose.foundation.clickable
7 | import androidx.compose.foundation.layout.*
8 | import androidx.compose.foundation.shape.RoundedCornerShape
9 | import androidx.compose.foundation.text.BasicTextField
10 | import androidx.compose.foundation.text.KeyboardOptions
11 | import androidx.compose.material.*
12 | import androidx.compose.material.icons.Icons
13 | import androidx.compose.material.icons.filled.ArrowLeft
14 | import androidx.compose.material.icons.filled.ArrowRight
15 | import androidx.compose.runtime.*
16 | import androidx.compose.ui.Alignment
17 | import androidx.compose.ui.Modifier
18 | import androidx.compose.ui.draw.clip
19 | import androidx.compose.ui.graphics.Color
20 | import androidx.compose.ui.text.TextStyle
21 | import androidx.compose.ui.text.input.KeyboardType
22 | import androidx.compose.ui.text.style.TextAlign
23 | import androidx.compose.ui.tooling.preview.Preview
24 | import androidx.compose.ui.unit.dp
25 | import androidx.compose.ui.unit.sp
26 | import kotlin.math.max
27 |
28 |
29 | @Composable
30 | fun Stepper(onStep: ((Int) -> Unit)? = null) {
31 |
32 | var step by remember {
33 | mutableStateOf("0")
34 | }
35 |
36 | Row(
37 | verticalAlignment = Alignment.CenterVertically,
38 | modifier = Modifier
39 | .border(0.5.dp, Color.Gray, shape = RoundedCornerShape(6.dp))
40 | .clip(RoundedCornerShape(6.dp))
41 | .background(Color.White)
42 | .height(IntrinsicSize.Min)
43 | .heightIn(30.dp)
44 | ) {
45 |
46 | Icon(
47 | imageVector = Icons.Default.ArrowLeft,
48 | contentDescription = null,
49 | tint = Color.White,
50 | modifier = Modifier
51 | .background(Color.Blue)
52 | .fillMaxHeight()
53 | .clickable {
54 | step = max(0, (step.toInt() - 1)).toString()
55 | }
56 | )
57 |
58 | BasicTextField(
59 | value = step,
60 | onValueChange = { value ->
61 | step = value
62 | if (value.isNotEmpty())
63 | onStep?.let {
64 | it(value.toInt())
65 | }
66 | },
67 | singleLine = true,
68 | keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
69 | textStyle = TextStyle(
70 | textAlign = TextAlign.Center,
71 | lineHeight = 14.sp,
72 | fontSize = 14.sp
73 | ),
74 | modifier = Modifier
75 | .background(Color.White)
76 | .padding(0.dp)
77 | .width(IntrinsicSize.Min)
78 | .widthIn(30.dp, 100.dp)
79 | )
80 |
81 | Icon(imageVector = Icons.Default.ArrowRight, contentDescription = null, tint = Color.White,
82 | modifier = Modifier
83 | .background(Color.Blue)
84 | .fillMaxHeight()
85 | .clickable {
86 | step = (step.toInt() + 1).toString()
87 | })
88 | }
89 |
90 | }
91 |
92 |
93 | @Preview
94 | @Composable
95 | fun StepperPreview() {
96 | Stepper()
97 | }
98 |
99 |
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/java/icu/bughub/app/composebasic/components/SurfaceSample.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.composebasic.components
2 |
3 |
4 | import androidx.compose.foundation.BorderStroke
5 | import androidx.compose.foundation.Image
6 | import androidx.compose.foundation.layout.size
7 | import androidx.compose.foundation.shape.CircleShape
8 | import androidx.compose.foundation.shape.CutCornerShape
9 | import androidx.compose.foundation.shape.RoundedCornerShape
10 | import androidx.compose.material.Surface
11 | import androidx.compose.runtime.Composable
12 | import androidx.compose.ui.Modifier
13 | import androidx.compose.ui.graphics.Color
14 | import androidx.compose.ui.res.painterResource
15 | import androidx.compose.ui.tooling.preview.Preview
16 | import androidx.compose.ui.unit.dp
17 | import icu.bughub.app.composebasic.R
18 |
19 |
20 | @Composable
21 | fun SurfaceSample() {
22 | //RectangleShape 矩形
23 | //RoundedCornerShape 圆角 ==》 CircleShape 50%圆角形成胶囊状
24 | //CutCornerShape 切角
25 | Surface(
26 | // modifier = Modifier.size(100.dp, 20.dp),
27 | shape = CutCornerShape(20),
28 | color = Color.Yellow,
29 | border = BorderStroke(1.dp, Color.Green),
30 | elevation = 10.dp
31 | ) {
32 | Image(
33 | painter = painterResource(id = R.drawable.newbanner4),
34 | contentDescription = null
35 | )
36 | }
37 |
38 | }
39 |
40 | @Preview
41 | @Composable
42 | fun SurfaceSamplePreview() {
43 | SurfaceSample()
44 | }
45 |
46 |
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/java/icu/bughub/app/composebasic/components/SwipeToDismissSample.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.composebasic.components
2 |
3 |
4 | import androidx.compose.animation.animateColorAsState
5 | import androidx.compose.animation.core.animateDpAsState
6 | import androidx.compose.animation.core.animateFloatAsState
7 | import androidx.compose.foundation.background
8 | import androidx.compose.foundation.layout.Box
9 | import androidx.compose.foundation.layout.fillMaxSize
10 | import androidx.compose.foundation.layout.padding
11 | import androidx.compose.foundation.lazy.LazyColumn
12 | import androidx.compose.foundation.lazy.items
13 | import androidx.compose.material.*
14 | import androidx.compose.material.icons.Icons
15 | import androidx.compose.material.icons.filled.Delete
16 | import androidx.compose.material.icons.filled.Done
17 | import androidx.compose.runtime.*
18 | import androidx.compose.ui.Alignment
19 | import androidx.compose.ui.Modifier
20 | import androidx.compose.ui.draw.scale
21 | import androidx.compose.ui.graphics.Color
22 | import androidx.compose.ui.text.font.FontWeight
23 | import androidx.compose.ui.tooling.preview.Preview
24 | import androidx.compose.ui.unit.dp
25 | import icu.bughub.app.composebasic.components.ListItem
26 |
27 |
28 | @OptIn(ExperimentalMaterialApi::class)
29 | @Composable
30 | fun SwipeToDismissSample() {
31 |
32 | val items = listOf(0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
33 |
34 | LazyColumn() {
35 | items(items) { item ->
36 |
37 | var unread by remember {
38 | mutableStateOf(false)
39 | }
40 |
41 | val dismissState = rememberDismissState(
42 | confirmStateChange = {
43 | if (it == DismissValue.DismissedToEnd) unread = !unread
44 | it != DismissValue.DismissedToEnd
45 | }
46 | )
47 |
48 | SwipeToDismiss(state = dismissState, modifier = Modifier.padding(vertical = 4.dp),
49 | directions = setOf( DismissDirection.EndToStart),
50 | background = {
51 | val direction = dismissState.dismissDirection ?: return@SwipeToDismiss
52 | val color by animateColorAsState(
53 | when (dismissState.targetValue) {
54 | DismissValue.Default -> Color.LightGray
55 | DismissValue.DismissedToEnd -> Color.Green
56 | DismissValue.DismissedToStart -> Color.Red
57 | }
58 | )
59 | val alignment = when (direction) {
60 | DismissDirection.StartToEnd -> Alignment.CenterStart
61 | DismissDirection.EndToStart -> Alignment.CenterEnd
62 | }
63 | val icon = when (direction) {
64 | DismissDirection.StartToEnd -> Icons.Default.Done
65 | DismissDirection.EndToStart -> Icons.Default.Delete
66 | }
67 | val scale by animateFloatAsState(
68 | if (dismissState.targetValue == DismissValue.Default) 0.75f else 1f
69 | )
70 | Box(
71 | Modifier
72 | .fillMaxSize()
73 | .background(color)
74 | .padding(horizontal = 20.dp),
75 | contentAlignment = alignment
76 | ) {
77 | Icon(
78 | icon,
79 | contentDescription = "Localized description",
80 | modifier = Modifier.scale(scale)
81 | )
82 | }
83 | }) {
84 | Card(
85 | elevation = animateDpAsState(
86 | if (dismissState.dismissDirection != null) 4.dp else 0.dp
87 | ).value
88 | ) {
89 | ListItem(
90 | text = {
91 | Text(
92 | item.toString(),
93 | fontWeight = if (unread) FontWeight.Bold else null
94 | )
95 | },
96 | secondaryText = { Text("Swipe me left or right!") }
97 | )
98 | }
99 | }
100 |
101 | }
102 | }
103 |
104 | }
105 |
106 | @androidx.compose.runtime.Composable
107 | fun SwipeSample() {
108 |
109 |
110 | }
111 |
112 | @Preview
113 | @Composable
114 | fun SwipeToDismissSamplePreview() {
115 | SwipeSample()
116 | }
117 |
118 |
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/java/icu/bughub/app/composebasic/components/SwitchSample.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.composebasic.components
2 |
3 |
4 | import androidx.compose.material.Switch
5 | import androidx.compose.runtime.*
6 | import androidx.compose.ui.tooling.preview.Preview
7 |
8 |
9 | @Composable
10 | fun SwitchSample() {
11 |
12 | var checked by remember { mutableStateOf(false) }
13 |
14 | Switch(checked = checked, onCheckedChange = {
15 | checked = it
16 | })
17 |
18 | }
19 |
20 | @Preview
21 | @Composable
22 | fun SwitchSamplePreview() {
23 | SwitchSample()
24 | }
25 |
26 |
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/java/icu/bughub/app/composebasic/components/TabSample.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.composebasic.components
2 |
3 |
4 | import androidx.compose.foundation.clickable
5 | import androidx.compose.foundation.layout.Column
6 | import androidx.compose.foundation.layout.padding
7 | import androidx.compose.material.*
8 | import androidx.compose.material.icons.Icons
9 | import androidx.compose.material.icons.filled.AccountBox
10 | import androidx.compose.material.icons.filled.Image
11 | import androidx.compose.runtime.*
12 | import androidx.compose.ui.Modifier
13 | import androidx.compose.ui.graphics.Color
14 | import androidx.compose.ui.tooling.preview.Preview
15 | import androidx.compose.ui.unit.dp
16 |
17 |
18 | @OptIn(ExperimentalMaterialApi::class)
19 | @Composable
20 | fun TabSample() {
21 |
22 | var selectedTabIndex by remember {
23 | mutableStateOf(0)
24 | }
25 |
26 | Column {
27 |
28 | TabRow(selectedTabIndex = selectedTabIndex) {
29 | Tab(
30 | selected = selectedTabIndex == 0,
31 | onClick = { selectedTabIndex = 0 },
32 | selectedContentColor = Color.Red,
33 | unselectedContentColor = Color.Gray
34 | ) {
35 | Text(text = "Tab0")
36 | }
37 |
38 | Tab(
39 | selected = selectedTabIndex == 1,
40 | onClick = { selectedTabIndex = 1 },
41 | icon = {
42 | Icon(
43 | imageVector = Icons.Default.Image,
44 | contentDescription = null
45 | )
46 | },
47 | text = {
48 | Text("Tab1")
49 | }
50 | )
51 |
52 | LeadingIconTab(
53 | selected = selectedTabIndex == 2,
54 | onClick = { selectedTabIndex = 2 },
55 | icon = {
56 | Icon(
57 | imageVector = Icons.Default.AccountBox,
58 | contentDescription = null
59 | )
60 | },
61 | text = {
62 | Text("Tab2")
63 | }
64 | )
65 |
66 | }
67 |
68 | Text("current index : $selectedTabIndex")
69 |
70 | }
71 |
72 | }
73 |
74 | @Preview
75 | @Composable
76 | fun TabSamplePreview() {
77 | TabSample()
78 | }
79 |
80 |
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/java/icu/bughub/app/composebasic/components/TextFieldSample.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.composebasic.components
2 |
3 |
4 | import androidx.compose.foundation.text.KeyboardActions
5 | import androidx.compose.foundation.text.KeyboardOptions
6 | import androidx.compose.material.Icon
7 | import androidx.compose.material.Text
8 | import androidx.compose.material.TextField
9 | import androidx.compose.material.icons.Icons
10 | import androidx.compose.material.icons.filled.AccountBox
11 | import androidx.compose.runtime.*
12 | import androidx.compose.ui.text.input.ImeAction
13 | import androidx.compose.ui.text.input.KeyboardType
14 | import androidx.compose.ui.tooling.preview.Preview
15 |
16 |
17 | @Composable
18 | fun TextFieldSample() {
19 |
20 | var value by remember {
21 | mutableStateOf("")
22 | }
23 |
24 | TextField(
25 | value = value,
26 | onValueChange = {
27 | value = it
28 | },
29 | label = {
30 | Text("姓名")
31 | },
32 | placeholder = {
33 | Text("请在这里输入姓名")
34 | },
35 | leadingIcon = {
36 | Icon(imageVector = Icons.Default.AccountBox, contentDescription = null)
37 | },
38 | keyboardActions = KeyboardActions(onDone = {
39 |
40 | }),
41 | singleLine = true,
42 | keyboardOptions = KeyboardOptions(
43 | imeAction = ImeAction.Done,
44 | keyboardType = KeyboardType.Number
45 | )
46 |
47 | )
48 |
49 | }
50 |
51 | @Preview
52 | @Composable
53 | fun TextFieldSamplePreview() {
54 | TextFieldSample()
55 | }
56 |
57 |
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/java/icu/bughub/app/composebasic/components/TextSample.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.composebasic.components
2 |
3 | import android.util.Log
4 | import androidx.compose.foundation.layout.Box
5 | import androidx.compose.foundation.text.ClickableText
6 | import androidx.compose.foundation.text.selection.SelectionContainer
7 | import androidx.compose.material.Text
8 | import androidx.compose.runtime.Composable
9 | import androidx.compose.ui.graphics.Color
10 | import androidx.compose.ui.res.stringResource
11 | import androidx.compose.ui.text.SpanStyle
12 | import androidx.compose.ui.text.TextStyle
13 | import androidx.compose.ui.text.buildAnnotatedString
14 | import androidx.compose.ui.text.style.TextAlign
15 | import androidx.compose.ui.text.style.TextDecoration
16 | import androidx.compose.ui.text.style.TextOverflow
17 | import androidx.compose.ui.text.withStyle
18 | import androidx.compose.ui.tooling.preview.Preview
19 | import androidx.compose.ui.unit.sp
20 | import icu.bughub.app.composebasic.R
21 |
22 |
23 | @Composable
24 | fun TextSample() {
25 | // Text(text = "我终于要学会Jetpack Compose 了!")
26 |
27 | val annotatedString = buildAnnotatedString {
28 | append("点击登录即代表您已知悉和同意")
29 | //往字符串中添加注解,tag 为标识,直到遇到 pop()
30 | pushStringAnnotation("protocol", "https://docs.bughub.icu/compose")
31 | withStyle(style = SpanStyle(Color.Blue, textDecoration = TextDecoration.Underline)) {
32 | append("用户协议")
33 | }
34 | pop()
35 |
36 | append("和")
37 |
38 | pushStringAnnotation("privacy", "https://github.com/RandyWei")
39 | withStyle(style = SpanStyle(Color.Blue, textDecoration = TextDecoration.Underline)) {
40 | append("隐私政策")
41 | }
42 | pop()
43 | }
44 |
45 | ClickableText(text = annotatedString, onClick = { offset ->
46 | //从字符串中根据 tag 查找注解
47 | annotatedString.getStringAnnotations("protocol", start = offset, end = offset).firstOrNull()
48 | ?.let { annotation ->
49 | Log.d("====", "你点击到${annotation.item}")
50 | }
51 |
52 | annotatedString.getStringAnnotations("privacy", start = offset, end = offset).firstOrNull()
53 | ?.let { annotation ->
54 | Log.d("====", "你点击到${annotation.item}")
55 | }
56 |
57 | })
58 |
59 | }
60 |
61 |
62 | @Preview
63 | @Composable
64 | fun TextSamplePreview() {
65 | TextSample()
66 | }
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/java/icu/bughub/app/composebasic/ui/theme/Color.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.composebasic.ui.theme
2 |
3 | import androidx.compose.ui.graphics.Color
4 |
5 | val Purple200 = Color(0xFFBB86FC)
6 | val Purple500 = Color(0xFF6200EE)
7 | val Purple700 = Color(0xFF3700B3)
8 | val Teal200 = Color(0xFF03DAC5)
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/java/icu/bughub/app/composebasic/ui/theme/Shape.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.composebasic.ui.theme
2 |
3 | import androidx.compose.foundation.shape.RoundedCornerShape
4 | import androidx.compose.material.Shapes
5 | import androidx.compose.ui.unit.dp
6 |
7 | val Shapes = Shapes(
8 | small = RoundedCornerShape(4.dp),
9 | medium = RoundedCornerShape(4.dp),
10 | large = RoundedCornerShape(0.dp)
11 | )
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/java/icu/bughub/app/composebasic/ui/theme/Theme.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.composebasic.ui.theme
2 |
3 | import androidx.compose.foundation.isSystemInDarkTheme
4 | import androidx.compose.material.MaterialTheme
5 | import androidx.compose.material.darkColors
6 | import androidx.compose.material.lightColors
7 | import androidx.compose.runtime.Composable
8 |
9 | private val DarkColorPalette = darkColors(
10 | primary = Purple200,
11 | primaryVariant = Purple700,
12 | secondary = Teal200
13 | )
14 |
15 | private val LightColorPalette = lightColors(
16 | primary = Purple500,
17 | primaryVariant = Purple700,
18 | secondary = Teal200
19 |
20 | /* Other default colors to override
21 | background = Color.White,
22 | surface = Color.White,
23 | onPrimary = Color.White,
24 | onSecondary = Color.Black,
25 | onBackground = Color.Black,
26 | onSurface = Color.Black,
27 | */
28 | )
29 |
30 | @Composable
31 | fun ComposeBasicTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composable () -> Unit) {
32 | val colors = if (darkTheme) {
33 | DarkColorPalette
34 | } else {
35 | LightColorPalette
36 | }
37 |
38 | MaterialTheme(
39 | colors = colors,
40 | typography = Typography,
41 | shapes = Shapes,
42 | content = content
43 | )
44 | }
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/java/icu/bughub/app/composebasic/ui/theme/Type.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.composebasic.ui.theme
2 |
3 | import androidx.compose.material.Typography
4 | import androidx.compose.ui.text.TextStyle
5 | import androidx.compose.ui.text.font.FontFamily
6 | import androidx.compose.ui.text.font.FontWeight
7 | import androidx.compose.ui.unit.sp
8 |
9 | // Set of Material typography styles to start with
10 | val Typography = Typography(
11 | body1 = TextStyle(
12 | fontFamily = FontFamily.Default,
13 | fontWeight = FontWeight.Normal,
14 | fontSize = 16.sp
15 | )
16 | /* Other default text styles to override
17 | button = TextStyle(
18 | fontFamily = FontFamily.Default,
19 | fontWeight = FontWeight.W500,
20 | fontSize = 14.sp
21 | ),
22 | caption = TextStyle(
23 | fontFamily = FontFamily.Default,
24 | fontWeight = FontWeight.Normal,
25 | fontSize = 12.sp
26 | )
27 | */
28 | )
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
7 |
8 |
9 |
15 |
18 |
21 |
22 |
23 |
24 |
30 |
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/res/drawable/ic_android_black_24dp.xml:
--------------------------------------------------------------------------------
1 |
6 |
9 |
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/res/drawable/newbanner4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/JetpackComposeCase/192eb509c12ea41eb8b9fef4ee69806a3a39afd7/ComposeBasic/app/src/main/res/drawable/newbanner4.png
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/res/layout/main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
13 |
17 |
18 |
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/res/mipmap-hdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/JetpackComposeCase/192eb509c12ea41eb8b9fef4ee69806a3a39afd7/ComposeBasic/app/src/main/res/mipmap-hdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/JetpackComposeCase/192eb509c12ea41eb8b9fef4ee69806a3a39afd7/ComposeBasic/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/res/mipmap-mdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/JetpackComposeCase/192eb509c12ea41eb8b9fef4ee69806a3a39afd7/ComposeBasic/app/src/main/res/mipmap-mdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/JetpackComposeCase/192eb509c12ea41eb8b9fef4ee69806a3a39afd7/ComposeBasic/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/res/mipmap-xhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/JetpackComposeCase/192eb509c12ea41eb8b9fef4ee69806a3a39afd7/ComposeBasic/app/src/main/res/mipmap-xhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/JetpackComposeCase/192eb509c12ea41eb8b9fef4ee69806a3a39afd7/ComposeBasic/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/JetpackComposeCase/192eb509c12ea41eb8b9fef4ee69806a3a39afd7/ComposeBasic/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/JetpackComposeCase/192eb509c12ea41eb8b9fef4ee69806a3a39afd7/ComposeBasic/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/JetpackComposeCase/192eb509c12ea41eb8b9fef4ee69806a3a39afd7/ComposeBasic/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/JetpackComposeCase/192eb509c12ea41eb8b9fef4ee69806a3a39afd7/ComposeBasic/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #FFBB86FC
4 | #FF6200EE
5 | #FF3700B3
6 | #FF03DAC5
7 | #FF018786
8 | #FF000000
9 | #FFFFFFFF
10 |
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | ComposeBasic
3 | 这是一个从 strings.xml 里面来的文本
4 |
--------------------------------------------------------------------------------
/ComposeBasic/app/src/main/res/values/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
--------------------------------------------------------------------------------
/ComposeBasic/app/src/test/java/icu/bughub/app/composebasic/ExampleUnitTest.kt:
--------------------------------------------------------------------------------
1 | package icu.bughub.app.composebasic
2 |
3 | import org.junit.Test
4 |
5 | import org.junit.Assert.*
6 |
7 | /**
8 | * Example local unit test, which will execute on the development machine (host).
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | class ExampleUnitTest {
13 | @Test
14 | fun addition_isCorrect() {
15 | assertEquals(4, 2 + 2)
16 | }
17 | }
--------------------------------------------------------------------------------
/ComposeBasic/build.gradle:
--------------------------------------------------------------------------------
1 | buildscript {
2 | ext {
3 | compose_version = '1.0.5'
4 | }
5 | }// Top-level build file where you can add configuration options common to all sub-projects/modules.
6 | plugins {
7 | id 'com.android.application' version '7.1.0-rc01' apply false
8 | id 'com.android.library' version '7.1.0-rc01' apply false
9 | id 'org.jetbrains.kotlin.android' version '1.5.31' apply false
10 | }
11 |
12 | task clean(type: Delete) {
13 | delete rootProject.buildDir
14 | }
--------------------------------------------------------------------------------
/ComposeBasic/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 | # IDE (e.g. Android Studio) users:
3 | # Gradle settings configured through the IDE *will override*
4 | # any settings specified in this file.
5 | # For more details on how to configure your build environment visit
6 | # http://www.gradle.org/docs/current/userguide/build_environment.html
7 | # Specifies the JVM arguments used for the daemon process.
8 | # The setting is particularly useful for tweaking memory settings.
9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
10 | # When configured, Gradle will run in incubating parallel mode.
11 | # This option should only be used with decoupled projects. More details, visit
12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
13 | # org.gradle.parallel=true
14 | # AndroidX package structure to make it clearer which packages are bundled with the
15 | # Android operating system, and which are packaged with your app"s APK
16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn
17 | android.useAndroidX=true
18 | # Kotlin code style for this project: "official" or "obsolete":
19 | kotlin.code.style=official
20 | # Enables namespacing of each library's R class so that its R class includes only the
21 | # resources declared in the library itself and none from the library's dependencies,
22 | # thereby reducing the size of the R class for that library
23 | android.nonTransitiveRClass=true
--------------------------------------------------------------------------------
/ComposeBasic/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/RandyWei/JetpackComposeCase/192eb509c12ea41eb8b9fef4ee69806a3a39afd7/ComposeBasic/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/ComposeBasic/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Tue Feb 08 06:53:47 CST 2022
2 | distributionBase=GRADLE_USER_HOME
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
4 | distributionPath=wrapper/dists
5 | zipStorePath=wrapper/dists
6 | zipStoreBase=GRADLE_USER_HOME
7 |
--------------------------------------------------------------------------------
/ComposeBasic/gradlew.bat:
--------------------------------------------------------------------------------
1 | @rem
2 | @rem Copyright 2015 the original author or authors.
3 | @rem
4 | @rem Licensed under the Apache License, Version 2.0 (the "License");
5 | @rem you may not use this file except in compliance with the License.
6 | @rem You may obtain a copy of the License at
7 | @rem
8 | @rem https://www.apache.org/licenses/LICENSE-2.0
9 | @rem
10 | @rem Unless required by applicable law or agreed to in writing, software
11 | @rem distributed under the License is distributed on an "AS IS" BASIS,
12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | @rem See the License for the specific language governing permissions and
14 | @rem limitations under the License.
15 | @rem
16 |
17 | @if "%DEBUG%" == "" @echo off
18 | @rem ##########################################################################
19 | @rem
20 | @rem Gradle startup script for Windows
21 | @rem
22 | @rem ##########################################################################
23 |
24 | @rem Set local scope for the variables with windows NT shell
25 | if "%OS%"=="Windows_NT" setlocal
26 |
27 | set DIRNAME=%~dp0
28 | if "%DIRNAME%" == "" set DIRNAME=.
29 | set APP_BASE_NAME=%~n0
30 | set APP_HOME=%DIRNAME%
31 |
32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter.
33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
34 |
35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
37 |
38 | @rem Find java.exe
39 | if defined JAVA_HOME goto findJavaFromJavaHome
40 |
41 | set JAVA_EXE=java.exe
42 | %JAVA_EXE% -version >NUL 2>&1
43 | if "%ERRORLEVEL%" == "0" goto execute
44 |
45 | echo.
46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
47 | echo.
48 | echo Please set the JAVA_HOME variable in your environment to match the
49 | echo location of your Java installation.
50 |
51 | goto fail
52 |
53 | :findJavaFromJavaHome
54 | set JAVA_HOME=%JAVA_HOME:"=%
55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe
56 |
57 | if exist "%JAVA_EXE%" goto execute
58 |
59 | echo.
60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
61 | echo.
62 | echo Please set the JAVA_HOME variable in your environment to match the
63 | echo location of your Java installation.
64 |
65 | goto fail
66 |
67 | :execute
68 | @rem Setup the command line
69 |
70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
71 |
72 |
73 | @rem Execute Gradle
74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %*
75 |
76 | :end
77 | @rem End local scope for the variables with windows NT shell
78 | if "%ERRORLEVEL%"=="0" goto mainEnd
79 |
80 | :fail
81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
82 | rem the _cmd.exe /c_ return code!
83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
84 | exit /b 1
85 |
86 | :mainEnd
87 | if "%OS%"=="Windows_NT" endlocal
88 |
89 | :omega
90 |
--------------------------------------------------------------------------------
/ComposeBasic/settings.gradle:
--------------------------------------------------------------------------------
1 | pluginManagement {
2 | repositories {
3 | gradlePluginPortal()
4 | google()
5 | mavenCentral()
6 | }
7 | }
8 | dependencyResolutionManagement {
9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
10 | repositories {
11 | google()
12 | mavenCentral()
13 | }
14 | }
15 | rootProject.name = "ComposeBasic"
16 | include ':app'
17 |
--------------------------------------------------------------------------------