diff --git a/.idea/migrations.xml b/.idea/migrations.xml new file mode 100644 index 0000000..f8051a6 --- /dev/null +++ b/.idea/migrations.xml @@ -0,0 +1,10 @@ + + + + + + \ No newline at end of file diff --git a/README.md b/README.md index 4c43f6c..3aebe89 100644 --- a/README.md +++ b/README.md @@ -1,50 +1,12 @@ -# Android Chat App entirely built with Jetpack Compose +# Zoomable Image with Jetpack Compose -![Run Unit Tests](https://github.com/mobiledevpro/Jetpack-Compose-ChatApp-Template/actions/workflows/run_tests.yml/badge.svg) -[![CodeFactor](https://www.codefactor.io/repository/github/mobiledevpro/jetpack-compose-chatapp-template/badge)](https://www.codefactor.io/repository/github/mobiledevpro/jetpack-compose-chatapp-template) -[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=mobiledevpro_Jetpack-Compose-ChatApp-Template&metric=alert_status)](https://sonarcloud.io/dashboard?id=mobiledevpro_Jetpack-Compose-ChatApp-Template) +Based on [App Template](https://github.com/mobiledevpro/Jetpack-Compose-ChatApp-Template) -[![Kotlin Version](https://img.shields.io/badge/Kotlin-1.9.22-blue.svg?style=flat-square)](http://kotlinlang.org/) -[![Compose Bom](https://img.shields.io/badge/Compose%20Bom-2024.01.00-blue.svg?style=flat-square)]([http://kotlinlang.org/](https://developer.android.com/jetpack/compose/bom/bom-mapping)) -[![Gradle](https://img.shields.io/badge/Gradle-8.2.0-blue.svg?style=flat-square)](https://developer.android.com/build/releases/gradle-plugin) -[![API](https://img.shields.io/badge/Min%20SDK-24%20[Android%207.0]-blue.svg?style=flat-square)](https://github.com/AndroidSDKSources/android-sdk-sources-list) -[![Target SDK](https://img.shields.io/badge/Target%20SDK-34%20[Android%2014]-blue.svg?style=flat-square)](https://developer.android.com/about/versions/13) -[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg?style=flat-square)](http://www.apache.org/licenses/LICENSE-2.0) -[![Android Studio](https://img.shields.io/badge/Android%20Studio%20Iguana-2023.2.1-orange.svg?style=flat-square)](https://developer.android.com/studio/preview) +![GitHub last commit](https://img.shields.io/github/last-commit/mobiledevpro/zoomable-image-jetpack-compose?color=red&style=flat-square) -![GitHub last commit](https://img.shields.io/github/last-commit/mobiledevpro/Jetpack-Compose-ChatApp-Template?color=red&style=flat-square) +Demo video - tap to watch: -![closetalk_github_social_preview](https://github.com/mobiledevpro/Jetpack-Compose-ChatApp-Template/assets/5750211/343f1ab5-54e4-41c2-a554-af0526aee382) - -[![Youtube](https://img.shields.io/badge/-youtube-red?logo=youtube&message=Youtube&style=for-the-badge&label=Watch+on)](https://www.youtube.com/playlist?list=PL9IBbMupfHWrW419OtGlzc7cBEMNqyLa4) - - -## -### Tech. stack - -* [Kotlin](https://kotlinlang.org/docs/getting-started.html) -* [Coroutines](https://kotlinlang.org/docs/coroutines-overview.html) -* [Jetpack Compose](https://developer.android.com/jetpack/compose) modern toolkit for building native UI -* [Jetpack libs: Navigation, Compose, etc.](https://developer.android.com/jetpack) -* [Material 3](https://m3.material.io/) -* [Koin](https://insert-koin.io/docs/reference/koin-android/compose) for dependency injection -* [Coil](https://coil-kt.github.io/coil/compose/) for image loading -* MVI + modularization with a clean architecture -* [Circle CI](https://circleci.com/) for continuous integration and delivery -* GitHub Actions [[Complete Guide](https://mobiledevpro.gumroad.com/l/zjbvsd)] - -## -## UI testing with [Maestro](https://maestro.mobile.dev/): - -* Install Maestro: run in terminal ```curl -Ls "https://get.maestro.mobile.dev" | bash``` -* Install the app on emulator (doesn't work with physical device) -* Run the flow: run in terminal ```maestro test -c maestro/people-profile-flow.yaml``` -* [Sample config](maestro/people-profile-flow.yaml) - -## -## Modularization - -![modularization](doc/modularization.png) +[![IMAGE ALT TEXT HERE](https://img.youtube.com/vi/mqjVI5_L9XA/0.jpg)](https://www.youtube.com/watch?v=mqjVI5_L9XA) ## ## Author: @@ -62,7 +24,7 @@ ## License: -Copyright 2023 Dmitri Chernysh +Copyright 2024 Dmitri Chernysh Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -75,12 +37,3 @@ distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. - -## Thanks for the support -**Stargazers** - -[![Stargazers repo roster for @mobiledevpro/Jetpack-Compose-ChatApp-Template](http://reporoster.com/stars/dark/mobiledevpro/Jetpack-Compose-ChatApp-Template)](https://github.com/mobiledevpro/Jetpack-Compose-ChatApp-Template/stargazers) - -**Forkers** - -[![Forkers repo roster for @mobiledevpro/Jetpack-Compose-ChatApp-Template](http://reporoster.com/forks/dark/mobiledevpro/Jetpack-Compose-ChatApp-Template)](https://github.com/mobiledevpro/Jetpack-Compose-ChatApp-Template/network/members) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index e1eddc7..85863c4 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -8,7 +8,7 @@ plugins { } android { - namespace = "com.mobiledevpro.apptemplate.compose" + namespace = "com.mobiledevpro.zoomable.image" compileSdk = libs.versions.sdk.compile.get().toInt() @@ -44,12 +44,10 @@ android { productFlavors { create("production") { dimension = "default" - applicationIdSuffix = ".closetalk" } create("dev") { dimension = "default" - applicationIdSuffix = ".apptemplate.compose" } } diff --git a/app/src/main/ic_launcher-playstore.png b/app/src/main/ic_launcher-playstore.png index 148f706..4d96dbe 100644 Binary files a/app/src/main/ic_launcher-playstore.png and b/app/src/main/ic_launcher-playstore.png differ diff --git a/app/src/main/kotlin/com/mobiledevpro/app/ui/MainApp.kt b/app/src/main/kotlin/com/mobiledevpro/app/ui/MainApp.kt index 9f19f40..088eb13 100644 --- a/app/src/main/kotlin/com/mobiledevpro/app/ui/MainApp.kt +++ b/app/src/main/kotlin/com/mobiledevpro/app/ui/MainApp.kt @@ -20,14 +20,14 @@ package com.mobiledevpro.app.ui import androidx.compose.runtime.Composable import androidx.navigation.compose.rememberNavController import com.mobiledevpro.navigation.Screen -import com.mobiledevpro.navigation.graph.RootNavGraph +import com.mobiledevpro.navigation.host.RootNavHost @Composable fun MainApp() { val navController = rememberNavController() - RootNavGraph( + RootNavHost( navController = navController, startDestination = Screen.Home ) diff --git a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/app/src/main/res/drawable-v24/ic_launcher_foreground.xml deleted file mode 100644 index 2b068d1..0000000 --- a/app/src/main/res/drawable-v24/ic_launcher_foreground.xml +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_launcher_background.xml b/app/src/main/res/drawable/ic_launcher_background.xml index 5cc10bf..ca3826a 100644 --- a/app/src/main/res/drawable/ic_launcher_background.xml +++ b/app/src/main/res/drawable/ic_launcher_background.xml @@ -1,170 +1,74 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/drawable/ic_launcher_foreground.xml b/app/src/main/res/drawable/ic_launcher_foreground.xml new file mode 100644 index 0000000..80d4342 --- /dev/null +++ b/app/src/main/res/drawable/ic_launcher_foreground.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml index 3f21f07..bbd3e02 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -1,5 +1,5 @@ - - + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml index 3f21f07..bbd3e02 100644 --- a/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ b/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -1,5 +1,5 @@ - - + + \ No newline at end of file diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.webp b/app/src/main/res/mipmap-hdpi/ic_launcher.webp index 9841941..e8551d5 100644 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher.webp and b/app/src/main/res/mipmap-hdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp index 34027f5..d36bcc0 100644 Binary files a/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-hdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.webp b/app/src/main/res/mipmap-mdpi/ic_launcher.webp index 398faee..4102456 100644 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher.webp and b/app/src/main/res/mipmap-mdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp index e996ddd..9c9b5f3 100644 Binary files a/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-mdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp index 7dff8c5..ccdc292 100644 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher.webp and b/app/src/main/res/mipmap-xhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp index ee5a145..1374325 100644 Binary files a/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-xhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp index b6c709e..6e7641c 100644 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp index ccd8ddf..c4bc86a 100644 Binary files a/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp index 3f2ae6b..c9e4720 100644 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.webp differ diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp index 03ead73..de33353 100644 Binary files a/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.webp differ diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index f9d739c..ea51bd2 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1,3 +1,3 @@ - Close Talk + Zoomable Image \ No newline at end of file diff --git a/build-logic/src/main/kotlin/feature-module.gradle.kts b/build-logic/src/main/kotlin/feature-module.gradle.kts index 75697df..814c8c2 100644 --- a/build-logic/src/main/kotlin/feature-module.gradle.kts +++ b/build-logic/src/main/kotlin/feature-module.gradle.kts @@ -13,7 +13,6 @@ android { dependencies { implementation(project(":core:ui")) implementation(project(":core:di")) - implementation(project(":core:domain")) implementation(project(":core:coroutines")) implementation(libs.bundle("lifecycle")) diff --git a/core/domain/build.gradle.kts b/core/domain/build.gradle.kts deleted file mode 100644 index 29b9440..0000000 --- a/core/domain/build.gradle.kts +++ /dev/null @@ -1,3 +0,0 @@ -plugins { - id("core-module") -} \ No newline at end of file diff --git a/core/domain/src/main/AndroidManifest.xml b/core/domain/src/main/AndroidManifest.xml deleted file mode 100644 index 1d26c87..0000000 --- a/core/domain/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/core/domain/src/main/kotlin/com/mobiledevpro/domain/model/Chat.kt b/core/domain/src/main/kotlin/com/mobiledevpro/domain/model/Chat.kt deleted file mode 100644 index e26a429..0000000 --- a/core/domain/src/main/kotlin/com/mobiledevpro/domain/model/Chat.kt +++ /dev/null @@ -1,44 +0,0 @@ -package com.mobiledevpro.domain.model - -import android.net.Uri - -/** - * Chat - * - * Created on May 06, 2023. - * - */ -data class Chat( - val user: UserProfile, - val peopleList: List, - val unreadMsgCount : Int = 0 -) - -val fakeUser = UserProfile( - name = "Joe Black", - nickname = "@nickname", - status = true, - photo = Uri.parse("https://media.istockphoto.com/id/1090878494/photo/close-up-portrait-of-young-smiling-handsome-man-in-blue-polo-shirt-isolated-on-gray-background.jpg?b=1&s=170667a&w=0&k=20&c=c3TaqVe9-0EcHl7mjO-9YChSvGBDhvzUai6obs1Ibz4=") -) - -val fakeChatList = arrayListOf( - Chat( - user = fakeUser, - peopleList = fakePeopleProfileList.take(5).sortedByDescending { !it.status }, - unreadMsgCount = 100 - ), - Chat( - user = fakeUser, - peopleList = fakePeopleProfileList.takeLast(3).sortedByDescending { !it.status }, - unreadMsgCount = 0 - ), - - Chat( - user = fakeUser, - peopleList = listOf(fakePeopleProfileList[6]), - unreadMsgCount = 3 - ) -) - -fun Chat.name() : String = - this.peopleList.toChatName() diff --git a/core/domain/src/main/kotlin/com/mobiledevpro/domain/model/PeopleProfile.kt b/core/domain/src/main/kotlin/com/mobiledevpro/domain/model/PeopleProfile.kt deleted file mode 100644 index 375400c..0000000 --- a/core/domain/src/main/kotlin/com/mobiledevpro/domain/model/PeopleProfile.kt +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright 2022 | Dmitri Chernysh | https://mobile-dev.pro - * - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package com.mobiledevpro.domain.model - -import android.net.Uri - -/** - * Profile - * - * Created on Feb 04, 2023. - * - */ - -data class PeopleProfile( - val id: Int, - val name: String, - val status: Boolean, - val photo: Uri? = null -) { - fun listKey(): String = "${id}_${name.replace("\\s+".toRegex(), "")}" -} - - -val fakePeopleProfileList = arrayListOf( - PeopleProfile( - id = 0, - name = "Michaela Runnings", - status = true, - Uri.parse("https://images.unsplash.com/photo-1485290334039-a3c69043e517?ixid=MXwxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=800&q=80") - ), - PeopleProfile( - id = 1, - name = "John Pestridge", - status = false, - Uri.parse("https://images.unsplash.com/photo-1542178243-bc20204b769f?ixid=MXwxMjA3fDB8MHxzZWFyY2h8MTB8fHBvcnRyYWl0fGVufDB8MnwwfA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60") - ), - PeopleProfile( - id = 2, - name = "Manilla Andrews", - status = true, - Uri.parse("https://images.unsplash.com/photo-1543123820-ac4a5f77da38?ixid=MXwxMjA3fDB8MHxzZWFyY2h8NDh8fHBvcnRyYWl0fGVufDB8MnwwfA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60") - ), - PeopleProfile( - id = 3, - name = "Dan Spicer", - status = false, - Uri.parse("https://images.unsplash.com/photo-1595152772835-219674b2a8a6?ixid=MXwxMjA3fDB8MHxzZWFyY2h8NDd8fHBvcnRyYWl0fGVufDB8MnwwfA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60") - ), - PeopleProfile( - id = 4, - name = "Keanu Dester", - status = false, - Uri.parse("https://images.unsplash.com/photo-1597528380214-aa94bde3fc32?ixid=MXwxMjA3fDB8MHxzZWFyY2h8NTZ8fHBvcnRyYWl0fGVufDB8MnwwfA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60") - ), - PeopleProfile( - id = 5, - name = "Anichu Patel", - status = false, - Uri.parse("https://images.unsplash.com/photo-1598641795816-a84ac9eac40c?ixid=MXwxMjA3fDB8MHxzZWFyY2h8NjJ8fHBvcnRyYWl0fGVufDB8MnwwfA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60") - ), - PeopleProfile( - id = 6, - name = "Kienla Onso", - status = true, - Uri.parse("https://images.unsplash.com/photo-1566895733044-d2bdda8b6234?ixid=MXwxMjA3fDB8MHxzZWFyY2h8ODh8fHBvcnRyYWl0fGVufDB8MnwwfA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60") - ), - PeopleProfile( - id = 7, - name = "Andra Matthews", - status = false, - Uri.parse("https://images.unsplash.com/photo-1530577197743-7adf14294584?ixid=MXwxMjA3fDB8MHxzZWFyY2h8NTV8fHBvcnRyYWl0fGVufDB8MnwwfA%3D%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60") - ), - PeopleProfile( - id = 8, - name = "Georgia S.", status = false, - Uri.parse("https://images.unsplash.com/photo-1547212371-eb5e6a4b590c?ixid=MXwxMjA3fDB8MHxzZWFyY2h8MTA3fHxwb3J0cmFpdHxlbnwwfDJ8MHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60") - ), - PeopleProfile( - id = 8, - name = "Matt Dengo", - status = false, - Uri.parse("https://images.unsplash.com/photo-1578176603894-57973e38890f?ixid=MXwxMjA3fDB8MHxzZWFyY2h8MTE0fHxwb3J0cmFpdHxlbnwwfDJ8MHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60") - ), - PeopleProfile( - id = 9, - name = "Marsha T.", - status = true, - Uri.parse("https://images.unsplash.com/photo-1605087880595-8cc6db61f3c6?ixid=MXwxMjA3fDB8MHxzZWFyY2h8MTI0fHxwb3J0cmFpdHxlbnwwfDJ8MHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60") - ), - PeopleProfile( - id = 10, - name = "Invshu Patel", - status = true, - Uri.parse("https://images.unsplash.com/photo-1561820009-8bef03ebf8e5?ixid=MXwxMjA3fDB8MHxzZWFyY2h8MTM3fHxwb3J0cmFpdHxlbnwwfDJ8MHw%3D&ixlib=rb-1.2.1&auto=format&fit=crop&w=500&q=60") - ) -) - -fun List.toChatName(): String = - mapTo(ArrayList()) { profile -> profile.name }.let { names -> - val stringBuilder = StringBuilder() - names.onEachIndexed { index, s -> - if (index > 0) - stringBuilder.append(", ") - stringBuilder.append(s) - } - stringBuilder.toString() - } \ No newline at end of file diff --git a/core/domain/src/main/kotlin/com/mobiledevpro/domain/model/UserProfile.kt b/core/domain/src/main/kotlin/com/mobiledevpro/domain/model/UserProfile.kt deleted file mode 100644 index 0116ae4..0000000 --- a/core/domain/src/main/kotlin/com/mobiledevpro/domain/model/UserProfile.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2022 | Dmitri Chernysh | https://mobile-dev.pro - * - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package com.mobiledevpro.domain.model - -import android.net.Uri - -/** - * App User Profile - * - * Created on Feb 04, 2023. - * - */ - -data class UserProfile( - val name : String, - val nickname: String, - val status: Boolean = false, - val photo : Uri = Uri.EMPTY -) diff --git a/core/navigation/build.gradle.kts b/core/navigation/build.gradle.kts index 0068d5c..7ea1ac8 100644 --- a/core/navigation/build.gradle.kts +++ b/core/navigation/build.gradle.kts @@ -14,12 +14,7 @@ dependencies { api(projects.core.ui) api(projects.core.di) - implementation(projects.core.domain) implementation(projects.feature.home) - implementation(projects.feature.onboarding) - implementation(projects.feature.subscription) - implementation(projects.feature.chatList) - implementation(projects.feature.people) - implementation(projects.feature.userProfile) + implementation(projects.feature.imageViewer) } \ No newline at end of file diff --git a/core/navigation/src/main/kotlin/com/mobiledevpro/navigation/HomeBottomNavigation.kt b/core/navigation/src/main/kotlin/com/mobiledevpro/navigation/HomeBottomNavigation.kt deleted file mode 100644 index dabeb35..0000000 --- a/core/navigation/src/main/kotlin/com/mobiledevpro/navigation/HomeBottomNavigation.kt +++ /dev/null @@ -1,73 +0,0 @@ -/* - * Copyright 2022 | Dmitri Chernysh | https://mobile-dev.pro - * - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package com.mobiledevpro.navigation - -import android.util.Log -import androidx.compose.animation.AnimatedVisibility -import androidx.compose.animation.slideInHorizontally -import androidx.compose.animation.slideOutHorizontally -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.Warning -import androidx.compose.material3.Icon -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.navigation.NavDestination -import androidx.navigation.NavDestination.Companion.hierarchy -import com.mobiledevpro.ui.component.AppBottomBar -import com.mobiledevpro.ui.component.AppBottomBarItem - - -@Composable -fun HomeBottomNavigation( - screens: List, - onNavigateTo: (Screen) -> Unit, - currentDestination: NavDestination? -) { - - Log.d("navigation", "HomeBottomNavigation") - - AnimatedVisibility( - visible = true, - enter = slideInHorizontally(initialOffsetX = { it }), - exit = slideOutHorizontally(targetOffsetX = { it }), - ) { - - - AppBottomBar { - - screens.forEach { screen -> - Log.d("navigation", "HomeBottomNavigation: hierarchy = $currentDestination") - - val selected: Boolean = - currentDestination?.hierarchy?.any { it.route == screen.route } ?: false - - AppBottomBarItem( - selected = selected, - onClick = { onNavigateTo(screen) }, - icon = { - Icon( - imageVector = screen.icon ?: Icons.Default.Warning, - contentDescription = null - ) - }, - label = { Text(text = screen.title ?: "") } - ) - } - } - } -} \ No newline at end of file diff --git a/core/navigation/src/main/kotlin/com/mobiledevpro/navigation/Route.kt b/core/navigation/src/main/kotlin/com/mobiledevpro/navigation/Route.kt index 7af7c6b..447dd1b 100644 --- a/core/navigation/src/main/kotlin/com/mobiledevpro/navigation/Route.kt +++ b/core/navigation/src/main/kotlin/com/mobiledevpro/navigation/Route.kt @@ -17,28 +17,11 @@ */ package com.mobiledevpro.navigation -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.rounded.Home -import androidx.compose.material.icons.rounded.Person -import androidx.compose.material.icons.rounded.Settings import androidx.compose.ui.graphics.vector.ImageVector -import com.mobiledevpro.people.profile.view.args.PeopleProfileArgs -const val navigationRouteOnBoarding = "on_boarding" -const val navigationRouteOnBoardingFirst = "on_boarding_first" -const val navigationRouteOnBoardingSecond = "on_boarding_second" -const val navigationRouteOnBoardingThird = "on_boarding_third" const val navigationRouteHome = "home" -const val navigationRouteChatList = "chat_list" -const val navigationRouteProfile = "profile" - -const val navigationRoutePeople = "people" -const val navigationRoutePeopleList = "people_list" -const val navigationRoutePeopleProfile = - "people_profile/{${PeopleProfileArgs.PEOPLE_PROFILE_ID_ARG}}" - -const val navigationRouteSubscription = "subscription" +const val navigationRouteImageViewer = "image_viewer" sealed class Screen( val route: String, @@ -55,30 +38,9 @@ sealed class Screen( routePath = path } - object OnBoarding : Screen(navigationRouteOnBoarding) - object OnBoardingFirst : Screen(navigationRouteOnBoardingFirst) - object OnBoardingSecond : Screen(navigationRouteOnBoardingSecond) - object OnBoardingThird : Screen(navigationRouteOnBoardingThird) - - object Home : Screen(navigationRouteHome) - - // 3 tabs of Bottom navigation - object ChatList : - Screen(route = navigationRouteChatList, title = "Chats", icon = Icons.Rounded.Home) - - object People : Screen( - route = navigationRoutePeople, - restoreState = false, - title = "People", - icon = Icons.Rounded.Person, - ) - - object Profile : - Screen(route = navigationRouteProfile, title = "Profile", icon = Icons.Rounded.Settings) - - object Subscription : Screen(navigationRouteSubscription) + data object Home : Screen(navigationRouteHome) - object PeopleList : Screen(navigationRoutePeopleList) - object PeopleProfile : Screen(navigationRoutePeopleProfile) + data object ImageViewer : + Screen(route = navigationRouteImageViewer) } \ No newline at end of file diff --git a/core/navigation/src/main/kotlin/com/mobiledevpro/navigation/ScreenNavigation.kt b/core/navigation/src/main/kotlin/com/mobiledevpro/navigation/ScreenNavigation.kt deleted file mode 100644 index 4214a75..0000000 --- a/core/navigation/src/main/kotlin/com/mobiledevpro/navigation/ScreenNavigation.kt +++ /dev/null @@ -1,259 +0,0 @@ -/* - * Copyright 2022 | Dmitri Chernysh | https://mobile-dev.pro - * - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package com.mobiledevpro.navigation - -import android.util.Log -import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember -import androidx.lifecycle.viewmodel.compose.viewModel -import androidx.navigation.NavGraphBuilder -import androidx.navigation.NavType -import androidx.navigation.compose.composable -import androidx.navigation.compose.currentBackStackEntryAsState -import androidx.navigation.compose.rememberNavController -import androidx.navigation.navArgument -import com.mobiledevpro.chatlist.di.featureChatListModule -import com.mobiledevpro.chatlist.view.ChatListScreen -import com.mobiledevpro.chatlist.view.ChatListViewModel -import com.mobiledevpro.di.rememberViewModel -import com.mobiledevpro.home.view.HomeScreen -import com.mobiledevpro.home.view.HomeViewModel -import com.mobiledevpro.navigation.ext.navigateTo -import com.mobiledevpro.navigation.graph.HomeNavGraph -import com.mobiledevpro.navigation.graph.OnBoardingNavGraph -import com.mobiledevpro.navigation.graph.PeopleNavGraph -import com.mobiledevpro.onboarding.view.OnBoardingFirstScreen -import com.mobiledevpro.onboarding.view.OnBoardingScreen -import com.mobiledevpro.onboarding.view.OnBoardingSecondScreen -import com.mobiledevpro.onboarding.view.OnBoardingThirdScreen -import com.mobiledevpro.people.profile.view.PeopleProfileScreen -import com.mobiledevpro.people.profile.view.PeopleProfileViewModel -import com.mobiledevpro.people.profile.view.args.PeopleProfileArgs -import com.mobiledevpro.people.view.PeopleScreen -import com.mobiledevpro.peoplelist.di.featurePeopleListModule -import com.mobiledevpro.peoplelist.view.PeopleListScreen -import com.mobiledevpro.peoplelist.view.PeopleListViewModel -import com.mobiledevpro.subscription.SubscriptionScreen -import com.mobiledevpro.user.profile.di.featureUserProfileModule -import com.mobiledevpro.user.profile.view.ProfileScreen -import com.mobiledevpro.user.profile.view.vm.ProfileViewModel - - -fun NavGraphBuilder.homeNavGraph(onNavigateToRoot: (Screen) -> Unit) { - composable( - route = Screen.Home.route - ) { - Log.d("navigation", "------homeNavGraph:START------------") - - //NavController for nested graph - //It will not work for root graph - val navController = rememberNavController() - val navBackStackEntry by navController.currentBackStackEntryAsState() - - val bottomBar: @Composable () -> Unit = { - Log.d("navigation", "homeNavGraph:bottomBar") - HomeBottomNavigation( - screens = listOf( - Screen.ChatList, - Screen.People, - Screen.Profile - ), onNavigateTo = navController::navigateTo, - currentDestination = navBackStackEntry?.destination - ) - } - - val nestedNavGraph: @Composable () -> Unit = { - Log.d("navigation", "homeNavGraph:nestedNavGraph") - HomeNavGraph( - navController = navController, - onNavigateToRoot = onNavigateToRoot - ) - } - - val viewModel: HomeViewModel = viewModel() - - HomeScreen( - bottomBar = bottomBar, - nestedNavGraph = nestedNavGraph - ) - - Log.d("navigation", "------homeNavGraph:END------------") - } - -} - -fun NavGraphBuilder.onBoardingNavGraph(onNavigateToRoot: (Screen) -> Unit) { - composable( - route = Screen.OnBoarding.route - ) { - - val navController = rememberNavController() - - val nestedNavGraph: @Composable () -> Unit = { - OnBoardingNavGraph( - navController = navController - ) - } - - OnBoardingScreen( - nestedNavGraph, - onNext = { - when (navController.currentDestination?.route) { - Screen.OnBoardingFirst.route -> navController.navigateTo(Screen.OnBoardingSecond) - Screen.OnBoardingSecond.route -> navController.navigateTo(Screen.OnBoardingThird) - Screen.OnBoardingThird.route -> Screen.Home.withClearBackStack() - .also(onNavigateToRoot) - - else -> {} - } - } - ) - } -} - -fun NavGraphBuilder.peopleNavGraph() { - composable( - route = Screen.People.route - ) { - val navController = rememberNavController() - - val nestedNavGraph: @Composable () -> Unit = { - PeopleNavGraph( - navController = navController - ) - } - - PeopleScreen(nestedNavGraph) - } -} - -fun NavGraphBuilder.onBoardingFirstScreen() { - composable( - route = Screen.OnBoardingFirst.route - ) { - OnBoardingFirstScreen() - } -} - -fun NavGraphBuilder.onBoardingSecondScreen() { - composable( - route = Screen.OnBoardingSecond.route - ) { - OnBoardingSecondScreen() - } -} - -fun NavGraphBuilder.onBoardingThirdScreen() { - composable( - route = Screen.OnBoardingThird.route - ) { - OnBoardingThirdScreen() - } -} - -fun NavGraphBuilder.subscriptionScreen(onNavigateBack: () -> Unit) { - composable( - route = Screen.Subscription.route - ) { - SubscriptionScreen(onNavigateBack) - } -} - -fun NavGraphBuilder.chatListScreen() { - composable( - route = Screen.ChatList.route - ) { - - val viewModel = rememberViewModel( - modules = { listOf(featureChatListModule) } - ) - - ChatListScreen( - state = viewModel.uiState, - onClick = { chat -> - //TODO: open chat screen - } - ) - } -} - -fun NavGraphBuilder.peopleListScreen(onNavigateTo: (Screen) -> Unit) { - composable( - route = Screen.PeopleList.route - ) { - - val viewModel = rememberViewModel( - modules = { listOf(featurePeopleListModule) } - ) - - PeopleListScreen( - viewModel.uiState, - onNavigateToProfile = { profileId: Int -> - Screen.PeopleProfile.routeWith(profileId.toString()) - .also(onNavigateTo) - } - ) - } -} - -fun NavGraphBuilder.peopleProfileScreen( - onNavigateBack: () -> Unit, - onNavigateTo: (Screen) -> Unit -) { - composable( - route = Screen.PeopleProfile.route, - arguments = listOf( - navArgument(PeopleProfileArgs.PEOPLE_PROFILE_ID_ARG) { type = NavType.IntType } - ) - ) { - - val viewModel: PeopleProfileViewModel = viewModel() - val peopleProfile = remember { viewModel.getProfile() } - - peopleProfile ?: return@composable - - PeopleProfileScreen( - peopleProfile, - onBackPressed = onNavigateBack, - onOpenChatWith = {} - ) - } -} - - -fun NavGraphBuilder.profileScreen(onNavigateTo: (Screen) -> Unit) { - composable( - route = Screen.Profile.route - ) { - - val viewModel = rememberViewModel( - modules = { - listOf(featureUserProfileModule) - } - ) - - ProfileScreen( - state = viewModel.uiState, - onNavigateToSubscription = { - onNavigateTo(Screen.Subscription) - } - ) - } -} - diff --git a/core/navigation/src/main/kotlin/com/mobiledevpro/navigation/graph/HomeNavGraph.kt b/core/navigation/src/main/kotlin/com/mobiledevpro/navigation/graph/HomeNavGraph.kt index bfa2da7..5c899cf 100644 --- a/core/navigation/src/main/kotlin/com/mobiledevpro/navigation/graph/HomeNavGraph.kt +++ b/core/navigation/src/main/kotlin/com/mobiledevpro/navigation/graph/HomeNavGraph.kt @@ -17,36 +17,47 @@ */ package com.mobiledevpro.navigation.graph +import android.util.Log import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.navigation.NavHostController -import androidx.navigation.compose.NavHost +import androidx.compose.runtime.getValue +import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.navigation.NavGraphBuilder +import androidx.navigation.compose.composable +import androidx.navigation.compose.currentBackStackEntryAsState +import androidx.navigation.compose.rememberNavController +import com.mobiledevpro.home.view.HomeScreen +import com.mobiledevpro.home.view.HomeViewModel import com.mobiledevpro.navigation.Screen -import com.mobiledevpro.navigation.chatListScreen -import com.mobiledevpro.navigation.peopleNavGraph -import com.mobiledevpro.navigation.profileScreen +import com.mobiledevpro.navigation.host.HomeNavHost -/** - * Nested navigation graph for Home screen - * - * Created on Jan 24, 2023. - * - */ -@Composable -fun HomeNavGraph( - modifier: Modifier = Modifier, - navController: NavHostController, - onNavigateToRoot: (Screen) -> Unit -) { - NavHost( - navController = navController, - startDestination = Screen.ChatList.route, - modifier = modifier, + +fun NavGraphBuilder.homeNavGraph(onNavigateToRoot: (Screen) -> Unit) { + composable( + route = Screen.Home.route ) { - chatListScreen() - peopleNavGraph() - profileScreen(onNavigateTo = onNavigateToRoot) + //NavController for nested graph + //It will not work for root graph + val navController = rememberNavController() + val navBackStackEntry by navController.currentBackStackEntryAsState() + + val nestedNavGraph: @Composable () -> Unit = { + Log.d("navigation", "homeNavGraph:nestedNavGraph") + HomeNavHost( + navController = navController, + onNavigateToRoot = onNavigateToRoot + ) + } + + val viewModel: HomeViewModel = viewModel() + + HomeScreen( + nestedNavGraph = nestedNavGraph + ) } -} \ No newline at end of file + +} + + + diff --git a/feature/user_profile/src/main/kotlin/com/mobiledevpro/user/profile/view/state/UserProfileUIState.kt b/core/navigation/src/main/kotlin/com/mobiledevpro/navigation/graph/ImageViewerNavGraph.kt similarity index 56% rename from feature/user_profile/src/main/kotlin/com/mobiledevpro/user/profile/view/state/UserProfileUIState.kt rename to core/navigation/src/main/kotlin/com/mobiledevpro/navigation/graph/ImageViewerNavGraph.kt index 5b1a1b2..3ac491c 100644 --- a/feature/user_profile/src/main/kotlin/com/mobiledevpro/user/profile/view/state/UserProfileUIState.kt +++ b/core/navigation/src/main/kotlin/com/mobiledevpro/navigation/graph/ImageViewerNavGraph.kt @@ -15,22 +15,22 @@ * limitations under the License. * */ -package com.mobiledevpro.user.profile.view.state +package com.mobiledevpro.navigation.graph -import com.mobiledevpro.domain.model.UserProfile -import com.mobiledevpro.ui.state.UIState +import androidx.navigation.NavGraphBuilder +import androidx.navigation.compose.composable +import androidx.navigation.compose.rememberNavController +import com.mobiledevpro.image.viewer.view.ImageViewerScreen +import com.mobiledevpro.navigation.Screen -/** - * UI state for [com.mobiledevpro.user.profile.view.ProfileScreen] - * - * Created on May 09, 2023. - * - */ -sealed interface UserProfileUIState : UIState { - object Empty : UserProfileUIState +fun NavGraphBuilder.imageViewerNavGraph() { + composable( + route = Screen.ImageViewer.route + ) { + val navController = rememberNavController() - class Success(val userProfile: UserProfile) : UserProfileUIState + ImageViewerScreen() + } +} - class Fail(val throwable: Throwable) : UserProfileUIState -} \ No newline at end of file diff --git a/core/navigation/src/main/kotlin/com/mobiledevpro/navigation/graph/PeopleNavGraph.kt b/core/navigation/src/main/kotlin/com/mobiledevpro/navigation/graph/PeopleNavGraph.kt deleted file mode 100644 index a37937f..0000000 --- a/core/navigation/src/main/kotlin/com/mobiledevpro/navigation/graph/PeopleNavGraph.kt +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright 2022 | Dmitri Chernysh | https://mobile-dev.pro - * - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package com.mobiledevpro.navigation.graph - -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.navigation.NavHostController -import androidx.navigation.compose.NavHost -import com.mobiledevpro.navigation.Screen -import com.mobiledevpro.navigation.ext.navigateTo -import com.mobiledevpro.navigation.peopleListScreen -import com.mobiledevpro.navigation.peopleProfileScreen - -/** - * Nested navigation graph for People screen - * - * Created on Feb 04, 2023. - * - */ - -@Composable -fun PeopleNavGraph( - modifier: Modifier = Modifier, - navController: NavHostController -) { - NavHost( - navController = navController, - startDestination = Screen.PeopleList.route, - modifier = modifier, - ) { - - peopleListScreen(onNavigateTo = navController::navigateTo) - peopleProfileScreen( - onNavigateTo = navController::navigateTo, - onNavigateBack = navController::navigateUp - ) - } -} \ No newline at end of file diff --git a/core/navigation/src/main/kotlin/com/mobiledevpro/navigation/graph/OnBoardingNavGraph.kt b/core/navigation/src/main/kotlin/com/mobiledevpro/navigation/host/HomeNavHost.kt similarity index 68% rename from core/navigation/src/main/kotlin/com/mobiledevpro/navigation/graph/OnBoardingNavGraph.kt rename to core/navigation/src/main/kotlin/com/mobiledevpro/navigation/host/HomeNavHost.kt index 886168a..0c33eb7 100644 --- a/core/navigation/src/main/kotlin/com/mobiledevpro/navigation/graph/OnBoardingNavGraph.kt +++ b/core/navigation/src/main/kotlin/com/mobiledevpro/navigation/host/HomeNavHost.kt @@ -15,36 +15,34 @@ * limitations under the License. * */ -package com.mobiledevpro.navigation.graph +package com.mobiledevpro.navigation.host import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import com.mobiledevpro.navigation.Screen -import com.mobiledevpro.navigation.onBoardingFirstScreen -import com.mobiledevpro.navigation.onBoardingSecondScreen -import com.mobiledevpro.navigation.onBoardingThirdScreen +import com.mobiledevpro.navigation.graph.imageViewerNavGraph /** - * Nested navigation graph for OnBoarding screen + * Nested navigation graph for Home screen * * Created on Jan 24, 2023. * */ @Composable -fun OnBoardingNavGraph( +fun HomeNavHost( modifier: Modifier = Modifier, - navController: NavHostController + navController: NavHostController, + onNavigateToRoot: (Screen) -> Unit ) { NavHost( navController = navController, - startDestination = Screen.OnBoardingFirst.route, + startDestination = Screen.ImageViewer.route, modifier = modifier, ) { - onBoardingFirstScreen() - onBoardingSecondScreen() - onBoardingThirdScreen() + imageViewerNavGraph() + } } \ No newline at end of file diff --git a/core/navigation/src/main/kotlin/com/mobiledevpro/navigation/graph/RootNavGraph.kt b/core/navigation/src/main/kotlin/com/mobiledevpro/navigation/host/RootNavHost.kt similarity index 79% rename from core/navigation/src/main/kotlin/com/mobiledevpro/navigation/graph/RootNavGraph.kt rename to core/navigation/src/main/kotlin/com/mobiledevpro/navigation/host/RootNavHost.kt index d08b18a..2ffb098 100644 --- a/core/navigation/src/main/kotlin/com/mobiledevpro/navigation/graph/RootNavGraph.kt +++ b/core/navigation/src/main/kotlin/com/mobiledevpro/navigation/host/RootNavHost.kt @@ -15,7 +15,7 @@ * limitations under the License. * */ -package com.mobiledevpro.navigation.graph +package com.mobiledevpro.navigation.host import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier @@ -23,9 +23,7 @@ import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import com.mobiledevpro.navigation.Screen import com.mobiledevpro.navigation.ext.navigateTo -import com.mobiledevpro.navigation.homeNavGraph -import com.mobiledevpro.navigation.onBoardingNavGraph -import com.mobiledevpro.navigation.subscriptionScreen +import com.mobiledevpro.navigation.graph.homeNavGraph /** * Top-level navigation host in the app @@ -35,7 +33,7 @@ import com.mobiledevpro.navigation.subscriptionScreen */ @Composable -fun RootNavGraph( +fun RootNavHost( navController: NavHostController, modifier: Modifier = Modifier, startDestination: Screen @@ -52,10 +50,6 @@ fun RootNavGraph( } //Nested Navigation Graphs - onBoardingNavGraph(onNavigateToRoot = navController::navigateTo) homeNavGraph(onNavigateToRoot = navController::navigateTo) - - //Root screens - subscriptionScreen(onNavigateBack = navigateBack) } } \ No newline at end of file diff --git a/feature/chat_list/.gitignore b/feature/chat_list/.gitignore deleted file mode 100644 index 42afabf..0000000 --- a/feature/chat_list/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build \ No newline at end of file diff --git a/feature/chat_list/consumer-rules.pro b/feature/chat_list/consumer-rules.pro deleted file mode 100644 index e69de29..0000000 diff --git a/feature/chat_list/proguard-rules.pro b/feature/chat_list/proguard-rules.pro deleted file mode 100644 index 481bb43..0000000 --- a/feature/chat_list/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/chat_list/src/main/AndroidManifest.xml b/feature/chat_list/src/main/AndroidManifest.xml deleted file mode 100644 index 1d26c87..0000000 --- a/feature/chat_list/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/feature/chat_list/src/main/kotlin/com/mobiledevpro/chatlist/di/Module.kt b/feature/chat_list/src/main/kotlin/com/mobiledevpro/chatlist/di/Module.kt deleted file mode 100644 index ae76a72..0000000 --- a/feature/chat_list/src/main/kotlin/com/mobiledevpro/chatlist/di/Module.kt +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2023 | Dmitri Chernysh | https://mobile-dev.pro - * - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package com.mobiledevpro.chatlist.di - -import com.mobiledevpro.chatlist.domain.usecase.GetChatListUseCase -import com.mobiledevpro.chatlist.view.ChatListViewModel -import org.koin.androidx.viewmodel.dsl.viewModelOf -import org.koin.core.module.dsl.scopedOf -import org.koin.dsl.module - -/** - * User Profile screen module - * - * Created on Jul 22, 2023. - * - */ - -val featureChatListModule = module { - - scope { - viewModelOf(::ChatListViewModel) - scopedOf(::GetChatListUseCase) - } -} \ No newline at end of file diff --git a/feature/chat_list/src/main/kotlin/com/mobiledevpro/chatlist/domain/usecase/GetChatListUseCase.kt b/feature/chat_list/src/main/kotlin/com/mobiledevpro/chatlist/domain/usecase/GetChatListUseCase.kt deleted file mode 100644 index 72af1d6..0000000 --- a/feature/chat_list/src/main/kotlin/com/mobiledevpro/chatlist/domain/usecase/GetChatListUseCase.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright 2023 | Dmitri Chernysh | https://mobile-dev.pro - * - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package com.mobiledevpro.chatlist.domain.usecase - -import com.mobiledevpro.coroutines.BaseCoroutinesFLowUseCase -import com.mobiledevpro.coroutines.None -import com.mobiledevpro.domain.model.Chat -import com.mobiledevpro.domain.model.fakeChatList -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flowOf - - -class GetChatListUseCase( - -) : BaseCoroutinesFLowUseCase, None>(Dispatchers.IO) { - - override fun buildUseCaseFlow(params: None?): Flow> = - flowOf(fakeChatList) - -} \ No newline at end of file diff --git a/feature/chat_list/src/main/kotlin/com/mobiledevpro/chatlist/view/ChatListScreen.kt b/feature/chat_list/src/main/kotlin/com/mobiledevpro/chatlist/view/ChatListScreen.kt deleted file mode 100644 index 04f6af4..0000000 --- a/feature/chat_list/src/main/kotlin/com/mobiledevpro/chatlist/view/ChatListScreen.kt +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright 2022 | Dmitri Chernysh | https://mobile-dev.pro - * - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package com.mobiledevpro.chatlist.view - -import android.widget.Toast -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.itemsIndexed -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.mobiledevpro.chatlist.view.component.ChatCard -import com.mobiledevpro.domain.model.Chat -import com.mobiledevpro.domain.model.fakeChatList -import com.mobiledevpro.ui.component.ScreenBackground -import com.mobiledevpro.ui.ext.dp -import com.mobiledevpro.ui.ext.statusBarHeight -import com.mobiledevpro.ui.theme.AppTheme -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow - - -@Composable -fun ChatListScreen( - state: StateFlow, - onClick: (Chat) -> Unit -) { - - val uiState by state.collectAsStateWithLifecycle() - val context = LocalContext.current - - ScreenBackground( - modifier = Modifier - .fillMaxSize() - ) { - - when (uiState) { - is ChatListUIState.Loading -> Loading() - is ChatListUIState.Empty -> NoChatFound() - is ChatListUIState.Success -> - ChatList( - chatList = (uiState as ChatListUIState.Success).profileList, - onClick = onClick - ) - - is ChatListUIState.Fail -> { - NoChatFound() - LaunchedEffect(Unit) { - Toast.makeText( - context, - (uiState as ChatListUIState.Fail).throwable.localizedMessage, - Toast.LENGTH_LONG - ).show() - } - } - - } - - - } -} - -@Composable -private fun ChatList(chatList: List, onClick: (Chat) -> Unit) { - - //Cause content is drawn edge-to-edge, let's set the top-padding for the first element in the list - val statusBarHeight: Dp = WindowInsets.statusBarHeight().dp() - - LazyColumn { - itemsIndexed(chatList) { index, chat -> - ChatCard( - modifier = Modifier.then( - if (index == 0) - Modifier.padding(top = statusBarHeight) - else - Modifier - ), - chat = chat, - onClick = { onClick(chat) } - ) - } - } -} - -@Composable -private fun NoChatFound() { - Box( - modifier = Modifier - .fillMaxSize() - .padding(16.dp) - ) { - Text( - text = "No chat found", - modifier = Modifier - .padding(16.dp) - .align(Alignment.Center), - style = MaterialTheme.typography.bodyLarge - ) - } -} - -@Composable -private fun Loading() { - Box( - modifier = Modifier - .fillMaxSize() - .padding(16.dp) - ) { - Text( - text = "Loading...", modifier = Modifier - .padding(16.dp) - .align(Alignment.Center), - style = MaterialTheme.typography.bodyLarge - ) - } -} - -@Composable -@Preview -fun ChatListScreenPreview() { - AppTheme { - ChatListScreen( - state = MutableStateFlow(ChatListUIState.Success(fakeChatList)), - onClick = {} - ) - } -} \ No newline at end of file diff --git a/feature/chat_list/src/main/kotlin/com/mobiledevpro/chatlist/view/ChatListUIState.kt b/feature/chat_list/src/main/kotlin/com/mobiledevpro/chatlist/view/ChatListUIState.kt deleted file mode 100644 index a94f7bd..0000000 --- a/feature/chat_list/src/main/kotlin/com/mobiledevpro/chatlist/view/ChatListUIState.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2022 | Dmitri Chernysh | https://mobile-dev.pro - * - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package com.mobiledevpro.chatlist.view - -import com.mobiledevpro.domain.model.Chat -import com.mobiledevpro.ui.state.UIState - -/** - * UI state for [com.mobiledevpro.chatlist.view.ChatListScreen] - * - * Created on Feb 04, 2023. - * - */ -sealed interface ChatListUIState : UIState { - - data object Empty : ChatListUIState - - data object Loading : ChatListUIState - - class Success(val profileList : List) : ChatListUIState - - class Fail(val throwable: Throwable) : ChatListUIState -} \ No newline at end of file diff --git a/feature/chat_list/src/main/kotlin/com/mobiledevpro/chatlist/view/ChatListViewModel.kt b/feature/chat_list/src/main/kotlin/com/mobiledevpro/chatlist/view/ChatListViewModel.kt deleted file mode 100644 index a73c80c..0000000 --- a/feature/chat_list/src/main/kotlin/com/mobiledevpro/chatlist/view/ChatListViewModel.kt +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2022 | Dmitri Chernysh | https://mobile-dev.pro - * - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package com.mobiledevpro.chatlist.view - -import android.util.Log -import androidx.lifecycle.viewModelScope -import com.mobiledevpro.chatlist.domain.usecase.GetChatListUseCase -import com.mobiledevpro.ui.vm.BaseViewModel -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach - - -class ChatListViewModel( - private val getChatListUseCase: GetChatListUseCase -) : BaseViewModel() { - - override fun initUIState(): ChatListUIState = ChatListUIState.Empty - - init { - Log.d("UI", "ChatListViewModel init") - observeChatList() - } - - private fun observeChatList() { - getChatListUseCase.execute() - .onEach { result -> - result.onSuccess { list -> - ChatListUIState.Success(list) - .also { _uiState.value = it } - }.onFailure { - //TODO: show an error - } - }.launchIn(viewModelScope) - } -} \ No newline at end of file diff --git a/feature/chat_list/src/main/kotlin/com/mobiledevpro/chatlist/view/component/ChatCard.kt b/feature/chat_list/src/main/kotlin/com/mobiledevpro/chatlist/view/component/ChatCard.kt deleted file mode 100644 index 0c50af9..0000000 --- a/feature/chat_list/src/main/kotlin/com/mobiledevpro/chatlist/view/component/ChatCard.kt +++ /dev/null @@ -1,142 +0,0 @@ -/* - * Copyright 2022 | Dmitri Chernysh | https://mobile-dev.pro - * - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package com.mobiledevpro.chatlist.view.component - -import android.net.Uri -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.Badge -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import com.mobiledevpro.domain.model.Chat -import com.mobiledevpro.domain.model.PeopleProfile -import com.mobiledevpro.domain.model.fakePeopleProfileList -import com.mobiledevpro.domain.model.fakeUser -import com.mobiledevpro.domain.model.name -import com.mobiledevpro.ui.component.CardItem -import com.mobiledevpro.ui.component.ProfilePicture -import com.mobiledevpro.ui.component.ProfilePictureSize -import com.mobiledevpro.ui.theme.AppTheme - -/** - * Fo chat list screen - * - * Created on May 06, 2023. - * - */ -@OptIn(ExperimentalMaterial3Api::class) -@Composable -internal fun ChatCard( - modifier: Modifier = Modifier, - chat: Chat, - onClick: () -> Unit -) { - - CardItem( - modifier = modifier - .clickable { onClick.invoke() } - ) { - - Box { - Row( - modifier = Modifier - .padding(horizontal = 16.dp, vertical = 24.dp) - .fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically - ) { - - ChatPicture( - profileList = chat.peopleList - ) - - Text( - text = chat.name(), - style = MaterialTheme.typography.bodyMedium, - maxLines = 2, - overflow = TextOverflow.Ellipsis, - modifier = Modifier - .padding(horizontal = 8.dp) - ) - - } - - if (chat.unreadMsgCount > 0) - Badge( - containerColor = MaterialTheme.colorScheme.tertiary, - modifier = Modifier - .align(Alignment.BottomEnd) - .padding(8.dp) - ) { - Text( - text = if (chat.unreadMsgCount > 99) "99+" else chat.unreadMsgCount.toString(), - style = MaterialTheme.typography.labelSmall - ) - } - } - - } -} - -@Composable -fun ChatPicture(profileList: List, modifier: Modifier = Modifier) { - - Row(verticalAlignment = Alignment.CenterVertically) { - Box(modifier = modifier) { - profileList.take(VISIBLE_PROFILES_COUNT).forEachIndexed { index, profile -> - - ProfilePicture( - profile.photo ?: Uri.EMPTY, - profile.status, - size = ProfilePictureSize.SMALL, - modifier = Modifier.padding(start = 16.dp * index) - ) - - } - } - if (profileList.size > VISIBLE_PROFILES_COUNT) - Text( - text = "+${profileList.size - 3}", - style = MaterialTheme.typography.bodySmall, - modifier = Modifier.padding(4.dp) - ) - } -} - -@Composable -@Preview -fun ChatCardPreview() { - val peopleList = fakePeopleProfileList.take(2).sortedByDescending { !it.status } - val chat = Chat(fakeUser, peopleList) - AppTheme { - ChatCard( - chat = chat, - onClick = {}) - } -} - -const val VISIBLE_PROFILES_COUNT = 3 \ No newline at end of file diff --git a/feature/home/src/main/kotlin/com/mobiledevpro/home/view/HomeScreen.kt b/feature/home/src/main/kotlin/com/mobiledevpro/home/view/HomeScreen.kt index 022b94f..bfb7cb4 100644 --- a/feature/home/src/main/kotlin/com/mobiledevpro/home/view/HomeScreen.kt +++ b/feature/home/src/main/kotlin/com/mobiledevpro/home/view/HomeScreen.kt @@ -27,12 +27,10 @@ import com.mobiledevpro.ui.component.ScreenBackground @Composable fun HomeScreen( - nestedNavGraph: @Composable () -> Unit, - bottomBar: @Composable () -> Unit + nestedNavGraph: @Composable () -> Unit ) { Scaffold( - bottomBar = bottomBar, contentWindowInsets = WindowInsets(left = 0, top = 0, right = 0, bottom = 0) ) { paddingValues -> ScreenBackground( diff --git a/core/domain/.gitignore b/feature/image_viewer/.gitignore similarity index 100% rename from core/domain/.gitignore rename to feature/image_viewer/.gitignore diff --git a/feature/chat_list/build.gradle.kts b/feature/image_viewer/build.gradle.kts similarity index 100% rename from feature/chat_list/build.gradle.kts rename to feature/image_viewer/build.gradle.kts diff --git a/core/domain/consumer-rules.pro b/feature/image_viewer/consumer-rules.pro similarity index 100% rename from core/domain/consumer-rules.pro rename to feature/image_viewer/consumer-rules.pro diff --git a/core/domain/proguard-rules.pro b/feature/image_viewer/proguard-rules.pro similarity index 100% rename from core/domain/proguard-rules.pro rename to feature/image_viewer/proguard-rules.pro diff --git a/feature/image_viewer/src/main/AndroidManifest.xml b/feature/image_viewer/src/main/AndroidManifest.xml new file mode 100644 index 0000000..a5918e6 --- /dev/null +++ b/feature/image_viewer/src/main/AndroidManifest.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/feature/image_viewer/src/main/java/com/mobiledevpro/image/viewer/view/ImageViewerScreen.kt b/feature/image_viewer/src/main/java/com/mobiledevpro/image/viewer/view/ImageViewerScreen.kt new file mode 100644 index 0000000..a53658c --- /dev/null +++ b/feature/image_viewer/src/main/java/com/mobiledevpro/image/viewer/view/ImageViewerScreen.kt @@ -0,0 +1,198 @@ +/* + * Copyright 2023 | Dmitri Chernysh | https://mobile-dev.pro + * + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +package com.mobiledevpro.image.viewer.view + +import android.icu.text.CaseMap.Upper +import android.net.Uri +import android.util.Log +import androidx.compose.foundation.background +import androidx.compose.foundation.gestures.detectTapGestures +import androidx.compose.foundation.gestures.rememberTransformableState +import androidx.compose.foundation.gestures.transformable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.BoxScope +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.systemBars +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableFloatStateOf +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.scale +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.input.pointer.pointerInput +import androidx.compose.ui.layout.ContentScale +import androidx.compose.ui.platform.LocalContext +import coil.compose.AsyncImage +import coil.request.CachePolicy +import coil.request.ImageRequest +import coil.size.Size + +/** + * Demo screen for zoomable image + * + * Created on Feb 18, 2024. + * + */ + +@Composable +fun ImageViewerScreen() { + + Scaffold( + contentWindowInsets = WindowInsets.systemBars + ) { paddings -> + + Box( + modifier = Modifier + .fillMaxSize() + .padding(paddings) + ) { + + Column( + modifier = Modifier.fillMaxSize(), + verticalArrangement = Arrangement.SpaceEvenly + ) { + + Box( + modifier = Modifier + .fillMaxSize() + .weight(1f) + ) { + UpperImage() + } + Box( + modifier = Modifier + .fillMaxWidth() + .weight(1f) + ) { + LowerImage() + } + } + + } + + } + + +} + +@Composable +fun BoxScope.UpperImage() { + var imageScale by remember { mutableFloatStateOf(1f) } + var imageOffset by remember { mutableStateOf(Offset.Zero) } + + val transformState = rememberTransformableState { zoomChange, panChange, rotationChange -> + imageScale = (imageScale * zoomChange).coerceIn(1f, 5f) + imageOffset += panChange + } + + val model = ImageRequest.Builder(LocalContext.current) + .data("https://c4.wallpaperflare.com/wallpaper/83/500/871/waterfall-high-resolution-desktop-wallpaper-preview.jpg") + .size(Size.ORIGINAL) // Set the target size to load the image at. + .crossfade(true) + .memoryCachePolicy(CachePolicy.DISABLED) + .diskCachePolicy(CachePolicy.DISABLED) + .build() + + AsyncImage( + model = model, + contentDescription = "", + contentScale = ContentScale.FillHeight, + onState = { state -> + Log.d("UI", "AsyncImage.state: $state") + }, + modifier = Modifier + .align(Alignment.Center) + .fillMaxWidth() + .scale(imageScale) + .graphicsLayer { + Log.d("UI", "ImageViewerScreen: graphicsLayer: scale $scaleX $scaleY") + scaleX = imageScale + scaleY = imageScale + translationX = imageOffset.x + translationY = imageOffset.y + } + .transformable(transformState) + .pointerInput(Unit) { + detectTapGestures(onDoubleTap = { + Log.d("UI", "ImageViewerScreen: on double tap") + imageScale = 1f + imageOffset = Offset.Zero + }) + } + ) +} + +@Composable +fun BoxScope.LowerImage() { + var imageScale by remember { mutableFloatStateOf(1f) } + var imageOffset by remember { mutableStateOf(Offset.Zero) } + + val transformState = rememberTransformableState { zoomChange, panChange, rotationChange -> + imageScale = (imageScale * zoomChange).coerceIn(1f, 5f) + imageOffset += panChange + } + + val model = ImageRequest.Builder(LocalContext.current) + .data("https://c4.wallpaperflare.com/wallpaper/352/684/15/underwater-high-resolution-desktop-backgrounds-wallpaper-preview.jpg") + .size(Size.ORIGINAL) // Set the target size to load the image at. + .crossfade(true) + .memoryCachePolicy(CachePolicy.DISABLED) + .diskCachePolicy(CachePolicy.DISABLED) + .build() + + AsyncImage( + model = model, + contentDescription = "", + contentScale = ContentScale.FillHeight, + onState = { state -> + Log.d("UI", "AsyncImage.state: $state") + }, + modifier = Modifier + .align(Alignment.Center) + .fillMaxWidth() + .scale(imageScale) + .graphicsLayer { + Log.d("UI", "ImageViewerScreen: graphicsLayer: scale $scaleX $scaleY") + scaleX = imageScale + scaleY = imageScale + translationX = imageOffset.x + translationY = imageOffset.y + } + .transformable(transformState) + .pointerInput(Unit) { + detectTapGestures(onDoubleTap = { + Log.d("UI", "ImageViewerScreen: on double tap") + imageScale = 1f + imageOffset = Offset.Zero + }) + } + ) +} diff --git a/feature/onboarding/.gitignore b/feature/onboarding/.gitignore deleted file mode 100644 index 42afabf..0000000 --- a/feature/onboarding/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build \ No newline at end of file diff --git a/feature/onboarding/build.gradle.kts b/feature/onboarding/build.gradle.kts deleted file mode 100644 index 988f39f..0000000 --- a/feature/onboarding/build.gradle.kts +++ /dev/null @@ -1,3 +0,0 @@ -plugins { - id("feature-module") -} diff --git a/feature/onboarding/consumer-rules.pro b/feature/onboarding/consumer-rules.pro deleted file mode 100644 index e69de29..0000000 diff --git a/feature/onboarding/proguard-rules.pro b/feature/onboarding/proguard-rules.pro deleted file mode 100644 index 481bb43..0000000 --- a/feature/onboarding/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/onboarding/src/main/AndroidManifest.xml b/feature/onboarding/src/main/AndroidManifest.xml deleted file mode 100644 index 568741e..0000000 --- a/feature/onboarding/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/feature/onboarding/src/main/kotlin/com/mobiledevpro/onboarding/view/OnBoardingFirstScreen.kt b/feature/onboarding/src/main/kotlin/com/mobiledevpro/onboarding/view/OnBoardingFirstScreen.kt deleted file mode 100644 index a9d3313..0000000 --- a/feature/onboarding/src/main/kotlin/com/mobiledevpro/onboarding/view/OnBoardingFirstScreen.kt +++ /dev/null @@ -1,65 +0,0 @@ -/* - * Copyright 2022 | Dmitri Chernysh | https://mobile-dev.pro - * - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package com.mobiledevpro.onboarding.view - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import com.mobiledevpro.ui.theme.AppTheme - - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun OnBoardingFirstScreen() { - Box( - modifier = Modifier - .fillMaxSize() - .background(color = Color(0x80FFCC99)) - ) { - Text( - text = "1st OnBoarding", - textAlign = TextAlign.Center, - modifier = Modifier - .padding(16.dp) - .align(Alignment.Center) - .fillMaxWidth(), - style = MaterialTheme.typography.bodyLarge - - ) - } -} - -@Preview -@Composable -fun OnBoardingFirstPreview() { - AppTheme { - OnBoardingFirstScreen() - } -} \ No newline at end of file diff --git a/feature/onboarding/src/main/kotlin/com/mobiledevpro/onboarding/view/OnBoardingScreen.kt b/feature/onboarding/src/main/kotlin/com/mobiledevpro/onboarding/view/OnBoardingScreen.kt deleted file mode 100644 index eb9b5c2..0000000 --- a/feature/onboarding/src/main/kotlin/com/mobiledevpro/onboarding/view/OnBoardingScreen.kt +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2022 | Dmitri Chernysh | https://mobile-dev.pro - * - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package com.mobiledevpro.onboarding.view - -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.defaultMinSize -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.Button -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Scaffold -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -import com.mobiledevpro.ui.component.ScreenBackground - - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun OnBoardingScreen( - nestedNavGraph: @Composable () -> Unit, - onNext: () -> Unit -) { - Scaffold { paddingValues -> - ScreenBackground( - modifier = Modifier - .fillMaxSize() - .padding(paddingValues) - ) { - Box( - modifier = Modifier - .fillMaxSize() - .padding(16.dp) - ) { - - nestedNavGraph.invoke() - - Button( - onClick = onNext, - modifier = Modifier - .align(Alignment.BottomCenter) - .defaultMinSize(minWidth = 144.dp, minHeight = 48.dp) - - ) { - Text(text = "Next") - } - - } - } - } - - -} \ No newline at end of file diff --git a/feature/onboarding/src/main/kotlin/com/mobiledevpro/onboarding/view/OnBoardingSecondScreen.kt b/feature/onboarding/src/main/kotlin/com/mobiledevpro/onboarding/view/OnBoardingSecondScreen.kt deleted file mode 100644 index ae8ac3f..0000000 --- a/feature/onboarding/src/main/kotlin/com/mobiledevpro/onboarding/view/OnBoardingSecondScreen.kt +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2022 | Dmitri Chernysh | https://mobile-dev.pro - * - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package com.mobiledevpro.onboarding.view - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import com.mobiledevpro.ui.theme.AppTheme - - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun OnBoardingSecondScreen() { - Box( - modifier = Modifier - .fillMaxSize() - .padding(16.dp) - .background(color = Color(0x80FFFF99)) - ) { - Text( - text = "2nd OnBoarding", - textAlign = TextAlign.Center, - modifier = Modifier - .padding(16.dp) - .align(Alignment.Center) - .fillMaxWidth(), - style = MaterialTheme.typography.bodyLarge - - ) - } -} - -@Preview -@Composable -fun OnBoardingSecondPreview() { - AppTheme { - OnBoardingSecondScreen() - } -} \ No newline at end of file diff --git a/feature/onboarding/src/main/kotlin/com/mobiledevpro/onboarding/view/OnBoardingThirdScreen.kt b/feature/onboarding/src/main/kotlin/com/mobiledevpro/onboarding/view/OnBoardingThirdScreen.kt deleted file mode 100644 index 25bca61..0000000 --- a/feature/onboarding/src/main/kotlin/com/mobiledevpro/onboarding/view/OnBoardingThirdScreen.kt +++ /dev/null @@ -1,66 +0,0 @@ -/* - * Copyright 2022 | Dmitri Chernysh | https://mobile-dev.pro - * - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package com.mobiledevpro.onboarding.view - -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import com.mobiledevpro.ui.theme.AppTheme - - -@OptIn(ExperimentalMaterial3Api::class) -@Composable -fun OnBoardingThirdScreen() { - Box( - modifier = Modifier - .fillMaxSize() - .padding(16.dp) - .background(color = Color(0x80FF9999)) - ) { - Text( - text = "3rd OnBoarding", - textAlign = TextAlign.Center, - modifier = Modifier - .padding(16.dp) - .align(Alignment.Center) - .fillMaxWidth(), - style = MaterialTheme.typography.bodyLarge - - ) - } -} - -@Preview -@Composable -fun OnBoardingThirdPreview() { - AppTheme { - OnBoardingThirdScreen() - } -} \ No newline at end of file diff --git a/feature/people/.gitignore b/feature/people/.gitignore deleted file mode 100644 index 42afabf..0000000 --- a/feature/people/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build \ No newline at end of file diff --git a/feature/people/build.gradle.kts b/feature/people/build.gradle.kts deleted file mode 100644 index 276865a..0000000 --- a/feature/people/build.gradle.kts +++ /dev/null @@ -1,8 +0,0 @@ -plugins { - id("feature-module") -} - -dependencies { - api(projects.feature.peopleList.dependencyProject) - api(projects.feature.peopleProfile.dependencyProject) -} \ No newline at end of file diff --git a/feature/people/consumer-rules.pro b/feature/people/consumer-rules.pro deleted file mode 100644 index e69de29..0000000 diff --git a/feature/people/proguard-rules.pro b/feature/people/proguard-rules.pro deleted file mode 100644 index 481bb43..0000000 --- a/feature/people/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/people/src/main/AndroidManifest.xml b/feature/people/src/main/AndroidManifest.xml deleted file mode 100644 index 1d26c87..0000000 --- a/feature/people/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/feature/people/src/main/kotlin/com/mobiledevpro/people/view/PeopleScreen.kt b/feature/people/src/main/kotlin/com/mobiledevpro/people/view/PeopleScreen.kt deleted file mode 100644 index 109c7cd..0000000 --- a/feature/people/src/main/kotlin/com/mobiledevpro/people/view/PeopleScreen.kt +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Copyright 2022 | Dmitri Chernysh | https://mobile-dev.pro - * - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package com.mobiledevpro.people.view - -import android.util.Log -import androidx.compose.runtime.Composable - -/** - * Host for People list and People profile - * - * Created on Feb 04, 2023. - * - */ -@Composable -fun PeopleScreen( - nestedNavGraph: @Composable () -> Unit -) { - - Log.d("navigation", "PeopleScreen: ") - - nestedNavGraph.invoke() -} \ No newline at end of file diff --git a/feature/people_list/.gitignore b/feature/people_list/.gitignore deleted file mode 100644 index 42afabf..0000000 --- a/feature/people_list/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build \ No newline at end of file diff --git a/feature/people_list/build.gradle.kts b/feature/people_list/build.gradle.kts deleted file mode 100644 index 8bc006b..0000000 --- a/feature/people_list/build.gradle.kts +++ /dev/null @@ -1,4 +0,0 @@ -plugins { - id("feature-module") - id("testing-module") -} diff --git a/feature/people_list/consumer-rules.pro b/feature/people_list/consumer-rules.pro deleted file mode 100644 index e69de29..0000000 diff --git a/feature/people_list/proguard-rules.pro b/feature/people_list/proguard-rules.pro deleted file mode 100644 index 481bb43..0000000 --- a/feature/people_list/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/people_list/src/main/AndroidManifest.xml b/feature/people_list/src/main/AndroidManifest.xml deleted file mode 100644 index 568741e..0000000 --- a/feature/people_list/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/feature/people_list/src/main/kotlin/com/mobiledevpro/peoplelist/di/Module.kt b/feature/people_list/src/main/kotlin/com/mobiledevpro/peoplelist/di/Module.kt deleted file mode 100644 index ce47a2c..0000000 --- a/feature/people_list/src/main/kotlin/com/mobiledevpro/peoplelist/di/Module.kt +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2023 | Dmitri Chernysh | https://mobile-dev.pro - * - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package com.mobiledevpro.peoplelist.di - -import com.mobiledevpro.peoplelist.domain.usecase.GetPeopleListUseCase -import com.mobiledevpro.peoplelist.view.PeopleListViewModel -import org.koin.androidx.viewmodel.dsl.viewModelOf -import org.koin.core.module.dsl.scopedOf -import org.koin.dsl.module - -/** - * User Profile screen module - * - * Created on Jul 22, 2023. - * - */ - -val featurePeopleListModule = module { - - scope { - viewModelOf(::PeopleListViewModel) - scopedOf(::GetPeopleListUseCase) - } -} \ No newline at end of file diff --git a/feature/people_list/src/main/kotlin/com/mobiledevpro/peoplelist/domain/usecase/GetPeopleListUseCase.kt b/feature/people_list/src/main/kotlin/com/mobiledevpro/peoplelist/domain/usecase/GetPeopleListUseCase.kt deleted file mode 100644 index ef46f05..0000000 --- a/feature/people_list/src/main/kotlin/com/mobiledevpro/peoplelist/domain/usecase/GetPeopleListUseCase.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2023 | Dmitri Chernysh | https://mobile-dev.pro - * - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package com.mobiledevpro.peoplelist.domain.usecase - -import com.mobiledevpro.coroutines.BaseCoroutinesFLowUseCase -import com.mobiledevpro.coroutines.None -import com.mobiledevpro.domain.model.PeopleProfile -import com.mobiledevpro.domain.model.fakePeopleProfileList -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flowOf - - -class GetPeopleListUseCase( - -) : BaseCoroutinesFLowUseCase, None>(Dispatchers.IO) { - - override fun buildUseCaseFlow(params: None?): Flow> = - flowOf(fakePeopleProfileList) -} \ No newline at end of file diff --git a/feature/people_list/src/main/kotlin/com/mobiledevpro/peoplelist/view/PeopleListScreen.kt b/feature/people_list/src/main/kotlin/com/mobiledevpro/peoplelist/view/PeopleListScreen.kt deleted file mode 100644 index 5744d4e..0000000 --- a/feature/people_list/src/main/kotlin/com/mobiledevpro/peoplelist/view/PeopleListScreen.kt +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright 2022 | Dmitri Chernysh | https://mobile-dev.pro - * - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package com.mobiledevpro.peoplelist.view - -import android.widget.Toast -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.lazy.itemsIndexed -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.mobiledevpro.domain.model.PeopleProfile -import com.mobiledevpro.peoplelist.view.component.ProfileCard -import com.mobiledevpro.ui.component.ScreenBackground -import com.mobiledevpro.ui.ext.dp -import com.mobiledevpro.ui.ext.statusBarHeight -import com.mobiledevpro.ui.theme.AppTheme -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow - - -@Composable -fun PeopleListScreen( - stateFlow: StateFlow, - onNavigateToProfile: (profileId: Int) -> Unit -) { - - val uiState by stateFlow.collectAsStateWithLifecycle() - val context = LocalContext.current - - ScreenBackground( - modifier = Modifier - .fillMaxSize() - ) { - - when (uiState) { - is PeopleProfileUIState.Loading -> Loading() - is PeopleProfileUIState.Empty -> NoPeopleFound() - is PeopleProfileUIState.Success -> - PeopleList( - list = (uiState as PeopleProfileUIState.Success).profileList, - onProfileClick = onNavigateToProfile - ) - - is PeopleProfileUIState.Fail -> { - NoPeopleFound() - LaunchedEffect(Unit) { - Toast.makeText( - context, - (uiState as PeopleProfileUIState.Fail).throwable.localizedMessage, - Toast.LENGTH_LONG - ).show() - } - } - - } - - } -} - -@Composable -private fun NoPeopleFound() { - Box( - modifier = Modifier - .fillMaxSize() - .padding(16.dp) - ) { - Text( - text = "No people found", - modifier = Modifier - .padding(16.dp) - .align(Alignment.Center), - style = MaterialTheme.typography.bodyLarge - ) - } -} - -@Composable -private fun Loading() { - Box( - modifier = Modifier - .fillMaxSize() - .padding(16.dp) - ) { - Text( - text = "Loading...", modifier = Modifier - .padding(16.dp) - .align(Alignment.Center), - style = MaterialTheme.typography.bodyLarge - ) - } -} - -@Composable -private fun PeopleList(list: List, onProfileClick: (profileId: Int) -> Unit) { - - //Cause content is drawn edge-to-edge, let's set the top-padding for the first element in the list - val statusBarHeight: Dp = WindowInsets.statusBarHeight().dp() - - LazyColumn { - itemsIndexed( - items = list, - key = { _, item -> item.listKey() } - ) { index, profile -> - ProfileCard( - modifier = Modifier.then( - if (index == 0) - Modifier.padding(top = statusBarHeight) - else - Modifier - ), - - - item = profile, - onClick = { onProfileClick(profile.id) }) - } - } -} - - -@Preview -@Composable -fun PeopleListPreview() { - AppTheme { - PeopleListScreen( - stateFlow = MutableStateFlow(PeopleProfileUIState.Empty), - onNavigateToProfile = { } - ) - } -} \ No newline at end of file diff --git a/feature/people_list/src/main/kotlin/com/mobiledevpro/peoplelist/view/PeopleListViewModel.kt b/feature/people_list/src/main/kotlin/com/mobiledevpro/peoplelist/view/PeopleListViewModel.kt deleted file mode 100644 index 448c33a..0000000 --- a/feature/people_list/src/main/kotlin/com/mobiledevpro/peoplelist/view/PeopleListViewModel.kt +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright 2022 | Dmitri Chernysh | https://mobile-dev.pro - * - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package com.mobiledevpro.peoplelist.view - -import androidx.lifecycle.viewModelScope -import com.mobiledevpro.peoplelist.domain.usecase.GetPeopleListUseCase -import com.mobiledevpro.ui.vm.BaseViewModel -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach - - -class PeopleListViewModel( - private val getPeopleListUseCase: GetPeopleListUseCase -) : BaseViewModel() { - - override fun initUIState(): PeopleProfileUIState = PeopleProfileUIState.Loading - - init { - observePeopleList() - } - - private fun observePeopleList() { - - getPeopleListUseCase.execute() - .onEach { result -> - result.onSuccess { list -> - PeopleProfileUIState.Success(list) - .also { _uiState.value = it } - }.onFailure { - //TODO: show error - } - }.launchIn(viewModelScope) - } - -} \ No newline at end of file diff --git a/feature/people_list/src/main/kotlin/com/mobiledevpro/peoplelist/view/PeopleProfileUIState.kt b/feature/people_list/src/main/kotlin/com/mobiledevpro/peoplelist/view/PeopleProfileUIState.kt deleted file mode 100644 index 3011ebb..0000000 --- a/feature/people_list/src/main/kotlin/com/mobiledevpro/peoplelist/view/PeopleProfileUIState.kt +++ /dev/null @@ -1,38 +0,0 @@ -/* - * Copyright 2022 | Dmitri Chernysh | https://mobile-dev.pro - * - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package com.mobiledevpro.peoplelist.view - -import com.mobiledevpro.domain.model.PeopleProfile -import com.mobiledevpro.ui.state.UIState - -/** - * UI state for [com.mobiledevpro.peoplelist.view.PeopleListScreen] - * - * Created on Feb 04, 2023. - * - */ -sealed interface PeopleProfileUIState : UIState { - - object Empty : PeopleProfileUIState - - object Loading : PeopleProfileUIState - - class Success(val profileList : List) : PeopleProfileUIState - - class Fail(val throwable: Throwable) : PeopleProfileUIState -} \ No newline at end of file diff --git a/feature/people_list/src/main/kotlin/com/mobiledevpro/peoplelist/view/component/ProfileCard.kt b/feature/people_list/src/main/kotlin/com/mobiledevpro/peoplelist/view/component/ProfileCard.kt deleted file mode 100644 index a7cdfb3..0000000 --- a/feature/people_list/src/main/kotlin/com/mobiledevpro/peoplelist/view/component/ProfileCard.kt +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2022 | Dmitri Chernysh | https://mobile-dev.pro - * - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package com.mobiledevpro.peoplelist.view.component - -import android.net.Uri -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -import com.mobiledevpro.domain.model.PeopleProfile -import com.mobiledevpro.ui.component.CardItem -import com.mobiledevpro.ui.component.ProfileContent -import com.mobiledevpro.ui.component.ProfilePicture -import com.mobiledevpro.ui.component.ProfilePictureSize - -/** - * For People list - * - * Created on Feb 05, 2023. - * - */ -@Composable -internal fun ProfileCard(modifier: Modifier = Modifier, item: PeopleProfile, onClick: () -> Unit) { - CardItem( - modifier = modifier - .clickable { onClick.invoke() } - ) { - - Row( - modifier = Modifier.fillMaxWidth(), - verticalAlignment = Alignment.CenterVertically, - horizontalArrangement = Arrangement.Start - ) { - ProfilePicture( - item.photo ?: Uri.EMPTY, - item.status, - size = ProfilePictureSize.MEDIUM, - modifier = Modifier.padding(16.dp) - ) - ProfileContent( - userName = item.name, - subName = null, - isOnline = item.status, - alignment = Alignment.Start, - modifier = Modifier.padding(8.dp) - ) - } - } -} \ No newline at end of file diff --git a/feature/people_list/src/test/kotlin/com/mobiledevpro/peoplelist/PeopleListViewModelTest.kt b/feature/people_list/src/test/kotlin/com/mobiledevpro/peoplelist/PeopleListViewModelTest.kt deleted file mode 100644 index 4a48f2d..0000000 --- a/feature/people_list/src/test/kotlin/com/mobiledevpro/peoplelist/PeopleListViewModelTest.kt +++ /dev/null @@ -1,69 +0,0 @@ -/* - * Copyright 2023 | Dmitri Chernysh | https://mobile-dev.pro - * - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package com.mobiledevpro.peoplelist - -import app.cash.turbine.test -import com.mobiledevpro.peoplelist.domain.usecase.GetPeopleListUseCase -import com.mobiledevpro.peoplelist.view.PeopleListViewModel -import com.mobiledevpro.peoplelist.view.PeopleProfileUIState -import com.mobiledevpro.ui.state.UIState -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.StandardTestDispatcher -import kotlinx.coroutines.test.resetMain -import kotlinx.coroutines.test.runTest -import kotlinx.coroutines.test.setMain -import org.junit.After -import org.junit.Assert.assertTrue -import org.junit.Before -import org.junit.Test - - -class PeopleListViewModelTest { - - private lateinit var vm: PeopleListViewModel - - @OptIn(ExperimentalCoroutinesApi::class) - @Before - fun setUp() { - Dispatchers.setMain(StandardTestDispatcher()) - - val useCase = GetPeopleListUseCase() - vm = PeopleListViewModel(getPeopleListUseCase = useCase) - assertTrue( - "Initial state is incorrect: ${vm.uiState.value}", - (vm.uiState.value as UIState) == PeopleProfileUIState.Loading - ) - } - - @Test - fun stateTest() = runTest { - - vm.uiState.test { - // assertEquals("State is not Loading", PeopleProfileUIState.Loading, awaitItem()) - - assertTrue( - "People list is empty", - (awaitItem() as PeopleProfileUIState.Success).profileList.isNotEmpty() - ) - } - } - - @After - fun finish() = Dispatchers.resetMain() -} \ No newline at end of file diff --git a/feature/people_profile/.gitignore b/feature/people_profile/.gitignore deleted file mode 100644 index 42afabf..0000000 --- a/feature/people_profile/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build \ No newline at end of file diff --git a/feature/people_profile/build.gradle.kts b/feature/people_profile/build.gradle.kts deleted file mode 100644 index d5d59fc..0000000 --- a/feature/people_profile/build.gradle.kts +++ /dev/null @@ -1,3 +0,0 @@ -plugins { - id("feature-module") -} \ No newline at end of file diff --git a/feature/people_profile/consumer-rules.pro b/feature/people_profile/consumer-rules.pro deleted file mode 100644 index e69de29..0000000 diff --git a/feature/people_profile/proguard-rules.pro b/feature/people_profile/proguard-rules.pro deleted file mode 100644 index 481bb43..0000000 --- a/feature/people_profile/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/people_profile/src/main/AndroidManifest.xml b/feature/people_profile/src/main/AndroidManifest.xml deleted file mode 100644 index 568741e..0000000 --- a/feature/people_profile/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/feature/people_profile/src/main/kotlin/com/mobiledevpro/people/profile/view/PeopleProfileScreen.kt b/feature/people_profile/src/main/kotlin/com/mobiledevpro/people/profile/view/PeopleProfileScreen.kt deleted file mode 100644 index e977860..0000000 --- a/feature/people_profile/src/main/kotlin/com/mobiledevpro/people/profile/view/PeopleProfileScreen.kt +++ /dev/null @@ -1,223 +0,0 @@ -/* - * Copyright 2022 | Dmitri Chernysh | https://mobile-dev.pro - * - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package com.mobiledevpro.people.profile.view - -import android.net.Uri -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.defaultMinSize -import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.offset -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.statusBarsPadding -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.material3.surfaceColorAtElevation -import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableIntStateOf -import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.layout.boundsInParent -import androidx.compose.ui.layout.onGloballyPositioned -import androidx.compose.ui.res.painterResource -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.IntOffset -import androidx.compose.ui.unit.dp -import com.mobiledevpro.domain.model.PeopleProfile -import com.mobiledevpro.domain.model.fakePeopleProfileList -import com.mobiledevpro.people.profile.R -import com.mobiledevpro.ui.component.ProfileContent -import com.mobiledevpro.ui.component.ProfilePicture -import com.mobiledevpro.ui.component.ProfilePictureSize -import com.mobiledevpro.ui.component.ScreenBackground -import com.mobiledevpro.ui.theme.AppTheme -import com.mobiledevpro.ui.R as RApp - -/** - * Profile screen for selected person from People list - * - * Created on Feb 03, 2023. - * - */ - -@Composable -fun PeopleProfileScreen( - profile: PeopleProfile, - onBackPressed: () -> Unit, - onOpenChatWith: (profile: PeopleProfile) -> Unit -) { - - val backgroundBoxTopOffset = remember { mutableIntStateOf(0) } - - ScreenBackground( - modifier = Modifier - .fillMaxSize() - .statusBarsPadding() - ) { - - //Background with rounded top-corners - Box( - modifier = Modifier - .offset { IntOffset(0, backgroundBoxTopOffset.value) } - .clip(RoundedCornerShape(topStart = 48.dp, topEnd = 48.dp)) - .background(color = MaterialTheme.colorScheme.surfaceColorAtElevation(4.dp)) - ) - - Column( - modifier = Modifier.fillMaxSize(), - verticalArrangement = Arrangement.Top - ) { - IconButton( - onClick = { onBackPressed() }, - modifier = Modifier.padding(16.dp) - ) { - Icon( - painter = painterResource(id = RApp.drawable.ic_arrow_back_white_24dp), - contentDescription = "" - ) - } - - ProfilePicture( - photoUri = profile.photo ?: Uri.EMPTY, - onlineStatus = profile.status, - size = ProfilePictureSize.LARGE, - modifier = Modifier - .padding(paddingValues = PaddingValues(16.dp, 16.dp, 16.dp, 16.dp)) - .align(Alignment.CenterHorizontally) - .onGloballyPositioned { - val rect = it.boundsInParent() - backgroundBoxTopOffset.value = - rect.topCenter.y.toInt() + (rect.bottomCenter.y - rect.topCenter.y).toInt() / 2 - } - ) - - ProfileContent( - userName = profile.name, - isOnline = profile.status, - alignment = Alignment.CenterHorizontally, - modifier = Modifier - .padding(8.dp) - .align(Alignment.CenterHorizontally) - ) - - ProfileSocialIcons(modifier = Modifier.align(Alignment.CenterHorizontally)) - - Row( - modifier = Modifier - .fillMaxHeight() - .align(Alignment.CenterHorizontally), - verticalAlignment = Alignment.Bottom - ) { - Button( - onClick = { - profile.also(onOpenChatWith) - }, - modifier = Modifier - .padding(bottom = 48.dp, top = 16.dp, start = 16.dp, end = 16.dp) - .defaultMinSize(minHeight = 48.dp, minWidth = 144.dp), - colors = ButtonDefaults.buttonColors(containerColor = MaterialTheme.colorScheme.tertiary), - ) { - Text(text = "Say Hi \uD83D\uDC4B") - } - } - } - - } -} - -@Composable -fun ProfileSocialIcons(modifier: Modifier) { - Row( - modifier = modifier - ) { - IconButton( - onClick = { - - } - ) { - Icon( - painter = painterResource(id = R.drawable.ic_instagram_white_48dp), - contentDescription = "", - modifier = Modifier.padding(4.dp), - ) - } - - IconButton( - onClick = { - - } - ) { - Icon( - painter = painterResource(id = R.drawable.ic_linkedin_white_48dp), - contentDescription = "", - modifier = Modifier.padding(4.dp) - ) - } - - IconButton( - onClick = { - - } - ) { - Icon( - painter = painterResource(id = R.drawable.ic_youtube_white_48dp), - contentDescription = "", - modifier = Modifier.padding(4.dp) - ) - } - - IconButton( - onClick = { - - } - ) { - Icon( - painter = painterResource(id = R.drawable.ic_twitter_white_48dp), - contentDescription = "", - modifier = Modifier.padding(4.dp) - ) - } - } -} - - -@Preview -@Composable -fun PeopleProfilePreview() { - AppTheme(darkTheme = true) { - fakePeopleProfileList.find { it.id == 2 }?.let { - PeopleProfileScreen( - it, - onBackPressed = {}, - onOpenChatWith = {} - ) - } - } -} \ No newline at end of file diff --git a/feature/people_profile/src/main/kotlin/com/mobiledevpro/people/profile/view/PeopleProfileViewModel.kt b/feature/people_profile/src/main/kotlin/com/mobiledevpro/people/profile/view/PeopleProfileViewModel.kt deleted file mode 100644 index dbbcf06..0000000 --- a/feature/people_profile/src/main/kotlin/com/mobiledevpro/people/profile/view/PeopleProfileViewModel.kt +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright 2022 | Dmitri Chernysh | https://mobile-dev.pro - * - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package com.mobiledevpro.people.profile.view - -import android.util.Log -import androidx.lifecycle.SavedStateHandle -import androidx.lifecycle.ViewModel -import com.mobiledevpro.domain.model.PeopleProfile -import com.mobiledevpro.domain.model.fakePeopleProfileList -import com.mobiledevpro.people.profile.view.args.PeopleProfileArgs - -/** - * Profile screen for selected person from People list - * - * Created on Feb 04, 2023. - * - */ -class PeopleProfileViewModel( - savedStateHandle: SavedStateHandle -) : ViewModel() { - - private val profileId : Int = PeopleProfileArgs(savedStateHandle).peopleProfileId - - - - init { - Log.d("navigation", "PeopleProfileViewModel: args = $profileId") - } - - fun getProfile() : PeopleProfile? = fakePeopleProfileList.find { it.id == profileId } -} \ No newline at end of file diff --git a/feature/people_profile/src/main/kotlin/com/mobiledevpro/people/profile/view/args/PeopleProfileArgs.kt b/feature/people_profile/src/main/kotlin/com/mobiledevpro/people/profile/view/args/PeopleProfileArgs.kt deleted file mode 100644 index 74068b8..0000000 --- a/feature/people_profile/src/main/kotlin/com/mobiledevpro/people/profile/view/args/PeopleProfileArgs.kt +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright 2022 | Dmitri Chernysh | https://mobile-dev.pro - * - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package com.mobiledevpro.people.profile.view.args - -import androidx.lifecycle.SavedStateHandle - -/** - * - * Created on Feb 04, 2023. - * - */ -class PeopleProfileArgs(val peopleProfileId: Int) { - constructor(savedStateHandle: SavedStateHandle) : - this(checkNotNull(savedStateHandle[PEOPLE_PROFILE_ID_ARG]) as Int) - - companion object { - const val PEOPLE_PROFILE_ID_ARG = "peopleProfileId" - } -} \ No newline at end of file diff --git a/feature/people_profile/src/main/res/drawable/ic_instagram_white_48dp.xml b/feature/people_profile/src/main/res/drawable/ic_instagram_white_48dp.xml deleted file mode 100644 index 9c6addd..0000000 --- a/feature/people_profile/src/main/res/drawable/ic_instagram_white_48dp.xml +++ /dev/null @@ -1,15 +0,0 @@ - - - - - diff --git a/feature/people_profile/src/main/res/drawable/ic_linkedin_white_48dp.xml b/feature/people_profile/src/main/res/drawable/ic_linkedin_white_48dp.xml deleted file mode 100644 index c3c09cf..0000000 --- a/feature/people_profile/src/main/res/drawable/ic_linkedin_white_48dp.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - diff --git a/feature/people_profile/src/main/res/drawable/ic_twitter_white_48dp.xml b/feature/people_profile/src/main/res/drawable/ic_twitter_white_48dp.xml deleted file mode 100644 index 80fd439..0000000 --- a/feature/people_profile/src/main/res/drawable/ic_twitter_white_48dp.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - diff --git a/feature/people_profile/src/main/res/drawable/ic_youtube_white_48dp.xml b/feature/people_profile/src/main/res/drawable/ic_youtube_white_48dp.xml deleted file mode 100644 index b4e7a9c..0000000 --- a/feature/people_profile/src/main/res/drawable/ic_youtube_white_48dp.xml +++ /dev/null @@ -1,12 +0,0 @@ - - - - diff --git a/feature/subscription/.gitignore b/feature/subscription/.gitignore deleted file mode 100644 index 42afabf..0000000 --- a/feature/subscription/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build \ No newline at end of file diff --git a/feature/subscription/build.gradle.kts b/feature/subscription/build.gradle.kts deleted file mode 100644 index d5d59fc..0000000 --- a/feature/subscription/build.gradle.kts +++ /dev/null @@ -1,3 +0,0 @@ -plugins { - id("feature-module") -} \ No newline at end of file diff --git a/feature/subscription/consumer-rules.pro b/feature/subscription/consumer-rules.pro deleted file mode 100644 index e69de29..0000000 diff --git a/feature/subscription/proguard-rules.pro b/feature/subscription/proguard-rules.pro deleted file mode 100644 index 481bb43..0000000 --- a/feature/subscription/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/subscription/src/main/AndroidManifest.xml b/feature/subscription/src/main/AndroidManifest.xml deleted file mode 100644 index 568741e..0000000 --- a/feature/subscription/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/feature/subscription/src/main/kotlin/com/mobiledevpro/subscription/SubscriptionScreen.kt b/feature/subscription/src/main/kotlin/com/mobiledevpro/subscription/SubscriptionScreen.kt deleted file mode 100644 index 1838323..0000000 --- a/feature/subscription/src/main/kotlin/com/mobiledevpro/subscription/SubscriptionScreen.kt +++ /dev/null @@ -1,224 +0,0 @@ -/* - * Copyright 2022 | Dmitri Chernysh | https://mobile-dev.pro - * - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package com.mobiledevpro.subscription - -import android.widget.Toast -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.WindowInsets -import androidx.compose.foundation.layout.defaultMinSize -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.systemBars -import androidx.compose.material3.Button -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Scaffold -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.text.style.TextAlign -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.dp -import com.mobiledevpro.ui.component.ScreenBackground -import com.mobiledevpro.ui.theme.AppTheme - -@Composable -fun SubscriptionScreen(onNavigateBack: () -> Unit) { - val context = LocalContext.current - - val showToast: (message: String) -> Unit = { message -> - Toast.makeText(context, message, Toast.LENGTH_SHORT).show() - } - - Scaffold( - contentWindowInsets = WindowInsets.systemBars - ) { paddingValues -> - ScreenBackground( - modifier = Modifier - .fillMaxSize() - .padding(paddingValues) - ) { - - Box( - modifier = Modifier - .fillMaxSize() - .padding(16.dp) - ) { - - Text( - text = "Be like a Pro", - style = MaterialTheme.typography.titleLarge, - modifier = Modifier - .align(Alignment.TopCenter) - .padding(16.dp) - ) - - Column( - modifier = Modifier - .fillMaxWidth() - .align(Alignment.Center), - verticalArrangement = Arrangement.Center - ) { - Text( - text = "Subscribe on", - textAlign = TextAlign.Center, - modifier = Modifier - .padding(16.dp) - .align(Alignment.CenterHorizontally) - .fillMaxWidth(), - style = MaterialTheme.typography.bodyLarge - - ) - - SubsButton( - text = "1 Month - 0.9\$", - modifier = Modifier.align(Alignment.CenterHorizontally) - ) { - showToast("Subscribing to 1 month...") - onNavigateBack() - } - - SubsButton( - text = "6 Months - 3.99\$", - modifier = Modifier.align(Alignment.CenterHorizontally) - ) { - showToast("Subscribing to 6 months...") - onNavigateBack() - } - - SubsButton( - text = "1 Year - 6.99\$", - modifier = Modifier.align(Alignment.CenterHorizontally) - ) { - showToast("Subscribing to 1 year...") - onNavigateBack() - } - - } - } - - /* - - Column( - modifier = Modifier - .padding(16.dp) - .verticalScroll(rememberScrollState()) - .height(IntrinsicSize.Max) - ) { - - Header() - - //If you remove Items, Footer will be centered at the screen as you could see on attached screenshots - Item(text = "Item 1") - Item(text = "Item 2") - Item(text = "Item 3") - - Box( - modifier = Modifier - .fillMaxSize() - //.background(color = Color.Blue) - ) { - Footer(modifier = Modifier.align(Alignment.Center)) - } - } - - */ - - } - } - - -} -/* -@Composable -fun Header() { - Box( - modifier = Modifier - .fillMaxWidth() - .height(100.dp) - .background(color = Color.Yellow) - ) -} - -@Composable -fun Item(text: String) { - Box( - modifier = Modifier - .padding(vertical = 8.dp) - .fillMaxWidth() - .height(50.dp) - .background(color = Color.White) - - ) { - Text( - text = text, - modifier = Modifier.align(Alignment.Center), - style = MaterialTheme.typography.bodyLarge - ) - } -} - -@Composable -fun Footer(modifier: Modifier) { - Box( - modifier = modifier - .padding(vertical = 8.dp) - .fillMaxWidth() - .height(100.dp) - .background(color = Color.Gray) - - ) { - Text( - text = "Optional block", - modifier = Modifier.align(Alignment.Center), - style = MaterialTheme.typography.bodyLarge - ) - } -} - - */ - -@Composable -fun SubsButton( - text: String, - modifier: Modifier, - onClick: () -> Unit -) { - Button( - onClick = onClick, - modifier = modifier - .padding(8.dp) - .defaultMinSize(minWidth = 192.dp, minHeight = 48.dp) - - - ) { - Text(text = text) - } -} - -@Preview -@Composable -fun SubscriptionScreenPreview() { - AppTheme { - SubscriptionScreen {} - } -} \ No newline at end of file diff --git a/feature/user_profile/.gitignore b/feature/user_profile/.gitignore deleted file mode 100644 index 42afabf..0000000 --- a/feature/user_profile/.gitignore +++ /dev/null @@ -1 +0,0 @@ -/build \ No newline at end of file diff --git a/feature/user_profile/build.gradle.kts b/feature/user_profile/build.gradle.kts deleted file mode 100644 index d5d59fc..0000000 --- a/feature/user_profile/build.gradle.kts +++ /dev/null @@ -1,3 +0,0 @@ -plugins { - id("feature-module") -} \ No newline at end of file diff --git a/feature/user_profile/consumer-rules.pro b/feature/user_profile/consumer-rules.pro deleted file mode 100644 index e69de29..0000000 diff --git a/feature/user_profile/proguard-rules.pro b/feature/user_profile/proguard-rules.pro deleted file mode 100644 index 481bb43..0000000 --- a/feature/user_profile/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/user_profile/src/main/AndroidManifest.xml b/feature/user_profile/src/main/AndroidManifest.xml deleted file mode 100644 index 568741e..0000000 --- a/feature/user_profile/src/main/AndroidManifest.xml +++ /dev/null @@ -1,2 +0,0 @@ - - \ No newline at end of file diff --git a/feature/user_profile/src/main/kotlin/com/mobiledevpro/user/profile/di/Module.kt b/feature/user_profile/src/main/kotlin/com/mobiledevpro/user/profile/di/Module.kt deleted file mode 100644 index bd3b149..0000000 --- a/feature/user_profile/src/main/kotlin/com/mobiledevpro/user/profile/di/Module.kt +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Copyright 2023 | Dmitri Chernysh | https://mobile-dev.pro - * - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package com.mobiledevpro.user.profile.di - -import com.mobiledevpro.user.profile.domain.usecase.GetUserProfileUseCase -import com.mobiledevpro.user.profile.view.vm.ProfileViewModel -import org.koin.androidx.viewmodel.dsl.viewModelOf -import org.koin.core.module.dsl.scopedOf -import org.koin.dsl.module - -/** - * User Profile screen module - * - * Created on Jul 22, 2023. - * - */ - -val featureUserProfileModule = module { - - scope { - viewModelOf(::ProfileViewModel) - scopedOf(::GetUserProfileUseCase) - } -} \ No newline at end of file diff --git a/feature/user_profile/src/main/kotlin/com/mobiledevpro/user/profile/domain/usecase/GetUserProfileUseCase.kt b/feature/user_profile/src/main/kotlin/com/mobiledevpro/user/profile/domain/usecase/GetUserProfileUseCase.kt deleted file mode 100644 index d920a4d..0000000 --- a/feature/user_profile/src/main/kotlin/com/mobiledevpro/user/profile/domain/usecase/GetUserProfileUseCase.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright 2023 | Dmitri Chernysh | https://mobile-dev.pro - * - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package com.mobiledevpro.user.profile.domain.usecase - -import com.mobiledevpro.coroutines.BaseCoroutinesFLowUseCase -import com.mobiledevpro.coroutines.None -import com.mobiledevpro.domain.model.UserProfile -import com.mobiledevpro.domain.model.fakeUser -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flowOf - - -class GetUserProfileUseCase( - -) : BaseCoroutinesFLowUseCase(Dispatchers.IO) { - - override fun buildUseCaseFlow(params: None?): Flow = - flowOf(fakeUser) -} \ No newline at end of file diff --git a/feature/user_profile/src/main/kotlin/com/mobiledevpro/user/profile/view/ProfileScreen.kt b/feature/user_profile/src/main/kotlin/com/mobiledevpro/user/profile/view/ProfileScreen.kt deleted file mode 100644 index d0ab13d..0000000 --- a/feature/user_profile/src/main/kotlin/com/mobiledevpro/user/profile/view/ProfileScreen.kt +++ /dev/null @@ -1,217 +0,0 @@ -/* - * Copyright 2022 | Dmitri Chernysh | https://mobile-dev.pro - * - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package com.mobiledevpro.user.profile.view - -import android.net.Uri -import android.util.Log -import android.widget.Toast -import androidx.compose.foundation.background -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.PaddingValues -import androidx.compose.foundation.layout.fillMaxHeight -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.offset -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.statusBarsPadding -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.rounded.ExitToApp -import androidx.compose.material3.Divider -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.surfaceColorAtElevation -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip -import androidx.compose.ui.layout.boundsInParent -import androidx.compose.ui.layout.onGloballyPositioned -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.tooling.preview.Preview -import androidx.compose.ui.unit.IntOffset -import androidx.compose.ui.unit.dp -import androidx.lifecycle.compose.collectAsStateWithLifecycle -import com.airbnb.lottie.compose.LottieAnimation -import com.airbnb.lottie.compose.LottieCompositionSpec -import com.airbnb.lottie.compose.rememberLottieComposition -import com.mobiledevpro.domain.model.UserProfile -import com.mobiledevpro.domain.model.fakeUser -import com.mobiledevpro.ui.component.LabeledDarkModeSwitch -import com.mobiledevpro.ui.component.ProfileContent -import com.mobiledevpro.ui.component.ProfilePicture -import com.mobiledevpro.ui.component.ProfilePictureSize -import com.mobiledevpro.ui.component.ScreenBackground -import com.mobiledevpro.ui.component.SettingsButton -import com.mobiledevpro.ui.theme.AppTheme -import com.mobiledevpro.ui.theme._darkModeState -import com.mobiledevpro.ui.theme.darkModeState -import com.mobiledevpro.user.profile.view.state.UserProfileUIState -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.update -import com.mobiledevpro.ui.R as RUi - -@Composable -fun ProfileScreen( - state: StateFlow, - onNavigateToSubscription: () -> Unit -) { - Log.d("navigation", "ProfileScreen:") - - val uiState by state.collectAsStateWithLifecycle() - val context = LocalContext.current - - val backgroundBoxTopOffset = remember { mutableStateOf(0) } - val darkModeOn = remember { mutableStateOf(darkModeState.value) } - - val user : UserProfile = when(uiState) { - is UserProfileUIState.Success -> (uiState as UserProfileUIState.Success).userProfile - is UserProfileUIState.Fail -> { - LaunchedEffect(Unit) { - Toast.makeText( - context, - (uiState as UserProfileUIState.Fail).throwable.localizedMessage, - Toast.LENGTH_LONG - ).show() - } - - UserProfile("", "", false, Uri.EMPTY) - } - - else -> UserProfile("", "", false, Uri.EMPTY) - } - - ScreenBackground( - modifier = Modifier - .fillMaxSize() - .statusBarsPadding() - ) { - - - //Background with rounded top-corners - Box( - modifier = Modifier - .offset { IntOffset(0, backgroundBoxTopOffset.value) } - .clip(RoundedCornerShape(topStart = 48.dp, topEnd = 48.dp)) - .background(color = MaterialTheme.colorScheme.surfaceColorAtElevation(4.dp)) - ) - - Box(modifier = Modifier.fillMaxSize()) { - - BeLikeAProButton(modifier = Modifier - .align(Alignment.TopEnd) - .padding(8.dp) - .size(56.dp) - .clickable { onNavigateToSubscription() }) - - Column( - modifier = Modifier - .fillMaxSize() - .padding(start = 16.dp, end = 16.dp, bottom = 32.dp), - verticalArrangement = Arrangement.Top - ) { - - ProfilePicture( - photoUri = user.photo, - onlineStatus = true, - size = ProfilePictureSize.LARGE, - modifier = Modifier - .padding(paddingValues = PaddingValues(16.dp, 16.dp, 16.dp, 16.dp)) - .align(Alignment.CenterHorizontally) - .onGloballyPositioned { - val rect = it.boundsInParent() - backgroundBoxTopOffset.value = - rect.topCenter.y.toInt() + (rect.bottomCenter.y - rect.topCenter.y).toInt() / 2 - } - ) - - ProfileContent( - userName = user.name, - subName = user.nickname, - isOnline = user.status, - alignment = Alignment.CenterHorizontally, - modifier = Modifier - .padding(8.dp) - .align(Alignment.CenterHorizontally) - ) - - - - - - Column( - modifier = Modifier - .fillMaxHeight() - .align(Alignment.CenterHorizontally), - verticalArrangement = Arrangement.Bottom - ) { - - LabeledDarkModeSwitch( - label = "Dark mode", - checked = darkModeOn.value, - onCheckedChanged = { isDark -> - Log.d("main", "ProfileScreen: dark $isDark") - darkModeOn.value = isDark - _darkModeState.update { - isDark - } - }) - - Divider() - - SettingsButton( - label = "Log Out", - icon = Icons.Rounded.ExitToApp, - onClick = { - - } - ) - } - } - - - } - } -} - -@Composable -fun BeLikeAProButton(modifier: Modifier) { - //Here is how to use Lottie animation with Compose https://github.com/airbnb/lottie/blob/master/android-compose.md - - val composition by rememberLottieComposition(LottieCompositionSpec.RawRes(RUi.raw.ic_crowd)) - - LottieAnimation(composition = composition, modifier = modifier) -} - -@Preview -@Composable -fun ProfileScreenPreview() { - AppTheme(darkTheme = true) { - ProfileScreen( - state = MutableStateFlow(UserProfileUIState.Success(fakeUser)), - onNavigateToSubscription = {} - ) - } -} \ No newline at end of file diff --git a/feature/user_profile/src/main/kotlin/com/mobiledevpro/user/profile/view/vm/ProfileViewModel.kt b/feature/user_profile/src/main/kotlin/com/mobiledevpro/user/profile/view/vm/ProfileViewModel.kt deleted file mode 100644 index 09fbf1b..0000000 --- a/feature/user_profile/src/main/kotlin/com/mobiledevpro/user/profile/view/vm/ProfileViewModel.kt +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright 2022 | Dmitri Chernysh | https://mobile-dev.pro - * - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ -package com.mobiledevpro.user.profile.view.vm - -import android.util.Log -import androidx.lifecycle.viewModelScope -import com.mobiledevpro.ui.vm.BaseViewModel -import com.mobiledevpro.user.profile.domain.usecase.GetUserProfileUseCase -import com.mobiledevpro.user.profile.view.state.UserProfileUIState -import kotlinx.coroutines.flow.launchIn -import kotlinx.coroutines.flow.onEach - -class ProfileViewModel( - private val getUserProfileUseCase: GetUserProfileUseCase -) : BaseViewModel() { - - override fun initUIState(): UserProfileUIState = UserProfileUIState.Empty - - init { - Log.d("UI", "ProfileViewModel init") - observeUserProfile() - } - - private fun observeUserProfile() { - getUserProfileUseCase.execute() - .onEach { result -> - result.onSuccess { profile -> - UserProfileUIState.Success(profile) - .also { _uiState.value = it } - }.onFailure { - //TODO: show the error - } - - }.launchIn(viewModelScope) - } -} \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index a00fe69..31be507 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -19,16 +19,9 @@ rootProject.name = "App-Template-Compose" include(":app") include(":core:ui") include(":core:navigation") -include(":core:domain") include(":core:coroutines") include(":feature:home") -include(":feature:onboarding") -include(":feature:subscription") -include(":feature:chat_list") -include(":feature:people_list") -include(":feature:user_profile") -include(":feature:people_profile") -include(":feature:people") include(":core:di") include(":core:util") +include(":feature:image_viewer")