├── .gitignore ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle.kts ├── proguard-rules.pro └── src │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── tech │ │ │ └── thdev │ │ │ └── app │ │ │ ├── MainActivity.kt │ │ │ └── ui │ │ │ ├── JavaJava.java │ │ │ ├── MainScreen.kt │ │ │ ├── SampleScreen.kt │ │ │ ├── first │ │ │ ├── FirstFragment.kt │ │ │ └── FirstViewModel.kt │ │ │ ├── holder │ │ │ ├── other │ │ │ │ └── OtherSampleScreen.kt │ │ │ └── web │ │ │ │ ├── LocalWebOwner.kt │ │ │ │ └── WebSampleScreen.kt │ │ │ ├── second │ │ │ ├── SecondFragment.kt │ │ │ └── SecondViewModel.kt │ │ │ └── theme │ │ │ ├── Color.kt │ │ │ ├── Theme.kt │ │ │ └── Type.kt │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ ├── content_main.xml │ │ ├── first_fragment.xml │ │ ├── main_activity.xml │ │ └── second_fragment.xml │ │ ├── menu │ │ └── menu_main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── navigation │ │ └── nav_graph.xml │ │ ├── values-night │ │ └── themes.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── themes.xml │ └── test │ └── java │ └── tech │ └── thdev │ └── app │ ├── Counter.kt │ ├── ExampleUnitTest.kt │ └── ui │ └── first │ ├── FirstViewModelTest.kt │ └── MutableStateF.kt ├── architecture-one ├── README.md ├── app │ ├── .gitignore │ ├── README.md │ ├── build.gradle.kts │ ├── proguard-rules.pro │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── tech │ │ │ └── thdev │ │ │ └── architecture │ │ │ └── app │ │ │ ├── ArchitectureOneApplication.kt │ │ │ ├── common │ │ │ ├── alert │ │ │ │ ├── AlertDialogScreen.kt │ │ │ │ ├── AlertDialogViewModel.kt │ │ │ │ └── model │ │ │ │ │ └── AlertDialogUiState.kt │ │ │ └── snackbar │ │ │ │ ├── SnackbarScreen.kt │ │ │ │ ├── SnackbarViewModel.kt │ │ │ │ └── model │ │ │ │ └── SnackbarUiState.kt │ │ │ ├── event │ │ │ ├── FlowComposeInteractionTrigger.kt │ │ │ ├── FlowInteraction.kt │ │ │ ├── FlowInteractionStream.kt │ │ │ ├── LocalComposeEventTriggerOwner.kt │ │ │ ├── MainEffect.kt │ │ │ └── di │ │ │ │ └── EventModule.kt │ │ │ ├── feature │ │ │ ├── MainActivity.kt │ │ │ ├── MainViewModel.kt │ │ │ └── model │ │ │ │ └── MainUiState.kt │ │ │ └── ui │ │ │ └── theme │ │ │ ├── Color.kt │ │ │ ├── Theme.kt │ │ │ └── Type.kt │ │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── baseline_close_24.xml │ │ ├── baseline_home_24.xml │ │ ├── baseline_web_24.xml │ │ └── ic_launcher_background.xml │ │ ├── menu │ │ └── menu_main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── values-night │ │ └── themes.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── themes.xml ├── core-data │ └── repository │ │ ├── alert-repository-api │ │ ├── README.md │ │ ├── build.gradle.kts │ │ └── src │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ └── java │ │ │ └── tech │ │ │ └── thdev │ │ │ └── architecture │ │ │ └── app │ │ │ └── alert │ │ │ └── repository │ │ │ └── api │ │ │ ├── AlertRepository.kt │ │ │ ├── InternalAlertRepository.kt │ │ │ └── model │ │ │ ├── AlertEndEvent.kt │ │ │ └── AlertItem.kt │ │ ├── alert-repository │ │ ├── README.md │ │ ├── build.gradle.kts │ │ └── src │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ └── java │ │ │ │ └── tech │ │ │ │ └── thdev │ │ │ │ └── architecture │ │ │ │ └── app │ │ │ │ └── alert │ │ │ │ └── repository │ │ │ │ ├── AlertRepositoryImpl.kt │ │ │ │ └── di │ │ │ │ └── AlertRepositoryModel.kt │ │ │ └── test │ │ │ └── java │ │ │ └── tech │ │ │ └── thdev │ │ │ └── app │ │ │ └── ExampleUnitTest.kt │ │ ├── snackbar-repository-api │ │ ├── README.md │ │ ├── build.gradle.kts │ │ └── src │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ └── java │ │ │ └── tech │ │ │ └── thdev │ │ │ └── architecture │ │ │ └── app │ │ │ └── snackbar │ │ │ └── repository │ │ │ └── api │ │ │ ├── InternalSnackbarRepository.kt │ │ │ ├── SnackbarRepository.kt │ │ │ └── model │ │ │ ├── SnackbarEndEvent.kt │ │ │ └── SnackbarItem.kt │ │ └── snackbar-repository │ │ ├── README.md │ │ ├── build.gradle.kts │ │ └── src │ │ ├── main │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── tech │ │ │ └── thdev │ │ │ └── architecture │ │ │ └── app │ │ │ └── snackbar │ │ │ └── repository │ │ │ ├── SnackbarRepositoryImpl.kt │ │ │ └── di │ │ │ └── SnackbarRepositoryModel.kt │ │ └── test │ │ └── java │ │ └── tech │ │ └── thdev │ │ └── app │ │ └── ExampleUnitTest.kt └── images │ ├── sample_01.png │ ├── sample_02.png │ └── sample_03.png ├── build-logic ├── README.md ├── convention │ ├── build.gradle.kts │ └── src │ │ └── main │ │ └── kotlin │ │ ├── AndroidApplicationConventionPlugin.kt │ │ ├── AndroidLibraryComposeConventionPlugin.kt │ │ ├── AndroidLibraryComposeFeatureConventionPlugin.kt │ │ ├── AndroidLibraryConventionPlugin.kt │ │ ├── AndroidLibraryHiltConventionPlugin.kt │ │ ├── AndroidLibraryRobolectricConventionPlugin.kt │ │ ├── AndroidLibraryUnitTestConventionPlugin.kt │ │ ├── KotlinLibraryConventionPlugin.kt │ │ ├── KotlinLibraryHiltConventionPlugin.kt │ │ ├── KotlinLibraryKspConventionPlugin.kt │ │ ├── KotlinLibrarySerializationConventionPlugin.kt │ │ ├── KotlinLibraryVerifyDetektConventionPlugin.kt │ │ ├── tech.thdev.kotlin.library.verify.test.gradle.kts │ │ └── tech │ │ └── thdev │ │ └── gradle │ │ ├── ComposeAndroid.kt │ │ ├── CoroutineAndroid.kt │ │ ├── DaggerAndroid.kt │ │ ├── KotlinAndroid.kt │ │ ├── KotlinKsp.kt │ │ ├── UnitTest.kt │ │ ├── VerifyDetekt.kt │ │ ├── VerifyJacoco.kt │ │ └── extensions │ │ ├── AppExtension.kt │ │ ├── ImportExtension.kt │ │ ├── InternalProjectExtension.kt │ │ └── InternalVersionExtension.kt ├── gradle.properties └── settings.gradle.kts ├── build.gradle.kts ├── gradle.properties ├── gradle ├── libs.versions.toml └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── sample ├── compose │ ├── compose-example-01 │ │ ├── .gitignore │ │ ├── README.md │ │ ├── build.gradle.kts │ │ ├── image │ │ │ ├── main.png │ │ │ └── web.png │ │ ├── proguard-rules.pro │ │ └── src │ │ │ ├── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ │ └── tech │ │ │ │ │ └── thdev │ │ │ │ │ └── compose │ │ │ │ │ └── web │ │ │ │ │ └── sample │ │ │ │ │ ├── MainActivity.kt │ │ │ │ │ ├── MainActivityBackup.kt │ │ │ │ │ └── ui │ │ │ │ │ ├── design │ │ │ │ │ └── system │ │ │ │ │ │ └── button │ │ │ │ │ │ ├── ExampleButtonComponent.kt │ │ │ │ │ │ └── ExampleButtonDefaults.kt │ │ │ │ │ ├── holder │ │ │ │ │ ├── home │ │ │ │ │ │ ├── HomeScreenOne.kt │ │ │ │ │ │ ├── HomeScreenThree.kt │ │ │ │ │ │ ├── HomeScreenTwo.kt │ │ │ │ │ │ └── component │ │ │ │ │ │ │ ├── HomeItemEditComponent.kt │ │ │ │ │ │ │ ├── HomeItemViewComponent.kt │ │ │ │ │ │ │ ├── TwoHomeItemEditComponent.kt │ │ │ │ │ │ │ └── TwoHomeItemViewComponent.kt │ │ │ │ │ └── web │ │ │ │ │ │ ├── CustomWebChromeClient.kt │ │ │ │ │ │ ├── CustomWebViewClient.kt │ │ │ │ │ │ ├── LocalWebOwner.kt │ │ │ │ │ │ └── WebScreen.kt │ │ │ │ │ ├── model │ │ │ │ │ ├── ListItem.kt │ │ │ │ │ └── NavigationSample.kt │ │ │ │ │ └── theme │ │ │ │ │ ├── Color.kt │ │ │ │ │ ├── Theme.kt │ │ │ │ │ └── Type.kt │ │ │ └── res │ │ │ │ ├── drawable-v24 │ │ │ │ └── ic_launcher_foreground.xml │ │ │ │ ├── drawable │ │ │ │ ├── baseline_close_24.xml │ │ │ │ ├── baseline_home_24.xml │ │ │ │ ├── baseline_web_24.xml │ │ │ │ └── ic_launcher_background.xml │ │ │ │ ├── mipmap-anydpi-v26 │ │ │ │ ├── ic_launcher.xml │ │ │ │ └── ic_launcher_round.xml │ │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ │ ├── values-night │ │ │ │ └── themes.xml │ │ │ │ └── values │ │ │ │ ├── colors.xml │ │ │ │ ├── dimens.xml │ │ │ │ ├── strings.xml │ │ │ │ └── themes.xml │ │ │ └── test │ │ │ └── java │ │ │ └── tech │ │ │ └── thdev │ │ │ └── app │ │ │ └── ExampleUnitTest.kt │ └── web-sample-01 │ │ ├── .gitignore │ │ ├── README.md │ │ ├── build.gradle.kts │ │ ├── image │ │ ├── after.png │ │ └── before.png │ │ ├── proguard-rules.pro │ │ └── src │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ │ └── tech │ │ │ │ └── thdev │ │ │ │ └── compose │ │ │ │ └── web │ │ │ │ └── sample │ │ │ │ ├── MainActivity.kt │ │ │ │ └── ui │ │ │ │ ├── MainScreen.kt │ │ │ │ ├── holder │ │ │ │ ├── other │ │ │ │ │ └── OtherSampleScreen.kt │ │ │ │ └── web │ │ │ │ │ ├── LocalWebOwner.kt │ │ │ │ │ └── WebSampleScreen.kt │ │ │ │ └── theme │ │ │ │ ├── Color.kt │ │ │ │ ├── Theme.kt │ │ │ │ └── Type.kt │ │ └── res │ │ │ ├── drawable-v24 │ │ │ └── ic_launcher_foreground.xml │ │ │ ├── drawable │ │ │ └── ic_launcher_background.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── values-night │ │ │ └── themes.xml │ │ │ └── values │ │ │ ├── colors.xml │ │ │ ├── dimens.xml │ │ │ ├── strings.xml │ │ │ └── themes.xml │ │ └── test │ │ └── java │ │ └── tech │ │ └── thdev │ │ └── app │ │ └── ExampleUnitTest.kt └── view │ └── recycler-view-ex │ ├── .gitignore │ ├── README.md │ ├── build.gradle.kts │ ├── images │ ├── after.png │ └── before.png │ ├── proguard-rules.pro │ └── src │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── tech │ │ │ └── thdev │ │ │ └── view │ │ │ └── recycler │ │ │ └── view │ │ │ └── ex │ │ │ ├── MainActivity.kt │ │ │ ├── MainViewModel.kt │ │ │ ├── adapter │ │ │ ├── MainAdapter.kt │ │ │ ├── MainTrashViewHolder.kt │ │ │ └── MainViewHolder.kt │ │ │ └── model │ │ │ └── SampleItem.kt │ └── res │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── baseline_perm_media_24.xml │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ ├── item_view.xml │ │ ├── main_activity.xml │ │ └── trash_item_view.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ │ ├── values-night │ │ └── themes.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── themes.xml │ └── test │ └── java │ └── tech │ └── thdev │ └── app │ ├── Counter.kt │ ├── ExampleUnitTest.kt │ └── ui │ └── first │ ├── FirstViewModelTest.kt │ └── MutableStateF.kt └── settings.gradle.kts /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Android template 3 | # Built application files 4 | *.apk 5 | *.ap_ 6 | 7 | # Files for the ART/Dalvik VM 8 | *.dex 9 | 10 | # Java class files 11 | *.class 12 | 13 | # Generated files 14 | bin/ 15 | gen/ 16 | out/ 17 | 18 | # Gradle files 19 | .gradle/ 20 | build/ 21 | 22 | # Local configuration file (sdk path, etc) 23 | local.properties 24 | 25 | # Proguard folder generated by Eclipse 26 | proguard/ 27 | 28 | # Log Files 29 | *.log 30 | 31 | # Android Studio Navigation editor temp files 32 | .navigation/ 33 | 34 | # Android Studio captures folder 35 | captures/ 36 | 37 | # Intellij 38 | *.iml 39 | .idea/ 40 | 41 | # Keystore files 42 | *.jks 43 | 44 | .DS_Store -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 블로그에 게시되는 안드로이드 예제를 정리합니다. 3 | 일부 예제는 별도의 Repository를 가질 수 있으며 해당 부분은 아래 링크를 참고해주세요. 4 | 5 | 6 | # Blog 7 | 관련 포스트는 다음의 블로그를 통해 확인 할 수 있습니다. 8 | - [블로그 링크 : thdev.tech](http://thdev.tech) 9 | 10 | 11 | # Etc 12 | 그외 다른 예제의 링크는 아래와 같습니다. 13 | - [MAppPermission](https://github.com/taehwandev/MAppPermission) 14 | - [MediaProjectionExample](https://github.com/taehwandev/MediaProjectionExample) 15 | - [MediaCodecExample](https://github.com/taehwandev/MediaCodecExample) 16 | 17 | 18 | ## Build info 19 | 20 | - Android studio 3.3+ 21 | 22 | - compile sdk version = 28 23 | - Target sdk version = 28 24 | - Min sdk version = Depending on the module. 25 | 26 | 27 | ## Library version info 28 | 29 | - Dependencies 30 | - Android Support 31 | - [Support library legacy](https://developer.android.com/topic/libraries/support-library/revisions) 32 | - [Jetpack AndroidX](https://developer.android.com/jetpack/androidx) 33 | - [Jetpack architecture](https://developer.android.com/topic/libraries/architecture/adding-components) 34 | 35 | - UI 36 | - [constraint layout](https://developer.android.com/training/constraint-layout/) 37 | 38 | - ImageLoader 39 | - [Glide](https://github.com/bumptech/glide) 40 | 41 | - Network 42 | - [Retrofit](https://square.github.io/retrofit/) 43 | - [OkHttp](https://github.com/square/okhttp) 44 | 45 | - ReactiveX 46 | - [RxJava](https://github.com/ReactiveX/RxJava) 47 | - [RxAndroid](https://github.com/ReactiveX/RxAndroid) 48 | - [RxKotlin](https://github.com/ReactiveX/RxKotlin) 49 | 50 | - Kotlin 51 | - [kotlin](https://github.com/JetBrains/kotlin) 52 | - [kotlinx.coroutines](https://github.com/Kotlin/kotlinx.coroutines) 53 | 54 | - thdev support library 55 | - [LifecycleExtensions](https://thdev.tech/LifecycleExtensions/) 56 | - [CoroutinesUIExtensions](https://thdev.tech/CoroutinesUIExtensions/) 57 | 58 | 59 | ## License 60 | 61 | ``` 62 | Copyright 2016 Tae-hwan 63 | 64 | Licensed under the Apache License, Version 2.0 (the "License"); 65 | you may not use this file except in compliance with the License. 66 | You may obtain a copy of the License at 67 | 68 | http://www.apache.org/licenses/LICENSE-2.0 69 | 70 | Unless required by applicable law or agreed to in writing, software 71 | distributed under the License is distributed on an "AS IS" BASIS, 72 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 73 | See the License for the specific language governing permissions and 74 | limitations under the License. 75 | ``` 76 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle.kts: -------------------------------------------------------------------------------- 1 | plugins { 2 | alias(libs.plugins.tech.thdev.android.application) 3 | } 4 | 5 | android { 6 | val (majorVersion, minorVersion, patchVersion, code) = getVersionInfo() 7 | 8 | defaultConfig { 9 | applicationId = "tech.thdev.app" 10 | minSdk = libs.versions.minSdk.get().toInt() 11 | targetSdk = libs.versions.targetSdk.get().toInt() 12 | vectorDrawables.useSupportLibrary = true 13 | versionCode = code 14 | versionName = "$majorVersion.$minorVersion.$patchVersion" 15 | testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" 16 | multiDexEnabled = true 17 | } 18 | 19 | buildFeatures { 20 | viewBinding = true 21 | dataBinding = true 22 | } 23 | } 24 | 25 | setNamespace("app") 26 | 27 | ksp { 28 | arg("moduleName", project.name) 29 | arg("rootDir", rootDir.absolutePath) 30 | } 31 | 32 | dependencies { 33 | implementation(libs.kotlin.stdlib) 34 | 35 | implementation(libs.google.material) 36 | 37 | implementation(libs.androidx.core) 38 | implementation(libs.androidx.appCompat) 39 | implementation(libs.androidx.activity) 40 | implementation(libs.androidx.constraintLayout) 41 | implementation(libs.androidx.vectorDrawable) 42 | implementation(libs.androidx.navigationFragment) 43 | implementation(libs.androidx.navigationUi) 44 | implementation(libs.androidx.liveData) 45 | 46 | implementation(libs.image.coil) 47 | 48 | implementation(libs.network.retrofit) 49 | implementation(libs.network.okhttp) 50 | implementation(libs.network.okhttp.logging) 51 | 52 | implementation(libs.compose.navigation) 53 | 54 | testImplementation(libs.test.coroutines) 55 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/java/tech/thdev/app/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package tech.thdev.app 2 | 3 | import android.os.Bundle 4 | import android.view.Menu 5 | import android.view.MenuItem 6 | import androidx.activity.compose.setContent 7 | import androidx.appcompat.app.AppCompatActivity 8 | import tech.thdev.app.databinding.MainActivityBinding 9 | import tech.thdev.app.ui.SampleScreen 10 | 11 | class MainActivity : AppCompatActivity() { 12 | 13 | private lateinit var binding: MainActivityBinding 14 | override fun onCreate(savedInstanceState: Bundle?) { 15 | super.onCreate(savedInstanceState) 16 | setContent { 17 | SampleScreen() 18 | } 19 | // binding = MainActivityBinding.inflate(layoutInflater) 20 | // setContentView(binding.root) 21 | // setSupportActionBar(binding.toolbar) 22 | } 23 | 24 | override fun onCreateOptionsMenu(menu: Menu): Boolean { 25 | // Inflate the menu; this adds items to the action bar if it is present. 26 | menuInflater.inflate(R.menu.menu_main, menu) 27 | return true 28 | } 29 | 30 | override fun onOptionsItemSelected(item: MenuItem): Boolean { 31 | // Handle action bar item clicks here. The action bar will 32 | // automatically handle clicks on the Home/Up button, so long 33 | // as you specify a parent activity in AndroidManifest.xml. 34 | return when (item.itemId) { 35 | R.id.action_settings -> true 36 | else -> super.onOptionsItemSelected(item) 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/tech/thdev/app/ui/JavaJava.java: -------------------------------------------------------------------------------- 1 | package tech.thdev.app.ui; 2 | 3 | public class JavaJava { 4 | 5 | private static JavaJava INSTANCE = new JavaJava(); 6 | 7 | public static final String value = "avalue"; 8 | 9 | private JavaJava() { 10 | System.out.println("init"); 11 | } 12 | 13 | public static String getValue() { 14 | return value; 15 | } 16 | 17 | public static JavaJava getInstance() { 18 | return INSTANCE; 19 | } 20 | 21 | static { 22 | System.out.println("static"); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/tech/thdev/app/ui/first/FirstFragment.kt: -------------------------------------------------------------------------------- 1 | package tech.thdev.app.ui.first 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import androidx.fragment.app.Fragment 8 | import androidx.fragment.app.viewModels 9 | import androidx.lifecycle.Lifecycle 10 | import androidx.lifecycle.lifecycleScope 11 | import androidx.lifecycle.repeatOnLifecycle 12 | import androidx.navigation.fragment.findNavController 13 | import kotlinx.coroutines.flow.collectLatest 14 | import kotlinx.coroutines.launch 15 | import tech.thdev.app.R 16 | import tech.thdev.app.databinding.FirstFragmentBinding 17 | 18 | class FirstFragment : Fragment() { 19 | 20 | private lateinit var binding: FirstFragmentBinding 21 | 22 | private val viewModel: FirstViewModel by viewModels() 23 | 24 | override fun onCreateView( 25 | inflater: LayoutInflater, container: ViewGroup?, 26 | savedInstanceState: Bundle? 27 | ): View { 28 | return FirstFragmentBinding.inflate(inflater).also { 29 | binding = it 30 | }.root 31 | } 32 | 33 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 34 | super.onViewCreated(view, savedInstanceState) 35 | 36 | viewLifecycleOwner.lifecycleScope.launch { 37 | viewLifecycleOwner.repeatOnLifecycle(Lifecycle.State.STARTED) { 38 | viewModel.state.collectLatest { 39 | // todo 40 | } 41 | } 42 | } 43 | 44 | binding.buttonFirst.setOnClickListener { 45 | findNavController().navigate(R.id.action_FirstFragment_to_SecondFragment) 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /app/src/main/java/tech/thdev/app/ui/first/FirstViewModel.kt: -------------------------------------------------------------------------------- 1 | package tech.thdev.app.ui.first 2 | 3 | import androidx.annotation.VisibleForTesting 4 | import androidx.lifecycle.ViewModel 5 | import androidx.lifecycle.viewModelScope 6 | import kotlinx.coroutines.flow.MutableStateFlow 7 | import kotlinx.coroutines.flow.asStateFlow 8 | import kotlinx.coroutines.launch 9 | 10 | class FirstViewModel( 11 | private val repository: Repository, 12 | ) : ViewModel() { 13 | 14 | @VisibleForTesting 15 | val _state = MutableStateFlow(UiState(data = "")) 16 | val state = _state.asStateFlow() 17 | 18 | fun loadData() { 19 | viewModelScope.launch { 20 | kotlin.runCatching { 21 | repository.getData() 22 | } 23 | .onFailure { 24 | _state.value = UiState( 25 | data = "Error", 26 | ) 27 | } 28 | .onSuccess { 29 | _state.value = UiState( 30 | data = it.plus(100).toString(), 31 | ) 32 | } 33 | } 34 | } 35 | } 36 | 37 | data class UiState( 38 | val data: String, 39 | ) 40 | 41 | interface Repository { 42 | 43 | suspend fun getData(): Int 44 | } -------------------------------------------------------------------------------- /app/src/main/java/tech/thdev/app/ui/holder/other/OtherSampleScreen.kt: -------------------------------------------------------------------------------- 1 | package tech.thdev.app.ui.holder.other 2 | 3 | import androidx.compose.foundation.layout.Box 4 | import androidx.compose.foundation.layout.fillMaxSize 5 | import androidx.compose.material3.Text 6 | import androidx.compose.runtime.Composable 7 | import androidx.compose.ui.Alignment 8 | import androidx.compose.ui.Modifier 9 | import androidx.compose.ui.tooling.preview.Preview 10 | 11 | @Composable 12 | internal fun OtherSampleScreen() { 13 | Box( 14 | contentAlignment = Alignment.Center, 15 | modifier = Modifier 16 | .fillMaxSize() 17 | ) { 18 | Text( 19 | text = "other screen", 20 | ) 21 | } 22 | } 23 | 24 | @Preview( 25 | backgroundColor = 0xFFFFFF, 26 | ) 27 | @Composable 28 | private fun PreviewOtherSampleScreen() { 29 | OtherSampleScreen() 30 | } -------------------------------------------------------------------------------- /app/src/main/java/tech/thdev/app/ui/holder/web/LocalWebOwner.kt: -------------------------------------------------------------------------------- 1 | package tech.thdev.app.ui.holder.web 2 | 3 | import android.webkit.WebView 4 | import androidx.compose.runtime.Composable 5 | import androidx.compose.runtime.ProvidedValue 6 | import androidx.compose.runtime.staticCompositionLocalOf 7 | 8 | internal object LocalWebOwner { 9 | 10 | private val LocalComposition = staticCompositionLocalOf { null } 11 | 12 | val current: WebView? 13 | @Composable 14 | get() = LocalComposition.current 15 | 16 | infix fun provides(registerOwner: WebView?): ProvidedValue = 17 | LocalComposition provides registerOwner 18 | } 19 | -------------------------------------------------------------------------------- /app/src/main/java/tech/thdev/app/ui/second/SecondFragment.kt: -------------------------------------------------------------------------------- 1 | package tech.thdev.app.ui.second 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import androidx.fragment.app.Fragment 8 | import androidx.fragment.app.viewModels 9 | import androidx.navigation.fragment.findNavController 10 | import tech.thdev.app.R 11 | import tech.thdev.app.databinding.SecondFragmentBinding 12 | 13 | class SecondFragment : Fragment() { 14 | 15 | private lateinit var binding: SecondFragmentBinding 16 | 17 | private val viewModel: SecondViewModel by viewModels() 18 | 19 | override fun onCreateView( 20 | inflater: LayoutInflater, container: ViewGroup?, 21 | savedInstanceState: Bundle? 22 | ): View { 23 | return SecondFragmentBinding.inflate(inflater).also { 24 | binding = it 25 | }.root 26 | } 27 | 28 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 29 | super.onViewCreated(view, savedInstanceState) 30 | 31 | binding.buttonSecond.setOnClickListener { 32 | findNavController().navigate(R.id.action_SecondFragment_to_FirstFragment) 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /app/src/main/java/tech/thdev/app/ui/second/SecondViewModel.kt: -------------------------------------------------------------------------------- 1 | package tech.thdev.app.ui.second 2 | 3 | import androidx.lifecycle.ViewModel 4 | 5 | class SecondViewModel : ViewModel() { 6 | // TODO: Implement the ViewModel 7 | } -------------------------------------------------------------------------------- /app/src/main/java/tech/thdev/app/ui/theme/Color.kt: -------------------------------------------------------------------------------- 1 | package tech.thdev.app.ui.theme 2 | 3 | import androidx.compose.ui.graphics.Color 4 | 5 | val Purple80 = Color(0xFFD0BCFF) 6 | val PurpleGrey80 = Color(0xFFCCC2DC) 7 | val Pink80 = Color(0xFFEFB8C8) 8 | 9 | val Purple40 = Color(0xFF6650a4) 10 | val PurpleGrey40 = Color(0xFF625b71) 11 | val Pink40 = Color(0xFF7D5260) -------------------------------------------------------------------------------- /app/src/main/java/tech/thdev/app/ui/theme/Theme.kt: -------------------------------------------------------------------------------- 1 | package tech.thdev.app.ui.theme 2 | 3 | import android.os.Build 4 | import androidx.compose.foundation.isSystemInDarkTheme 5 | import androidx.compose.material3.MaterialTheme 6 | import androidx.compose.material3.darkColorScheme 7 | import androidx.compose.material3.dynamicDarkColorScheme 8 | import androidx.compose.material3.dynamicLightColorScheme 9 | import androidx.compose.material3.lightColorScheme 10 | import androidx.compose.runtime.Composable 11 | import androidx.compose.ui.platform.LocalContext 12 | 13 | private val DarkColorScheme = darkColorScheme( 14 | primary = Purple80, 15 | secondary = PurpleGrey80, 16 | tertiary = Pink80 17 | ) 18 | 19 | private val LightColorScheme = lightColorScheme( 20 | primary = Purple40, 21 | secondary = PurpleGrey40, 22 | tertiary = Pink40 23 | 24 | /* Other default colors to override 25 | background = Color(0xFFFFFBFE), 26 | surface = Color(0xFFFFFBFE), 27 | onPrimary = Color.White, 28 | onSecondary = Color.White, 29 | onTertiary = Color.White, 30 | onBackground = Color(0xFF1C1B1F), 31 | onSurface = Color(0xFF1C1B1F), 32 | */ 33 | ) 34 | 35 | @Composable 36 | fun MyApplicationTheme( 37 | darkTheme: Boolean = isSystemInDarkTheme(), 38 | // Dynamic color is available on Android 12+ 39 | dynamicColor: Boolean = true, 40 | content: @Composable () -> Unit 41 | ) { 42 | val colorScheme = when { 43 | dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { 44 | val context = LocalContext.current 45 | if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context) 46 | } 47 | 48 | darkTheme -> DarkColorScheme 49 | else -> LightColorScheme 50 | } 51 | 52 | MaterialTheme( 53 | colorScheme = colorScheme, 54 | typography = Typography, 55 | content = content, 56 | ) 57 | } -------------------------------------------------------------------------------- /app/src/main/java/tech/thdev/app/ui/theme/Type.kt: -------------------------------------------------------------------------------- 1 | package tech.thdev.app.ui.theme 2 | 3 | import androidx.compose.material3.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 | bodyLarge = TextStyle( 12 | fontFamily = FontFamily.Default, 13 | fontWeight = FontWeight.Normal, 14 | fontSize = 16.sp, 15 | lineHeight = 24.sp, 16 | letterSpacing = 0.5.sp 17 | ) 18 | /* Other default text styles to override 19 | titleLarge = TextStyle( 20 | fontFamily = FontFamily.Default, 21 | fontWeight = FontWeight.Normal, 22 | fontSize = 22.sp, 23 | lineHeight = 28.sp, 24 | letterSpacing = 0.sp 25 | ), 26 | labelSmall = TextStyle( 27 | fontFamily = FontFamily.Default, 28 | fontWeight = FontWeight.Medium, 29 | fontSize = 11.sp, 30 | lineHeight = 16.sp, 31 | letterSpacing = 0.5.sp 32 | ) 33 | */ 34 | ) -------------------------------------------------------------------------------- /app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /app/src/main/res/layout/content_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 19 | -------------------------------------------------------------------------------- /app/src/main/res/layout/first_fragment.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 18 | 19 |