From e7bd66b7b799f70f3d596357ccd98efff4849f07 Mon Sep 17 00:00:00 2001 From: Ilya Eremin Date: Sun, 21 May 2017 22:48:05 +0300 Subject: [PATCH 1/8] copy android base to android examples --- kotlin-base/.gitignore | 13 + kotlin-base/.travis.yml | 42 ++ kotlin-base/README.md | 72 +++ kotlin-base/app/build.gradle | 125 ++++++ kotlin-base/app/checkstyle/checkstyle.gradle | 17 + kotlin-base/app/checkstyle/checkstyle.xml | 419 ++++++++++++++++++ kotlin-base/app/checkstyle/suppressions.xml | 43 ++ kotlin-base/app/findbugs-exclude.xml | 9 + kotlin-base/app/lint.xml | 47 ++ kotlin-base/app/proguard-rules.pro | 65 +++ kotlin-base/app/quality.gradle | 20 + kotlin-base/app/set_animation_permissions.sh | 15 + .../app/src/androidTest/AndroidManifest.xml | 13 + .../com/flatstack/android/MainScreenTest.java | 34 ++ .../android/ScreenshotActivityRule.java | 37 ++ kotlin-base/app/src/main/AndroidManifest.xml | 27 ++ kotlin-base/app/src/main/ic_launcher-web.png | Bin 0 -> 47072 bytes .../main/java/com/flatstack/android/App.java | 12 + .../android/main_screen/MainActivity.java | 27 ++ .../android/main_screen/MainFragment.java | 34 ++ .../android/main_screen/TestDialog.java | 54 +++ .../com/flatstack/android/utils/Keyboard.java | 40 ++ .../com/flatstack/android/utils/Lists.java | 24 + .../com/flatstack/android/utils/Views.java | 24 + .../utils/recycler_view/BaseAdapter.java | 60 +++ .../utils/recycler_view/BaseHolder.java | 16 + .../recycler_view/OnItemClickListener.java | 11 + .../android/utils/storage/IStorage.java | 40 ++ .../android/utils/storage/Storage.java | 83 ++++ .../android/utils/ui/BaseActivity.java | 81 ++++ .../android/utils/ui/BaseDialogFragment.java | 84 ++++ .../android/utils/ui/BaseFragment.java | 40 ++ .../flatstack/android/utils/ui/UiInfo.java | 63 +++ .../main/res/drawable-hdpi/ic_launcher.png | Bin 0 -> 599 bytes .../main/res/drawable-mdpi/ic_launcher.png | Bin 0 -> 464 bytes .../main/res/drawable-xhdpi/ic_launcher.png | Bin 0 -> 759 bytes .../main/res/drawable-xxhdpi/ic_launcher.png | Bin 0 -> 1273 bytes .../main/res/drawable-xxxhdpi/ic_launcher.png | Bin 0 -> 1719 bytes .../app/src/main/res/layout/activity_main.xml | 25 ++ .../app/src/main/res/layout/dialog_test.xml | 32 ++ .../app/src/main/res/layout/fragment_main.xml | 29 ++ .../app/src/main/res/values/dimens.xml | 3 + .../app/src/main/res/values/strings.xml | 4 + .../app/src/main/res/values/styles.xml | 2 + .../app/src/main/res/values/themes.xml | 14 + .../app/src/main/res/values/tokens.xml | 3 + .../android/utils/storage/RuntimeStorage.java | 71 +++ kotlin-base/build.gradle | 20 + kotlin-base/circle.yml | 53 +++ kotlin-base/deps.gradle | 52 +++ kotlin-base/gradle/wrapper/gradle-wrapper.jar | Bin 0 -> 51106 bytes .../gradle/wrapper/gradle-wrapper.properties | 6 + kotlin-base/gradlew | 164 +++++++ kotlin-base/gradlew.bat | 90 ++++ kotlin-base/keystore.properties | 4 + kotlin-base/proguard.cfg | 12 + kotlin-base/settings.gradle | 1 + 57 files changed, 2276 insertions(+) create mode 100644 kotlin-base/.gitignore create mode 100644 kotlin-base/.travis.yml create mode 100644 kotlin-base/README.md create mode 100644 kotlin-base/app/build.gradle create mode 100644 kotlin-base/app/checkstyle/checkstyle.gradle create mode 100644 kotlin-base/app/checkstyle/checkstyle.xml create mode 100644 kotlin-base/app/checkstyle/suppressions.xml create mode 100644 kotlin-base/app/findbugs-exclude.xml create mode 100644 kotlin-base/app/lint.xml create mode 100644 kotlin-base/app/proguard-rules.pro create mode 100644 kotlin-base/app/quality.gradle create mode 100755 kotlin-base/app/set_animation_permissions.sh create mode 100644 kotlin-base/app/src/androidTest/AndroidManifest.xml create mode 100644 kotlin-base/app/src/androidTest/java/com/flatstack/android/MainScreenTest.java create mode 100644 kotlin-base/app/src/androidTest/java/com/flatstack/android/ScreenshotActivityRule.java create mode 100644 kotlin-base/app/src/main/AndroidManifest.xml create mode 100644 kotlin-base/app/src/main/ic_launcher-web.png create mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/App.java create mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/main_screen/MainActivity.java create mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/main_screen/MainFragment.java create mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/main_screen/TestDialog.java create mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/utils/Keyboard.java create mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/utils/Lists.java create mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/utils/Views.java create mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/utils/recycler_view/BaseAdapter.java create mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/utils/recycler_view/BaseHolder.java create mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/utils/recycler_view/OnItemClickListener.java create mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/utils/storage/IStorage.java create mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/utils/storage/Storage.java create mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/BaseActivity.java create mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/BaseDialogFragment.java create mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/BaseFragment.java create mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/UiInfo.java create mode 100644 kotlin-base/app/src/main/res/drawable-hdpi/ic_launcher.png create mode 100644 kotlin-base/app/src/main/res/drawable-mdpi/ic_launcher.png create mode 100644 kotlin-base/app/src/main/res/drawable-xhdpi/ic_launcher.png create mode 100644 kotlin-base/app/src/main/res/drawable-xxhdpi/ic_launcher.png create mode 100644 kotlin-base/app/src/main/res/drawable-xxxhdpi/ic_launcher.png create mode 100644 kotlin-base/app/src/main/res/layout/activity_main.xml create mode 100644 kotlin-base/app/src/main/res/layout/dialog_test.xml create mode 100644 kotlin-base/app/src/main/res/layout/fragment_main.xml create mode 100644 kotlin-base/app/src/main/res/values/dimens.xml create mode 100644 kotlin-base/app/src/main/res/values/strings.xml create mode 100644 kotlin-base/app/src/main/res/values/styles.xml create mode 100644 kotlin-base/app/src/main/res/values/themes.xml create mode 100644 kotlin-base/app/src/main/res/values/tokens.xml create mode 100644 kotlin-base/app/src/test/java/com/flatstack/android/utils/storage/RuntimeStorage.java create mode 100644 kotlin-base/build.gradle create mode 100644 kotlin-base/circle.yml create mode 100644 kotlin-base/deps.gradle create mode 100644 kotlin-base/gradle/wrapper/gradle-wrapper.jar create mode 100644 kotlin-base/gradle/wrapper/gradle-wrapper.properties create mode 100755 kotlin-base/gradlew create mode 100644 kotlin-base/gradlew.bat create mode 100644 kotlin-base/keystore.properties create mode 100644 kotlin-base/proguard.cfg create mode 100644 kotlin-base/settings.gradle 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 [![Build Status](https://travis-ci.org/fs/android-base.png)](https://travis-ci.org/fs/android-base/pull_requests) +* Circle CI [![Build Status](https://circleci.com/gh/fs/android-base.png?style=shield&circle-token=c932b3e8650c436df970e9d1e9e06e8ef8fc9893)](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..93563d5 --- /dev/null +++ b/kotlin-base/app/build.gradle @@ -0,0 +1,125 @@ +apply plugin: 'com.android.application' +apply plugin: 'me.tatarka.retrolambda' +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) + } + + productFlavors { + staging { + buildConfigField "String", "API_URL", "\"https://example-staging.com\"" + applicationIdSuffix ".staging" + } + + production { + buildConfigField "String", "API_URL", "\"https://example.com\"" + } + } + +// 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 + compile 'com.github.bumptech.glide:glide:3.7.0' + + testCompile unitTestLibs + androidTestCompile androidTestsLibs +} + +// 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 0000000000000000000000000000000000000000..5c63bc5e8c8c0cf90e8398e5de9aee577e987f6b GIT binary patch literal 47072 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 + TestDialog.show("Example Hello", "Ublyudok, mat' tvoyu, a nu idi syuda, " + + "govno sobachye, reshil ko mne lezt'? Ti, zasranec vonyuchiy, mat' tvoyu, a?", + getFragmentManager()) + ); + + 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.java b/kotlin-base/app/src/main/java/com/flatstack/android/main_screen/TestDialog.java new file mode 100644 index 0000000..421d99b --- /dev/null +++ b/kotlin-base/app/src/main/java/com/flatstack/android/main_screen/TestDialog.java @@ -0,0 +1,54 @@ +package com.flatstack.android.main_screen; + +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.FragmentManager; +import android.view.View; +import android.widget.TextView; + +import com.flatstack.android.R; +import com.flatstack.android.utils.ui.BaseDialogFragment; + +import butterknife.Bind; + +public class TestDialog extends BaseDialogFragment { + + private static final String KEY_TITLE = "dialogTitle"; + private static final String KEY_MESSAGE = "dialogMessage"; + + @Bind(R.id.dialog_title) TextView uiTitle; + @Bind(R.id.dialog_message) TextView uiMessage; + + private String title; + private String message; + + @Override public int getLayoutRes() { + return R.layout.dialog_test; + } + + public static void show(@Nullable String title, + @Nullable String message, + @NonNull FragmentManager fm) { + + TestDialog dialog = new TestDialog(); + + Bundle args = new Bundle(); + args.putString(KEY_TITLE, title != null ? title : ""); + args.putString(KEY_MESSAGE, message != null ? message : ""); + dialog.setArguments(args); + + dialog.show(fm, TestDialog.class.getName()); + } + + @Override protected void parseArguments(@NonNull Bundle args) { + title = getArguments().getString(KEY_TITLE); + message = getArguments().getString(KEY_MESSAGE); + } + + @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + uiTitle.setText(title != null ? title : ""); + uiMessage.setText(message != null ? message : ""); + } +} diff --git a/kotlin-base/app/src/main/java/com/flatstack/android/utils/Keyboard.java b/kotlin-base/app/src/main/java/com/flatstack/android/utils/Keyboard.java new file mode 100644 index 0000000..295b3e0 --- /dev/null +++ b/kotlin-base/app/src/main/java/com/flatstack/android/utils/Keyboard.java @@ -0,0 +1,40 @@ +package com.flatstack.android.utils; + +import android.app.Activity; +import android.content.Context; +import android.support.annotation.NonNull; +import android.view.View; +import android.view.inputmethod.InputMethodManager; + +public class Keyboard { + + public static void hide(@NonNull Activity activity) { + InputMethodManager inputManager = (InputMethodManager) activity + .getSystemService(Context.INPUT_METHOD_SERVICE); + View v = activity.getCurrentFocus(); + if (v != null) { + inputManager.hideSoftInputFromWindow(v.getWindowToken(), 0); + } + } + + public static void hide(@NonNull View view) { + InputMethodManager imm = (InputMethodManager) view.getContext() + .getSystemService(Context.INPUT_METHOD_SERVICE); + if (!imm.isActive()) { + return; + } + imm.hideSoftInputFromWindow(view.getWindowToken(), 0); + } + + public static void show(@NonNull Context context) { + InputMethodManager imm = (InputMethodManager) context + .getSystemService(Context.INPUT_METHOD_SERVICE); + imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); + } + + public static void show(@NonNull View view) { + InputMethodManager inputManager = (InputMethodManager) view.getContext() + .getSystemService(Context.INPUT_METHOD_SERVICE); + inputManager.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT); + } +} diff --git a/kotlin-base/app/src/main/java/com/flatstack/android/utils/Lists.java b/kotlin-base/app/src/main/java/com/flatstack/android/utils/Lists.java new file mode 100644 index 0000000..ef6dac9 --- /dev/null +++ b/kotlin-base/app/src/main/java/com/flatstack/android/utils/Lists.java @@ -0,0 +1,24 @@ +package com.flatstack.android.utils; + +import android.support.annotation.NonNull; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +/** + * Created by adel on 6/7/14 + */ +public class Lists { + @SafeVarargs @NonNull public static List mutableOf(@NonNull T... objects) { + List list = new ArrayList<>(objects.length); + Collections.addAll(list, objects); + return list; + } + + @SafeVarargs @NonNull public static List add(@NonNull List list, + @NonNull T... objects) { + Collections.addAll(list, objects); + return list; + } +} diff --git a/kotlin-base/app/src/main/java/com/flatstack/android/utils/Views.java b/kotlin-base/app/src/main/java/com/flatstack/android/utils/Views.java new file mode 100644 index 0000000..0ff47cf --- /dev/null +++ b/kotlin-base/app/src/main/java/com/flatstack/android/utils/Views.java @@ -0,0 +1,24 @@ +package com.flatstack.android.utils; + +import android.app.Activity; +import android.support.annotation.NonNull; +import android.view.View; +import android.view.ViewGroup; + +/** + * Created by adelnizamutdinov on 04/12/14 + */ +public class Views { + + @NonNull public static View root(@NonNull Activity activity) { + return activity.findViewById(android.R.id.content); + } + + public static void setHeight(@NonNull View view, int height) { + final ViewGroup.LayoutParams params = view.getLayoutParams(); + if (params != null) { + params.height = height; + view.setLayoutParams(params); + } + } +} diff --git a/kotlin-base/app/src/main/java/com/flatstack/android/utils/recycler_view/BaseAdapter.java b/kotlin-base/app/src/main/java/com/flatstack/android/utils/recycler_view/BaseAdapter.java new file mode 100644 index 0000000..22b78d7 --- /dev/null +++ b/kotlin-base/app/src/main/java/com/flatstack/android/utils/recycler_view/BaseAdapter.java @@ -0,0 +1,60 @@ +package com.flatstack.android.utils.recycler_view; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v7.widget.RecyclerView; +import android.view.ViewGroup; + +import java.util.List; + +import rx.functions.Func1; + +/** + * Created by ereminilya on 14/12/16. + */ +public class BaseAdapter> extends RecyclerView.Adapter { + + @NonNull private final Func1 func0; + @NonNull private final List items; + + @Nullable private OnItemClickListener onItemClickListener; + + public BaseAdapter(@NonNull List items, @NonNull Func1 func0) { + this(items, func0, null); + } + + public BaseAdapter(@NonNull List items, @NonNull Func1 func0, + @Nullable OnItemClickListener onItemClickListener) { + this.items = items; + this.func0 = func0; + this.onItemClickListener = onItemClickListener; + } + + @Override public VH onCreateViewHolder(ViewGroup viewGroup, int holderType) { + VH holder = func0.call(viewGroup); + holder.itemView.setOnClickListener(v -> { + int position = holder.getAdapterPosition(); + if (position != RecyclerView.NO_POSITION && onItemClickListener != null) { + onItemClickListener.onItemClick(getData().get(position)); + } + }); + return holder; + } + + @Override public void onBindViewHolder(VH vh, int position) { + vh.bind(items.get(position)); + } + + @Override public int getItemCount() { + return items.size(); + } + + public List getData() { + return items; + } + + public void add(T item) { + 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.java b/kotlin-base/app/src/main/java/com/flatstack/android/utils/recycler_view/BaseHolder.java new file mode 100644 index 0000000..bf1a477 --- /dev/null +++ b/kotlin-base/app/src/main/java/com/flatstack/android/utils/recycler_view/BaseHolder.java @@ -0,0 +1,16 @@ +package com.flatstack.android.utils.recycler_view; + +import android.support.v7.widget.RecyclerView; +import android.view.View; + +/** + * Created by ereminilya on 14/12/16. + */ +public abstract class BaseHolder extends RecyclerView.ViewHolder { + + public BaseHolder(View itemView) { + super(itemView); + } + + protected abstract void bind(T item); +} diff --git a/kotlin-base/app/src/main/java/com/flatstack/android/utils/recycler_view/OnItemClickListener.java b/kotlin-base/app/src/main/java/com/flatstack/android/utils/recycler_view/OnItemClickListener.java new file mode 100644 index 0000000..79a8305 --- /dev/null +++ b/kotlin-base/app/src/main/java/com/flatstack/android/utils/recycler_view/OnItemClickListener.java @@ -0,0 +1,11 @@ +package com.flatstack.android.utils.recycler_view; + +import android.support.annotation.NonNull; + +/** + * Created by ereminilya on 14/12/16. + */ +public interface OnItemClickListener { + + void onItemClick(@NonNull T item); +} diff --git a/kotlin-base/app/src/main/java/com/flatstack/android/utils/storage/IStorage.java b/kotlin-base/app/src/main/java/com/flatstack/android/utils/storage/IStorage.java new file mode 100644 index 0000000..aa0bfcb --- /dev/null +++ b/kotlin-base/app/src/main/java/com/flatstack/android/utils/storage/IStorage.java @@ -0,0 +1,40 @@ +package com.flatstack.android.utils.storage; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import java.lang.reflect.Type; +import java.util.List; + +/** + * Created by ereminilya on 8/1/17. + */ + +public interface IStorage { + + @Nullable T get(@NonNull String key, @NonNull Type type); + + void put(@NonNull String key, @NonNull T item); + + void putString(@NonNull final String key, @NonNull String str); + + @Nullable String getString(@NonNull String key); + + void putLong(@NonNull String key, long number); + + long getLong(@NonNull String key, long defaultValue); + + void putInt(@NonNull String key, int number); + + int getInt(@NonNull String key, int defaultValue); + + void putBoolean(@NonNull String key, boolean value); + + boolean getBoolean(@NonNull String key, boolean defaultValue); + + void remove(@NonNull String key); + + void putCollection(@NonNull String key, @NonNull List items); + + @Nullable List getCollection(@NonNull String key, @NonNull Type type); +} diff --git a/kotlin-base/app/src/main/java/com/flatstack/android/utils/storage/Storage.java b/kotlin-base/app/src/main/java/com/flatstack/android/utils/storage/Storage.java new file mode 100644 index 0000000..b395c3f --- /dev/null +++ b/kotlin-base/app/src/main/java/com/flatstack/android/utils/storage/Storage.java @@ -0,0 +1,83 @@ +package com.flatstack.android.utils.storage; + +import android.content.SharedPreferences; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import com.google.gson.Gson; + +import java.lang.reflect.Type; +import java.util.List; + +/** + * Created by ereminilya on 8/1/17. + */ + +public class Storage implements IStorage { + + private final SharedPreferences sp; + private final Gson gson; + + public Storage(SharedPreferences sp, Gson gson) { + this.sp = sp; + this.gson = gson; + } + + @Override + @Nullable public T get(@NonNull String key, @NonNull Type type) { + String json = sp.getString(key, ""); + if ("".equals(json)) return null; + else { + return gson.fromJson(json, type); + } + } + + @Override public void put(@NonNull String key, @NonNull Object items) { + sp.edit().putString(key, gson.toJson(items)).apply(); + } + + @Override public void putString(@NonNull String key, @NonNull String str) { + sp.edit().putString(key, str).apply(); + } + + @Nullable @Override public String getString(@NonNull String key) { + return sp.getString(key, null); + } + + @Override public void putLong(@NonNull String key, long number) { + sp.edit().putLong(key, number).apply(); + } + + @Override public long getLong(@NonNull String key, long defaultValue) { + return sp.getLong(key, defaultValue); + } + + @Override public void putInt(@NonNull String key, int number) { + sp.edit().putInt(key, number).apply(); + } + + @Override public int getInt(@NonNull String key, int defaultValue) { + return sp.getInt(key, defaultValue); + } + + @Override public void putBoolean(@NonNull String key, boolean value) { + sp.edit().putBoolean(key, value).apply(); + } + + @Override public boolean getBoolean(@NonNull String key, boolean defaultValue) { + return sp.getBoolean(key, defaultValue); + } + + @Override public void remove(@NonNull String key) { + sp.edit().remove(key).apply(); + } + + public void putCollection(@NonNull String key, @NonNull List items) { + put(key, items); + } + + @Override public List getCollection(@NonNull String key, @NonNull Type type) { + 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.java b/kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/BaseActivity.java new file mode 100644 index 0000000..0d9876c --- /dev/null +++ b/kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/BaseActivity.java @@ -0,0 +1,81 @@ +package com.flatstack.android.utils.ui; + +import android.content.Context; +import android.os.Bundle; +import android.support.annotation.NonNull; +import android.support.v7.app.AppCompatActivity; +import android.support.v7.widget.Toolbar; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; + +import com.flatstack.android.R; +import com.flatstack.android.utils.Keyboard; + +import butterknife.Bind; +import butterknife.ButterKnife; + +public abstract class BaseActivity extends AppCompatActivity { + + @Bind(R.id.toolbar) View toolbar; + + @NonNull public abstract UiInfo getUiInfo(); + + @Override protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setContentView(getUiInfo().getLayoutRes()); + ButterKnife.bind(this); + if (getIntent() != null && getIntent().getExtras() != null) { + parseArguments(getIntent().getExtras()); + } + if (toolbar != null) { + setSupportActionBar((Toolbar) toolbar); + if (getUiInfo().getTitleRes() != 0) { + setTitle(getUiInfo().getTitleRes()); + } else if (getUiInfo().getTitle() != null) { + setTitle(getUiInfo().getTitle()); + } + if (getUiInfo().isHasBackButton()) { + getSupportActionBar().setDisplayHomeAsUpEnabled(true); + } + } + if (savedInstanceState != null) { + restoreState(savedInstanceState); + } + } + + @Override public void setContentView(int layoutResID) { + super.setContentView(layoutResID); + ButterKnife.bind(this); + } + + protected void restoreState(@NonNull Bundle savedState) { + } + + protected void parseArguments(@NonNull Bundle extras) { + throw new IllegalStateException("should be overridden"); + } + + @Override public boolean onCreateOptionsMenu(Menu menu) { + if (getUiInfo().getMenuRes() != 0) { + MenuInflater inflater = getMenuInflater(); + inflater.inflate(getUiInfo().getMenuRes(), menu); + return true; + } + return super.onCreateOptionsMenu(menu); + } + + @Override public boolean onOptionsItemSelected(MenuItem item) { + if (item.getItemId() == android.R.id.home && getUiInfo().isHasBackButton()) { + Keyboard.hide(this); + finish(); + return true; + } + return super.onOptionsItemSelected(item); + } + + public Context context() { + return this; + } +} diff --git a/kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/BaseDialogFragment.java b/kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/BaseDialogFragment.java new file mode 100644 index 0000000..d09d698 --- /dev/null +++ b/kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/BaseDialogFragment.java @@ -0,0 +1,84 @@ +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.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.DialogFragment; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentActivity; +import android.support.v4.app.FragmentTransaction; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; + +import butterknife.ButterKnife; + +public abstract class BaseDialogFragment extends DialogFragment { + + @LayoutRes public abstract int getLayoutRes(); + + protected static T show(@NonNull T dialogFragment, + @NonNull FragmentActivity activity) { + FragmentTransaction ft = activity.getSupportFragmentManager().beginTransaction(); + Fragment prev = activity.getSupportFragmentManager() + .findFragmentByTag(dialogFragment.getClass().getName()); + if (prev != null) { + ft.remove(prev); + DialogFragment df = (DialogFragment) prev; + df.dismissAllowingStateLoss(); + } + ft.addToBackStack(null); + dialogFragment.show(ft, dialogFragment.getClass().getName()); + return dialogFragment; + } + + @Override public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + if (getArguments() != null) { + parseArguments(getArguments()); + } + if (savedInstanceState != null) { + restoreState(savedInstanceState); + } + } + + protected void restoreState(@NonNull Bundle savedState) { + } + + protected void parseArguments(@NonNull Bundle args) { + throw new IllegalStateException("should be overridden"); + } + + @NonNull @Override + public Dialog onCreateDialog(Bundle savedInstanceState) { + Dialog dialog = super.onCreateDialog(savedInstanceState); + dialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE); + return dialog; + } + + @Override + public View onCreateView(LayoutInflater inflater, + @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + View v = inflater.inflate(getLayoutRes(), container, false); + ButterKnife.bind(this, v); + return v; + } + + @Override public void onDestroyView() { + ButterKnife.unbind(this); + super.onDestroyView(); + } + + public Context context() { + return getActivity(); + } + + public FragmentActivity activity() { + return getActivity(); + } +} diff --git a/kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/BaseFragment.java b/kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/BaseFragment.java new file mode 100644 index 0000000..2395d3a --- /dev/null +++ b/kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/BaseFragment.java @@ -0,0 +1,40 @@ +package com.flatstack.android.utils.ui; + +import android.content.Context; +import android.os.Bundle; +import android.support.annotation.LayoutRes; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v4.app.Fragment; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import butterknife.ButterKnife; + +public abstract class BaseFragment extends Fragment { + + @LayoutRes public abstract int getLayoutRes(); + + @Override public void onCreate(@Nullable Bundle savedState) { + super.onCreate(savedState); + if (savedState != null) { + restoreState(savedState); + } + } + + public void restoreState(@NonNull Bundle savedState) { + } + + @Nullable @Override public View onCreateView(LayoutInflater inflater, + @Nullable ViewGroup container, + @Nullable Bundle savedInstanceState) { + View rootView = inflater.inflate(getLayoutRes(), container, false); + ButterKnife.bind(this, rootView); + return rootView; + } + + protected Context context() { + return getContext(); + } +} diff --git a/kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/UiInfo.java b/kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/UiInfo.java new file mode 100644 index 0000000..26e2574 --- /dev/null +++ b/kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/UiInfo.java @@ -0,0 +1,63 @@ +package com.flatstack.android.utils.ui; + +import android.support.annotation.LayoutRes; +import android.support.annotation.MenuRes; +import android.support.annotation.NonNull; +import android.support.annotation.StringRes; + +/** + * Created by almaziskhakov on 20/12/2016. + */ + +public class UiInfo { + + @LayoutRes private final int layoutRes; + @StringRes private int titleRes; + @MenuRes private int menuRes; + private String titleStr; + private boolean hasBackButton; + + public UiInfo(@LayoutRes int layoutRes) { + this.layoutRes = layoutRes; + } + + public int getMenuRes() { + return menuRes; + } + + public UiInfo setMenuRes(int menuRes) { + this.menuRes = menuRes; + return this; + } + + public int getLayoutRes() { + return layoutRes; + } + + public int getTitleRes() { + return titleRes; + } + + public UiInfo setTitleRes(int titleRes) { + this.titleRes = titleRes; + return this; + } + + public UiInfo setTitle(@NonNull String title) { + this.titleStr = title; + return this; + } + + public String getTitle() { + return titleStr; + } + + public boolean isHasBackButton() { + return hasBackButton; + } + + public UiInfo enableBackButton() { + this.hasBackButton = true; + return this; + } +} 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 0000000000000000000000000000000000000000..a6ed594f7ebac23f6601273237f0f923fdebdb18 GIT binary patch literal 599 zcmeAS@N?(olHy`uVBq!ia0vp^9w5xY0wn)GsXhaw6p}rHd>I(3)EF2VS{N990fib~ zFff!FFfhDIU|_JC!N4G1FlSew4N!t9$=lt9;eUJonf*W>XMsm#F#`j)FbFd;%$g&? zz`%Ik)5S3)%9+9)`2S*NVElSc0RgUlb;YPlwExYS#pf50h8 zS*ycz-)H2mZ-j176-dYUG$0!nS%7A-K~7R%w=mU zHU`|XNI2Nl|8d#AmpA|2nI+DxTEUwepL%Fkf8h?jww@J+&s+L8)~W3Hb#dQ?#|x%> z+Gutx^jqblW`4Pw%1`H9pBvR0^5Q-R?_|Z}jMJyAyu8SDt`O_DlM@qHxIdLD75Kom zkkRPPhXflP`5S7!^(Wl^={<68O*zfIQd#zQ6ZE3MQscv)-4wf6?cIvFkfmn?z&=TXmoNt-5&610zv4Ca2dI zEBB{xU%$!2;p_hV@1#w|3zHl&46NQy-!g~0cH3Wdp_$*k>y!^3w=!Dqq_xDUUUiA} iR#-4k;QD*0WbcEP)004R> z004l5008;`004mK004C`008P>0026e000+ooVrmw00002VoOIv0RM-N%)bBt010qN zS#tmY3ljhU3ljkVnw%H_00BrzL_t(o!|j#7O2beThXdAB`U3t#U40RK1JTtL87p}J0000I(3)EF2VS{N990fib~ zFff!FFfhDIU|_JC!N4G1FlSew4N!t9$=lt9;eUJonf*W>XMsm#F#`j)FbFd;%$g$s zRFUuL;uunK>+Oxb*};Jl$3I$FtakQoy(J?1gJzZgtGA&+D)gDe_gmtOUTZF>&f+`WQ~8pcG5C*) z=KUQ{_UN=}#%h87BKPO6lu5IaU*pi=UU8{Qd>Fznj*R~fd(7fz@ z^K5+it?dO%_MG{C(^w4g{uEo?Py8`(gi{ zgf``s#^+UX)^Gl1%ve^~MZRyw z@$`2t499(cKkV2Z$C$6wAP7{k=%wg|4u-PX-wq4z6aFhD{Y?}a!9Uy?s?z=75eZxMG`L|c@)?*O89ZJ6T-G@yGywpADo=y} literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..fffa6a6a28dabbec2285e0e319469eeca30b9cb9 GIT binary patch literal 1273 zcmeAS@N?(olHy`uVBq!ia0vp^6F``Q1xWh(YZ(J6g=CK)Uj~LMH3o);76yi2K%s^g z3=E|P3=FRl7#OT(FffQ0%-I!a1C(G&@^*J&_}|`tWO>_%)r1c48n{Iv*t)J zFt8MQx;TbZ+jxFf6^s#yC6V@Np+5CZQLPnS5mCSbx8_^ZDFj zwaR%n&%XV8)^fjPdDZ#qb7uv;c0O6^(4^u4M-vq|C&5q9BhFO z>a!T`98h{@(0X`Bgz}&NZF%vfcaFZ7oT~XQ#V+ID|G2!bJs-r6J(bzccG_I@$_Zwl zS?6~cS?t@mpln{!ET7`gfSP2-w{y~O%}LBQ05W?u_us8AZu;u@;Vi3c!qT4_96+Ul zVe?Y=J(w&1`Bds1wwQLyoD&8A_+NZKWj;Ua&50{D4=ndQo0_kek^e0EXZhYw$&=p- zy*~Q$jO>nfzL>8^%;g=G{g$29uYNq6TdKq;Uhc~=bMcUZON~wD`*tqyQ-|KeTrKTIYGc;8NS`dq18viOnrZxF~%;=E9uGd}l!VXBAy)^vtjMywZ)ouGBy@ zqC0!uk7vp+n3frrCv5Tgy!L145A*%@#q8gzpGwCkImkWVV0ieavBZZ{Ku4UOsb%&t zJ^J(G#|tw4yB;-t%-{TA_WCT7f@O=I`4_kf{L#{p*;c4m`q_W8szjaWw0XBHjGiuv z%MSRXl@pio;CW~GYP*#QXD`o+pZZqcnNzQIbB$rdzMTpN=Vj;g{7LKotoy9#nRR@m z?%a3n(NA_=;CWyr_R(0)u4FBkHSXru7Ru&kxGIM+ z{gB@NQGMM*u4x5=(>F9!-Z}Zj^YD#!vn>w_uRPwzc>iU}R?hRu41aI5%scYI}s93gKn_c-m8@%*FPD^5+A?|8u~ z=WyAcmg6#U^U4hy*MH=l7PK|l=A+qXuOIxAR_(iX$adma8}Z0zobS?#AAMW%uu0QC z^yJq0@gL1rPmH!Xah5as>QQ^2pPa0FPiBT$Prfs6MK1H6yzpBcMb>sI+wVvKhz}exc>K+U+jOn6`!cx@2-^kmw9jNBlp@TT5^dVKlRcye+FyU zz3kWiVB>t6$$XmNmB5Gor8@hs#1_c+Ei~T|nJlk%YRju~nN1I$d*7MgmsmPM__e8o zv-^{a`nf;){Y;A_j$dn7&7UmMXA#tXy5f-S|BHfK)H7@kXjiBgvDKexinnQcwXx;f z0quzJ4=V4b+I+IJD?6_*=Xks6I`f)iuLa%fHa!sMz7;7PdSScrz5OW%^~JvU_;Pe* zJx%4V+qhueEP*SsAOGoc*M00SS^H+$Dc7+7b3Si-YP?3@0j5T{>%Mu-ojE2U9nuMr?F(YHC$Cgma zl3jL#%Fc{+qA)R(LB})xfS&U_KYZ@z{kgBtIiGXyx#xY8khYe>0@4Bi00>*b&Fwk4 z~gm7%K&go69B%U0KhOC03^f6^>)S_z~y6Y zX$~9^()yb@6@M7q6%7D_;KKj`IbIKma|V|U7? z*y7b&c>XgK{~G-nbh0ZqhS|R#aG#EO|CvQXH^scfP<>x^h&|6J@?sqQ!*IB$f9g;c|wCX-c;?1d>I>bJVgl7Nq8dG&SjftnunZ(!t(781u)x=A> z>R>jN%h^3kqW4IH-Zn?;y1tfZcXB6vi-d?n=~j1BkR&0K=Q{SEKH5kfGGqU6M0=X&nrRlGD(b=rx2H;EyBp6HOmU)L`Z zymG~=DMoZ0g5A!~Q7`Gitb?4${*gRiC#jBD`*bFaTUwz6Hr-O{xY!`*A~!M4-gR6w zx)vY2V^V#zwvDF(YD{nBRZ9!|8F)MyoX6Bac^84Blf@5JRFSZz>IP?Yuwp2lm^6ry zM4kxZM!;j68t@O{GANIE7Fg@}Y1t!@me?lGLq5de9*9HEfXmEA&a-BYkRQAS*+OA- z&z5|+3`J{$eY>Ewwvsgy(_-+*d=$vzEpEc#xYoI&EJlF_`^U^!{&*EP#q6i|&fX;0$(E3-nLFft#|K@HDa2+EAvr^x3=!v+l$^1IGl@An4wf@C62Hgf1-j} z;7W@i-^U~+?8E82`P@9b4Yq96zIEiF-MI*63Yu~Z7$1%=jT?IDr(g<_sp(r=G1g0t znk;nAA?}+Smr^?4xnn53n29o7Ei~uSG|NBfx)*K>S1E7F#B@OYbWKM+pCmZgoJ`LC zh)_Af8#&w)Wc#T*i`*=->01wmP%&+Pe!~aiV|(0JvO6MgUN_XUv(XuG)P8a-1;8%P zoi$vcB#7YVgw)>-L57KH-l`)AKVylMA~G$TbwJmAY}@Tvg|D0Ll)X6+QxlJ<#r~CSbev%<{J}-meP0bs~-YzI6_*LWNyW;*4KT5Q_IFO-b-T1 z5{iSOxaF{zHma+1;G3?HXfFZUk3LcCL+iCO*5IztaIa>9Eq005AduV$_BoT9>^0@> z%dJ|>>K(ijjBzV_@eJZF=F;kwx9`zD{Cz@h>UZc4VgV2arQHFcd*s?|t-24$AEIZj zaBbgBGu|W^=AX=zpou7<>MU^+)(N*#ua4&sgZ{S2_~Aq?bPJ@Ytnat-gfMCl1N-WQ zslEnjIgVT1NE(}4v~ysV_p*P;=C(H4;ORm(8>DoPe9drne=UZ111HtqV35X!we@se z*yI%-YN5Y%-O=X<_1hBHOHLtjcivI^yM*yAx literal 0 HcmV?d00001 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..ff25940 --- /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 item) { + map.put(key, item); + } + + @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..99ad0e6 --- /dev/null +++ b/kotlin-base/build.gradle @@ -0,0 +1,20 @@ +buildscript { + repositories { + jcenter() + } + + dependencies { + classpath 'com.android.tools.build:gradle:3.0.0-alpha1' + classpath 'me.tatarka:gradle-retrolambda:3.2.5' // use Java 8 lambdas on android + classpath('com.stanfy.spoon:spoon-gradle-plugin:1.2.2') { + exclude module: 'guava' + } + classpath 'com.google.guava:guava:17.0' + } +} + +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 0000000000000000000000000000000000000000..3c7abdf12790879c06b07176de29647f77aa4129 GIT binary patch literal 51106 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

CflOl1i4(-2^1KXo)&I5QlgjRKFQgM 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 z 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%0DvTus6fLI&f~`9YxjD=W5pR41LBYQt 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 literal 0 HcmV?d00001 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' From fc5bfa3213be27a4d15e42a70e9f551f149ffc71 Mon Sep 17 00:00:00 2001 From: Ilya Eremin Date: Sun, 21 May 2017 23:01:02 +0300 Subject: [PATCH 2/8] add dimension to flavors --- kotlin-base/app/build.gradle | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/kotlin-base/app/build.gradle b/kotlin-base/app/build.gradle index 93563d5..f09b0d7 100644 --- a/kotlin-base/app/build.gradle +++ b/kotlin-base/app/build.gradle @@ -26,14 +26,18 @@ android { 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" } } @@ -96,6 +100,7 @@ dependencies { 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 compile 'com.github.bumptech.glide:glide:3.7.0' testCompile unitTestLibs From 7b437df093c890f2cffa3b5c55ae85f6efcb9e8b Mon Sep 17 00:00:00 2001 From: Ilya Eremin Date: Sun, 21 May 2017 23:01:14 +0300 Subject: [PATCH 3/8] remove retrolambda --- kotlin-base/app/build.gradle | 1 - kotlin-base/build.gradle | 1 - 2 files changed, 2 deletions(-) diff --git a/kotlin-base/app/build.gradle b/kotlin-base/app/build.gradle index f09b0d7..3774958 100644 --- a/kotlin-base/app/build.gradle +++ b/kotlin-base/app/build.gradle @@ -1,5 +1,4 @@ apply plugin: 'com.android.application' -apply plugin: 'me.tatarka.retrolambda' apply plugin: 'spoon' apply from: 'checkstyle/checkstyle.gradle' diff --git a/kotlin-base/build.gradle b/kotlin-base/build.gradle index 99ad0e6..b7f60b1 100644 --- a/kotlin-base/build.gradle +++ b/kotlin-base/build.gradle @@ -5,7 +5,6 @@ buildscript { dependencies { classpath 'com.android.tools.build:gradle:3.0.0-alpha1' - classpath 'me.tatarka:gradle-retrolambda:3.2.5' // use Java 8 lambdas on android classpath('com.stanfy.spoon:spoon-gradle-plugin:1.2.2') { exclude module: 'guava' } From ac7d7effb796292d9b19f8daca3b728592b9f4d4 Mon Sep 17 00:00:00 2001 From: Ilya Eremin Date: Sun, 21 May 2017 23:02:30 +0300 Subject: [PATCH 4/8] configure kotlin --- kotlin-base/app/build.gradle | 2 ++ kotlin-base/build.gradle | 2 ++ 2 files changed, 4 insertions(+) diff --git a/kotlin-base/app/build.gradle b/kotlin-base/app/build.gradle index 3774958..1e849c9 100644 --- a/kotlin-base/app/build.gradle +++ b/kotlin-base/app/build.gradle @@ -1,4 +1,5 @@ apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' apply plugin: 'spoon' apply from: 'checkstyle/checkstyle.gradle' @@ -104,6 +105,7 @@ dependencies { testCompile unitTestLibs androidTestCompile androidTestsLibs + compile "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" } // Grant animation permissions to avoid test failure because of ui sync. diff --git a/kotlin-base/build.gradle b/kotlin-base/build.gradle index b7f60b1..b745fe0 100644 --- a/kotlin-base/build.gradle +++ b/kotlin-base/build.gradle @@ -1,4 +1,5 @@ buildscript { + ext.kotlin_version = '1.1.2-4' repositories { jcenter() } @@ -9,6 +10,7 @@ buildscript { exclude module: 'guava' } classpath 'com.google.guava:guava:17.0' + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" } } From bdd40b3711028b128ad186dc7472bf6739ba6ba9 Mon Sep 17 00:00:00 2001 From: Ilya Eremin Date: Sun, 21 May 2017 23:38:21 +0300 Subject: [PATCH 5/8] use kotlin annotation processing for butterknife --- kotlin-base/app/build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/kotlin-base/app/build.gradle b/kotlin-base/app/build.gradle index 1e849c9..8731263 100644 --- a/kotlin-base/app/build.gradle +++ b/kotlin-base/app/build.gradle @@ -101,6 +101,7 @@ dependencies { 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 From cb4097becf5340fb52535262594587201623fd05 Mon Sep 17 00:00:00 2001 From: Ilya Eremin Date: Sun, 21 May 2017 23:38:34 +0300 Subject: [PATCH 6/8] convert all java classes into kotlin ones --- .../main/java/com/flatstack/android/App.java | 12 --- .../main/java/com/flatstack/android/App.kt | 11 +++ .../android/main_screen/MainActivity.java | 27 ------ .../android/main_screen/MainActivity.kt | 22 +++++ .../android/main_screen/MainFragment.java | 34 -------- .../android/main_screen/MainFragment.kt | 30 +++++++ .../android/main_screen/TestDialog.java | 54 ------------ .../android/main_screen/TestDialog.kt | 49 +++++++++++ .../com/flatstack/android/utils/Keyboard.java | 40 --------- .../com/flatstack/android/utils/Keyboard.kt | 39 +++++++++ .../com/flatstack/android/utils/Lists.java | 24 ------ .../com/flatstack/android/utils/Views.java | 24 ------ .../utils/recycler_view/BaseAdapter.java | 60 ------------- .../utils/recycler_view/BaseAdapter.kt | 38 +++++++++ .../utils/recycler_view/BaseHolder.java | 16 ---- .../android/utils/recycler_view/BaseHolder.kt | 12 +++ .../recycler_view/OnItemClickListener.java | 11 --- .../recycler_view/OnItemClickListener.kt | 9 ++ .../android/utils/storage/IStorage.java | 40 --------- .../android/utils/storage/IStorage.kt | 36 ++++++++ .../android/utils/storage/Storage.java | 83 ------------------ .../android/utils/storage/Storage.kt | 71 ++++++++++++++++ .../android/utils/ui/BaseActivity.java | 81 ------------------ .../android/utils/ui/BaseActivity.kt | 72 ++++++++++++++++ .../android/utils/ui/BaseDialogFragment.java | 84 ------------------- .../android/utils/ui/BaseDialogFragment.kt | 75 +++++++++++++++++ .../android/utils/ui/BaseFragment.java | 40 --------- .../android/utils/ui/BaseFragment.kt | 31 +++++++ .../flatstack/android/utils/ui/UiInfo.java | 63 -------------- .../com/flatstack/android/utils/ui/UiInfo.kt | 9 ++ .../android/utils/storage/RuntimeStorage.java | 4 +- 31 files changed, 506 insertions(+), 695 deletions(-) delete mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/App.java create mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/App.kt delete mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/main_screen/MainActivity.java create mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/main_screen/MainActivity.kt delete mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/main_screen/MainFragment.java create mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/main_screen/MainFragment.kt delete mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/main_screen/TestDialog.java create mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/main_screen/TestDialog.kt delete mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/utils/Keyboard.java create mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/utils/Keyboard.kt delete mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/utils/Lists.java delete mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/utils/Views.java delete mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/utils/recycler_view/BaseAdapter.java create mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/utils/recycler_view/BaseAdapter.kt delete mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/utils/recycler_view/BaseHolder.java create mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/utils/recycler_view/BaseHolder.kt delete mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/utils/recycler_view/OnItemClickListener.java create mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/utils/recycler_view/OnItemClickListener.kt delete mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/utils/storage/IStorage.java create mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/utils/storage/IStorage.kt delete mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/utils/storage/Storage.java create mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/utils/storage/Storage.kt delete mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/BaseActivity.java create mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/BaseActivity.kt delete mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/BaseDialogFragment.java create mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/BaseDialogFragment.kt delete mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/BaseFragment.java create mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/BaseFragment.kt delete mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/UiInfo.java create mode 100644 kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/UiInfo.kt diff --git a/kotlin-base/app/src/main/java/com/flatstack/android/App.java b/kotlin-base/app/src/main/java/com/flatstack/android/App.java deleted file mode 100644 index e56e149..0000000 --- a/kotlin-base/app/src/main/java/com/flatstack/android/App.java +++ /dev/null @@ -1,12 +0,0 @@ -package com.flatstack.android; - -import android.app.Application; - -public class App extends Application { - - @Override - public void 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/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.java b/kotlin-base/app/src/main/java/com/flatstack/android/main_screen/MainActivity.java deleted file mode 100644 index b0c50d0..0000000 --- a/kotlin-base/app/src/main/java/com/flatstack/android/main_screen/MainActivity.java +++ /dev/null @@ -1,27 +0,0 @@ -package com.flatstack.android.main_screen; - -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.flatstack.android.utils.ui.BaseActivity; -import com.flatstack.android.R; -import com.flatstack.android.utils.ui.UiInfo; - -public class MainActivity extends BaseActivity { - - @NonNull @Override public UiInfo getUiInfo() { - return new UiInfo(R.layout.activity_main) - .setTitleRes(R.string.app_name) - .enableBackButton(); - } - - @Override protected void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (savedInstanceState == null) { - getSupportFragmentManager().beginTransaction() - .replace(R.id.content, new MainFragment()) - .commit(); - } - } -} \ 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.java b/kotlin-base/app/src/main/java/com/flatstack/android/main_screen/MainFragment.java deleted file mode 100644 index 459a8c9..0000000 --- a/kotlin-base/app/src/main/java/com/flatstack/android/main_screen/MainFragment.java +++ /dev/null @@ -1,34 +0,0 @@ -package com.flatstack.android.main_screen; - -import android.os.Bundle; -import android.view.View; -import android.widget.ImageView; - -import com.bumptech.glide.Glide; -import com.flatstack.android.R; -import com.flatstack.android.utils.ui.BaseFragment; - -import butterknife.Bind; - -public class MainFragment extends BaseFragment { - - @Bind(R.id.image) ImageView image; - - @Override public int getLayoutRes() { - return R.layout.fragment_main; - } - - @Override public void onViewCreated(View view, Bundle savedInstanceState) { - 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?", - getFragmentManager()) - ); - - 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/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.java b/kotlin-base/app/src/main/java/com/flatstack/android/main_screen/TestDialog.java deleted file mode 100644 index 421d99b..0000000 --- a/kotlin-base/app/src/main/java/com/flatstack/android/main_screen/TestDialog.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.flatstack.android.main_screen; - -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.app.FragmentManager; -import android.view.View; -import android.widget.TextView; - -import com.flatstack.android.R; -import com.flatstack.android.utils.ui.BaseDialogFragment; - -import butterknife.Bind; - -public class TestDialog extends BaseDialogFragment { - - private static final String KEY_TITLE = "dialogTitle"; - private static final String KEY_MESSAGE = "dialogMessage"; - - @Bind(R.id.dialog_title) TextView uiTitle; - @Bind(R.id.dialog_message) TextView uiMessage; - - private String title; - private String message; - - @Override public int getLayoutRes() { - return R.layout.dialog_test; - } - - public static void show(@Nullable String title, - @Nullable String message, - @NonNull FragmentManager fm) { - - TestDialog dialog = new TestDialog(); - - Bundle args = new Bundle(); - args.putString(KEY_TITLE, title != null ? title : ""); - args.putString(KEY_MESSAGE, message != null ? message : ""); - dialog.setArguments(args); - - dialog.show(fm, TestDialog.class.getName()); - } - - @Override protected void parseArguments(@NonNull Bundle args) { - title = getArguments().getString(KEY_TITLE); - message = getArguments().getString(KEY_MESSAGE); - } - - @Override public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { - super.onViewCreated(view, savedInstanceState); - uiTitle.setText(title != null ? title : ""); - uiMessage.setText(message != null ? message : ""); - } -} 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..6fabb98 --- /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() + + val args = Bundle() + args.putString(KEY_TITLE, title ?: "") + args.putString(KEY_MESSAGE, message ?: "") + dialog.arguments = args + + dialog.show(fm, TestDialog::class.java.name) + } + } +} diff --git a/kotlin-base/app/src/main/java/com/flatstack/android/utils/Keyboard.java b/kotlin-base/app/src/main/java/com/flatstack/android/utils/Keyboard.java deleted file mode 100644 index 295b3e0..0000000 --- a/kotlin-base/app/src/main/java/com/flatstack/android/utils/Keyboard.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.flatstack.android.utils; - -import android.app.Activity; -import android.content.Context; -import android.support.annotation.NonNull; -import android.view.View; -import android.view.inputmethod.InputMethodManager; - -public class Keyboard { - - public static void hide(@NonNull Activity activity) { - InputMethodManager inputManager = (InputMethodManager) activity - .getSystemService(Context.INPUT_METHOD_SERVICE); - View v = activity.getCurrentFocus(); - if (v != null) { - inputManager.hideSoftInputFromWindow(v.getWindowToken(), 0); - } - } - - public static void hide(@NonNull View view) { - InputMethodManager imm = (InputMethodManager) view.getContext() - .getSystemService(Context.INPUT_METHOD_SERVICE); - if (!imm.isActive()) { - return; - } - imm.hideSoftInputFromWindow(view.getWindowToken(), 0); - } - - public static void show(@NonNull Context context) { - InputMethodManager imm = (InputMethodManager) context - .getSystemService(Context.INPUT_METHOD_SERVICE); - imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0); - } - - public static void show(@NonNull View view) { - InputMethodManager inputManager = (InputMethodManager) view.getContext() - .getSystemService(Context.INPUT_METHOD_SERVICE); - inputManager.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT); - } -} 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..a4b422f --- /dev/null +++ b/kotlin-base/app/src/main/java/com/flatstack/android/utils/Keyboard.kt @@ -0,0 +1,39 @@ +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 + if (v != null) { + inputManager.hideSoftInputFromWindow(v.windowToken, 0) + } + } + + fun hide(view: View) { + val imm = view.context + .getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + if (!imm.isActive) { + return + } + 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/Lists.java b/kotlin-base/app/src/main/java/com/flatstack/android/utils/Lists.java deleted file mode 100644 index ef6dac9..0000000 --- a/kotlin-base/app/src/main/java/com/flatstack/android/utils/Lists.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.flatstack.android.utils; - -import android.support.annotation.NonNull; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.List; - -/** - * Created by adel on 6/7/14 - */ -public class Lists { - @SafeVarargs @NonNull public static List mutableOf(@NonNull T... objects) { - List list = new ArrayList<>(objects.length); - Collections.addAll(list, objects); - return list; - } - - @SafeVarargs @NonNull public static List add(@NonNull List list, - @NonNull T... objects) { - Collections.addAll(list, objects); - return list; - } -} diff --git a/kotlin-base/app/src/main/java/com/flatstack/android/utils/Views.java b/kotlin-base/app/src/main/java/com/flatstack/android/utils/Views.java deleted file mode 100644 index 0ff47cf..0000000 --- a/kotlin-base/app/src/main/java/com/flatstack/android/utils/Views.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.flatstack.android.utils; - -import android.app.Activity; -import android.support.annotation.NonNull; -import android.view.View; -import android.view.ViewGroup; - -/** - * Created by adelnizamutdinov on 04/12/14 - */ -public class Views { - - @NonNull public static View root(@NonNull Activity activity) { - return activity.findViewById(android.R.id.content); - } - - public static void setHeight(@NonNull View view, int height) { - final ViewGroup.LayoutParams params = view.getLayoutParams(); - if (params != null) { - params.height = height; - view.setLayoutParams(params); - } - } -} diff --git a/kotlin-base/app/src/main/java/com/flatstack/android/utils/recycler_view/BaseAdapter.java b/kotlin-base/app/src/main/java/com/flatstack/android/utils/recycler_view/BaseAdapter.java deleted file mode 100644 index 22b78d7..0000000 --- a/kotlin-base/app/src/main/java/com/flatstack/android/utils/recycler_view/BaseAdapter.java +++ /dev/null @@ -1,60 +0,0 @@ -package com.flatstack.android.utils.recycler_view; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v7.widget.RecyclerView; -import android.view.ViewGroup; - -import java.util.List; - -import rx.functions.Func1; - -/** - * Created by ereminilya on 14/12/16. - */ -public class BaseAdapter> extends RecyclerView.Adapter { - - @NonNull private final Func1 func0; - @NonNull private final List items; - - @Nullable private OnItemClickListener onItemClickListener; - - public BaseAdapter(@NonNull List items, @NonNull Func1 func0) { - this(items, func0, null); - } - - public BaseAdapter(@NonNull List items, @NonNull Func1 func0, - @Nullable OnItemClickListener onItemClickListener) { - this.items = items; - this.func0 = func0; - this.onItemClickListener = onItemClickListener; - } - - @Override public VH onCreateViewHolder(ViewGroup viewGroup, int holderType) { - VH holder = func0.call(viewGroup); - holder.itemView.setOnClickListener(v -> { - int position = holder.getAdapterPosition(); - if (position != RecyclerView.NO_POSITION && onItemClickListener != null) { - onItemClickListener.onItemClick(getData().get(position)); - } - }); - return holder; - } - - @Override public void onBindViewHolder(VH vh, int position) { - vh.bind(items.get(position)); - } - - @Override public int getItemCount() { - return items.size(); - } - - public List getData() { - return items; - } - - public void add(T item) { - 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/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.java b/kotlin-base/app/src/main/java/com/flatstack/android/utils/recycler_view/BaseHolder.java deleted file mode 100644 index bf1a477..0000000 --- a/kotlin-base/app/src/main/java/com/flatstack/android/utils/recycler_view/BaseHolder.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.flatstack.android.utils.recycler_view; - -import android.support.v7.widget.RecyclerView; -import android.view.View; - -/** - * Created by ereminilya on 14/12/16. - */ -public abstract class BaseHolder extends RecyclerView.ViewHolder { - - public BaseHolder(View itemView) { - super(itemView); - } - - protected abstract void bind(T item); -} 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.java b/kotlin-base/app/src/main/java/com/flatstack/android/utils/recycler_view/OnItemClickListener.java deleted file mode 100644 index 79a8305..0000000 --- a/kotlin-base/app/src/main/java/com/flatstack/android/utils/recycler_view/OnItemClickListener.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.flatstack.android.utils.recycler_view; - -import android.support.annotation.NonNull; - -/** - * Created by ereminilya on 14/12/16. - */ -public interface OnItemClickListener { - - void onItemClick(@NonNull T item); -} 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.java b/kotlin-base/app/src/main/java/com/flatstack/android/utils/storage/IStorage.java deleted file mode 100644 index aa0bfcb..0000000 --- a/kotlin-base/app/src/main/java/com/flatstack/android/utils/storage/IStorage.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.flatstack.android.utils.storage; - -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import java.lang.reflect.Type; -import java.util.List; - -/** - * Created by ereminilya on 8/1/17. - */ - -public interface IStorage { - - @Nullable T get(@NonNull String key, @NonNull Type type); - - void put(@NonNull String key, @NonNull T item); - - void putString(@NonNull final String key, @NonNull String str); - - @Nullable String getString(@NonNull String key); - - void putLong(@NonNull String key, long number); - - long getLong(@NonNull String key, long defaultValue); - - void putInt(@NonNull String key, int number); - - int getInt(@NonNull String key, int defaultValue); - - void putBoolean(@NonNull String key, boolean value); - - boolean getBoolean(@NonNull String key, boolean defaultValue); - - void remove(@NonNull String key); - - void putCollection(@NonNull String key, @NonNull List items); - - @Nullable List getCollection(@NonNull String key, @NonNull Type type); -} 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.java b/kotlin-base/app/src/main/java/com/flatstack/android/utils/storage/Storage.java deleted file mode 100644 index b395c3f..0000000 --- a/kotlin-base/app/src/main/java/com/flatstack/android/utils/storage/Storage.java +++ /dev/null @@ -1,83 +0,0 @@ -package com.flatstack.android.utils.storage; - -import android.content.SharedPreferences; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; - -import com.google.gson.Gson; - -import java.lang.reflect.Type; -import java.util.List; - -/** - * Created by ereminilya on 8/1/17. - */ - -public class Storage implements IStorage { - - private final SharedPreferences sp; - private final Gson gson; - - public Storage(SharedPreferences sp, Gson gson) { - this.sp = sp; - this.gson = gson; - } - - @Override - @Nullable public T get(@NonNull String key, @NonNull Type type) { - String json = sp.getString(key, ""); - if ("".equals(json)) return null; - else { - return gson.fromJson(json, type); - } - } - - @Override public void put(@NonNull String key, @NonNull Object items) { - sp.edit().putString(key, gson.toJson(items)).apply(); - } - - @Override public void putString(@NonNull String key, @NonNull String str) { - sp.edit().putString(key, str).apply(); - } - - @Nullable @Override public String getString(@NonNull String key) { - return sp.getString(key, null); - } - - @Override public void putLong(@NonNull String key, long number) { - sp.edit().putLong(key, number).apply(); - } - - @Override public long getLong(@NonNull String key, long defaultValue) { - return sp.getLong(key, defaultValue); - } - - @Override public void putInt(@NonNull String key, int number) { - sp.edit().putInt(key, number).apply(); - } - - @Override public int getInt(@NonNull String key, int defaultValue) { - return sp.getInt(key, defaultValue); - } - - @Override public void putBoolean(@NonNull String key, boolean value) { - sp.edit().putBoolean(key, value).apply(); - } - - @Override public boolean getBoolean(@NonNull String key, boolean defaultValue) { - return sp.getBoolean(key, defaultValue); - } - - @Override public void remove(@NonNull String key) { - sp.edit().remove(key).apply(); - } - - public void putCollection(@NonNull String key, @NonNull List items) { - put(key, items); - } - - @Override public List getCollection(@NonNull String key, @NonNull Type type) { - return get(key, type); - } - -} \ No newline at end of file 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..7d86513 --- /dev/null +++ b/kotlin-base/app/src/main/java/com/flatstack/android/utils/storage/Storage.kt @@ -0,0 +1,71 @@ +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, "") + if ("" == json) + return null + else { + return 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.java b/kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/BaseActivity.java deleted file mode 100644 index 0d9876c..0000000 --- a/kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/BaseActivity.java +++ /dev/null @@ -1,81 +0,0 @@ -package com.flatstack.android.utils.ui; - -import android.content.Context; -import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.Toolbar; -import android.view.Menu; -import android.view.MenuInflater; -import android.view.MenuItem; -import android.view.View; - -import com.flatstack.android.R; -import com.flatstack.android.utils.Keyboard; - -import butterknife.Bind; -import butterknife.ButterKnife; - -public abstract class BaseActivity extends AppCompatActivity { - - @Bind(R.id.toolbar) View toolbar; - - @NonNull public abstract UiInfo getUiInfo(); - - @Override protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(getUiInfo().getLayoutRes()); - ButterKnife.bind(this); - if (getIntent() != null && getIntent().getExtras() != null) { - parseArguments(getIntent().getExtras()); - } - if (toolbar != null) { - setSupportActionBar((Toolbar) toolbar); - if (getUiInfo().getTitleRes() != 0) { - setTitle(getUiInfo().getTitleRes()); - } else if (getUiInfo().getTitle() != null) { - setTitle(getUiInfo().getTitle()); - } - if (getUiInfo().isHasBackButton()) { - getSupportActionBar().setDisplayHomeAsUpEnabled(true); - } - } - if (savedInstanceState != null) { - restoreState(savedInstanceState); - } - } - - @Override public void setContentView(int layoutResID) { - super.setContentView(layoutResID); - ButterKnife.bind(this); - } - - protected void restoreState(@NonNull Bundle savedState) { - } - - protected void parseArguments(@NonNull Bundle extras) { - throw new IllegalStateException("should be overridden"); - } - - @Override public boolean onCreateOptionsMenu(Menu menu) { - if (getUiInfo().getMenuRes() != 0) { - MenuInflater inflater = getMenuInflater(); - inflater.inflate(getUiInfo().getMenuRes(), menu); - return true; - } - return super.onCreateOptionsMenu(menu); - } - - @Override public boolean onOptionsItemSelected(MenuItem item) { - if (item.getItemId() == android.R.id.home && getUiInfo().isHasBackButton()) { - Keyboard.hide(this); - finish(); - return true; - } - return super.onOptionsItemSelected(item); - } - - public Context context() { - return this; - } -} 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.java b/kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/BaseDialogFragment.java deleted file mode 100644 index d09d698..0000000 --- a/kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/BaseDialogFragment.java +++ /dev/null @@ -1,84 +0,0 @@ -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.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.app.DialogFragment; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentActivity; -import android.support.v4.app.FragmentTransaction; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.view.Window; - -import butterknife.ButterKnife; - -public abstract class BaseDialogFragment extends DialogFragment { - - @LayoutRes public abstract int getLayoutRes(); - - protected static T show(@NonNull T dialogFragment, - @NonNull FragmentActivity activity) { - FragmentTransaction ft = activity.getSupportFragmentManager().beginTransaction(); - Fragment prev = activity.getSupportFragmentManager() - .findFragmentByTag(dialogFragment.getClass().getName()); - if (prev != null) { - ft.remove(prev); - DialogFragment df = (DialogFragment) prev; - df.dismissAllowingStateLoss(); - } - ft.addToBackStack(null); - dialogFragment.show(ft, dialogFragment.getClass().getName()); - return dialogFragment; - } - - @Override public void onCreate(@Nullable Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - if (getArguments() != null) { - parseArguments(getArguments()); - } - if (savedInstanceState != null) { - restoreState(savedInstanceState); - } - } - - protected void restoreState(@NonNull Bundle savedState) { - } - - protected void parseArguments(@NonNull Bundle args) { - throw new IllegalStateException("should be overridden"); - } - - @NonNull @Override - public Dialog onCreateDialog(Bundle savedInstanceState) { - Dialog dialog = super.onCreateDialog(savedInstanceState); - dialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE); - return dialog; - } - - @Override - public View onCreateView(LayoutInflater inflater, - @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View v = inflater.inflate(getLayoutRes(), container, false); - ButterKnife.bind(this, v); - return v; - } - - @Override public void onDestroyView() { - ButterKnife.unbind(this); - super.onDestroyView(); - } - - public Context context() { - return getActivity(); - } - - public FragmentActivity activity() { - return getActivity(); - } -} 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..91e59a3 --- /dev/null +++ b/kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/BaseDialogFragment.kt @@ -0,0 +1,75 @@ +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) + val df = prev as DialogFragment + df.dismissAllowingStateLoss() + } + ft.addToBackStack(null) + dialogFragment.show(ft, dialogFragment.javaClass.name) + return dialogFragment + } + } +} diff --git a/kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/BaseFragment.java b/kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/BaseFragment.java deleted file mode 100644 index 2395d3a..0000000 --- a/kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/BaseFragment.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.flatstack.android.utils.ui; - -import android.content.Context; -import android.os.Bundle; -import android.support.annotation.LayoutRes; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -import butterknife.ButterKnife; - -public abstract class BaseFragment extends Fragment { - - @LayoutRes public abstract int getLayoutRes(); - - @Override public void onCreate(@Nullable Bundle savedState) { - super.onCreate(savedState); - if (savedState != null) { - restoreState(savedState); - } - } - - public void restoreState(@NonNull Bundle savedState) { - } - - @Nullable @Override public View onCreateView(LayoutInflater inflater, - @Nullable ViewGroup container, - @Nullable Bundle savedInstanceState) { - View rootView = inflater.inflate(getLayoutRes(), container, false); - ButterKnife.bind(this, rootView); - return rootView; - } - - protected Context context() { - return getContext(); - } -} 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.java b/kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/UiInfo.java deleted file mode 100644 index 26e2574..0000000 --- a/kotlin-base/app/src/main/java/com/flatstack/android/utils/ui/UiInfo.java +++ /dev/null @@ -1,63 +0,0 @@ -package com.flatstack.android.utils.ui; - -import android.support.annotation.LayoutRes; -import android.support.annotation.MenuRes; -import android.support.annotation.NonNull; -import android.support.annotation.StringRes; - -/** - * Created by almaziskhakov on 20/12/2016. - */ - -public class UiInfo { - - @LayoutRes private final int layoutRes; - @StringRes private int titleRes; - @MenuRes private int menuRes; - private String titleStr; - private boolean hasBackButton; - - public UiInfo(@LayoutRes int layoutRes) { - this.layoutRes = layoutRes; - } - - public int getMenuRes() { - return menuRes; - } - - public UiInfo setMenuRes(int menuRes) { - this.menuRes = menuRes; - return this; - } - - public int getLayoutRes() { - return layoutRes; - } - - public int getTitleRes() { - return titleRes; - } - - public UiInfo setTitleRes(int titleRes) { - this.titleRes = titleRes; - return this; - } - - public UiInfo setTitle(@NonNull String title) { - this.titleStr = title; - return this; - } - - public String getTitle() { - return titleStr; - } - - public boolean isHasBackButton() { - return hasBackButton; - } - - public UiInfo enableBackButton() { - this.hasBackButton = true; - return this; - } -} 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/test/java/com/flatstack/android/utils/storage/RuntimeStorage.java b/kotlin-base/app/src/test/java/com/flatstack/android/utils/storage/RuntimeStorage.java index ff25940..1c7faae 100644 --- 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 @@ -12,8 +12,8 @@ public class RuntimeStorage implements IStorage { private Map map = new HashMap<>(); - @Override public void put(@NonNull String key, @NonNull T item) { - map.put(key, item); + @Override public void put(@NonNull String key, @NonNull T items) { + map.put(key, items); } @Nullable @Override From d13597208e90b3381c6cccf52348264dce565725 Mon Sep 17 00:00:00 2001 From: Ilya Eremin Date: Mon, 22 May 2017 10:53:56 +0300 Subject: [PATCH 7/8] refactoring --- .../java/com/flatstack/android/utils/storage/Storage.kt | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) 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 index 7d86513..7ee2556 100644 --- 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 @@ -14,11 +14,7 @@ class Storage(private val sp: SharedPreferences, private val gson: Gson) : IStor override fun get(key: String, type: Type): T? { val json = sp.getString(key, "") - if ("" == json) - return null - else { - return gson.fromJson(json, type) - } + return if("" == json) null else gson.fromJson(json, type) } override fun put(key: String, item: T) { From c332d49dbbfcf92848f45282d9b976555913933c Mon Sep 17 00:00:00 2001 From: Ilya Eremin Date: Tue, 23 May 2017 10:30:10 +0300 Subject: [PATCH 8/8] refactoring --- .../com/flatstack/android/main_screen/TestDialog.kt | 12 ++++++------ .../java/com/flatstack/android/utils/Keyboard.kt | 9 +++------ .../flatstack/android/utils/ui/BaseDialogFragment.kt | 11 +++++------ 3 files changed, 14 insertions(+), 18 deletions(-) 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 index 6fabb98..f0dee1c 100644 --- 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 @@ -36,12 +36,12 @@ class TestDialog : BaseDialogFragment() { fun show(title: String?, message: String?, fm: FragmentManager) { - val dialog = TestDialog() - - val args = Bundle() - args.putString(KEY_TITLE, title ?: "") - args.putString(KEY_MESSAGE, message ?: "") - dialog.arguments = args + 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 index a4b422f..8a3f927 100644 --- 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 @@ -11,18 +11,15 @@ object Keyboard { val inputManager = activity .getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager val v = activity.currentFocus - if (v != null) { - inputManager.hideSoftInputFromWindow(v.windowToken, 0) - } + inputManager.hideSoftInputFromWindow(v?.windowToken, 0) } fun hide(view: View) { val imm = view.context .getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager - if (!imm.isActive) { - return + if (imm.isActive) { + imm.hideSoftInputFromWindow(view.windowToken, 0) } - imm.hideSoftInputFromWindow(view.windowToken, 0) } fun show(context: Context) { 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 index 91e59a3..3a5915f 100644 --- 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 @@ -34,14 +34,14 @@ abstract class BaseDialogFragment : DialogFragment() { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val dialog = super.onCreateDialog(savedInstanceState) - dialog.window!!.requestFeature(Window.FEATURE_NO_TITLE) + dialog.window.requestFeature(Window.FEATURE_NO_TITLE) return dialog } - override fun onCreateView(inflater: LayoutInflater?, + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - val v = inflater!!.inflate(layoutRes, container, false) + val v = inflater.inflate(layoutRes, container, false) ButterKnife.bind(this, v) return v } @@ -64,12 +64,11 @@ abstract class BaseDialogFragment : DialogFragment() { .findFragmentByTag(dialogFragment.javaClass.name) if (prev != null) { ft.remove(prev) - val df = prev as DialogFragment - df.dismissAllowingStateLoss() + (prev as DialogFragment).dismissAllowingStateLoss() } ft.addToBackStack(null) dialogFragment.show(ft, dialogFragment.javaClass.name) return dialogFragment } } -} +} \ No newline at end of file