├── app ├── .gitignore ├── src │ ├── main │ │ ├── res │ │ │ ├── drawable-xhdpi │ │ │ │ ├── logo.png │ │ │ │ ├── ic_edit.png │ │ │ │ ├── ic_delete.png │ │ │ │ └── ic_settings.png │ │ │ ├── drawable-xxhdpi │ │ │ │ ├── logo.png │ │ │ │ ├── ic_edit.png │ │ │ │ ├── ic_delete.png │ │ │ │ └── ic_settings.png │ │ │ ├── drawable-xxxhdpi │ │ │ │ ├── logo.png │ │ │ │ ├── ic_delete.png │ │ │ │ ├── ic_edit.png │ │ │ │ └── ic_settings.png │ │ │ ├── mipmap-hdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ │ └── ic_launcher.png │ │ │ ├── layout │ │ │ │ ├── recycler_view.xml │ │ │ │ ├── pref_dialog_time.xml │ │ │ │ ├── spinner_layout.xml │ │ │ │ ├── content_home.xml │ │ │ │ ├── exercise_loading.xml │ │ │ │ ├── exercise_break_splash.xml │ │ │ │ ├── content_fragment.xml │ │ │ │ ├── home.xml │ │ │ │ ├── exercise_series.xml │ │ │ │ ├── exercises_item.xml │ │ │ │ ├── exercise_break.xml │ │ │ │ └── exercise_new.xml │ │ │ ├── drawable │ │ │ │ ├── progress_bar_background.xml │ │ │ │ ├── splash_background.xml │ │ │ │ ├── oval_accent.xml │ │ │ │ ├── oval_primary.xml │ │ │ │ └── progress_bar_accent.xml │ │ │ ├── drawable-v21 │ │ │ │ ├── oval_accent.xml │ │ │ │ └── oval_primary.xml │ │ │ ├── values │ │ │ │ ├── arrays.xml │ │ │ │ ├── dimens.xml │ │ │ │ ├── colors.xml │ │ │ │ ├── themes.xml │ │ │ │ ├── strings.xml │ │ │ │ └── styles.xml │ │ │ ├── values-pl │ │ │ │ ├── arrays.xml │ │ │ │ └── strings.xml │ │ │ ├── menu │ │ │ │ ├── home.xml │ │ │ │ └── exercise.xml │ │ │ └── xml │ │ │ │ └── settings.xml │ │ ├── java │ │ │ └── com │ │ │ │ └── morcinek │ │ │ │ └── workout │ │ │ │ ├── exercise │ │ │ │ ├── ExerciseState.kt │ │ │ │ ├── di │ │ │ │ │ ├── ExerciseComponent.kt │ │ │ │ │ └── ExerciseModule.kt │ │ │ │ ├── fragments │ │ │ │ │ ├── SeriesFragment.kt │ │ │ │ │ ├── BreakSplashFragment.kt │ │ │ │ │ ├── LoadingFragment.kt │ │ │ │ │ ├── BreakFragment.kt │ │ │ │ │ └── NewFragment.kt │ │ │ │ ├── ExerciseDataManager.kt │ │ │ │ ├── TimerService.kt │ │ │ │ └── ExerciseActivity.kt │ │ │ │ ├── common │ │ │ │ ├── firebase │ │ │ │ │ ├── data │ │ │ │ │ │ ├── DataModel.kt │ │ │ │ │ │ ├── InteractorDelegate.kt │ │ │ │ │ │ ├── DataManagerImpl.kt │ │ │ │ │ │ ├── DataManager.kt │ │ │ │ │ │ └── DataProvider.kt │ │ │ │ │ ├── database │ │ │ │ │ │ └── ReferenceManager.kt │ │ │ │ │ ├── auth │ │ │ │ │ │ └── UserManager.kt │ │ │ │ │ └── di │ │ │ │ │ │ └── modules │ │ │ │ │ │ └── FirebaseModule.kt │ │ │ │ ├── di │ │ │ │ │ ├── ActivityScope.kt │ │ │ │ │ ├── DaggerUtils.kt │ │ │ │ │ ├── modules │ │ │ │ │ │ ├── ApplicationModule.kt │ │ │ │ │ │ └── AndroidModule.kt │ │ │ │ │ └── ApplicationComponent.kt │ │ │ │ ├── utils │ │ │ │ │ ├── FragmentUtils.kt │ │ │ │ │ ├── CalendarUtils.kt │ │ │ │ │ ├── AndroidUtils.kt │ │ │ │ │ ├── StartActivityUtils.kt │ │ │ │ │ ├── IntentUtils.kt │ │ │ │ │ └── SharedPreferencesUtils.kt │ │ │ │ ├── FunctionalCountDownTimer.kt │ │ │ │ ├── preference │ │ │ │ │ ├── TimePreference.kt │ │ │ │ │ └── TimePreferenceDialogFragmentCompat.kt │ │ │ │ ├── fragment │ │ │ │ │ ├── BaseFragment.kt │ │ │ │ │ ├── ContentFragmentManager.kt │ │ │ │ │ └── SingleFragmentActivity.kt │ │ │ │ └── NotificationCenter.kt │ │ │ │ ├── core │ │ │ │ └── data │ │ │ │ │ ├── exercises │ │ │ │ │ ├── ExercisesProvider.kt │ │ │ │ │ ├── ExerciseManager.kt │ │ │ │ │ └── ExerciseDataModel.kt │ │ │ │ │ ├── DataUtils.kt │ │ │ │ │ └── DataModule.kt │ │ │ │ ├── Application.kt │ │ │ │ ├── settings │ │ │ │ ├── SettingsPreferencesUtils.kt │ │ │ │ └── SettingsFragment.kt │ │ │ │ ├── splash │ │ │ │ └── SplashActivity.kt │ │ │ │ └── home │ │ │ │ ├── exercises │ │ │ │ ├── adapter │ │ │ │ │ └── ExerciseViewAdapter.kt │ │ │ │ ├── ExercisesFragment.kt │ │ │ │ └── ExercisesInteractor.kt │ │ │ │ └── HomeActivity.kt │ │ └── AndroidManifest.xml │ ├── debug │ │ └── res │ │ │ └── values │ │ │ └── strings.xml │ └── test │ │ └── java │ │ └── com │ │ └── morcinek │ │ └── workout │ │ └── ExampleUnitTest.java ├── proguard-rules.pro └── build.gradle ├── settings.gradle ├── config └── debug.keystore ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── .gitignore ├── gradle.properties ├── gradlew.bat ├── gradlew └── README.md /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /config/debug.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmorcinek/kotlin-android-tutorial/HEAD/config/debug.keystore -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmorcinek/kotlin-android-tutorial/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmorcinek/kotlin-android-tutorial/HEAD/app/src/main/res/drawable-xhdpi/logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmorcinek/kotlin-android-tutorial/HEAD/app/src/main/res/drawable-xxhdpi/logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmorcinek/kotlin-android-tutorial/HEAD/app/src/main/res/drawable-xxxhdpi/logo.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmorcinek/kotlin-android-tutorial/HEAD/app/src/main/res/drawable-xhdpi/ic_edit.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmorcinek/kotlin-android-tutorial/HEAD/app/src/main/res/drawable-xxhdpi/ic_edit.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmorcinek/kotlin-android-tutorial/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/debug/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Workout-Debug 4 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmorcinek/kotlin-android-tutorial/HEAD/app/src/main/res/drawable-xhdpi/ic_delete.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmorcinek/kotlin-android-tutorial/HEAD/app/src/main/res/drawable-xhdpi/ic_settings.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmorcinek/kotlin-android-tutorial/HEAD/app/src/main/res/drawable-xxhdpi/ic_delete.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmorcinek/kotlin-android-tutorial/HEAD/app/src/main/res/drawable-xxxhdpi/ic_delete.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_edit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmorcinek/kotlin-android-tutorial/HEAD/app/src/main/res/drawable-xxxhdpi/ic_edit.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmorcinek/kotlin-android-tutorial/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmorcinek/kotlin-android-tutorial/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmorcinek/kotlin-android-tutorial/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/ 5 | .DS_Store 6 | /build 7 | /captures 8 | .externalNativeBuild 9 | google-services.json 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmorcinek/kotlin-android-tutorial/HEAD/app/src/main/res/drawable-xxhdpi/ic_settings.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tmorcinek/kotlin-android-tutorial/HEAD/app/src/main/res/drawable-xxxhdpi/ic_settings.png -------------------------------------------------------------------------------- /app/src/main/java/com/morcinek/workout/exercise/ExerciseState.kt: -------------------------------------------------------------------------------- 1 | package com.morcinek.workout.exercise 2 | 3 | enum class ExerciseState { 4 | Series, Break, Splash, New, Loading 5 | } 6 | -------------------------------------------------------------------------------- /app/src/main/java/com/morcinek/workout/common/firebase/data/DataModel.kt: -------------------------------------------------------------------------------- 1 | package com.morcinek.workout.common.firebase.data 2 | 3 | interface DataModel { 4 | 5 | fun toMap(): Map 6 | } -------------------------------------------------------------------------------- /app/src/main/java/com/morcinek/workout/common/di/ActivityScope.kt: -------------------------------------------------------------------------------- 1 | package com.morcinek.workout.common.di 2 | 3 | import javax.inject.Scope 4 | 5 | @Scope 6 | @Retention 7 | annotation class ActivityScope -------------------------------------------------------------------------------- /app/src/main/java/com/morcinek/workout/common/firebase/data/InteractorDelegate.kt: -------------------------------------------------------------------------------- 1 | package com.morcinek.workout.common.firebase.data 2 | 3 | interface InteractorDelegate { 4 | fun success(values: T) 5 | fun failed(errorMessage: String) 6 | } -------------------------------------------------------------------------------- /app/src/main/java/com/morcinek/workout/common/firebase/data/DataManagerImpl.kt: -------------------------------------------------------------------------------- 1 | package com.morcinek.workout.common.firebase.data 2 | 3 | import com.google.firebase.database.DatabaseReference 4 | 5 | open class DataManagerImpl(override val reference: DatabaseReference) : DataManager() -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Dec 02 10:30:21 CET 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.1-all.zip 7 | -------------------------------------------------------------------------------- /app/src/main/res/layout/recycler_view.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | -------------------------------------------------------------------------------- /app/src/main/java/com/morcinek/workout/common/utils/FragmentUtils.kt: -------------------------------------------------------------------------------- 1 | package com.morcinek.workout.common.utils 2 | 3 | import android.support.v7.app.AppCompatActivity 4 | 5 | fun android.support.v4.app.Fragment.setTitle(resourceId: Int) { 6 | (activity as AppCompatActivity).supportActionBar!!.setTitle(resourceId) 7 | } 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/progress_bar_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/oval_accent.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/oval_primary.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/splash_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/pref_dialog_time.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/layout/spinner_layout.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/values/arrays.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Chest 6 | Back 7 | Shoulders 8 | Biceps 9 | Triceps 10 | Legs 11 | Calves 12 | Forearms 13 | Abs 14 | 15 | -------------------------------------------------------------------------------- /app/src/main/res/values-pl/arrays.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Klatka 6 | Plecy 7 | Barki 8 | Biceps 9 | Triceps 10 | Nogi 11 | Łydki 12 | Przedramiona 13 | Brzuch 14 | 15 | -------------------------------------------------------------------------------- /app/src/test/java/com/morcinek/workout/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package com.morcinek.workout; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /app/src/main/java/com/morcinek/workout/core/data/exercises/ExercisesProvider.kt: -------------------------------------------------------------------------------- 1 | package com.morcinek.workout.core.data.exercises 2 | 3 | import com.morcinek.workout.common.firebase.data.DataProvider 4 | import com.morcinek.workout.common.firebase.database.ReferenceManager 5 | 6 | class ExercisesProvider(private val referenceManager: ReferenceManager) : DataProvider() { 7 | 8 | override val type = ExerciseDataModel::class.java 9 | 10 | override val reference by lazy { referenceManager.exercises() } 11 | } -------------------------------------------------------------------------------- /app/src/main/res/menu/home.xml: -------------------------------------------------------------------------------- 1 | 5 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/oval_accent.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/oval_primary.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /app/src/main/java/com/morcinek/workout/common/utils/CalendarUtils.kt: -------------------------------------------------------------------------------- 1 | package com.morcinek.workout.common.utils 2 | 3 | import com.morcinek.workout.BuildConfig 4 | import java.text.SimpleDateFormat 5 | import java.util.* 6 | 7 | /** 8 | * Copyright 2016 Tomasz Morcinek. All rights reserved. 9 | */ 10 | 11 | fun Calendar.formatWith(dateFormat: SimpleDateFormat) = dateFormat.format(time) 12 | 13 | fun dateFormat() = SimpleDateFormat(BuildConfig.DATE_FORMAT) 14 | fun timeFormat() = SimpleDateFormat(BuildConfig.TIME_FORMAT) 15 | fun dayOfWeekFormat() = SimpleDateFormat(BuildConfig.DAY_OF_WEEK_FORMAT) 16 | -------------------------------------------------------------------------------- /app/src/main/res/layout/content_home.xml: -------------------------------------------------------------------------------- 1 | 2 | 12 | -------------------------------------------------------------------------------- /app/src/main/java/com/morcinek/workout/common/di/DaggerUtils.kt: -------------------------------------------------------------------------------- 1 | package com.morcinek.workout.common.di 2 | 3 | import android.app.Service 4 | import android.support.v4.app.Fragment 5 | import android.support.v7.app.AppCompatActivity 6 | import com.morcinek.workout.Application 7 | 8 | val AppCompatActivity.component: ApplicationComponent 9 | get() = (application as Application).component 10 | 11 | val Fragment.component: ApplicationComponent 12 | get() = (activity.application as Application).component 13 | 14 | val Service.component: ApplicationComponent 15 | get() = (application as Application).component 16 | -------------------------------------------------------------------------------- /app/src/main/java/com/morcinek/workout/core/data/DataUtils.kt: -------------------------------------------------------------------------------- 1 | package com.morcinek.workout.core.data 2 | 3 | import android.content.Intent 4 | import android.os.Bundle 5 | import android.os.Parcelable 6 | import com.morcinek.workout.common.utils.putParcelableExtra 7 | 8 | fun Intent.putKeyExtra(value: String) = putExtra("key", value) 9 | fun Intent.getKeyExtra() : String? = getStringExtra("key") 10 | 11 | fun Bundle.putKey(value: String) = putString("key", value) 12 | 13 | inline fun Intent.putPair(pair: Pair) { 14 | putKeyExtra(pair.first) 15 | putParcelableExtra(pair.second) 16 | } -------------------------------------------------------------------------------- /app/src/main/java/com/morcinek/workout/common/firebase/data/DataManager.kt: -------------------------------------------------------------------------------- 1 | package com.morcinek.workout.common.firebase.data 2 | 3 | import com.google.firebase.database.DatabaseReference 4 | 5 | abstract class DataManager { 6 | 7 | protected abstract val reference: DatabaseReference 8 | 9 | var key: String? = null 10 | 11 | private val objectReference by lazy { if (key != null) reference.child(key) else reference.push() } 12 | 13 | fun update(data: DataModel) = objectReference.updateChildren(data.toMap()) 14 | 15 | fun remove() = objectReference.removeValue() 16 | 17 | fun get() = objectReference 18 | } -------------------------------------------------------------------------------- /app/src/main/java/com/morcinek/workout/core/data/exercises/ExerciseManager.kt: -------------------------------------------------------------------------------- 1 | package com.morcinek.workout.core.data.exercises 2 | 3 | import com.morcinek.workout.common.firebase.data.DataManagerImpl 4 | import com.morcinek.workout.common.firebase.database.ReferenceManager 5 | 6 | class ExerciseManager(referenceManager: ReferenceManager) : DataManagerImpl(referenceManager.exercises()){ 7 | 8 | fun setName(name: String) = get().child("name").setValue(name) 9 | fun setCategory(category: String) = get().child("category").setValue(category) 10 | fun setNumberOfSeries(numberOfSeries: Int) = get().child("numberOfSeries").setValue(numberOfSeries) 11 | } -------------------------------------------------------------------------------- /app/src/main/java/com/morcinek/workout/common/firebase/database/ReferenceManager.kt: -------------------------------------------------------------------------------- 1 | package com.morcinek.workout.common.firebase.database 2 | 3 | import com.google.firebase.auth.FirebaseAuth 4 | import com.google.firebase.database.FirebaseDatabase 5 | 6 | class ReferenceManager(val firebaseAuth: FirebaseAuth, val firebaseDatabase: FirebaseDatabase) { 7 | 8 | init { 9 | firebaseDatabase.setPersistenceEnabled(true) 10 | } 11 | 12 | private val userId: String 13 | get() = firebaseAuth.currentUser!!.uid 14 | 15 | fun exercises() = firebaseDatabase.getReference("data") 16 | .child(userId) 17 | .child("exercises") 18 | } -------------------------------------------------------------------------------- /app/src/main/java/com/morcinek/workout/common/di/modules/ApplicationModule.kt: -------------------------------------------------------------------------------- 1 | package com.morcinek.workout.common.di.modules 2 | 3 | import android.content.Context 4 | import android.content.SharedPreferences 5 | import android.os.PowerManager 6 | import android.os.Vibrator 7 | import com.morcinek.workout.common.NotificationCenter 8 | import dagger.Module 9 | import dagger.Provides 10 | 11 | @Module 12 | class ApplicationModule() { 13 | 14 | @Provides 15 | fun provideNotificationCenter(context: Context, vibrator: Vibrator, powerManager: PowerManager, sharedPreferences: SharedPreferences) 16 | = NotificationCenter(context, vibrator, powerManager, sharedPreferences) 17 | } -------------------------------------------------------------------------------- /app/src/main/java/com/morcinek/workout/exercise/di/ExerciseComponent.kt: -------------------------------------------------------------------------------- 1 | package com.morcinek.workout.exercise.di 2 | 3 | import com.morcinek.workout.common.di.ActivityScope 4 | import com.morcinek.workout.exercise.ExerciseActivity 5 | import com.morcinek.workout.exercise.fragments.* 6 | import dagger.Subcomponent 7 | 8 | @ActivityScope 9 | @Subcomponent(modules = arrayOf(ExerciseModule::class)) 10 | interface ExerciseComponent { 11 | 12 | fun inject(activity: ExerciseActivity) 13 | 14 | fun inject(fragment: SeriesFragment) 15 | fun inject(fragment: BreakFragment) 16 | fun inject(fragment: BreakSplashFragment) 17 | fun inject(fragment: LoadingFragment) 18 | fun inject(fragment: NewFragment) 19 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/progress_bar_accent.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 10 | 11 | 17 | 18 | -------------------------------------------------------------------------------- /app/src/main/res/layout/exercise_loading.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /app/src/main/java/com/morcinek/workout/common/utils/AndroidUtils.kt: -------------------------------------------------------------------------------- 1 | package com.morcinek.workout.common.utils 2 | 3 | import android.app.Activity 4 | import android.app.Service 5 | import android.content.BroadcastReceiver 6 | import android.content.Context 7 | import android.content.Intent 8 | 9 | inline fun Activity.stopService() { 10 | stopService(Intent(this, T::class.java)) 11 | } 12 | 13 | inline fun broadcastReceiver(crossinline onReceiveFunction: (Intent) -> Unit) = object : BroadcastReceiver() { 14 | override fun onReceive(context: Context, intent: Intent) = onReceiveFunction(intent) 15 | } 16 | 17 | fun handleOptionItemAction(function: () -> Any): Boolean { 18 | function() 19 | return true 20 | } 21 | -------------------------------------------------------------------------------- /app/src/main/java/com/morcinek/workout/common/utils/StartActivityUtils.kt: -------------------------------------------------------------------------------- 1 | package com.morcinek.workout.common.utils 2 | 3 | import android.app.Activity 4 | import android.content.Context 5 | import android.content.Intent 6 | import org.jetbrains.anko.internals.AnkoInternals 7 | 8 | inline fun Context.startActivity(vararg params: Any) { 9 | val arrayOfPairs = params.map { Pair(it.javaClass.name, it) }.toTypedArray() 10 | AnkoInternals.internalStartActivity(this, T::class.java, arrayOfPairs) 11 | } 12 | 13 | inline fun Activity.startActivityFun(function: Intent.() -> Any) { 14 | val intent = AnkoInternals.createIntent(this, T::class.java, emptyArray()) 15 | function(intent) 16 | startActivity(intent) 17 | } 18 | -------------------------------------------------------------------------------- /app/src/main/java/com/morcinek/workout/core/data/DataModule.kt: -------------------------------------------------------------------------------- 1 | package com.morcinek.workout.core.data 2 | 3 | import android.content.Context 4 | import com.morcinek.workout.common.firebase.database.ReferenceManager 5 | import com.morcinek.workout.common.firebase.di.modules.FirebaseModule 6 | import com.morcinek.workout.core.data.exercises.ExercisesProvider 7 | import com.morcinek.workout.home.exercises.ExercisesInteractor 8 | import dagger.Module 9 | import dagger.Provides 10 | 11 | @Module(includes = arrayOf(FirebaseModule::class)) 12 | class DataModule { 13 | 14 | @Provides 15 | fun provideExercisesProvider(referenceManager: ReferenceManager) = ExercisesProvider(referenceManager) 16 | 17 | @Provides 18 | fun provideExercisesInteractor(context: Context, exercisesProvider: ExercisesProvider) = ExercisesInteractor(context, exercisesProvider) 19 | } 20 | -------------------------------------------------------------------------------- /app/src/main/java/com/morcinek/workout/common/FunctionalCountDownTimer.kt: -------------------------------------------------------------------------------- 1 | package com.morcinek.workout.common 2 | 3 | import android.os.CountDownTimer 4 | 5 | class FunctionalCountDownTimer(millisInFuture: Long, countDownInterval: Long) : CountDownTimer(millisInFuture, countDownInterval) { 6 | 7 | private var onFinishFunction: (() -> Unit)? = null 8 | private var onTickFunction: ((Long) -> Unit)? = null 9 | 10 | fun onFinish(onFinishFunction: () -> Unit) { 11 | this.onFinishFunction = onFinishFunction 12 | } 13 | 14 | fun onTick(onTickFunction: (Long) -> Unit) { 15 | this.onTickFunction = onTickFunction 16 | } 17 | 18 | override fun onFinish() { 19 | onFinishFunction?.invoke() 20 | } 21 | 22 | override fun onTick(millisUntilFinished: Long) { 23 | onTickFunction?.invoke(millisUntilFinished) 24 | } 25 | } -------------------------------------------------------------------------------- /app/src/main/java/com/morcinek/workout/common/firebase/auth/UserManager.kt: -------------------------------------------------------------------------------- 1 | package com.morcinek.workout.common.firebase.auth 2 | 3 | import com.google.firebase.auth.AuthCredential 4 | import com.google.firebase.auth.FirebaseAuth 5 | import com.google.firebase.auth.FirebaseUser 6 | 7 | class UserManager(private val firebaseAuth: FirebaseAuth) { 8 | 9 | val currentUser: FirebaseUser? 10 | get() = firebaseAuth.currentUser 11 | 12 | val isUserAuthenticated: Boolean 13 | get() = currentUser != null 14 | 15 | fun signInAnonymously() = firebaseAuth.signInAnonymously() 16 | 17 | fun connectWithCredentials(authCredential: AuthCredential) = 18 | if (isUserAuthenticated) { 19 | currentUser!!.linkWithCredential(authCredential) 20 | } else { 21 | firebaseAuth.signInWithCredential(authCredential) 22 | } 23 | } -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 16dp 3 | 100dp 4 | 42dp 5 | 32dp 6 | 7 | 8dp 8 | 12dp 9 | 16dp 10 | 11 | 24dp 12 | 72dp 13 | 14 | 260dp 15 | 68sp 16 | 17 | 22sp 18 | 19 | 15sp 20 | 18sp 21 | 12sp 22 | 13sp 23 | 24 | 25 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #2374AB 4 | #1A557D 5 | #FF8484 6 | #D16D6D 7 | 8 | #FFFFFF 9 | #FEFEFE 10 | 11 | 12 | #DD000000 13 | #89000000 14 | #42000000 15 | 16 | 17 | #FFFFFF 18 | #B2FFFFFF 19 | #4CFFFFFF 20 | 21 | #4DCCBD 22 | #104259 23 | 24 | -------------------------------------------------------------------------------- /app/src/main/java/com/morcinek/workout/common/preference/TimePreference.kt: -------------------------------------------------------------------------------- 1 | package com.morcinek.workout.common.preference 2 | 3 | import android.content.Context 4 | import android.content.res.TypedArray 5 | import android.support.v7.preference.DialogPreference 6 | import android.util.AttributeSet 7 | import com.morcinek.workout.R 8 | 9 | class TimePreference(context: Context?, attrs: AttributeSet?) : DialogPreference(context, attrs) { 10 | 11 | var time: Int = 0 12 | set(value) { 13 | field = value 14 | persistInt(time) 15 | } 16 | 17 | override fun getDialogLayoutResource() = R.layout.pref_dialog_time 18 | 19 | override fun onGetDefaultValue(a: TypedArray?, index: Int): Any? = a?.getInt(index, 0) 20 | 21 | override fun onSetInitialValue(restorePersistedValue: Boolean, defaultValue: Any?) { 22 | time = if (restorePersistedValue) getPersistedInt(time) else defaultValue as Int 23 | } 24 | } -------------------------------------------------------------------------------- /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 | 19 | DEBUG_STORE_FILE = ../config/debug.keystore 20 | DEBUG_STORE_PASSWORD = android 21 | DEBUG_KEY_ALIAS = androiddebugkey 22 | DEBUG_KEY_PASSWORD = android 23 | -------------------------------------------------------------------------------- /app/src/main/java/com/morcinek/workout/common/di/modules/AndroidModule.kt: -------------------------------------------------------------------------------- 1 | package com.morcinek.workout.common.di.modules 2 | 3 | import android.app.Application 4 | import android.content.Context 5 | import android.os.PowerManager 6 | import android.os.Vibrator 7 | import dagger.Module 8 | import dagger.Provides 9 | import org.jetbrains.anko.defaultSharedPreferences 10 | import javax.inject.Singleton 11 | 12 | @Module 13 | class AndroidModule(private val application: Application) { 14 | 15 | @Provides 16 | fun provideApplicationContext(): Context = application 17 | 18 | @Provides 19 | @Singleton 20 | fun providePowerManager() = application.getSystemService(Context.POWER_SERVICE) as PowerManager 21 | 22 | @Provides 23 | @Singleton 24 | fun provideVibrator() = application.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator 25 | 26 | @Provides 27 | @Singleton 28 | fun provideSharedPreferences() = application.defaultSharedPreferences 29 | } 30 | -------------------------------------------------------------------------------- /app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | 18 | 19 | 12 | 17 | 18 | 24 | 25 | 32 | 33 | 38 | 39 | 44 | 45 | 50 | 51 | 56 | 57 | 62 | 63 | 72 | 73 | -------------------------------------------------------------------------------- /app/src/main/java/com/morcinek/workout/exercise/ExerciseActivity.kt: -------------------------------------------------------------------------------- 1 | package com.morcinek.workout.exercise 2 | 3 | import android.content.IntentFilter 4 | import android.os.Bundle 5 | import android.support.v7.app.AppCompatActivity 6 | import android.view.Menu 7 | import android.view.MenuItem 8 | import com.morcinek.workout.R 9 | import com.morcinek.workout.common.NotificationCenter 10 | import com.morcinek.workout.common.di.component 11 | import com.morcinek.workout.common.fragment.BaseFragment 12 | import com.morcinek.workout.common.fragment.ContentFragmentManager 13 | import com.morcinek.workout.common.fragment.SingleFragmentActivity 14 | import com.morcinek.workout.common.utils.* 15 | import com.morcinek.workout.core.data.exercises.ExerciseManager 16 | import com.morcinek.workout.core.data.getKeyExtra 17 | import com.morcinek.workout.exercise.di.ExerciseComponent 18 | import com.morcinek.workout.exercise.di.ExerciseModule 19 | import com.morcinek.workout.exercise.fragments.* 20 | import com.morcinek.workout.settings.SettingsFragment 21 | import kotlinx.android.synthetic.main.content_fragment.* 22 | import org.jetbrains.anko.alert 23 | import javax.inject.Inject 24 | 25 | class ExerciseActivity : AppCompatActivity(), ExerciseDataManager.Delegate { 26 | 27 | @Inject lateinit var contentFragmentManager: ContentFragmentManager 28 | 29 | @Inject lateinit var exerciseDataManager: ExerciseDataManager 30 | @Inject lateinit var exerciseManager: ExerciseManager 31 | @Inject lateinit var notificationCenter: NotificationCenter 32 | 33 | val key by lazy { intent.getKeyExtra() } 34 | 35 | val exerciseComponent by lazy { component.add(ExerciseModule(this)) } 36 | 37 | override fun onCreate(savedInstanceState: Bundle?) { 38 | super.onCreate(savedInstanceState) 39 | setContentView(R.layout.content_fragment) 40 | exerciseComponent.inject(this) 41 | setupToolbar() 42 | registerReceiver(timerReceiver, IntentFilter(TIMER_SERVICE_FINISH)) 43 | } 44 | 45 | override fun onDestroy() { 46 | super.onDestroy() 47 | unregisterReceiver(timerReceiver) 48 | stopService() 49 | } 50 | 51 | private fun setupToolbar() { 52 | setSupportActionBar(toolbar) 53 | supportActionBar?.setDisplayHomeAsUpEnabled(true) 54 | } 55 | 56 | override fun onCreateOptionsMenu(menu: Menu): Boolean { 57 | menuInflater.inflate(R.menu.exercise, menu) 58 | return true 59 | } 60 | 61 | override fun onPrepareOptionsMenu(menu: Menu?): Boolean { 62 | menu?.findItem(R.id.action_edit)?.isVisible = exerciseDataManager.isEditable 63 | return super.onPrepareOptionsMenu(menu) 64 | } 65 | 66 | override fun onStateChanged(exerciseState: ExerciseState) { 67 | contentFragmentManager.replaceFragment(when (exerciseState) { 68 | ExerciseState.Series -> SeriesFragment() 69 | ExerciseState.Break -> BreakFragment() 70 | ExerciseState.Splash -> BreakSplashFragment() 71 | ExerciseState.New -> NewFragment() 72 | ExerciseState.Loading -> LoadingFragment() 73 | }) 74 | invalidateOptionsMenu() 75 | } 76 | 77 | override fun onResume() { 78 | super.onResume() 79 | exerciseDataManager.delegate = this 80 | } 81 | 82 | override fun onPause() { 83 | super.onPause() 84 | exerciseDataManager.delegate = null 85 | } 86 | 87 | private val timerReceiver = broadcastReceiver { 88 | notificationCenter.sendNotifications() 89 | exerciseDataManager.incrementSeriesNumber() 90 | exerciseDataManager.showBreakSplash() 91 | } 92 | 93 | override fun onOptionsItemSelected(item: MenuItem?) = when (item?.itemId) { 94 | android.R.id.home -> handleOptionItemAction { onBackPressed() } 95 | R.id.action_edit -> handleOptionItemAction { exerciseDataManager.showNew() } 96 | R.id.action_delete -> handleOptionItemAction { 97 | alert(R.string.exercise_exit_message) { 98 | positiveButton(R.string.yes) { finishWithDelete() } 99 | negativeButton(R.string.no) { } 100 | }.show() 101 | } 102 | R.id.action_settings -> handleOptionItemAction { 103 | startActivityFun { putSerializableExtra(SettingsFragment::class.java) } 104 | } 105 | 106 | else -> super.onOptionsItemSelected(item) 107 | } 108 | 109 | 110 | override fun onBackPressed() { 111 | if (key == null) { 112 | alert(R.string.exercise_exit_message) { 113 | positiveButton(R.string.yes) { saveAndFinish() } 114 | negativeButton(R.string.no) { finishWithDelete() } 115 | }.show() 116 | } else { 117 | saveAndFinish() 118 | } 119 | } 120 | 121 | private fun finishWithDelete() { 122 | exerciseManager.remove() 123 | finish() 124 | } 125 | 126 | private fun saveAndFinish() { 127 | exerciseManager.update(exerciseDataManager.exerciseDataModel) 128 | finish() 129 | } 130 | } 131 | 132 | inline val BaseFragment.exerciseComponent: ExerciseComponent 133 | get() = (activity as ExerciseActivity).exerciseComponent 134 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Kotlin Android Tutorial 2 | ================ 3 | 4 | 5 | ## Content 6 | 7 | This is a tutorial project to show how to setup and use Kotlin in Android Project. 8 | I have recently started going to the gym, and I found out that it would be easier if I had 9 | application to assist me. I am calling this application 'Workout'. 10 | 11 | Work on this application goes in steps, each step either integrates some new functionality 12 | or demonstrates usage of specific feature of Kotlin Language. 13 | 14 | Each step has its own tag (name in the round brackets), and a corresponding video on Youtube. 15 | 16 | ### 1 Project Setup (project-setup) 17 | 18 | https://youtu.be/zfmT8fVlMhY 19 | 20 | - Creating new Project in Android Studio 21 | - Initialising GIT 22 | 23 | 24 | ### 2 Kotlin Integration (kotlin-integration) 25 | 26 | https://youtu.be/8HuBoPYb0gw 27 | 28 | - Adding Kotlin plugins to build.gradle 29 | - Converting Java Code into Kotlin Code 30 | 31 | 32 | ### 3 Kotlin View Injections (view-injections) 33 | 34 | https://youtu.be/cs4YZg3P4Ik 35 | 36 | - Adding Kotlin Android Extensions Plugin 37 | `apply plugin: 'kotlin-android-extensions'` 38 | - Using auto-generated properties for views in View Class (Activity) 39 | `import kotlinx.android.synthetic.main.home.*` 40 | 41 | 42 | ### 4 Anko Integration (anko-integration) 43 | 44 | https://youtu.be/SG77qTm38nw 45 | 46 | - Code Cleanup 47 | - Adding Anko library to the project 48 | `compile "org.jetbrains.anko:anko:$versions.anko"` 49 | - Examples of one line functions (Kotlin introduces specific syntax) 50 | `fun isThisAJoke() = "NO Its not a Joke"` 51 | 52 | 53 | ### 5 Splash Screen (splash-screen) 54 | 55 | https://youtu.be/kUI1LRmPCBo 56 | 57 | - Adding Anko Design library to the project 58 | `compile "org.jetbrains.anko:anko-design:$versions.anko"` 59 | - Examples of Anko usage: 60 | `snackbar` 61 | `doAsync` 62 | `uiThread` 63 | `startActivity()` 64 | 65 | 66 | ### 6 Splash Screen according to Google Guidelines (google-splash) 67 | 68 | https://youtu.be/r9KPvkAk0BM 69 | 70 | - Implementation of Splash Screen to enhance User Experience 71 | - Based on: https://www.bignerdranch.com/blog/splash-screens-the-right-way/ 72 | 73 | 74 | ### 7 Dagger Integration (dagger-integration) 75 | 76 | https://youtu.be/F0OwtSrhh0o 77 | 78 | - Integration of dagger into Android Kotlin Project. 79 | - Creation and usage of 'Extension Function' 80 | `fun AppCompatActivity.component() : ApplicationComponent = (application as Application).component` 81 | - Creation and usage of 'Extension Property' 82 | `val AppCompatActivity.component: ApplicationComponent 83 | get() = (application as Application).component` 84 | 85 | 86 | ### 8 Counter Initial (counter-initial) 87 | 88 | https://youtu.be/v7pe5JN_eLs 89 | 90 | - Using CountDownTimer for executing task after interval. 91 | - Introduction to 'val' property 92 | `val name: String = "tomasz" ` 93 | - Introduction to 'var' property 94 | `@Inject lateinit var context: Context` 95 | - Introduction to anonymous class in kotlin 96 | `val timer = object : CountDownTimer(...` 97 | 98 | 99 | ### 9 Lateinit, Nullability (lateinit-nullability) 100 | 101 | https://youtu.be/0N72ZisrwSE 102 | 103 | - Lateinit var property 104 | `lateinit var onFinishFunction: () -> Unit)` 105 | - Nullability in practice 106 | `private var onFinishFunction: (() -> Unit)? = null` 107 | - Function/Lambda as a property 108 | `lateinit var onTickFunction: (Long) -> Unit` 109 | - Function/Lambda as a method argument 110 | `fun onFinish(onFinishFunction: () -> Unit) {` 111 | - Invocation of Function/Lambda 112 | `onFinishFunction.invoke()` 113 | `onFinishFunction()` 114 | 115 | 116 | ### 10 Dagger Injections, Val Property (dagger-val-property) 117 | 118 | https://youtu.be/DXamJSAHyGQ 119 | 120 | - Adding new Dagger Module 121 | - Initializing component with two Modules 122 | - Creating Singleton object in Module 123 | - Val Property Lazy Initialization 124 | `private val ringtone by lazy {` 125 | ` RingtoneManager.getRingtone(context, RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION))` 126 | `}` 127 | - Val Property with Getter 128 | `private val flags: Int` 129 | `get() = PowerManager.SCREEN_BRIGHT_WAKE_LOCK or PowerManager.ACQUIRE_CAUSES_WAKEUP` 130 | 131 | 132 | ### 11 Method with Default Arguments (default-arguments-method) 133 | 134 | https://youtu.be/mbXjX3xeOL0 135 | 136 | - Method with Default Arguments 137 | ` fun replaceFragment(fragment: Fragment, tag: String = fragment.javaClass.name, addToBackStack: Boolean = false) {` 138 | - Constructor with Default Arguments 139 | `class ContentFragmentManager(private val activity: FragmentActivity, private val contentLayoutId: Int = R.id.contentFrame) {` 140 | - Showing Fragment inside Activity 141 | - Creating `ContentFragmentManager` to replace fragments in `FrameLayout` 142 | 143 | 144 | ### 12 Abstract Property, Open Modifier (abstract-open-property) 145 | 146 | https://youtu.be/W08I-X7fWVw 147 | 148 | - Creating BaseFragment 149 | - Creating Abstract Property 150 | `protected abstract val layoutResourceId: Int` 151 | - Creating Open Property 152 | `open protected val menuResourceId: Int? = null` 153 | - Making 'Java Virtual method' final 154 | `final override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?)` 155 | 156 | 157 | ### 13 Custom Property Setter (custom-property-setter) 158 | 159 | https://youtu.be/xyzYjlaSkAs 160 | 161 | - Creating Custom Property Setter 162 | ` var delegate: Delegate? = null` 163 | ` set(value) {` 164 | ` field = value` 165 | ` notifyStateChanged()` 166 | ` }` 167 | - Creating Data Model 168 | - Creating Data Manager 169 | 170 | 171 | ### 14 String Templates (string-templates) 172 | 173 | https://youtu.be/jM3YmULqUcQ 174 | 175 | - String Template with value 176 | `timerText.text = "$value"` 177 | - String Template with expression 178 | `seriesNumberText.text = "${exerciseDataManager.exerciseData.seriesNumber}"` 179 | 180 | 181 | ### 15 Elvis Operator, Equality (elvis-operator-equality) 182 | 183 | https://youtu.be/Wn9-JarDvok 184 | 185 | - Using Elvis Operator 186 | `fragmentManager.fragments.firstOrNull { it?.isVisible ?: false }` 187 | - Using equality '==' sign correctly 188 | `if (currentFragment?.javaClass != fragment.javaClass) {` 189 | 190 | 191 | ### 16 Dagger Subcomponent, Custom Scope (dagger-subcomponent) 192 | 193 | https://youtu.be/xRRRujQly-0 194 | 195 | - Creating Dagger Module for specific Activity 196 | - Creating Dagger Subcomponent 197 | `@Subcomponent(modules = arrayOf(ExerciseModule::class))` 198 | - Creating Custom Scope to be used by Subcomponent 199 | `annotation class ActivityScope` 200 | 201 | 202 | ### 17 Anko Alert Dialog (anko-alert-dialog) 203 | 204 | https://youtu.be/qOxt4xOas2M 205 | 206 | - Creating Alert Dialog using Anko Library 207 | 208 | 209 | ## Useful links 210 | 211 | [Kotlin.org] (https://kotlinlang.org/) 212 | 213 | [Kotlink.Anko] (https://github.com/Kotlin/anko/) 214 | 215 | [Dagger] (https://github.com/google/dagger) 216 | 217 | 218 | ## Developed By 219 | 220 | Tomasz Morcinek 221 | tomasz.morcinek@gmail.com 222 | tomasz.morcinek.dev@gmail.com 223 | 224 | 225 | Follow me on Google+ 227 | 228 | 229 | Checkout my Applications in Google Play 231 | 232 | 233 | 234 | ## License 235 | 236 | You are free to do whatever you want with it. 237 | --------------------------------------------------------------------------------