├── app
├── .gitignore
├── src
│ ├── main
│ │ ├── assets
│ │ │ ├── my_text.json
│ │ │ └── .gitignore
│ │ ├── res
│ │ │ ├── raw
│ │ │ │ └── my_raw.json
│ │ │ ├── font
│ │ │ │ └── lato.ttf
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ └── colors.xml
│ │ │ ├── mipmap-hdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-mdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ │ ├── ic_launcher.png
│ │ │ │ └── ic_launcher_round.png
│ │ │ ├── anim
│ │ │ │ ├── layout_animation.xml
│ │ │ │ └── grow.xml
│ │ │ ├── drawable
│ │ │ │ ├── line_drawable.xml
│ │ │ │ ├── thumb_drawable.xml
│ │ │ │ ├── line.xml
│ │ │ │ ├── ic_arrow_back_red_24dp.xml
│ │ │ │ ├── thumb.xml
│ │ │ │ └── layer_launch_screen.xml
│ │ │ ├── mipmap-anydpi-v26
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_launcher_round.xml
│ │ │ ├── xml
│ │ │ │ ├── network_security_config.xml
│ │ │ │ └── lorem_ipsum.xml
│ │ │ ├── layout
│ │ │ │ ├── recycler_view_item.xml
│ │ │ │ ├── activity_main.xml
│ │ │ │ ├── layout_fab_sample.xml
│ │ │ │ └── layout_toolbar.xml
│ │ │ ├── animator
│ │ │ │ └── flip_animation.xml
│ │ │ └── drawable-v24
│ │ │ │ └── ic_launcher_foreground.xml
│ │ └── java
│ │ │ └── com
│ │ │ └── exozet
│ │ │ └── android
│ │ │ └── core
│ │ │ └── demo
│ │ │ ├── App.kt
│ │ │ ├── features
│ │ │ └── realmLiveData
│ │ │ │ ├── StringPresenter.kt
│ │ │ │ └── ViewModelRealmSampleFragment.kt
│ │ │ └── WidgetSampleFragment.kt
│ ├── test
│ │ ├── res
│ │ │ └── values
│ │ │ │ └── strings.xml
│ │ └── java
│ │ │ └── com
│ │ │ └── exozet
│ │ │ └── android
│ │ │ └── core
│ │ │ ├── base
│ │ │ ├── SerializableTest.kt
│ │ │ ├── SerializableTests.kt
│ │ │ └── BaseTest.kt
│ │ │ ├── models
│ │ │ └── ModelTest.kt
│ │ │ ├── sha256Test.kt
│ │ │ ├── NumberTest.kt
│ │ │ └── WhenTests.kt
│ └── androidTest
│ │ └── java
│ │ └── com
│ │ └── exozet
│ │ └── android
│ │ └── core
│ │ └── demo
│ │ └── ExampleInstrumentedTest.kt
├── updateLocalization.sh
└── google-services.json
├── core
├── .gitignore
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── drawable
│ │ │ │ ├── ic_launcher.xml
│ │ │ │ └── ic_share.xml
│ │ │ ├── values-sw600dp
│ │ │ │ └── tablet-config.xml
│ │ │ ├── values
│ │ │ │ ├── strings.xml
│ │ │ │ ├── ltr-config.xml
│ │ │ │ ├── transition_names.xml
│ │ │ │ ├── fonts.xml
│ │ │ │ ├── config.xml
│ │ │ │ ├── integers.xml
│ │ │ │ ├── dimens.xml
│ │ │ │ ├── debug.xml
│ │ │ │ ├── styles.xml
│ │ │ │ └── attr.xml
│ │ │ ├── values-ldrtl
│ │ │ │ └── rtl-config.xml
│ │ │ ├── anim
│ │ │ │ ├── side_in.xml
│ │ │ │ ├── side_out.xml
│ │ │ │ ├── fade_in.xml
│ │ │ │ ├── fade_out.xml
│ │ │ │ ├── shake.xml
│ │ │ │ ├── slide_enter.xml
│ │ │ │ ├── slide_exit.xml
│ │ │ │ ├── slide_in.xml
│ │ │ │ ├── slide_out.xml
│ │ │ │ ├── slide_pop_enter.xml
│ │ │ │ ├── slide_pop_exit.xml
│ │ │ │ ├── slide_exit_left.xml
│ │ │ │ ├── slide_enter_right.xml
│ │ │ │ ├── slide_in_top.xml
│ │ │ │ ├── slide_out_bottom.xml
│ │ │ │ ├── enter_from_left.xml
│ │ │ │ ├── enter_from_right.xml
│ │ │ │ ├── exit_to_bottom.xml
│ │ │ │ ├── enter_from_bottom.xml
│ │ │ │ ├── pop_out.xml
│ │ │ │ ├── shrink.xml
│ │ │ │ ├── exit_to_left.xml
│ │ │ │ ├── exit_to_right.xml
│ │ │ │ ├── pop_in.xml
│ │ │ │ ├── grow.xml
│ │ │ │ ├── custom_exit_to_bottom.xml
│ │ │ │ ├── custom_enter_from_bottom.xml
│ │ │ │ ├── custom_exit_to_top.xml
│ │ │ │ └── custom_enter_from_top.xml
│ │ │ ├── xml
│ │ │ │ └── backup.xml
│ │ │ ├── values-ldltr
│ │ │ │ └── ltr-config.xml
│ │ │ ├── animator
│ │ │ │ ├── fade_in.xml
│ │ │ │ ├── fade_out.xml
│ │ │ │ ├── alpha_in.xml
│ │ │ │ ├── alpha_out.xml
│ │ │ │ ├── shrink_scale_animation.xml
│ │ │ │ ├── flip_animation.xml
│ │ │ │ └── raise.xml
│ │ │ ├── values-v21
│ │ │ │ ├── styles.xml
│ │ │ │ └── themes.xml
│ │ │ ├── transition
│ │ │ │ └── move.xml
│ │ │ └── layout
│ │ │ │ ├── activity_main.xml
│ │ │ │ ├── fragment_markdown.xml
│ │ │ │ └── fragment_raw_output.xml
│ │ ├── java
│ │ │ └── com
│ │ │ │ └── exozet
│ │ │ │ └── android
│ │ │ │ └── core
│ │ │ │ ├── services
│ │ │ │ ├── connectivity
│ │ │ │ │ ├── NoNetworkException.kt
│ │ │ │ │ ├── LiveNetworkMonitor.kt
│ │ │ │ │ └── NetworkChangeReceiver.kt
│ │ │ │ ├── network
│ │ │ │ │ ├── fcm
│ │ │ │ │ │ ├── NotificationContent.kt
│ │ │ │ │ │ └── FcmMessage.kt
│ │ │ │ │ ├── interceptors
│ │ │ │ │ │ ├── EmptyInterceptor.kt
│ │ │ │ │ │ ├── ContentTypeInterceptor.kt
│ │ │ │ │ │ ├── ErrorInterceptor.kt
│ │ │ │ │ │ ├── AuthorizationInterceptor.kt
│ │ │ │ │ │ ├── LoadingInterceptor.kt
│ │ │ │ │ │ ├── InterceptorFactory.kt
│ │ │ │ │ │ └── UrlDecodedInterceptor.kt
│ │ │ │ │ ├── GoogleApi.kt
│ │ │ │ │ └── NullableConverterFactory.java
│ │ │ │ ├── notifications
│ │ │ │ │ └── FBNotificationJobService.kt
│ │ │ │ ├── sms
│ │ │ │ │ └── SmsReceiver.kt
│ │ │ │ └── crypto
│ │ │ │ │ └── Blowfish.kt
│ │ │ │ ├── interfaces
│ │ │ │ ├── BackPress.kt
│ │ │ │ ├── Command.java
│ │ │ │ ├── LayoutProvider.kt
│ │ │ │ ├── annotations
│ │ │ │ │ ├── Visibility.kt
│ │ │ │ │ ├── Transit.kt
│ │ │ │ │ ├── HapticFeedback.kt
│ │ │ │ │ └── ScreenOrientation.kt
│ │ │ │ ├── ChainableCommand.java
│ │ │ │ ├── StorageProvider.java
│ │ │ │ ├── DispatchTouchEventHandler.kt
│ │ │ │ ├── DispatchTouchEvent.kt
│ │ │ │ └── Repository.kt
│ │ │ │ ├── gson
│ │ │ │ ├── Exclude.kt
│ │ │ │ ├── GsonExclusionStrategy.kt
│ │ │ │ └── GsonProvider.kt
│ │ │ │ ├── extensions
│ │ │ │ ├── MutablieList+Extensions.kt
│ │ │ │ ├── Any+Extensions.kt
│ │ │ │ ├── List+Extensions.kt
│ │ │ │ ├── Snackbar+Extensions.kt
│ │ │ │ ├── InputStream+Extensions.kt
│ │ │ │ ├── ViewPager+Extensions.kt
│ │ │ │ ├── Bitmap+Extensions.kt
│ │ │ │ ├── ByteArray+Extensions.kt
│ │ │ │ ├── Disposable+Extenions.kt
│ │ │ │ ├── Parcels+Extensions.kt
│ │ │ │ ├── EditText+Extensions.kt
│ │ │ │ ├── URL+Extensions.kt
│ │ │ │ ├── WebView+Extensions.kt
│ │ │ │ ├── Bundle+Extensions.kt
│ │ │ │ ├── ViewModel+Extensions.kt
│ │ │ │ ├── Koin+Extensions.kt
│ │ │ │ ├── RecyclerView+Extensions.kt
│ │ │ │ ├── Number+Extensions.kt
│ │ │ │ ├── Boolean+Extensions.kt
│ │ │ │ ├── LocalDateTime+Extensions.kt
│ │ │ │ ├── Int+Extensions.kt
│ │ │ │ ├── Dialog+Extenions.kt
│ │ │ │ ├── Iterable+Extensions.kt
│ │ │ │ ├── Long+Extensions.kt
│ │ │ │ ├── Formatting+Extensions.kt
│ │ │ │ ├── Fragment+Extensions.kt
│ │ │ │ ├── RxJava+Extensions.kt
│ │ │ │ ├── Retrofit+Extensions.kt
│ │ │ │ ├── Date+Extensions.kt
│ │ │ │ ├── BottomSheetBehaviour+Extensions.kt
│ │ │ │ ├── Uri+Extensions.kt
│ │ │ │ ├── Collection+Extensions.kt
│ │ │ │ ├── Button+Extensions.kt
│ │ │ │ ├── AudioManager+Extensions.kt
│ │ │ │ └── Realm+Extensions.kt
│ │ │ │ ├── base
│ │ │ │ ├── CompositeDisposableHolder.kt
│ │ │ │ ├── ViewHolder.kt
│ │ │ │ └── BaseActivity.kt
│ │ │ │ ├── input
│ │ │ │ └── KeyListener.java
│ │ │ │ ├── models
│ │ │ │ └── LoadingInfo.kt
│ │ │ │ ├── storage
│ │ │ │ ├── FirebaseDatabaseHelper.kt
│ │ │ │ └── SecurePreferencesLongString.kt
│ │ │ │ ├── ui
│ │ │ │ ├── custom
│ │ │ │ │ ├── NonSwipeableViewPager.kt
│ │ │ │ │ ├── CustomBottomSheetBehavior.kt
│ │ │ │ │ ├── ZoomageView.kt
│ │ │ │ │ ├── DisableableAppBarLayoutBehavior.kt
│ │ │ │ │ ├── ScrollListener.kt
│ │ │ │ │ ├── CustomCardView.kt
│ │ │ │ │ └── BottomNavigationBehavior.kt
│ │ │ │ ├── nullobjects
│ │ │ │ │ ├── SimpleBaseFragmentAdapter.kt
│ │ │ │ │ ├── SimpleOnCheckedChangeListener.kt
│ │ │ │ │ ├── SimpleAnimationListener.kt
│ │ │ │ │ ├── SimpleTabSelectionListener.kt
│ │ │ │ │ ├── SimpleOnItemSelectedListener.kt
│ │ │ │ │ ├── SimpleOnPageChangeListener.kt
│ │ │ │ │ ├── SimpleAnimatorListener.kt
│ │ │ │ │ ├── SimpleMotionListener.kt
│ │ │ │ │ └── SimpleTextWatcher.kt
│ │ │ │ └── itemDecorator
│ │ │ │ │ ├── SpacesItemDecorationEntrust.java
│ │ │ │ │ ├── GridSpacingItemDecoration.java
│ │ │ │ │ ├── SpacesItemDecoration.java
│ │ │ │ │ └── GridItemDecoration.kt
│ │ │ │ ├── misc
│ │ │ │ ├── UIDGenerator.kt
│ │ │ │ ├── WeakReferenceDelegate.kt
│ │ │ │ ├── SynchronizedValue.java
│ │ │ │ ├── AutoClearedValue.kt
│ │ │ │ └── FakeDataGenerator.kt
│ │ │ │ ├── realm
│ │ │ │ ├── RealmModelLiveData.kt
│ │ │ │ ├── RealmResultsLiveData.kt
│ │ │ │ ├── RealmDao.kt
│ │ │ │ └── RealmRepository.kt
│ │ │ │ ├── utils
│ │ │ │ ├── JUnitExtensions.java
│ │ │ │ ├── DrawableExtensions.java
│ │ │ │ ├── KeyGuardExtensions.java
│ │ │ │ ├── FontExtensions.java
│ │ │ │ ├── ThreadExtensions.java
│ │ │ │ ├── EncryptionExtensions.java
│ │ │ │ ├── ShellExtensions.kt
│ │ │ │ ├── BitmapExtensions.java
│ │ │ │ ├── ByteExtensions.java
│ │ │ │ ├── CalendarExtensions.java
│ │ │ │ └── DateExtensions.java
│ │ │ │ ├── uid
│ │ │ │ └── PrimaryKeyGenerator.kt
│ │ │ │ ├── markdown
│ │ │ │ ├── MarkdownFragment.kt
│ │ │ │ └── RawOutputFragment.kt
│ │ │ │ ├── io
│ │ │ │ └── Cache.java
│ │ │ │ ├── math
│ │ │ │ └── Line.java
│ │ │ │ └── locale
│ │ │ │ └── CountryCode.kt
│ │ └── AndroidManifest.xml
│ └── debug
│ │ └── AndroidManifest.xml
├── gradle.properties
└── proguard-rules.pro
├── settings.gradle
├── debug.jks
├── release.jks
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── .gitmodules
├── documentation
└── AmazonFireTvRemoteControlListener.jpg
├── jitpack.yml
├── bintray.properties
├── LICENSE.md
└── gradle.properties
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/core/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app', ':core'
2 |
--------------------------------------------------------------------------------
/app/src/main/assets/my_text.json:
--------------------------------------------------------------------------------
1 | {
2 | "lorem" : "ipsum"
3 | }
--------------------------------------------------------------------------------
/app/src/main/res/raw/my_raw.json:
--------------------------------------------------------------------------------
1 | {
2 | "lorem" : "ipsum"
3 | }
--------------------------------------------------------------------------------
/app/src/test/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/assets/.gitignore:
--------------------------------------------------------------------------------
1 | CHANGELOG.md
2 | README.md
3 | RELEASE.md
--------------------------------------------------------------------------------
/debug.jks:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kibotu/AndroidCore/HEAD/debug.jks
--------------------------------------------------------------------------------
/release.jks:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kibotu/AndroidCore/HEAD/release.jks
--------------------------------------------------------------------------------
/core/src/main/res/drawable/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/app/src/main/res/font/lato.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kibotu/AndroidCore/HEAD/app/src/main/res/font/lato.ttf
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | AndroidCore
3 |
4 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kibotu/AndroidCore/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kibotu/AndroidCore/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kibotu/AndroidCore/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "Android-Dependencies"]
2 | path = Android-Dependencies
3 | url = https://github.com/exozet/Android-Dependencies.git
4 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kibotu/AndroidCore/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kibotu/AndroidCore/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kibotu/AndroidCore/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-hdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kibotu/AndroidCore/HEAD/app/src/main/res/mipmap-hdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-mdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kibotu/AndroidCore/HEAD/app/src/main/res/mipmap-mdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kibotu/AndroidCore/HEAD/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kibotu/AndroidCore/HEAD/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/documentation/AmazonFireTvRemoteControlListener.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kibotu/AndroidCore/HEAD/documentation/AmazonFireTvRemoteControlListener.jpg
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/kibotu/AndroidCore/HEAD/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png
--------------------------------------------------------------------------------
/core/src/main/res/values-sw600dp/tablet-config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | false
4 |
--------------------------------------------------------------------------------
/core/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Share with:
5 |
6 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/services/connectivity/NoNetworkException.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.services.connectivity
2 |
3 | class NoNetworkException : Exception()
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/interfaces/BackPress.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.interfaces
2 |
3 | interface BackPress {
4 |
5 | fun consumeBackPress(): Boolean
6 | }
7 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/gson/Exclude.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.gson
2 |
3 |
4 | @Retention(AnnotationRetention.RUNTIME)
5 | @Target(AnnotationTarget.FIELD)
6 | annotation class Exclude
--------------------------------------------------------------------------------
/jitpack.yml:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | before_install:
4 | - curl https://gist.githubusercontent.com/kibotu/2485f0f88df159c6bc215ab9946e23e3/raw/28334f15bb6641667966b1732d8d8ef86dc1afb3/android-sdk-licenses.sh | sh
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/extensions/MutablieList+Extensions.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.extensions
2 |
3 |
4 | inline fun MutableList.addNotNull(item: T?) = item?.let { add(it) }
--------------------------------------------------------------------------------
/app/src/main/res/anim/layout_animation.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/extensions/Any+Extensions.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.extensions
2 |
3 |
4 | inline fun tryCatch(function: () -> T) = try {
5 | function()
6 | } catch (e: Exception) {
7 | null
8 | }
--------------------------------------------------------------------------------
/core/src/main/res/values-ldrtl/rtl-config.xml:
--------------------------------------------------------------------------------
1 |
2 | true
3 | -1
4 | 1
5 | 180
6 |
--------------------------------------------------------------------------------
/core/src/main/res/values/ltr-config.xml:
--------------------------------------------------------------------------------
1 |
2 | 0
3 | 1
4 | -1
5 | false
6 |
7 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionBase=GRADLE_USER_HOME
2 | distributionPath=wrapper/dists
3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.2.1-bin.zip
4 | zipStoreBase=GRADLE_USER_HOME
5 | zipStorePath=wrapper/dists
6 |
--------------------------------------------------------------------------------
/app/src/main/res/values/colors.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | #3F51B5
4 | #303F9F
5 | #FF4081
6 |
7 |
--------------------------------------------------------------------------------
/core/src/main/res/anim/side_in.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/core/src/main/res/anim/side_out.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
--------------------------------------------------------------------------------
/core/src/main/res/xml/backup.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
9 |
--------------------------------------------------------------------------------
/core/gradle.properties:
--------------------------------------------------------------------------------
1 | #Project Object Model https://maven.apache.org/pom.html#What_is_the_POM
2 | POM_NAME=AndroidCore
3 | POM_DESCRIPTION=Common used utilities
4 | # https://bintray.com/exozetag/maven/AndroidCore
5 | POM_BINTRAY_NAME=AndroidCore
6 | POM_ARTIFACT_ID=core
7 | POM_PACKAGING=jar
--------------------------------------------------------------------------------
/core/src/main/res/values-ldltr/ltr-config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | 0
4 | 1
5 | -1
6 | false
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/line_drawable.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/core/src/main/res/values/transition_names.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 | toolbar_transition
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/thumb_drawable.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/interfaces/Command.java:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.interfaces;
2 |
3 |
4 | import androidx.annotation.NonNull;
5 |
6 | /**
7 | * Created by Jan Rabe on 25/09/15.
8 | */
9 | public interface Command {
10 |
11 | void execute(@NonNull final T t);
12 | }
--------------------------------------------------------------------------------
/core/src/main/res/animator/fade_in.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/core/src/main/res/animator/fade_out.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/interfaces/LayoutProvider.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.interfaces
2 |
3 |
4 | import androidx.annotation.LayoutRes
5 |
6 | /**
7 | * Created by jan.rabe on 02/02/16.
8 | */
9 | interface LayoutProvider {
10 |
11 | @get:LayoutRes
12 | val layout: Int
13 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/services/network/fcm/NotificationContent.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.services.network.fcm
2 |
3 | import org.parceler.Parcel
4 |
5 | @Parcel(Parcel.Serialization.BEAN)
6 | data class NotificationContent(
7 | val title: String? = null,
8 | val text: String? = null
9 | )
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/base/CompositeDisposableHolder.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.base
2 |
3 | import io.reactivex.disposables.CompositeDisposable
4 |
5 | interface CompositeDisposableHolder {
6 |
7 | var subscription: CompositeDisposable
8 |
9 | fun disposeCompositeDisposable()
10 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/interfaces/annotations/Visibility.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.interfaces.annotations
2 |
3 |
4 | import android.view.View.*
5 | import androidx.annotation.IntDef
6 |
7 | @IntDef(VISIBLE, INVISIBLE, GONE)
8 | @Retention(AnnotationRetention.SOURCE)
9 | annotation class Visibility
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/input/KeyListener.java:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.input;
2 |
3 | import android.view.KeyEvent;
4 |
5 | public interface KeyListener {
6 |
7 | boolean onKeyUp(final int keyCode, final KeyEvent event);
8 |
9 | boolean onKeyDown(final int keyCode, final KeyEvent event);
10 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/interfaces/ChainableCommand.java:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.interfaces;
2 |
3 |
4 | import androidx.annotation.NonNull;
5 |
6 | /**
7 | * Created by Jan Rabe on 25/09/15.
8 | */
9 | public interface ChainableCommand {
10 |
11 | T execute(@NonNull final T t);
12 | }
13 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/models/LoadingInfo.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.models
2 |
3 | import org.parceler.Parcel
4 |
5 | @Parcel(Parcel.Serialization.BEAN)
6 | data class LoadingInfo(
7 | val name: String = "",
8 | val isLoading: Boolean = true,
9 | val time: Long = System.currentTimeMillis()
10 | )
--------------------------------------------------------------------------------
/core/src/main/res/anim/fade_in.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/core/src/main/res/anim/fade_out.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
--------------------------------------------------------------------------------
/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/app/src/test/java/com/exozet/android/core/base/SerializableTest.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.base
2 |
3 | import org.junit.Test
4 |
5 | /**
6 | * Created by [Jan Rabe](https://about.me/janrabe).
7 | */
8 |
9 | interface SerializableTest {
10 |
11 | @Test
12 | fun serialize()
13 |
14 | @Test
15 | fun deserialize()
16 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/extensions/List+Extensions.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.extensions
2 |
3 |
4 | inline fun List.circularIndex(index: Int): Int =
5 | if (index < 0) (index % size + size) % size
6 | else index % size
7 |
8 | inline fun List.circularAt(index: Int): T = this[circularIndex(index)]
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/services/network/interceptors/EmptyInterceptor.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.services.network.interceptors
2 |
3 | import okhttp3.Interceptor
4 | import okhttp3.Response
5 |
6 | class EmptyInterceptor : Interceptor {
7 | override fun intercept(chain: Interceptor.Chain): Response = chain.proceed(chain.request())
8 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/interfaces/StorageProvider.java:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.interfaces;
2 |
3 | import java.util.List;
4 |
5 | public interface StorageProvider {
6 |
7 | boolean create(T t);
8 |
9 | List read();
10 |
11 | boolean delete(T t);
12 |
13 | void clear();
14 |
15 | boolean contains(T t);
16 | }
--------------------------------------------------------------------------------
/core/src/main/res/anim/shake.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/interfaces/DispatchTouchEventHandler.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.interfaces
2 |
3 | import android.view.MotionEvent
4 | import android.view.View
5 |
6 | interface DispatchTouchEventHandler {
7 |
8 | val viewsHideKeyboardOnFocusLoss: Array?
9 |
10 | fun dispatchTouchEvent(event: MotionEvent): Boolean
11 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/exozet/android/core/demo/App.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.demo
2 |
3 | import com.exozet.android.core.base.BaseApplication
4 |
5 | /**
6 | * Created by [Jan Rabe](https://about.me/janrabe).
7 | */
8 |
9 | class App : BaseApplication() {
10 |
11 | override fun onCreate() {
12 | super.onCreate()
13 | initRealm()
14 | }
15 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/storage/FirebaseDatabaseHelper.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.storage
2 |
3 | import com.google.firebase.database.FirebaseDatabase
4 |
5 | /**
6 | * Created by Jan Rabe .
7 | */
8 |
9 | object FirebaseDatabaseHelper {
10 |
11 | val database by lazy { FirebaseDatabase.getInstance() }
12 | }
--------------------------------------------------------------------------------
/core/src/main/res/anim/slide_enter.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
--------------------------------------------------------------------------------
/core/src/main/res/anim/slide_exit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
--------------------------------------------------------------------------------
/core/src/main/res/anim/slide_in.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
--------------------------------------------------------------------------------
/core/src/main/res/anim/slide_out.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
--------------------------------------------------------------------------------
/core/src/main/res/anim/slide_pop_enter.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
--------------------------------------------------------------------------------
/core/src/main/res/anim/slide_pop_exit.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
--------------------------------------------------------------------------------
/core/src/main/res/anim/slide_exit_left.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
--------------------------------------------------------------------------------
/core/src/main/res/anim/slide_enter_right.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
11 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/interfaces/annotations/Transit.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.interfaces.annotations
2 |
3 |
4 | import androidx.annotation.IntDef
5 |
6 |
7 | import androidx.fragment.app.FragmentTransaction.*
8 |
9 | @IntDef(TRANSIT_NONE, TRANSIT_FRAGMENT_OPEN, TRANSIT_FRAGMENT_CLOSE, TRANSIT_FRAGMENT_FADE)
10 | @Retention(AnnotationRetention.SOURCE)
11 | annotation class Transit
--------------------------------------------------------------------------------
/app/src/main/res/drawable/line.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
13 |
--------------------------------------------------------------------------------
/bintray.properties:
--------------------------------------------------------------------------------
1 | bintray.user=exozet
2 | bintray.apikey=
3 | bintray.organization=exozetag
4 | bintray.gpg.password=
5 | binrtray.group=com.exozet
6 | binrtray.repo=maven
7 | binrtray.name=AndroidCore
8 | bintray.licenses=MIT
9 | bintray.vcsUrl=https://github.com/exozet/AndroidCore.git
10 | bintray.websiteUrl=https://github.com/exozet/AndroidCore
11 | bintray.version.desc="Collection of re-usable android functions."
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/interfaces/DispatchTouchEvent.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.interfaces
2 |
3 | import android.view.MotionEvent
4 |
5 | /**
6 | * Created by Jan Rabe on 28/09/15.
7 | */
8 | @Deprecated("use DispatchTouchEventHandler", ReplaceWith("DispatchTouchEventHandler"))
9 | interface DispatchTouchEvent {
10 |
11 | fun dispatchTouchEvent(event: MotionEvent): Boolean
12 | }
13 |
14 |
--------------------------------------------------------------------------------
/app/src/main/res/xml/network_security_config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/core/src/main/res/values-v21/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/core/src/main/res/values-v21/themes.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 |
8 |
11 |
12 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/extensions/Snackbar+Extensions.kt:
--------------------------------------------------------------------------------
1 | @file:JvmName("SnackbarExtensions")
2 |
3 | package com.exozet.android.core.extensions
4 |
5 | import android.widget.TextView
6 | import com.google.android.material.snackbar.Snackbar
7 |
8 | fun Snackbar.withTextColor(color: Int): Snackbar {
9 | view.findViewById(com.google.android.material.R.id.snackbar_text).setTextColor(color)
10 | return this
11 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/extensions/InputStream+Extensions.kt:
--------------------------------------------------------------------------------
1 | @file:JvmName("InputStreamExtensions")
2 |
3 | package com.exozet.android.core.extensions
4 |
5 | import java.io.InputStream
6 | import java.nio.charset.Charset
7 |
8 | /**
9 | * Created by [Jan Rabe](https://about.me/janrabe).
10 | */
11 |
12 | fun InputStream.readTextAndClose(charset: Charset = Charsets.UTF_8): String = bufferedReader(charset).use { it.readText() }
13 |
--------------------------------------------------------------------------------
/core/src/main/res/animator/alpha_in.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
12 |
--------------------------------------------------------------------------------
/core/src/main/res/animator/alpha_out.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
12 |
--------------------------------------------------------------------------------
/app/src/test/java/com/exozet/android/core/models/ModelTest.kt:
--------------------------------------------------------------------------------
1 | package de.charite.balsam.models
2 |
3 | import com.exozet.android.core.base.SerializableTests
4 |
5 | /**
6 | * Created by [Jan Rabe](https://about.me/janrabe).
7 | */
8 |
9 | class ModelTest : SerializableTests(TestModel::class.java) {
10 |
11 | override val expectedJson = """
12 | {
13 | "message": "test"
14 | }
15 | """
16 | }
17 |
18 | data class TestModel(var message: String? = null)
--------------------------------------------------------------------------------
/core/src/main/res/anim/slide_in_top.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
11 |
--------------------------------------------------------------------------------
/core/src/main/res/anim/slide_out_bottom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
11 |
--------------------------------------------------------------------------------
/core/src/main/res/transition/move.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/ic_arrow_back_red_24dp.xml:
--------------------------------------------------------------------------------
1 |
9 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/recycler_view_item.xml:
--------------------------------------------------------------------------------
1 |
2 |
12 |
--------------------------------------------------------------------------------
/core/src/main/res/anim/enter_from_left.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
11 |
--------------------------------------------------------------------------------
/core/src/main/res/anim/enter_from_right.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
11 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/extensions/ViewPager+Extensions.kt:
--------------------------------------------------------------------------------
1 | @file:JvmName("ViewPagerExtensions")
2 |
3 |
4 | package com.exozet.android.core.extensions
5 |
6 | import androidx.viewpager.widget.ViewPager
7 |
8 | fun ViewPager.scrollRight(pages: Int = 1) {
9 | setCurrentItem(currentItem.plus(pages).clamp(0, (adapter?.count ?: 0) - 1), true)
10 | }
11 |
12 | fun ViewPager.scrollLeft(pages: Int = 1) {
13 | setCurrentItem(currentItem.minus(pages).clamp(0, (adapter?.count ?: 0) - 1), true)
14 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable/thumb.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
10 |
11 |
14 |
15 |
16 |
17 |
--------------------------------------------------------------------------------
/core/src/main/res/values/fonts.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | sans-serif-light
6 | sans-serif-medium
7 | sans-serif
8 | sans-serif-condensed
9 | sans-serif-black
10 | sans-serif-thin
11 |
12 |
13 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/extensions/Bitmap+Extensions.kt:
--------------------------------------------------------------------------------
1 | @file:JvmName("BitmapExtensions")
2 |
3 | package com.exozet.android.core.extensions
4 |
5 | import android.graphics.Bitmap
6 | import net.kibotu.logger.Logger
7 |
8 | /**
9 | * Created by [Jan Rabe](https://about.me/janrabe).
10 | */
11 |
12 | fun Bitmap?.freeBitmap() {
13 | if (this == null || isRecycled) {
14 | return
15 | }
16 |
17 | try {
18 | recycle()
19 | } catch (e: Exception) {
20 | Logger.e(e)
21 | }
22 | }
--------------------------------------------------------------------------------
/app/updateLocalization.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | authKey=""
3 | valuesDir="src/main/res/values"
4 | languages="de"
5 | defaultLanguage="de"
6 |
7 | # Main string files
8 | for language in $languages
9 | do
10 | if [ "$language" == $defaultLanguage ]; then
11 | saveDir="$valuesDir"
12 | else
13 | saveDir="$valuesDir-$language"
14 | fi
15 |
16 | mkdir -p $saveDir
17 |
18 | curl "https://localise.biz/api/export/locale/$language.xml?key=$authKey&format=android&fallback=$defaultLanguage" > "$saveDir/strings.xml"
19 | done
--------------------------------------------------------------------------------
/core/src/debug/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/gson/GsonExclusionStrategy.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.gson
2 |
3 | import com.google.gson.ExclusionStrategy
4 | import com.google.gson.FieldAttributes
5 |
6 | /**
7 | * https://www.baeldung.com/gson-exclude-fields-serialization
8 | */
9 | class GsonExclusionStrategy : ExclusionStrategy {
10 |
11 | override fun shouldSkipClass(clazz: Class<*>): Boolean = false
12 |
13 | override fun shouldSkipField(field: FieldAttributes): Boolean = field.getAnnotation(Exclude::class.java) != null
14 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/ui/custom/NonSwipeableViewPager.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.ui.custom
2 |
3 | import android.content.Context
4 | import android.util.AttributeSet
5 | import android.view.MotionEvent
6 | import androidx.viewpager.widget.ViewPager
7 |
8 | class NonSwipeableViewPager constructor(context: Context, attrs: AttributeSet?) : ViewPager(context, attrs) {
9 |
10 | override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean = false
11 |
12 | override fun onTouchEvent(ev: MotionEvent?): Boolean = false
13 | }
--------------------------------------------------------------------------------
/core/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
11 |
12 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/extensions/ByteArray+Extensions.kt:
--------------------------------------------------------------------------------
1 | @file:JvmName("ByteArrayExtensions")
2 |
3 | package com.exozet.android.core.extensions
4 |
5 | import net.kibotu.logger.Logger
6 | import java.io.File
7 | import java.io.FileOutputStream
8 |
9 | /**
10 | * Created by [Jan Rabe](https://about.me/janrabe).
11 | */
12 |
13 | fun ByteArray.saveAt(pathName: String): Boolean = try {
14 | FileOutputStream(File(pathName)).use { it.write(this) }
15 | true
16 | } catch (e: Exception) {
17 | Logger.e(e)
18 | false
19 | }
20 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable/layer_launch_screen.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
14 |
15 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/extensions/Disposable+Extenions.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.extensions
2 |
3 | import android.view.View
4 | import io.reactivex.disposables.Disposable
5 |
6 |
7 | fun Disposable.clearOnDetach(view: View) = view.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
8 |
9 | override fun onViewAttachedToWindow(v: View?) {
10 | }
11 |
12 | override fun onViewDetachedFromWindow(v: View?) {
13 | if (!isDisposed)
14 | dispose()
15 |
16 | view.removeOnAttachStateChangeListener(this)
17 | }
18 | })
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/extensions/Parcels+Extensions.kt:
--------------------------------------------------------------------------------
1 | @file:JvmName("ParcelsExtensions")
2 |
3 | package com.exozet.android.core.extensions
4 |
5 | import android.os.Bundle
6 | import android.os.Parcelable
7 | import org.parceler.Parcels
8 |
9 | /**
10 | * Created by [Jan Rabe](https://about.me/janrabe).
11 | */
12 |
13 | inline fun T.wrap(): Parcelable = Parcels.wrap(this)
14 |
15 | inline fun Parcelable?.unwrap(): T? = Parcels.unwrap(this)
16 |
17 | inline fun Bundle.unwrap(key: String?): T? = getParcelable(key).unwrap()
18 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/services/network/interceptors/ContentTypeInterceptor.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.services.network.interceptors
2 |
3 | import okhttp3.Interceptor
4 | import okhttp3.Response
5 |
6 | class ContentTypeInterceptor(private val contentType: String = "application/json") : Interceptor {
7 |
8 | override fun intercept(chain: Interceptor.Chain): Response = chain
9 | .proceed(with(
10 | chain.request()
11 | .newBuilder()
12 | ) {
13 | header("Content-Type", contentType)
14 | .build()
15 | })
16 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/base/ViewHolder.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.base
2 |
3 | import android.view.ViewGroup
4 | import androidx.annotation.CallSuper
5 | import androidx.annotation.LayoutRes
6 | import io.reactivex.disposables.Disposable
7 |
8 | abstract class ViewHolder(var itemView: ViewGroup?) : Disposable {
9 |
10 | @get:LayoutRes
11 | protected abstract val layout: Int
12 |
13 | protected var disposed = false
14 |
15 | override fun isDisposed() = disposed
16 |
17 | @CallSuper
18 | override fun dispose() {
19 | itemView = null
20 | disposed = true
21 | }
22 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/ui/nullobjects/SimpleBaseFragmentAdapter.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.ui.nullobjects
2 |
3 | import androidx.fragment.app.Fragment
4 | import androidx.fragment.app.FragmentManager
5 | import androidx.fragment.app.FragmentStatePagerAdapter
6 |
7 | class SimpleBaseFragmentAdapter(fm: FragmentManager, val size: Int, fragmentProvider: () -> Fragment) : FragmentStatePagerAdapter(fm) {
8 |
9 | val fragments: List = (0 until size).map { fragmentProvider() }
10 |
11 | override fun getItem(position: Int): Fragment = fragments[position]
12 |
13 | override fun getCount(): Int = size
14 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/services/connectivity/LiveNetworkMonitor.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.services.connectivity
2 |
3 | import android.content.Context
4 | import android.net.ConnectivityManager
5 | import net.kibotu.ContextHelper
6 |
7 | object LiveNetworkMonitor {
8 |
9 | val isConnected: Boolean
10 | get() {
11 | val cm = ContextHelper.getApplication()?.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
12 | @Suppress("DEPRECATION")
13 | return cm.activeNetworkInfo?.isAvailable == true && cm.activeNetworkInfo?.isConnectedOrConnecting == true
14 | }
15 | }
--------------------------------------------------------------------------------
/core/src/main/res/anim/exit_to_bottom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
9 |
14 |
--------------------------------------------------------------------------------
/core/src/main/res/anim/enter_from_bottom.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
9 |
14 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/ui/nullobjects/SimpleOnCheckedChangeListener.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.ui.nullobjects
2 |
3 | import android.widget.CompoundButton
4 |
5 | open class SimpleOnCheckedChangeListener : CompoundButton.OnCheckedChangeListener {
6 | override fun onCheckedChanged(buttonView: CompoundButton?, isChecked: Boolean) {
7 | }
8 | }
9 |
10 | inline fun CompoundButton.onCheckedChanged(crossinline block: (Boolean) -> Unit) {
11 | setOnCheckedChangeListener(object : SimpleOnCheckedChangeListener() {
12 | override fun onCheckedChanged(buttonView: CompoundButton?, isChecked: Boolean) = block(isChecked)
13 | })
14 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/services/network/interceptors/ErrorInterceptor.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.services.network.interceptors
2 |
3 | import okhttp3.Interceptor
4 | import okhttp3.Response
5 |
6 | class ErrorInterceptor(val onError: (Response) -> Unit) : Interceptor {
7 |
8 | override fun intercept(chain: Interceptor.Chain): Response {
9 |
10 | val response = chain.proceed(chain.request())
11 |
12 | return when {
13 | !response.isSuccessful -> {
14 | onError(response)
15 | Response.Builder().build()
16 | }
17 | else -> response
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/extensions/EditText+Extensions.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.extensions
2 |
3 | import android.text.TextWatcher
4 | import android.widget.EditText
5 | import com.exozet.android.core.ui.nullobjects.SimpleTextWatcher
6 |
7 |
8 | fun EditText.onTextChanged(block: (s: CharSequence?, start: Int, before: Int, count: Int) -> Unit): TextWatcher {
9 |
10 | val listener = object : SimpleTextWatcher() {
11 |
12 | override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
13 | block(s, start, before, count)
14 | }
15 | }
16 | addTextChangedListener(listener)
17 | return listener
18 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/extensions/URL+Extensions.kt:
--------------------------------------------------------------------------------
1 | @file:JvmName("URLExtensions")
2 |
3 | package com.exozet.android.core.extensions
4 |
5 | import java.net.HttpURLConnection
6 | import java.net.URL
7 | import java.net.URLConnection
8 |
9 | /**
10 | * Created by [Jan Rabe](https://about.me/janrabe).
11 | */
12 |
13 | inline fun URL.use(block: (URLConnection) -> R): R {
14 | var connection: URLConnection? = null
15 | try {
16 | connection = openConnection()
17 | return block(connection)
18 | } catch (e: Exception) {
19 | throw e
20 | } finally {
21 | (connection as? HttpURLConnection)?.disconnect()
22 | }
23 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/extensions/WebView+Extensions.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.extensions
2 |
3 |
4 | import android.os.Build
5 | import android.webkit.WebView
6 | import androidx.annotation.RequiresApi
7 |
8 |
9 | @RequiresApi(Build.VERSION_CODES.KITKAT)
10 | fun WebView.evaluateJavascriptCommandFetch(command: String, onSuccess: (String?) -> Unit) = evaluateJavascript("(function() { return $command; })();") { onSuccess(it) }
11 |
12 | @RequiresApi(Build.VERSION_CODES.KITKAT)
13 | fun WebView.evaluateJavascriptCommand(command: String, onSuccess: ((String?) -> Unit)? = null) = evaluateJavascript(""" (function() { $command })(); """.trimMargin()) { onSuccess?.invoke(it) }
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_fab_sample.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/core/src/main/res/anim/pop_out.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 |
17 |
--------------------------------------------------------------------------------
/core/src/main/res/anim/shrink.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
13 |
17 |
--------------------------------------------------------------------------------
/app/src/androidTest/java/com/exozet/android/core/demo/ExampleInstrumentedTest.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.demo
2 |
3 | import org.junit.Assert.assertEquals
4 | import org.junit.Test
5 | import org.junit.runner.RunWith
6 |
7 | /**
8 | * Instrumented test, which will execute on an Android device.
9 | *
10 | * See [testing documentation](http://d.android.com/tools/testing).
11 | */
12 | @RunWith(AndroidJUnit4::class)
13 | class ExampleInstrumentedTest {
14 | @Test
15 | fun useAppContext() {
16 | // Context of the app under test.
17 | val appContext = InstrumentationRegistry.getTargetContext()
18 | assertEquals("com.exozet.android.core", appContext.packageName)
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/services/network/GoogleApi.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.services.network
2 |
3 | import com.exozet.android.core.services.network.fcm.FcmMessage
4 | import io.reactivex.Observable
5 | import okhttp3.ResponseBody
6 | import retrofit2.http.Body
7 | import retrofit2.http.Header
8 | import retrofit2.http.Headers
9 | import retrofit2.http.POST
10 |
11 | interface GoogleApi {
12 |
13 | /**
14 | * https://developers.google.com/cloud-messaging/http
15 | */
16 | @Headers("Content-Type: application/json")
17 | @POST("/gcm/send")
18 | fun sendPushNotification(@Body message: FcmMessage, @Header("Authorization") apiKey: String): Observable
19 | }
--------------------------------------------------------------------------------
/core/src/main/res/values/config.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | true
7 | false
8 | false
9 | false
10 | true
11 |
12 | exozet
13 | exozet
14 |
15 |
16 |
17 | 250
18 | 750
19 |
20 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/extensions/Bundle+Extensions.kt:
--------------------------------------------------------------------------------
1 | @file:JvmName("BundleExtensions")
2 |
3 |
4 | package com.exozet.android.core.extensions
5 |
6 | import android.os.Bundle
7 | import net.kibotu.logger.Logger.logv
8 | import net.kibotu.logger.TAG
9 |
10 |
11 | fun Bundle.printBundle() {
12 | for (key in keySet()) {
13 | val value = get(key)
14 | logv { "${value?.TAG} $key: $value" }
15 | }
16 | }
17 |
18 | val Bundle.string
19 | get() = with(StringBuilder()) {
20 | append("{")
21 | for (key in keySet()) {
22 | val value = get(key)
23 | append(key).append(":").append(value)
24 | }
25 | append("}")
26 | }.toString()
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/extensions/ViewModel+Extensions.kt:
--------------------------------------------------------------------------------
1 | @file:JvmName("ViewModelExtensions")
2 |
3 | package com.exozet.android.core.extensions
4 |
5 | import androidx.annotation.MainThread
6 | import androidx.lifecycle.ViewModel
7 | import androidx.lifecycle.ViewModelProvider
8 | import androidx.lifecycle.ViewModelStoreOwner
9 | import com.github.florent37.application.provider.ActivityProvider
10 |
11 |
12 | /**
13 | * Try using `by viewModels` if possible.
14 | */
15 | @MainThread
16 | inline fun viewModel(viewModelStoreOwner: ViewModelStoreOwner = ActivityProvider.currentActivity as ViewModelStoreOwner) = lazy { ViewModelProvider(viewModelStoreOwner).get(VM::class.java) }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/extensions/Koin+Extensions.kt:
--------------------------------------------------------------------------------
1 | @file:JvmName("KoinExtensions")
2 |
3 | package com.exozet.android.core.extensions
4 |
5 | import org.koin.core.context.GlobalContext
6 | import org.koin.core.parameter.ParametersDefinition
7 | import org.koin.core.qualifier.Qualifier
8 |
9 |
10 | /**
11 | * inject lazily given dependency for Android koincomponent
12 | * @param qualifier - bean qualifier / optional
13 | * @param scope
14 | * @param parameters - injection parameters
15 | */
16 | inline fun Any.inject(
17 | qualifier: Qualifier? = null,
18 | noinline parameters: ParametersDefinition? = null
19 | ) = lazy { GlobalContext.get().koin.get(qualifier, parameters) }
--------------------------------------------------------------------------------
/core/src/main/res/anim/exit_to_left.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
11 |
16 |
--------------------------------------------------------------------------------
/core/src/main/res/anim/exit_to_right.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
11 |
16 |
--------------------------------------------------------------------------------
/core/src/main/res/values/integers.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 | 120
6 |
7 | - 300
8 | - 2200
9 |
10 | - 400
11 |
12 | - 100
13 |
14 | 1500
15 | 500
16 | 300
17 |
18 |
--------------------------------------------------------------------------------
/app/src/main/res/layout/layout_toolbar.xml:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/extensions/RecyclerView+Extensions.kt:
--------------------------------------------------------------------------------
1 | @file:JvmName("RecyclerViewExtensions")
2 |
3 | package com.exozet.android.core.extensions
4 |
5 | import androidx.recyclerview.widget.RecyclerView
6 | import androidx.recyclerview.widget.SimpleItemAnimator
7 |
8 | /**
9 | * Created by [Jan Rabe](https://about.me/janrabe).
10 | */
11 |
12 | val RecyclerView.Adapter.isEmpty: Boolean where VH : RecyclerView.ViewHolder
13 | get() = itemCount == 0
14 |
15 |
16 | var RecyclerView.supportsChangeAnimations: Boolean
17 | get() = (itemAnimator as SimpleItemAnimator).supportsChangeAnimations
18 | set(value) {
19 | (itemAnimator as SimpleItemAnimator).supportsChangeAnimations = value
20 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/extensions/Number+Extensions.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.extensions
2 |
3 | import kotlin.contracts.ExperimentalContracts
4 | import kotlin.contracts.contract
5 |
6 |
7 | /**
8 | * Returns `true` if this not null or zero.
9 | */
10 | @UseExperimental(ExperimentalContracts::class)
11 | inline fun T?.onNotNullOrZero(block: T.() -> Unit): Boolean {
12 | contract {
13 | returns(true) implies (this@onNotNullOrZero != null)
14 | }
15 |
16 | return if (this != null && this.isNotZero()) {
17 | block(this)
18 | true
19 | } else {
20 | false
21 | }
22 | }
23 |
24 | inline fun T.format(digits: Int) = "%.${digits}f".format(this)
--------------------------------------------------------------------------------
/core/src/main/res/drawable/ic_share.xml:
--------------------------------------------------------------------------------
1 |
7 |
10 |
11 |
--------------------------------------------------------------------------------
/core/src/main/res/anim/pop_in.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
14 |
19 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/extensions/Boolean+Extensions.kt:
--------------------------------------------------------------------------------
1 | @file:JvmName("BooleanExtensions")
2 |
3 | package com.exozet.android.core.extensions
4 |
5 | /**
6 | * Created by [Jan Rabe](https://about.me/janrabe).
7 | */
8 |
9 | inline fun Boolean.whether(yes: () -> T, no: () -> T): T = if (this) yes() else no()
10 |
11 | inline fun Boolean.either(t: T): Pair = Pair(this, t)
12 |
13 | inline infix fun Pair.or(t: T): T = if (first) second else t
14 |
15 | fun Boolean.onTrue(function: () -> Unit) {
16 | if (this) {
17 | function()
18 | } else {
19 |
20 | }
21 | }
22 |
23 | fun Boolean.onFalse(function: () -> Unit) {
24 | if (this) {
25 | } else {
26 | function()
27 | }
28 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/misc/UIDGenerator.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.misc
2 |
3 | import java.util.concurrent.atomic.AtomicInteger
4 |
5 | /**
6 | * Created by [Jan Rabe](https://about.me/janrabe).
7 | */
8 | object UIDGenerator {
9 |
10 | private const val START_UID = 0
11 |
12 | /**
13 | * https://winterbe.com/posts/2015/05/22/java8-concurrency-tutorial-atomic-concurrent-map-examples/
14 | */
15 | private val nextUID = AtomicInteger(START_UID)
16 |
17 | fun newUID(): Int {
18 | if (!isValid(nextUID.get())) {
19 | throw IllegalStateException("UID pool depleted")
20 | }
21 | return nextUID.incrementAndGet()
22 | }
23 |
24 | private fun isValid(uid: Int): Boolean = uid >= START_UID
25 | }
26 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/services/network/interceptors/AuthorizationInterceptor.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.services.network.interceptors
2 |
3 | import okhttp3.Interceptor
4 | import okhttp3.Response
5 |
6 | class AuthorizationInterceptor(private val tokenProvider: () -> String?) : Interceptor {
7 |
8 | override fun intercept(chain: Interceptor.Chain): Response {
9 | var request = chain.request()
10 |
11 | // add jwt token
12 | if (request.header("Authorization") == "Bearer") {
13 | request = request.newBuilder().removeHeader("Authorization")
14 | .addHeader("Authorization", "Bearer ${tokenProvider()}")
15 | .build()
16 | }
17 |
18 | return chain.proceed(request)
19 | }
20 | }
--------------------------------------------------------------------------------
/app/src/main/res/anim/grow.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
14 |
19 |
20 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/realm/RealmModelLiveData.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.realm
2 |
3 | import androidx.lifecycle.LiveData
4 | import io.realm.RealmChangeListener
5 | import io.realm.RealmModel
6 | import io.realm.RealmObject
7 | import io.realm.kotlin.addChangeListener
8 | import io.realm.kotlin.removeChangeListener
9 |
10 | open class RealmModelLiveData(val model: T, var valueOnActive: Boolean = false) : LiveData() {
11 |
12 | protected val listener = RealmChangeListener { postValue(it.freeze()) }
13 |
14 | override fun onActive() {
15 | model.addChangeListener(listener)
16 | if (valueOnActive)
17 | value = model
18 | }
19 |
20 | override fun onInactive() = model.removeChangeListener(listener)
21 | }
--------------------------------------------------------------------------------
/core/src/main/res/anim/grow.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
14 |
19 |
20 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/realm/RealmResultsLiveData.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.realm
2 |
3 | import androidx.lifecycle.LiveData
4 | import io.realm.RealmChangeListener
5 | import io.realm.RealmModel
6 | import io.realm.RealmObject
7 | import io.realm.RealmResults
8 |
9 |
10 | open class RealmResultsLiveData(protected val results: RealmResults, var valueOnActive: Boolean = false) : LiveData>() {
11 |
12 | protected val listener = RealmChangeListener> { postValue(it.freeze()) }
13 |
14 | override fun onActive() {
15 | results.addChangeListener(listener)
16 | if (valueOnActive)
17 | value = results.freeze()
18 | }
19 |
20 | override fun onInactive() = results.removeChangeListener(listener)
21 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/misc/WeakReferenceDelegate.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.misc
2 |
3 | import java.lang.ref.WeakReference
4 | import kotlin.reflect.KProperty
5 |
6 | inline fun weak() = WeakReferenceDelegate()
7 |
8 | inline fun weak(value: T) = WeakReferenceDelegate(value)
9 |
10 | class WeakReferenceDelegate {
11 |
12 | private var weakReference: WeakReference? = null
13 |
14 | constructor()
15 |
16 | constructor(value: T) {
17 | weakReference = WeakReference(value)
18 | }
19 |
20 | operator fun getValue(thisRef: Any, property: KProperty<*>): T? = weakReference?.get()
21 |
22 | operator fun setValue(thisRef: Any, property: KProperty<*>, value: T) {
23 | weakReference = WeakReference(value)
24 | }
25 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/utils/JUnitExtensions.java:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.utils;
2 |
3 | import java.util.Arrays;
4 | import java.util.List;
5 |
6 | /**
7 | * Created by jan.rabe on 10/08/16.
8 | */
9 |
10 | public class JUnitExtensions {
11 |
12 | private JUnitExtensions() throws IllegalAccessException {
13 | throw new IllegalAccessException();
14 | }
15 |
16 | public static boolean isJUnitTest() {
17 | StackTraceElement[] stackTrace = Thread.currentThread().getStackTrace();
18 | StackTraceElement[] list = stackTrace;
19 | for (StackTraceElement element : list) {
20 | if (element.getClassName().startsWith("org.junit.")) {
21 | return true;
22 | }
23 | }
24 | return false;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/extensions/LocalDateTime+Extensions.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.extensions
2 |
3 | import org.joda.time.DateTimeZone
4 | import org.joda.time.Duration
5 | import org.joda.time.LocalDateTime
6 | import org.joda.time.format.DateTimeFormat
7 | import org.joda.time.format.DateTimeFormatter
8 |
9 |
10 | fun LocalDateTime.durationUntil(end: LocalDateTime) = Duration(toDateTime(DateTimeZone.UTC), end.toDateTime(DateTimeZone.UTC))
11 |
12 | val String.iso8601Date: LocalDateTime
13 | get() = LocalDateTime.parse(this, iso8601Formatter)
14 |
15 | val LocalDateTime.iso8601Date: String
16 | get() = toString(iso8601Formatter)
17 |
18 | /**
19 | * 2013-01-09T19:32:49.103+05:30
20 | */
21 | val iso8601Formatter: DateTimeFormatter by lazy { DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ssZ") }
22 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/interfaces/Repository.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.interfaces
2 |
3 | import androidx.lifecycle.LiveData
4 |
5 | /**
6 | * Answers where to get data.
7 | */
8 | interface Repository {
9 |
10 | /**
11 | * Gets all [T].
12 | */
13 | fun all(): LiveData>
14 |
15 | /**
16 | * Gets [T] by id.
17 | */
18 | fun getById(id: String): LiveData
19 |
20 | /**
21 | * Deletes [T].
22 | */
23 | fun delete(item: T)
24 |
25 | /**
26 | * Updates and inserts [T].
27 | */
28 | fun insertOrUpdate(item: T)
29 |
30 | /**
31 | * Updates and inserts list of [T].
32 | */
33 | fun insertOrUpdate(items: List)
34 |
35 | /**
36 | * Loads and updates.
37 | */
38 | suspend fun loadAndUpdate()
39 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/uid/PrimaryKeyGenerator.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.uid
2 |
3 | import java.util.concurrent.atomic.AtomicInteger
4 |
5 | /**
6 | * Created by [Jan Rabe](https://about.me/janrabe).
7 | */
8 | class PrimaryKeyGenerator {
9 |
10 | private val START_UID = 0
11 |
12 | /**
13 | * https://winterbe.com/posts/2015/05/22/java8-concurrency-tutorial-atomic-concurrent-map-examples/
14 | */
15 | private val nextUID = AtomicInteger(START_UID)
16 |
17 | fun newUID(): Int {
18 | if (!isValid(nextUID.get())) {
19 | throw IllegalStateException("UID pool depleted")
20 | }
21 | return nextUID.incrementAndGet()
22 | }
23 |
24 | fun nextUID() = newUID().toString()
25 |
26 | private fun isValid(uid: Int): Boolean = uid >= START_UID
27 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/services/network/NullableConverterFactory.java:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.services.network;
2 |
3 | import java.lang.annotation.Annotation;
4 | import java.lang.reflect.Type;
5 |
6 | import io.reactivex.annotations.Nullable;
7 | import okhttp3.ResponseBody;
8 | import retrofit2.Converter;
9 | import retrofit2.Retrofit;
10 |
11 | public class NullableConverterFactory extends Converter.Factory {
12 | @Nullable
13 | @Override
14 | public Converter responseBodyConverter(Type type, Annotation[] annotations, Retrofit retrofit) {
15 | final Converter delegate = retrofit.nextResponseBodyConverter(this, type, annotations);
16 | return (Converter) body -> body.contentLength() != 0 ? delegate.convert(body) : null;
17 | }
18 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/services/network/interceptors/LoadingInterceptor.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.services.network.interceptors
2 |
3 | import com.exozet.android.core.models.LoadingInfo
4 | import okhttp3.Interceptor
5 | import okhttp3.Response
6 |
7 | class LoadingInterceptor(private val onLoading: (LoadingInfo) -> Unit) : Interceptor {
8 |
9 | override fun intercept(chain: Interceptor.Chain): Response {
10 |
11 | val request = chain.request()
12 |
13 | val loadingInfo = LoadingInfo(name = request.url.encodedPath)
14 | onLoading(loadingInfo)
15 |
16 | try {
17 | return chain.proceed(request)
18 | } catch (e: Exception) {
19 | throw e
20 | } finally {
21 | onLoading(LoadingInfo(loadingInfo.name, false))
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/extensions/Int+Extensions.kt:
--------------------------------------------------------------------------------
1 | @file:JvmName("IntExtensions")
2 |
3 | package com.exozet.android.core.extensions
4 |
5 | import kotlin.random.Random
6 |
7 |
8 | fun Int.sleep() = this.toLong().sleep()
9 |
10 | fun Long.sleep() = Thread.sleep(this)
11 |
12 | fun Int.randomStrings(): String {
13 | val randomStringBuilder = StringBuilder()
14 | var tempChar: Char
15 | for (i in 0 until this) {
16 | tempChar = (Random.nextInt(96) + 32).toChar()
17 | randomStringBuilder.append(tempChar)
18 | }
19 | return randomStringBuilder.toString()
20 | }
21 |
22 | inline fun Int.repeat(factory: (index: Int) -> T) = arrayListOf().apply { for (i in 0..this@repeat) add(factory(i)) }
23 |
24 | inline fun Int.clampToListSize(list: List): Int = clamp(0, list.lastIndex)
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/services/network/fcm/FcmMessage.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.services.network.fcm
2 |
3 | import org.parceler.Parcel
4 |
5 | /**
6 | * FCM message
7 | *
8 | * {
9 | * "to": "/topics/global",
10 | * "notification": {
11 | * "title": "hello",
12 | * "text": "world"
13 | * },
14 | * "data": {
15 | * "key1": "value1",
16 | * "key2": "value2"
17 | * }
18 | * }
19 | *
20 | */
21 | @Parcel(Parcel.Serialization.BEAN)
22 | data class FcmMessage(
23 | val to: String? = null,
24 | val notification: NotificationContent? = null,
25 | val data: Map? = null
26 | ) {
27 | constructor(recipient: String, title: String, text: String, data: Map? = null)
28 | : this(recipient, NotificationContent(title, text), data)
29 | }
--------------------------------------------------------------------------------
/core/proguard-rules.pro:
--------------------------------------------------------------------------------
1 | # Add project specific ProGuard rules here.
2 | # You can control the set of applied configuration files using the
3 | # proguardFiles setting in build.gradle.
4 | #
5 | # For more details, see
6 | # http://developer.android.com/guide/developing/tools/proguard.html
7 |
8 | # If your project uses WebView with JS, uncomment the following
9 | # and specify the fully qualified class name to the JavaScript interface
10 | # class:
11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview {
12 | # public *;
13 | #}
14 |
15 | # Uncomment this to preserve the line number information for
16 | # debugging stack traces.
17 | #-keepattributes SourceFile,LineNumberTable
18 |
19 | # If you keep the line number information, uncomment this to
20 | # hide the original source file name.
21 | #-renamesourcefileattribute SourceFile
22 |
23 | -keep com.exozet.android.core.** { *; }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/extensions/Dialog+Extenions.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.extensions
2 |
3 | import android.app.Dialog
4 | import android.os.Build
5 | import android.view.Window
6 | import android.view.WindowManager
7 |
8 |
9 | fun Dialog.addFullScreenFlags(): Dialog {
10 | window?.addFullScreenFlags()
11 | return this
12 | }
13 |
14 | fun Window.addFullScreenFlags() {
15 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
16 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
17 | requestFeature(Window.FEATURE_NO_TITLE)
18 | setBackgroundDrawableResource(android.R.color.transparent)
19 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
20 | setClipToOutline(false)
21 | }
22 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/interfaces/annotations/HapticFeedback.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.interfaces.annotations
2 |
3 | import android.annotation.TargetApi
4 | import android.os.Build
5 | import android.view.HapticFeedbackConstants
6 | import androidx.annotation.IntDef
7 |
8 | @TargetApi(Build.VERSION_CODES.O_MR1)
9 | @IntDef(
10 | value = [
11 | HapticFeedbackConstants.CLOCK_TICK,
12 | HapticFeedbackConstants.CONTEXT_CLICK,
13 | HapticFeedbackConstants.KEYBOARD_PRESS, // same as HapticFeedbackConstants.KEYBOARD_TAP
14 | HapticFeedbackConstants.KEYBOARD_RELEASE,
15 | HapticFeedbackConstants.LONG_PRESS,
16 | HapticFeedbackConstants.TEXT_HANDLE_MOVE,
17 | HapticFeedbackConstants.VIRTUAL_KEY,
18 | HapticFeedbackConstants.VIRTUAL_KEY_RELEASE]
19 | )
20 | @Retention(AnnotationRetention.SOURCE)
21 | annotation class HapticFeedback
--------------------------------------------------------------------------------
/core/src/main/res/animator/shrink_scale_animation.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
15 |
16 |
25 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/services/notifications/FBNotificationJobService.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.services.notifications
2 |
3 | import com.firebase.jobdispatcher.JobParameters
4 | import com.firebase.jobdispatcher.JobService
5 | import net.kibotu.logger.Logger
6 |
7 |
8 | /**
9 | * Created by [Jan Rabe](https://about.me/janrabe).
10 | */
11 | class FBNotificationJobService : JobService() {
12 |
13 | override fun onStartJob(job: JobParameters?): Boolean {
14 | Logger.d(TAG, "[onStartJob] Performing long running task in scheduled job")
15 | // TODO(developer): add long running task here.
16 | return false
17 | }
18 |
19 | override fun onStopJob(job: JobParameters?): Boolean {
20 | Logger.d(TAG, "[onStopJob] Performing long running task in scheduled job")
21 | return false
22 | }
23 |
24 | companion object {
25 | val TAG: String = FBNotificationJobService::class.java.simpleName
26 | }
27 | }
--------------------------------------------------------------------------------
/core/src/main/res/anim/custom_exit_to_bottom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
11 |
16 |
17 |
25 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/markdown/MarkdownFragment.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.markdown
2 |
3 | import android.os.Bundle
4 | import android.text.TextUtils.isEmpty
5 | import android.view.View
6 | import com.exozet.android.core.R
7 | import com.exozet.android.core.base.BaseFragment
8 | import com.exozet.android.core.storage.bundle
9 | import kotlinx.android.synthetic.main.fragment_markdown.*
10 |
11 | class MarkdownFragment : BaseFragment() {
12 |
13 | override val layout = R.layout.fragment_markdown
14 |
15 | var filename by bundle()
16 |
17 | var url by bundle()
18 |
19 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
20 | super.onViewCreated(view, savedInstanceState)
21 |
22 | when {
23 | !isEmpty(filename) -> markdown_view.loadMarkdownFromAssets(filename)
24 | !isEmpty(url) -> markdown_view.loadUrl(url)
25 | }
26 |
27 | markdown_view.isOpenUrlInBrowser = true
28 | }
29 | }
--------------------------------------------------------------------------------
/core/src/main/res/anim/custom_enter_from_bottom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
12 |
17 |
18 |
26 |
--------------------------------------------------------------------------------
/core/src/main/res/anim/custom_exit_to_top.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
12 |
13 |
18 |
19 |
27 |
--------------------------------------------------------------------------------
/app/src/main/java/com/exozet/android/core/demo/features/realmLiveData/StringPresenter.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.demo.features.realmLiveData
2 |
3 | import androidx.recyclerview.widget.RecyclerView
4 | import com.exozet.android.core.demo.R
5 | import kotlinx.android.synthetic.main.recycler_view_item.view.*
6 | import net.kibotu.android.recyclerviewpresenter.Adapter
7 | import net.kibotu.android.recyclerviewpresenter.Presenter
8 | import net.kibotu.android.recyclerviewpresenter.PresenterModel
9 | import net.kibotu.logger.Logger.logv
10 |
11 | class StringPresenter : Presenter() {
12 |
13 | override val layout: Int = R.layout.recycler_view_item
14 |
15 | override fun bindViewHolder(viewHolder: RecyclerView.ViewHolder, item: PresenterModel, position: Int, payloads: MutableList?, adapter: Adapter) {
16 | logv { "bindViewHolder: ${item.model}" }
17 |
18 | with(viewHolder.itemView) {
19 | itemTextView.text = item.model.value
20 | }
21 | }
22 | }
--------------------------------------------------------------------------------
/app/src/test/java/com/exozet/android/core/base/SerializableTests.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.base
2 |
3 | import com.exozet.android.core.gson.gson
4 | import com.exozet.android.core.gson.toJsonPrettyPrinting
5 | import net.javacrumbs.jsonunit.JsonAssert.assertJsonEquals
6 | import org.junit.Assert
7 | import org.junit.Test
8 |
9 | /**
10 | * Created by [Jan Rabe](https://about.me/janrabe).
11 | */
12 |
13 | abstract class SerializableTests(var klass: Class<*>) : BaseTest() {
14 |
15 | open val expectedJson: String = ""
16 |
17 | @Test
18 | open fun serialize() {
19 | val actual = gson.fromJson(expectedJson.trimMargin(), klass)
20 | Assert.assertNotNull(actual)
21 | }
22 |
23 | @Test
24 | open fun deserialize() {
25 | val actual = gson.fromJson(expectedJson.trimMargin(), klass)
26 | Assert.assertNotNull(actual)
27 |
28 | val actualJson = actual.toJsonPrettyPrinting()
29 | assertJsonEquals(expectedJson.trimMargin(), actualJson)
30 | }
31 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/extensions/Iterable+Extensions.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.extensions
2 |
3 | import java.text.Collator
4 | import java.util.*
5 | import kotlin.math.absoluteValue
6 |
7 |
8 | inline fun Iterable.mostFrequent(crossinline keySelector: (T) -> K) =
9 | groupingBy(keySelector).eachCount().maxBy { it.value }?.key
10 |
11 | fun Iterable.closestValue(value: Int) = minBy { value.minus(it).absoluteValue }
12 |
13 | inline fun Iterable.sortedWithLocale(locale: Locale = Locale.GERMAN, crossinline selector: (T) -> Comparable<*>?): List {
14 |
15 | // ensure locale
16 | val comparator: Comparator = compareBy(Collator.getInstance(locale), selector)
17 |
18 | if (this is Collection) {
19 | if (size <= 1) return this.toList()
20 | @Suppress("UNCHECKED_CAST")
21 | return (toTypedArray() as Array).apply { sortWith(comparator) }.asList()
22 | }
23 | return toMutableList().apply { sortWith(comparator) }
24 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/extensions/Long+Extensions.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.extensions
2 |
3 | import java.util.*
4 | import kotlin.math.abs
5 |
6 |
7 | /**
8 | * https://stackoverflow.com/a/3758880/1006741
9 | */
10 | fun Long.humanReadableByteCountBinary(): String {
11 | val b = when (this) {
12 | Long.MIN_VALUE -> Long.MAX_VALUE
13 | else -> abs(this)
14 | }
15 | return when {
16 | b < 1024L -> "$this B"
17 | b <= 0xfffccccccccccccL shr 40 -> "%.1f KiB".format(Locale.UK, this / 1024.0)
18 | b <= 0xfffccccccccccccL shr 30 -> "%.1f MiB".format(Locale.UK, this / 1048576.0)
19 | b <= 0xfffccccccccccccL shr 20 -> "%.1f GiB".format(Locale.UK, this / 1.073741824E9)
20 | b <= 0xfffccccccccccccL shr 10 -> "%.1f TiB".format(Locale.UK, this / 1.099511627776E12)
21 | b <= 0xfffccccccccccccL -> "%.1f PiB".format(Locale.UK, (this shr 10) / 1.099511627776E12)
22 | else -> "%.1f EiB".format(Locale.UK, (this shr 20) / 1.099511627776E12)
23 | }
24 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/interfaces/annotations/ScreenOrientation.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.interfaces.annotations
2 |
3 | import android.content.pm.ActivityInfo.*
4 | import androidx.annotation.IntDef
5 |
6 |
7 | @IntDef(
8 | value = [
9 | SCREEN_ORIENTATION_UNSPECIFIED,
10 | SCREEN_ORIENTATION_LANDSCAPE,
11 | SCREEN_ORIENTATION_PORTRAIT,
12 | SCREEN_ORIENTATION_USER,
13 | SCREEN_ORIENTATION_BEHIND,
14 | SCREEN_ORIENTATION_SENSOR,
15 | SCREEN_ORIENTATION_NOSENSOR,
16 | SCREEN_ORIENTATION_SENSOR_LANDSCAPE,
17 | SCREEN_ORIENTATION_SENSOR_PORTRAIT,
18 | SCREEN_ORIENTATION_REVERSE_LANDSCAPE,
19 | SCREEN_ORIENTATION_REVERSE_PORTRAIT,
20 | SCREEN_ORIENTATION_FULL_SENSOR,
21 | SCREEN_ORIENTATION_USER_LANDSCAPE,
22 | SCREEN_ORIENTATION_USER_PORTRAIT,
23 | SCREEN_ORIENTATION_FULL_USER,
24 | SCREEN_ORIENTATION_LOCKED]
25 | )
26 | @Retention(AnnotationRetention.SOURCE)
27 | annotation class ScreenOrientation
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) Exozet GmbH
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/extensions/Formatting+Extensions.kt:
--------------------------------------------------------------------------------
1 | @file:JvmName("FormattingExtensions")
2 |
3 |
4 | package com.exozet.android.core.extensions
5 |
6 | /**
7 | * Created by [Jan Rabe](https://about.me/janrabe).
8 | */
9 |
10 | const val BYTES_TO_KB: Long = 1024
11 | const val BYTES_TO_MB = BYTES_TO_KB * 1024
12 | const val BYTES_TO_GB = BYTES_TO_MB * 1024
13 | const val BYTES_TO_TB = BYTES_TO_GB * 1024
14 |
15 | /**
16 | * alternative to Formatter.formatFileSize which doesn't show bytes and rounds to int
17 | */
18 | fun Long.formatBytes(): String {
19 | if (this <= 0)
20 | return "0 bytes"
21 |
22 | return if (this / BYTES_TO_TB > 0)
23 | String.format("%.2f TB", this / BYTES_TO_TB.toFloat())
24 | else if (this / BYTES_TO_GB > 0)
25 | String.format("%.2f GB", this / BYTES_TO_GB.toFloat())
26 | else if (this / BYTES_TO_MB > 0)
27 | String.format("%.2f MB", this / BYTES_TO_MB.toFloat())
28 | else if (this / BYTES_TO_KB > 0) String.format("%.2f KB", this / BYTES_TO_KB.toFloat()) else "$this bytes"
29 | }
30 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/extensions/Fragment+Extensions.kt:
--------------------------------------------------------------------------------
1 | @file:JvmName("FragmentExtensions")
2 |
3 | package com.exozet.android.core.extensions
4 |
5 | import androidx.annotation.IdRes
6 | import androidx.fragment.app.Fragment
7 | import com.exozet.android.core.R
8 | import net.kibotu.ContextHelper.getAppCompatActivity
9 | import net.kibotu.logger.Logger
10 |
11 |
12 | /**
13 | * Created by [Jan Rabe](https://about.me/janrabe).
14 | */
15 |
16 | @IdRes
17 | var fragmentContainerId: Int = R.id.fragment_container
18 |
19 | fun currentFragment(@IdRes container: Int = fragmentContainerId): Fragment? {
20 | return getAppCompatActivity()!!.supportFragmentManager.findFragmentById(container)
21 | }
22 |
23 | fun printBackStack() {
24 | val fm = getAppCompatActivity()!!.supportFragmentManager
25 | Logger.v("Fragment", "Current BackStack: " + fm.backStackEntryCount)
26 | for (entry in 0 until fm.backStackEntryCount) {
27 | val stackEntry = fm.getBackStackEntryAt(entry)
28 | Logger.v("Fragment", "[" + stackEntry.id + "] " + stackEntry.name)
29 | }
30 | }
31 |
32 |
--------------------------------------------------------------------------------
/app/src/test/java/com/exozet/android/core/sha256Test.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core
2 |
3 | import com.exozet.android.core.base.BaseTest
4 | import com.exozet.android.core.extensions.sha256_HMAC
5 | import com.google.common.truth.Truth
6 | import org.junit.Test
7 |
8 |
9 | class sha : BaseTest() {
10 | /**
11 | * https://tools.ietf.org/html/rfc7636#appendix-A
12 | */
13 | @Test
14 | fun sha256_HMACTest() {
15 | Truth.assertThat("any carnal pleasure.".sha256_HMAC()).isEqualTo("klV7mnDnMorgaiIdbLmVSjsQDWP3UpClQSSnaI2nOXE=")
16 | }
17 |
18 | @Test
19 | fun retrievalTokenEncryptionTest() {
20 | val actual = "any carnal pleasure.".sha256_HMAC()
21 |
22 | val expected = "klV7mnDnMorgaiIdbLmVSjsQDWP3UpClQSSnaI2nOXE="
23 | Truth.assertThat(actual).isEqualTo(expected)
24 |
25 | val expected2 = "klV7mnDnMorgaiIdbLmVSjsQDWP3UpClQSSnaI2nOXE"
26 | val removedCharacters = actual.replace("+", "-")
27 | .replace("/", "_")
28 | .replace("=", "")
29 | Truth.assertThat(removedCharacters).isEqualTo(expected2)
30 | }
31 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/ui/nullobjects/SimpleAnimationListener.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.ui.nullobjects
2 |
3 | import android.view.animation.Animation
4 |
5 | open class SimpleAnimationListener : Animation.AnimationListener {
6 |
7 | override fun onAnimationStart(animation: Animation?) {
8 | }
9 |
10 | override fun onAnimationEnd(animation: Animation?) {
11 | }
12 |
13 | override fun onAnimationRepeat(animation: Animation?) {
14 | }
15 | }
16 |
17 | fun Animation.onAnimationStart(block: (Animation?) -> Unit) = setAnimationListener(object : SimpleAnimationListener() {
18 | override fun onAnimationStart(animation: Animation?) = block(animation)
19 | })
20 |
21 | fun Animation.onAnimationEnd(block: (Animation?) -> Unit) = setAnimationListener(object : SimpleAnimationListener() {
22 | override fun onAnimationEnd(animation: Animation?) = block(animation)
23 | })
24 |
25 | fun Animation.onAnimationRepeat(block: (Animation?) -> Unit) = setAnimationListener(object : SimpleAnimationListener() {
26 | override fun onAnimationRepeat(animation: Animation?) = block(animation)
27 | })
--------------------------------------------------------------------------------
/core/src/main/res/layout/fragment_markdown.xml:
--------------------------------------------------------------------------------
1 |
2 |
8 |
9 |
23 |
24 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/utils/DrawableExtensions.java:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.utils;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.drawable.BitmapDrawable;
5 | import android.graphics.drawable.Drawable;
6 |
7 | import androidx.annotation.Nullable;
8 |
9 | import static net.kibotu.ContextHelper.getApplication;
10 |
11 | /**
12 | * Created by Nyaruhodo on 21.05.2016.
13 | */
14 | public class DrawableExtensions {
15 |
16 | @Nullable
17 | public static Drawable scaleImage(@Nullable Drawable image, float scaleFactor) {
18 |
19 | if (!(image instanceof BitmapDrawable)) {
20 | return image;
21 | }
22 |
23 | Bitmap b = ((BitmapDrawable) image).getBitmap();
24 |
25 | int sizeX = Math.round(image.getIntrinsicWidth() * scaleFactor);
26 | int sizeY = Math.round(image.getIntrinsicHeight() * scaleFactor);
27 |
28 | Bitmap bitmapResized = Bitmap.createScaledBitmap(b, sizeX, sizeY, false);
29 |
30 | image = new BitmapDrawable(getApplication().getResources(), bitmapResized);
31 |
32 | return image;
33 | }
34 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/ui/nullobjects/SimpleTabSelectionListener.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.ui.nullobjects
2 |
3 | import com.google.android.material.tabs.TabLayout
4 |
5 | open class SimpleTabSelectionListener : TabLayout.OnTabSelectedListener {
6 |
7 | override fun onTabSelected(tab: TabLayout.Tab?) {
8 | }
9 |
10 | override fun onTabUnselected(tab: TabLayout.Tab?) {
11 | }
12 |
13 | override fun onTabReselected(tab: TabLayout.Tab?) {
14 | }
15 | }
16 |
17 | fun TabLayout.onTabSelected(block: (TabLayout.Tab?) -> Unit) = addOnTabSelectedListener(object : SimpleTabSelectionListener() {
18 | override fun onTabSelected(tab: TabLayout.Tab?) = block(tab)
19 | })
20 |
21 | fun TabLayout.onTabUnselected(block: (TabLayout.Tab?) -> Unit) = addOnTabSelectedListener(object : SimpleTabSelectionListener() {
22 | override fun onTabUnselected(tab: TabLayout.Tab?) = block(tab)
23 | })
24 |
25 | fun TabLayout.onTabReselected(block: (TabLayout.Tab?) -> Unit) = addOnTabSelectedListener(object : SimpleTabSelectionListener() {
26 | override fun onTabReselected(tab: TabLayout.Tab?) = block(tab)
27 | })
--------------------------------------------------------------------------------
/app/src/main/java/com/exozet/android/core/demo/WidgetSampleFragment.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.demo
2 |
3 | import android.os.Bundle
4 | import android.view.View
5 | import androidx.appcompat.app.AppCompatActivity
6 | import com.exozet.android.core.base.BaseFragment
7 | import com.exozet.android.core.extensions.indeterminateDrawableColor
8 | import com.exozet.android.core.extensions.onClick
9 | import kotlinx.android.synthetic.main.fragment_widget_sample.*
10 | import kotlinx.android.synthetic.main.layout_toolbar.*
11 | import net.kibotu.logger.snack
12 |
13 | /**
14 | * Created by armando.shkurti on 04.04.18.
15 | */
16 | class WidgetSampleFragment : BaseFragment() {
17 |
18 | override val layout = R.layout.fragment_widget_sample
19 |
20 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
21 | super.onViewCreated(view, savedInstanceState)
22 |
23 | toolbar.title = "Sample"
24 | (activity as? AppCompatActivity)?.setSupportActionBar(toolbar)
25 |
26 | progressBar.indeterminateDrawableColor(R.color.white)
27 |
28 | customToolbarBack.onClick {
29 | snack("Back")
30 | }
31 | }
32 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/io/Cache.java:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.io;
2 |
3 | import android.content.Context;
4 |
5 | import androidx.annotation.NonNull;
6 | import androidx.annotation.Nullable;
7 |
8 | import java.io.File;
9 |
10 | /**
11 | * Created by Jan Rabe on 18/06/15.
12 | */
13 | public class Cache {
14 |
15 | public static void deleteCache(@NonNull final Context context) {
16 | try {
17 | final File dir = context.getCacheDir();
18 | if (dir != null && dir.isDirectory()) {
19 | deleteDir(dir);
20 | }
21 | } catch (@NonNull final Exception e) {
22 | }
23 | }
24 |
25 | public static boolean deleteDir(@Nullable final File dir) {
26 | if (dir != null && dir.isDirectory()) {
27 | final String[] children = dir.list();
28 | for (int i = 0; i < children.length; i++) {
29 | final boolean success = deleteDir(new File(dir, children[i]));
30 | if (!success) {
31 | return false;
32 | }
33 | }
34 | }
35 | return dir.delete();
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/utils/KeyGuardExtensions.java:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.utils;
2 |
3 | import android.annotation.SuppressLint;
4 | import android.app.KeyguardManager;
5 | import android.content.Context;
6 | import android.os.PowerManager;
7 |
8 | import androidx.annotation.RequiresPermission;
9 |
10 | import static android.Manifest.permission.DISABLE_KEYGUARD;
11 |
12 | public class KeyGuardExtensions {
13 |
14 | @RequiresPermission(DISABLE_KEYGUARD)
15 | public static void unlockScreen(Context context) {
16 | KeyguardManager km = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
17 | final KeyguardManager.KeyguardLock kl = km.newKeyguardLock("MyKeyguardLock");
18 | kl.disableKeyguard();
19 |
20 | PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
21 | @SuppressLint("InvalidWakeLockTag") PowerManager.WakeLock wakeLock = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK
22 | | PowerManager.ACQUIRE_CAUSES_WAKEUP
23 | | PowerManager.ON_AFTER_RELEASE, "MyWakeLock");
24 | wakeLock.acquire(10 * 60 * 1000L /*10 minutes*/);
25 | }
26 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/services/network/interceptors/InterceptorFactory.kt:
--------------------------------------------------------------------------------
1 | @file:JvmName("InterceptorFactory")
2 |
3 | package com.exozet.android.core.services.network.interceptors
4 |
5 | import com.github.simonpercic.oklog3.OkLogInterceptor
6 | import net.kibotu.logger.Logger
7 | import okhttp3.OkHttpClient
8 | import okhttp3.logging.HttpLoggingInterceptor
9 |
10 | fun createHttpLoggingInterceptor(enableLogging: () -> Boolean): HttpLoggingInterceptor = HttpLoggingInterceptor(object : HttpLoggingInterceptor.Logger {
11 | override fun log(message: String) {
12 | Logger.v(message)
13 | }
14 | }).apply {
15 | level = if (enableLogging())
16 | HttpLoggingInterceptor.Level.BODY
17 | else
18 | HttpLoggingInterceptor.Level.NONE
19 | }
20 |
21 | @Deprecated("removed, use app/res/xml/network-config.xml for certificates", replaceWith = ReplaceWith(""))
22 | fun OkHttpClient.Builder.addCertificates(certificate: String? = null, dangerouslyTrustingAllHosts: Boolean = false): OkHttpClient.Builder = this
23 |
24 | fun createOKLogInterceptor() = OkLogInterceptor.builder()
25 | .withRequestHeaders(true)
26 | .withResponseHeaders(true)
27 | .build()
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/ui/itemDecorator/SpacesItemDecorationEntrust.java:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.ui.itemDecorator;
2 |
3 | import android.graphics.Canvas;
4 | import android.graphics.Rect;
5 | import android.graphics.drawable.ColorDrawable;
6 | import android.graphics.drawable.Drawable;
7 | import android.view.View;
8 |
9 | import androidx.recyclerview.widget.RecyclerView;
10 |
11 | /**
12 | * 作者:请叫我百米冲刺 on 2016/12/6 上午11:37
13 | * 邮箱:mail@hezhilin.cc
14 | */
15 |
16 | public abstract class SpacesItemDecorationEntrust {
17 |
18 | //color的传入方式是resouce.getcolor
19 | protected Drawable mDivider;
20 |
21 | protected int leftRight;
22 |
23 | protected int topBottom;
24 |
25 | public SpacesItemDecorationEntrust(int leftRight, int topBottom, int mColor) {
26 | this.leftRight = leftRight;
27 | this.topBottom = topBottom;
28 | if (mColor != 0) {
29 | mDivider = new ColorDrawable(mColor);
30 | }
31 | }
32 |
33 |
34 | abstract void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state);
35 |
36 | abstract void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state);
37 |
38 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/services/connectivity/NetworkChangeReceiver.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.services.connectivity
2 |
3 | import android.content.BroadcastReceiver
4 | import android.content.Context
5 | import android.content.Intent
6 | import android.content.IntentFilter
7 | import android.net.ConnectivityManager
8 |
9 | class NetworkChangeReceiver(var onConnectivityUpdate: ((Boolean) -> Unit)? = null) : BroadcastReceiver() {
10 |
11 | override fun onReceive(context: Context?, intent: Intent?) {
12 | onConnectivityUpdate?.invoke(LiveNetworkMonitor.isConnected)
13 | }
14 |
15 | fun unregister(context: Context) {
16 | context.unregisterReceiver(this)
17 | }
18 |
19 | companion object {
20 |
21 | fun register(context: Context, onConnectivityUpdate: ((Boolean) -> Unit)? = null): NetworkChangeReceiver = NetworkChangeReceiver(onConnectivityUpdate)
22 | .also {
23 | // https://developer.android.com/training/monitoring-device-state/connectivity-monitoring#MonitorChanges
24 | @Suppress("DEPRECATION")
25 | context.registerReceiver(it, IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION))
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/ui/nullobjects/SimpleOnItemSelectedListener.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.ui.nullobjects
2 |
3 | import android.view.View
4 | import android.widget.Adapter
5 | import android.widget.AdapterView
6 |
7 | open class SimpleOnItemSelectedListener : AdapterView.OnItemSelectedListener {
8 |
9 | override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
10 | }
11 |
12 | override fun onNothingSelected(parent: AdapterView<*>?) {
13 | }
14 | }
15 |
16 | inline fun AdapterView.onItemSelected(crossinline block: (parent: AdapterView<*>?, view: View?, position: Int, id: Long) -> Unit) where T : Adapter {
17 | onItemSelectedListener = object : SimpleOnItemSelectedListener() {
18 | override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) = block(parent, view, position, id)
19 | }
20 | }
21 |
22 | inline fun AdapterView.onNothingSelected(crossinline block: (parent: AdapterView<*>?) -> Unit) where T : Adapter {
23 |
24 | onItemSelectedListener = object : SimpleOnItemSelectedListener() {
25 | override fun onNothingSelected(parent: AdapterView<*>?) = block(parent)
26 | }
27 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/ui/nullobjects/SimpleOnPageChangeListener.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.ui.nullobjects
2 |
3 | import androidx.viewpager.widget.ViewPager
4 |
5 | open class SimpleOnPageChangeListener : ViewPager.OnPageChangeListener {
6 |
7 | override fun onPageScrollStateChanged(state: Int) {
8 | }
9 |
10 | override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
11 | }
12 |
13 | override fun onPageSelected(position: Int) {
14 | }
15 | }
16 |
17 | fun ViewPager.onPageScrollStateChanged(block: (Int) -> Unit) = addOnPageChangeListener(object : SimpleOnPageChangeListener() {
18 | override fun onPageScrollStateChanged(state: Int) = block(state)
19 | })
20 |
21 | fun ViewPager.onPageScrolled(block: (Int, Float, Int) -> Unit) = addOnPageChangeListener(object : SimpleOnPageChangeListener() {
22 | override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) = block(position, positionOffset, positionOffsetPixels)
23 | })
24 |
25 | fun ViewPager.onPageSelected(block: (Int) -> Unit) = addOnPageChangeListener(object : SimpleOnPageChangeListener() {
26 | override fun onPageSelected(position: Int) = block(position)
27 | })
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/ui/custom/CustomBottomSheetBehavior.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.ui.custom
2 |
3 | import android.content.Context
4 | import android.util.AttributeSet
5 | import android.view.View
6 | import androidx.coordinatorlayout.widget.CoordinatorLayout
7 | import com.google.android.material.bottomsheet.BottomSheetBehavior
8 | import com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_HALF_EXPANDED
9 | import net.kibotu.logger.Logger.i
10 |
11 | /**
12 | * Custom behavior to inflate layout with [STATE_HALF_EXPANDED] state.
13 | */
14 | open class CustomBottomSheetBehavior : BottomSheetBehavior {
15 |
16 | constructor() : super()
17 |
18 | constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
19 |
20 | init {
21 | isHideable = false
22 | // we force initialization to be half expanded, default seems to be expanded
23 | state = STATE_HALF_EXPANDED
24 | }
25 |
26 | override fun layoutDependsOn(parent: CoordinatorLayout, child: V, dependency: View): Boolean {
27 | i("layoutDependsOn parent=$parent child=$child dependency=$dependency")
28 | return super.layoutDependsOn(parent, child, dependency)
29 | }
30 | }
--------------------------------------------------------------------------------
/core/src/main/res/animator/flip_animation.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
12 |
13 |
19 |
20 |
26 |
27 |
31 |
32 |
36 |
--------------------------------------------------------------------------------
/core/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 16dp
5 | 16dp
6 | 16dp
7 |
8 | 16dp
9 | 24dp
10 | 32dp
11 | 48dp
12 | 56dp
13 |
14 | 112sp
15 | 70sp
16 | 56sp
17 | 45sp
18 | 34sp
19 | 24sp
20 | 20sp
21 | 18sp
22 | 16sp
23 | 14sp
24 | 12sp
25 |
26 | 4dp
27 | 6dp
28 | 6dp
29 |
30 |
31 |
32 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/utils/FontExtensions.java:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.utils;
2 |
3 | import android.graphics.Typeface;
4 | import android.view.View;
5 | import android.widget.TextView;
6 |
7 | import androidx.annotation.ColorRes;
8 | import androidx.annotation.NonNull;
9 |
10 | import java.util.Collection;
11 |
12 | import static com.exozet.android.core.utils.ViewExtensions.setColor;
13 |
14 | /**
15 | * Created by jan.rabe on 17/12/15.
16 | */
17 | final public class FontExtensions {
18 |
19 | private FontExtensions() throws IllegalAccessException {
20 | throw new IllegalAccessException();
21 | }
22 |
23 | public static void applyFont(@NonNull final Typeface font, @NonNull final TextView... views) {
24 | for (final TextView view : views)
25 | view.setTypeface(font);
26 | }
27 |
28 | public static void applyColor(@ColorRes final int resourceId, @NonNull final Collection views) {
29 | for (final View v : views)
30 | setColor(v, resourceId);
31 | }
32 |
33 | public static void applyColor(@ColorRes final int resourceId, @NonNull final View... views) {
34 | for (final View v : views)
35 | setColor(v, resourceId);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/app/src/main/res/animator/flip_animation.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
12 |
13 |
19 |
20 |
26 |
27 |
31 |
32 |
36 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/extensions/RxJava+Extensions.kt:
--------------------------------------------------------------------------------
1 | @file:JvmName("RxJavaExtensions")
2 |
3 | package com.exozet.android.core.extensions
4 |
5 | import io.reactivex.Flowable
6 | import io.reactivex.android.schedulers.AndroidSchedulers
7 | import io.reactivex.disposables.Disposable
8 | import io.reactivex.schedulers.Schedulers
9 |
10 | /**
11 | * Created by armando.shkurti on 31.01.18.
12 | */
13 | fun Flowable.applySchedulersIO(): Flowable {
14 | return subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
15 | }
16 |
17 | fun Flowable.applySchedulersComputation(): Flowable {
18 | return subscribeOn(Schedulers.computation()).observeOn(AndroidSchedulers.mainThread())
19 | }
20 |
21 | fun Flowable.subscribeUIToIO(onNext: (T) -> Unit, onError: (Throwable) -> Unit): Disposable {
22 | return applySchedulersIO().subscribe({ next ->
23 | onNext(next)
24 | }, { error ->
25 | onError(error)
26 | })
27 | }
28 |
29 | fun Flowable.subscribeUIToComputation(onNext: (T) -> Unit, onError: (Throwable) -> Unit): Disposable {
30 | return applySchedulersComputation().subscribe({ next ->
31 | onNext(next)
32 | }, { error ->
33 | onError(error)
34 | })
35 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/extensions/Retrofit+Extensions.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.extensions
2 |
3 | import net.kibotu.logger.Logger.loge
4 | import okhttp3.Request
5 | import okhttp3.Response
6 |
7 | inline fun retrofit2.Response.log() {
8 | val message = "${code()} ${message()} ${errorBody()}"
9 | loge { message }
10 | }
11 |
12 | fun Response.setCustomHeader(key: String, value: String) = request.setAuthHeader(key, value)
13 |
14 | fun Request.setCustomHeader(key: String, value: String) = newBuilder()
15 | .removeHeader(key)
16 | .addHeader(key, value)
17 | .build()
18 |
19 | fun Response.setAuthHeader(authorization: String = "JWTAuthorization", token: String) = request.setAuthHeader(authorization, token)
20 |
21 | fun Request.setAuthHeader(authorization: String = "JWTAuthorization", token: String) = newBuilder()
22 | .removeHeader(authorization)
23 | .addHeader(authorization, "Bearer $token")
24 | .build()
25 |
26 | fun Response.setCacheKey(cacheKey: String = "JWTCacheKey", token: String) = request.setAuthHeader(cacheKey, token)
27 |
28 | fun Request.setCacheKey(cacheKey: String = "JWTCacheKey", token: String) = newBuilder()
29 | .removeHeader(cacheKey)
30 | .addHeader(cacheKey, token)
31 | .build()
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/utils/ThreadExtensions.java:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.utils;
2 |
3 | import androidx.annotation.NonNull;
4 |
5 | import net.kibotu.logger.Logger;
6 |
7 |
8 | /**
9 | * Created by Jan Rabe on 22/10/15.
10 | */
11 | final public class ThreadExtensions {
12 |
13 | private static final String TAG = ThreadExtensions.class.getSimpleName();
14 |
15 | private ThreadExtensions() throws IllegalAccessException {
16 | throw new IllegalAccessException();
17 | }
18 |
19 | public static void setThreadPriority(final int priority) {
20 | Logger.v(TAG, "From thread priority: " + android.os.Process.getThreadPriority(android.os.Process.myTid()));
21 | android.os.Process.setThreadPriority(priority);
22 | Logger.v(TAG, "To thread priority: " + android.os.Process.getThreadPriority(android.os.Process.myTid()));
23 | }
24 |
25 | public static void safeSleep(final int millisecond) {
26 | try {
27 | Thread.sleep(millisecond);
28 | } catch (final InterruptedException e) {
29 | e.printStackTrace();
30 | }
31 | }
32 |
33 | public static void startNewThread(@NonNull final Runnable runnable) {
34 | new Thread(runnable).start();
35 | }
36 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/utils/EncryptionExtensions.java:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.utils;
2 |
3 |
4 | import androidx.annotation.NonNull;
5 | import androidx.annotation.Nullable;
6 |
7 | import java.security.MessageDigest;
8 | import java.security.NoSuchAlgorithmException;
9 |
10 | /**
11 | * Created by Nyaruhodo on 07.05.2016.
12 | */
13 | public class EncryptionExtensions {
14 |
15 | private EncryptionExtensions() throws IllegalAccessException {
16 | throw new IllegalAccessException();
17 | }
18 |
19 | @NonNull
20 | public static String md5(@Nullable final String s) {
21 | try {
22 | // Create MD5 Hash
23 | MessageDigest digest = MessageDigest.getInstance("MD5");
24 | digest.update(s.getBytes());
25 | byte[] messageDigest = digest.digest();
26 |
27 | // Create Hex String
28 | StringBuilder hexString = new StringBuilder();
29 | for (int i = 0; i < messageDigest.length; i++)
30 | hexString.append(Integer.toHexString(0xFF & messageDigest[i]));
31 | return hexString.toString();
32 |
33 | } catch (NoSuchAlgorithmException e) {
34 | e.printStackTrace();
35 | }
36 | return "";
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/misc/SynchronizedValue.java:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.misc;
2 |
3 | public class SynchronizedValue {
4 |
5 | private T t = null;
6 | private final Object lock = new Object();
7 |
8 | public void set(T t) {
9 | synchronized (lock) {
10 | this.t = t;
11 | }
12 | }
13 |
14 | public T get() {
15 | synchronized (lock) {
16 | return t;
17 | }
18 | }
19 |
20 | @Override
21 | public String toString() {
22 | return "SynchronizedValue{" +
23 | "t=" + get() +
24 | '}';
25 | }
26 |
27 | @Override
28 | public boolean equals(Object o) {
29 | if (this == o) return true;
30 | if (o == null || getClass() != o.getClass()) return false;
31 |
32 | SynchronizedValue> that = (SynchronizedValue>) o;
33 |
34 | if (t != null ? !t.equals(that.t) : that.t != null) return false;
35 | return lock != null ? lock.equals(that.lock) : that.lock == null;
36 |
37 | }
38 |
39 | @Override
40 | public int hashCode() {
41 | int result = t != null ? t.hashCode() : 0;
42 | result = 31 * result + (lock != null ? lock.hashCode() : 0);
43 | return result;
44 | }
45 |
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/services/sms/SmsReceiver.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.services.sms
2 |
3 | import android.content.BroadcastReceiver
4 | import android.content.Context
5 | import android.content.Intent
6 | import android.os.Build
7 | import android.provider.Telephony
8 | import android.telephony.SmsMessage
9 |
10 | /**
11 | * Created by habib.birdal on 07.02.18.
12 | *
13 | *
16 | *
17 | *
18 | *
19 | *
20 | *
21 | */
22 |
23 | abstract class SmsReceiver : BroadcastReceiver() {
24 |
25 | override fun onReceive(context: Context, intent: Intent) {
26 |
27 | if (intent.extras == null)
28 | return
29 |
30 | if (Build.VERSION.SDK_INT < 19)
31 | return
32 |
33 | // @see https://developer.android.com/reference/android/telephony/SmsMessage.html
34 | val messages = Telephony.Sms.Intents.getMessagesFromIntent(intent)
35 |
36 | onSmsMessages(messages)
37 | }
38 |
39 | abstract fun onSmsMessages(messages: Array)
40 | }
--------------------------------------------------------------------------------
/core/src/main/res/values/debug.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Section
7 |
8 |
9 |
10 |
11 | Reset App
12 | Restart App
13 | Toggle Logging RAM
14 | Log Current Threads
15 | Log Current Backstack
16 | Log Current Fragment
17 |
18 | Trigger Local Push
19 |
20 |
21 |
22 | Splash Screen
23 | README.md
24 | CHANGELOG.md
25 | Build Info
26 |
27 |
28 |
29 | Default
30 | Korean
31 |
32 | Device is online.
33 | Device is not online.
34 |
35 |
--------------------------------------------------------------------------------
/core/src/main/res/anim/custom_enter_from_top.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
11 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
34 |
--------------------------------------------------------------------------------
/core/src/main/res/animator/raise.xml:
--------------------------------------------------------------------------------
1 |
16 |
17 |
18 | -
21 |
25 |
26 | -
27 |
31 |
32 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/math/Line.java:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.math;
2 |
3 | /**
4 | * Created by jan.rabe on 27/07/16.
5 | */
6 |
7 | public class Line {
8 |
9 | public Vector2 start;
10 | public Vector2 end;
11 |
12 | public Line(Vector2 start, Vector2 end) {
13 | this.start = start;
14 | this.end = end;
15 | }
16 |
17 | public Line setStart(Vector2 start) {
18 | this.start = start;
19 | return this;
20 | }
21 |
22 | public Line setEnd(Vector2 end) {
23 | this.end = end;
24 | return this;
25 | }
26 |
27 | @Override
28 | public boolean equals(Object o) {
29 | if (this == o) return true;
30 | if (o == null || getClass() != o.getClass()) return false;
31 |
32 | Line line = (Line) o;
33 |
34 | if (start != null ? !start.equals(line.start) : line.start != null) return false;
35 | return end != null ? end.equals(line.end) : line.end == null;
36 |
37 | }
38 |
39 | @Override
40 | public int hashCode() {
41 | int result = start != null ? start.hashCode() : 0;
42 | result = 31 * result + (end != null ? end.hashCode() : 0);
43 | return result;
44 | }
45 |
46 | @Override
47 | public String toString() {
48 | return start + " -> " + end;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/ui/custom/ZoomageView.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.ui.custom
2 |
3 | import android.content.Context
4 | import android.content.res.Configuration
5 | import android.util.AttributeSet
6 | import android.view.MotionEvent
7 | import android.view.View
8 |
9 | open class ZoomageView @JvmOverloads constructor(
10 | context: Context,
11 | attrs: AttributeSet? = null,
12 | defStyleAttr: Int = 0,
13 | defStyleRes: Int = 0
14 | ) : com.jsibbold.zoomage.ZoomageView(context, attrs, defStyleAttr) {
15 |
16 | internal var view: View? = null
17 |
18 | override fun onTouchEvent(event: MotionEvent?): Boolean = when {
19 | event?.pointerCount ?: 0 <= 1 -> {
20 | view?.onTouchEvent(event)
21 | isTranslatable = false // disable translation while we have only one finger on screen
22 | super.onTouchEvent(event)
23 | }
24 | else -> {
25 |
26 | // consume touch event inside scroll container like view pagers
27 | parent.requestDisallowInterceptTouchEvent(true)
28 |
29 | isTranslatable = true
30 | super.onTouchEvent(event)
31 | }
32 | }
33 |
34 | override fun onConfigurationChanged(newConfig: Configuration) {
35 | super.onConfigurationChanged(newConfig)
36 | scaleType = ScaleType.FIT_CENTER
37 | }
38 | }
--------------------------------------------------------------------------------
/app/src/test/java/com/exozet/android/core/NumberTest.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core
2 |
3 | import com.exozet.android.core.base.BaseTest
4 | import com.exozet.android.core.extensions.percentToValueOfRange
5 | import com.exozet.android.core.extensions.valueToPercentOfRange
6 | import com.google.common.truth.Truth
7 | import org.junit.Test
8 |
9 | class NumberTest : BaseTest() {
10 |
11 | @Test
12 | fun percentTest() {
13 |
14 | val min = 0f
15 | val max = 100f
16 |
17 | (0..100).forEach {
18 |
19 | val expected = (it - min) / (max - min)
20 | Truth.assertThat(it.toFloat().valueToPercentOfRange(min, max)).isEqualTo(expected)
21 | }
22 |
23 | Truth.assertThat(0f.valueToPercentOfRange(-100f, 100f)).isEqualTo(0.5f)
24 | Truth.assertThat(0f.valueToPercentOfRange(100f, -100f)).isEqualTo(0.5f)
25 | }
26 |
27 | @Test
28 | fun percentValueTest() {
29 |
30 | val min = 0f
31 | val max = 100f
32 |
33 | (0..100).forEach {
34 |
35 | val expected = min + it * (max - min)
36 | Truth.assertThat(it.toFloat().percentToValueOfRange(min, max)).isEqualTo(expected)
37 | }
38 |
39 | Truth.assertThat(0.5f.percentToValueOfRange(-100f, 100f)).isEqualTo(0f)
40 | Truth.assertThat(0.5f.percentToValueOfRange(100f, -100f)).isEqualTo(0f)
41 | }
42 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/extensions/Date+Extensions.kt:
--------------------------------------------------------------------------------
1 | @file:JvmName("DateExtensions")
2 |
3 | package com.exozet.android.core.extensions
4 |
5 | import android.annotation.SuppressLint
6 | import android.os.Build
7 | import android.provider.Settings
8 | import android.text.TextUtils
9 | import net.kibotu.ContextHelper
10 | import java.text.SimpleDateFormat
11 | import java.util.*
12 |
13 | /**
14 | * Created by [Jan Rabe](https://about.me/janrabe).
15 | */
16 |
17 | /**
18 | * https://stackoverflow.com/a/43550290
19 | */
20 | @SuppressLint("SimpleDateFormat")
21 | fun Date?.localize(): String {
22 | if (this == null)
23 | return ""
24 |
25 | if (Build.VERSION.SDK_INT > Build.VERSION_CODES.JELLY_BEAN_MR2) {
26 | val pattern = android.text.format.DateFormat.getBestDateTimePattern(Locale.getDefault(), "EEEEMMMMdyyyy") //order on the String doesn't matter
27 | val formatter = SimpleDateFormat(pattern, Locale.getDefault())
28 |
29 | return formatter.format(this)
30 | }
31 |
32 | val format = Settings.System.getString(ContextHelper.getApplication()!!.contentResolver, Settings.System.DATE_FORMAT)
33 | val formatter = if (TextUtils.isEmpty(format))
34 | android.text.format.DateFormat.getMediumDateFormat(ContextHelper.getApplication())
35 | else
36 | SimpleDateFormat(format)
37 |
38 | return formatter.format(this)
39 | }
--------------------------------------------------------------------------------
/core/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
8 |
9 |
13 |
14 |
18 |
19 |
23 |
24 |
31 |
32 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/markdown/RawOutputFragment.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.markdown
2 |
3 | import android.content.Intent
4 | import android.os.Bundle
5 | import android.text.method.ScrollingMovementMethod
6 | import android.view.View
7 | import com.exozet.android.core.R
8 | import com.exozet.android.core.base.BaseFragment
9 | import com.exozet.android.core.extensions.onClick
10 | import com.exozet.android.core.storage.bundle
11 | import kotlinx.android.synthetic.main.fragment_raw_output.*
12 | import net.kibotu.logger.Logger.logv
13 | import net.kibotu.resourceextension.resString
14 |
15 |
16 | class RawOutputFragment : BaseFragment() {
17 |
18 | override val layout = R.layout.fragment_raw_output
19 |
20 | private var raw by bundle()
21 |
22 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
23 | super.onViewCreated(view, savedInstanceState)
24 |
25 | content.movementMethod = ScrollingMovementMethod()
26 |
27 | logv { "$raw" }
28 | content.text = "$raw"
29 |
30 | share.onClick {
31 | share()
32 | }
33 | }
34 |
35 | private fun share() =
36 | Intent().apply {
37 | action = Intent.ACTION_SEND
38 | putExtra(Intent.EXTRA_TEXT, raw)
39 | type = "text/plain"
40 | startActivity(Intent.createChooser(this, R.string.share_with.resString))
41 | }
42 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/extensions/BottomSheetBehaviour+Extensions.kt:
--------------------------------------------------------------------------------
1 | @file:JvmName("BottomSheetBehaviourExtensions")
2 |
3 | package com.exozet.android.core.extensions
4 |
5 | import android.view.View
6 | import com.google.android.material.bottomsheet.BottomSheetBehavior
7 |
8 | inline var BottomSheetBehavior.isExpanded: Boolean
9 | get() = state == BottomSheetBehavior.STATE_EXPANDED
10 | set(expand) {
11 | state = if (expand) BottomSheetBehavior.STATE_EXPANDED else BottomSheetBehavior.STATE_COLLAPSED
12 | }
13 |
14 | inline var BottomSheetBehavior.isCollapsed: Boolean
15 | get() = state == BottomSheetBehavior.STATE_COLLAPSED
16 | set(collapse) {
17 | state = if (collapse) BottomSheetBehavior.STATE_COLLAPSED else BottomSheetBehavior.STATE_EXPANDED
18 | }
19 |
20 | inline var BottomSheetBehavior.isExpandedHalf: Boolean
21 | get() = state == BottomSheetBehavior.STATE_HALF_EXPANDED
22 | set(expandHalf) {
23 | state = if (expandHalf) BottomSheetBehavior.STATE_HALF_EXPANDED else BottomSheetBehavior.STATE_EXPANDED
24 | }
25 |
26 | inline var BottomSheetBehavior.isHidden: Boolean
27 | get() = state == BottomSheetBehavior.STATE_HIDDEN
28 | set(hide) {
29 | isHideable = true
30 | state = if (hide) BottomSheetBehavior.STATE_HIDDEN else BottomSheetBehavior.STATE_COLLAPSED
31 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/realm/RealmDao.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.realm
2 |
3 | import io.realm.Realm
4 | import io.realm.RealmModel
5 | import io.realm.RealmResults
6 | import io.realm.kotlin.deleteFromRealm
7 |
8 | /**
9 | * {@inheritDoc}
10 | */
11 | open class RealmDao(val realm: Realm) {
12 |
13 | /**
14 | * Gets all [T].
15 | */
16 | inline fun all(): RealmResults = realm.where(T::class.java).findAllAsync()
17 |
18 | /**
19 | * Updates and inserts [T].
20 | */
21 | open fun insertOrUpdate(item: T) = realm.executeTransactionAsync { it.insertOrUpdate(item) }
22 |
23 | /**
24 | * Updates and inserts list of [T].
25 | */
26 | open fun insertOrUpdate(items: List) = realm.executeTransactionAsync { it.insertOrUpdate(items) }
27 |
28 | /**
29 | * Runs realm transaction so we can update managed objects directly.
30 | */
31 | open fun update(item: T, block: (item: T) -> Unit) { realm.executeTransaction { block(item) } }
32 |
33 | /**
34 | * Deletes [T].
35 | */
36 | open fun delete(item: T) = item.deleteFromRealm()
37 |
38 | /**
39 | * Deletes all [T].
40 | */
41 | inline fun deleteAll() = realm.executeTransactionAsync { realm ->
42 | realm.where(T::class.java)
43 | .findAllAsync()
44 | .deleteAllFromRealm()
45 | }
46 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/extensions/Uri+Extensions.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.extensions
2 |
3 | import android.net.Uri
4 | import android.os.Environment
5 | import androidx.browser.customtabs.CustomTabsIntent
6 | import com.github.florent37.application.provider.ActivityProvider
7 | import com.github.florent37.application.provider.application
8 | import de.cketti.shareintentbuilder.ShareIntentBuilder
9 | import net.kibotu.ContextHelper
10 | import java.io.File
11 |
12 | /**
13 | * Created by [Jan Rabe](https://about.me/janrabe).
14 | */
15 |
16 | fun String.parseAssetFile(): Uri = Uri.parse("file:///android_asset/$this")
17 |
18 | fun String.parseInternalStorageFile(): Uri = Uri.parse("${ContextHelper.getApplication()!!.filesDir.absolutePath}/$this")
19 |
20 | fun String.parseExternalStorageFile(): Uri = Uri.parse("${Environment.getExternalStorageDirectory()}/$this")
21 |
22 | fun String.parseFile(): Uri = Uri.fromFile(File(this))
23 |
24 | val Uri.fileExists: Boolean
25 | get() = File(toString()).exists()
26 |
27 | fun Uri.share() = ShareIntentBuilder.from(application!!)
28 | .text(this.toString())
29 | .share()
30 |
31 | fun Uri.openChromeTab() = CustomTabsIntent.Builder()
32 | .build()
33 | .launchUrl(ActivityProvider.currentActivity!!, this)
34 |
35 | fun Uri.openDialPad() = toString().replace("tel:", "").openDialPad()
36 |
37 | fun Uri.sendEmail() =
38 | sendEmail(toString().replace("mailto:", ""))
39 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/locale/CountryCode.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.locale
2 |
3 | /**
4 | * Created by jan.rabe on 14/04/15.
5 | */
6 | ///
7 | /// @see http://countrycode.org/
8 | ///
9 | enum class CountryCode {
10 | UNKNOWN,
11 | ALB,
12 | AND,
13 | ARE,
14 | ARG,
15 | AUS,
16 | AUT,
17 | BEL,
18 | BGR,
19 | BHR,
20 | BHS,
21 | BIH,
22 | BLR,
23 | BRA,
24 | BRN,
25 | BWA,
26 | CAN,
27 | CHE,
28 | CHL,
29 | CHN,
30 | CYM,
31 | CZE,
32 | DEU,
33 | DNK,
34 | EGY,
35 | ESP,
36 | EST,
37 | FIN,
38 | FRA,
39 | GBR,
40 | GIB,
41 | GLP,
42 | GRC,
43 | GUF,
44 | GUY,
45 | HKG,
46 | HRV,
47 | HUN,
48 | IDN,
49 | IND,
50 | IRL,
51 | ISL,
52 | ITA,
53 | JEY,
54 | JOR,
55 | KWT,
56 | KOS,
57 | LBN,
58 | LIE,
59 | LTU,
60 | LUX,
61 | LVA,
62 | MCO,
63 | MDA,
64 | MEX,
65 | MKD,
66 | MLT,
67 | MNE,
68 | MTQ,
69 | MYS,
70 | NLD,
71 | NOR,
72 | NZL,
73 | OMN,
74 | POL,
75 | PRI,
76 | PRT,
77 | QAT,
78 | REU,
79 | ROU,
80 | RUS,
81 | SAU,
82 | SGP,
83 | SMR,
84 | SRB,
85 | SVK,
86 | SVN,
87 | SWE,
88 | SWZ,
89 | THA,
90 | TUR,
91 | TWN,
92 | UKR,
93 | USA,
94 | VAT,
95 | VEN,
96 | VIR,
97 | ZAF
98 | }
99 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/misc/AutoClearedValue.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.misc
2 |
3 | import androidx.fragment.app.Fragment
4 | import androidx.lifecycle.Lifecycle
5 | import androidx.lifecycle.LifecycleObserver
6 | import androidx.lifecycle.OnLifecycleEvent
7 | import kotlin.properties.ReadWriteProperty
8 | import kotlin.reflect.KProperty
9 |
10 | /**
11 | * A lazy property that gets cleaned up when the fragment is destroyed.
12 | *
13 | * Accessing this variable in a destroyed fragment will throw NPE.
14 | */
15 | class AutoClearedValue(val fragment: Fragment) : ReadWriteProperty {
16 |
17 | private var _value: T? = null
18 |
19 | init {
20 | fragment.lifecycle.addObserver(object : LifecycleObserver {
21 | @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
22 | fun onDestroy() {
23 | _value = null
24 | }
25 | })
26 | }
27 |
28 | override fun getValue(thisRef: Fragment, property: KProperty<*>): T {
29 | return _value ?: throw IllegalStateException(
30 | "should never call auto-cleared-value get when it might not be available"
31 | )
32 | }
33 |
34 | override fun setValue(thisRef: Fragment, property: KProperty<*>, value: T) {
35 | _value = value
36 | }
37 | }
38 |
39 | /**
40 | * Creates an [AutoClearedValue] associated with this fragment.
41 | */
42 | fun Fragment.autoCleared() = AutoClearedValue(this)
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/utils/ShellExtensions.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.utils
2 |
3 | import android.text.TextUtils
4 | import android.util.Log
5 | import net.kibotu.logger.Logger
6 | import org.apache.commons.io.IOUtils
7 |
8 | /**
9 | * Created by [Jan Rabe](https://about.me/janrabe).
10 | */
11 |
12 |
13 | fun executeShellCommand(command: String): Boolean {
14 | var process: Process? = null
15 | try {
16 | Logger.v("Command", "> $command")
17 | process = Runtime.getRuntime().exec(command)
18 |
19 | val outputStreamResult = IOUtils.toString(process.errorStream)
20 | if (!TextUtils.isEmpty(outputStreamResult)) {
21 | Logger.e("Command", "> outputStreamResult")
22 | Logger.snackbar(outputStreamResult)
23 | return false
24 | }
25 |
26 | val inputStreamResult = IOUtils.toString(process.inputStream)
27 | if (!TextUtils.isEmpty(inputStreamResult)) {
28 | Logger.v("Command", "> $inputStreamResult")
29 | Logger.snackbar(inputStreamResult)
30 | }
31 |
32 | return true
33 | } catch (e: Exception) {
34 | Log.e("Command", "" + e.message)
35 | return false
36 | } finally {
37 | if (process != null) {
38 | try {
39 | process.destroy()
40 | } catch (e: Exception) {
41 | Log.e("Command", "" + e.message)
42 | }
43 |
44 | }
45 | }
46 | }
--------------------------------------------------------------------------------
/app/src/main/res/xml/lorem_ipsum.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Brian Cantoni
5 | Generate random Lorem Ipsum text from lipsum.com. Parameters: amount=number of things to fetch; what: paras, words, bytes, or lists; start: set to "no" to not start all strings with Lorem Ipsum.
6 | http://www.lipsum.com/
7 | select * from {table} where amount='5' and what='paras';
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/utils/BitmapExtensions.java:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.utils;
2 |
3 | import android.graphics.Bitmap;
4 | import android.graphics.Matrix;
5 | import android.graphics.drawable.BitmapDrawable;
6 | import android.widget.ImageView;
7 |
8 | import androidx.annotation.NonNull;
9 |
10 | /**
11 | * Created by Jan Rabe on 24/09/15.
12 | */
13 | final public class BitmapExtensions {
14 |
15 | private BitmapExtensions() throws IllegalAccessException {
16 | throw new IllegalAccessException();
17 | }
18 |
19 | public static Bitmap flipHorizontally(@NonNull final Bitmap src) {
20 | final Matrix matrix = new Matrix();
21 | matrix.preScale(1.0f, -1.0f);
22 | return createBitmapWithMatrix(src, matrix);
23 | }
24 |
25 | public static Bitmap flipVertically(@NonNull final Bitmap src) {
26 | final Matrix matrix = new Matrix();
27 | matrix.preScale(-1.0f, 1.0f);
28 | return createBitmapWithMatrix(src, matrix);
29 | }
30 |
31 | public static Bitmap createBitmapWithMatrix(@NonNull final Bitmap src, @NonNull final Matrix matrix) {
32 | final Bitmap dst = Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, true);
33 | if (src != dst) {
34 | // src.recycle();
35 | }
36 | return dst;
37 | }
38 |
39 | public static Bitmap getBitmap(@NonNull final ImageView imageView) {
40 | return ((BitmapDrawable) imageView.getDrawable()).getBitmap();
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/core/src/main/res/values/attr.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/ui/custom/DisableableAppBarLayoutBehavior.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.ui.custom
2 |
3 | import android.content.Context
4 | import android.util.AttributeSet
5 | import android.view.View
6 | import androidx.coordinatorlayout.widget.CoordinatorLayout
7 | import com.google.android.material.appbar.AppBarLayout
8 |
9 | open class DisableableAppBarLayoutBehavior(context: Context? = null, attrs: AttributeSet? = null) : AppBarLayout.Behavior(context, attrs) {
10 |
11 | var isEnabled = true
12 |
13 | override fun onStartNestedScroll(parent: CoordinatorLayout, child: AppBarLayout, directTargetChild: View, target: View, nestedScrollAxes: Int, type: Int): Boolean {
14 | return isEnabled && super.onStartNestedScroll(parent, child, directTargetChild, target, nestedScrollAxes, type)
15 | }
16 |
17 | override fun onNestedScroll(coordinatorLayout: CoordinatorLayout, child: AppBarLayout, target: View, dxConsumed: Int, dyConsumed: Int, dxUnconsumed: Int, dyUnconsumed: Int, type: Int, consumed: IntArray) {
18 | if (!isEnabled) return
19 | super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type, consumed)
20 | }
21 |
22 | override fun onNestedPreScroll(coordinatorLayout: CoordinatorLayout, child: AppBarLayout, target: View, dx: Int, dy: Int, consumed: IntArray, type: Int) {
23 | if (!isEnabled) return
24 | super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type)
25 | }
26 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/ui/nullobjects/SimpleAnimatorListener.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.ui.nullobjects
2 |
3 | import android.animation.Animator
4 |
5 | open class SimpleAnimatorListener : Animator.AnimatorListener {
6 |
7 | override fun onAnimationStart(animation: Animator?) {
8 | }
9 |
10 | override fun onAnimationEnd(animation: Animator?) {
11 | }
12 |
13 | override fun onAnimationCancel(animation: Animator?) {
14 | }
15 |
16 | override fun onAnimationRepeat(animation: Animator?) {
17 | }
18 | }
19 |
20 | @Deprecated("use androidktx", ReplaceWith("doOnStart"))
21 | fun Animator.onAnimationStart(block: (Animator?) -> Unit) = addListener(object : SimpleAnimatorListener() {
22 | override fun onAnimationStart(animation: Animator?) = block(animation)
23 | })
24 |
25 | @Deprecated("use androidktx", ReplaceWith("doOnEnd"))
26 | fun Animator.onAnimationEnd(block: (Animator?) -> Unit) = addListener(object : SimpleAnimatorListener() {
27 | override fun onAnimationEnd(animation: Animator?) = block(animation)
28 | })
29 |
30 | @Deprecated("use androidktx", ReplaceWith("doOnCancel"))
31 | fun Animator.onAnimationCancel(block: (Animator?) -> Unit) = addListener(object : SimpleAnimatorListener() {
32 | override fun onAnimationCancel(animation: Animator?) = block(animation)
33 | })
34 |
35 | @Deprecated("use androidktx", ReplaceWith("doOnRepeat"))
36 | fun Animator.onAnimationRepeat(block: (Animator?) -> Unit) = addListener(object : SimpleAnimatorListener() {
37 | override fun onAnimationRepeat(animation: Animator?) = block(animation)
38 | })
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/ui/custom/ScrollListener.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.ui.custom
2 |
3 | import android.view.GestureDetector
4 | import android.view.MotionEvent
5 | import com.exozet.android.core.extensions.px
6 | import net.kibotu.resourceextension.dp
7 |
8 | open class ScrollListener(val width: () -> Int, val height: () -> Int) : GestureDetector.SimpleOnGestureListener() {
9 |
10 | var onScroll: ((percentX: Float, percentY: Float) -> Unit)? = null
11 | /**
12 | * Min Swipe X-Distance
13 | */
14 | var thresholdX = 3.dp
15 |
16 | /**
17 | * Min Swipe Y-Distance
18 | */
19 | var thresholdY = 3.dp
20 |
21 | /**
22 | * Starting X-Position
23 | */
24 | protected var startX = 0f
25 |
26 | /**
27 | * Starting Y-Position
28 | */
29 | protected var startY = 0f
30 |
31 | override fun onDown(e: MotionEvent?): Boolean {
32 | startX = e?.x ?: 0f
33 | startY = e?.y ?: 0f
34 | return true
35 | }
36 |
37 | override fun onScroll(e1: MotionEvent?, e2: MotionEvent?, distanceX: Float, distanceY: Float): Boolean {
38 | val dX = startX - (e2?.x ?: 0f)
39 | val dY = startY - (e2?.y ?: 0f)
40 |
41 | return when {
42 | Math.abs(dX) <= thresholdX && Math.abs(dY) <= thresholdY -> false
43 | else -> {
44 |
45 | val percentX = (dX) / width()
46 | val percentY = (dY) / height()
47 |
48 | onScroll?.invoke(percentX, percentY)
49 |
50 | true
51 | }
52 | }
53 | }
54 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/services/crypto/Blowfish.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.services.crypto
2 |
3 | import android.util.Base64
4 | import net.kibotu.logger.Logger
5 | import javax.crypto.Cipher
6 | import javax.crypto.spec.IvParameterSpec
7 | import javax.crypto.spec.SecretKeySpec
8 |
9 | object Blowfish {
10 |
11 | private const val ALGORITHM = "Blowfish"
12 | private const val MODE = "Blowfish/CBC/PKCS5Padding"
13 | private const val IV = "abcdefgh"
14 |
15 | /**
16 | * e.g. class App : MultiDexApplication() { init { Blowfish.KEY = "mysecurepassword" } }
17 | */
18 | var KEY = "changemeplease"
19 |
20 | fun encrypt(value: String) = try {
21 | val secretKeySpec = SecretKeySpec(KEY.toByteArray(), ALGORITHM)
22 | val cipher = Cipher.getInstance(MODE)
23 | cipher.init(Cipher.ENCRYPT_MODE, secretKeySpec, IvParameterSpec(IV.toByteArray()))
24 | val values = cipher.doFinal(value.trimMargin().toByteArray())
25 | Base64.encodeToString(values, Base64.DEFAULT).trimMargin()
26 | } catch (e: Exception) {
27 | Logger.e(e)
28 | ""
29 | }
30 |
31 | fun decrypt(value: String?) = try {
32 | val values = Base64.decode(value, Base64.DEFAULT)
33 | val secretKeySpec = SecretKeySpec(KEY.toByteArray(), ALGORITHM)
34 | val cipher = Cipher.getInstance(MODE)
35 | cipher.init(Cipher.DECRYPT_MODE, secretKeySpec, IvParameterSpec(IV.toByteArray()))
36 | String(cipher.doFinal(values)).trimMargin()
37 | } catch (e: Exception) {
38 | Logger.e(e)
39 | ""
40 | }
41 | }
--------------------------------------------------------------------------------
/app/src/test/java/com/exozet/android/core/WhenTests.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core
2 |
3 | import com.exozet.android.core.base.BaseTest
4 | import org.junit.Assert
5 | import org.junit.Test
6 |
7 | /**
8 | * Created by [Jan Rabe](https://about.me/janrabe).
9 | */
10 |
11 | class WhenTests : BaseTest() {
12 |
13 | @Test
14 | fun whenTestTrueTrue() {
15 |
16 | val a = true
17 | val b = true
18 |
19 | when {
20 | a and b -> Assert.assertTrue(true)
21 | a -> Assert.fail()
22 | else -> Assert.fail()
23 | }
24 |
25 | when {
26 | a -> Assert.assertTrue(true)
27 | a and b -> Assert.fail()
28 | else -> Assert.fail()
29 | }
30 | }
31 |
32 | @Test
33 | fun whenTestTrueFalse() {
34 |
35 | val a = true
36 | val b = false
37 |
38 | when {
39 | a and b -> Assert.fail()
40 | a -> Assert.assertTrue(true)
41 | else -> Assert.fail()
42 | }
43 | }
44 |
45 | @Test
46 | fun whenTestFalseFalse() {
47 |
48 | val a = false
49 | val b = false
50 |
51 | when {
52 | a and b -> Assert.fail()
53 | a -> Assert.fail()
54 | else -> Assert.assertTrue(true)
55 | }
56 | }
57 |
58 | @Test
59 | fun whenTestFalseTrue() {
60 |
61 | val a = false
62 | val b = true
63 |
64 | when {
65 | a and b -> Assert.fail()
66 | a -> Assert.fail()
67 | else -> Assert.assertTrue(true)
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/misc/FakeDataGenerator.kt:
--------------------------------------------------------------------------------
1 | @file:JvmName("FakeDataGenerator")
2 |
3 | package com.exozet.android.core.misc
4 |
5 | import android.graphics.Color
6 | import android.graphics.drawable.ColorDrawable
7 | import com.exozet.android.core.utils.MathExtensions.random
8 | import com.exozet.android.core.utils.MathExtensions.randomBoolean
9 | import java.text.MessageFormat.format
10 | import kotlin.math.roundToInt
11 |
12 | /**
13 | * Created by Nyaruhodo on 07.05.2016.
14 | */
15 |
16 | fun createRandomImageUrl(): String {
17 |
18 | val landscape = randomBoolean()
19 | val endpoint = randomBoolean()
20 |
21 | val width = random(300, 400)
22 | val height = random(200, 300)
23 |
24 | return format(
25 | if (endpoint)
26 | "https://lorempixel.com/{0}/{1}/"
27 | else
28 | "https://picsum.photos/{0}/{1}/",
29 | if (landscape) width else height, if (landscape) height else width
30 | )
31 | }
32 |
33 | @JvmOverloads
34 | fun generateRandomColorDrawable(alpha: Int = 255, red: Int = 255, green: Int = 255, blue: Int = 255): ColorDrawable = ColorDrawable(generateRandomColor(alpha, red, green, blue))
35 |
36 | @JvmOverloads
37 | fun generateRandomColor(alpha: Int = 255, red: Int = 255, green: Int = 255, blue: Int = 255): Int {
38 |
39 | var r = random(256)
40 | var g = random(256)
41 | var b = random(256)
42 |
43 | // mix the color
44 | r = ((r + red) / 2f).roundToInt()
45 | g = ((g + green) / 2f).roundToInt()
46 | b = ((b + blue) / 2f).roundToInt()
47 |
48 | return Color.argb(alpha, r, g, b)
49 | }
50 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/ui/nullobjects/SimpleMotionListener.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.ui.nullobjects
2 |
3 | import androidx.constraintlayout.motion.widget.MotionLayout
4 | import androidx.constraintlayout.motion.widget.MotionScene
5 | import io.reactivex.Observable
6 |
7 | /**
8 | * https://developer.android.com/reference/android/support/constraint/motion/MotionLayout.TransitionListener
9 | */
10 | open class SimpleMotionListener : MotionLayout.TransitionListener {
11 |
12 | override fun onTransitionTrigger(motionLayout: MotionLayout, trigger: Int, positive: Boolean, progress: Float) {
13 | }
14 |
15 | override fun onTransitionStarted(motionLayout: MotionLayout, startId: Int, endId: Int) {
16 | }
17 |
18 | override fun onTransitionChange(motionLayout: MotionLayout, startId: Int, endId: Int, progress: Float) {
19 | }
20 |
21 | override fun onTransitionCompleted(motionLayout: MotionLayout, currentId: Int) {
22 | }
23 | }
24 |
25 | fun MotionLayout.onCollapseStateChanged(): Observable {
26 |
27 | var listener: MotionLayout.TransitionListener?
28 |
29 | return io.reactivex.subjects.BehaviorSubject.create { emitter ->
30 |
31 | if (emitter.isDisposed)
32 | return@create
33 |
34 | listener = object : SimpleMotionListener() {
35 | override fun onTransitionChange(motionLayout: MotionLayout, startId: Int, endId: Int, progress: Float) {
36 | emitter.onNext(progress)
37 | }
38 | }
39 |
40 | setTransitionListener(listener)
41 |
42 | }.doOnDispose {
43 | setTransitionListener(null)
44 | }.distinctUntilChanged()
45 | }
--------------------------------------------------------------------------------
/app/src/main/java/com/exozet/android/core/demo/features/realmLiveData/ViewModelRealmSampleFragment.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.demo.features.realmLiveData
2 |
3 | import android.os.Bundle
4 | import android.view.View
5 | import androidx.lifecycle.Observer
6 | import androidx.lifecycle.ViewModelProvider
7 | import com.exozet.android.core.base.BaseFragment
8 | import com.exozet.android.core.demo.R
9 | import com.exozet.android.core.extensions.onClick
10 | import com.exozet.android.core.uid.PrimaryKeyGenerator
11 | import kotlinx.android.synthetic.main.fragment_viewmodel_sample.*
12 | import net.kibotu.android.recyclerviewpresenter.PresenterAdapter
13 | import net.kibotu.android.recyclerviewpresenter.PresenterModel
14 |
15 |
16 | class ViewModelRealmSampleFragment : BaseFragment() {
17 |
18 | override val layout: Int = R.layout.fragment_viewmodel_sample
19 |
20 | override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
21 | super.onViewCreated(view, savedInstanceState)
22 |
23 | val adapter = PresenterAdapter()
24 | adapter.registerPresenter(StringPresenter())
25 | list.adapter = adapter
26 |
27 | val vm = ViewModelProvider(this).get(RepositoryViewModel::class.java)
28 |
29 | vm.strings.observe(this, Observer {
30 |
31 | val primaryKeyGenerator = PrimaryKeyGenerator()
32 |
33 | adapter.submitList(it.map { PresenterModel(it, R.layout.recycler_view_item, uuid = primaryKeyGenerator.newUID().toString()) })
34 |
35 | list.smoothScrollToPosition(it.lastIndex.coerceAtLeast(0))
36 | })
37 |
38 | add.onClick { vm.addItem() }
39 |
40 | remove.onClick { vm.deleteAll() }
41 | }
42 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/storage/SecurePreferencesLongString.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.storage
2 |
3 | import android.content.Context
4 | import de.adorsys.android.securestoragelibrary.SecurePreferences
5 |
6 |
7 | private const val chunkSize = 240
8 |
9 | private fun getNumberOfChunksKey(key: String) = "${key}_numberOfChunks"
10 |
11 | internal fun Context.setLongStringValue(key: String, value: String) {
12 | val chunks = value.chunked(chunkSize)
13 |
14 | SecurePreferences.setValue(this, getNumberOfChunksKey(key), chunks.size)
15 |
16 | chunks.forEachIndexed { index, chunk ->
17 | SecurePreferences.setValue(this, "$key$index", chunk)
18 | }
19 | }
20 |
21 | internal fun Context.getLongStringValue(key: String): String? {
22 | val numberOfChunks = SecurePreferences.getIntValue(this, getNumberOfChunksKey(key), 0)
23 |
24 | if (numberOfChunks == 0) {
25 | return null
26 | }
27 |
28 | return (0 until numberOfChunks)
29 | .map { index ->
30 | val string = SecurePreferences.getStringValue(this, "$key$index", null) ?: run {
31 | return null
32 | }
33 |
34 | string
35 | }.reduce { accumulator, chunk -> accumulator + chunk }
36 | }
37 |
38 | internal fun Context.removeLongStringValue(key: String) {
39 | val numberOfChunks = SecurePreferences.getIntValue(this, getNumberOfChunksKey(key), 0)
40 |
41 | (0 until numberOfChunks).map { SecurePreferences.removeValue(this, "$key$it") }
42 | SecurePreferences.removeValue(this, getNumberOfChunksKey(key))
43 | }
44 |
45 | internal fun Context.containsLongStringValue(key: String): Boolean = SecurePreferences.contains(this, getNumberOfChunksKey(key))
46 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/services/network/interceptors/UrlDecodedInterceptor.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.services.network.interceptors
2 |
3 | import okhttp3.Interceptor
4 | import okhttp3.RequestBody
5 | import okhttp3.Response
6 | import okio.Buffer
7 | import java.io.IOException
8 |
9 | class UrlDecodedInterceptor : Interceptor {
10 |
11 | @Throws(IOException::class)
12 | override fun intercept(chain: Interceptor.Chain): Response {
13 | val original = chain.request()
14 |
15 | val postBody = bodyToString(original.body)
16 | val body = original.body
17 | var requestBody: RequestBody? = null
18 |
19 | if (body != null) {
20 | requestBody = RequestBody.create(original.body!!.contentType(), postBody)
21 | }
22 |
23 | val request = when {
24 | original.method == "post" -> original.newBuilder()
25 | .method(original.method, original.body)
26 | .post(requestBody!!)
27 | original.method == "put" -> original.newBuilder()
28 | .method(original.method, original.body)
29 | .put(requestBody!!)
30 | else -> original.newBuilder()
31 | .method(original.method, original.body)
32 | }
33 |
34 | return chain.proceed(request.build())
35 | }
36 |
37 | fun bodyToString(request: RequestBody?): String {
38 | return try {
39 | val buffer = Buffer()
40 | if (request != null)
41 | request.writeTo(buffer)
42 | else
43 | return ""
44 | buffer.readUtf8()
45 | } catch (e: IOException) {
46 | "did not work"
47 | }
48 | }
49 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/gson/GsonProvider.kt:
--------------------------------------------------------------------------------
1 | @file:JvmName("GsonProvider")
2 |
3 | package com.exozet.android.core.gson
4 |
5 | import com.exozet.android.core.time.TimestampConvert
6 | import com.google.gson.Gson
7 | import com.google.gson.reflect.TypeToken
8 |
9 | /**
10 | * Created by [Jan Rabe](https://about.me/janrabe).
11 | */
12 |
13 |
14 | val gsonWithDate by lazy {
15 | with(GsonExclusionStrategy()) {
16 | com.google.gson.GsonBuilder()
17 | .addSerializationExclusionStrategy(this)
18 | .addDeserializationExclusionStrategy(this)
19 | .disableHtmlEscaping()
20 | .setDateFormat(TimestampConvert.iso8601Format())
21 | .create()
22 | }
23 | }
24 |
25 | val gson by lazy {
26 | with(GsonExclusionStrategy()) {
27 | com.google.gson.GsonBuilder()
28 | .addSerializationExclusionStrategy(this)
29 | .addDeserializationExclusionStrategy(this)
30 | .disableHtmlEscaping()
31 | .create()
32 | }
33 | }
34 |
35 | val gsonPrettyPrinting by lazy {
36 | with(GsonExclusionStrategy()) {
37 | com.google.gson.GsonBuilder()
38 | .addSerializationExclusionStrategy(this)
39 | .addDeserializationExclusionStrategy(this)
40 | .disableHtmlEscaping()
41 | .setPrettyPrinting()
42 | .create()
43 | }
44 | }
45 |
46 | inline fun T.toJson(): String = gson.toJson(this)
47 |
48 | inline fun T.toJsonPrettyPrinting(): String = gsonPrettyPrinting.toJson(this)
49 |
50 | inline fun String.fromJson(): T = gson.fromJson(this)
51 |
52 | inline fun Gson.fromJson(json: String): T = this.fromJson(json, object : TypeToken() {}.type)
--------------------------------------------------------------------------------
/app/google-services.json:
--------------------------------------------------------------------------------
1 | {
2 | "project_info": {
3 | "project_number": "761920839110",
4 | "firebase_url": "https://androidcore-exozet.firebaseio.com",
5 | "project_id": "androidcore-exozet",
6 | "storage_bucket": "androidcore-exozet.appspot.com"
7 | },
8 | "client": [
9 | {
10 | "client_info": {
11 | "mobilesdk_app_id": "1:761920839110:android:588901dc9a9996c3",
12 | "android_client_info": {
13 | "package_name": "com.exozet.android.core.demo"
14 | }
15 | },
16 | "oauth_client": [
17 | {
18 | "client_id": "761920839110-7kp2qohhetr2emeq7t8frmfr9mfpl36v.apps.googleusercontent.com",
19 | "client_type": 1,
20 | "android_info": {
21 | "package_name": "com.exozet.android.core.demo",
22 | "certificate_hash": "ae98a892e282182458bb7d2d3d366d13381f0801"
23 | }
24 | },
25 | {
26 | "client_id": "761920839110-1ev37noehfuu5qkqjf2qraefli0nvgci.apps.googleusercontent.com",
27 | "client_type": 3
28 | }
29 | ],
30 | "api_key": [
31 | {
32 | "current_key": "AIzaSyAkiWv6PXIVlYmrf2BF6Y7TyO-S15SKXm0"
33 | }
34 | ],
35 | "services": {
36 | "analytics_service": {
37 | "status": 1
38 | },
39 | "appinvite_service": {
40 | "status": 2,
41 | "other_platform_oauth_client": [
42 | {
43 | "client_id": "761920839110-1ev37noehfuu5qkqjf2qraefli0nvgci.apps.googleusercontent.com",
44 | "client_type": 3
45 | }
46 | ]
47 | },
48 | "ads_service": {
49 | "status": 2
50 | }
51 | }
52 | }
53 | ],
54 | "configuration_version": "1"
55 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/ui/custom/CustomCardView.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.ui.custom
2 |
3 | import android.content.Context
4 | import android.graphics.Canvas
5 | import android.graphics.Path
6 | import android.graphics.RectF
7 | import android.util.AttributeSet
8 | import com.exozet.android.core.R
9 | import com.google.android.material.card.MaterialCardView
10 | import com.google.android.material.shape.CornerFamily
11 | import com.google.android.material.shape.ShapeAppearanceModel
12 | import com.google.android.material.shape.ShapeAppearancePathProvider
13 | import net.kibotu.resourceextension.dp
14 |
15 |
16 | /** A Card view that clips the content of any shape, this should be done upstream in card,
17 | * working around it for now.
18 | */
19 | open class CustomCardView @JvmOverloads constructor(
20 | context: Context,
21 | attrs: AttributeSet? = null,
22 | defStyle: Int = R.attr.materialCardViewStyle
23 | ) : MaterialCardView(context, attrs, defStyle) {
24 |
25 | private val pathProvider = ShapeAppearancePathProvider()
26 |
27 | private val path: Path = Path()
28 |
29 | // context, attrs, defStyle, R.style.Widget_MaterialComponents_CardView
30 | private val shapeAppearance: ShapeAppearanceModel = ShapeAppearanceModel
31 | .Builder()
32 | .setTopLeftCorner(CornerFamily.ROUNDED, 6f.dp)
33 | .setTopRightCorner(CornerFamily.ROUNDED, 6f.dp)
34 | .build()
35 |
36 | private val rectF = RectF(0f, 0f, 0f, 0f)
37 |
38 | override fun onDraw(canvas: Canvas?) {
39 | rectF.right = width.toFloat()
40 | rectF.bottom = height.toFloat()
41 | pathProvider.calculatePath(shapeAppearance, 1f, rectF, path)
42 | canvas!!.clipPath(path)
43 | super.onDraw(canvas)
44 | }
45 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/extensions/Collection+Extensions.kt:
--------------------------------------------------------------------------------
1 | @file:JvmName("CollectionExtensions")
2 |
3 | package com.exozet.android.core.extensions
4 |
5 | import java.util.*
6 | import kotlin.random.Random
7 |
8 | /**
9 | * Created by [Jan Rabe](https://about.me/janrabe).
10 | */
11 |
12 |
13 | fun Collection.isLast(position: Int) = position == size - 1
14 |
15 |
16 | inline fun T.addToList(list: MutableList): T = this.apply {
17 | list.add(this)
18 | }
19 |
20 | fun Collection<*>?.indexOutOfBounds(index: Int): Boolean {
21 | return this == null || index < 0 || index > this.size - 1
22 | }
23 |
24 | fun java.util.Collection.removeInRange(position: Int, count: Int) {
25 | this.removeAll(drop(position).take(count))
26 | }
27 |
28 | fun java.util.List.removeInRange(position: Int, count: Int) {
29 | this.removeAll(drop(position).take(count))
30 | }
31 |
32 | fun java.util.ArrayList.removeInRange(position: Int, count: Int) {
33 | this.removeAll(drop(position).take(count))
34 | }
35 |
36 | fun Collection.intersect(other: MutableCollection): Collection {
37 | val result = ArrayList()
38 | for (t in this) {
39 | if (other.remove(t)) result.add(t)
40 | }
41 | return result
42 | }
43 |
44 | fun Collection<*>?.isEmpty() = this == null || this.isEmpty()
45 |
46 | /**
47 | * Returns a random element from this collection using the specified source of randomness.
48 | *
49 | * @throws NoSuchElementException if this collection is empty.
50 | */
51 | @SinceKotlin("1.3")
52 | fun Collection.random(random: Int): List {
53 | if (isEmpty())
54 | throw NoSuchElementException("Collection is empty.")
55 | return (0 until random).map { elementAt(Random.nextInt(size)) }
56 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/extensions/Button+Extensions.kt:
--------------------------------------------------------------------------------
1 | @file:JvmName("ButtonExtensions")
2 |
3 | package com.exozet.android.core.extensions
4 |
5 | import android.R
6 | import android.content.res.ColorStateList
7 | import android.graphics.drawable.Drawable
8 | import android.graphics.drawable.StateListDrawable
9 | import android.view.View
10 | import android.widget.CompoundButton
11 | import androidx.annotation.ColorRes
12 | import androidx.appcompat.widget.AppCompatButton
13 | import net.kibotu.resourceextension.resColor
14 |
15 | /**
16 | * Created by [Jan Rabe](https://about.me/janrabe).
17 | */
18 |
19 |
20 | fun CompoundButton.setCheckedWithoutAnimation(checked: Boolean) {
21 | val beforeVisibility = visibility
22 | visibility = View.INVISIBLE
23 | isChecked = checked
24 | visibility = beforeVisibility
25 | }
26 |
27 | fun AppCompatButton.iconWith(defaultIcon: Drawable, selectedIcon: Drawable): StateListDrawable = StateListDrawable().apply {
28 | val iconSelected = selectedIcon
29 | iconSelected.setBounds(0, 0, 21, 21)
30 | val defaultIcon = defaultIcon
31 | defaultIcon.setBounds(0, 0, 21, 21)
32 |
33 | addState(intArrayOf(R.attr.state_selected), iconSelected)
34 | addState(intArrayOf(-R.attr.state_selected), defaultIcon)
35 | addState(intArrayOf(), defaultIcon)
36 | }
37 |
38 | fun AppCompatButton.textColorsWith(@ColorRes color: Int, @ColorRes selectedColor: Int): ColorStateList {
39 | return ColorStateList(
40 | arrayOf(
41 | intArrayOf(R.attr.state_selected),
42 | intArrayOf(-R.attr.state_selected),
43 | intArrayOf()
44 | ),
45 | intArrayOf(
46 | selectedColor.resColor,
47 | color.resColor,
48 | color.resColor
49 | )
50 | )
51 | }
52 |
53 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/ui/itemDecorator/GridSpacingItemDecoration.java:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.ui.itemDecorator;
2 |
3 | import android.graphics.Rect;
4 | import android.view.View;
5 |
6 | import androidx.annotation.NonNull;
7 | import androidx.recyclerview.widget.RecyclerView;
8 |
9 | public class GridSpacingItemDecoration extends RecyclerView.ItemDecoration {
10 |
11 | private final int spanCount;
12 | private final int spacing;
13 | private final boolean includeEdge;
14 |
15 | public GridSpacingItemDecoration(int spanCount, int spacing, boolean includeEdge) {
16 | this.spanCount = spanCount;
17 | this.spacing = spacing;
18 | this.includeEdge = includeEdge;
19 | }
20 |
21 | @Override
22 | public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
23 | int position = parent.getChildAdapterPosition(view); // item position
24 | int column = position % spanCount; // item column
25 |
26 | if (includeEdge) {
27 | outRect.left = spacing - column * spacing / spanCount; // spacing - column * ((1f / spanCount) * spacing)
28 | outRect.right = (column + 1) * spacing / spanCount; // (column + 1) * ((1f / spanCount) * spacing)
29 |
30 | if (position < spanCount) { // top edge
31 | outRect.top = spacing;
32 | }
33 | outRect.bottom = spacing; // item bottom
34 | } else {
35 | outRect.left = column * spacing / spanCount; // column * ((1f / spanCount) * spacing)
36 | outRect.right = spacing - (column + 1) * spacing / spanCount; // spacing - (column + 1) * ((1f / spanCount) * spacing)
37 | if (position >= spanCount) {
38 | outRect.top = spacing; // item top
39 | }
40 | }
41 | }
42 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/ui/nullobjects/SimpleTextWatcher.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.ui.nullobjects
2 |
3 | import android.text.Editable
4 | import android.text.TextWatcher
5 | import android.widget.EditText
6 |
7 | open class SimpleTextWatcher : TextWatcher {
8 |
9 | override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
10 | }
11 |
12 | override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
13 | }
14 |
15 | override fun afterTextChanged(s: Editable?) {
16 | }
17 | }
18 |
19 | fun EditText.beforeTextChanged(block: (s: CharSequence?, start: Int, count: Int, after: Int) -> Unit): TextWatcher {
20 | val listener = object : SimpleTextWatcher() {
21 |
22 | override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
23 | if (s?.isNotEmpty() == true)
24 | block(s, start, count, after)
25 | }
26 | }
27 | addTextChangedListener(listener)
28 | return listener
29 | }
30 |
31 | fun EditText.onTextChanged(block: (s: CharSequence?, start: Int, before: Int, count: Int) -> Unit): TextWatcher {
32 |
33 | val listener = object : SimpleTextWatcher() {
34 |
35 | override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
36 | if (s?.isNotEmpty() == true)
37 | block(s, start, before, count)
38 | }
39 | }
40 | addTextChangedListener(listener)
41 | return listener
42 | }
43 |
44 | fun EditText.afterTextChanged(block: (s: CharSequence?) -> Unit): TextWatcher {
45 | val listener = object : SimpleTextWatcher() {
46 |
47 | override fun afterTextChanged(s: Editable?) {
48 | if (s?.isNotEmpty() == true)
49 | block(s)
50 | }
51 | }
52 | addTextChangedListener(listener)
53 | return listener
54 | }
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # gralde config
2 | org.gradle.jvmargs=-Xms8g
3 | org.gradle.daemon=true
4 | org.gradle.parallel=true
5 | org.gradle.workers.max=32
6 | org.gradle.caching=true
7 |
8 | # kotlin config
9 | #android.enableAapt2=false
10 | kotlinOptions.allWarningsAsErrors=false
11 | android.enableR8=true
12 | android.enableR8.fullMode=true
13 | android.enableD8=true
14 | android.enableD8.desugaring=true
15 | # https://developer.android.com/topic/libraries/support-library/androidx-overview
16 | android.useAndroidX=true
17 | # Automatically convert third-party libraries to use AndroidX
18 | android.enableJetifier=true
19 | # Kotlin code style for this project: "official" or "obsolete":
20 | kotlin.code.style=official
21 |
22 | #robolectric
23 | android.enableUnitTestBinaryResources=true
24 | # https://speakerdeck.com/snehpandya18/mastering-gradle-3
25 | # gradle=build -x lint -x lintVitalRelease
26 | # https://developer.android.com/studio/preview/features/?utm_source=android-studio#lazy_task_config
27 | android.debug.obsoleteApi=false
28 | #android.proguard.enableRulesExtraction=false
29 |
30 | kapt.incremental.apt=true
31 | android.enableSeparateAnnotationProcessing=true
32 | javaCompileOptions.annotationProcessorOptions.includeCompileClasspath = true
33 |
34 | # app
35 | APPLICATION_ID=com.exozet.android.core.demo
36 | VERSION_NAME=1.0.0
37 |
38 | # debug keystore
39 | DEBUG_KEYSYORE_PATH=../debug.jks
40 | DEBUG_KEYSTORE_ALLIAS=exozet
41 | DEBUG_STORE_PASSWORD=Ex0zet!337
42 | DEBUG_KEY_PASSWORD=Ex0zet!337
43 | # AE:98:A8:92:E2:82:18:24:58:BB:7D:2D:3D:36:6D:13:38:1F:08:01
44 |
45 | # release keystore
46 | RELEASE_KEYSYORE_PATH=../release.jks
47 | RELEASE_KEYSTORE_ALIAS=exozet
48 | RELEASE_STORE_PASSWORD=Ex0zet!337
49 | RELEASE_KEY_PASSWORD=Ex0zet!337
50 | # 77:16:5A:50:63:37:6D:1F:79:D4:F2:54:A4:EA:28:6E:8A:FE:74:82
51 |
52 | # VSC config
53 | VSC_PATH=https://github.com/exozet/AndroidCore.git
54 |
55 | # github, gitlab, bitbucket
56 | VSC_TYPE=github
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/extensions/AudioManager+Extensions.kt:
--------------------------------------------------------------------------------
1 | @file:JvmName("AudioManagerExtensions")
2 |
3 | package com.exozet.android.core.extensions
4 |
5 | import android.media.AudioManager
6 | import android.media.AudioManager.*
7 | import android.os.Build.VERSION.SDK_INT
8 | import android.os.Build.VERSION_CODES.M
9 | import androidx.core.math.MathUtils.clamp
10 |
11 | /**
12 | * Created by [Jan Rabe](https://about.me/janrabe).
13 | */
14 |
15 | private var _isMuted: Boolean = false
16 |
17 | var AudioManager.isMuted: Boolean
18 | get() = _isMuted
19 | private set(value) {
20 | _isMuted = value
21 | }
22 |
23 | @Suppress("DEPRECATION")
24 | fun AudioManager.mute(mute: Boolean = true) {
25 | isMuted = mute
26 | if (SDK_INT >= M) {
27 | val flag = if (mute) ADJUST_MUTE else ADJUST_UNMUTE
28 | adjustStreamVolume(STREAM_NOTIFICATION, flag, 0)
29 | adjustStreamVolume(STREAM_ALARM, flag, 0)
30 | adjustStreamVolume(STREAM_MUSIC, flag, 0)
31 | adjustStreamVolume(STREAM_RING, flag, 0)
32 | adjustStreamVolume(STREAM_SYSTEM, flag, 0)
33 | } else {
34 | setStreamMute(STREAM_NOTIFICATION, mute)
35 | setStreamMute(STREAM_ALARM, mute)
36 | setStreamMute(STREAM_MUSIC, mute)
37 | setStreamMute(STREAM_RING, mute)
38 | setStreamMute(STREAM_SYSTEM, mute)
39 | }
40 | }
41 |
42 | @Suppress("DEPRECATION")
43 | fun AudioManager.unmute() {
44 | mute(false)
45 | }
46 |
47 | fun AudioManager.decreaseVolume() {
48 | changeVolume(-10)
49 | }
50 |
51 | fun AudioManager.increaseVolume() {
52 | changeVolume(10)
53 | }
54 |
55 | fun AudioManager.toggleMute() {
56 | mute(!isMuted)
57 | }
58 |
59 | fun AudioManager.changeVolume(dt: Int) {
60 | val streamMaxVolume = audioManager.getStreamVolume(STREAM_MUSIC)
61 | val volume = clamp(streamMaxVolume + dt, 0, streamMaxVolume)
62 | audioManager.setStreamVolume(STREAM_MUSIC, volume, 0)
63 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/utils/ByteExtensions.java:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.utils;
2 |
3 | /**
4 | * Created by jan.rabe on 10/08/16.
5 | */
6 |
7 | public class ByteExtensions {
8 |
9 | final protected static char[] hexArray = "0123456789ABCDEF".toCharArray();
10 |
11 | public static String bytesToHex(byte[] bytes) {
12 | char[] hexChars = new char[bytes.length * 2];
13 | for (int j = 0; j < bytes.length; j++) {
14 | int v = bytes[j] & 0xFF;
15 | hexChars[j * 2] = hexArray[v >>> 4];
16 | hexChars[j * 2 + 1] = hexArray[v & 0x0F];
17 | }
18 | return new String(hexChars);
19 | }
20 |
21 | public static boolean validUTF8(byte[] input) {
22 | int i = 0;
23 | // Check for BOM
24 | if (input.length >= 3 && (input[0] & 0xFF) == 0xEF
25 | && (input[1] & 0xFF) == 0xBB & (input[2] & 0xFF) == 0xBF) {
26 | i = 3;
27 | }
28 |
29 | int end;
30 | for (int j = input.length; i < j; ++i) {
31 | int octet = input[i];
32 | if ((octet & 0x80) == 0) {
33 | continue; // ASCII
34 | }
35 |
36 | // Check for UTF-8 leading byte
37 | if ((octet & 0xE0) == 0xC0) {
38 | end = i + 1;
39 | } else if ((octet & 0xF0) == 0xE0) {
40 | end = i + 2;
41 | } else if ((octet & 0xF8) == 0xF0) {
42 | end = i + 3;
43 | } else {
44 | // Java only supports BMP so 3 is max
45 | return false;
46 | }
47 |
48 | while (i < end) {
49 | i++;
50 | octet = input[i];
51 | if ((octet & 0xC0) != 0x80) {
52 | // Not a valid trailing byte
53 | return false;
54 | }
55 | }
56 | }
57 | return true;
58 | }
59 |
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/realm/RealmRepository.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.realm
2 |
3 | import androidx.annotation.WorkerThread
4 | import io.realm.RealmModel
5 | import io.realm.RealmResults
6 | import kotlinx.coroutines.Dispatchers
7 | import kotlinx.coroutines.withContext
8 |
9 | abstract class RealmRepository, T : RealmModel> {
10 |
11 | abstract val db: Dao
12 |
13 | /**
14 | * Gets all [T].
15 | */
16 | inline fun all(): RealmResults = db.all()
17 |
18 | /**
19 | * Updates and inserts [T].
20 | */
21 | fun insertOrUpdate(item: T) = db.insertOrUpdate(item)
22 |
23 | /**
24 | * Updates and inserts list of [T].
25 | */
26 | fun insertOrUpdate(items: List) = db.insertOrUpdate(items)
27 |
28 | /**
29 | * Deletes [T].
30 | */
31 | fun delete(item: T) = db.delete(item)
32 |
33 | /**
34 | * Deletes all [T].
35 | */
36 | inline fun deleteAll() = db.deleteAll()
37 |
38 | /**
39 | * Loads [T] from network and inserts into [Dao].
40 | */
41 | suspend inline fun loadAndUpdate() {
42 | val result = withContext(Dispatchers.IO) {
43 | load()
44 | } ?: return
45 |
46 | db.insertOrUpdate(result)
47 | }
48 |
49 | /**
50 | * Loads [T] on [Dispatchers.IO] thread.
51 | */
52 | @WorkerThread
53 | open suspend fun load(): T? = TODO()
54 |
55 | /**
56 | * Loads [T] from network and inserts into [Dao].
57 | */
58 | suspend inline fun > loadAndUpdateList() {
59 | val result = withContext(Dispatchers.IO) {
60 | loadList()
61 | } ?: return
62 |
63 | db.insertOrUpdate(result)
64 | }
65 |
66 | /**
67 | * Loads a list of [T] on [Dispatchers.IO] thread.
68 | */
69 | @WorkerThread
70 | open suspend fun loadList(): List? = TODO()
71 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/ui/custom/BottomNavigationBehavior.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.ui.custom
2 |
3 | import android.content.Context
4 | import android.util.AttributeSet
5 | import android.view.Gravity
6 | import android.view.View
7 | import androidx.coordinatorlayout.widget.CoordinatorLayout
8 | import androidx.core.view.ViewCompat
9 | import com.google.android.material.snackbar.Snackbar
10 | import kotlin.math.max
11 | import kotlin.math.min
12 |
13 | open class BottomNavigationBehavior(context: Context, attrs: AttributeSet) : CoordinatorLayout.Behavior(context, attrs) {
14 |
15 | override fun onStartNestedScroll(coordinatorLayout: CoordinatorLayout, child: V, directTargetChild: View, target: View, axes: Int, type: Int): Boolean {
16 | return axes == ViewCompat.SCROLL_AXIS_VERTICAL
17 | }
18 |
19 | override fun onNestedPreScroll(coordinatorLayout: CoordinatorLayout, child: V, target: View, dx: Int, dy: Int, consumed: IntArray, type: Int) {
20 | super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type)
21 | child.translationY = max(0f, min(child.height.toFloat(), child.translationY + dy))
22 | }
23 |
24 | override fun layoutDependsOn(parent: CoordinatorLayout, child: V, dependency: View): Boolean {
25 | if (dependency is Snackbar.SnackbarLayout) {
26 | updateSnackbar(child, dependency)
27 | }
28 | return super.layoutDependsOn(parent, child, dependency)
29 | }
30 |
31 | private fun updateSnackbar(child: View, snackbarLayout: Snackbar.SnackbarLayout) {
32 | if (snackbarLayout.layoutParams is CoordinatorLayout.LayoutParams) {
33 | val params = snackbarLayout.layoutParams as CoordinatorLayout.LayoutParams
34 |
35 | params.anchorId = child.id
36 | params.anchorGravity = Gravity.TOP
37 | params.gravity = Gravity.TOP
38 | snackbarLayout.layoutParams = params
39 | }
40 | }
41 | }
--------------------------------------------------------------------------------
/app/src/main/res/drawable-v24/ic_launcher_foreground.xml:
--------------------------------------------------------------------------------
1 |
8 |
13 |
14 |
20 |
23 |
26 |
27 |
28 |
29 |
35 |
36 |
--------------------------------------------------------------------------------
/core/src/main/res/layout/fragment_raw_output.xml:
--------------------------------------------------------------------------------
1 |
2 |
9 |
10 |
18 |
19 |
32 |
33 |
34 |
35 |
44 |
45 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/utils/CalendarExtensions.java:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.utils;
2 |
3 |
4 | import androidx.annotation.NonNull;
5 | import androidx.annotation.Nullable;
6 |
7 | import java.util.Calendar;
8 | import java.util.Date;
9 | import java.util.Locale;
10 |
11 | /**
12 | * Created by Jan Rabe on 01/10/15.
13 | */
14 | final public class CalendarExtensions {
15 |
16 | public CalendarExtensions() throws IllegalAccessException {
17 | throw new IllegalAccessException();
18 | }
19 |
20 | public static int getDayFromTimestamp(final long timestamp) {
21 | final Calendar cal = Calendar.getInstance();
22 | cal.setTimeInMillis(timestamp);
23 | return cal.get(Calendar.DAY_OF_MONTH);
24 | }
25 |
26 | @NonNull
27 | public static Date[] getPastMonths(final int amount) {
28 | final Calendar calendar = Calendar.getInstance();
29 | final Date[] result = new Date[amount];
30 | for (int i = 0; i < amount; ++i) {
31 | result[i] = calendar.getTime();
32 | calendar.add(Calendar.MONTH, -1);
33 | }
34 | return result;
35 | }
36 |
37 | public static int getYear(@Nullable final Date date, @NonNull final Locale locale) {
38 | if (date == null)
39 | return 0;
40 |
41 | final Calendar cal = Calendar.getInstance(locale);
42 | cal.setTime(date);
43 | return cal.get(Calendar.YEAR);
44 | }
45 |
46 | public static int getDay(@Nullable final Date date, @NonNull final Locale locale) {
47 | if (date == null)
48 | return 0;
49 |
50 | final Calendar cal = Calendar.getInstance(locale);
51 | cal.setTime(date);
52 | return cal.get(Calendar.DAY_OF_MONTH);
53 | }
54 |
55 | public static int getMonth(@Nullable final Date date, @NonNull final Locale locale) {
56 | if (date == null)
57 | return 0;
58 |
59 | final Calendar cal = Calendar.getInstance(locale);
60 | cal.setTime(date);
61 | return cal.get(Calendar.MONTH);
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/utils/DateExtensions.java:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.utils;
2 |
3 |
4 | import androidx.annotation.NonNull;
5 | import androidx.annotation.Nullable;
6 |
7 | import java.text.DateFormat;
8 | import java.text.ParseException;
9 | import java.text.SimpleDateFormat;
10 | import java.util.Calendar;
11 | import java.util.Date;
12 | import java.util.GregorianCalendar;
13 | import java.util.Locale;
14 | import java.util.TimeZone;
15 | import java.util.concurrent.TimeUnit;
16 |
17 | /**
18 | * Created by Jan Rabe on 24/09/15.
19 | */
20 | final public class DateExtensions {
21 |
22 | private DateExtensions() throws IllegalAccessException {
23 | throw new IllegalAccessException();
24 | }
25 |
26 | /**
27 | * Format dd.MM.yy hh:mm e.g.: 24.09.15 12:26
28 | */
29 | @Nullable
30 | public static Date parseDate(final String date) {
31 | final DateFormat df = new SimpleDateFormat("dd.MM.yy hh:mm");
32 | Date result = null;
33 | try {
34 | result = df.parse(date);
35 | } catch (final ParseException e) {
36 | e.printStackTrace();
37 | }
38 | return result;
39 | }
40 |
41 | @NonNull
42 | public static String parseDateInHoursMinutesDay(final Date date) {
43 | final DateFormat hours = new SimpleDateFormat("hh:mm");
44 | final DateFormat day = new SimpleDateFormat("dd.MM.yy");
45 | return hours.format(date) + " am " + day.format(date);
46 | }
47 |
48 | public static long getTimeZoneOffset() {
49 | return getTimeZoneOffset(Locale.getDefault());
50 | }
51 |
52 | public static long getTimeZoneOffset(Locale locale) {
53 | Calendar calendar = new GregorianCalendar(locale);
54 | TimeZone timeZone = calendar.getTimeZone();
55 | int GMTOffset = timeZone.getOffset(new Date().getTime());
56 | return TimeUnit.MINUTES.convert(GMTOffset, TimeUnit.MILLISECONDS);
57 | }
58 |
59 | public static boolean usesDayLightSaving() {
60 | return TimeZone.getDefault().inDaylightTime(new Date());
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/ui/itemDecorator/SpacesItemDecoration.java:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.ui.itemDecorator;
2 |
3 | import android.graphics.Canvas;
4 | import android.graphics.Rect;
5 | import android.view.View;
6 |
7 | import androidx.recyclerview.widget.GridLayoutManager;
8 | import androidx.recyclerview.widget.RecyclerView;
9 |
10 | /**
11 | * Created by Au61 on 2016/1/15.
12 | */
13 | public class SpacesItemDecoration extends RecyclerView.ItemDecoration {
14 |
15 | private SpacesItemDecorationEntrust mEntrust;
16 | private int mColor;
17 | private int leftRight;
18 | private int topBottom;
19 |
20 |
21 | public SpacesItemDecoration(int leftRight, int topBottom) {
22 | this.leftRight = leftRight;
23 | this.topBottom = topBottom;
24 | }
25 |
26 | public SpacesItemDecoration(int leftRight, int topBottom, int mColor) {
27 | this(leftRight, topBottom);
28 | this.mColor = mColor;
29 | }
30 |
31 | @Override
32 | public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
33 | if (mEntrust == null) {
34 | mEntrust = getEntrust(parent.getLayoutManager());
35 | }
36 | mEntrust.onDraw(c, parent, state);
37 | super.onDraw(c, parent, state);
38 | }
39 |
40 | @Override
41 | public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
42 | if (mEntrust == null) {
43 | mEntrust = getEntrust(parent.getLayoutManager());
44 | }
45 | mEntrust.getItemOffsets(outRect, view, parent, state);
46 | }
47 |
48 | private SpacesItemDecorationEntrust getEntrust(RecyclerView.LayoutManager manager) {
49 | SpacesItemDecorationEntrust entrust = null;
50 | //要注意这边的GridLayoutManager是继承LinearLayoutManager,所以要先判断GridLayoutManager
51 | if (manager instanceof GridLayoutManager) {
52 | entrust = new GridEntrust(leftRight, topBottom, mColor);
53 | } else {//其他的都当做Linear来进行计算
54 | entrust = new LinearEntrust(leftRight, topBottom, mColor);
55 | }
56 | return entrust;
57 | }
58 |
59 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/base/BaseActivity.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.base
2 |
3 | import android.os.Bundle
4 | import androidx.annotation.LayoutRes
5 | import androidx.appcompat.app.AppCompatActivity
6 | import com.exozet.android.core.R
7 | import com.exozet.android.core.extensions.currentFragment
8 | import com.exozet.android.core.extensions.printBackStack
9 | import com.exozet.android.core.interfaces.BackPress
10 | import com.google.android.gms.common.ConnectionResult.*
11 | import com.google.android.gms.common.GoogleApiAvailability
12 |
13 | /**
14 | * Created by armando.shkurti on 14/12/17.
15 | */
16 | @Deprecated("just demo, don't use")
17 | abstract class BaseActivity : AppCompatActivity() {
18 |
19 | @LayoutRes
20 | var activityLayoutId = R.layout.activity_main
21 |
22 | override fun onCreate(savedInstanceState: Bundle?) {
23 | super.onCreate(savedInstanceState)
24 |
25 | setContentView(activityLayoutId)
26 |
27 | checkGooglePlayServices()
28 | }
29 |
30 | override fun onBackPressed() {
31 |
32 | // hide keyboard
33 | // DeviceExtensions.hideKeyboard()
34 |
35 | // let fragments handle back press
36 | val fragment = currentFragment()
37 | if (fragment is BackPress && fragment.consumeBackPress())
38 | return
39 |
40 | // pop back stack
41 | if (supportFragmentManager.backStackEntryCount > 0) {
42 | supportFragmentManager.popBackStack()
43 | printBackStack()
44 | return
45 | }
46 |
47 | super.onBackPressed()
48 | }
49 |
50 | private fun checkGooglePlayServices() {
51 | when (GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(this)) {
52 | SERVICE_MISSING -> GoogleApiAvailability.getInstance().getErrorDialog(this, SERVICE_MISSING, 0).show()
53 | SERVICE_VERSION_UPDATE_REQUIRED -> GoogleApiAvailability.getInstance().getErrorDialog(this, SERVICE_VERSION_UPDATE_REQUIRED, 0).show()
54 | SERVICE_DISABLED -> GoogleApiAvailability.getInstance().getErrorDialog(this, SERVICE_DISABLED, 0).show()
55 | }
56 | }
57 | }
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/ui/itemDecorator/GridItemDecoration.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.ui.itemDecorator
2 |
3 | import android.graphics.Rect
4 | import android.view.View
5 | import androidx.recyclerview.widget.RecyclerView
6 |
7 | /***
8 | * Made by Lokesh Desai (Android4Dev)
9 | * https://www.android4dev.com/how-to-use-recyclerview-with-gridlayoutmanager-android/
10 | */
11 | class GridItemDecoration(private val gridSpacing: Int, private val gridSize: Int) : RecyclerView.ItemDecoration() {
12 |
13 | private var needLeftSpacing = false
14 |
15 | override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
16 | val frameWidth = ((parent.width - gridSpacing.toFloat() * (gridSize - 1)) / gridSize).toInt()
17 | val padding = parent.width / gridSize - frameWidth
18 | val itemPosition = (view.layoutParams as RecyclerView.LayoutParams).viewAdapterPosition
19 | if (itemPosition < gridSize) {
20 | outRect.top = 0
21 | } else {
22 | outRect.top = gridSpacing
23 | }
24 | if (itemPosition % gridSize == 0) {
25 | outRect.left = 0
26 | outRect.right = padding
27 | needLeftSpacing = true
28 | } else if ((itemPosition + 1) % gridSize == 0) {
29 | needLeftSpacing = false
30 | outRect.right = 0
31 | outRect.left = padding
32 | } else if (needLeftSpacing) {
33 | needLeftSpacing = false
34 | outRect.left = gridSpacing - padding
35 | if ((itemPosition + 2) % gridSize == 0) {
36 | outRect.right = gridSpacing - padding
37 | } else {
38 | outRect.right = gridSpacing / 2
39 | }
40 | } else if ((itemPosition + 2) % gridSize == 0) {
41 | needLeftSpacing = false
42 | outRect.left = gridSpacing / 2
43 | outRect.right = gridSpacing - padding
44 | } else {
45 | needLeftSpacing = false
46 | outRect.left = gridSpacing / 2
47 | outRect.right = gridSpacing / 2
48 | }
49 | outRect.bottom = 0
50 | }
51 | }
--------------------------------------------------------------------------------
/core/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
17 |
18 |
19 |
22 |
23 |
26 |
27 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
44 |
45 |
46 |
47 |
48 |
49 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/core/src/main/java/com/exozet/android/core/extensions/Realm+Extensions.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.extensions
2 |
3 | import com.exozet.android.core.realm.RealmDao
4 | import com.exozet.android.core.realm.RealmModelLiveData
5 | import com.exozet.android.core.realm.RealmResultsLiveData
6 | import io.realm.RealmList
7 | import io.realm.RealmModel
8 | import io.realm.RealmObject
9 | import io.realm.RealmResults
10 |
11 | inline fun RealmResults.asLiveData(valueOnActive: Boolean = false) = RealmResultsLiveData(this, valueOnActive)
12 |
13 | inline fun T.asLiveData(valueOnActive: Boolean = false) = RealmModelLiveData(this, valueOnActive)
14 |
15 | inline fun T.detachRealm(): T = realm?.copyFromRealm(this) ?: this
16 |
17 | /**
18 | * https://github.com/vicpinm/Kotlin-Realm-Extensions/blob/master/library-base/src/main/java/com/vicpin/krealmextensions/RealmExtensions.kt#L275
19 | */
20 | inline fun T.update(crossinline block: T.(t: T) -> Unit) {
21 | realm.executeTransaction {
22 | block(this)
23 | }
24 | }
25 |
26 | /**
27 | * Deletes all [T].
28 | */
29 | inline fun RealmDao.deleteAllSync() = realm.executeTransaction { realm ->
30 | realm.where(T::class.java)
31 | .findAll()
32 | .deleteAllFromRealm()
33 | }
34 |
35 | /**
36 | * Inserts or updates all [T].
37 | */
38 | inline fun RealmDao.insertOrUpdateSync(t: T) = realm.executeTransaction { realm -> realm.insertOrUpdate(t) }
39 |
40 | /**
41 | * Inserts or updates all [T].
42 | */
43 | inline fun RealmDao.insertOrUpdateSync(t: List) = realm.executeTransaction { realm -> realm.insertOrUpdate(t) }
44 |
45 | /**
46 | * Deletes all [T].
47 | */
48 | inline fun RealmDao.allSync(): RealmResults = realm.where(T::class.java).findAll()
49 |
50 | inline fun RealmDao.isEmpty() = realm.where(T::class.java).findFirst() == null
51 |
52 | inline fun List.toRealmList(): RealmList? {
53 | val list = RealmList()
54 | list.addAll(this)
55 | return list
56 | }
57 |
--------------------------------------------------------------------------------
/app/src/test/java/com/exozet/android/core/base/BaseTest.kt:
--------------------------------------------------------------------------------
1 | package com.exozet.android.core.base
2 |
3 | import android.app.Activity
4 | import android.app.Application
5 | import androidx.test.core.app.ApplicationProvider
6 | import net.kibotu.ContextHelper
7 | import net.kibotu.logger.Logger
8 | import org.junit.After
9 | import org.junit.AfterClass
10 | import org.junit.Before
11 | import org.junit.BeforeClass
12 | import org.junit.runner.RunWith
13 | import org.robolectric.Robolectric
14 | import org.robolectric.RobolectricTestRunner
15 | import org.robolectric.annotation.Config
16 |
17 |
18 | /**
19 | * Created by [Jan Rabe](https://about.me/janrabe).
20 | */
21 |
22 | @RunWith(RobolectricTestRunner::class)
23 | @Config(application = AppStub::class, sdk = [28], manifest = Config.NONE)
24 | abstract class BaseTest {
25 |
26 | private val application: Application
27 | get() = ApplicationProvider.getApplicationContext()
28 |
29 | private val cacheDir
30 | get() = application.cacheDir
31 |
32 | /**
33 | * Runs just once before all tests
34 | */
35 | @Before
36 | @Throws(Exception::class)
37 | open fun setup() {
38 |
39 | val app: Application = ApplicationProvider.getApplicationContext()
40 | Logger.with(app)
41 | val activity = Robolectric.buildActivity(ActivityStub::class.java).create().start().get()
42 | ContextHelper.setContext(activity)
43 | }
44 |
45 | fun String.stringFromAssets(): String = try {
46 | application.assets.open(this).bufferedReader().use { it.readText() }
47 | } catch (e: Exception) {
48 | Logger.e(e)
49 | ""
50 | }
51 |
52 | /**
53 | * Runs after every test
54 | */
55 | @After
56 | fun breakdown() {
57 | }
58 |
59 | companion object {
60 |
61 | /**
62 | * Runs just once before all tests
63 | */
64 | @BeforeClass
65 | fun beforeEverything() {
66 | }
67 |
68 | /**
69 | * Runs just once after all the tests
70 | */
71 | @AfterClass
72 | fun afterEverything() {
73 | }
74 | }
75 | }
76 |
77 | internal class AppStub : Application()
78 | internal class ActivityStub : Activity()
--------------------------------------------------------------------------------