diff --git a/kotlin-base/.gitignore b/kotlin-base/.gitignore
new file mode 100644
index 0000000..90e5879
--- /dev/null
+++ b/kotlin-base/.gitignore
@@ -0,0 +1,13 @@
+.gradle
+/local.properties
+/.idea/workspace.xml
+.DS_Store
+.idea/
+*.iml
+build/
+.gradletasknamecache
+
+crashlytics.properties
+crashlytics-build.properties
+com_crashlytics_export_strings.xml
+dagger-proguard-keepnames.cfg
\ No newline at end of file
diff --git a/kotlin-base/.travis.yml b/kotlin-base/.travis.yml
new file mode 100644
index 0000000..07dfa37
--- /dev/null
+++ b/kotlin-base/.travis.yml
@@ -0,0 +1,42 @@
+language: android
+jdk: oraclejdk8
+sudo: false
+env:
+ - GRADLE_OPTS='-Dorg.gradle.jvmargs="-Xmx2048m -XX:+HeapDumpOnOutOfMemoryError"'
+
+machine:
+ environment:
+ GRADLE_OPTS: '-Dorg.gradle.jvmargs="-Xmx2048m -XX:+HeapDumpOnOutOfMemoryError"'
+
+android:
+ components:
+ - tools
+ - platform-tools
+ - build-tools-25.0.1
+ - android-25
+ - extra-android-m2repository
+ # Uncomment this if you need emulator
+ # - sys-img-x86-android-23
+ licenses:
+ - 'android-sdk-license-.+'
+
+before_install:
+ - export TERM=dumb
+ - chmod +x gradlew
+
+# Uncomment this if you need emulator
+# before_script:
+# - echo no | android create avd --force -n test -t android-21 --abi x86
+# - emulator -avd test -no-skin -no-audio -no-window &
+# - android-wait-for-emulator
+# - adb shell input keyevent 82 &
+
+script:
+ - ./gradlew checkstyle lintProductionRelease testProductionReleaseUnitTest
+
+branches:
+ only:
+ - master
+ - production
+
+# after_success: uncomment this and add your own deployment targets
\ No newline at end of file
diff --git a/kotlin-base/README.md b/kotlin-base/README.md
new file mode 100644
index 0000000..a9043a7
--- /dev/null
+++ b/kotlin-base/README.md
@@ -0,0 +1,72 @@
+Android app skeleton
+=======================================
+## Continuous integration
+* Travis CI [](https://travis-ci.org/fs/android-base/pull_requests)
+* Circle CI [](https://circleci.com/gh/fs/android-base)
+
+##Prerequisites
+* JDK 8
+* `JAVA_HOME` pointing to your jdk8
+
+##Plugins for Android Studio for comfortable work
+* [Parcelable generator](https://github.com/mcharmas/android-parcelable-intellij-plugin)
+
+##What's included:
+* [Staging and Production](https://github.com/fs/android-base/blob/master/app/build.gradle#L29-L38) build flavors with different package names ([read more](http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Product-flavors))
+* Logger configuration [supporting `Exception` logging](https://github.com/fs/android-base/blob/master/app/src/main/java/com/flatstack/android/App.java#L24-L26) ([read more](https://github.com/JakeWharton/timber))
+* [Robolectric support and configuration](https://github.com/fs/android-base/blob/master/app-tests/build.gradle) ([read more](http://blog.blundell-apps.com/android-gradle-app-with-robolectric-junit-tests/))
+* *Android Lint* [configuration](https://github.com/fs/android-base/blob/master/app/build.gradle#L56-L61)
+* *Travis CI* and *CircleCI* build [script1](https://github.com/fs/android-base/blob/master/.travis.yml) [script2](https://github.com/fs/android-base/blob/master/circle.yml):
+ * Downloading an *Android SDK*
+ * Building
+ * Running *Android Lint*
+ * Running *Robolectric* tests
+ * Hook up your continuous deployment target in [`after_success`](https://github.com/fs/android-base/blob/master/.travis.yml#L40) for travis and in ['deployment'](https://github.com/fs/android-base/blob/master/circle.yml#L20) for CircleCi
+* Release build signing and naming configuration
+
+##What's not included
+* [Crashlytics](crashlytics.com): they live in their own world, and including their plugin in template project just fails the build, if `apikey` is not specified. Also, getting `apikey` without an IDE plugin is impossible. You can get it [here](https://crashlytics.com/downloads/android-studio)
+* Test coverage: still in the process of figuring out what's the best way to enable unit test coverage for Android with Robolectric. Any suggestions will be highly appreciated
+
+##Setup
+ 1. Clone application as new project with original remote named "android-base"
+
+ git clone --depth 1 git://github.com/fs/android-base.git --origin android-base [MY-NEW-PROJECT]
+
+ **Note: we use depth parameter here in order to not copy the history of changes in base project**
+
+ 2. Create your new repository on the GitHub and push master into it. Make sure master branch is tracking origin repo.
+
+ cd [MY-NEW-PROJECT]
+ git remote add origin git@github.com:[MY-GITHUB-ACCOUNT]/[MY-NEW-PROJECT].git
+ git push -u origin master
+
+ 3. Import the project into your favourite IDE (only [Android Studio](https://developer.android.com/sdk/installing/studio.html) and [IntelliJ IDEA](http://www.jetbrains.com/idea/) are supported).
+Just select the root `build.gradle` and your IDE will do the rest.
+It will ask you to change the language level - do it, we're using Java 8 now
+
+###Configuration
+* Change your app's package by either [renaming the folder structure for Java sources](https://github.com/fs/android-base/tree/master/app/src/main/java/com/flatstack/android) or by just changing this [constant](https://github.com/fs/android-base/blob/master/app/build.gradle#L5) in `build.gradle`
+
+#Codestyles
+Project contains default codestyle scheme at `./idea/codestyleSettings.xml`. Make sure that you use default project scheme to avoid mess with codestyle with your collegues.
+Go to the Settings (`cmd + ,`) -> Editor -> Code Style look on top of right panel and select `Project` from `Scheme` dropdown. Apply -> Ok.
+
+###Making a release build
+* Just uncomment [these lines](https://github.com/fs/android-base/blob/master/app/build.gradle#L41-L48) and fill them up with your credentials
+
+##Notes on ProGuarding
+Project already has proguard config for included libraries.
+If you add library check proguard section of documentation and update [proguard-rules.pro](https://github.com/fs/android-base/blob/master/app/proguard-rules.pr)
+
+##Notes on lambdas support: retrolambda and jack and Jill
+Now Jack and Jil allow to use lambdas. Also it aims to speed up compilation process.
+But it's buggy and does not support annotation processing (butterknife, greenrobot eventbus3, dagger use it).
+When these drawbacks will be eliminated we move from retrolambda to jack&jill
+
+## Credits
+Android app skeleton is maintained by [Adel Nizamutdinov](http://github.com/adelnizamutdinov) and [Ilya Eremin](http://github.com/ilyaeremin).
+It was written by [Flatstack](http://www.flatstack.com) with the help of our
+[contributors](http://github.com/fs/android-base/contributors)
+
+[
](http://www.flatstack.com)
diff --git a/kotlin-base/app/build.gradle b/kotlin-base/app/build.gradle
new file mode 100644
index 0000000..8731263
--- /dev/null
+++ b/kotlin-base/app/build.gradle
@@ -0,0 +1,132 @@
+apply plugin: 'com.android.application'
+apply plugin: 'kotlin-android'
+apply plugin: 'spoon'
+apply from: 'checkstyle/checkstyle.gradle'
+
+apply from: '../deps.gradle'
+
+ext {
+ APPLICATION_ID = "com.flatstack.android"
+ isCI = "true".equals(System.getenv("CI"))
+ commitMessage = 'git log -1 --pretty=%B'.execute().text.trim()
+}
+
+android {
+ compileSdkVersion versions.TARGET_SDK_VERSION
+ buildToolsVersion versions.BUILD_TOOLS_VERSION
+
+ defaultConfig {
+ minSdkVersion versions.MIN_SDK_VERSION
+ targetSdkVersion versions.TARGET_SDK_VERSION
+
+ applicationId APPLICATION_ID
+ versionCode 1
+ versionName '1.0-beta'
+ testInstrumentationRunner 'android.support.test.runner.AndroidJUnitRunner'
+ com.android.ddmlib.DdmPreferences.setTimeOut(60000)
+ }
+
+ flavorDimensions "server"
+
+ productFlavors {
+ staging {
+ buildConfigField "String", "API_URL", "\"https://example-staging.com\""
+ applicationIdSuffix ".staging"
+ dimension "server"
+ }
+
+ production {
+ buildConfigField "String", "API_URL", "\"https://example.com\""
+ dimension "server"
+ }
+ }
+
+// signingConfigs {
+// release {
+// def props = new Properties()
+// props.load(new FileInputStream(file("$rootDir/keys.properties")))
+// storeFile file("$rootDir/your.jks")
+// storePassword props['storePassword']
+// keyAlias props['keyAlias']
+// keyPassword props['keyPassword']
+// }
+// }
+
+ buildTypes {
+ debug {
+ }
+ release {
+ minifyEnabled true
+ proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
+ }
+ }
+
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+
+ dexOptions {
+ preDexLibraries = !isCI
+ }
+
+ packagingOptions {
+ exclude 'META-INF/LICENSE'
+ exclude 'META-INF/NOTICE'
+ exclude 'META-INF/services/javax.annotation.processing.Processor'
+ }
+
+ lintOptions {
+ textReport true
+ textOutput "stdout"
+ lintConfig file("$projectDir/lint.xml")
+ warningsAsErrors true
+ }
+
+ configurations.all {
+// resolutionStrategy.force "com.android.support:support-annotations:$versions.support"
+ }
+}
+
+repositories {
+ mavenCentral()
+}
+
+dependencies {
+ compile supportLibs
+ compile rxJavaLibs
+// compile retrofitLibs
+// compile okHttpLibs
+
+ compile 'com.google.code.gson:gson:2.4'
+ compile 'com.jakewharton:butterknife:7.0.0' // view injection
+ annotationProcessor 'com.jakewharton:butterknife:7.0.0' // view injection
+ kapt 'com.jakewharton:butterknife:7.0.0' // view injection
+ compile 'com.github.bumptech.glide:glide:3.7.0'
+
+ testCompile unitTestLibs
+ androidTestCompile androidTestsLibs
+ compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version"
+}
+
+// Grant animation permissions to avoid test failure because of ui sync.
+task grantAnimationPermissions(type: Exec, dependsOn: ['installStagingDebug', 'installProductionDebug']) {
+ group = 'test'
+ description = 'Grant permissions for testing.'
+
+ def absolutePath = file('..') // Get project absolute path
+ commandLine "$absolutePath/app/set_animation_permissions.sh com.flatstack.android".split(" ")
+}
+
+// Source: http://stackoverflow.com/q/29908110/112705
+afterEvaluate {
+ // When launching individual tests from Android Studio, it seems that only the assemble tasks
+ // get called directly, not the install* versions
+ tasks.each { task ->
+ if (task.name.endsWith('AndroidTest')) {
+ task.dependsOn grantAnimationPermissions
+ }
+ }
+}
+
+apply from: "quality.gradle"
\ No newline at end of file
diff --git a/kotlin-base/app/checkstyle/checkstyle.gradle b/kotlin-base/app/checkstyle/checkstyle.gradle
new file mode 100644
index 0000000..a384942
--- /dev/null
+++ b/kotlin-base/app/checkstyle/checkstyle.gradle
@@ -0,0 +1,17 @@
+apply plugin: 'checkstyle'
+
+check.dependsOn 'checkstyle'
+
+checkstyle {
+ toolVersion '6.5'
+
+ configFile file("checkstyle/checkstyle.xml")
+ configProperties.checkstyleSuppressionFilterPath = file("checkstyle/suppressions.xml")
+ .absolutePath
+}
+task checkstyle(type: Checkstyle, group: 'verification') {
+ source 'src'
+ include '**/*.java'
+ exclude '**/gen/**'
+ classpath = files()
+}
\ No newline at end of file
diff --git a/kotlin-base/app/checkstyle/checkstyle.xml b/kotlin-base/app/checkstyle/checkstyle.xml
new file mode 100644
index 0000000..ae78e69
--- /dev/null
+++ b/kotlin-base/app/checkstyle/checkstyle.xml
@@ -0,0 +1,419 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/kotlin-base/app/checkstyle/suppressions.xml b/kotlin-base/app/checkstyle/suppressions.xml
new file mode 100644
index 0000000..7880252
--- /dev/null
+++ b/kotlin-base/app/checkstyle/suppressions.xml
@@ -0,0 +1,43 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/kotlin-base/app/findbugs-exclude.xml b/kotlin-base/app/findbugs-exclude.xml
new file mode 100644
index 0000000..acdff83
--- /dev/null
+++ b/kotlin-base/app/findbugs-exclude.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/kotlin-base/app/lint.xml b/kotlin-base/app/lint.xml
new file mode 100644
index 0000000..840a880
--- /dev/null
+++ b/kotlin-base/app/lint.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/kotlin-base/app/proguard-rules.pro b/kotlin-base/app/proguard-rules.pro
new file mode 100644
index 0000000..7a6d45d
--- /dev/null
+++ b/kotlin-base/app/proguard-rules.pro
@@ -0,0 +1,65 @@
+#your models
+#if you store all model under one package then use:
+# -keep class your.package.name.models.** { *; }
+#if you store models under different models packages then use:
+#-keep class **.models.** { *; }
+
+#retrolambda
+-dontwarn java.lang.invoke.*
+
+#butterknife
+-keep class butterknife.** { *; }
+-dontwarn butterknife.internal.**
+-keep class **$$ViewBinder { *; }
+-keepclasseswithmembernames class * {
+ @butterknife.* ;
+}
+-keepclasseswithmembernames class * {
+ @butterknife.* ;
+}
+
+#picasso
+-dontwarn com.squareup.okhttp3.**
+
+# OkHttp
+-keepattributes Signature
+-keepattributes *Annotation*
+-keep class com.squareup.okhttp3.** { *; }
+-keep interface com.squareup.okhttp3.** { *; }
+-dontwarn com.squareup.okhttp3.**
+
+#rxjava
+-keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* {
+ long producerIndex;
+ long consumerIndex;
+}
+-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef {
+ rx.internal.util.atomic.LinkedQueueNode producerNode;
+}
+-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef {
+ rx.internal.util.atomic.LinkedQueueNode consumerNode;
+}
+-dontwarn sun.misc.Unsafe
+
+#start gson
+-keepattributes Signature
+# For using GSON @Expose annotation
+-keepattributes *Annotation*
+
+# Gson specific classes
+-keep class sun.misc.Unsafe { *; }
+#end of gson
+
+#glide
+-keep public class * implements com.bumptech.glide.module.GlideModule
+-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
+ **[] $VALUES;
+ public *;
+}
+
+#debug
+-renamesourcefileattribute SourceFile
+-keepattributes SourceFile,LineNumberTable
+
+
+
diff --git a/kotlin-base/app/quality.gradle b/kotlin-base/app/quality.gradle
new file mode 100644
index 0000000..df2568d
--- /dev/null
+++ b/kotlin-base/app/quality.gradle
@@ -0,0 +1,20 @@
+apply plugin: 'findbugs'
+
+final classesDir = "build/intermediates/classes/production/debug"
+final sourceDir = "src/main/java"
+
+task findBugs(type: FindBugs, dependsOn: "assembleProductionDebug") {
+ classes = fileTree(classesDir)
+ source = fileTree(sourceDir)
+ classpath = files()
+ excludeFilter = file("findbugs-exclude.xml")
+
+ effort = 'max'
+
+ reports {
+ xml.enabled = false
+ html.enabled = true
+ }
+}
+
+tasks.check.dependsOn findBugs
diff --git a/kotlin-base/app/set_animation_permissions.sh b/kotlin-base/app/set_animation_permissions.sh
new file mode 100755
index 0000000..b25127e
--- /dev/null
+++ b/kotlin-base/app/set_animation_permissions.sh
@@ -0,0 +1,15 @@
+adb=$ANDROID_HOME/platform-tools/adb
+package=$1
+
+if [ "$#" = 0 ]; then
+ echo "No parameters found, run with sh set_animation_permissions.sh "
+ exit 0
+fi
+
+# get all the devices
+devices=$($adb devices | grep -v 'List of devices' | cut -f1 | grep '.')
+
+for device in $devices; do
+ echo "Setting permissions to device" $device "for package" $package
+ $adb -s $device shell pm grant $package android.permission.SET_ANIMATION_SCALE
+done
\ No newline at end of file
diff --git a/kotlin-base/app/src/androidTest/AndroidManifest.xml b/kotlin-base/app/src/androidTest/AndroidManifest.xml
new file mode 100644
index 0000000..899bbbc
--- /dev/null
+++ b/kotlin-base/app/src/androidTest/AndroidManifest.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
diff --git a/kotlin-base/app/src/androidTest/java/com/flatstack/android/MainScreenTest.java b/kotlin-base/app/src/androidTest/java/com/flatstack/android/MainScreenTest.java
new file mode 100644
index 0000000..f5b74cb
--- /dev/null
+++ b/kotlin-base/app/src/androidTest/java/com/flatstack/android/MainScreenTest.java
@@ -0,0 +1,34 @@
+package com.flatstack.android;
+
+import android.support.test.runner.AndroidJUnit4;
+
+import com.flatstack.android.main_screen.MainActivity;
+
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+
+import static android.support.test.espresso.Espresso.onView;
+import static android.support.test.espresso.assertion.ViewAssertions.matches;
+import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
+import static android.support.test.espresso.matcher.ViewMatchers.withId;
+import static android.support.test.espresso.matcher.ViewMatchers.withText;
+import static org.hamcrest.Matchers.allOf;
+
+/**
+ * Created by ereminilya on 6/4/17.
+ */
+
+@RunWith(AndroidJUnit4.class)
+public class MainScreenTest {
+
+ @Rule
+ public ScreenshotActivityRule testRule =
+ new ScreenshotActivityRule<>(MainActivity.class);
+
+ @Test
+ public void whenAppLaunch_androidBaseVisible() throws Exception {
+ onView(allOf(withId(R.id.title), withText(R.string.app_name)))
+ .check(matches(isDisplayed()));
+ }
+}
\ No newline at end of file
diff --git a/kotlin-base/app/src/androidTest/java/com/flatstack/android/ScreenshotActivityRule.java b/kotlin-base/app/src/androidTest/java/com/flatstack/android/ScreenshotActivityRule.java
new file mode 100644
index 0000000..9e3f182
--- /dev/null
+++ b/kotlin-base/app/src/androidTest/java/com/flatstack/android/ScreenshotActivityRule.java
@@ -0,0 +1,37 @@
+package com.flatstack.android;
+
+import android.app.Activity;
+import android.content.Context;
+import android.support.test.InstrumentationRegistry;
+import android.support.test.espresso.Espresso;
+import android.support.test.espresso.base.DefaultFailureHandler;
+import android.support.test.rule.ActivityTestRule;
+
+import com.squareup.spoon.Spoon;
+
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
+
+/**
+ * Created by ereminilya on 6/4/17.
+ */
+
+public class ScreenshotActivityRule extends ActivityTestRule {
+
+ public ScreenshotActivityRule(Class activityClass) {
+ super(activityClass);
+ }
+
+ @Override
+ public Statement apply(Statement base, Description description) {
+ String testClassName = description.getClassName();
+ String testMethodName = description.getMethodName();
+ Context context = InstrumentationRegistry.getTargetContext();
+ Espresso.setFailureHandler((error, matcher) -> {
+ Spoon.screenshot(getActivity(), "failure", testClassName, testMethodName);
+ new DefaultFailureHandler(context).handle(error, matcher);
+ });
+ return super.apply(base, description);
+ }
+
+}
diff --git a/kotlin-base/app/src/main/AndroidManifest.xml b/kotlin-base/app/src/main/AndroidManifest.xml
new file mode 100644
index 0000000..d0ee39f
--- /dev/null
+++ b/kotlin-base/app/src/main/AndroidManifest.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/kotlin-base/app/src/main/ic_launcher-web.png b/kotlin-base/app/src/main/ic_launcher-web.png
new file mode 100644
index 0000000..5c63bc5
Binary files /dev/null and b/kotlin-base/app/src/main/ic_launcher-web.png differ
diff --git a/kotlin-base/app/src/main/java/com/flatstack/android/App.kt b/kotlin-base/app/src/main/java/com/flatstack/android/App.kt
new file mode 100644
index 0000000..e994a33
--- /dev/null
+++ b/kotlin-base/app/src/main/java/com/flatstack/android/App.kt
@@ -0,0 +1,11 @@
+package com.flatstack.android
+
+import android.app.Application
+
+class App : Application() {
+
+ override fun onCreate() {
+ super.onCreate()
+ // your super code here
+ }
+}
\ No newline at end of file
diff --git a/kotlin-base/app/src/main/java/com/flatstack/android/main_screen/MainActivity.kt b/kotlin-base/app/src/main/java/com/flatstack/android/main_screen/MainActivity.kt
new file mode 100644
index 0000000..a9a9a8d
--- /dev/null
+++ b/kotlin-base/app/src/main/java/com/flatstack/android/main_screen/MainActivity.kt
@@ -0,0 +1,22 @@
+package com.flatstack.android.main_screen
+
+import android.os.Bundle
+
+import com.flatstack.android.utils.ui.BaseActivity
+import com.flatstack.android.R
+import com.flatstack.android.utils.ui.UiInfo
+
+class MainActivity : BaseActivity() {
+
+ override val uiInfo = UiInfo(layoutRes = R.layout.activity_main, titleRes = R.string.app_name,
+ hasBackButton = true)
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ if (savedInstanceState == null) {
+ supportFragmentManager.beginTransaction()
+ .replace(R.id.content, MainFragment())
+ .commit()
+ }
+ }
+}
\ No newline at end of file
diff --git a/kotlin-base/app/src/main/java/com/flatstack/android/main_screen/MainFragment.kt b/kotlin-base/app/src/main/java/com/flatstack/android/main_screen/MainFragment.kt
new file mode 100644
index 0000000..3fb87e6
--- /dev/null
+++ b/kotlin-base/app/src/main/java/com/flatstack/android/main_screen/MainFragment.kt
@@ -0,0 +1,30 @@
+package com.flatstack.android.main_screen
+
+import android.os.Bundle
+import android.view.View
+import android.widget.ImageView
+import butterknife.Bind
+import com.bumptech.glide.Glide
+import com.flatstack.android.R
+import com.flatstack.android.utils.ui.BaseFragment
+
+class MainFragment : BaseFragment() {
+
+ @Bind(R.id.image) @JvmField var image: ImageView? = null
+
+ override val layoutRes = R.layout.fragment_main
+
+ override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ image!!.setOnClickListener { listener ->
+ TestDialog.show("Example Hello", "Ublyudok, mat' tvoyu, a nu idi syuda, " +
+ "govno sobachye, reshil ko mne lezt'? Ti, zasranec vonyuchiy, mat' tvoyu, a?",
+ fragmentManager)
+ }
+
+ Glide.with(this)
+ .load("https://pbs.twimg.com/profile_images/502109671600033792/QOAC0YGo.png")
+ .into(image!!)
+ }
+}
diff --git a/kotlin-base/app/src/main/java/com/flatstack/android/main_screen/TestDialog.kt b/kotlin-base/app/src/main/java/com/flatstack/android/main_screen/TestDialog.kt
new file mode 100644
index 0000000..f0dee1c
--- /dev/null
+++ b/kotlin-base/app/src/main/java/com/flatstack/android/main_screen/TestDialog.kt
@@ -0,0 +1,49 @@
+package com.flatstack.android.main_screen
+
+import android.os.Bundle
+import android.support.v4.app.FragmentManager
+import android.view.View
+import android.widget.TextView
+import butterknife.Bind
+import com.flatstack.android.R
+import com.flatstack.android.utils.ui.BaseDialogFragment
+
+class TestDialog : BaseDialogFragment() {
+
+ @Bind(R.id.dialog_title) @JvmField var uiTitle: TextView? = null
+ @Bind(R.id.dialog_message) @JvmField var uiMessage: TextView? = null
+
+ private var title: String? = null
+ private var message: String? = null
+
+ override val layoutRes = R.layout.dialog_test
+
+ override fun parseArguments(args: Bundle) {
+ title = arguments.getString(KEY_TITLE)
+ message = arguments.getString(KEY_MESSAGE)
+ }
+
+ override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+ uiTitle!!.text = title ?: ""
+ uiMessage!!.text = message ?: ""
+ }
+
+ companion object {
+
+ private val KEY_TITLE = "dialogTitle"
+ private val KEY_MESSAGE = "dialogMessage"
+
+ fun show(title: String?, message: String?, fm: FragmentManager) {
+
+ val dialog = TestDialog().apply {
+ arguments = Bundle().apply {
+ putString(KEY_TITLE, title ?: "")
+ putString(KEY_MESSAGE, message ?: "")
+ }
+ }
+
+ dialog.show(fm, TestDialog::class.java.name)
+ }
+ }
+}
diff --git a/kotlin-base/app/src/main/java/com/flatstack/android/utils/Keyboard.kt b/kotlin-base/app/src/main/java/com/flatstack/android/utils/Keyboard.kt
new file mode 100644
index 0000000..8a3f927
--- /dev/null
+++ b/kotlin-base/app/src/main/java/com/flatstack/android/utils/Keyboard.kt
@@ -0,0 +1,36 @@
+package com.flatstack.android.utils
+
+import android.app.Activity
+import android.content.Context
+import android.view.View
+import android.view.inputmethod.InputMethodManager
+
+object Keyboard {
+
+ fun hide(activity: Activity) {
+ val inputManager = activity
+ .getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
+ val v = activity.currentFocus
+ inputManager.hideSoftInputFromWindow(v?.windowToken, 0)
+ }
+
+ fun hide(view: View) {
+ val imm = view.context
+ .getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
+ if (imm.isActive) {
+ imm.hideSoftInputFromWindow(view.windowToken, 0)
+ }
+ }
+
+ fun show(context: Context) {
+ val imm = context
+ .getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
+ imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0)
+ }
+
+ fun show(view: View) {
+ val inputManager = view.context
+ .getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
+ inputManager.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT)
+ }
+}
diff --git a/kotlin-base/app/src/main/java/com/flatstack/android/utils/recycler_view/BaseAdapter.kt b/kotlin-base/app/src/main/java/com/flatstack/android/utils/recycler_view/BaseAdapter.kt
new file mode 100644
index 0000000..b58df39
--- /dev/null
+++ b/kotlin-base/app/src/main/java/com/flatstack/android/utils/recycler_view/BaseAdapter.kt
@@ -0,0 +1,38 @@
+package com.flatstack.android.utils.recycler_view
+
+import android.support.v7.widget.RecyclerView
+import android.view.ViewGroup
+
+import rx.functions.Func1
+
+/**
+ * Created by ereminilya on 14/12/16.
+ */
+class BaseAdapter> @JvmOverloads
+constructor(private val items: MutableList,
+ private val func0: Func1,
+ private val onItemClickListener: OnItemClickListener? = null)
+ : RecyclerView.Adapter() {
+
+ override fun onCreateViewHolder(viewGroup: ViewGroup, holderType: Int): VH {
+ val holder = func0.call(viewGroup)
+ holder.itemView.setOnClickListener { v ->
+ val position = holder.adapterPosition
+ if (position != RecyclerView.NO_POSITION && onItemClickListener != null) {
+ onItemClickListener.onItemClick(data[position])
+ }
+ }
+ return holder
+ }
+
+ override fun onBindViewHolder(vh: VH, position: Int) = vh.bind(items[position])
+
+ override fun getItemCount() = items.size
+
+ val data: List get() = items
+
+ fun add(item: T) {
+ this.items.add(item)
+ notifyItemInserted(items.size - 1)
+ }
+}
\ No newline at end of file
diff --git a/kotlin-base/app/src/main/java/com/flatstack/android/utils/recycler_view/BaseHolder.kt b/kotlin-base/app/src/main/java/com/flatstack/android/utils/recycler_view/BaseHolder.kt
new file mode 100644
index 0000000..fb7bc1d
--- /dev/null
+++ b/kotlin-base/app/src/main/java/com/flatstack/android/utils/recycler_view/BaseHolder.kt
@@ -0,0 +1,12 @@
+package com.flatstack.android.utils.recycler_view
+
+import android.support.v7.widget.RecyclerView
+import android.view.View
+
+/**
+ * Created by ereminilya on 14/12/16.
+ */
+abstract class BaseHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
+
+ abstract fun bind(item: T)
+}
\ No newline at end of file
diff --git a/kotlin-base/app/src/main/java/com/flatstack/android/utils/recycler_view/OnItemClickListener.kt b/kotlin-base/app/src/main/java/com/flatstack/android/utils/recycler_view/OnItemClickListener.kt
new file mode 100644
index 0000000..fe4120a
--- /dev/null
+++ b/kotlin-base/app/src/main/java/com/flatstack/android/utils/recycler_view/OnItemClickListener.kt
@@ -0,0 +1,9 @@
+package com.flatstack.android.utils.recycler_view
+
+/**
+ * Created by ereminilya on 14/12/16.
+ */
+interface OnItemClickListener {
+
+ fun onItemClick(item: T)
+}
diff --git a/kotlin-base/app/src/main/java/com/flatstack/android/utils/storage/IStorage.kt b/kotlin-base/app/src/main/java/com/flatstack/android/utils/storage/IStorage.kt
new file mode 100644
index 0000000..8864e86
--- /dev/null
+++ b/kotlin-base/app/src/main/java/com/flatstack/android/utils/storage/IStorage.kt
@@ -0,0 +1,36 @@
+package com.flatstack.android.utils.storage
+
+import java.lang.reflect.Type
+
+/**
+ * Created by ereminilya on 8/1/17.
+ */
+
+interface IStorage {
+
+ operator fun get(key: String, type: Type): T?
+
+ fun put(key: String, item: T)
+
+ fun putString(key: String, str: String)
+
+ fun getString(key: String): String?
+
+ fun putLong(key: String, number: Long)
+
+ fun getLong(key: String, defaultValue: Long): Long
+
+ fun putInt(key: String, number: Int)
+
+ fun getInt(key: String, defaultValue: Int): Int
+
+ fun putBoolean(key: String, value: Boolean)
+
+ fun getBoolean(key: String, defaultValue: Boolean): Boolean
+
+ fun remove(key: String)
+
+ fun putCollection(key: String, items: List)
+
+ fun getCollection(key: String, type: Type): List?
+}
diff --git a/kotlin-base/app/src/main/java/com/flatstack/android/utils/storage/Storage.kt b/kotlin-base/app/src/main/java/com/flatstack/android/utils/storage/Storage.kt
new file mode 100644
index 0000000..7ee2556
--- /dev/null
+++ b/kotlin-base/app/src/main/java/com/flatstack/android/utils/storage/Storage.kt
@@ -0,0 +1,67 @@
+package com.flatstack.android.utils.storage
+
+import android.content.SharedPreferences
+
+import com.google.gson.Gson
+
+import java.lang.reflect.Type
+
+/**
+ * Created by ereminilya on 8/1/17.
+ */
+
+class Storage(private val sp: SharedPreferences, private val gson: Gson) : IStorage {
+
+ override fun get(key: String, type: Type): T? {
+ val json = sp.getString(key, "")
+ return if("" == json) null else gson.fromJson(json, type)
+ }
+
+ override fun put(key: String, item: T) {
+ sp.edit().putString(key, gson.toJson(item)).apply()
+ }
+
+ override fun putString(key: String, str: String) {
+ sp.edit().putString(key, str).apply()
+ }
+
+ override fun getString(key: String): String? {
+ return sp.getString(key, null)
+ }
+
+ override fun putLong(key: String, number: Long) {
+ sp.edit().putLong(key, number).apply()
+ }
+
+ override fun getLong(key: String, defaultValue: Long): Long {
+ return sp.getLong(key, defaultValue)
+ }
+
+ override fun putInt(key: String, number: Int) {
+ sp.edit().putInt(key, number).apply()
+ }
+
+ override fun getInt(key: String, defaultValue: Int): Int {
+ return sp.getInt(key, defaultValue)
+ }
+
+ override fun putBoolean(key: String, value: Boolean) {
+ sp.edit().putBoolean(key, value).apply()
+ }
+
+ override fun getBoolean(key: String, defaultValue: Boolean): Boolean {
+ return sp.getBoolean(key, defaultValue)
+ }
+
+ override fun remove(key: String) {
+ sp.edit().remove(key).apply()
+ }
+
+ override fun putCollection(key: String, items: List) {
+ put(key, items)
+ }
+
+ override fun getCollection(key: String, type: Type): List? {
+ return get>(key, type)
+ }
+}
\ No newline at end of file
diff --git a/kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/BaseActivity.kt b/kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/BaseActivity.kt
new file mode 100644
index 0000000..8772394
--- /dev/null
+++ b/kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/BaseActivity.kt
@@ -0,0 +1,72 @@
+package com.flatstack.android.utils.ui
+
+import android.content.Context
+import android.os.Bundle
+import android.support.v7.app.AppCompatActivity
+import android.support.v7.widget.Toolbar
+import android.view.Menu
+import android.view.MenuItem
+import android.view.View
+import butterknife.Bind
+import butterknife.ButterKnife
+import com.flatstack.android.R
+import com.flatstack.android.utils.Keyboard
+
+abstract class BaseActivity : AppCompatActivity() {
+
+ @Bind(R.id.toolbar) @JvmField var toolbar: View? = null
+
+ abstract val uiInfo: UiInfo
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ setContentView(uiInfo.layoutRes)
+ ButterKnife.bind(this)
+ if (intent != null && intent.extras != null) {
+ parseArguments(intent.extras)
+ }
+ if (toolbar != null) {
+ setSupportActionBar(toolbar as Toolbar?)
+ if (uiInfo.titleRes != 0) {
+ setTitle(uiInfo.titleRes)
+ } else if (uiInfo.titleStr != null) {
+ title = uiInfo.titleStr
+ }
+ if (uiInfo.hasBackButton) {
+ supportActionBar!!.setDisplayHomeAsUpEnabled(true)
+ }
+ }
+ if (savedInstanceState != null) {
+ restoreState(savedInstanceState)
+ }
+ }
+
+ override fun setContentView(layoutResID: Int) {
+ super.setContentView(layoutResID)
+ ButterKnife.bind(this)
+ }
+
+ protected fun restoreState(savedState: Bundle) {}
+
+ protected fun parseArguments(extras: Bundle) {}
+
+ override fun onCreateOptionsMenu(menu: Menu): Boolean {
+ if (uiInfo.menuRes != 0) {
+ val inflater = menuInflater
+ inflater.inflate(uiInfo.menuRes, menu)
+ return true
+ }
+ return super.onCreateOptionsMenu(menu)
+ }
+
+ override fun onOptionsItemSelected(item: MenuItem): Boolean {
+ if (item.itemId == android.R.id.home && uiInfo.hasBackButton) {
+ Keyboard.hide(this)
+ finish()
+ return true
+ }
+ return super.onOptionsItemSelected(item)
+ }
+
+ fun context(): Context = this
+}
diff --git a/kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/BaseDialogFragment.kt b/kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/BaseDialogFragment.kt
new file mode 100644
index 0000000..3a5915f
--- /dev/null
+++ b/kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/BaseDialogFragment.kt
@@ -0,0 +1,74 @@
+package com.flatstack.android.utils.ui
+
+import android.app.Dialog
+import android.content.Context
+import android.os.Bundle
+import android.support.annotation.LayoutRes
+import android.support.v4.app.DialogFragment
+import android.support.v4.app.FragmentActivity
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.view.Window
+import butterknife.ButterKnife
+
+abstract class BaseDialogFragment : DialogFragment() {
+
+ @get:LayoutRes abstract val layoutRes: Int
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ if (arguments != null) {
+ parseArguments(arguments)
+ }
+ if (savedInstanceState != null) {
+ restoreState(savedInstanceState)
+ }
+ }
+
+ protected fun restoreState(savedState: Bundle) {}
+
+ protected open fun parseArguments(args: Bundle) {
+ throw IllegalStateException("should be overridden")
+ }
+
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ val dialog = super.onCreateDialog(savedInstanceState)
+ dialog.window.requestFeature(Window.FEATURE_NO_TITLE)
+ return dialog
+ }
+
+ override fun onCreateView(inflater: LayoutInflater,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?): View? {
+ val v = inflater.inflate(layoutRes, container, false)
+ ButterKnife.bind(this, v)
+ return v
+ }
+
+ override fun onDestroyView() {
+ ButterKnife.unbind(this)
+ super.onDestroyView()
+ }
+
+ fun activity(): FragmentActivity = activity
+
+ fun context(): Context = activity()
+
+ companion object {
+
+ protected fun show(dialogFragment: T,
+ activity: FragmentActivity): T {
+ val ft = activity.supportFragmentManager.beginTransaction()
+ val prev = activity.supportFragmentManager
+ .findFragmentByTag(dialogFragment.javaClass.name)
+ if (prev != null) {
+ ft.remove(prev)
+ (prev as DialogFragment).dismissAllowingStateLoss()
+ }
+ ft.addToBackStack(null)
+ dialogFragment.show(ft, dialogFragment.javaClass.name)
+ return dialogFragment
+ }
+ }
+}
\ No newline at end of file
diff --git a/kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/BaseFragment.kt b/kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/BaseFragment.kt
new file mode 100644
index 0000000..9e4db23
--- /dev/null
+++ b/kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/BaseFragment.kt
@@ -0,0 +1,31 @@
+package com.flatstack.android.utils.ui
+
+import android.os.Bundle
+import android.support.annotation.LayoutRes
+import android.support.v4.app.Fragment
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import butterknife.ButterKnife
+
+abstract class BaseFragment : Fragment() {
+
+ @get:LayoutRes abstract val layoutRes: Int
+
+ override fun onCreate(savedState: Bundle?) {
+ super.onCreate(savedState)
+ if (savedState != null) {
+ restoreState(savedState)
+ }
+ }
+
+ fun restoreState(savedState: Bundle) {}
+
+ override fun onCreateView(inflater: LayoutInflater?,
+ container: ViewGroup?,
+ savedInstanceState: Bundle?): View? {
+ val rootView = inflater!!.inflate(layoutRes, container, false)
+ ButterKnife.bind(this, rootView)
+ return rootView
+ }
+}
diff --git a/kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/UiInfo.kt b/kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/UiInfo.kt
new file mode 100644
index 0000000..aa13dc7
--- /dev/null
+++ b/kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/UiInfo.kt
@@ -0,0 +1,9 @@
+package com.flatstack.android.utils.ui
+
+import android.support.annotation.LayoutRes
+import android.support.annotation.MenuRes
+import android.support.annotation.StringRes
+
+class UiInfo(@param:LayoutRes val layoutRes: Int, @param:StringRes val titleRes: Int = 0,
+ @param:MenuRes val menuRes: Int = 0, val titleStr: String? = null,
+ val hasBackButton: Boolean = false)
diff --git a/kotlin-base/app/src/main/res/drawable-hdpi/ic_launcher.png b/kotlin-base/app/src/main/res/drawable-hdpi/ic_launcher.png
new file mode 100644
index 0000000..a6ed594
Binary files /dev/null and b/kotlin-base/app/src/main/res/drawable-hdpi/ic_launcher.png differ
diff --git a/kotlin-base/app/src/main/res/drawable-mdpi/ic_launcher.png b/kotlin-base/app/src/main/res/drawable-mdpi/ic_launcher.png
new file mode 100644
index 0000000..0c5fd33
Binary files /dev/null and b/kotlin-base/app/src/main/res/drawable-mdpi/ic_launcher.png differ
diff --git a/kotlin-base/app/src/main/res/drawable-xhdpi/ic_launcher.png b/kotlin-base/app/src/main/res/drawable-xhdpi/ic_launcher.png
new file mode 100644
index 0000000..a62b567
Binary files /dev/null and b/kotlin-base/app/src/main/res/drawable-xhdpi/ic_launcher.png differ
diff --git a/kotlin-base/app/src/main/res/drawable-xxhdpi/ic_launcher.png b/kotlin-base/app/src/main/res/drawable-xxhdpi/ic_launcher.png
new file mode 100644
index 0000000..fffa6a6
Binary files /dev/null and b/kotlin-base/app/src/main/res/drawable-xxhdpi/ic_launcher.png differ
diff --git a/kotlin-base/app/src/main/res/drawable-xxxhdpi/ic_launcher.png b/kotlin-base/app/src/main/res/drawable-xxxhdpi/ic_launcher.png
new file mode 100644
index 0000000..2a27316
Binary files /dev/null and b/kotlin-base/app/src/main/res/drawable-xxxhdpi/ic_launcher.png differ
diff --git a/kotlin-base/app/src/main/res/layout/activity_main.xml b/kotlin-base/app/src/main/res/layout/activity_main.xml
new file mode 100644
index 0000000..788acd1
--- /dev/null
+++ b/kotlin-base/app/src/main/res/layout/activity_main.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/kotlin-base/app/src/main/res/layout/dialog_test.xml b/kotlin-base/app/src/main/res/layout/dialog_test.xml
new file mode 100644
index 0000000..81ff62d
--- /dev/null
+++ b/kotlin-base/app/src/main/res/layout/dialog_test.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/kotlin-base/app/src/main/res/layout/fragment_main.xml b/kotlin-base/app/src/main/res/layout/fragment_main.xml
new file mode 100644
index 0000000..5196178
--- /dev/null
+++ b/kotlin-base/app/src/main/res/layout/fragment_main.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
+
+
diff --git a/kotlin-base/app/src/main/res/values/dimens.xml b/kotlin-base/app/src/main/res/values/dimens.xml
new file mode 100644
index 0000000..f11f745
--- /dev/null
+++ b/kotlin-base/app/src/main/res/values/dimens.xml
@@ -0,0 +1,3 @@
+
+
+
diff --git a/kotlin-base/app/src/main/res/values/strings.xml b/kotlin-base/app/src/main/res/values/strings.xml
new file mode 100644
index 0000000..c677d36
--- /dev/null
+++ b/kotlin-base/app/src/main/res/values/strings.xml
@@ -0,0 +1,4 @@
+
+
+ Kotlin Ilya Base
+
diff --git a/kotlin-base/app/src/main/res/values/styles.xml b/kotlin-base/app/src/main/res/values/styles.xml
new file mode 100644
index 0000000..8542005
--- /dev/null
+++ b/kotlin-base/app/src/main/res/values/styles.xml
@@ -0,0 +1,2 @@
+
+
diff --git a/kotlin-base/app/src/main/res/values/themes.xml b/kotlin-base/app/src/main/res/values/themes.xml
new file mode 100644
index 0000000..a582f33
--- /dev/null
+++ b/kotlin-base/app/src/main/res/values/themes.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/kotlin-base/app/src/main/res/values/tokens.xml b/kotlin-base/app/src/main/res/values/tokens.xml
new file mode 100644
index 0000000..55344e5
--- /dev/null
+++ b/kotlin-base/app/src/main/res/values/tokens.xml
@@ -0,0 +1,3 @@
+
+
+
\ No newline at end of file
diff --git a/kotlin-base/app/src/test/java/com/flatstack/android/utils/storage/RuntimeStorage.java b/kotlin-base/app/src/test/java/com/flatstack/android/utils/storage/RuntimeStorage.java
new file mode 100644
index 0000000..1c7faae
--- /dev/null
+++ b/kotlin-base/app/src/test/java/com/flatstack/android/utils/storage/RuntimeStorage.java
@@ -0,0 +1,71 @@
+package com.flatstack.android.utils.storage;
+
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+
+import java.lang.reflect.Type;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class RuntimeStorage implements IStorage {
+
+ private Map map = new HashMap<>();
+
+ @Override public void put(@NonNull String key, @NonNull T items) {
+ map.put(key, items);
+ }
+
+ @Nullable @Override
+ public T get(@NonNull String key, @NonNull Type clazz) {
+ return (T) map.get(key);
+ }
+
+ @Override public void putLong(@NonNull String key, long number) {
+ map.put(key, Long.valueOf(number));
+ }
+
+ @Override public long getLong(@NonNull String key, long defaultValue) {
+ Object o = map.get(key);
+ return o != null ? (long) o : defaultValue;
+ }
+
+ @Override public void putInt(@NonNull String key, int number) {
+ map.put(key, Integer.valueOf(number));
+ }
+
+ @Override public int getInt(@NonNull String key, int defaultValue) {
+ Object o = map.get(key);
+ return o != null ? (int) o : defaultValue;
+ }
+
+ @Override public void putBoolean(@NonNull String key, boolean value) {
+ map.put(key, Boolean.valueOf(value));
+ }
+
+ @Override public boolean getBoolean(@NonNull String key, boolean defaultValue) {
+ Object o = map.get(key);
+ return o != null ? (boolean) o : defaultValue;
+ }
+
+ @Override public void putString(@NonNull String key, @NonNull String str) {
+ map.put(key, str);
+ }
+
+ @Override public String getString(@NonNull String key) {
+ Object o = map.get(key);
+ return (o != null) ? (String) o : null;
+ }
+
+ @Override public void remove(@NonNull String key) {
+ map.remove(key);
+ }
+
+ @Override public void putCollection(@NonNull String key, @NonNull List items) {
+ map.put(key, items);
+ }
+
+ @Override public List getCollection(@NonNull String key, @NonNull Type type) {
+ return (List) map.get(key);
+ }
+}
\ No newline at end of file
diff --git a/kotlin-base/build.gradle b/kotlin-base/build.gradle
new file mode 100644
index 0000000..b745fe0
--- /dev/null
+++ b/kotlin-base/build.gradle
@@ -0,0 +1,21 @@
+buildscript {
+ ext.kotlin_version = '1.1.2-4'
+ repositories {
+ jcenter()
+ }
+
+ dependencies {
+ classpath 'com.android.tools.build:gradle:3.0.0-alpha1'
+ classpath('com.stanfy.spoon:spoon-gradle-plugin:1.2.2') {
+ exclude module: 'guava'
+ }
+ classpath 'com.google.guava:guava:17.0'
+ classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
+ }
+}
+
+allprojects {
+ repositories {
+ jcenter()
+ }
+}
\ No newline at end of file
diff --git a/kotlin-base/circle.yml b/kotlin-base/circle.yml
new file mode 100644
index 0000000..d6fdce2
--- /dev/null
+++ b/kotlin-base/circle.yml
@@ -0,0 +1,53 @@
+machine:
+ java:
+ version: oraclejdk8
+ environment:
+ TERM: "dumb"
+ ADB_INSTALL_TIMEOUT: 5000
+ GRADLE_OPTS: '-Dorg.gradle.jvmargs="-Xmx2048m -XX:+HeapDumpOnOutOfMemoryError"'
+
+general:
+ artifacts:
+ - "/home/ubuntu/android-base/app/build/reports"
+ - "/home/ubuntu/android-base/app/build/spoon"
+
+dependencies:
+ pre:
+ - chmod +x app/set_animation_permissions.sh
+ - echo y | android update sdk --no-ui --all --filter "tools,platform-tools,android-25,extra-google-m2repository"
+ - echo y | android update sdk --no-ui --all --filter "build-tools-25.0.1"
+ - echo y | android update sdk --no-ui --all --filter "extra-android-m2repository"
+
+ post:
+ # Create the android emulator
+ - echo n | android create avd -n test_android_17 -f -t android-17
+ # [HINT] Circle-CI already have built-in emulator (circleci-android22), but it's so heavy.
+ # Make a SD Card image file for the android emulator
+ - mksdcard -l e 128M sdcard.img
+
+test:
+ pre:
+ - emulator -avd test_android_17 -no-audio -no-boot-anim -no-window -sdcard sdcard.img:
+ background: true
+ parallel: true
+ override:
+ - ./gradlew checkstyle lintProductionRelease testProductionReleaseUnitTest
+ - circle-android wait-for-boot
+ - sleep 20
+ - adb shell input keyevent 82
+ - adb shell settings put global window_animation_scale 0.0
+ - adb shell settings put global transition_animation_scale 0.0
+ - adb shell settings put global animator_duration_scale 0.0
+ - ./gradlew spoonProductionDebugAndroidTest
+
+#deployment:
+# production: # just a label; label names are completely up to you
+# branch: master
+# commands:
+# - ./gradlew publishApkRelease
+# -Dorg.gradle.project.track=production
+# beta:
+# branch: develop
+# commands:
+# - ./gradlew publishApkRelease
+# -Dorg.gradle.project.track=beta
diff --git a/kotlin-base/deps.gradle b/kotlin-base/deps.gradle
new file mode 100644
index 0000000..a1d8f62
--- /dev/null
+++ b/kotlin-base/deps.gradle
@@ -0,0 +1,52 @@
+ext {
+ versions = [
+ okHttp : '3.3.1',
+ support : '25.1.0',
+ retrofit : '2.0.2',
+ espressoVersion : '2.2.2',
+ BUILD_TOOLS_VERSION: '25.0.1',
+ MIN_SDK_VERSION : 14,
+ TARGET_SDK_VERSION : 25
+ ]
+ supportDeps = [
+ appcompatV7 : "com.android.support:appcompat-v7:$versions.support",
+ recyclerView: "com.android.support:recyclerview-v7:$versions.support",
+ design : "com.android.support:design:$versions.support"
+// gridLayout : "com.android.support:gridlayout-v7:$versions.support"
+// cardView : "com.android.support:cardview-v7:$versions.support"
+// palette : "com.android.support:palette-v7:$versions.support"
+ ]
+ rxJava = [
+ rxJava : 'io.reactivex:rxjava:1.1.6',
+ rxAndroid: 'io.reactivex:rxandroid:1.2.1'
+ ]
+ retrofit = [
+ retrofit : "com.squareup.retrofit2:retrofit:$versions.retrofit",
+ rxAdapter : "com.squareup.retrofit2:adapter-rxjava:$versions.retrofit",
+ gsonConverter: "com.squareup.retrofit2:converter-gson:$versions.retrofit"
+ ]
+ okHttp = [
+ logger: "com.squareup.okhttp3:logging-interceptor:$versions.okHttp",
+ self : "com.squareup.okhttp3:okhttp:$versions.okHttp"
+ ]
+ unitTests = [
+ supportAnnotation: "com.android.support:support-annotations:$versions.support",
+ junit : 'junit:junit:4.12',
+ assertj : 'com.squareup.assertj:assertj-android:1.0.0'
+ ]
+ androidTests = [
+ espressoCore : "com.android.support.test.espresso:espresso-core:$versions.espressoVersion",
+ espressoContrib: "com.android.support.test.espresso:espresso-contrib:$versions.espressoVersion",
+ espressoIntents: "com.android.support.test.espresso:espresso-intents:$versions.espressoVersion",
+ testRunner : "com.android.support.test:runner:0.5",
+ testRules : "com.android.support.test:rules:0.5",
+ spoon : 'com.squareup.spoon:spoon-client:1.6.4'
+ ]
+
+ supportLibs = supportDeps.values()
+ retrofitLibs = retrofit.values()
+ okHttpLibs = okHttp.values()
+ rxJavaLibs = rxJava.values()
+ unitTestLibs = unitTests.values()
+ androidTestsLibs = androidTests.values() + supportLibs
+}
\ No newline at end of file
diff --git a/kotlin-base/gradle/wrapper/gradle-wrapper.jar b/kotlin-base/gradle/wrapper/gradle-wrapper.jar
new file mode 100644
index 0000000..3c7abdf
Binary files /dev/null and b/kotlin-base/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/kotlin-base/gradle/wrapper/gradle-wrapper.properties b/kotlin-base/gradle/wrapper/gradle-wrapper.properties
new file mode 100644
index 0000000..0a54e9b
--- /dev/null
+++ b/kotlin-base/gradle/wrapper/gradle-wrapper.properties
@@ -0,0 +1,6 @@
+#Sun May 21 22:45:49 MSK 2017
+distributionBase=GRADLE_USER_HOME
+distributionPath=wrapper/dists
+zipStoreBase=GRADLE_USER_HOME
+zipStorePath=wrapper/dists
+distributionUrl=https\://services.gradle.org/distributions/gradle-4.0-milestone-1-all.zip
diff --git a/kotlin-base/gradlew b/kotlin-base/gradlew
new file mode 100755
index 0000000..91a7e26
--- /dev/null
+++ b/kotlin-base/gradlew
@@ -0,0 +1,164 @@
+#!/usr/bin/env bash
+
+##############################################################################
+##
+## Gradle start up script for UN*X
+##
+##############################################################################
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS=""
+
+APP_NAME="Gradle"
+APP_BASE_NAME=`basename "$0"`
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD="maximum"
+
+warn ( ) {
+ echo "$*"
+}
+
+die ( ) {
+ echo
+ echo "$*"
+ echo
+ exit 1
+}
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+case "`uname`" in
+ CYGWIN* )
+ cygwin=true
+ ;;
+ Darwin* )
+ darwin=true
+ ;;
+ MINGW* )
+ msys=true
+ ;;
+esac
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched.
+if $cygwin ; then
+ [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
+fi
+
+# Attempt to set APP_HOME
+# Resolve links: $0 may be a link
+PRG="$0"
+# Need this for relative symlinks.
+while [ -h "$PRG" ] ; do
+ ls=`ls -ld "$PRG"`
+ link=`expr "$ls" : '.*-> \(.*\)$'`
+ if expr "$link" : '/.*' > /dev/null; then
+ PRG="$link"
+ else
+ PRG=`dirname "$PRG"`"/$link"
+ fi
+done
+SAVED="`pwd`"
+cd "`dirname \"$PRG\"`/" >&-
+APP_HOME="`pwd -P`"
+cd "$SAVED" >&-
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+ if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+ # IBM's JDK on AIX uses strange locations for the executables
+ JAVACMD="$JAVA_HOME/jre/sh/java"
+ else
+ JAVACMD="$JAVA_HOME/bin/java"
+ fi
+ if [ ! -x "$JAVACMD" ] ; then
+ die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+ fi
+else
+ JAVACMD="java"
+ which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then
+ MAX_FD_LIMIT=`ulimit -H -n`
+ if [ $? -eq 0 ] ; then
+ if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then
+ MAX_FD="$MAX_FD_LIMIT"
+ fi
+ ulimit -n $MAX_FD
+ if [ $? -ne 0 ] ; then
+ warn "Could not set maximum file descriptor limit: $MAX_FD"
+ fi
+ else
+ warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT"
+ fi
+fi
+
+# For Darwin, add options to specify how the application appears in the dock
+if $darwin; then
+ GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\""
+fi
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin ; then
+ APP_HOME=`cygpath --path --mixed "$APP_HOME"`
+ CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
+
+ # We build the pattern for arguments to be converted via cygpath
+ ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`
+ SEP=""
+ for dir in $ROOTDIRSRAW ; do
+ ROOTDIRS="$ROOTDIRS$SEP$dir"
+ SEP="|"
+ done
+ OURCYGPATTERN="(^($ROOTDIRS))"
+ # Add a user-defined pattern to the cygpath arguments
+ if [ "$GRADLE_CYGPATTERN" != "" ] ; then
+ OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)"
+ fi
+ # Now convert the arguments - kludge to limit ourselves to /bin/sh
+ i=0
+ for arg in "$@" ; do
+ CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -`
+ CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option
+
+ if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition
+ eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"`
+ else
+ eval `echo args$i`="\"$arg\""
+ fi
+ i=$((i+1))
+ done
+ case $i in
+ (0) set -- ;;
+ (1) set -- "$args0" ;;
+ (2) set -- "$args0" "$args1" ;;
+ (3) set -- "$args0" "$args1" "$args2" ;;
+ (4) set -- "$args0" "$args1" "$args2" "$args3" ;;
+ (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;;
+ (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;;
+ (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;;
+ (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;;
+ (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;;
+ esac
+fi
+
+# Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules
+function splitJvmOpts() {
+ JVM_OPTS=("$@")
+}
+eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS
+JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME"
+
+exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@"
diff --git a/kotlin-base/gradlew.bat b/kotlin-base/gradlew.bat
new file mode 100644
index 0000000..aec9973
--- /dev/null
+++ b/kotlin-base/gradlew.bat
@@ -0,0 +1,90 @@
+@if "%DEBUG%" == "" @echo off
+@rem ##########################################################################
+@rem
+@rem Gradle startup script for Windows
+@rem
+@rem ##########################################################################
+
+@rem Set local scope for the variables with windows NT shell
+if "%OS%"=="Windows_NT" setlocal
+
+@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+set DEFAULT_JVM_OPTS=
+
+set DIRNAME=%~dp0
+if "%DIRNAME%" == "" set DIRNAME=.
+set APP_BASE_NAME=%~n0
+set APP_HOME=%DIRNAME%
+
+@rem Find java.exe
+if defined JAVA_HOME goto findJavaFromJavaHome
+
+set JAVA_EXE=java.exe
+%JAVA_EXE% -version >NUL 2>&1
+if "%ERRORLEVEL%" == "0" goto init
+
+echo.
+echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:findJavaFromJavaHome
+set JAVA_HOME=%JAVA_HOME:"=%
+set JAVA_EXE=%JAVA_HOME%/bin/java.exe
+
+if exist "%JAVA_EXE%" goto init
+
+echo.
+echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%
+echo.
+echo Please set the JAVA_HOME variable in your environment to match the
+echo location of your Java installation.
+
+goto fail
+
+:init
+@rem Get command-line arguments, handling Windowz variants
+
+if not "%OS%" == "Windows_NT" goto win9xME_args
+if "%@eval[2+2]" == "4" goto 4NT_args
+
+:win9xME_args
+@rem Slurp the command line arguments.
+set CMD_LINE_ARGS=
+set _SKIP=2
+
+:win9xME_args_slurp
+if "x%~1" == "x" goto execute
+
+set CMD_LINE_ARGS=%*
+goto execute
+
+:4NT_args
+@rem Get arguments from the 4NT Shell from JP Software
+set CMD_LINE_ARGS=%$
+
+:execute
+@rem Setup the command line
+
+set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar
+
+@rem Execute Gradle
+"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%
+
+:end
+@rem End local scope for the variables with windows NT shell
+if "%ERRORLEVEL%"=="0" goto mainEnd
+
+:fail
+rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of
+rem the _cmd.exe /c_ return code!
+if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1
+exit /b 1
+
+:mainEnd
+if "%OS%"=="Windows_NT" endlocal
+
+:omega
diff --git a/kotlin-base/keystore.properties b/kotlin-base/keystore.properties
new file mode 100644
index 0000000..3eb1f61
--- /dev/null
+++ b/kotlin-base/keystore.properties
@@ -0,0 +1,4 @@
+#DO NOT FORGET EXCLUDE THIS FILE FROM REPO
+storePassword=your_password
+keyAlias=your_alias
+keyPassword=your_key_password
\ No newline at end of file
diff --git a/kotlin-base/proguard.cfg b/kotlin-base/proguard.cfg
new file mode 100644
index 0000000..58899e0
--- /dev/null
+++ b/kotlin-base/proguard.cfg
@@ -0,0 +1,12 @@
+# Dagger
+-dontwarn dagger.internal.codegen.**
+-keepclassmembers,allowobfuscation class * {
+ @javax.inject.* *;
+ @dagger.* *;
+ ();
+}
+-keep class dagger.* { *; }
+-keep class javax.inject.* { *; }
+-keep class * extends dagger.internal.Binding
+-keep class * extends dagger.internal.ModuleAdapter
+-keep class * extends dagger.internal.StaticInjection
\ No newline at end of file
diff --git a/kotlin-base/settings.gradle b/kotlin-base/settings.gradle
new file mode 100644
index 0000000..e7b4def
--- /dev/null
+++ b/kotlin-base/settings.gradle
@@ -0,0 +1 @@
+include ':app'