├── app ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── mipmap-hdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ ├── ic_launcher.png │ │ │ │ └── ic_launcher_round.png │ │ │ ├── values │ │ │ │ ├── colors.xml │ │ │ │ ├── styles.xml │ │ │ │ └── strings.xml │ │ │ ├── layout │ │ │ │ ├── empty_view_example.xml │ │ │ │ ├── fragment_recycler_example.xml │ │ │ │ ├── item_recycler_example.xml │ │ │ │ ├── fragment_login.xml │ │ │ │ ├── fragment_onboarding_page.xml │ │ │ │ └── fragment_onboarding.xml │ │ │ └── drawable │ │ │ │ └── primary_btn_background.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── xmartlabs │ │ │ │ └── template │ │ │ │ ├── model │ │ │ │ ├── Session.kt │ │ │ │ ├── common │ │ │ │ │ ├── BuildType.kt │ │ │ │ │ └── BuildInfo.kt │ │ │ │ └── AuthResponse.kt │ │ │ │ ├── ui │ │ │ │ ├── recyclerfragmentexample │ │ │ │ │ ├── RecyclerExampleView.kt │ │ │ │ │ ├── RecyclerExamplePresenter.kt │ │ │ │ │ ├── RecyclerExampleActivity.kt │ │ │ │ │ ├── RecyclerExampleAdapter.kt │ │ │ │ │ └── RecyclerExampleFragment.kt │ │ │ │ ├── login │ │ │ │ │ ├── LoginView.kt │ │ │ │ │ ├── LoginActivity.kt │ │ │ │ │ ├── LoginPresenter.kt │ │ │ │ │ └── LoginFragment.kt │ │ │ │ ├── common │ │ │ │ │ ├── TemplatePresenter.kt │ │ │ │ │ ├── TemplateView.kt │ │ │ │ │ └── TemplateFragment.kt │ │ │ │ ├── onboarding │ │ │ │ │ ├── OnboardingActivity.kt │ │ │ │ │ ├── OnboardingView.kt │ │ │ │ │ ├── OnboardingPageAdapter.kt │ │ │ │ │ ├── page │ │ │ │ │ │ ├── OnboardingPage.kt │ │ │ │ │ │ └── OnboardingPageFragment.kt │ │ │ │ │ ├── OnboardingPresenter.kt │ │ │ │ │ └── OnboardingFragment.kt │ │ │ │ └── StartActivity.kt │ │ │ │ ├── database │ │ │ │ └── AppDataBase.kt │ │ │ │ ├── di │ │ │ │ ├── RestServiceModuleApi.kt │ │ │ │ ├── AppModule.kt │ │ │ │ ├── OkHttpModule.kt │ │ │ │ ├── RestServiceModule.kt │ │ │ │ ├── ServiceGsonModule.kt │ │ │ │ ├── ControllerModule.kt │ │ │ │ ├── FragmentBuildersModule.kt │ │ │ │ ├── ActivityModule.kt │ │ │ │ └── ApplicationComponent.kt │ │ │ │ ├── helper │ │ │ │ ├── EmptyOnPageChangeListener.kt │ │ │ │ ├── GeneralSingleSubscriber.kt │ │ │ │ ├── GeneralCompletableSubscriber.kt │ │ │ │ ├── GeneralObservableSubscriber.kt │ │ │ │ └── GeneralFlowableSubscriber.kt │ │ │ │ ├── service │ │ │ │ └── AuthService.kt │ │ │ │ ├── controller │ │ │ │ ├── SessionController.kt │ │ │ │ └── AuthController.kt │ │ │ │ └── App.kt │ │ └── AndroidManifest.xml │ ├── staging │ │ └── res │ │ │ └── values │ │ │ └── strings.xml │ ├── androidTest │ │ ├── AndroidManifest.xml │ │ └── java │ │ │ └── com │ │ │ └── xmartlabs │ │ │ └── template │ │ │ ├── TestApplication.kt │ │ │ ├── common │ │ │ ├── SleepingAction.kt │ │ │ ├── BaseInstrumentationTest.kt │ │ │ ├── ImmediateNewThreadScheduler.kt │ │ │ └── SingleActivityInstrumentationTest.kt │ │ │ ├── uitest │ │ │ └── sign │ │ │ │ └── LoginInstrumentalTest.kt │ │ │ ├── InstrumentalTestComponent.kt │ │ │ └── TestRunner.kt │ ├── sharedTest │ │ └── java │ │ │ └── com │ │ │ └── xmartlabs │ │ │ └── template │ │ │ ├── controller │ │ │ └── MockAuthController.kt │ │ │ └── di │ │ │ ├── MockRestServiceModule.kt │ │ │ ├── MockClockModule.kt │ │ │ └── MockControllerModule.kt │ └── test │ │ └── java │ │ └── com │ │ └── xmartlabs │ │ └── template │ │ ├── di │ │ └── MockAndroidModule.kt │ │ ├── tests │ │ └── signin │ │ │ └── SignInUnitTest.kt │ │ └── common │ │ ├── BaseUnitTest.kt │ │ └── TestComponent.kt ├── proguard-rules.pro ├── detekt-config.yml └── build.gradle ├── settings.gradle ├── .codebeatignore ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── detekt.gradle ├── README.md ├── gradle.properties ├── .github └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── circle.yml ├── gradlew.bat └── gradlew /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /.codebeatignore: -------------------------------------------------------------------------------- 1 | app/src/main/java/com/xmartlabs/template/ApplicationComponent.kt 2 | app/src/sharedTest 3 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/bigbang-template/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/bigbang-template/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/bigbang-template/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/bigbang-template/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/bigbang-template/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/bigbang-template/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/bigbang-template/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/bigbang-template/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/bigbang-template/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/bigbang-template/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xmartlabs/bigbang-template/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/java/com/xmartlabs/template/model/Session.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.model 2 | 3 | import com.xmartlabs.bigbang.core.model.SessionType 4 | 5 | class Session(override var accessToken: String? = null) : SessionType 6 | -------------------------------------------------------------------------------- /app/src/staging/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Template STA 3 | staging.template.com 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/java/com/xmartlabs/template/ui/recyclerfragmentexample/RecyclerExampleView.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.ui.recyclerfragmentexample 2 | 3 | import com.xmartlabs.template.ui.common.TemplateView 4 | 5 | interface RecyclerExampleView : TemplateView 6 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Thu Nov 09 12:41:32 UYT 2017 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.10.2-all.zip 7 | -------------------------------------------------------------------------------- /app/src/main/java/com/xmartlabs/template/ui/login/LoginView.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.ui.login 2 | 3 | import com.xmartlabs.template.ui.common.TemplateView 4 | 5 | interface LoginView : TemplateView { 6 | fun setIsLoading(loading: Boolean) 7 | fun gotoRecyclerExampleActivity() 8 | } 9 | -------------------------------------------------------------------------------- /app/src/main/java/com/xmartlabs/template/model/common/BuildType.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.model.common 2 | 3 | import java.util.Locale 4 | 5 | enum class BuildType { 6 | STAGING, 7 | PRODUCTION, 8 | ; 9 | 10 | override fun toString() = name.toLowerCase(Locale.getDefault()) 11 | } 12 | -------------------------------------------------------------------------------- /app/src/androidTest/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #4a90e2 4 | #3F51B5 5 | #303F9F 6 | @android:color/black 7 | 8 | -------------------------------------------------------------------------------- /detekt.gradle: -------------------------------------------------------------------------------- 1 | detekt { 2 | version = "1.0.0.RC2" 3 | profile("main") { 4 | input = "$projectDir/src/main/" 5 | config = "$projectDir/detekt-config.yml" 6 | filters = ".*test.*,.*/resources/.*,.*/tmp/.*" 7 | output = "$projectDir/build/reports/detekt" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/empty_view_example.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | -------------------------------------------------------------------------------- /app/src/main/java/com/xmartlabs/template/ui/recyclerfragmentexample/RecyclerExamplePresenter.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.ui.recyclerfragmentexample 2 | 3 | import com.xmartlabs.template.ui.common.TemplatePresenter 4 | import javax.inject.Inject 5 | 6 | class RecyclerExamplePresenter @Inject constructor() : TemplatePresenter() 7 | -------------------------------------------------------------------------------- /app/src/main/java/com/xmartlabs/template/ui/common/TemplatePresenter.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.ui.common 2 | 3 | import com.xmartlabs.bigbang.ui.mvp.BaseMvpPresenter 4 | 5 | abstract class TemplatePresenter : BaseMvpPresenter() { 6 | override fun attachView(view: T) { 7 | super.attachView(view) 8 | view.setup() 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/com/xmartlabs/template/ui/login/LoginActivity.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.ui.login 2 | 3 | import com.f2prateek.dart.HensonNavigable 4 | import com.xmartlabs.bigbang.ui.SingleFragmentActivity 5 | 6 | @HensonNavigable 7 | class LoginActivity : SingleFragmentActivity() { 8 | override fun createFragment() = LoginFragmentBuilder().build() 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/xmartlabs/template/database/AppDataBase.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.database 2 | 3 | import com.raizlabs.android.dbflow.annotation.Database 4 | 5 | @Database(name = AppDataBase.NAME, version = AppDataBase.VERSION) 6 | object AppDataBase { 7 | //TODO: change db name 8 | const val NAME = "template_database" 9 | const val VERSION = 1 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/com/xmartlabs/template/model/AuthResponse.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.model 2 | 3 | import org.parceler.Parcel 4 | 5 | //TODO: check service auth response to match app's AuthResponse fields 6 | @Parcel(Parcel.Serialization.BEAN) 7 | class AuthResponse { 8 | var accessToken: String? = null 9 | var scope: String? = null 10 | var tokenType: String? = null 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/primary_btn_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 10 | 11 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /app/src/main/java/com/xmartlabs/template/ui/onboarding/OnboardingActivity.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.ui.onboarding 2 | 3 | import com.f2prateek.dart.HensonNavigable 4 | import com.xmartlabs.bigbang.ui.SingleFragmentActivity 5 | 6 | @HensonNavigable 7 | class OnboardingActivity : SingleFragmentActivity() { 8 | override fun createFragment() = OnboardingFragmentBuilder().build() 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/xmartlabs/template/ui/recyclerfragmentexample/RecyclerExampleActivity.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.ui.recyclerfragmentexample 2 | 3 | import com.f2prateek.dart.HensonNavigable 4 | import com.xmartlabs.bigbang.ui.SingleFragmentActivity 5 | 6 | @HensonNavigable 7 | class RecyclerExampleActivity : SingleFragmentActivity() { 8 | override fun createFragment() = RecyclerExampleFragmentBuilder().build() 9 | } 10 | -------------------------------------------------------------------------------- /app/src/main/java/com/xmartlabs/template/di/RestServiceModuleApi.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.di 2 | 3 | import com.xmartlabs.template.service.AuthService 4 | import dagger.Module 5 | import dagger.Provides 6 | import retrofit2.Retrofit 7 | import javax.inject.Singleton 8 | 9 | @Module 10 | class RestServiceModuleApi { 11 | @Provides 12 | @Singleton 13 | internal fun provideAuthService(retrofit: Retrofit) = retrofit.create(AuthService::class.java) 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/java/com/xmartlabs/template/helper/EmptyOnPageChangeListener.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.helper 2 | 3 | import android.support.v4.view.ViewPager 4 | 5 | open class EmptyOnPageChangeListener : ViewPager.OnPageChangeListener { 6 | override fun onPageScrollStateChanged(state: Int) = Unit 7 | override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) = Unit 8 | override fun onPageSelected(position: Int) = Unit 9 | } 10 | -------------------------------------------------------------------------------- /app/src/sharedTest/java/com/xmartlabs/template/controller/MockAuthController.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.controller 2 | 3 | import com.xmartlabs.template.service.AuthService 4 | import io.reactivex.Completable 5 | import javax.inject.Inject 6 | 7 | class MockAuthController @Inject constructor(authService: AuthService, sessionController: SessionController) 8 | : AuthController(authService, sessionController) { 9 | override fun signIn() = Completable.complete() 10 | } 11 | -------------------------------------------------------------------------------- /app/src/main/java/com/xmartlabs/template/di/AppModule.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.di 2 | 3 | import com.xmartlabs.template.model.common.BuildInfo 4 | import dagger.Module 5 | import dagger.Provides 6 | import javax.inject.Singleton 7 | import com.xmartlabs.bigbang.core.model.BuildInfo as CoreBuildInfo 8 | 9 | @Module 10 | class AppModule { 11 | @Provides 12 | @Singleton 13 | fun provideBuildInformation(coreBuildInfo : BuildInfo) : CoreBuildInfo = coreBuildInfo 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/java/com/xmartlabs/template/ui/login/LoginPresenter.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.ui.login 2 | 3 | import com.xmartlabs.template.ui.common.TemplatePresenter 4 | import javax.inject.Inject 5 | 6 | class LoginPresenter @Inject constructor() : TemplatePresenter() { 7 | //TODO: Do something on login button clicked 8 | fun loginButtonClicked() = view?.gotoRecyclerExampleActivity() 9 | 10 | private fun setIsLoading(loading: Boolean) = view?.setIsLoading(loading) 11 | } 12 | -------------------------------------------------------------------------------- /app/src/sharedTest/java/com/xmartlabs/template/di/MockRestServiceModule.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.di 2 | 3 | import android.content.Context 4 | import com.xmartlabs.bigbang.retrofit.module.RestServiceModule 5 | import io.appflate.restmock.RESTMockServer 6 | import okhttp3.HttpUrl 7 | 8 | class MockRestServiceModule : RestServiceModule() { 9 | @Suppress("UnsafeCallOnNullableType") 10 | override fun provideBaseUrl(context: Context): HttpUrl = HttpUrl.parse(RESTMockServer.getUrl())!! 11 | } 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/xmartlabs/template/model/common/BuildInfo.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.model.common 2 | 3 | import com.xmartlabs.template.BuildConfig 4 | import com.xmartlabs.bigbang.core.model.BuildInfo as CoreBuildInfo 5 | 6 | class BuildInfo : CoreBuildInfo { 7 | override val isDebug = BuildConfig.DEBUG 8 | override val isProduction: Boolean 9 | get() = BuildConfig.FLAVOR == BuildType.STAGING.toString() 10 | override val isStaging: Boolean 11 | get() = BuildConfig.FLAVOR == BuildType.PRODUCTION.toString() 12 | } 13 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/xmartlabs/template/TestApplication.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template 2 | 3 | import com.xmartlabs.template.di.ApplicationComponent 4 | import com.xmartlabs.template.di.MockRestServiceModule 5 | import com.xmartlabs.template.model.common.BuildInfo 6 | 7 | class TestApplication : App() { 8 | override fun createComponent(): ApplicationComponent = DaggerInstrumentalTestComponent.builder() 9 | .application(this) 10 | .buildInfo(BuildInfo()) 11 | .restServiceModule(MockRestServiceModule()) 12 | .build() 13 | } 14 | -------------------------------------------------------------------------------- /app/src/main/java/com/xmartlabs/template/ui/common/TemplateView.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.ui.common 2 | 3 | import android.support.annotation.StringRes 4 | 5 | import com.xmartlabs.bigbang.ui.mvp.MvpView 6 | import com.xmartlabs.template.R 7 | 8 | interface TemplateView : MvpView { 9 | fun setup() 10 | fun showError(@StringRes message: Int, @StringRes title: Int = R.string.error, 11 | @StringRes buttonTitle: Int = android.R.string.ok) 12 | fun showError(error: Throwable, @StringRes message: Int?) 13 | val isViewAlive: Boolean 14 | } 15 | -------------------------------------------------------------------------------- /app/src/main/java/com/xmartlabs/template/ui/onboarding/OnboardingView.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.ui.onboarding 2 | 3 | import android.content.Intent 4 | 5 | import com.xmartlabs.template.ui.common.TemplateView 6 | 7 | interface OnboardingView : TemplateView { 8 | fun createPageAdapter(): OnboardingPageAdapter 9 | fun setup(pageAdapter: OnboardingPageAdapter) 10 | fun setSkipButtonVisibility(visible: Boolean) 11 | fun moveToPage(page: Int) 12 | fun startActivity(intent: Intent) 13 | fun handleNextButtonVisibility() 14 | fun goToLoginActivity() 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/com/xmartlabs/template/service/AuthService.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.service 2 | 3 | import com.xmartlabs.template.model.AuthResponse 4 | 5 | import io.reactivex.Single 6 | import retrofit2.http.POST 7 | 8 | interface AuthService { 9 | companion object { 10 | //TODO: replace with url path to get access token 11 | const val URL_ACCESS_TOKEN = "accessToken" 12 | } 13 | 14 | //TODO: change signature to the required one to fetch the access token and check AuthResponse to match response 15 | @get:POST(URL_ACCESS_TOKEN) 16 | val accessToken: Single 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/xmartlabs/template/di/OkHttpModule.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.di 2 | 3 | import com.xmartlabs.bigbang.core.module.SessionInterceptor 4 | import dagger.Module 5 | import dagger.Provides 6 | import okhttp3.Interceptor 7 | import javax.inject.Named 8 | import javax.inject.Singleton 9 | import com.xmartlabs.bigbang.core.module.OkHttpModule as CoreOkHttpModule 10 | 11 | @Module 12 | class OkHttpModule : CoreOkHttpModule() { 13 | @Named(SESSION_INTERCEPTOR) 14 | @Provides 15 | @Singleton 16 | fun provideSessionInterceptor(sessionInterceptor: SessionInterceptor): Interceptor = sessionInterceptor 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_recycler_example.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | -------------------------------------------------------------------------------- /app/src/main/java/com/xmartlabs/template/di/RestServiceModule.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.di 2 | 3 | import android.content.Context 4 | import com.xmartlabs.template.App 5 | import com.xmartlabs.template.R 6 | import okhttp3.HttpUrl 7 | import com.xmartlabs.bigbang.retrofit.module.RestServiceModule as CoreRestServiceModule 8 | 9 | class RestServiceModule : CoreRestServiceModule() { 10 | companion object { 11 | private val BASE_URL = App.context.resources.getString(R.string.base_url) 12 | } 13 | 14 | @Suppress("UnsafeCallOnNullableType") 15 | override fun provideBaseUrl(context: Context): HttpUrl = HttpUrl.parse(BASE_URL)!! 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/xmartlabs/template/di/ServiceGsonModule.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.di 2 | 3 | import com.google.gson.FieldNamingPolicy 4 | import com.google.gson.GsonBuilder 5 | import com.xmartlabs.bigbang.core.model.BuildInfo 6 | import com.xmartlabs.bigbang.retrofit.module.ServiceGsonModule as CoreServiceGsonModule 7 | 8 | class ServiceGsonModule : CoreServiceGsonModule() { 9 | override fun modifyGsonBuilder(builder: GsonBuilder, buildInfo: BuildInfo): GsonBuilder { 10 | if (buildInfo.isDebug) { 11 | builder.setPrettyPrinting() 12 | } 13 | builder.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) 14 | return builder 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /app/src/main/res/layout/item_recycler_example.xml: -------------------------------------------------------------------------------- 1 | 2 | 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/xmartlabs/template/ui/onboarding/OnboardingPageAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.ui.onboarding 2 | 3 | import android.support.v4.app.Fragment 4 | import android.support.v4.app.FragmentManager 5 | import android.support.v4.app.FragmentStatePagerAdapter 6 | import com.xmartlabs.template.ui.onboarding.page.OnboardingPage 7 | import com.xmartlabs.template.ui.onboarding.page.OnboardingPageFragmentBuilder 8 | 9 | class OnboardingPageAdapter(fragmentManager: FragmentManager) : FragmentStatePagerAdapter(fragmentManager) { 10 | private val pages = OnboardingPage.values().map { OnboardingPageFragmentBuilder(it).build() } 11 | 12 | override fun getItem(position: Int): Fragment = pages[position] 13 | 14 | override fun getCount() = pages.size 15 | } 16 | -------------------------------------------------------------------------------- /app/src/main/java/com/xmartlabs/template/controller/SessionController.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.controller 2 | 3 | import com.xmartlabs.bigbang.core.controller.CoreSessionController 4 | import com.xmartlabs.bigbang.core.controller.SharedPreferencesController 5 | import com.xmartlabs.template.model.Session 6 | import javax.inject.Inject 7 | 8 | class SessionController @Inject constructor(sharedPreferencesController: SharedPreferencesController) 9 | : CoreSessionController(sharedPreferencesController) { 10 | override fun getSessionType() = Session::class.java 11 | 12 | var session 13 | get() = abstractSession as? Session? 14 | set(value) { value?.let { saveSession(it) } ?: deleteSession() } 15 | 16 | fun update(block: (Session?) -> Session) { session = block(session) } 17 | } 18 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Android Template project 2 | 3 | This is [Xmartlabs](https://xmartlabs.com) Android Template project. 4 | Please read through this README before start working. 5 | 6 | ## Style guide 7 | 8 | Please, remember to always use and follow our [style guide](https://github.com/xmartlabs/Android-Style-Guide) 9 | when making contributions to this project. 10 | 11 | ## Code review process 12 | 13 | Any code that you want to contribute to the project is welcome. However, 14 | before it is accepted, at least one member of the team must validate and approve 15 | the changes, preferably more than one. 16 | 17 | Please request the review by creating a Pull Request. Always include 18 | a clear description of what the changes are. A basic template can be 19 | found [here](.github/PULL_REQUEST_TEMPLATE.md) 20 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/xmartlabs/template/di/ControllerModule.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.di 2 | 3 | import com.xmartlabs.bigbang.core.controller.CoreSessionController 4 | import com.xmartlabs.bigbang.core.controller.SharedPreferencesController 5 | import com.xmartlabs.template.controller.SessionController 6 | import dagger.Module 7 | import dagger.Provides 8 | import javax.inject.Singleton 9 | 10 | @Module 11 | class ControllerModule { 12 | @Provides 13 | @Singleton 14 | internal fun provideSessionController(sharedPreferencesController: SharedPreferencesController) 15 | = SessionController(sharedPreferencesController) 16 | 17 | @Provides 18 | @Singleton 19 | internal fun provideCoreSessionController(sessionController: SessionController): CoreSessionController 20 | = sessionController 21 | } 22 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/xmartlabs/template/common/SleepingAction.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.common 2 | 3 | import io.reactivex.Scheduler 4 | import java.util.concurrent.TimeUnit 5 | 6 | /** Copied from rx.internal.schedulers.SleepingAction. */ 7 | internal class SleepingAction( 8 | private val innerScheduler: Scheduler.Worker, 9 | private val execTime: Long, 10 | private val unit: TimeUnit, 11 | private val underlying: () -> Unit 12 | ) : Runnable { 13 | 14 | override fun run() { 15 | val delay = execTime - innerScheduler.now(unit) 16 | if (delay == 0L) { 17 | return 18 | } 19 | 20 | try { 21 | Thread.sleep(delay) 22 | underlying() 23 | } catch (e: Exception) { 24 | Thread.currentThread().interrupt() 25 | throw RuntimeException(e) 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ### Related Trello cards: 4 | 5 | 6 | ### Additional Info: 7 | 9 | 10 | ### UI preview: 11 | 12 | 13 | 20 | 21 | /cc @xmartlabs/android 22 | -------------------------------------------------------------------------------- /app/src/test/java/com/xmartlabs/template/di/MockAndroidModule.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.di 2 | 3 | import android.content.Context 4 | import android.content.SharedPreferences 5 | import com.xmartlabs.template.App 6 | import dagger.Module 7 | import dagger.Provides 8 | import org.mockito.Mockito 9 | import javax.inject.Singleton 10 | import com.xmartlabs.template.model.common.BuildInfo as CoreBuildInfo 11 | 12 | @Module 13 | class MockAndroidModule { 14 | companion object { 15 | internal val MOCK_CONTEXT = Mockito.mock(App::class.java) 16 | } 17 | 18 | @Provides 19 | @Singleton 20 | fun provideApplicationContext(app: App): Context = app 21 | 22 | @Provides 23 | @Singleton 24 | fun provideApplication(): App = MOCK_CONTEXT 25 | 26 | @Provides 27 | @Singleton 28 | fun provideSharedPreferences(): SharedPreferences = Mockito.mock(SharedPreferences::class.java) 29 | } 30 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/xmartlabs/template/common/BaseInstrumentationTest.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.common 2 | 3 | import android.app.Activity 4 | import android.app.Instrumentation 5 | import android.support.annotation.CallSuper 6 | import android.support.test.InstrumentationRegistry 7 | import android.support.test.runner.AndroidJUnit4 8 | import com.xmartlabs.bigbang.test.extensions.getFirstActivityInstance 9 | import io.appflate.restmock.RESTMockServer 10 | import org.junit.Before 11 | import org.junit.runner.RunWith 12 | 13 | @RunWith(AndroidJUnit4::class) 14 | abstract class BaseInstrumentationTest { 15 | lateinit var instrumentation: Instrumentation 16 | 17 | @Before 18 | @CallSuper 19 | fun setUp() { 20 | instrumentation = InstrumentationRegistry.getInstrumentation() 21 | RESTMockServer.reset() 22 | } 23 | 24 | val activityInstance: Activity? 25 | get() = instrumentation.getFirstActivityInstance() 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/xmartlabs/template/di/FragmentBuildersModule.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.di 2 | 3 | import com.xmartlabs.template.ui.login.LoginFragment 4 | import com.xmartlabs.template.ui.onboarding.OnboardingFragment 5 | import com.xmartlabs.template.ui.onboarding.page.OnboardingPageFragment 6 | import com.xmartlabs.template.ui.recyclerfragmentexample.RecyclerExampleFragment 7 | import dagger.Module 8 | import dagger.android.ContributesAndroidInjector 9 | 10 | @Suppress("unused") 11 | @Module 12 | abstract class FragmentBuildersModule { 13 | @ContributesAndroidInjector 14 | abstract fun contributeLoginFragment(): LoginFragment 15 | 16 | @ContributesAndroidInjector 17 | abstract fun contributeOnboardingFragment(): OnboardingFragment 18 | 19 | @ContributesAndroidInjector 20 | abstract fun contributeOnboardingPageFragment(): OnboardingPageFragment 21 | 22 | @ContributesAndroidInjector 23 | abstract fun contributeRecyclerExampleFragment(): RecyclerExampleFragment 24 | } 25 | -------------------------------------------------------------------------------- /app/src/sharedTest/java/com/xmartlabs/template/di/MockClockModule.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.di 2 | 3 | import dagger.Module 4 | import dagger.Provides 5 | import org.threeten.bp.* 6 | import java.util.* 7 | import javax.inject.Singleton 8 | 9 | /** 10 | * Created by medina on 22/09/2016. 11 | */ 12 | @Module 13 | class MockClockModule { 14 | companion object { 15 | internal val DEFAULT_TIME_ZONE_ID = ZoneId.of("GMT-03:00") 16 | internal val DEFAULT_TIME_ZONE = TimeZone.getTimeZone(DEFAULT_TIME_ZONE_ID.id) 17 | private val DEFAULT_DATE = LocalDate.of(1991, Month.MARCH, 6) 18 | private val DEFAULT_TIME = LocalTime.of(11, 45) 19 | internal val DEFAULT_ZONED_DATE_TIME = ZonedDateTime.of(DEFAULT_DATE, DEFAULT_TIME, DEFAULT_TIME_ZONE_ID) 20 | } 21 | 22 | @Provides 23 | @Singleton 24 | fun provideTimeZone(): TimeZone = DEFAULT_TIME_ZONE 25 | 26 | @Provides 27 | @Singleton 28 | fun provideClock() = Clock.fixed(DEFAULT_ZONED_DATE_TIME.toInstant(), DEFAULT_TIME_ZONE_ID) 29 | } 30 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/diegomedina24/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /app/src/main/java/com/xmartlabs/template/ui/onboarding/page/OnboardingPage.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.ui.onboarding.page 2 | 3 | import android.support.annotation.DrawableRes 4 | import android.support.annotation.StringRes 5 | import com.xmartlabs.bigbang.ui.common.parcel.ParcelerEnumTypeConverter 6 | import com.xmartlabs.template.R 7 | import org.parceler.Parcel 8 | 9 | @Parcel(converter = OnboardingPageTypeConverter::class, value = Parcel.Serialization.BEAN) 10 | enum class OnboardingPage(@StringRes val title: Int, @StringRes val description: Int, @DrawableRes val image: Int) { 11 | //TODO: Replace texts and images for your onboarding 12 | FIRST(R.string.onboarding_title_1, R.string.onboarding_description_1, R.mipmap.ic_launcher), 13 | SECOND(R.string.onboarding_title_2, R.string.onboarding_description_2, R.mipmap.ic_launcher), 14 | THIRD(R.string.onboarding_title_3, R.string.onboarding_description_3, R.mipmap.ic_launcher), 15 | } 16 | 17 | class OnboardingPageTypeConverter : ParcelerEnumTypeConverter(OnboardingPage::class.java) 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/xmartlabs/template/ui/StartActivity.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.ui 2 | 3 | import android.content.Intent 4 | import android.os.Bundle 5 | import com.f2prateek.dart.HensonNavigable 6 | import com.xmartlabs.bigbang.ui.BaseAppCompatActivity 7 | import com.xmartlabs.template.controller.SessionController 8 | import javax.inject.Inject 9 | 10 | @HensonNavigable 11 | class StartActivity : BaseAppCompatActivity() { 12 | @Inject 13 | internal lateinit var sessionController: SessionController 14 | 15 | override fun onCreate(savedInstanceState: Bundle?) { 16 | super.onCreate(savedInstanceState) 17 | 18 | val session = sessionController.session 19 | if (session == null) { 20 | //TODO: handle on start activity no session 21 | val intent = Henson.with(context) 22 | .gotoOnboardingActivity() 23 | .build() 24 | .setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION or Intent.FLAG_ACTIVITY_CLEAR_TASK) 25 | startActivity(intent) 26 | } else { 27 | TODO("Handle on start activity with session") 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /app/src/sharedTest/java/com/xmartlabs/template/di/MockControllerModule.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.di 2 | 3 | import com.xmartlabs.bigbang.core.controller.CoreSessionController 4 | import com.xmartlabs.bigbang.core.controller.SharedPreferencesController 5 | import com.xmartlabs.template.controller.AuthController 6 | import com.xmartlabs.template.controller.MockAuthController 7 | import com.xmartlabs.template.controller.SessionController 8 | import dagger.Module 9 | import dagger.Provides 10 | import javax.inject.Singleton 11 | 12 | @Module 13 | class MockControllerModule { 14 | @Provides 15 | @Singleton 16 | internal fun provideAuthController(mockAuthController: MockAuthController): AuthController = mockAuthController 17 | 18 | @Provides 19 | @Singleton 20 | internal fun provideCoreSessionController(sessionController: SessionController) : CoreSessionController = sessionController 21 | 22 | @Provides 23 | @Singleton 24 | internal fun provideSessionController(sharedPreferencesController: SharedPreferencesController) = SessionController(sharedPreferencesController) 25 | } 26 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/xmartlabs/template/uitest/sign/LoginInstrumentalTest.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.uitest.sign 2 | 3 | import com.xmartlabs.bigbang.test.extensions.checkHasText 4 | import com.xmartlabs.bigbang.test.extensions.checkIsDisplayed 5 | import com.xmartlabs.bigbang.test.helpers.EspressoUtils 6 | import com.xmartlabs.template.R 7 | import com.xmartlabs.template.common.SingleActivityInstrumentationTest 8 | import com.xmartlabs.template.ui.login.LoginActivity 9 | import org.junit.Test 10 | 11 | class LoginInstrumentalTest : SingleActivityInstrumentationTest() { 12 | override val activityClass: Class 13 | get() = LoginActivity::class.java 14 | 15 | @Test 16 | fun testSignInButtonIsVisible() { 17 | launchActivityWithDefaultIntent() 18 | EspressoUtils.onViewWithId(R.id.loginButton) 19 | .checkIsDisplayed() 20 | } 21 | 22 | @Test 23 | fun testSignInButtonText() { 24 | launchActivityWithDefaultIntent() 25 | EspressoUtils.onViewWithId(R.id.loginButton) 26 | .checkHasText(R.string.login) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/xmartlabs/template/di/ActivityModule.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.di 2 | 3 | import com.xmartlabs.template.ui.StartActivity 4 | import com.xmartlabs.template.ui.login.LoginActivity 5 | import com.xmartlabs.template.ui.onboarding.OnboardingActivity 6 | import com.xmartlabs.template.ui.recyclerfragmentexample.RecyclerExampleActivity 7 | import dagger.Module 8 | import dagger.android.ContributesAndroidInjector 9 | 10 | @Suppress("unused") 11 | @Module 12 | abstract class ActivityModule { 13 | @ContributesAndroidInjector(modules = [FragmentBuildersModule::class]) 14 | abstract fun contributeLoginActivity(): LoginActivity 15 | 16 | @ContributesAndroidInjector(modules = [FragmentBuildersModule::class]) 17 | abstract fun contributeOnboardingActivity(): OnboardingActivity 18 | 19 | @ContributesAndroidInjector(modules = [FragmentBuildersModule::class]) 20 | abstract fun contributeRecyclerExampleActivity(): RecyclerExampleActivity 21 | 22 | @ContributesAndroidInjector(modules = [FragmentBuildersModule::class]) 23 | abstract fun contributeStartActivity(): StartActivity 24 | } 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # Files for the ART/Dalvik VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # Generated files 12 | bin/ 13 | gen/ 14 | out/ 15 | 16 | # Gradle files 17 | .gradle/ 18 | build/ 19 | 20 | # Local configuration file (sdk path, etc) 21 | local.properties 22 | 23 | # Log Files 24 | *.log 25 | 26 | # Android Studio Navigation editor temp files 27 | .navigation/ 28 | 29 | # Android Studio captures folder 30 | captures/ 31 | 32 | # Intellij 33 | *.iml 34 | .idea/ 35 | 36 | # Keystore files 37 | # Uncomment the following line if you do not want to check your keystore files in. 38 | #*.jks 39 | 40 | # External native build folder generated in Android Studio 2.2 and later 41 | .externalNativeBuild 42 | 43 | # Google Services (e.g. APIs or Firebase) 44 | google-services.json 45 | 46 | # Freeline 47 | freeline.py 48 | freeline/ 49 | freeline_project_description.json 50 | 51 | # Java class files 52 | *.class1 53 | 54 | # Local configuration file (sdk path, etc) 55 | release.keystore 56 | build.properties 57 | 58 | # OSX files 59 | .DS_Store 60 | -------------------------------------------------------------------------------- /app/src/main/java/com/xmartlabs/template/controller/AuthController.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.controller 2 | 3 | import android.support.annotation.CheckResult 4 | import com.xmartlabs.bigbang.core.controller.Controller 5 | import com.xmartlabs.template.model.Session 6 | import com.xmartlabs.template.service.AuthService 7 | import io.reactivex.Completable 8 | import io.reactivex.Single 9 | import javax.inject.Inject 10 | 11 | open class AuthController @Inject constructor( 12 | private val authService: AuthService, 13 | private val sessionController: SessionController 14 | ) : Controller() { 15 | 16 | //TODO: Change signature and method to match authService request to fetch the Access Token 17 | val accessToken: Single 18 | @CheckResult 19 | get() = authService.accessToken 20 | .applyIoSchedulers() 21 | .filter { authResponse -> authResponse.accessToken != null } 22 | .toSingle() 23 | .map { Session(it.accessToken) } 24 | .doOnSuccess { sessionController.session = it } 25 | 26 | open fun signIn() = Completable.error(NotImplementedError()) 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/xmartlabs/template/helper/GeneralSingleSubscriber.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.helper 2 | 3 | import android.support.annotation.CheckResult 4 | import android.support.annotation.StringRes 5 | import com.xmartlabs.template.ui.common.TemplateView 6 | import io.reactivex.SingleObserver 7 | import io.reactivex.disposables.Disposable 8 | import java.lang.ref.WeakReference 9 | 10 | open class GeneralSingleSubscriber constructor(templateView: TemplateView? = null) : SingleObserver { 11 | private val viewReference = WeakReference(templateView) 12 | 13 | override fun onSubscribe(disposable: Disposable) { } 14 | 15 | override fun onError(throwable: Throwable) { 16 | val view = viewReference.get() 17 | if (alertOnError(throwable) && view != null && view.isViewAlive) { 18 | view.showError(throwable, getErrorMessage(throwable)) 19 | } 20 | } 21 | 22 | override fun onSuccess(t: T) { } 23 | 24 | @StringRes 25 | protected open fun getErrorMessage(throwable: Throwable): Int? = null 26 | 27 | @CheckResult 28 | protected open fun alertOnError(throwable: Throwable) = true 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/xmartlabs/template/helper/GeneralCompletableSubscriber.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.helper 2 | 3 | import android.support.annotation.CheckResult 4 | import android.support.annotation.StringRes 5 | import com.xmartlabs.template.ui.common.TemplateView 6 | import io.reactivex.CompletableObserver 7 | import io.reactivex.disposables.Disposable 8 | import java.lang.ref.WeakReference 9 | 10 | open class GeneralCompletableSubscriber constructor(templateView: TemplateView? = null) : CompletableObserver { 11 | private val viewReference = WeakReference(templateView) 12 | 13 | override fun onSubscribe(disposable: Disposable) { } 14 | 15 | override fun onComplete() { } 16 | 17 | override fun onError(throwable: Throwable) { 18 | val view = viewReference.get() 19 | if (alertOnError(throwable) && view != null && view.isViewAlive) { 20 | view.showError(throwable, getErrorMessage(throwable)) 21 | } 22 | } 23 | 24 | @StringRes 25 | protected open fun getErrorMessage(throwable: Throwable): Int? = null 26 | 27 | @CheckResult 28 | protected open fun alertOnError(throwable: Throwable) = true 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/xmartlabs/template/ui/onboarding/page/OnboardingPageFragment.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.ui.onboarding.page 2 | 3 | import android.os.Bundle 4 | import android.view.View 5 | import com.hannesdorfmann.fragmentargs.annotation.Arg 6 | import com.hannesdorfmann.fragmentargs.annotation.FragmentWithArgs 7 | import com.hannesdorfmann.fragmentargs.bundler.ParcelerArgsBundler 8 | import com.xmartlabs.bigbang.ui.BaseFragment 9 | import com.xmartlabs.template.R 10 | import kotlinx.android.synthetic.main.fragment_onboarding_page.* 11 | 12 | @FragmentWithArgs 13 | class OnboardingPageFragment : BaseFragment() { 14 | @Arg(bundler = ParcelerArgsBundler::class) 15 | internal lateinit var onboardingPage: OnboardingPage 16 | 17 | override val layoutResId = R.layout.fragment_onboarding_page 18 | 19 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) { 20 | super.onViewCreated(view, savedInstanceState) 21 | title.setText(onboardingPage.title) 22 | description.setText(onboardingPage.description) 23 | 24 | @Suppress("DEPRECATION") 25 | image.setImageDrawable(view.context.resources.getDrawable(onboardingPage.image)) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /app/src/main/java/com/xmartlabs/template/ui/recyclerfragmentexample/RecyclerExampleAdapter.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.ui.recyclerfragmentexample 2 | 3 | import android.support.v7.widget.RecyclerView 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import android.widget.TextView 8 | 9 | import com.xmartlabs.template.R 10 | 11 | internal class RecyclerExampleAdapter(private val items: List) 12 | : RecyclerView.Adapter() { 13 | 14 | override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerExampleViewHolder { 15 | val view = LayoutInflater.from(parent.context).inflate(R.layout.item_recycler_example, parent, false) 16 | return RecyclerExampleViewHolder(view) 17 | } 18 | 19 | override fun onBindViewHolder(holder: RecyclerExampleViewHolder, position: Int) = holder.bind(items[position]) 20 | 21 | override fun getItemCount() = items.size 22 | 23 | internal class RecyclerExampleViewHolder(view: View) : RecyclerView.ViewHolder(view) { 24 | fun bind(string: String) = (itemView as? TextView)?.let { it.text = string } ?: Unit 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /app/src/main/java/com/xmartlabs/template/helper/GeneralObservableSubscriber.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.helper 2 | 3 | import android.support.annotation.CheckResult 4 | import android.support.annotation.StringRes 5 | 6 | import com.xmartlabs.template.ui.common.TemplateView 7 | 8 | import org.reactivestreams.Subscriber 9 | import org.reactivestreams.Subscription 10 | 11 | import java.lang.ref.WeakReference 12 | 13 | open class GeneralObservableSubscriber constructor(templateView: TemplateView? = null) : Subscriber { 14 | private val viewReference = WeakReference(templateView) 15 | 16 | override fun onSubscribe(subscription: Subscription) { } 17 | 18 | override fun onNext(t: T) { } 19 | 20 | override fun onError(throwable: Throwable) { 21 | val view = viewReference.get() 22 | if (alertOnError(throwable) && view != null && view.isViewAlive) { 23 | view.showError(throwable, getErrorMessage(throwable)) 24 | } 25 | } 26 | 27 | override fun onComplete() { } 28 | 29 | @StringRes 30 | protected open fun getErrorMessage(throwable: Throwable): Int? = null 31 | 32 | @CheckResult 33 | protected open fun alertOnError(throwable: Throwable) = true 34 | } 35 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/xmartlabs/template/common/ImmediateNewThreadScheduler.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.common 2 | 3 | import io.reactivex.Scheduler 4 | import io.reactivex.annotations.NonNull 5 | import io.reactivex.disposables.Disposable 6 | import io.reactivex.internal.subscriptions.BooleanSubscription 7 | import java.util.concurrent.TimeUnit 8 | 9 | class ImmediateNewThreadScheduler : Scheduler() { 10 | override fun createWorker() = ImmediateNewThreadWorker() 11 | 12 | // Inspired from ImmediateScheduler.InnerImmediateScheduler 13 | class ImmediateNewThreadWorker : Scheduler.Worker() { 14 | internal val innerSubscription = BooleanSubscription() 15 | 16 | override fun schedule(@NonNull run: Runnable, delay: Long, @NonNull unit: TimeUnit): Disposable { 17 | // since we are executing immediately on this thread we must cause this thread to sleep 18 | val execTime = this@ImmediateNewThreadWorker.now(unit) + unit.toMillis(delay) 19 | 20 | return schedule(SleepingAction(this, execTime, unit) { run.run() }) 21 | } 22 | 23 | override fun dispose() = innerSubscription.cancel() 24 | 25 | override fun isDisposed() = innerSubscription.isCancelled 26 | } 27 | } 28 | 29 | -------------------------------------------------------------------------------- /app/src/main/java/com/xmartlabs/template/ui/recyclerfragmentexample/RecyclerExampleFragment.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.ui.recyclerfragmentexample 2 | 3 | import android.support.annotation.LayoutRes 4 | import android.support.v7.widget.LinearLayoutManager 5 | import com.hannesdorfmann.fragmentargs.annotation.FragmentWithArgs 6 | import com.xmartlabs.template.R 7 | import com.xmartlabs.template.ui.common.TemplateFragment 8 | import kotlinx.android.synthetic.main.fragment_recycler_example.* 9 | import java.util.Locale 10 | import javax.inject.Inject 11 | 12 | @FragmentWithArgs 13 | class RecyclerExampleFragment : TemplateFragment(), RecyclerExampleView { 14 | companion object { 15 | private const val LIST_COUNT = 39 16 | } 17 | 18 | @Inject 19 | override lateinit var presenter: RecyclerExamplePresenter 20 | 21 | @LayoutRes 22 | override val layoutResId = R.layout.fragment_recycler_example 23 | 24 | override fun setup() { 25 | val strings = listOf(1..LIST_COUNT) 26 | .flatten() 27 | .map { String.format(Locale.getDefault(), "Item %d", it) } 28 | 29 | recyclerView.adapter = RecyclerExampleAdapter(strings) 30 | recyclerView.layoutManager = LinearLayoutManager(context) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | jobs: 3 | build: 4 | docker: 5 | - image: circleci/android:api-26-alpha 6 | working_directory: ~/bigbang-template 7 | environment: 8 | JVM_OPTS: -Xmx3g 9 | steps: 10 | - checkout 11 | - restore_cache: 12 | key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }} 13 | - run: 14 | name: Download Dependencies 15 | command: ./gradlew androidDependencies 16 | - save_cache: 17 | paths: 18 | - ~/.gradle 19 | key: jars-{{ checksum "build.gradle" }}-{{ checksum "app/build.gradle" }} 20 | - run: 21 | name: Check Lints 22 | command: ./gradlew clean 23 | detektCheck 24 | detektFormat 25 | lint 26 | --max-workers=2 27 | - run: 28 | name: Assemble 29 | command: ./gradlew 30 | assemble 31 | assembleAndroidTest 32 | --max-workers=2 33 | - run: 34 | name: Run tests and checks 35 | command: ./gradlew build --max-workers=2 36 | - store_artifacts: 37 | path: app/build/reports 38 | destination: reports 39 | - store_test_results: 40 | path: app/build/test-results 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/xmartlabs/template/helper/GeneralFlowableSubscriber.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.helper 2 | 3 | import android.support.annotation.CheckResult 4 | import android.support.annotation.StringRes 5 | import com.xmartlabs.template.ui.common.TemplateView 6 | import io.reactivex.FlowableSubscriber 7 | import org.reactivestreams.Subscription 8 | import java.lang.ref.WeakReference 9 | 10 | open class GeneralFlowableSubscriber constructor(templateView: TemplateView? = null) : FlowableSubscriber { 11 | protected val maxNumberOfElements = java.lang.Long.MAX_VALUE 12 | private val viewReference: WeakReference = WeakReference(templateView) 13 | 14 | override fun onSubscribe(subscription: Subscription) = subscription.request(maxNumberOfElements) 15 | 16 | override fun onNext(t: T) { } 17 | 18 | override fun onError(throwable: Throwable) { 19 | val view = viewReference.get() 20 | if (alertOnError(throwable) && view != null && view.isViewAlive) { 21 | view.showError(throwable, getErrorMessage(throwable)) 22 | } 23 | } 24 | 25 | override fun onComplete() { } 26 | 27 | @StringRes 28 | protected open fun getErrorMessage(throwable: Throwable): Int? = null 29 | 30 | @CheckResult 31 | protected open fun alertOnError(throwable: Throwable) = true 32 | } 33 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 9 | 13 | 14 | 21 | 22 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /app/src/main/java/com/xmartlabs/template/ui/login/LoginFragment.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.ui.login 2 | 3 | import android.content.Intent 4 | import android.view.View 5 | import com.hannesdorfmann.fragmentargs.annotation.FragmentWithArgs 6 | import com.xmartlabs.template.App 7 | import com.xmartlabs.template.R 8 | import com.xmartlabs.template.ui.Henson 9 | import com.xmartlabs.template.ui.common.TemplateFragment 10 | import kotlinx.android.synthetic.main.fragment_login.* 11 | import javax.inject.Inject 12 | 13 | @FragmentWithArgs 14 | class LoginFragment : TemplateFragment(), LoginView { 15 | @Inject 16 | override lateinit var presenter: LoginPresenter 17 | 18 | override val layoutResId = R.layout.fragment_login 19 | 20 | override fun setup() { 21 | loginButton.setOnClickListener { presenter.loginButtonClicked() } 22 | } 23 | 24 | override fun setIsLoading(loading: Boolean) { 25 | loginButton.visibility = if (loading) View.GONE else View.VISIBLE 26 | progressBar.visibility = if (loading) View.VISIBLE else View.GONE 27 | } 28 | 29 | override fun gotoRecyclerExampleActivity() { 30 | val intent = Henson.with(App.context) 31 | .gotoRecyclerExampleActivity() 32 | .build() 33 | .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) 34 | startActivity(intent) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/com/xmartlabs/template/ui/onboarding/OnboardingPresenter.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.ui.onboarding 2 | 3 | import com.xmartlabs.template.ui.common.TemplatePresenter 4 | import javax.inject.Inject 5 | 6 | class OnboardingPresenter @Inject constructor() : TemplatePresenter() { 7 | companion object { 8 | private val FIRST_PAGE = 0 9 | } 10 | 11 | val isLastPage: Boolean 12 | get() = currentPage == pageAdapter.count - 1 13 | 14 | @Suppress("LateinitUsage") 15 | private lateinit var pageAdapter: OnboardingPageAdapter 16 | private var currentPage = FIRST_PAGE 17 | 18 | override fun attachView(view: OnboardingView) { 19 | super.attachView(view) 20 | pageAdapter = view.createPageAdapter() 21 | view.setup(pageAdapter) 22 | } 23 | 24 | internal fun nextButtonPressed() { 25 | if (isLastPage) { 26 | skipButtonPressed() 27 | } else { 28 | currentPage++ 29 | view?.moveToPage(currentPage) 30 | handleSkipButtonVisibility() 31 | } 32 | } 33 | 34 | private fun handleSkipButtonVisibility() = view?.setSkipButtonVisibility(!isLastPage) 35 | 36 | internal fun pageChanged(newPage: Int) { 37 | currentPage = newPage 38 | view?.handleNextButtonVisibility() 39 | handleSkipButtonVisibility() 40 | } 41 | 42 | internal fun skipButtonPressed() = view?.goToLoginActivity() 43 | } 44 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/xmartlabs/template/common/SingleActivityInstrumentationTest.kt: -------------------------------------------------------------------------------- 1 | package com.xmartlabs.template.common 2 | 3 | import android.app.Activity 4 | import android.content.Intent 5 | import android.support.test.espresso.intent.rule.IntentsTestRule 6 | import android.support.test.rule.ActivityTestRule 7 | import com.xmartlabs.bigbang.core.extensions.ignoreException 8 | import com.xmartlabs.bigbang.test.extensions.performClick 9 | import com.xmartlabs.bigbang.test.helpers.EspressoUtils 10 | import org.junit.Rule 11 | import org.junit.Test 12 | 13 | /** 14 | * Created by mirland on 10/6/17. 15 | */ 16 | abstract class SingleActivityInstrumentationTest : BaseInstrumentationTest() { 17 | @Rule 18 | @JvmField 19 | var activityTestRule = createTestRule() 20 | 21 | protected val defaultIntent: Intent? = null 22 | 23 | protected fun createTestRule(): ActivityTestRule = IntentsTestRule(activityClass, true, false) 24 | 25 | @Test 26 | fun checkActivityOpens() { 27 | launchActivityWithDefaultIntent() 28 | } 29 | 30 | @Test 31 | fun checkUpNavigation() { 32 | launchActivityWithDefaultIntent() 33 | EspressoUtils.onUpButtonView().ignoreException { performClick() } 34 | } 35 | 36 | protected fun launchActivityWithDefaultIntent() { 37 | activityTestRule.launchActivity(defaultIntent) 38 | } 39 | 40 | protected abstract val activityClass: Class 41 | } 42 | 43 | -------------------------------------------------------------------------------- /app/src/main/res/layout/fragment_login.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 20 | 21 |