├── .gitignore ├── .idea ├── caches │ └── build_file_checksums.ser ├── codeStyles │ └── Project.xml ├── gradle.xml ├── misc.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── cogitator │ │ └── androidcleanarchitecture │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── kotlin │ │ └── com │ │ │ └── cogitator │ │ │ └── androidcleanarchitecture │ │ │ ├── CleanAndroidApp.kt │ │ │ ├── base │ │ │ ├── BaseActivity.kt │ │ │ ├── BaseAdapter.kt │ │ │ ├── BaseFragment.kt │ │ │ ├── BaseRepoCallback.kt │ │ │ ├── BaseRepository.kt │ │ │ ├── BaseViewHolder.kt │ │ │ ├── BaseViewModel.kt │ │ │ └── ViewModelFactory.kt │ │ │ ├── di │ │ │ ├── component │ │ │ │ └── ApplicationComponent.kt │ │ │ └── module │ │ │ │ ├── AppModule.kt │ │ │ │ └── NetModule.kt │ │ │ ├── model │ │ │ ├── Responses.kt │ │ │ └── network │ │ │ │ ├── ApiHelper.kt │ │ │ │ └── ServiceApi.kt │ │ │ ├── utils │ │ │ ├── Contants.kt │ │ │ └── ViewExt.kt │ │ │ ├── view │ │ │ └── MainActivity.kt │ │ │ └── viewModel │ │ │ └── MainViewModel.kt │ └── res │ │ ├── drawable │ │ └── launch_screen.xml │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── cogitator │ └── androidcleanarchitecture │ └── ExampleUnitTest.kt ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/libraries 5 | /.idea/modules.xml 6 | /.idea/workspace.xml 7 | .DS_Store 8 | /build 9 | /captures 10 | .externalNativeBuild 11 | -------------------------------------------------------------------------------- /.idea/caches/build_file_checksums.ser: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArchitectAK/AndroidCleanArchitecture/2084180923d1284817013bd5ad3b8c9e854d93c3/.idea/caches/build_file_checksums.ser -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 15 | 16 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AndroidCleanArchitecture 2 | Android Project with clean android architecture contain Dagger, Retrofit, Retrofit, Android architecture components, LiveData with MVVM architecture 3 | 4 | 5 | ### Contact - Let's connect to learn together 6 | - [Twitter](https://twitter.com/KumarAnkitRKE) 7 | - [Github](https://github.com/AnkitDroidGit) 8 | - [LinkedIn](https://www.linkedin.com/in/kumarankitkumar/) 9 | - [Facebook](https://www.facebook.com/ankitoid) 10 | - [Slack](https://ankitdroid.slack.com) 11 | - [Stackoverflow](https://stackoverflow.com/users/3282461/android) 12 | - [Android App](https://play.google.com/store/apps/details?id=com.freeankit.ankitprofile) 13 | 14 | 15 | ### License 16 | 17 | Copyright 2018 Ankit Kumar 18 | 19 | Licensed under the Apache License, Version 2.0 (the "License"); 20 | you may not use this file except in compliance with the License. 21 | You may obtain a copy of the License at 22 | 23 | http://www.apache.org/licenses/LICENSE-2.0 24 | 25 | Unless required by applicable law or agreed to in writing, software 26 | distributed under the License is distributed on an "AS IS" BASIS, 27 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 28 | See the License for the specific language governing permissions and 29 | limitations under the License. 30 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | apply plugin: 'kotlin-android' 4 | 5 | apply plugin: 'kotlin-android-extensions' 6 | 7 | android { 8 | dataBinding { 9 | enabled = true 10 | } 11 | 12 | compileSdkVersion rootProject.ext.compileSdkVersion 13 | buildToolsVersion '28.0.3' 14 | 15 | defaultConfig { 16 | applicationId "com.cogitator.androidcleanarchitecture" 17 | minSdkVersion rootProject.ext.minSdkVersion 18 | targetSdkVersion rootProject.ext.targetSdkVersion 19 | versionCode 1 20 | versionName "1.0" 21 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 22 | } 23 | 24 | sourceSets { 25 | main.java.srcDirs += 'src/main/kotlin' 26 | test.java.srcDirs += ['src/test/kotlin', 'src/debug/kotlin'] 27 | } 28 | lintOptions { 29 | lintConfig file('../gradle/android-lint-config.xml') 30 | } 31 | buildTypes { 32 | release { 33 | minifyEnabled false 34 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 35 | } 36 | } 37 | 38 | compileOptions { 39 | sourceCompatibility JavaVersion.VERSION_1_8 40 | targetCompatibility JavaVersion.VERSION_1_8 41 | } 42 | packagingOptions { 43 | exclude 'META-INF/rxjava.properties' 44 | } 45 | compileOptions.incremental = false 46 | } 47 | 48 | dependencies { 49 | implementation fileTree(dir: 'libs', include: ['*.jar']) 50 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" 51 | 52 | implementation "com.android.support:appcompat-v7:$rootProject.supportLibraryVersion" 53 | implementation "com.android.support:recyclerview-v7:$rootProject.supportLibraryVersion" 54 | 55 | // dagger2 56 | implementation "com.google.dagger:dagger:$rootProject.dagger2Version" 57 | implementation "com.google.dagger:dagger-android:$rootProject.dagger2Version" 58 | implementation "com.google.dagger:dagger-android-support:$rootProject.dagger2Version" 59 | kapt "com.google.dagger:dagger-compiler:$rootProject.dagger2Version" 60 | kapt "com.google.dagger:dagger-android-processor:$rootProject.dagger2Version" 61 | 62 | //retrofit 63 | implementation "com.squareup.retrofit2:retrofit:$rootProject.retrofitVersion" 64 | implementation "com.squareup.retrofit2:converter-gson:$rootProject.retrofitVersion" 65 | implementation "com.squareup.retrofit2:adapter-rxjava2:$rootProject.retrofitVersion" 66 | implementation "com.squareup.okhttp3:logging-interceptor:$rootProject.okhttpVersion" 67 | 68 | 69 | testImplementation 'junit:junit:4.12' 70 | androidTestImplementation 'com.android.support.test:runner:1.0.2' 71 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 72 | } 73 | 74 | apply plugin: 'kotlin-kapt' 75 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/cogitator/androidcleanarchitecture/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package com.cogitator.androidcleanarchitecture 2 | 3 | import android.support.test.InstrumentationRegistry 4 | import android.support.test.runner.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.getTargetContext() 22 | assertEquals("com.cogitator.androidcleanarchitecture", appContext.packageName) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/kotlin/com/cogitator/androidcleanarchitecture/CleanAndroidApp.kt: -------------------------------------------------------------------------------- 1 | package com.cogitator.androidcleanarchitecture 2 | 3 | import android.app.Application 4 | import com.cogitator.androidcleanarchitecture.di.component.ApplicationComponent 5 | import com.cogitator.androidcleanarchitecture.di.module.AppModule 6 | import com.cogitator.androidcleanarchitecture.di.module.NetModule 7 | 8 | /** 9 | * @author Ankit Kumar on 01/10/2018 10 | */ 11 | class CleanAndroidApp : Application() { 12 | companion object { 13 | lateinit var component: ApplicationComponent 14 | } 15 | 16 | override fun onCreate() { 17 | super.onCreate() 18 | // Fabric.with(this, Crashlytics()) 19 | component = DaggerApplicationComponent.builder() 20 | .appModule(AppModule(this)) 21 | .netModule(NetModule(this)) 22 | .build() 23 | component.inject(this) 24 | } 25 | } -------------------------------------------------------------------------------- /app/src/main/kotlin/com/cogitator/androidcleanarchitecture/base/BaseActivity.kt: -------------------------------------------------------------------------------- 1 | package com.cogitator.androidcleanarchitecture.base 2 | 3 | import android.animation.Animator 4 | import android.animation.AnimatorListenerAdapter 5 | import android.annotation.TargetApi 6 | import android.arch.lifecycle.ViewModelProvider 7 | import android.content.pm.PackageManager 8 | import android.databinding.DataBindingUtil 9 | import android.databinding.ViewDataBinding 10 | import android.graphics.Rect 11 | import android.os.Build 12 | import android.os.Bundle 13 | import android.support.annotation.LayoutRes 14 | import android.support.v4.app.Fragment 15 | import android.support.v7.app.AppCompatActivity 16 | import android.transition.Explode 17 | import android.transition.Transition 18 | import android.util.DisplayMetrics 19 | import android.view.View 20 | import android.view.ViewAnimationUtils 21 | import android.view.ViewTreeObserver 22 | import android.view.animation.AccelerateInterpolator 23 | import com.cogitator.androidcleanarchitecture.CleanAndroidApp 24 | import com.cogitator.androidcleanarchitecture.R 25 | import com.cogitator.androidcleanarchitecture.utils.invisble 26 | import com.cogitator.androidcleanarchitecture.utils.visible 27 | 28 | /** 29 | * @author Ankit Kumar on 01/10/2018 30 | */ 31 | abstract class BaseActivity(private val mViewModelClass: Class) : AppCompatActivity(), BaseFragment.Callback { 32 | 33 | // Initialization of ViewModelFactory 34 | var viewModelFactory: ViewModelProvider.Factory = CleanAndroidApp.component.factory() 35 | 36 | @LayoutRes 37 | abstract fun getLayoutRes(): Int 38 | 39 | val binding by lazy { 40 | DataBindingUtil.setContentView(this, getLayoutRes()) as DB 41 | } 42 | 43 | protected fun getViewModel(clazz: Class, viewModelFactory: ViewModelProvider.Factory): T { 44 | return ViewModelProviders.of(this, viewModelFactory) 45 | .get(clazz) 46 | } 47 | 48 | private fun getViewModel(viewmodel: Class): VM { 49 | return ViewModelProviders.of(this, viewModelFactory).get(viewmodel) 50 | } 51 | 52 | val viewModel by lazy { 53 | getViewModel(mViewModelClass) 54 | } 55 | 56 | /** 57 | * If you want to inject Dependency Injection 58 | * on your activity, you can override this method. 59 | */ 60 | open fun onInject() {} 61 | 62 | override fun onCreate(savedInstanceState: Bundle?) { 63 | initViewModel(viewModel) 64 | super.onCreate(savedInstanceState) 65 | onInject() 66 | } 67 | 68 | /** 69 | * 70 | * You need to override this method. 71 | * And you need to set viewModel to binding: binding.viewModel = viewModel 72 | * 73 | */ 74 | abstract fun initViewModel(viewModel: VM) 75 | 76 | val isNetworkConnected: Boolean 77 | get() = NetworkUtils.isNetworkConnected(applicationContext) 78 | 79 | override fun onFragmentAttached() { 80 | 81 | } 82 | 83 | override fun onFragmentDetached(tag: String) { 84 | 85 | } 86 | 87 | public override fun onSaveInstanceState(savedInstanceState: Bundle) { 88 | // Always call the superclass so it can save the view hierarchy state 89 | super.onSaveInstanceState(savedInstanceState) 90 | } 91 | 92 | override fun onRestoreInstanceState(savedInstanceState: Bundle) { 93 | //do nothing 94 | } 95 | 96 | @TargetApi(Build.VERSION_CODES.M) 97 | fun hasPermission(permission: String): Boolean { 98 | return Build.VERSION.SDK_INT < Build.VERSION_CODES.M || checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED 99 | } 100 | 101 | @TargetApi(Build.VERSION_CODES.M) 102 | fun requestPermissionsSafely(permissions: Array, requestCode: Int) { 103 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 104 | requestPermissions(permissions, requestCode) 105 | } 106 | } 107 | 108 | fun getLastNotNull(list: List): Fragment? { 109 | list.indices.reversed() 110 | .map { list[it] } 111 | .forEach { 112 | return it 113 | } 114 | return null 115 | } 116 | 117 | override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { 118 | super.onRequestPermissionsResult(requestCode, permissions, grantResults) 119 | getTopFragment()?.onRequestPermissionsResult(requestCode, permissions, grantResults) 120 | } 121 | 122 | /** 123 | * function to show the fragment 124 | * 125 | * @param name fragment to be shown 126 | */ 127 | fun showFragment(name: Fragment) { 128 | val fragmentManager = supportFragmentManager 129 | // check if the fragment is in back stack 130 | val fragmentTransaction = fragmentManager.beginTransaction() 131 | fragmentTransaction.replace(R.id.flContent, name, name.javaClass.name) 132 | fragmentTransaction.addToBackStack(name.javaClass.name) 133 | fragmentTransaction.commitAllowingStateLoss() 134 | } 135 | 136 | /** 137 | * function to show the fragment 138 | * 139 | * @param current current visible fragment 140 | * @param newFragment next visible fragment 141 | * @param sharedView view which show the transition 142 | * 143 | */ 144 | fun showFragmentWithTransition(current: Fragment, newFragment: Fragment, sharedView: View) { 145 | val fragmentManager = supportFragmentManager 146 | // check if the fragment is in back stack 147 | val fragmentPopped = fragmentManager.popBackStackImmediate(newFragment.javaClass.name, 0) 148 | if (fragmentPopped) { 149 | // fragment is pop from backStack 150 | } else { 151 | val fragmentTransaction = fragmentManager.beginTransaction() 152 | fragmentTransaction.setReorderingAllowed(true) 153 | fragmentTransaction.add(R.id.flContent, newFragment, newFragment.javaClass.name) 154 | fragmentTransaction.addToBackStack(newFragment.javaClass.name) 155 | fragmentTransaction.addSharedElement(sharedView, sharedView.transitionName) 156 | fragmentTransaction.commitAllowingStateLoss() 157 | // newFragment.view?.rootView?.let { fragmentCircularReveal(sharedView, it) } 158 | } 159 | } 160 | 161 | /** 162 | * function to get the transition of the list item 163 | * 164 | * @param itemView view which show the transition 165 | * 166 | */ 167 | fun getListFragmentExitTransition(itemView: View) { 168 | val epicCenterRect = Rect() 169 | //itemView is the full-width inbox item's view 170 | itemView.getGlobalVisibleRect(epicCenterRect) 171 | // Set Epic center to a imaginary horizontal full width line under the clicked item, so the explosion happens vertically away from it 172 | epicCenterRect.top = epicCenterRect.bottom 173 | val exitTransition = Explode() 174 | exitTransition.epicenterCallback = object : Transition.EpicenterCallback() { 175 | override fun onGetEpicenter(transition: Transition): Rect { 176 | return epicCenterRect 177 | } 178 | } 179 | } 180 | 181 | /** 182 | * function to show the fragment with default transition 183 | * 184 | * @param newFragment next visible fragment 185 | * 186 | */ 187 | 188 | fun showFragmentWithOutTransition(newFragment: Fragment) { 189 | val fragmentManager = supportFragmentManager 190 | // check if the fragment is in back stack 191 | val fragmentPopped = fragmentManager.popBackStackImmediate(newFragment.javaClass.name, 0) 192 | if (fragmentPopped) { 193 | // fragment is pop from backStack 194 | } else { 195 | val fragmentTransaction = fragmentManager.beginTransaction() 196 | fragmentTransaction.add(R.id.flContent, newFragment, newFragment.javaClass.name) 197 | fragmentTransaction.addToBackStack(newFragment.javaClass.name) 198 | fragmentTransaction.commitAllowingStateLoss() 199 | } 200 | } 201 | 202 | /** 203 | * function to retrieve top fragment from the back stack entry 204 | * 205 | */ 206 | 207 | fun getTopFragment(): Fragment? { 208 | if (supportFragmentManager.backStackEntryCount == 0) { 209 | return null 210 | } 211 | val fragmentTag = supportFragmentManager.getBackStackEntryAt(supportFragmentManager.backStackEntryCount - 1).name 212 | return supportFragmentManager.findFragmentByTag(fragmentTag) 213 | } 214 | 215 | fun animateIt(view: View) { 216 | view.invisble() 217 | val displayMetrics = DisplayMetrics() 218 | windowManager.defaultDisplay.getMetrics(displayMetrics) 219 | val height = displayMetrics.heightPixels 220 | val width = displayMetrics.widthPixels 221 | val revealX = if (view.width > 0) view.width / 2 else width / 2 222 | val revealY = if (view.height > 0) view.height / 2 else height / 2 223 | val viewTreeObserver = view.viewTreeObserver 224 | if (viewTreeObserver.isAlive) { 225 | viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener { 226 | override fun onGlobalLayout() { 227 | revealActivity(revealX, revealY, view) 228 | view.viewTreeObserver.removeOnGlobalLayoutListener(this) 229 | } 230 | }) 231 | } 232 | } 233 | 234 | private fun revealActivity(x: Int, y: Int, view: View) { 235 | val finalRadius: Float = ((Math.max(view.rootView.width, view.rootView.height) * 1.1).toFloat()) 236 | 237 | // create the animator for this view (the start radius is zero) 238 | val circularReveal = ViewAnimationUtils.createCircularReveal(view, x, y, 0f, finalRadius) 239 | circularReveal.duration = 600 240 | circularReveal.interpolator = AccelerateInterpolator() 241 | 242 | // make the view visible and start the animation 243 | view.visible() 244 | circularReveal.start() 245 | // finish() 246 | } 247 | 248 | fun exitReveal(view: View) { 249 | 250 | // get the center for the clipping circle 251 | val cx = view.measuredWidth / 2 252 | val cy = view.measuredHeight / 2 253 | 254 | // get the initial radius for the clipping circle 255 | val initialRadius = view.width / 2 256 | 257 | // create the animation (the final radius is zero) 258 | val anim = ViewAnimationUtils.createCircularReveal(view, cx, cy, initialRadius.toFloat(), 0f) 259 | 260 | // make the view invisible when the animation is done 261 | anim.addListener(object : AnimatorListenerAdapter() { 262 | override fun onAnimationEnd(animation: Animator) { 263 | super.onAnimationEnd(animation) 264 | view.invisble() 265 | } 266 | }) 267 | 268 | // start the animation 269 | anim.start() 270 | } 271 | 272 | fun fragmentCircularReveal(sharedView: View, view: View) { 273 | val x = sharedView.measuredWidth / 2 274 | val y = sharedView.measuredHeight / 2 275 | 276 | val finalRadius: Float = ((Math.max(view.width, view.height) * 1.1).toFloat()) 277 | 278 | // create the animator for this view (the start radius is zero) 279 | val circularReveal = ViewAnimationUtils.createCircularReveal(sharedView, x, y, 0f, finalRadius) 280 | circularReveal.duration = 400 281 | circularReveal.interpolator = AccelerateInterpolator() 282 | 283 | // make the view visible and start the animation 284 | view.visible() 285 | circularReveal.start() 286 | } 287 | } -------------------------------------------------------------------------------- /app/src/main/kotlin/com/cogitator/androidcleanarchitecture/base/BaseAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.cogitator.androidcleanarchitecture.base 2 | 3 | import android.databinding.ViewDataBinding 4 | import android.support.v7.util.DiffUtil 5 | import android.view.ViewGroup 6 | import android.support.v7.recyclerview.extensions.ListAdapter 7 | 8 | /** 9 | * @author Ankit Kumar on 01/10/2018 10 | */ 11 | 12 | abstract class BaseAdapter(callback: DiffUtil.ItemCallback) : ListAdapter>(callback) { 13 | 14 | override fun onBindViewHolder(holder: BaseViewHolder, position: Int) { 15 | bind(holder.binding, getItem(position), position) 16 | holder.binding.executePendingBindings() 17 | } 18 | 19 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder { 20 | val binding = createBinding(parent, viewType) 21 | return BaseViewHolder(binding) 22 | } 23 | 24 | abstract fun createBinding(parent: ViewGroup, viewType: Int): VB 25 | 26 | protected abstract fun bind(binding: VB, item: M, position: Int) 27 | 28 | } -------------------------------------------------------------------------------- /app/src/main/kotlin/com/cogitator/androidcleanarchitecture/base/BaseFragment.kt: -------------------------------------------------------------------------------- 1 | package com.cogitator.androidcleanarchitecture.base 2 | 3 | import android.arch.lifecycle.ViewModelProvider 4 | import android.arch.lifecycle.ViewModelProviders 5 | import android.content.Context 6 | import android.databinding.DataBindingUtil 7 | import android.databinding.ViewDataBinding 8 | import android.os.Bundle 9 | import android.support.annotation.LayoutRes 10 | import android.support.v4.app.Fragment 11 | import android.view.LayoutInflater 12 | import android.view.View 13 | import android.view.ViewGroup 14 | import com.cogitator.androidcleanarchitecture.CleanAndroidApp 15 | import dagger.android.support.AndroidSupportInjection 16 | 17 | /** 18 | * @author Ankit Kumar on 01/10/2018 19 | */ 20 | 21 | 22 | abstract class BaseFragment(private val mViewModelClass: Class) : Fragment() { 23 | 24 | var viewModelFactory: ViewModelProvider.Factory = CleanAndroidApp.component.factory() 25 | 26 | var binding: DB? = null 27 | private set 28 | var baseActivity: BaseActivity<*, *>? = null 29 | private set 30 | private var mRootView: View? = null 31 | 32 | private fun getViewModel(viewmodel: Class): VM { 33 | return ViewModelProviders.of(this, viewModelFactory).get(viewmodel) 34 | } 35 | 36 | val viewModel by lazy { 37 | getViewModel(mViewModelClass) 38 | } 39 | 40 | abstract fun initViewModel(viewModel: VM) 41 | 42 | /** 43 | * Override for set binding variable 44 | * 45 | * @return variable id 46 | */ 47 | abstract val bindingVariable: Int 48 | 49 | /** 50 | * @return layout resource id 51 | */ 52 | @get:LayoutRes 53 | abstract val layoutId: Int 54 | 55 | 56 | val isNetworkConnected: Boolean 57 | get() = baseActivity != null && baseActivity!!.isNetworkConnected 58 | 59 | override fun onAttach(context: Context?) { 60 | super.onAttach(context) 61 | if (context is BaseActivity<*, *>) { 62 | val activity = context as BaseActivity<*, *>? 63 | this.baseActivity = activity 64 | activity!!.onFragmentAttached() 65 | } 66 | } 67 | 68 | 69 | override fun onCreate(savedInstanceState: Bundle?) { 70 | initViewModel(viewModel) 71 | super.onCreate(savedInstanceState) 72 | onInject() 73 | retainInstance = true 74 | } 75 | 76 | open fun onInject() {} 77 | 78 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { 79 | binding = DataBindingUtil.inflate(inflater, layoutId, container, false) 80 | mRootView = binding?.root 81 | return mRootView 82 | } 83 | 84 | override fun onDetach() { 85 | baseActivity = null 86 | super.onDetach() 87 | } 88 | 89 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 90 | super.onViewCreated(view, savedInstanceState) 91 | binding?.setVariable(bindingVariable, viewModel) 92 | binding?.executePendingBindings() 93 | } 94 | 95 | private fun performDependencyInjection() { 96 | AndroidSupportInjection.inject(this) 97 | } 98 | 99 | interface Callback { 100 | 101 | fun onFragmentAttached() 102 | 103 | fun onFragmentDetached(tag: String) 104 | } 105 | 106 | } -------------------------------------------------------------------------------- /app/src/main/kotlin/com/cogitator/androidcleanarchitecture/base/BaseRepoCallback.kt: -------------------------------------------------------------------------------- 1 | package com.cogitator.androidcleanarchitecture.base 2 | 3 | /** 4 | * @author Ankit Kumar on 25/10/2018 5 | */ 6 | 7 | 8 | interface BaseRepoCallback { 9 | fun onErrorWithId(error: String) 10 | } -------------------------------------------------------------------------------- /app/src/main/kotlin/com/cogitator/androidcleanarchitecture/base/BaseRepository.kt: -------------------------------------------------------------------------------- 1 | package com.cogitator.androidcleanarchitecture.base 2 | 3 | import android.annotation.SuppressLint 4 | import android.support.annotation.CallSuper 5 | import com.cogitator.androidcleanarchitecture.model.network.ApiHelper 6 | import com.google.gson.Gson 7 | import io.reactivex.Single 8 | import io.reactivex.schedulers.Schedulers 9 | import org.xml.sax.ErrorHandler 10 | import retrofit2.HttpException 11 | import retrofit2.Response 12 | import java.io.IOException 13 | import java.net.SocketTimeoutException 14 | import java.net.UnknownHostException 15 | 16 | /** 17 | * @author Ankit Kumar on 01/10/2018 18 | */ 19 | 20 | abstract class BaseRepository constructor( 21 | protected val apiHelper: ApiHelper? = null, 22 | protected val errorHandler: ErrorHandler? = null) { 23 | 24 | protected var listener: LISTENER? = null 25 | 26 | @CallSuper 27 | open fun call(listener: LISTENER) { 28 | this.listener = listener 29 | } 30 | 31 | @SuppressLint("CheckResult") 32 | protected inline fun executeNetworkCall( 33 | crossinline request: () -> Single>, 34 | crossinline successful: (t: RESPONSE) -> Unit, 35 | crossinline errorful: (t: BaseResponse) -> Unit) { 36 | request().subscribeOn(Schedulers.io()) 37 | .observeOn(AndroidSchedulers.mainThread()) 38 | .subscribe( 39 | { 40 | if (it.isSuccessful) { 41 | it.body()?.let { it1 -> 42 | successful(it1) 43 | } 44 | } else { 45 | if (it.errorBody() != null) { 46 | val errorResponse = Gson().fromJson(it.errorBody()?.charStream(), BaseResponse::class.java) 47 | errorResponse?.let { errorful(it) } 48 | } 49 | } 50 | }, 51 | { t: Throwable -> 52 | when (t) { 53 | is HttpException -> { 54 | listener?.onErrorWithId(t.message!! + "") 55 | } 56 | is SocketTimeoutException -> { 57 | listener?.onErrorWithId("Timeout") 58 | } 59 | is UnknownHostException -> { 60 | listener?.onErrorWithId("Unknown Host") 61 | } 62 | is IOException -> { 63 | listener?.onErrorWithId(t.message!! + "") 64 | } 65 | else -> { 66 | listener?.onErrorWithId(t.message!! + "") 67 | } 68 | } 69 | hideLoading() 70 | } 71 | ) 72 | } 73 | 74 | @SuppressLint("CheckResult") 75 | protected inline fun executeNetworkCall( 76 | crossinline request: () -> Single>) { 77 | request().subscribeOn(Schedulers.io()) 78 | .observeOn(AndroidSchedulers.mainThread()) 79 | .subscribe { _, _ -> } 80 | } 81 | } -------------------------------------------------------------------------------- /app/src/main/kotlin/com/cogitator/androidcleanarchitecture/base/BaseViewHolder.kt: -------------------------------------------------------------------------------- 1 | package com.cogitator.androidcleanarchitecture.base 2 | 3 | import android.databinding.ViewDataBinding 4 | import android.support.v7.widget.RecyclerView 5 | 6 | /** 7 | * @author Ankit Kumar on 01/10/2018 8 | */ 9 | 10 | open class BaseViewHolder(val binding: V) : RecyclerView.ViewHolder(binding.root) -------------------------------------------------------------------------------- /app/src/main/kotlin/com/cogitator/androidcleanarchitecture/base/BaseViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.cogitator.androidcleanarchitecture.base 2 | 3 | import android.annotation.SuppressLint 4 | import android.app.Application 5 | import android.arch.lifecycle.AndroidViewModel 6 | import android.arch.lifecycle.MutableLiveData 7 | import android.content.Context 8 | 9 | /** 10 | * @author Ankit Kumar on 01/10/2018 11 | */ 12 | 13 | abstract class BaseViewModel(application: Application) : AndroidViewModel(application) { 14 | @SuppressLint("StaticFieldLeak") 15 | lateinit var context: Any 16 | 17 | protected fun getAppContext(): Context = getApplication().applicationContext 18 | 19 | protected fun setObserverValue(observer: MutableLiveData, data: T) { 20 | observer.value = data 21 | } 22 | } -------------------------------------------------------------------------------- /app/src/main/kotlin/com/cogitator/androidcleanarchitecture/base/ViewModelFactory.kt: -------------------------------------------------------------------------------- 1 | package com.cogitator.androidcleanarchitecture.base 2 | 3 | import android.arch.lifecycle.AndroidViewModel 4 | import android.arch.lifecycle.ViewModel 5 | import android.arch.lifecycle.ViewModelProvider 6 | import java.lang.IllegalArgumentException 7 | import javax.inject.Inject 8 | import javax.inject.Provider 9 | import javax.inject.Singleton 10 | 11 | /** 12 | * @author Ankit Kumar on 01/10/2018 13 | */ 14 | 15 | 16 | @Singleton 17 | class ViewModelFactory @Inject constructor( 18 | private val creators: Map, @JvmSuppressWildcards Provider>) 19 | : ViewModelProvider.Factory { 20 | 21 | @Suppress("UNCHECKED_CAST") 22 | override fun create(modelClass: Class): T { 23 | val creator = creators[modelClass] 24 | ?: creators.asIterable().firstOrNull { modelClass.isAssignableFrom(it.key) }?.value 25 | ?: throw IllegalArgumentException("unknown model class $modelClass") 26 | 27 | return try { 28 | creator.get() as T 29 | } catch (e: Exception) { 30 | throw RuntimeException(e) 31 | } 32 | } 33 | } -------------------------------------------------------------------------------- /app/src/main/kotlin/com/cogitator/androidcleanarchitecture/di/component/ApplicationComponent.kt: -------------------------------------------------------------------------------- 1 | package com.cogitator.androidcleanarchitecture.di.component 2 | 3 | import android.app.Application 4 | import android.arch.lifecycle.ViewModelProvider 5 | import android.content.Context 6 | import android.content.SharedPreferences 7 | import com.cogitator.androidcleanarchitecture.CleanAndroidApp 8 | import com.cogitator.androidcleanarchitecture.di.module.AppModule 9 | import com.cogitator.androidcleanarchitecture.di.module.NetModule 10 | import com.cogitator.androidcleanarchitecture.model.network.ApiHelper 11 | import dagger.Component 12 | import org.xml.sax.ErrorHandler 13 | import javax.inject.Singleton 14 | 15 | /** 16 | * @author Ankit Kumar on 01/10/2018 17 | */ 18 | 19 | @Singleton 20 | @Component(modules = [AppModule::class, NetModule::class]) 21 | interface ApplicationComponent { 22 | 23 | fun inject(app: CleanAndroidApp) 24 | 25 | fun app(): Application 26 | 27 | fun factory(): ViewModelProvider.Factory 28 | fun context(): Context 29 | 30 | fun preferences(): SharedPreferences 31 | fun errorHandler(): ErrorHandler 32 | fun apiHelper(): ApiHelper 33 | 34 | } -------------------------------------------------------------------------------- /app/src/main/kotlin/com/cogitator/androidcleanarchitecture/di/module/AppModule.kt: -------------------------------------------------------------------------------- 1 | package com.cogitator.androidcleanarchitecture.di.module 2 | 3 | import android.app.Application 4 | import android.content.Context 5 | import android.content.SharedPreferences 6 | import android.preference.PreferenceManager 7 | import com.cogitator.androidcleanarchitecture.CleanAndroidApp 8 | import dagger.Provides 9 | import javax.inject.Singleton 10 | 11 | /** 12 | * @author Ankit Kumar on 01/10/2018 13 | */ 14 | 15 | 16 | class AppModule(var app: CleanAndroidApp) { 17 | 18 | @Provides 19 | @Singleton 20 | fun provideApp(): Application = app 21 | 22 | 23 | @Provides 24 | @Singleton 25 | fun provideContext(): Context = app.applicationContext 26 | 27 | @Provides 28 | @Singleton 29 | fun provideSharedPreferences(): SharedPreferences = PreferenceManager.getDefaultSharedPreferences(app) 30 | } -------------------------------------------------------------------------------- /app/src/main/kotlin/com/cogitator/androidcleanarchitecture/di/module/NetModule.kt: -------------------------------------------------------------------------------- 1 | package com.cogitator.androidcleanarchitecture.di.module 2 | 3 | import com.cogitator.androidcleanarchitecture.CleanAndroidApp 4 | import com.cogitator.androidcleanarchitecture.model.network.ApiHelper 5 | import com.cogitator.androidcleanarchitecture.model.network.ServiceApi 6 | import com.cogitator.androidcleanarchitecture.utils.BASE_URL 7 | import dagger.Module 8 | import dagger.Provides 9 | import okhttp3.Cache 10 | import okhttp3.CookieJar 11 | import okhttp3.OkHttpClient 12 | import okhttp3.logging.HttpLoggingInterceptor 13 | import retrofit2.Retrofit 14 | import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory 15 | import retrofit2.converter.gson.GsonConverterFactory 16 | import java.io.File 17 | import java.io.IOException 18 | import java.util.concurrent.TimeUnit 19 | import javax.inject.Singleton 20 | 21 | /** 22 | * @author Ankit Kumar on 01/10/2018 23 | */ 24 | 25 | @Module 26 | class NetModule(var app: CleanAndroidApp) { 27 | @Provides 28 | @Singleton 29 | fun provideNetworkHelper(retrofit: Retrofit): ApiHelper { 30 | val platformAPI = retrofit.create(ServiceApi::class.java) 31 | return ApiHelper(platformAPI) 32 | } 33 | 34 | @Provides 35 | @Singleton 36 | fun provideRetrofit(): Retrofit { 37 | 38 | val interceptor = HttpLoggingInterceptor() 39 | interceptor.level = HttpLoggingInterceptor.Level.BODY 40 | 41 | val httpCacheDirectory = File(app.cacheDir, "responses") 42 | 43 | var cache: Cache? = null 44 | try { 45 | cache = Cache(httpCacheDirectory, 10 * 1024 * 1024) 46 | } catch (e: IOException) { 47 | e.printStackTrace() 48 | } 49 | 50 | val httpClient = OkHttpClient.Builder() 51 | .addInterceptor(interceptor) 52 | .cookieJar(CookieJar.NO_COOKIES) 53 | .cache(cache) 54 | .connectTimeout(45, TimeUnit.SECONDS) 55 | .readTimeout(45, TimeUnit.SECONDS) 56 | .writeTimeout(45, TimeUnit.SECONDS) 57 | .build() 58 | 59 | return Retrofit 60 | .Builder() 61 | .baseUrl(BASE_URL) 62 | .client(httpClient) 63 | .addCallAdapterFactory(RxJava2CallAdapterFactory.create()) 64 | .addConverterFactory(GsonConverterFactory.create()) 65 | .build() 66 | } 67 | } -------------------------------------------------------------------------------- /app/src/main/kotlin/com/cogitator/androidcleanarchitecture/model/Responses.kt: -------------------------------------------------------------------------------- 1 | package com.cogitator.androidcleanarchitecture.model 2 | 3 | import android.view.View 4 | import com.google.gson.annotations.SerializedName 5 | 6 | /** 7 | * @author Ankit Kumar on 08/10/2018 8 | */ 9 | 10 | data class Repo(@SerializedName("stargazers_count") 11 | val stargazersCount: Int, 12 | @SerializedName("pushed_at") 13 | val pushedAt: String, 14 | @SerializedName("subscription_url") 15 | val subscriptionUrl: String, 16 | val language: String, 17 | @SerializedName("branches_url") 18 | val branchesUrl: String, 19 | @SerializedName("issue_comment_url") 20 | val issueCommentUrl: String, 21 | @SerializedName("labels_url") 22 | val labelsUrl: String, 23 | @SerializedName("subscribers_url") 24 | val subscribersUrl: String, 25 | @SerializedName("releases_url") 26 | val releasesUrl: String, 27 | @SerializedName("svn_url") 28 | val svnUrl: String, 29 | val id: Int, 30 | val forks: Int, 31 | @SerializedName("archive_url") 32 | val archiveUrl: String, 33 | @SerializedName("git_refs_url") 34 | val gitRefsUrl: String, 35 | @SerializedName("forks_url") 36 | val forksUrl: String, 37 | @SerializedName("statuses_url") 38 | val statusesUrl: String, 39 | @SerializedName("ssh_url") 40 | val sshUrl: String, 41 | @SerializedName("full_name") 42 | val fullName: String, 43 | val size: Int, 44 | @SerializedName("languages_url") 45 | val languagesUrl: String, 46 | @SerializedName("html_url") 47 | val htmlUrl: String, 48 | @SerializedName("collaborators_url") 49 | val collaboratorsUrl: String, 50 | @SerializedName("clone_url") 51 | val cloneUrl: String, 52 | val name: String, 53 | @SerializedName("pulls_url") 54 | val pullsUrl: String, 55 | @SerializedName("default_branch") 56 | val defaultBranch: String, 57 | @SerializedName("hooks_url") 58 | val hooksUrl: String, 59 | @SerializedName("trees_url") 60 | val treesUrl: String, 61 | @SerializedName("tags_url") 62 | val tagsUrl: String, 63 | val private: Boolean, 64 | @SerializedName("contributors_url") 65 | val contributorsUrl: String, 66 | @SerializedName("has_downloads") 67 | val hasDownloads: Boolean, 68 | @SerializedName("notifications_url") 69 | val notificationsUrl: String, 70 | @SerializedName("open_issues_count") 71 | val openIssuesCount: Int, 72 | val description: String, 73 | @SerializedName("created_at") 74 | val createdAt: String, 75 | val watchers: Int, 76 | @SerializedName("keys_url") 77 | val keysUrl: String, 78 | @SerializedName("deployments_url") 79 | val deploymentsUrl: String, 80 | @SerializedName("has_projects") 81 | val hasProjects: Boolean, 82 | val archived: Boolean, 83 | @SerializedName("has_wiki") 84 | val hasWiki: Boolean, 85 | @SerializedName("updated_at") 86 | val updatedAt: String, 87 | @SerializedName("comments_url") 88 | val commentsUrl: String, 89 | @SerializedName("stargazers_url") 90 | val stargazersUrl: String, 91 | @SerializedName("git_url") 92 | val gitUrl: String, 93 | @SerializedName("has_pages") 94 | val hasPages: Boolean, 95 | @SerializedName("commits_url") 96 | val commitsUrl: String, 97 | @SerializedName("compare_url") 98 | val compareUrl: String, 99 | @SerializedName("git_commits_url") 100 | val gitCommitsUrl: String, 101 | @SerializedName("blobs_url") 102 | val blobsUrl: String, 103 | @SerializedName("git_tags_url") 104 | val gitTagsUrl: String, 105 | @SerializedName("merges_url") 106 | val mergesUrl: String, 107 | @SerializedName("downloads_url") 108 | val downloadsUrl: String, 109 | @SerializedName("has_issues") 110 | val hasIssues: Boolean, 111 | val url: String, 112 | @SerializedName("contents_url") 113 | val contentsUrl: String, 114 | @SerializedName("mirror_url") 115 | val mirrorUrl: String, 116 | @SerializedName("milestones_url") 117 | val milestonesUrl: String, 118 | @SerializedName("teams_url") 119 | val teamsUrl: String, 120 | val fork: Boolean, 121 | @SerializedName("issues_url") 122 | val issuesUrl: String, 123 | @SerializedName("events_url") 124 | val eventsUrl: String, 125 | @SerializedName("issue_events_url") 126 | val issueEventsUrl: String, 127 | @SerializedName("assignees_url") 128 | val assigneesUrl: String, 129 | @SerializedName("open_issues") 130 | val openIssues: Int, 131 | @SerializedName("watchers_count") 132 | val watchersCount: Int, 133 | val homepage: String, 134 | @SerializedName("forks_count") 135 | val forksCount: Int, 136 | var requestBtnClickListener: View.OnClickListener?) 137 | 138 | data class UserProfile(@SerializedName("gists_url") 139 | val gistsUrl: String, 140 | @SerializedName("repos_url") 141 | val reposUrl: String, 142 | @SerializedName("following_url") 143 | val followingUrl: String, 144 | @SerializedName("bio") 145 | val bio: String?, 146 | @SerializedName("created_at") 147 | val createdAt: String, 148 | @SerializedName("login") 149 | val login: String, 150 | @SerializedName("type") 151 | val type: String, 152 | @SerializedName("blog") 153 | val blog: String?, 154 | @SerializedName("subscriptions_url") 155 | val subscriptionsUrl: String, 156 | @SerializedName("updated_at") 157 | val updatedAt: String, 158 | @SerializedName("site_admin") 159 | val siteAdmin: Boolean = false, 160 | @SerializedName("company") 161 | val company: String?, 162 | @SerializedName("id") 163 | val id: Int = 0, 164 | @SerializedName("public_repos") 165 | val publicRepos: Int = 0, 166 | @SerializedName("gravatar_id") 167 | val gravatarId: String, 168 | @SerializedName("email") 169 | val email: String?, 170 | @SerializedName("organizations_url") 171 | val organizationsUrl: String, 172 | @SerializedName("hireable") 173 | val hireable: Boolean = false, 174 | @SerializedName("starred_url") 175 | val starredUrl: String, 176 | @SerializedName("followers_url") 177 | val followersUrl: String, 178 | @SerializedName("public_gists") 179 | val publicGists: Int = 0, 180 | @SerializedName("url") 181 | val url: String, 182 | @SerializedName("received_events_url") 183 | val receivedEventsUrl: String, 184 | @SerializedName("followers") 185 | val followers: Int = 0, 186 | @SerializedName("avatar_url") 187 | val avatarUrl: String, 188 | @SerializedName("events_url") 189 | val eventsUrl: String, 190 | @SerializedName("html_url") 191 | val htmlUrl: String, 192 | @SerializedName("following") 193 | val following: Int = 0, 194 | @SerializedName("name") 195 | val name: String, 196 | @SerializedName("location") 197 | val location: String?) 198 | -------------------------------------------------------------------------------- /app/src/main/kotlin/com/cogitator/androidcleanarchitecture/model/network/ApiHelper.kt: -------------------------------------------------------------------------------- 1 | package com.cogitator.androidcleanarchitecture.model.network 2 | 3 | import com.cogitator.androidcleanarchitecture.model.Repo 4 | import com.cogitator.androidcleanarchitecture.model.UserProfile 5 | import io.reactivex.Single 6 | import retrofit2.Response 7 | 8 | /** 9 | * @author Ankit Kumar on 03/10/2018 10 | */ 11 | open class ApiHelper constructor(private val serviceApi: ServiceApi) { 12 | open fun getUser(username: String): Single> { 13 | return serviceApi.getUserProfile(username) 14 | } 15 | 16 | open fun getRepos(username: String, count: Int, page: Int): Single>> { 17 | return serviceApi.getUserAllRepository(username, count, page) 18 | } 19 | } -------------------------------------------------------------------------------- /app/src/main/kotlin/com/cogitator/androidcleanarchitecture/model/network/ServiceApi.kt: -------------------------------------------------------------------------------- 1 | package com.cogitator.androidcleanarchitecture.model.network 2 | 3 | import com.cogitator.androidcleanarchitecture.model.Repo 4 | import com.cogitator.androidcleanarchitecture.model.UserProfile 5 | import io.reactivex.Single 6 | import retrofit2.Response 7 | import retrofit2.http.GET 8 | import retrofit2.http.Path 9 | import retrofit2.http.Query 10 | 11 | /** 12 | * @author Ankit Kumar on 03/10/2018 13 | */ 14 | interface ServiceApi { 15 | 16 | @GET("/users/{user}") 17 | fun getUserProfile(@Path("user") user: String): Single> 18 | 19 | 20 | @GET("/users/{user}/repos") 21 | fun getUserAllRepository(@Path("user") user: String, 22 | @Query("per_page") count: Int, 23 | @Query("page") page: Int): Single>> 24 | 25 | } -------------------------------------------------------------------------------- /app/src/main/kotlin/com/cogitator/androidcleanarchitecture/utils/Contants.kt: -------------------------------------------------------------------------------- 1 | package com.cogitator.androidcleanarchitecture.utils 2 | 3 | /** 4 | * @author Ankit Kumar on 03/10/2018 5 | */ 6 | 7 | const val BASE_URL: String = "https://api.github.com/" -------------------------------------------------------------------------------- /app/src/main/kotlin/com/cogitator/androidcleanarchitecture/utils/ViewExt.kt: -------------------------------------------------------------------------------- 1 | package com.cogitator.androidcleanarchitecture.utils 2 | 3 | import android.content.ContextWrapper 4 | import android.support.v4.app.Fragment 5 | import android.support.v7.app.AppCompatActivity 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | 10 | /** 11 | * @author Ankit Kumar (ankit.kumar@capitalplatforms.net) on 09/08/2018 12 | */ 13 | 14 | 15 | /** 16 | * The `view` is asking the container to tell about its parent activity. The operation is 17 | * performed by the `ContextWrapper`. 18 | */ 19 | fun Fragment.getParentActivity(): AppCompatActivity? { 20 | var context = this.context 21 | while (context is ContextWrapper) { 22 | if (context is AppCompatActivity) { 23 | return context 24 | } 25 | context = context.baseContext 26 | } 27 | return null 28 | } 29 | 30 | fun ViewGroup.inflate(layoutRes: Int) = LayoutInflater.from(context).inflate(layoutRes, this, false) 31 | 32 | fun View.visible() { 33 | visibility = View.VISIBLE 34 | } 35 | 36 | fun View.invisble() { 37 | visibility = View.INVISIBLE 38 | } 39 | 40 | fun View.gone() { 41 | visibility = View.GONE 42 | } -------------------------------------------------------------------------------- /app/src/main/kotlin/com/cogitator/androidcleanarchitecture/view/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package com.cogitator.androidcleanarchitecture.view 2 | 3 | import com.cogitator.androidcleanarchitecture.base.BaseActivity 4 | import com.cogitator.androidcleanarchitecture.databinding.ActivityMainBinding 5 | import com.cogitator.androidcleanarchitecture.viewModel.MainViewModel 6 | 7 | /** 8 | * @author Ankit Kumar on 04/10/2018 9 | */ 10 | class MainActivity : BaseActivity(MainViewModel::class.java) { 11 | override fun getLayoutRes(): Int { 12 | return R.layout.activity_main 13 | } 14 | 15 | override fun initViewModel(viewModel: MainViewModel) { 16 | 17 | } 18 | 19 | } -------------------------------------------------------------------------------- /app/src/main/kotlin/com/cogitator/androidcleanarchitecture/viewModel/MainViewModel.kt: -------------------------------------------------------------------------------- 1 | package com.cogitator.androidcleanarchitecture.viewModel 2 | 3 | import android.app.Application 4 | import com.cogitator.androidcleanarchitecture.base.BaseViewModel 5 | import javax.inject.Inject 6 | 7 | /** 8 | * @author Ankit Kumar on 08/10/2018 9 | */ 10 | class MainViewModel @Inject constructor(app: Application) 11 | : BaseViewModel(app), AccountRepository.AccountCallback { 12 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/launch_screen.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 10 | 11 | 12 | 13 | 17 | 18 | 23 | 24 | 34 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArchitectAK/AndroidCleanArchitecture/2084180923d1284817013bd5ad3b8c9e854d93c3/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArchitectAK/AndroidCleanArchitecture/2084180923d1284817013bd5ad3b8c9e854d93c3/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArchitectAK/AndroidCleanArchitecture/2084180923d1284817013bd5ad3b8c9e854d93c3/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArchitectAK/AndroidCleanArchitecture/2084180923d1284817013bd5ad3b8c9e854d93c3/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ArchitectAK/AndroidCleanArchitecture/2084180923d1284817013bd5ad3b8c9e854d93c3/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | #ffffff 7 | #00000000 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | AndroidCleanArchitecture 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 16 | 17 |