Skip to content

Commit 2987177

Browse files
Phase 2 done for app
0 parents  commit 2987177

File tree

97 files changed

+3451
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

97 files changed

+3451
-0
lines changed

.gitignore

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
*.iml
2+
.gradle
3+
/local.properties
4+
/.idea/caches
5+
/.idea/libraries
6+
/.idea/modules.xml
7+
/.idea/workspace.xml
8+
/.idea/navEditor.xml
9+
/.idea/assetWizardSettings.xml
10+
.DS_Store
11+
/build
12+
/captures
13+
.externalNativeBuild
14+
.cxx
15+
local.properties
16+
**/build/

.idea/.gitignore

+3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/.name

+1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/compiler.xml

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/gradle.xml

+21
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/kotlinc.xml

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/misc.xml

+37
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/vcs.xml

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# GithubCompose (Kotlin + Jetpack Compose + Navigation + MVI)
2+
3+
![Language](https://img.shields.io/github/languages/top/cortinico/kotlin-android-template?color=blue&logo=kotlin)
4+
5+
GithubCompose is a sample project that presents a modern approach to Android app development.
6+
7+
The project tries to combine popular Android tools and to demonstrate best development practices by utilizing up to date tech-stack like Compose, Kotlin Flow and Koin.
8+
9+
The sample app presents a modern Android application Architecture that is scalable and maintainable through a MVI.
10+
11+
## Description
12+
13+
* UI
14+
* [Compose](https://developer.android.com/jetpack/compose) declarative UI framework
15+
* [Material design](https://material.io/design)
16+
17+
* Tech/Tools
18+
* [Kotlin](https://kotlinlang.org/) 100% coverage
19+
* [Coroutines](https://kotlinlang.org/docs/reference/coroutines-overview.html) and [Flow](https://developer.android.com/kotlin/flow) for async operations
20+
* [Koin](https://insert-koin.io/) for dependency injection
21+
* [Jetpack](https://developer.android.com/jetpack)
22+
* [Compose](https://developer.android.com/jetpack/compose)
23+
* [Navigation](https://developer.android.com/topic/libraries/architecture/navigation/) for navigation between composables
24+
* [ViewModel](https://developer.android.com/topic/libraries/architecture/viewmodel) that stores, exposes and manages UI state
25+
* [Retrofit](https://square.github.io/retrofit/) for networking
26+
* [Coil](https://github.com/coil-kt/coil) for image loading
27+
28+
* Modern Architecture
29+
* Single activity architecture (with [Navigation component](https://developer.android.com/guide/navigation/navigation-getting-started)) that defines navigation graphs
30+
* MVI
31+
* [Android Architecture components](https://developer.android.com/topic/libraries/architecture) ([ViewModel](https://developer.android.com/topic/libraries/architecture/viewmodel), [Navigation](https://developer.android.com/jetpack/androidx/releases/navigation))
32+
* [Android KTX](https://developer.android.com/kotlin/ktx) - Jetpack Kotlin extensions
33+
34+
## Light and Dark mode
35+
<img src="misc/demo_light_mode.gif" width="360" height="820"> <img src="misc/demo_dark_mode.gif" width="360" height="820">
36+
37+
## Architecture
38+
The project is layered traditionally with a View, Presentation, Model separation and presents a MVI inspired from [Yusuf Ceylan's architecture](https://proandroiddev.com/mvi-architecture-with-kotlin-flows-and-channels-d36820b2028d) but adapted to Compose.
39+
40+
Architecture layers:
41+
* View - Composable screens that consume state, apply effects and delegate events.
42+
* ViewModel - [AAC ViewModel](https://developer.android.com/topic/libraries/architecture/viewmodel) that manages and reduces the state of the corresponding screen. Additionally, it intercepts UI events and produces side-effects. The ViewModel lifecycle scope is tied to the corresponding screen composable.
43+
* Model - Repository classes that retrieve data. In a clean architecture context, one should use use-cases that tap into repositories.
44+
45+
![](https://i.imgur.com/UXwFbmv.png)
46+
47+
There are a three core components described:
48+
* **State** - data class that holds the state content of the corresponding screen e.g. list of `User`, loading status etc. The state is exposed as a Compose runtime `MutableState` object from that perfectly matches the use-case of receiving continuous updates with initial value.
49+
50+
* **Event** - plain object that is sent through callbacks from the UI to the presentation layer. Events should reflect UI events caused by the user. Event updates are exposed as a `MutableSharedFlow` type which is similar to `StateFlow` and that behaves as in the absence of a subscriber, any posted event will be immediately dropped.
51+
52+
* **Effect** - plain object that signals one-time side-effect actions that should impact the UI e.g. triggering a navigation action, showing a Toast, SnackBar etc. Effects are exposed as `ChannelFlow` which behave as in each event is delivered to a single subscriber. An attempt to post an event without subscribers will suspend as soon as the channel buffer becomes full, waiting for a subscriber to appear.
53+
54+
Every screen/flow defines its own contract class that states all corresponding core components described above: state content, events and effects.

app/.gitignore

+1
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
/build

app/build.gradle.kts

+112
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
plugins {
2+
id(Dependencies.Plugins.application)
3+
id(Dependencies.Plugins.kotlinAndroid)
4+
}
5+
6+
android {
7+
compileSdk = AppConfig.compileSdk
8+
9+
defaultConfig {
10+
applicationId = AppConfig.applicationId
11+
minSdk = AppConfig.minSdk
12+
targetSdk = AppConfig.targetSdk
13+
versionCode = AppConfig.versionCode
14+
versionName = AppConfig.versionName
15+
16+
testInstrumentationRunner = AppConfig.androidTestInstrumentation
17+
vectorDrawables {
18+
useSupportLibrary = true
19+
}
20+
}
21+
22+
buildTypes {
23+
release {
24+
isMinifyEnabled = false
25+
proguardFiles(
26+
getDefaultProguardFile("proguard-android-optimize.txt"),
27+
"proguard-rules.pro"
28+
)
29+
}
30+
}
31+
32+
compileOptions {
33+
sourceCompatibility = AppConfig.javaCompatibility
34+
targetCompatibility = AppConfig.javaCompatibility
35+
}
36+
37+
kotlinOptions {
38+
jvmTarget = AppConfig.javaJvmTarget
39+
}
40+
41+
buildFeatures {
42+
compose = true
43+
}
44+
45+
composeOptions {
46+
kotlinCompilerExtensionVersion = Dependencies.Android.Version.compose
47+
}
48+
49+
packagingOptions {
50+
resources {
51+
excludes += "/META-INF/{AL2.0,LGPL2.1}"
52+
}
53+
}
54+
55+
testOptions {
56+
unitTests.apply {
57+
isReturnDefaultValues = true
58+
}
59+
}
60+
61+
sourceSets {
62+
getByName("main").java.srcDirs("src/main/resources", "src/main/test/resources")
63+
getByName("test").java.srcDirs("src/test/resources")
64+
getByName("androidTest").java.srcDirs("src/androidTest/resources")
65+
}
66+
}
67+
68+
dependencies {
69+
// Android
70+
implementation(Dependencies.Android.coreKts)
71+
implementation(Dependencies.Android.appCompat)
72+
implementation(Dependencies.Android.activityCompose)
73+
implementation(Dependencies.Android.navigationCompose)
74+
75+
implementation(Dependencies.Android.composeUi)
76+
implementation(Dependencies.Android.composeMaterial)
77+
implementation(Dependencies.Android.composeMaterialIconsExtended)
78+
implementation(Dependencies.Android.composeUiTooling)
79+
implementation(Dependencies.Android.composeUiToolingPreview)
80+
81+
implementation(Dependencies.Android.lifecycleRuntimeKtx)
82+
implementation(Dependencies.Android.lifecycleViewModelKtx)
83+
implementation(Dependencies.Android.lifecycleExtensions)
84+
implementation(Dependencies.Android.lifecycleViewModelCompose)
85+
86+
// Third Party
87+
implementation(Dependencies.ThirdParty.coilCompose)
88+
implementation(Dependencies.ThirdParty.androidMaterial)
89+
90+
implementation(Dependencies.ThirdParty.kotlinxCoroutinesCore)
91+
implementation(Dependencies.ThirdParty.kotlinxCoroutinesAndroid)
92+
93+
implementation(Dependencies.ThirdParty.retrofit)
94+
implementation(Dependencies.ThirdParty.retrofitConverterGson)
95+
96+
implementation(Dependencies.ThirdParty.koinAndroid)
97+
implementation(Dependencies.ThirdParty.koinAndroidxCompose)
98+
99+
// Test
100+
testImplementation(Dependencies.Test.junit)
101+
testImplementation(Dependencies.Test.roboeletric)
102+
testImplementation(Dependencies.Test.mockk)
103+
testImplementation(Dependencies.Test.kotlinxCoroutinesTest)
104+
testImplementation(Dependencies.Test.koinTest)
105+
testImplementation(Dependencies.Test.okHttp3MockWebServer)
106+
107+
// Android Test
108+
androidTestImplementation(Dependencies.AndroidTest.junit)
109+
androidTestImplementation(Dependencies.AndroidTest.espressoCore)
110+
androidTestImplementation(Dependencies.AndroidTest.composeJunit)
111+
debugImplementation(Dependencies.AndroidTest.composeUiTooling)
112+
}

app/proguard-rules.pro

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Add project specific ProGuard rules here.
2+
# You can control the set of applied configuration files using the
3+
# proguardFiles setting in build.gradle.kts.
4+
#
5+
# For more details, see
6+
# http://developer.android.com/guide/developing/tools/proguard.html
7+
8+
# If your project uses WebView with JS, uncomment the following
9+
# and specify the fully qualified class name to the JavaScript interface
10+
# class:
11+
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12+
# public *;
13+
#}
14+
15+
# Uncomment this to preserve the line number information for
16+
# debugging stack traces.
17+
#-keepattributes SourceFile,LineNumberTable
18+
19+
# If you keep the line number information, uncomment this to
20+
# hide the original source file name.
21+
#-renamesourcefileattribute SourceFile
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.abhishek.pathak.kotlin.android.githubcompose
2+
3+
import androidx.test.platform.app.InstrumentationRegistry
4+
import androidx.test.ext.junit.runners.AndroidJUnit4
5+
6+
import org.junit.Test
7+
import org.junit.runner.RunWith
8+
9+
import org.junit.Assert.*
10+
11+
/**
12+
* Instrumented test, which will execute on an Android device.
13+
*
14+
* See [testing documentation](http://d.android.com/tools/testing).
15+
*/
16+
@RunWith(AndroidJUnit4::class)
17+
class ExampleInstrumentedTest {
18+
@Test
19+
fun useAppContext() {
20+
// Context of the app under test.
21+
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
22+
assertEquals("com.br.wcabral.kotlin.android.githubcompose", appContext.packageName)
23+
}
24+
}

app/src/main/AndroidManifest.xml

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
3+
package="com.abhishek.pathak.kotlin.android.githubcompose">
4+
5+
<uses-permission android:name="android.permission.INTERNET" />
6+
7+
<application
8+
android:name="com.abhishek.pathak.kotlin.android.githubcompose.GithubApplication"
9+
android:allowBackup="true"
10+
android:icon="@mipmap/ic_launcher"
11+
android:label="@string/app_name"
12+
android:roundIcon="@mipmap/ic_launcher_round"
13+
android:supportsRtl="true"
14+
android:theme="@style/Theme.GithubCompose">
15+
<activity
16+
android:name="com.abhishek.pathak.kotlin.android.githubcompose.ui.feature.main.MainActivity"
17+
android:exported="true"
18+
android:theme="@style/Theme.GithubCompose.NoActionBar">
19+
<intent-filter>
20+
<action android:name="android.intent.action.MAIN" />
21+
22+
<category android:name="android.intent.category.LAUNCHER" />
23+
</intent-filter>
24+
</activity>
25+
</application>
26+
27+
</manifest>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
package com.abhishek.pathak.kotlin.android.githubcompose
2+
3+
import android.app.Application
4+
import com.abhishek.pathak.kotlin.android.githubcompose.di.appModules
5+
import org.koin.android.ext.koin.androidContext
6+
import org.koin.android.ext.koin.androidLogger
7+
import org.koin.core.context.startKoin
8+
import org.koin.core.logger.Level
9+
10+
class GithubApplication: Application() {
11+
12+
override fun onCreate() {
13+
super.onCreate()
14+
startKoin {
15+
androidLogger(Level.ERROR)
16+
androidContext(this@GithubApplication)
17+
modules(appModules)
18+
}
19+
}
20+
21+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
package com.abhishek.pathak.kotlin.android.githubcompose.common
2+
3+
import android.content.Intent
4+
import android.net.Uri
5+
6+
fun buildUrlIntent(url: String) = Intent(Intent.ACTION_VIEW, Uri.parse(url))

0 commit comments

Comments
 (0)