├── .gitignore ├── .idea ├── dictionaries │ └── gilgoldzweig.xml ├── encodings.xml ├── gradle.xml ├── kannotator.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── goldzweigapps │ │ └── com │ │ └── extentions │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── goldzweigapps │ │ │ └── com │ │ │ └── extentions │ │ │ └── MainActivity.kt │ └── res │ │ ├── drawable │ │ └── ic_launcher_background.xml │ │ ├── layout │ │ └── activity_main.xml │ │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── goldzweigapps │ └── com │ └── extentions │ └── ExampleUnitTest.kt ├── build.gradle ├── core ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── goldzweigapps │ │ │ └── com │ │ │ └── core │ │ │ ├── bundles │ │ │ ├── Bundlify.kt │ │ │ ├── BundlifyDelegate.kt │ │ │ └── BundlifyType.kt │ │ │ ├── collections │ │ │ └── CollectionExtensions.kt │ │ │ ├── colors │ │ │ └── ColorGenerator.kt │ │ │ ├── context │ │ │ └── ContextExtension.kt │ │ │ ├── database │ │ │ └── SQLDatabaseExtensions.kt │ │ │ ├── exceptions │ │ │ ├── InitializeException.kt │ │ │ ├── UnsupportedTypeException.kt │ │ │ └── WrongThreadException.kt │ │ │ ├── notifications │ │ │ ├── Notification.kt │ │ │ └── NotificationManager.kt │ │ │ ├── other │ │ │ └── LazyInitializeObject.kt │ │ │ ├── permissions │ │ │ └── PermissionsExtensions.kt │ │ │ ├── preferences │ │ │ ├── GlobalSharedPreferences.kt │ │ │ └── PreferencesProperty.kt │ │ │ ├── resources │ │ │ └── ResourcesExtensions.kt │ │ │ ├── streams │ │ │ └── StreamsExtensions.kt │ │ │ ├── support │ │ │ ├── GenericRecyclerAdapter.kt │ │ │ └── GenericRecyclerViewAdapterExtensions.kt │ │ │ ├── threads │ │ │ └── ThreadsExtensions.kt │ │ │ └── views │ │ │ └── ViewsExtensions.kt │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── goldzweigapps │ └── com │ └── core │ └── ExampleUnitTest.java ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── jackson ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── goldzweigapps │ │ │ └── com │ │ │ └── jackson │ │ │ └── jacksonExtensions.kt │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── goldzweigapps │ └── com │ └── jackson │ └── JacksonUnitTest.kt ├── projectFilesBackup └── .idea │ └── workspace.xml ├── reactive ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── goldzweigapps │ │ └── com │ │ └── reactive │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── goldzweigapps │ │ │ └── com │ │ │ └── reactive │ │ │ └── ReactiveExtensions.kt │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── goldzweigapps │ └── com │ └── reactive │ └── ExampleUnitTest.java ├── settings.gradle └── timber ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src ├── androidTest └── java │ └── goldzweigapps │ └── com │ └── timber │ └── ExampleInstrumentedTest.java ├── main ├── AndroidManifest.xml ├── java │ └── goldzweigapps │ │ └── com │ │ └── timber │ │ └── Timber.kt └── res │ └── values │ └── strings.xml └── test └── java └── goldzweigapps └── com └── timber └── ExampleUnitTest.java /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | .externalNativeBuild 10 | -------------------------------------------------------------------------------- /.idea/dictionaries/gilgoldzweig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | bundlify 5 | 6 | 7 | -------------------------------------------------------------------------------- /.idea/encodings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/kannotator.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 23 | 33 | 34 | 35 | 36 | 37 | 38 | 40 | 41 | 42 | 43 | 44 | 1.8 45 | 46 | 51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Kotlinify 2 | 3 | ![Release](https://jitpack.io/v/GilGoldzweig/kotlinify.svg) 4 | 5 | 6 | Kotlinify is a suite of extension and classes for easier daily android development in kotlin. 7 | I've created it after my own needs and i decided i should publish it and save time for others. 8 | 9 | The library contanis 4 modules: 10 | - Core extensions. 11 | - Reactive extensions. 12 | - Jackson extensions. 13 | - Custom edition of JakeWharton/timber 14 | ## Installiton 15 | In your app's build.gradle add the following. 16 | ``` 17 | repositories { 18 | maven {url "https://jitpack.io" } 19 | } 20 | 21 | dependencies { 22 | You can add each module seperatly 23 | 24 | //All of the modules 25 | implementation 'com.github.GilGoldzweig:kotlinify:latestVersion' 26 | // Core module 27 | implementation 'com.github.GilGoldzweig.kotlinify:core:latestVersion' 28 | // Reactive module 29 | implementation 'com.github.GilGoldzweig.kotlinify:reactive:latestVersion' 30 | // Jackson module 31 | implementation 'com.github.GilGoldzweig.kotlinify:jackson:latestVersion' 32 | // Timber module 33 | implementation 'com.github.GilGoldzweig.kotlinify:timber:latestVersion' 34 | } 35 | ``` 36 | 37 | # Core 38 | ##### The core module provides the following classes and extensions. 39 | 40 | 41 | ### Bundlify 42 | Let's you create bundles in a simple way and with operators. 43 | ``` 44 | val bundle = bundle { 45 | //put key(String), value(Any) 46 | 47 | //String 48 | put("key", "value") 49 | 50 | //Int 51 | put("key", 1) 52 | 53 | //Long 54 | put("key", 1L) 55 | 56 | //Float 57 | put("key", 1.5131F) 58 | 59 | //parcelable 60 | put("key", Parcelable) //Some parcelable object 61 | 62 | //parcelable array 63 | put("key", Array()) //Some parcelable Array 64 | 65 | //parcelable arrayList 66 | put("key", ArrayList())//Some parcelable ArrayList 67 | 68 | "key" += 5 69 | this += "keyPair" to 6L 70 | 71 | //remove 72 | remove("keyToRemove") 73 | 74 | this - "" 75 | 76 | } 77 | "key" in bundle 78 | 79 | ``` 80 | ### GlobalSharedPreferences 81 | GlobalSharedPrefrences a SharedPreferences object that let you use it anywhere without the direct access to context. The class also contains an easy usage 82 | ``` 83 | First initialize it in your application class 84 | //You don't have to provide a name if nothing is placed it will use the default value 85 | GlobalSharedPreferences.initialize(application, "sharedPreferencesName") 86 | pref { 87 | //put key(String), value(Any) 88 | 89 | //String 90 | put("key", "value") 91 | 92 | //Int 93 | put("key", 1) 94 | 95 | //Long 96 | put("key", 1L) 97 | 98 | //Float 99 | put("key", 1.5131F) 100 | 101 | "key" += 5 102 | this += "keyPair" to 6L 103 | 104 | //remove 105 | remove("keyToRemove") 106 | this - "" 107 | 108 | } //not need to apply inside pref 109 | 110 | "key" in GlobalSharedPreferences // return's Boolean 111 | 112 | (GlobalSharedPreferences += "keyPair" to 6L).apply() 113 | 114 | (GlobalSharedPreferences - "key").apply() 115 | ``` 116 | ### Fragment 117 | An easier way to create a fragment using a dsl extension of fragment 118 | ``` 119 | fragment(layoutRes = R.layout.fragment_test) { 120 | //every field in fragment is accessable from here 121 | arguments = bundle // we use the bundle from before to insert the arguments (optinal, you can use a normal Bundle) 122 | var name: TextView // declaring your views 123 | onViewCreated { view, context, savedInstanceState -> 124 | name = view.findViewById(R.id.text) //accessing the views 125 | } 126 | } //return's a ready to use fragment 127 | ``` 128 | ### Notification 129 | //You can use but it did not get tested so i'm not writing description 130 | //will be added soon 131 | 132 | ### GenericRecyclerAdapter 133 | An abstract extension of RecyclerView.Adapter that reduces the creating time of a RecyclerView. 134 | In additon you receive a lot of extension function to make the adapter work simieler to List so by extending the class you receive a lot of bounses 135 | ``` 136 | The adapter 137 | class CustomRecyclerAdapter(context: Context, // Require context to make it easily accessible 138 | listOfObjects: ArrayList = ArrayList() //Put your list of objects 139 | ): 140 | GenericRecyclerAdapter(context, 141 | ArrayList() 142 | //the list of objects could by empty and it will use the default value 143 | , R.layout.item_recycler_test // the item's layout currently one one view type supported 144 | ) { 145 | 146 | override fun View.onBind(currentElementPosition: Int, 147 | currentElement: String, // the current element with the type provided above in this example a String 148 | holder: GenericViewHolder) { 149 | //because of the extension of View i can just call 150 | // findViewById and that's it you don't need to call holder.view.findViewById 151 | } 152 | } 153 | extension given 154 | 155 | val customRecyclerAdapter = CustomRecyclerAdapter(this, ArrayList()) 156 | 157 | customRecyclerAdapter - "" //removing item and notifying the adapter if you are on uiThread 158 | 159 | customRecyclerAdapter - 2 //removing item and notifying the adapter if you are on uiThread 160 | 161 | customRecyclerAdapter + "" //adding item and notifying the adapter if you are on uiThread 162 | 163 | customRecyclerAdapter.add("",5) 164 | 165 | customRecyclerAdapter[""] //getItem by object 166 | 167 | customRecyclerAdapter[12] //getItem by position 168 | 169 | customRecyclerAdapter - (1..4) //removing position range and notifying the adapter if you are on uiThread 170 | 171 | customRecyclerAdapter - (listOf("", "")) //removing list of objects and notifying the adapter if you are on uiThread 172 | 173 | customRecyclerAdapter.setItem(5, "gg") 174 | 175 | customRecyclerAdapter.clear() 176 | 177 | customRecyclerAdapter.count() 178 | 179 | customRecyclerAdapter.setItems(listOf("", "", "", "", "")) //replace the current list and notifying the adapter if you are on uiThread 180 | 181 | customRecyclerAdapter.isEmpty() 182 | 183 | customRecyclerAdapter.isNotEmpty() 184 | ``` 185 | ### threads 186 | ``` 187 | //single function to run in background 188 | runInBackground { 189 | 190 | } 191 | 192 | //multiple functions to run in background 193 | runInBackground({}, {}, {}) 194 | 195 | //function to run on ui thread 196 | runOnUI { 197 | 198 | } 199 | 200 | //run's a task after 2 secounds on UI thread 201 | runAfter(millis = 2000, thread = RunnableThread.UI) { 202 | 203 | } 204 | 205 | //run's a task after 2 secounds in background 206 | runAfter(millis = 2000, thread = RunnableThread.BACKGROUND) { 207 | 208 | } 209 | 210 | //run's a task after 2 secounds on the current thread 211 | runAfter(millis = 2000, thread = RunnableThread.CURRENT) { 212 | 213 | } 214 | 215 | //run's a task after 2 secounds 216 | runAfter(millis = 2000) { 217 | 218 | } 219 | 220 | isUiThread() //Boolean is the current thread is UI 221 | ``` 222 | ### views 223 | ``` 224 | val group = LinearLayout(this)//LinearLayout as example can be any ViewGroup 225 | 226 | group += TextView(this) 227 | group += TextView(this) 228 | group += TextView(this) 229 | group += TextView(this) 230 | "in" in TextView(this) 231 | group -= TextView(this) 232 | 233 | group[0] 234 | for (view in group.iterator()) { 235 | 236 | } 237 | group.first() 238 | group.last() 239 | group.forEach { } 240 | group.forEachIndexed { i, view -> } 241 | group.forEachRevered { } 242 | group.forEachReveredIndexed { i, view -> } 243 | 244 | group.inflate(R.layout.some_layout) 245 | this.inflate(R.layout.some_layout, false, group) //this = context 246 | 247 | View(this).onClick { 248 | 249 | } 250 | View(this).onLongClick { 251 | 252 | } 253 | 254 | View(this).hide() 255 | View(this).hide() 256 | View(this).show() 257 | View(this).invisible() 258 | View(this).toggleVisibility() 259 | View(this).isVisible() 260 | ``` 261 | ### collections 262 | ``` 263 | val testMap = mapOf(0 to 9, 0 to 9, 0 to 9, 0 to 9, 0 to 9) 264 | val testSet = setOf(0 to 9, 0 to 9, 0 to 9, 0 to 9, 0 to 9) 265 | val testList = listOf(0 to 9, 0 to 9, 0 to 9, 0 to 9, 0 to 9) 266 | val testArrayList = ArrayList() 267 | 268 | testMap.isNullOrEmpty() 269 | testMap.isNotNullOrEmpty() 270 | 271 | testSet.isNullOrEmpty() 272 | testSet.isNotNullOrEmpty() 273 | 274 | testArrayList.isNullOrEmpty() 275 | testArrayList.isNotNullOrEmpty() 276 | testArrayList addIfNotExist 5 277 | testArrayList removeIfExist 5 278 | 279 | 280 | testList.isNullOrEmpty() 281 | testList.isNotNullOrEmpty() 282 | testList / 3 //returns a map of page number and the amount of items given in this case 3 283 | testList.random() //returns a random element from the list 284 | 285 | ``` 286 | ### resourses 287 | Provides two extensions 288 | ``` 289 | 15.toDp() //return's the number as a convertion from px to dp 290 | 15.toPx()//return's the number as a convertion from dp to px 291 | ``` 292 | ### permission 293 | Provides few functions 294 | ``` 295 | isVersionAbove(26) // boolean 296 | isVersionAbove(14) // boolean same as above but with diffrent version 297 | 298 | isMarshmallowOrAbove() //boolean is current version is at least Marshmallow(23) 299 | isLollipopOrAbove() //boolean is current version is at least Lollipop(21) 300 | 301 | Context.isGranted("StringPermission") // boolean checks if the permission is granted or not 302 | 303 | ``` 304 | ### ColorGenerator 305 | A class with 2 lists of colors one normal colors and one material design colors 306 | the class let's you get all the colors or a random color 307 | ``` 308 | ColorGenerator.DEFAULT_COLOR_LIST 309 | ColorGenerator.instance.randomColor 310 | 311 | ColorGenerator.MATERIAL_COLOR_LIST 312 | ColorGenerator.materialInstance.randomColor 313 | ``` 314 | 315 | 316 | # Reactive 317 | ##### The Reactive module provides the following extensions. 318 | Provides two extension functions to most if not all reactive types 319 | ``` 320 | /** 321 | * observe on main thread 322 | * subscribe on new thread 323 | * unsubsidised on error and on complete and removes the need to handle it afterwards 324 | * @usage 325 | * someObservable //or any other reactive type 326 | * .runSafeOnMain() 327 | * .subscribe({}, {]) 328 | */ 329 | fun Observable.runSafeOnMain(): Observable = 330 | observeOn(mainThread) 331 | .subscribeOn(newThread) 332 | .doOnError({ unsubscribeOn(newThread) }) 333 | .doOnComplete { unsubscribeOn(newThread) } 334 | 335 | /** 336 | * observe on io thread 337 | * subscribe on new thread 338 | * unsubsidised on error and on complete and removes the need to handle it afterwards 339 | * @usage 340 | * someObservable //or any other reactive type 341 | * .runSafeOnIO() 342 | * .subscribe({}, {]) 343 | */ 344 | fun Observable.runSafeOnIO(): Observable = 345 | observeOn(ioThread) 346 | .subscribeOn(newThread) 347 | .doOnError({ unsubscribeOn(newThread) }) 348 | .doOnComplete { unsubscribeOn(newThread) } 349 | ``` 350 | 351 | # Jackson 352 | ##### The jackson module provides a few extension functions and an annotation 353 | ``` 354 | val locationObject = LocationObject("Tel-Aviv", arrayOf(5.152155, 1512.5120)) 355 | 356 | val jsonString = "{\"locationText\":\"Tel-Aviv\",\"locationCoordinates\":[5.152155,1512.512]}" 357 | 358 | locationObject.toJson() //{"locationText":"Tel-Aviv","locationCoordinates":[5.152155,1512.512]} 359 | 360 | 361 | locationObject.toPrettyJson() /** { 362 | "locationText": "Tel-Aviv", 363 | "locationCoordinates": [ 364 | 5.152155, 365 | 1512.512 366 | ] 367 | }*/ 368 | 369 | jsonString.fromJson() // A new LocationObject 370 | 371 | LocationObject 372 | 373 | @JsonIgnoreUnknown //A new annotation to make it easier to use jackson instad of @JsonIgnoreProperties(ignoreUnknown = true) 374 | data class LocationObject(val locationText: String, val locationCoordinates: Array) 375 | ``` 376 | 377 | # Timber 378 | ##### The timber module provides the following class. 379 | Adds a possibilty to print any object using timber a very small change but saves a lot of annying toString() 380 | ``` 381 | You can use it like so or just put the object 382 | Timber.d(someObject.toString()) 383 | Timber.d(5.toString()) 384 | Timber.d(someBoolean.toString()) 385 | 386 | //you can just do like so 387 | Timber.d(someObject) 388 | Timber.d(5) 389 | Timber.d(someBoolean) 390 | someObject.d()// no need to call Timber 391 | someOtherObject.e()// no need to call Timber 392 | someOtherObject.i()// no need to call Timber 393 | someOtherObject.wtf()// no need to call Timber 394 | someOtherObject.w()// no need to call Timber 395 | ``` 396 | 397 | License 398 | ---- 399 | 400 | MIT 401 | 402 | 403 | **Free Software, Hell Yeah!** 404 | -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | apply plugin: 'kotlin-android' 4 | 5 | apply plugin: 'kotlin-android-extensions' 6 | 7 | android { 8 | compileSdkVersion 27 9 | buildToolsVersion '27.0.3' 10 | defaultConfig { 11 | applicationId "goldzweigapps.com.extentions" 12 | minSdkVersion 15 13 | targetSdkVersion 27 14 | versionCode 1 15 | versionName "1.0" 16 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 17 | } 18 | buildTypes { 19 | release { 20 | minifyEnabled false 21 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 22 | } 23 | } 24 | } 25 | //repositories { 26 | //// maven { url 'https://jitpack.io' } 27 | // maven { 28 | // url "https://dl.bintray.com/gilgoldzweig/maven" 29 | // } 30 | //} 31 | 32 | dependencies { 33 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 34 | implementation "com.android.support:appcompat-v7:$rootProject.versions.support" 35 | implementation 'com.android.support.constraint:constraint-layout:1.0.2' 36 | testImplementation 'junit:junit:4.12' 37 | // compileProject('timber') 38 | // compile 'com.github.gilgoldzweig.kotlinify:core:1.1' 39 | // compile 'com.github.GilGoldzweig.kotlinify:timber:1.0.1' 40 | // compile 'com.github.GilGoldzweig.kotlinify:reactive:1.0.0' 41 | // compile 'com.github.GilGoldzweig.kotlinify:jackson:1.0.0' 42 | implementation project(':timber') 43 | implementation project(':core') 44 | // compile project(':core') 45 | } 46 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/gilgoldzweig/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /app/src/androidTest/java/goldzweigapps/com/extentions/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package goldzweigapps.com.extentions 2 | 3 | 4 | import org.junit.Assert.assertEquals 5 | import org.junit.Test 6 | import org.junit.runner.RunWith 7 | 8 | /** 9 | * Instrumented test, which will execute on an Android device. 10 | * 11 | * See [testing documentation](http://d.android.com/tools/testing). 12 | */ 13 | @RunWith(AndroidJUnit4::class) 14 | class ExampleInstrumentedTest { 15 | @Test 16 | fun useAppContext() { 17 | // Context of the app under test. 18 | val appContext = InstrumentationRegistry.getTargetContext() 19 | assertEquals("goldzweigapps.com.extentions", appContext.packageName) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/java/goldzweigapps/com/extentions/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package goldzweigapps.com.extentions 2 | 3 | import android.os.Bundle 4 | import android.support.v7.app.AppCompatActivity 5 | import goldzweigapps.com.core.threads.RunnableThread 6 | import goldzweigapps.com.timber.Timber 7 | 8 | class MainActivity : AppCompatActivity() { 9 | 10 | 11 | override fun onCreate(savedInstanceState: Bundle?) { 12 | super.onCreate(savedInstanceState) 13 | setContentView(R.layout.activity_main) 14 | Timber.plant(Timber.DebugTree()) 15 | 16 | 17 | // findViewById<>() 18 | // Timber.plant(Timber.DebugTree()) 19 | // runAfter(2000, RunnableThread.BACKGROUND) { 20 | // "run a function in background".d() 21 | // if (isUiThread()) "Something is not working".e() 22 | // } 23 | // runAfter(3000, RunnableThread.UI) { 24 | // "run a function on ui".d() 25 | // if (isUiThread()) "Something is working".d() 26 | // } 27 | // notificationManager { 28 | // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { 29 | // showSingle(15,notification("channel", 15) { 30 | // title = "testTitle" 31 | // contentText = "contextTextTest" 32 | // smallIconRes = R.mipmap.ic_launcher_round 33 | // intent = PendingIntent.getActivity(this@MainActivity, 34 | // 5, 35 | // Intent(), 36 | // PendingIntent.FLAG_UPDATE_CURRENT) 37 | // if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { 38 | // action { 39 | // title = "customTitle" 40 | // icon = Icon.createWithResource(this@MainActivity, R.mipmap.ic_launcher_round) 41 | // intent = PendingIntent.getActivity(this@MainActivity, 42 | // 5, 43 | // Intent(), 44 | // PendingIntent.FLAG_UPDATE_CURRENT) 45 | // } 46 | // } 47 | // }) 48 | // } 49 | // } 50 | // 51 | // runInBackground { 52 | // if (isUiThread()) "Something is not working".e() 53 | // runOnUiThread { 54 | // if (isUiThread()) "Something is working".d() 55 | // } 56 | // } 57 | 58 | 59 | // locationObject.toJson() //{"locationText":"Tel-Aviv","locationCoordinates":[5.152155,1512.512]} 60 | // locationObject.toPrettyJson() 61 | /**{ 62 | "locationText": "Tel-Aviv", 63 | "locationCoordinates": [ 64 | 5.152155, 65 | 1512.512 66 | ] 67 | }*/ 68 | // jsonString.fromJson() // A new LocationObject 69 | // 70 | // 71 | // //single function 72 | // runInBackground { 73 | // 74 | // } 75 | // 76 | // //multiple functions 77 | // runInBackground({}, {}, {}) 78 | // runOnUI { 79 | // 80 | // } 81 | // runAfter(2000) { 82 | // 83 | // } 84 | // runAfter(millis = 2000, thread = RunnableThread.BACKGROUND) { 85 | // 86 | // } 87 | // runAfter(millis = 2000, thread = RunnableThread.CURRENT) { 88 | // 89 | // } 90 | // runAfter(millis = 2000) { 91 | // 92 | // } 93 | // 94 | // isUiThread() 95 | // GlobalSharedPreferences.initialize(application, "") 96 | // 97 | // 98 | // pref { 99 | // //put key(String), value(Any) 100 | // //String 101 | // put("key", "value") 102 | // //Int 103 | // put("key", 1) 104 | // //Long 105 | // put("key", 1L) 106 | // //Float 107 | // put("key", 1.5131F) 108 | // 109 | // "key" += 5 110 | // this += "keyPair" to 6L 111 | // 112 | // //remove 113 | // remove("keyToRemove") 114 | // this - "" 115 | // 116 | // } 117 | // "key" in GlobalSharedPreferences 118 | // (GlobalSharedPreferences - "").apply() 119 | // 120 | // val bundle = bundle { 121 | // //put key(String), value(Any) 122 | // //String 123 | // put("key", "value") 124 | // //Int 125 | // put("key", 1) 126 | // //Long 127 | // put("key", 1L) 128 | // //Float 129 | // put("key", 1.5131F) 130 | // //parcelable 131 | // put("key", Parcelable) //Some parcelable object 132 | // //parcelable array 133 | // put("key", Array()) //Some parcelable Array 134 | // //parcelable arrayList 135 | // put("key", ArrayList())//Some parcelable ArrayList 136 | // 137 | // "key" += 5 138 | // this += "keyPair" to 6L 139 | // 140 | // //remove 141 | // remove("keyToRemove") 142 | // this - "" 143 | // 144 | // } 145 | // "key" in bundle 146 | // 147 | // val re = Recy(this) 148 | // re + UserKT() 149 | // re - UserKT() 150 | // for (count in 0..100) { 151 | // 152 | // } 153 | 154 | // fragment(layoutRes = R.layout.activity_main) { 155 | // 156 | // arguments = bundle 157 | // var name: TextView 158 | // onViewCreated { view, context, savedInstanceState -> 159 | // name = view.findViewById(R.id.text) 160 | // 161 | // } 162 | // } 163 | // 5.toDp() 164 | // 165 | // val group = LinearLayout(this) 166 | // group += TextView(this) 167 | // group += TextView(this) 168 | // group += TextView(this) 169 | // group += TextView(this) 170 | // "in" in TextView(this) 171 | // group -= TextView(this) 172 | // group[0] 173 | // for (view in group.iterator()) { 174 | // 175 | // } 176 | // group.first() 177 | // group.last() 178 | // group.forEach { } 179 | // group.forEachIndexed { i, view -> } 180 | // group.forEachRevered { } 181 | // group.forEachReveredIndexed { i, view -> } 182 | // 183 | // group.inflate(R.layout.some_layout) 184 | // this.inflate(R.layout.some_layout, false, group) 185 | // 186 | // View(this).onClick { 187 | // 188 | // } 189 | // View(this).onLongClick { 190 | // 191 | // } 192 | // View(this).hide() 193 | // View(this).hide() 194 | // View(this).show() 195 | // View(this).invisible() 196 | // View(this).toggleVisibility() 197 | // View(this).isVisible() 198 | // 199 | // 200 | // 201 | // isVersionAbove(23) 202 | // 203 | // val customRecyclerAdapter = CustomRecyclerAdapter(this, ArrayList()) 204 | // customRecyclerAdapter - "" //removing item and notifying the adapter if you are on uiThread 205 | // customRecyclerAdapter - 2 //removing item and notifying the adapter if you are on uiThread 206 | // customRecyclerAdapter + "" //adding item and notifying the adapter if you are on uiThread 207 | // customRecyclerAdapter.add("",5) 208 | // customRecyclerAdapter[""] //getItem by object 209 | // customRecyclerAdapter[12] //getItem by position 210 | // customRecyclerAdapter - (1..4) //removing position range and notifying the adapter if you are on uiThread 211 | // customRecyclerAdapter - (listOf("", "")) //removing list of objects and notifying the adapter if you are on uiThread 212 | // customRecyclerAdapter.setItem(5, "gg") 213 | // customRecyclerAdapter.clear() 214 | // customRecyclerAdapter.count() 215 | // customRecyclerAdapter.setItems(listOf("", "", "", "", "")) //replace the current list and notifying the adapter if you are on uiThread 216 | // customRecyclerAdapter.isEmpty() 217 | // customRecyclerAdapter.isNotEmpty() 218 | // 219 | // ColorGenerator.DEFAULT_COLOR_LIST 220 | // ColorGenerator.instance.randomColor 221 | // 222 | // ColorGenerator.MATERIAL_COLOR_LIST 223 | // ColorGenerator.materialInstance.randomColor 224 | // 225 | // val testMap = mapOf(0 to 9, 0 to 9, 0 to 9, 0 to 9, 0 to 9) 226 | // val testSet = setOf(0 to 9, 0 to 9, 0 to 9, 0 to 9, 0 to 9) 227 | // val testList = listOf(0 to 9, 0 to 9, 0 to 9, 0 to 9, 0 to 9) 228 | // val testArrayList = ArrayList() 229 | // 230 | // testMap.isNullOrEmpty() 231 | // testMap.isNotNullOrEmpty() 232 | // 233 | // testSet.isNullOrEmpty() 234 | // testSet.isNotNullOrEmpty() 235 | // 236 | // testArrayList.isNullOrEmpty() 237 | // testArrayList.isNotNullOrEmpty() 238 | // testArrayList addIfNotExist 5 239 | // testArrayList removeIfExist 5 240 | // 241 | // 242 | // testList.isNullOrEmpty() 243 | // testList.isNotNullOrEmpty() 244 | // testList / 3 //returns a map of page number and the amount of items given in this case 3 245 | // testList.random() //returns a random element from the list 246 | // 247 | 248 | 249 | } 250 | } 251 | class NewRunnableThread: RunnableThread { 252 | override fun run(vararg functions: () -> Unit) { 253 | for (func in functions) { 254 | Thread({ func.invoke() }).start() 255 | } 256 | } 257 | } -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 12 | 17 | 22 | 27 | 32 | 37 | 42 | 47 | 52 | 57 | 62 | 67 | 72 | 77 | 82 | 87 | 92 | 97 | 102 | 107 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gilgoldzweig/Kotlinify/5b45da2be009d00cc29beabe4794b59b681aacf5/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gilgoldzweig/Kotlinify/5b45da2be009d00cc29beabe4794b59b681aacf5/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gilgoldzweig/Kotlinify/5b45da2be009d00cc29beabe4794b59b681aacf5/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gilgoldzweig/Kotlinify/5b45da2be009d00cc29beabe4794b59b681aacf5/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gilgoldzweig/Kotlinify/5b45da2be009d00cc29beabe4794b59b681aacf5/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gilgoldzweig/Kotlinify/5b45da2be009d00cc29beabe4794b59b681aacf5/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gilgoldzweig/Kotlinify/5b45da2be009d00cc29beabe4794b59b681aacf5/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gilgoldzweig/Kotlinify/5b45da2be009d00cc29beabe4794b59b681aacf5/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gilgoldzweig/Kotlinify/5b45da2be009d00cc29beabe4794b59b681aacf5/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gilgoldzweig/Kotlinify/5b45da2be009d00cc29beabe4794b59b681aacf5/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gilgoldzweig/Kotlinify/5b45da2be009d00cc29beabe4794b59b681aacf5/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gilgoldzweig/Kotlinify/5b45da2be009d00cc29beabe4794b59b681aacf5/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gilgoldzweig/Kotlinify/5b45da2be009d00cc29beabe4794b59b681aacf5/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gilgoldzweig/Kotlinify/5b45da2be009d00cc29beabe4794b59b681aacf5/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gilgoldzweig/Kotlinify/5b45da2be009d00cc29beabe4794b59b681aacf5/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Extentions 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/test/java/goldzweigapps/com/extentions/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package goldzweigapps.com.extentions 2 | 3 | import org.junit.Test 4 | 5 | import org.junit.Assert.* 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * See [testing documentation](http://d.android.com/tools/testing). 11 | */ 12 | class ExampleUnitTest { 13 | @Test 14 | fun addition_isCorrect() { 15 | assertEquals(4, 2 + 2) 16 | 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext.kotlin_version = '1.2.30' 5 | repositories { 6 | google() 7 | jcenter() 8 | } 9 | dependencies { 10 | classpath 'com.android.tools.build:gradle:3.1.0' 11 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 12 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.5' 13 | classpath 'com.novoda:bintray-release:0.8.0' 14 | // classpath 'com.novoda:bintray-release:0.5.0' 15 | // NOTE: Do not place your application dependencies here; they belong 16 | // in the individual module build.gradle files 17 | } 18 | } 19 | 20 | 21 | allprojects { 22 | repositories { 23 | google() 24 | jcenter() 25 | // maven { url 'https://dl.bintray.com/kotlin/kotlin-eap-1.2' } 26 | mavenCentral() 27 | } 28 | tasks.withType(Javadoc).all { 29 | enabled = false 30 | } 31 | } 32 | 33 | task clean(type: Delete) { 34 | delete rootProject.buildDir 35 | } 36 | //tasks.withType(Javadoc).all { 37 | // enabled = false 38 | //} 39 | ext { 40 | versions = [ 41 | support : '27.1.1', 42 | mockito : "2.8.9", 43 | junit : "4.12" 44 | ] 45 | userOrg = 'gilgoldzweig' 46 | publishVersion = '1.3.1' 47 | groupId = 'com.github.gilgoldzweig.kotlinify' 48 | website = 'https://github.com/gilgoldzweig/Kotlinify' 49 | } 50 | -------------------------------------------------------------------------------- /core/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /core/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'com.github.dcendents.android-maven' 4 | apply plugin: 'com.novoda.bintray-release' 5 | 6 | group = 'com.github.gilgoldzweig' 7 | 8 | publish { 9 | userOrg = "$rootProject.userOrg" 10 | groupId = "$rootProject.groupId" 11 | publishVersion = "$rootProject.publishVersion" 12 | website = "$rootProject.website" 13 | artifactId = 'core' 14 | desc = 'The core module provides the following classes and extensions.' 15 | } 16 | 17 | android { 18 | compileSdkVersion 27 19 | buildToolsVersion "27.0.3" 20 | defaultConfig { 21 | minSdkVersion 14 22 | targetSdkVersion 27 23 | versionCode 1 24 | versionName "1.0" 25 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 26 | 27 | } 28 | buildTypes { 29 | release { 30 | minifyEnabled false 31 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 32 | } 33 | } 34 | } 35 | dependencies { 36 | implementation fileTree(dir: 'libs', include: ['*.jar']) 37 | 38 | testImplementation 'junit:junit:4.12' 39 | androidTestImplementation 'com.android.support.test:runner:1.0.1' 40 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.1' 41 | 42 | implementation "com.android.support:appcompat-v7:$rootProject.versions.support" 43 | implementation "com.android.support:design:$rootProject.versions.support" 44 | implementation "com.android.support:recyclerview-v7:$rootProject.versions.support" 45 | 46 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" 47 | implementation "org.jetbrains.kotlin:kotlin-reflect:1.2.30" 48 | } -------------------------------------------------------------------------------- /core/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/gilgoldzweig/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /core/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /core/src/main/java/goldzweigapps/com/core/bundles/Bundlify.kt: -------------------------------------------------------------------------------- 1 | package goldzweigapps.com.core.bundles 2 | 3 | import android.os.Bundle 4 | import android.os.Parcelable 5 | import kotlin.reflect.KClass 6 | 7 | /** 8 | * Created by gilgoldzweig on 12/09/2017. 9 | */ 10 | @Suppress("unused") 11 | class Bundlify { 12 | var bundle = Bundle() 13 | 14 | //region get 15 | fun getAll() = bundle 16 | 17 | fun String.getStringArray(): Array = 18 | bundle.getStringArray(this) 19 | 20 | fun String.getStringArrayList(): ArrayList = 21 | bundle.getStringArrayList(this) 22 | 23 | fun String.getInt() = bundle.getInt(this) 24 | 25 | fun String.getLong() = bundle.getLong(this) 26 | 27 | fun String.getFloat() = bundle.getFloat(this) 28 | 29 | fun String.getBoolean() = bundle.getBoolean(this) 30 | 31 | fun String.getString(): String = bundle.getString(this) 32 | 33 | fun String.getParcelable(): T = bundle.getParcelable(this) 34 | fun String.getParcelableArray() = bundle.getParcelableArray(this) as Array 35 | fun String.getParcelableArrayList(): ArrayList = 36 | bundle.getParcelableArrayList(this) 37 | 38 | fun get(type: KClass, key: String) = 39 | when (type) { 40 | String::class -> 41 | key.getString() 42 | Int::class -> 43 | key.getInt() 44 | Long::class -> 45 | key.getLong() 46 | Boolean::class -> 47 | key.getBoolean() 48 | Float::class -> 49 | key.getFloat() 50 | Parcelable::class -> 51 | key.getParcelable() 52 | Array::class -> 53 | key.getParcelableArray() 54 | Array::class -> 55 | key.getStringArray() 56 | else -> throw ClassCastException("Bundlify only support bundle types") 57 | } as T 58 | 59 | //endregion get 60 | 61 | //region contains 62 | operator fun contains(key: String) = bundle.containsKey(key) 63 | //endregion contains 64 | 65 | //region put 66 | fun put(key: String, value: String): Bundlify { 67 | bundle.putString(key, value) 68 | return this 69 | } 70 | 71 | fun put(key: String, value: Int): Bundlify { 72 | bundle.putInt(key, value) 73 | return this 74 | } 75 | 76 | fun put(key: String, value: Long): Bundlify { 77 | bundle.putLong(key, value) 78 | return this 79 | } 80 | 81 | fun put(key: String, value: Boolean): Bundlify { 82 | bundle.putBoolean(key, value) 83 | return this 84 | } 85 | 86 | fun put(key: String, value: Float): Bundlify { 87 | bundle.putFloat(key, value) 88 | return this 89 | } 90 | 91 | fun put(key: String, value: Parcelable): Bundlify { 92 | bundle.putParcelable(key, value) 93 | return this 94 | } 95 | 96 | fun put(key: String, value: Array): Bundlify { 97 | bundle.putParcelableArray(key, value) 98 | return this 99 | } 100 | 101 | fun put(key: String, value: ArrayList): Bundlify { 102 | bundle.putParcelableArrayList(key, value) 103 | return this 104 | } 105 | 106 | fun put(bundlifyType: BundlifyType): Bundlify { 107 | put(bundlifyType.key, bundlifyType.value) 108 | return this 109 | } 110 | 111 | fun put(key: String, value: Any) { 112 | when (value) { 113 | is String -> 114 | put(key, value) 115 | is Int -> 116 | put(key, value) 117 | is Long -> 118 | put(key, value) 119 | is Boolean -> 120 | put(key, value) 121 | is Float -> 122 | put(key, value) 123 | is Parcelable -> 124 | put(key, value) 125 | is Array<*> -> 126 | put(key, value as Array) 127 | is ArrayList<*> -> 128 | put(key, value as ArrayList) 129 | } 130 | } 131 | private fun putUnit(key: String, value: Any) { 132 | put(key, value) 133 | return Unit 134 | } 135 | 136 | operator fun String.plusAssign(value: Any) = putUnit(this, value) 137 | 138 | operator fun plusAssign(keyValuePair: Pair) = 139 | putUnit(keyValuePair.first, keyValuePair.second) 140 | 141 | operator fun plus(keyValuePair: Pair) = plusAssign(keyValuePair) 142 | 143 | //endregion put 144 | 145 | //region remove 146 | fun remove(key: String): Bundlify { 147 | bundle.remove(key) 148 | return this 149 | } 150 | 151 | operator fun minus(key: String) = remove(key) 152 | //endregion remove 153 | } 154 | 155 | inline fun bundle(bundle: Bundlify.() -> Unit) = Bundlify().apply(bundle).bundle 156 | operator fun Bundle.contains(key: String) = this.containsKey(key) 157 | -------------------------------------------------------------------------------- /core/src/main/java/goldzweigapps/com/core/bundles/BundlifyDelegate.kt: -------------------------------------------------------------------------------- 1 | package goldzweigapps.com.core.bundles 2 | 3 | import kotlin.properties.ReadWriteProperty 4 | import kotlin.reflect.KProperty 5 | 6 | /** 7 | * Created by gilgoldzweig on 16/10/2017. 8 | */ 9 | class BundlifyDelegate(val bundlify: Bundlify): ReadWriteProperty { 10 | 11 | override fun getValue(thisRef: Any, property: KProperty<*>) = 12 | bundlify.get(thisRef::class, property.name) as T 13 | 14 | override fun setValue(thisRef: Any, property: KProperty<*>, value: T) = 15 | bundlify.put(property.name, value) 16 | } -------------------------------------------------------------------------------- /core/src/main/java/goldzweigapps/com/core/bundles/BundlifyType.kt: -------------------------------------------------------------------------------- 1 | package goldzweigapps.com.core.bundles 2 | 3 | /** 4 | * Created by gilgoldzweig on 05/11/2017. 5 | */ 6 | interface BundlifyType { 7 | var key: String 8 | var value: E 9 | } -------------------------------------------------------------------------------- /core/src/main/java/goldzweigapps/com/core/collections/CollectionExtensions.kt: -------------------------------------------------------------------------------- 1 | package goldzweigapps.com.core.collections 2 | 3 | import java.util.* 4 | import kotlin.collections.HashMap 5 | 6 | /** 7 | * Created by gilgoldzweig on 04/09/2017. 8 | */ 9 | 10 | private val random = Random() 11 | 12 | //region collections 13 | fun Collection?.isNullOrEmpty(): Boolean = this == null || this.isEmpty() 14 | fun Collection?.isNotNullOrEmpty() = !isNullOrEmpty() 15 | //endregion collections 16 | 17 | //region maps 18 | fun Map?.isNullOrEmpty() = this == null || this.isEmpty() 19 | fun Map?.isNotNullOrEmpty() = !isNullOrEmpty() 20 | //endregion maps 21 | 22 | //region sets 23 | fun Set?.isNullOrEmpty() = this == null || this.isEmpty() 24 | fun Set?.isNotNullOrEmpty() = !isNullOrEmpty() 25 | //endregion sets 26 | 27 | //region lists 28 | fun List?.isNullOrEmpty() = this == null || this.isEmpty() 29 | 30 | fun List?.isNotNullOrEmpty(): Boolean = !isNullOrEmpty() 31 | 32 | fun List.random() = this[random.nextInt(size)] 33 | 34 | operator fun List.div(amount: Int): Map> { 35 | val map = HashMap>() 36 | var startIndexOfCattedList = 0 37 | var page = 0 38 | for (indexOfList in 0 until this.size) { 39 | if ((indexOfList + 1) % amount == 0) { 40 | if (startIndexOfCattedList == 0) { 41 | map.put(page++, subList(startIndexOfCattedList, indexOfList + 1)) 42 | } else { 43 | map.put(page++, subList(startIndexOfCattedList, indexOfList)) 44 | } 45 | startIndexOfCattedList = indexOfList 46 | } 47 | } 48 | return map 49 | } 50 | 51 | operator fun List.get(e: E) = indexOf(e) 52 | //endregion lists 53 | 54 | //region array lists 55 | operator fun ArrayList.plusAssign(obj: E) = add(size, obj) 56 | infix fun ArrayList.addIfNotExist(obj: E) = if (!contains(obj)) add(obj) else false 57 | infix fun ArrayList.removeIfExist(obj: E) = if (contains(obj)) remove(obj) else false 58 | //endregion array lists 59 | 60 | //region Iterable 61 | fun E.asIterable(hasNext: E.() -> Boolean, next: E.() -> T) = 62 | object : Iterable { 63 | override fun iterator(): Iterator { 64 | return object : Iterator { 65 | override fun hasNext() = hasNext(this@asIterable) 66 | override fun next() = next(this@asIterable) 67 | } 68 | } 69 | } 70 | 71 | fun E.asIterable(hasNext: Boolean, next: (it: E) -> T) = 72 | object : Iterable { 73 | override fun iterator(): Iterator { 74 | return object : Iterator { 75 | override fun hasNext() = hasNext 76 | override fun next() = next(this@asIterable) 77 | } 78 | } 79 | } 80 | 81 | fun E.asIterableIndexed(hasNext: E.(index: Int) -> Boolean, 82 | next: E.(index: Int) -> T, 83 | changeOnNext: (index: Int) -> Int): Iterable { 84 | var index = 0 85 | return object : Iterable { 86 | override fun iterator(): Iterator { 87 | return object : Iterator { 88 | override fun hasNext() = this@asIterableIndexed.hasNext(index) 89 | override fun next(): T { 90 | val element = this@asIterableIndexed.next(index) 91 | index = changeOnNext(index) 92 | return element 93 | } 94 | } 95 | } 96 | } 97 | } 98 | 99 | fun E.asIterableIndexed(hasNext: Boolean, 100 | next: E.(index: Int) -> T, 101 | changeOnNext: (index: Int) -> Int) = 102 | asIterableIndexed({ hasNext }, next, changeOnNext) 103 | 104 | operator fun Iterable.get(e: E) = firstOrNull { it == e } 105 | 106 | operator fun Iterable.get(position: Int) = 107 | filterIndexed { index, _ -> index == position}.firstOrNull() 108 | 109 | //endregion Iterable -------------------------------------------------------------------------------- /core/src/main/java/goldzweigapps/com/core/colors/ColorGenerator.kt: -------------------------------------------------------------------------------- 1 | package goldzweigapps.com.core.colors 2 | 3 | import java.util.* 4 | 5 | class ColorGenerator private constructor(private val mColors: List) { 6 | private val mRandom: Random = Random(System.currentTimeMillis()) 7 | 8 | val randomColor: Int 9 | get() = mColors[mRandom.nextInt(mColors.size)] 10 | 11 | @Suppress("unused") 12 | fun getColor(key: Any) = 13 | mColors[Math.abs(key.hashCode()) % mColors.size] 14 | 15 | 16 | companion object { 17 | 18 | private var DEFAULT: ColorGenerator 19 | 20 | private var MATERIAL: ColorGenerator 21 | 22 | var MATERIAL_COLOR_LIST: List = listOf( 23 | 0xffe57373.toInt(), 24 | 0xfff06292.toInt(), 25 | 0xffba68c8.toInt(), 26 | 0xff9575cd.toInt(), 27 | 0xff7986cb.toInt(), 28 | 0xff64b5f6.toInt(), 29 | 0xff4fc3f7.toInt(), 30 | 0xff4dd0e1.toInt(), 31 | 0xff4db6ac.toInt(), 32 | 0xff81c784.toInt(), 33 | 0xffaed581.toInt(), 34 | 0xffff8a65.toInt(), 35 | 0xffd4e157.toInt(), 36 | 0xffffd54f.toInt(), 37 | 0xffffb74d.toInt(), 38 | 0xffa1887f.toInt(), 39 | 0xff90a4ae.toInt() 40 | ) 41 | var DEFAULT_COLOR_LIST: List = listOf( 42 | 0xfff16364.toInt(), 43 | 0xfff58559.toInt(), 44 | 0xfff9a43e.toInt(), 45 | 0xffe4c62e.toInt(), 46 | 0xff67bf74.toInt(), 47 | 0xff59a2be.toInt(), 48 | 0xff2093cd.toInt(), 49 | 0xffad62a7.toInt(), 50 | 0xff805781.toInt()) 51 | 52 | init { 53 | DEFAULT = create(DEFAULT_COLOR_LIST) 54 | MATERIAL = create(MATERIAL_COLOR_LIST) 55 | } 56 | 57 | fun create(colorList: List) = ColorGenerator(colorList) 58 | val materialInstance by lazy { MATERIAL } 59 | val instance by lazy { DEFAULT } 60 | } 61 | } -------------------------------------------------------------------------------- /core/src/main/java/goldzweigapps/com/core/context/ContextExtension.kt: -------------------------------------------------------------------------------- 1 | package goldzweigapps.com.core.context 2 | 3 | /** 4 | * Created by pablisco/ContextExtensions.kt 5 | * Found in medium article which contains good code and a lot of added value so thank him 6 | * His GitHub gist: https://gist.github.com/pablisco/64775eba5afa982f4cfb2362aa7bd9b4 7 | * The Medium article: https://medium.com/@pablisco/fluent-intents-8006ec63130 8 | */ 9 | import android.app.Activity 10 | import android.app.Service 11 | import android.content.ComponentName 12 | import android.content.Context 13 | import android.content.Intent 14 | import android.content.Intent.EXTRA_SUBJECT 15 | import android.content.Intent.EXTRA_TEXT 16 | import android.net.Uri 17 | import kotlin.properties.ReadWriteProperty 18 | import kotlin.reflect.KClass 19 | import kotlin.reflect.KProperty 20 | 21 | fun Context.startActivity(f: Intent.() -> Unit): Unit = 22 | Intent().apply(f).run(this::startActivity) 23 | 24 | inline fun Context.start(noinline intent: Intent.() -> Unit = {}) = 25 | startActivity { 26 | component = componentFor(T::class.java) 27 | intent(this) 28 | } 29 | 30 | fun Context.start(action: String, func: Intent.() -> Unit = {}) = 31 | startActivity { 32 | this.action = action 33 | func(this) 34 | } 35 | 36 | fun Context.chooseActivity(title: String = "", f: Intent.() -> Unit) = 37 | startActivity(Intent.createChooser(Intent().apply(f), title)) 38 | 39 | fun Activity.startForResult(requestCode: Int, f: Intent.() -> Unit): Unit = 40 | Intent().apply(f).run { startActivityForResult(this, requestCode) } 41 | 42 | 43 | fun Context.startService(f: Intent.() -> Unit): ComponentName = 44 | Intent().apply(f).run(this::startService) 45 | 46 | inline fun Context.launch(noinline f: Intent.() -> Unit = {}) = 47 | startService(T::class.java, f) 48 | 49 | fun Context.startService(type: Class, f: Intent.() -> Unit): ComponentName = 50 | Intent().apply(f).run(this::startService) 51 | 52 | 53 | fun Context.send(f: Intent.() -> Unit) = 54 | Intent().apply(f).run(this::sendBroadcast) 55 | 56 | fun Context.send(action: String, f: Intent.() -> Unit = {}) = 57 | Intent(action).apply(f).run(this::sendBroadcast) 58 | 59 | fun Context.send(action: String, permission: String, f: Intent.() -> Unit = {}) = 60 | Intent(action).apply(f).run { sendBroadcast(this, permission) } 61 | 62 | 63 | fun Context.componentFor(targetType: KClass<*>): ComponentName = 64 | componentFor(targetType.java) 65 | 66 | fun Context.componentFor(targetType: Class<*>) = ComponentName(this, targetType) 67 | 68 | inline var Intent.url: String 69 | get() = dataString 70 | set(value) { 71 | data = Uri.parse(value) 72 | } 73 | 74 | inline var Intent.subject 75 | get() = getStringExtra(EXTRA_SUBJECT) 76 | set(value) { 77 | putExtra(EXTRA_SUBJECT, value) 78 | } 79 | 80 | var Intent.text by ExtraProperty.string(EXTRA_TEXT) 81 | 82 | sealed class ExtraProperty : ReadWriteProperty { 83 | 84 | companion object { 85 | fun string(name: String) = StringProperty(name) 86 | fun int(name: String) = IntProperty(name) 87 | } 88 | 89 | class StringProperty internal constructor(private val name: String) : ExtraProperty() { 90 | override fun getValue(thisRef: Intent, property: KProperty<*>): String = 91 | thisRef.getStringExtra(name) 92 | 93 | override fun setValue(thisRef: Intent, property: KProperty<*>, value: String) { 94 | thisRef.putExtra(name, value) 95 | } 96 | } 97 | 98 | class IntProperty internal constructor(private val name: String) : ExtraProperty() { 99 | override fun getValue(thisRef: Intent, property: KProperty<*>) = 100 | thisRef.getIntExtra(name, -1) 101 | 102 | override fun setValue(thisRef: Intent, property: KProperty<*>, value: Int) { 103 | thisRef.putExtra(name, value) 104 | } 105 | } 106 | 107 | } -------------------------------------------------------------------------------- /core/src/main/java/goldzweigapps/com/core/database/SQLDatabaseExtensions.kt: -------------------------------------------------------------------------------- 1 | package goldzweigapps.com.core.database 2 | 3 | import android.database.sqlite.SQLiteDatabase 4 | 5 | /** 6 | * Created by gilgoldzweig on 08/09/2017. 7 | */ 8 | inline fun SQLiteDatabase.inTransaction(func: SQLiteDatabase.() -> Unit) { 9 | beginTransaction() 10 | try { 11 | func() 12 | setTransactionSuccessful() 13 | } finally { 14 | endTransaction() 15 | } 16 | 17 | } -------------------------------------------------------------------------------- /core/src/main/java/goldzweigapps/com/core/exceptions/InitializeException.kt: -------------------------------------------------------------------------------- 1 | package goldzweigapps.com.core.exceptions 2 | 3 | /** 4 | * Created by gilgoldzweig on 17/09/2017. 5 | * custom exception to handle cases where user did not initialized the wanted class 6 | * @param className the name of the class in order to print the right class that caused the exception 7 | * @param initializeFunctionName the name of the initialize function in order to explain to user how to initialize 8 | */ 9 | class InitializeException(className: String, 10 | initializeFunctionName: String 11 | ) : RuntimeException("$className must be initialize,\ncall $initializeFunctionName in order to initialize") -------------------------------------------------------------------------------- /core/src/main/java/goldzweigapps/com/core/exceptions/UnsupportedTypeException.kt: -------------------------------------------------------------------------------- 1 | package goldzweigapps.com.core.exceptions 2 | 3 | class UnsupportedTypeException(classType: String) : 4 | RuntimeException("SharedPreferences does not support $classType") -------------------------------------------------------------------------------- /core/src/main/java/goldzweigapps/com/core/exceptions/WrongThreadException.kt: -------------------------------------------------------------------------------- 1 | package goldzweigapps.com.core.exceptions 2 | 3 | /** 4 | * Created by gilgoldzweig on 22/01/2018. 5 | */ 6 | 7 | open class WrongThreadException(override val message: String = ""): Exception() -------------------------------------------------------------------------------- /core/src/main/java/goldzweigapps/com/core/notifications/Notification.kt: -------------------------------------------------------------------------------- 1 | package goldzweigapps.com.core.notifications 2 | 3 | import android.app.NotificationChannel 4 | import android.app.NotificationManager 5 | import android.app.PendingIntent 6 | import android.app.RemoteInput 7 | import android.content.Context 8 | import android.graphics.Bitmap 9 | import android.graphics.drawable.Icon 10 | import android.os.Build 11 | import android.os.Bundle 12 | import android.support.annotation.DrawableRes 13 | import android.support.annotation.RequiresApi 14 | 15 | 16 | /** 17 | * Created by gilgoldzweig on 04/09/2017. 18 | */ 19 | class Notification(context: Context, channelId: String) : 20 | android.app.Notification.Builder(context) { 21 | var title: String = "" 22 | set(value) { 23 | setContentTitle(value) 24 | } 25 | 26 | var contentText: String = "" 27 | set(value) { 28 | setContentText(value) 29 | } 30 | 31 | var smallIconRes: Int? = null 32 | @DrawableRes 33 | set(value) { 34 | if (value != null) setSmallIcon(value) 35 | } 36 | 37 | var largeIcon: Bitmap? = null 38 | set(value) { 39 | if (value != null) setLargeIcon(value) 40 | } 41 | 42 | var autoCancel: Boolean = true 43 | set(value) { 44 | setAutoCancel(value) 45 | } 46 | var intent: PendingIntent? = null 47 | set(value) { 48 | if (value != null) setContentIntent(value) 49 | } 50 | 51 | val actions = ArrayList() 52 | 53 | 54 | 55 | @RequiresApi(Build.VERSION_CODES.O) 56 | fun channel(id: String = NotificationChannel.DEFAULT_CHANNEL_ID, 57 | name: String = "default channel name", 58 | importance: Int = NotificationManager.IMPORTANCE_DEFAULT, 59 | channel: NotificationChannel.() -> Unit): NotificationChannel { 60 | val notificationChannel = NotificationChannel(id, name, importance) 61 | .apply(channel) 62 | setChannelId(id) 63 | return notificationChannel 64 | } 65 | 66 | 67 | @RequiresApi(Build.VERSION_CODES.N) 68 | inline fun action(init: Action.() -> Unit): android.app.Notification.Action { 69 | val action = Action().apply(init) 70 | val actionBuilder = with(action) { 71 | android.app.Notification.Action.Builder(icon, title, intent) 72 | .setAllowGeneratedReplies(allowGeneratedReplies) 73 | .addRemoteInput(remoteInput) 74 | } 75 | for (extra in action.extras) actionBuilder.addExtras(extra) 76 | val notificationAction = actionBuilder.build() 77 | actions + notificationAction 78 | setActions(*actions.toTypedArray()) 79 | return notificationAction 80 | } 81 | } 82 | 83 | @RequiresApi(Build.VERSION_CODES.JELLY_BEAN) 84 | inline fun Context.notification(channelId: String, notificationId: Int, 85 | notification: Notification.() -> Unit): android.app.Notification = 86 | Notification(this, channelId) 87 | .apply(notification) 88 | .build() 89 | 90 | @RequiresApi(Build.VERSION_CODES.JELLY_BEAN) 91 | inline fun goldzweigapps.com.core.notifications.NotificationManager.notification(channelId: String, notificationId: Int, 92 | notificationFunc: Notification.() -> Unit): Notification { 93 | val notification = Notification(context, channelId) 94 | .apply(notificationFunc) 95 | notifications + notificationId to notification 96 | return notification 97 | } 98 | 99 | 100 | 101 | 102 | 103 | class Action(var title: CharSequence = "", 104 | var icon: Icon? = null, 105 | var intent: PendingIntent? = null, 106 | var allowGeneratedReplies: Boolean = false, 107 | var remoteInput: RemoteInput? = null, 108 | var extras: Array = emptyArray()) 109 | 110 | 111 | -------------------------------------------------------------------------------- /core/src/main/java/goldzweigapps/com/core/notifications/NotificationManager.kt: -------------------------------------------------------------------------------- 1 | package goldzweigapps.com.core.notifications 2 | 3 | import android.app.NotificationChannel 4 | import android.app.NotificationChannelGroup 5 | import android.content.Context 6 | import android.os.Build 7 | import android.support.annotation.RequiresApi 8 | import android.app.NotificationManager as AppNotificationMangaer 9 | 10 | /** 11 | * Created by gilgoldzweig on 13/09/2017. 12 | */ 13 | class NotificationManager(val context: Context) { 14 | val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) 15 | as AppNotificationMangaer 16 | val notificationChannels = ArrayList() 17 | val notificationChannelGroups = ArrayList() 18 | val notifications = ArrayList>() 19 | 20 | 21 | @RequiresApi(Build.VERSION_CODES.O) 22 | fun channel(id: String = NotificationChannel.DEFAULT_CHANNEL_ID, 23 | name: String = "default channel name", 24 | importance: Int = AppNotificationMangaer.IMPORTANCE_DEFAULT, 25 | channel: NotificationChannel.() -> Unit): NotificationChannel { 26 | val notificationChannel = NotificationChannel(id, name, importance) 27 | .apply(channel) 28 | notificationChannels + notificationChannel 29 | notificationManager.createNotificationChannels(notificationChannels) 30 | return notificationChannel 31 | } 32 | 33 | @RequiresApi(Build.VERSION_CODES.O) 34 | inline fun channelGroup(id: String = NotificationChannel.DEFAULT_CHANNEL_ID, 35 | name: String = "default channel name", 36 | channel: NotificationChannelGroup.() -> Unit): NotificationChannelGroup { 37 | val notificationsChannelGroup = NotificationChannelGroup(id, name) 38 | .apply(channel) 39 | notificationChannelGroups + notificationsChannelGroup 40 | notificationManager.createNotificationChannelGroups(notificationChannelGroups) 41 | return notificationsChannelGroup 42 | } 43 | 44 | @RequiresApi(Build.VERSION_CODES.JELLY_BEAN) 45 | fun showSingle(notificationId: Int, notification: Notification) { 46 | notificationManager.notify(notificationId, notification.build()) 47 | } 48 | 49 | @RequiresApi(Build.VERSION_CODES.JELLY_BEAN) 50 | fun showAll() { 51 | notifications.forEach { showSingle(it.first, it.second) } 52 | } 53 | } 54 | 55 | inline fun Context.notificationManager(manager: NotificationManager.() -> Unit) = 56 | NotificationManager(this) 57 | .apply(manager) 58 | -------------------------------------------------------------------------------- /core/src/main/java/goldzweigapps/com/core/other/LazyInitializeObject.kt: -------------------------------------------------------------------------------- 1 | package goldzweigapps.com.core.other 2 | 3 | /** 4 | * Created by gilgoldzweig on 05/11/2017. 5 | */ 6 | open class LazyInitializeObject { 7 | protected lateinit var type: T 8 | 9 | open fun initialize(type: T) { 10 | try { 11 | this.type 12 | } catch (ex: UninitializedPropertyAccessException) { 13 | this.type = type 14 | } 15 | } 16 | } -------------------------------------------------------------------------------- /core/src/main/java/goldzweigapps/com/core/permissions/PermissionsExtensions.kt: -------------------------------------------------------------------------------- 1 | package goldzweigapps.com.core.permissions 2 | 3 | import android.annotation.SuppressLint 4 | import android.annotation.TargetApi 5 | import android.content.Context 6 | import android.content.pm.PackageManager 7 | import android.os.Build 8 | import android.support.v4.content.ContextCompat 9 | 10 | /** 11 | * Created by gilgoldzweig on 04/09/2017. 12 | */ 13 | @SuppressLint("NewApi") 14 | fun isVersionAbove(version: Int) = Build.VERSION.SDK_INT >= version 15 | 16 | @TargetApi(Build.VERSION_CODES.M) 17 | fun isMarshmallowOrAbove() = isVersionAbove(Build.VERSION_CODES.M) 18 | fun isLollipopOrAbove() = isVersionAbove(Build.VERSION_CODES.LOLLIPOP) 19 | 20 | infix fun Context.isGranted(permission: String) = 21 | ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED 22 | -------------------------------------------------------------------------------- /core/src/main/java/goldzweigapps/com/core/preferences/GlobalSharedPreferences.kt: -------------------------------------------------------------------------------- 1 | package goldzweigapps.com.core.preferences 2 | 3 | import android.app.Application 4 | import android.content.Context 5 | import android.content.SharedPreferences 6 | 7 | @Suppress("unused") 8 | object GlobalSharedPreferences { 9 | private lateinit var sharedPreferences: SharedPreferences 10 | private var initialized = false 11 | 12 | /** 13 | * @param application Global context in order use everywhere 14 | * without the need for context every time 15 | * @param sharedPreferencesName custom name for SharedPreferences 16 | * @return instance of the GlobalSharedPreferences 17 | */ 18 | fun initialize(application: Application, sharedPreferencesName: String = "DefaultSharedPreferences"): 19 | GlobalSharedPreferences { 20 | sharedPreferences = application.getSharedPreferences(sharedPreferencesName, Context.MODE_PRIVATE) 21 | initialized = true 22 | return this 23 | } 24 | 25 | 26 | //region editor 27 | private val edit: SharedPreferences.Editor by lazy { requiredOrThrow(sharedPreferences.edit()) } 28 | //endregion editor 29 | 30 | //region get 31 | fun getAll(): Map = requiredOrThrow(sharedPreferences.all) 32 | 33 | fun getInt(key: String, defaultValue: Int) = 34 | requiredOrThrow(sharedPreferences.getInt(key, defaultValue)) 35 | 36 | fun getLong(key: String, defaultValue: Long) = 37 | requiredOrThrow(sharedPreferences.getLong(key, defaultValue)) 38 | 39 | fun getFloat(key: String, defaultValue: Float) = 40 | requiredOrThrow(sharedPreferences.getFloat(key, defaultValue)) 41 | 42 | fun getBoolean(key: String, defaultValue: Boolean) = 43 | requiredOrThrow(sharedPreferences.getBoolean(key, defaultValue)) 44 | 45 | fun getString(key: String, defaultValue: String): String = 46 | requiredOrThrow(sharedPreferences.getString(key, defaultValue)) 47 | 48 | fun getStringSet(key: String, defaultValue: Set): MutableSet? = 49 | requiredOrThrow(sharedPreferences.getStringSet(key, defaultValue)) 50 | 51 | infix fun String.forStringSet(defaultValue: Set): MutableSet? = 52 | requiredOrThrow(sharedPreferences.getStringSet(this, defaultValue)) 53 | 54 | infix fun String.forInt(defaultValue: Int) = 55 | requiredOrThrow(sharedPreferences.getInt(this, defaultValue)) 56 | 57 | infix fun String.forLong(defaultValue: Long) = 58 | requiredOrThrow(sharedPreferences.getLong(this, defaultValue)) 59 | 60 | infix fun String.forFloat(defaultValue: Float) = 61 | requiredOrThrow(sharedPreferences.getFloat(this, defaultValue)) 62 | 63 | infix fun String.forBoolean(defaultValue: Boolean) = 64 | requiredOrThrow(sharedPreferences.getBoolean(this, defaultValue)) 65 | 66 | infix fun String.forString(defaultValue: String): String = 67 | requiredOrThrow(sharedPreferences.getString(this, defaultValue)) 68 | 69 | fun get(key: String, defaultValue: T) = 70 | try { 71 | when (defaultValue) { 72 | is String -> 73 | getString(key, defaultValue) 74 | is Int -> 75 | getInt(key, defaultValue) 76 | is Long -> 77 | getLong(key, defaultValue) 78 | is Boolean -> 79 | getBoolean(key, defaultValue) 80 | is Float -> 81 | getFloat(key, defaultValue) 82 | is Set<*> -> { 83 | getStringSet(key, defaultValue as Set) 84 | } 85 | else -> { 86 | throw ClassCastException("GlobalSharedPreferences Only support SharedPreferences types\n type ${defaultValue::class}") 87 | } 88 | } as T 89 | } catch (ex: ClassCastException) { 90 | throw Throwable(""" 91 | Exception while performing get from shared preferences 92 | key: $key 93 | default value: $defaultValue 94 | exception: ${ex.message} 95 | ${ex.stackTrace} 96 | """.trimIndent()) 97 | } 98 | 99 | 100 | //endregion get 101 | 102 | //region contains 103 | operator fun contains(key: String) = requiredOrThrow(sharedPreferences.contains(key)) 104 | //endregion contains 105 | 106 | //region put 107 | fun put(key: String, value: String) { 108 | edit.putString(key, value).commit() 109 | } 110 | 111 | fun put(key: String, value: Int) = also { edit.putInt(key, value).commit() } 112 | 113 | fun put(key: String, value: Long) = also { edit.putLong(key, value).commit() } 114 | 115 | fun put(key: String, value: Boolean) = also { edit.putBoolean(key, value).commit() } 116 | 117 | fun put(key: String, value: Float) = also { edit.putFloat(key, value).commit() } 118 | 119 | fun put(key: String, value: Set) = also { edit.putStringSet(key, value).commit() } 120 | 121 | fun put(key: String, value: Any) = 122 | also { 123 | when (value) { 124 | is String -> 125 | put(key, value) 126 | is Int -> 127 | put(key, value) 128 | is Long -> 129 | put(key, value) 130 | is Boolean -> 131 | put(key, value) 132 | is Float -> 133 | put(key, value) 134 | is Set<*> -> 135 | put(key, value.map { it.toString() }.toSet()) 136 | } 137 | } 138 | 139 | private fun putUnit(key: String, value: Any) { 140 | put(key, value) 141 | } 142 | 143 | operator fun String.plusAssign(value: Any) = putUnit(this, value) 144 | 145 | operator fun plusAssign(keyValuePair: Pair) = 146 | putUnit(keyValuePair.first, keyValuePair.second) 147 | 148 | operator fun plus(keyValuePair: Pair) = 149 | put(keyValuePair.first, keyValuePair.second) 150 | 151 | //endregion put 152 | 153 | //region remove 154 | fun remove(key: String) = also { edit.remove(key).commit() } 155 | 156 | operator fun minus(key: String) = remove(key) 157 | //endregion remove 158 | 159 | //region commit/apply 160 | fun commit() = edit.commit() 161 | 162 | fun apply() = edit.apply() 163 | //endregion commit/apply 164 | 165 | /** 166 | * @param returnIfInitialized object to be returned if class is initialized 167 | * @throws InitializeException 168 | */ 169 | @Throws(UninitializedPropertyAccessException::class) 170 | private fun requiredOrThrow(returnIfInitialized: T) = if (initialized) { 171 | returnIfInitialized 172 | } else { 173 | throw UninitializedPropertyAccessException("GlobalSharedPreferences") 174 | } 175 | 176 | } 177 | 178 | inline fun pref(sharedPreferences: GlobalSharedPreferences.() -> Unit) = with(GlobalSharedPreferences) { 179 | also { 180 | sharedPreferences() 181 | apply() 182 | } 183 | } -------------------------------------------------------------------------------- /core/src/main/java/goldzweigapps/com/core/preferences/PreferencesProperty.kt: -------------------------------------------------------------------------------- 1 | package goldzweigapps.com.core.preferences 2 | 3 | import goldzweigapps.com.core.threads.RunnableThread 4 | import goldzweigapps.com.core.threads.RunnableThreads 5 | import kotlin.properties.ReadWriteProperty 6 | import kotlin.reflect.KProperty 7 | 8 | class PreferencesProperty internal constructor(private val defaultValue: T, 9 | private val key: String = "", 10 | private val runnableThread: RunnableThread): 11 | ReadWriteProperty { 12 | 13 | private val pref = GlobalSharedPreferences 14 | 15 | override fun setValue(thisRef: Any, property: KProperty<*>, value: T) { 16 | val prefsKey = if (key.isEmpty()) property.name else key 17 | when (runnableThread) { 18 | 19 | RunnableThreads.BACKGROUND -> 20 | pref.put(prefsKey, value).apply() 21 | 22 | RunnableThreads.CURRENT -> 23 | pref.put(prefsKey, value) 24 | 25 | else -> 26 | runnableThread.run({ pref.put(prefsKey, value).commit() }) 27 | } 28 | } 29 | 30 | override fun getValue(thisRef: Any, property: KProperty<*>) = 31 | pref.get(if (key.isEmpty()) property.name else key, defaultValue) 32 | 33 | } 34 | fun preferences(defaultValue: T, 35 | key: String = "", 36 | thread: RunnableThread = RunnableThreads.CURRENT) = 37 | PreferencesProperty(defaultValue, key, thread) -------------------------------------------------------------------------------- /core/src/main/java/goldzweigapps/com/core/resources/ResourcesExtensions.kt: -------------------------------------------------------------------------------- 1 | package goldzweigapps.com.core.resources 2 | 3 | import android.content.Context 4 | import android.content.res.Resources 5 | import android.support.annotation.ColorRes 6 | import android.support.annotation.DrawableRes 7 | import android.support.v4.content.ContextCompat 8 | 9 | /** 10 | * Created by gilgoldzweig on 04/09/2017. 11 | */ 12 | 13 | fun Int.dpToPx() = (this * Resources.getSystem().displayMetrics.density).toInt() 14 | 15 | fun Int.PxToDp() = (this / Resources.getSystem().displayMetrics.density).toInt() 16 | 17 | fun Context.getDrawable(@DrawableRes drawableRes: Int) = 18 | ContextCompat.getDrawable(this, drawableRes) 19 | 20 | fun Context.color(@ColorRes drawableRes: Int) = 21 | ContextCompat.getColor(this, drawableRes) 22 | 23 | -------------------------------------------------------------------------------- /core/src/main/java/goldzweigapps/com/core/streams/StreamsExtensions.kt: -------------------------------------------------------------------------------- 1 | package goldzweigapps.com.core.streams 2 | 3 | import java.io.InputStream 4 | import java.util.* 5 | 6 | /** 7 | * Created by gilgoldzweig on 21/11/2017. 8 | */ 9 | fun InputStream.toString(): String { 10 | val s = Scanner(this) 11 | .useDelimiter("\\A") 12 | return if (s.hasNext()) s.next() else "" 13 | } -------------------------------------------------------------------------------- /core/src/main/java/goldzweigapps/com/core/support/GenericRecyclerAdapter.kt: -------------------------------------------------------------------------------- 1 | package goldzweigapps.com.core.support 2 | 3 | import android.content.Context 4 | import android.support.annotation.LayoutRes 5 | import android.support.v7.widget.RecyclerView 6 | import android.view.LayoutInflater 7 | import android.view.View 8 | import android.view.ViewGroup 9 | import goldzweigapps.com.core.support.GenericRecyclerViewAdapterExtensions 10 | 11 | abstract class GenericRecyclerAdapter(context: Context, 12 | genericList: MutableList = ArrayList(), 13 | @LayoutRes private val layoutRes: Int) : 14 | GenericRecyclerViewAdapterExtensions.GenericViewHolder>(genericList) { 15 | 16 | private var layoutInflater: LayoutInflater = LayoutInflater.from(context) 17 | 18 | //region setup and binding 19 | inner class GenericViewHolder(private val v: View) : RecyclerView.ViewHolder(v) { 20 | 21 | fun onBind(currentElementPosition: Int, currentElement: E) { 22 | v.onBind(currentElementPosition, currentElement, this) 23 | } 24 | } 25 | 26 | override fun getItemCount() = elements.size 27 | 28 | open fun count() = itemCount 29 | 30 | override fun onCreateViewHolder(p0: ViewGroup, p1: Int) = 31 | GenericViewHolder(layoutInflater.inflate(layoutRes, p0, false)) 32 | 33 | override fun onBindViewHolder(holder: GenericViewHolder, p1: Int) = 34 | if (elements.isNotEmpty()) 35 | holder.onBind(p1, elements[p1]) else Unit 36 | 37 | abstract fun View.onBind(currentElementPosition: Int, currentElement: E, holder: GenericViewHolder) 38 | } 39 | -------------------------------------------------------------------------------- /core/src/main/java/goldzweigapps/com/core/support/GenericRecyclerViewAdapterExtensions.kt: -------------------------------------------------------------------------------- 1 | package goldzweigapps.com.core.support 2 | 3 | import android.support.v7.widget.RecyclerView 4 | import goldzweigapps.com.core.threads.isUiThread 5 | 6 | abstract class GenericRecyclerViewAdapterExtensions(var elements: MutableList) : 7 | RecyclerView.Adapter() { 8 | 9 | override fun getItemCount() = elements.size 10 | 11 | open fun replaceList(newGenericList: List) { 12 | elements.clear() 13 | elements.addAll(newGenericList) 14 | if (isUiThread()) notifyDataSetChanged() 15 | } 16 | 17 | open fun setItems(newGenericList: List) = replaceList(newGenericList) 18 | 19 | open fun setItem(position: Int, newGenericItem: E) { 20 | if (position in 0..elements.size) { 21 | elements[position] = newGenericItem 22 | if (isUiThread()) notifyItemChanged(position) 23 | } 24 | } 25 | 26 | open fun add(itemToAdd: E, position: Int = elements.size) { 27 | elements.add(position, itemToAdd) 28 | if (isUiThread()) notifyItemInserted(position) 29 | } 30 | 31 | open fun addRange(itemsToAdd: List, positionToInsert: Int = elements.size) { 32 | elements.addAll(positionToInsert, itemsToAdd) 33 | if (isUiThread()) notifyItemRangeInserted(positionToInsert, itemsToAdd.size) 34 | } 35 | 36 | open fun removeRange(itemsToRemove: List) { 37 | val startPosition = elements.indexOf(itemsToRemove[0]) 38 | elements.removeAll(itemsToRemove) 39 | if (isUiThread()) notifyItemRangeRemoved(startPosition, itemsToRemove.size) 40 | } 41 | 42 | open fun removeRange(rangeToRemove: IntRange) { 43 | removeRange(elements.subList(rangeToRemove.start, rangeToRemove.last)) 44 | } 45 | 46 | open fun remove(itemToRemove: E) { 47 | val removePosition = elements.indexOf(itemToRemove) 48 | if (removePosition != -1) { 49 | elements.removeAt(removePosition) 50 | if (isUiThread()) notifyItemRemoved(removePosition) 51 | } 52 | } 53 | 54 | open fun remove(removePosition: Int) { 55 | if (removePosition != -1) { 56 | elements.removeAt(removePosition) 57 | if (isUiThread()) notifyItemRemoved(removePosition) 58 | } 59 | } 60 | 61 | open fun clear() { 62 | elements.clear() 63 | if (isUiThread()) notifyDataSetChanged() 64 | } 65 | 66 | open fun isEmpty() = elements.isEmpty() 67 | 68 | open fun isNotEmpty() = !isEmpty() 69 | 70 | open operator fun minus(rangeToRemove: IntRange) = removeRange(rangeToRemove) 71 | 72 | open operator fun minus(itemsToRemove: List) = removeRange(itemsToRemove) 73 | open operator fun minus(itemToRemove: E) = remove(itemToRemove) 74 | open operator fun minus(removePosition: Int) = remove(removePosition) 75 | 76 | open operator fun plus(itemsToAdd: List) = addRange(itemsToAdd) 77 | 78 | open operator fun plus(itemToAdd: E) = add(itemToAdd) 79 | open operator fun plus(itemToAddInPosition: Pair) = 80 | with(itemToAddInPosition) { add(first, second) } 81 | 82 | open operator fun get(position: Int) = elements[position] 83 | 84 | open operator fun get(item: E) = elements.indexOf(item) 85 | } -------------------------------------------------------------------------------- /core/src/main/java/goldzweigapps/com/core/threads/ThreadsExtensions.kt: -------------------------------------------------------------------------------- 1 | package goldzweigapps.com.core.threads 2 | 3 | import android.os.AsyncTask 4 | import android.os.Build 5 | import android.os.Handler 6 | import android.os.Looper 7 | 8 | 9 | /** 10 | * Created by gilgoldzweig on 04/09/2017. 11 | */ 12 | private val handler = Handler() 13 | 14 | /** 15 | * @return current thread is UI thread or not 16 | */ 17 | fun isUiThread() = 18 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) 19 | Looper.getMainLooper().isCurrentThread 20 | else 21 | Thread.currentThread() == Looper.getMainLooper().thread 22 | 23 | /** 24 | * run a function after a provided millis 25 | * @param millis millisecond to wait before running the function 26 | * @param thread run the function on selected thread 27 | * @param func function to run after provided time 28 | * runAfter(2000) { 29 | * //running this part after 2000 millis in current thread 30 | * } 31 | * @return true for success and false for failure 32 | */ 33 | fun runAfter(millis: Long, 34 | thread: RunnableThread = RunnableThreads.CURRENT, 35 | func: () -> Unit) = 36 | handler.postDelayed({ thread.run(func) }, millis) 37 | 38 | fun run(thread: RunnableThread = RunnableThreads.CURRENT, func: () -> Unit) = 39 | thread.run(func) 40 | 41 | 42 | /** 43 | * run a function on ui thread 44 | * @param func 45 | * runOnUI { 46 | * //running this part on ui thread 47 | * } 48 | * @return true for success and false for failure 49 | */ 50 | fun runOnUI(func: () -> Unit) = RunnableThreads.UI.run(func) 51 | 52 | /** 53 | * run a vararg functions on background thread 54 | * @param functions 55 | * runInBackground ({ 56 | * //running this part in background 57 | * },{ 58 | * //running also this part in background 59 | * },{ 60 | * //running also this part in background 61 | * }) 62 | * @return Unit 63 | */ 64 | fun runInBackground(vararg functions: () -> Unit) = 65 | RunnableThreads.BACKGROUND.run(*functions) 66 | 67 | /** 68 | * running a single function in background same as runInBackground(vararg functions: () -> Unit) 69 | * but with an easier lambda access 70 | * on background thread 71 | * @param func 72 | * runInBackground { 73 | * //running this part in background 74 | * } 75 | * @return Unit 76 | */ 77 | fun runInBackground(func: () -> Unit) = RunnableThreads.BACKGROUND.run(func) 78 | 79 | class BackgroundReturnTask(val returnValue: (R) -> Unit) : AsyncTask<() -> R, Any, Any>() { 80 | var values: List = ArrayList() 81 | override fun doInBackground(vararg params: (() -> R)): Any { 82 | params.forEach { values += it.invoke() } 83 | return false 84 | } 85 | 86 | override fun onPostExecute(result: Any?) { 87 | super.onPostExecute(result) 88 | values.forEach(returnValue::invoke) 89 | } 90 | } 91 | 92 | fun runInBackground(func: () -> R, returnValue: (R) -> Unit) { 93 | BackgroundReturnTask(returnValue).execute(func) 94 | } 95 | 96 | interface RunnableThread { 97 | fun run(vararg functions: () -> Unit) 98 | } 99 | 100 | sealed class RunnableThreads: RunnableThread { 101 | object UI : RunnableThreads() { 102 | override fun run(vararg functions: () -> Unit) { 103 | for (function in functions) { 104 | if (!isUiThread()) { 105 | handler.post(function::invoke) 106 | } else { 107 | function.invoke() 108 | } 109 | } 110 | } 111 | } 112 | object BACKGROUND : RunnableThreads() { 113 | override fun run(vararg functions: () -> Unit) { 114 | functions.forEach { Thread(it::invoke).start() } 115 | } 116 | } 117 | object CURRENT : RunnableThreads() { 118 | override fun run(vararg functions: () -> Unit) { 119 | for (function in functions) function.invoke() 120 | } 121 | } 122 | } 123 | 124 | -------------------------------------------------------------------------------- /core/src/main/java/goldzweigapps/com/core/views/ViewsExtensions.kt: -------------------------------------------------------------------------------- 1 | package goldzweigapps.com.core.views 2 | 3 | import android.content.Context 4 | import android.support.annotation.LayoutRes 5 | import android.support.annotation.StringRes 6 | import android.support.design.widget.Snackbar 7 | import android.support.v7.widget.LinearLayoutManager 8 | import android.text.Editable 9 | import android.text.TextWatcher 10 | import android.view.LayoutInflater 11 | import android.view.View 12 | import android.view.ViewGroup 13 | import android.widget.EditText 14 | import android.widget.TextView 15 | import goldzweigapps.com.core.collections.asIterableIndexed 16 | 17 | /** 18 | * Created by gilgoldzweig on 04/09/2017. 19 | */ 20 | 21 | //region view visibility 22 | fun View.toggleVisibility() { 23 | visibility = if (isVisible()) View.GONE else View.VISIBLE 24 | } 25 | 26 | fun View.isVisible() = visibility == View.VISIBLE 27 | 28 | fun View.hide() { 29 | visibility = View.GONE 30 | } 31 | 32 | fun View.show() { 33 | visibility = View.VISIBLE 34 | } 35 | 36 | fun View.invisible() { 37 | visibility = View.INVISIBLE 38 | } 39 | //endregion view visibility 40 | 41 | fun View.onClick(onClick: () -> Unit) = setOnClickListener { onClick() } 42 | 43 | fun View.onLongClick(onLongClick: () -> Unit) = setOnLongClickListener { 44 | onLongClick() 45 | false 46 | } 47 | 48 | fun EditText.onTextChange(onTextChange: (text: CharSequence) -> Unit) { 49 | addTextChangedListener(object : TextWatcher { 50 | override fun afterTextChanged(s: Editable?) = Unit 51 | 52 | override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit 53 | 54 | override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) = 55 | onTextChange(s ?: "") 56 | }) 57 | } 58 | 59 | typealias ActionString = Pair 60 | typealias ActionResId = Pair 61 | 62 | val Context.linearLayoutManager: LinearLayoutManager 63 | get() = LinearLayoutManager(this) 64 | 65 | val Context.horizontalLinearLayoutManager: LinearLayoutManager 66 | get() = LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false) 67 | 68 | fun View.snackBar(message: CharSequence, length: Int = Snackbar.LENGTH_LONG, action: ActionString? = null) { 69 | val snackBar = Snackbar.make(this, message, length) 70 | action?.let { 71 | snackBar.setAction(it.first, it.second) 72 | } 73 | snackBar.show() 74 | } 75 | fun View.snackBar(@StringRes message: Int, length: Int = Snackbar.LENGTH_LONG, action: ActionResId? = null) { 76 | val snackBar = Snackbar.make(this, message, length) 77 | action?.let { 78 | snackBar.setAction(it.first, it.second) 79 | } 80 | snackBar.show() 81 | } 82 | 83 | fun View.height(height: Int) { 84 | layoutParams.height = height 85 | } 86 | fun View.width(width: Int) { 87 | layoutParams.height = height 88 | } 89 | 90 | operator fun TextView.plusAssign(valueToAdd: String) { 91 | text = "$text$valueToAdd" 92 | } 93 | operator fun TextView.minusAssign(valueToRemove: String) { 94 | text = text.removePrefix(valueToRemove) 95 | } 96 | operator fun TextView.contains(value: String) = value in text 97 | 98 | @Throws(IndexOutOfBoundsException::class) 99 | operator fun TextView.get(index: Int): Char { 100 | return if (index in 0..text.length) { 101 | text[index] 102 | } else { 103 | throw IndexOutOfBoundsException(""" 104 | Index: $index 105 | Start: 0 106 | End: ${text.length} 107 | """.trimIndent()) 108 | } 109 | } 110 | 111 | operator fun TextView.get(char: Char, ignoreCase: Boolean = false) = 112 | text.toString().indexOf(char, 0, ignoreCase) 113 | 114 | fun ViewGroup.inflate(@LayoutRes layoutRes: Int, 115 | attachToRoot: Boolean = false) = 116 | LayoutInflater.from(context).inflate(layoutRes, this, attachToRoot) 117 | 118 | fun Context.inflate(@LayoutRes layoutRes: Int, 119 | attachToRoot: Boolean = false, 120 | root: ViewGroup? = null) = 121 | LayoutInflater.from(this).inflate(layoutRes, root, attachToRoot) 122 | 123 | //region ViewGroup operators 124 | /** 125 | * [position] = getChildAt(position) 126 | */ 127 | operator fun ViewGroup.get(position: Int): View? = getChildAt(position) 128 | 129 | /** 130 | * [view] = indexOfChild(view) 131 | */ 132 | operator fun ViewGroup.get(view: View) = indexOfChild(view) 133 | 134 | /** 135 | * -= 136 | */ 137 | operator fun ViewGroup.minusAssign(child: View) = removeView(child) 138 | 139 | /** 140 | * += 141 | */ 142 | operator fun ViewGroup.plusAssign(child: View) = addView(child) 143 | 144 | /** 145 | * if (view in views) 146 | */ 147 | operator fun ViewGroup.contains(child: View) = get(child) != -1 148 | 149 | /** 150 | * for (view in views.iterator) 151 | * @param T Any layout that extends ViewGroup for example LinearLayout 152 | */ 153 | operator fun T.iterator(): Iterable { 154 | return asIterableIndexed({ it < childCount }, { 155 | getChildAt(it) 156 | }, { 157 | it.inc() 158 | }) 159 | } 160 | //endregion ViewGroup operators 161 | 162 | //region ViewGroup iterations 163 | fun ViewGroup.first() = this[0] 164 | 165 | fun ViewGroup.last() = this[childCount] 166 | 167 | inline fun ViewGroup.forEach(action: (View) -> Unit) { 168 | for (i in 0 until childCount) { 169 | action(getChildAt(i)) 170 | } 171 | } 172 | 173 | inline fun ViewGroup.forEachIndexed(action: (Int, View) -> Unit) { 174 | for (i in 0 until childCount) { 175 | action(i, getChildAt(i)) 176 | } 177 | } 178 | 179 | inline fun ViewGroup.forEachRevered(action: (View) -> Unit) { 180 | for (i in childCount downTo 0) { 181 | action(getChildAt(i)) 182 | } 183 | } 184 | 185 | inline fun ViewGroup.forEachReveredIndexed(action: (Int, View) -> Unit) { 186 | for (i in childCount downTo 0) { 187 | action(i, getChildAt(i)) 188 | } 189 | } 190 | //endregion ViewGroup iterations -------------------------------------------------------------------------------- /core/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Library 3 | 4 | -------------------------------------------------------------------------------- /core/src/test/java/goldzweigapps/com/core/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package goldzweigapps.com.core; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.assertEquals; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | @Test 18 | public void intIterable_isCounting() throws Exception { 19 | } 20 | 21 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gilgoldzweig/Kotlinify/5b45da2be009d00cc29beabe4794b59b681aacf5/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sat Apr 07 15:02:32 IDT 2018 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /jackson/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /jackson/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'com.github.dcendents.android-maven' 4 | apply plugin: 'com.novoda.bintray-release' 5 | group = 'com.github.gilgoldzweig' 6 | android { 7 | compileSdkVersion 27 8 | buildToolsVersion "27.0.3" 9 | 10 | 11 | defaultConfig { 12 | minSdkVersion 14 13 | targetSdkVersion 27 14 | versionCode 1 15 | versionName "1.0" 16 | 17 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 18 | 19 | } 20 | buildTypes { 21 | release { 22 | minifyEnabled false 23 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 24 | } 25 | } 26 | } 27 | publish { 28 | userOrg = "$rootProject.userOrg" 29 | groupId = "$rootProject.groupId" 30 | publishVersion = "$rootProject.publishVersion" 31 | website = "$rootProject.website" 32 | artifactId = 'jackson' 33 | desc = 'The jackson module provides the following classes and extensions.' 34 | } 35 | 36 | dependencies { 37 | implementation fileTree(dir: 'libs', include: ['*.jar']) 38 | testImplementation 'junit:junit:4.12' 39 | 40 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" 41 | implementation "com.fasterxml.jackson.module:jackson-module-kotlin:2.9.0" 42 | } 43 | -------------------------------------------------------------------------------- /jackson/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/gilgoldzweig/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /jackson/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /jackson/src/main/java/goldzweigapps/com/jackson/jacksonExtensions.kt: -------------------------------------------------------------------------------- 1 | package goldzweigapps.com.jackson 2 | 3 | import com.fasterxml.jackson.core.JsonParser 4 | import com.fasterxml.jackson.databind.ObjectMapper 5 | import com.fasterxml.jackson.module.kotlin.readValue 6 | import java.io.File 7 | import java.io.InputStream 8 | import java.io.Reader 9 | import java.net.URL 10 | 11 | /** 12 | * Created by gilgoldzweig on 13/09/2017. 13 | */ 14 | val objectMapper by lazy { ObjectMapper() } 15 | 16 | fun Any.toJson(): String = objectMapper.writeValueAsString(this) 17 | fun Any.toPrettyJson(): String = objectMapper.writerWithDefaultPrettyPrinter() 18 | .writeValueAsString(this) 19 | 20 | inline fun String.fromJson() = objectMapper.readValue(this) 21 | inline fun InputStream.fromJson() = objectMapper.readValue(this) 22 | inline fun JsonParser.fromJson() = objectMapper.readValue(this) 23 | inline fun ByteArray.fromJson() = objectMapper.readValue(this) 24 | inline fun File.fromJson() = objectMapper.readValue(this) 25 | inline fun Reader.fromJson() = objectMapper.readValue(this) 26 | inline fun URL.fromJson() = objectMapper.readValue(this) 27 | -------------------------------------------------------------------------------- /jackson/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Activites 3 | 4 | -------------------------------------------------------------------------------- /jackson/src/test/java/goldzweigapps/com/jackson/JacksonUnitTest.kt: -------------------------------------------------------------------------------- 1 | package goldzweigapps.com.jackson 2 | 3 | import org.junit.Assert 4 | import org.junit.Test 5 | 6 | /** 7 | * Created by gilgoldzweig on 19/10/2017. 8 | */ 9 | class JacksonUnitTest { 10 | val simplePersonTestJson = "{\"firstName\":\"testFirstName\",\"lastName\":\"testFamilyName\",\"age\":41}" 11 | val simplePersonTestPrettyJson = "{\n" + 12 | " \"firstName\" : \"testFirstName\",\n" + 13 | " \"lastName\" : \"testFamilyName\",\n" + 14 | " \"age\" : 41\n" + 15 | "}\n" 16 | @Test 17 | fun tojson_isCurrent() { 18 | Assert.assertEquals(simplePersonTestJson, 19 | PersonTestObject("testFirstName", "testFamilyName", 41) 20 | .toJson()) 21 | } 22 | @Test 23 | fun prettyjson_IsCurrect() { 24 | Assert.assertEquals(simplePersonTestJson, 25 | PersonTestObject("testFirstName", "testFamilyName", 41) 26 | .toPrettyJson()) 27 | } 28 | } 29 | 30 | data class PersonTestObject(var firstName: String = "", 31 | var lastName: String = "", 32 | var age: Int = 0) -------------------------------------------------------------------------------- /reactive/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /reactive/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'com.github.dcendents.android-maven' 4 | apply plugin: 'com.novoda.bintray-release' 5 | group = 'com.github.gilgoldzweig' 6 | 7 | publish { 8 | userOrg = "$rootProject.userOrg" 9 | groupId = "$rootProject.groupId" 10 | publishVersion = "$rootProject.publishVersion" 11 | website = "$rootProject.website" 12 | artifactId = 'reactive' 13 | desc = 'The Reactive module provides the following extensions.' 14 | } 15 | android { 16 | compileSdkVersion 27 17 | buildToolsVersion '27.0.3' 18 | 19 | 20 | defaultConfig { 21 | minSdkVersion 14 22 | targetSdkVersion 27 23 | versionCode 1 24 | versionName "1.0" 25 | 26 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 27 | 28 | } 29 | buildTypes { 30 | release { 31 | minifyEnabled false 32 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 33 | } 34 | } 35 | } 36 | 37 | dependencies { 38 | implementation fileTree(dir: 'libs', include: ['*.jar']) 39 | testImplementation 'junit:junit:4.12' 40 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jre7:$kotlin_version" 41 | 42 | implementation 'io.reactivex.rxjava2:rxandroid:2.0.1' 43 | implementation 'io.reactivex.rxjava2:rxjava:2.1.0' 44 | } 45 | -------------------------------------------------------------------------------- /reactive/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/gilgoldzweig/Library/Android/sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | # Uncomment this to preserve the line number information for 20 | # debugging stack traces. 21 | #-keepattributes SourceFile,LineNumberTable 22 | 23 | # If you keep the line number information, uncomment this to 24 | # hide the original source file name. 25 | #-renamesourcefileattribute SourceFile 26 | -------------------------------------------------------------------------------- /reactive/src/androidTest/java/goldzweigapps/com/reactive/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package goldzweigapps.com.reactive; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.*; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("goldzweigapps.com.reactive.test", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /reactive/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /reactive/src/main/java/goldzweigapps/com/reactive/ReactiveExtensions.kt: -------------------------------------------------------------------------------- 1 | package goldzweigapps.com.reactive 2 | 3 | import io.reactivex.* 4 | import io.reactivex.android.schedulers.AndroidSchedulers 5 | import io.reactivex.schedulers.Schedulers 6 | 7 | /** 8 | * Created by gilgoldzweig on 30/08/2017. 9 | * some small extensions function to allow easier usage with Reactive in android 10 | */ 11 | private val mainThread = AndroidSchedulers.mainThread() 12 | private val newThread = Schedulers.newThread() 13 | private val ioThread = Schedulers.io() 14 | 15 | /** 16 | * observe on main thread 17 | * subscribe on new thread 18 | * unsubsidised on error and on complete and removes the need to handle it afterwards 19 | * @usage 20 | * someObservable 21 | * .runSafeOnMain() 22 | * .subscribe({}, {]) 23 | */ 24 | fun Observable.runSafeOnMain(): Observable = 25 | observeOn(mainThread) 26 | .subscribeOn(newThread) 27 | .doOnError({ unsubscribeOn(newThread) }) 28 | .doOnComplete { unsubscribeOn(newThread) } 29 | 30 | 31 | fun Observable.runSafeOnIO(): Observable = 32 | observeOn(ioThread) 33 | .subscribeOn(newThread) 34 | .doOnError({ unsubscribeOn(newThread) }) 35 | .doOnComplete { unsubscribeOn(newThread) } 36 | 37 | fun Flowable.runSafeOnMain(): Flowable = 38 | observeOn(mainThread) 39 | .subscribeOn(newThread) 40 | .doOnError({ unsubscribeOn(newThread) }) 41 | .doOnComplete { unsubscribeOn(newThread) } 42 | 43 | fun Flowable.runSafeOnIO(): Flowable = 44 | observeOn(ioThread) 45 | .subscribeOn(newThread) 46 | .doOnError({ unsubscribeOn(newThread) }) 47 | .doOnComplete { unsubscribeOn(newThread) } 48 | 49 | fun Single.runSafeOnMain(): Single = 50 | observeOn(mainThread) 51 | .subscribeOn(newThread) 52 | .doOnError({ unsubscribeOn(newThread) }) 53 | .doOnSuccess { unsubscribeOn(newThread) } 54 | 55 | fun Single.runSafeOnIO(): Single = 56 | observeOn(ioThread) 57 | .subscribeOn(newThread) 58 | .doOnError({ unsubscribeOn(newThread) }) 59 | .doOnSuccess { unsubscribeOn(newThread) } 60 | 61 | fun Completable.runSafeOnMain(): Completable = 62 | observeOn(mainThread) 63 | .subscribeOn(newThread) 64 | .doOnError({ unsubscribeOn(newThread) }) 65 | .doOnComplete({ unsubscribeOn(newThread) }) 66 | 67 | fun Completable.runSafeOnIO(): Completable = 68 | observeOn(ioThread) 69 | .subscribeOn(newThread) 70 | .doOnError({ unsubscribeOn(newThread) }) 71 | .doOnComplete({ unsubscribeOn(newThread) }) 72 | 73 | 74 | fun Maybe.runSafeOnMain(): Maybe = 75 | observeOn(mainThread) 76 | .subscribeOn(newThread) 77 | .doOnError({ unsubscribeOn(newThread) }) 78 | .doOnSuccess { unsubscribeOn(newThread) } 79 | 80 | fun Maybe.runSafeOnIO(): Maybe = 81 | observeOn(ioThread) 82 | .subscribeOn(newThread) 83 | .doOnError({ unsubscribeOn(newThread) }) 84 | .doOnSuccess { unsubscribeOn(newThread) } 85 | 86 | -------------------------------------------------------------------------------- /reactive/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Reactive 3 | 4 | -------------------------------------------------------------------------------- /reactive/src/test/java/goldzweigapps/com/reactive/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package goldzweigapps.com.reactive; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app', ':reactive', ':jackson', ':core', ':timber' 2 | -------------------------------------------------------------------------------- /timber/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /timber/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'com.github.dcendents.android-maven' 4 | apply plugin: 'com.novoda.bintray-release' 5 | group = 'com.github.gilgoldzweig' 6 | 7 | 8 | android { 9 | compileSdkVersion 27 10 | buildToolsVersion "27.0.3" 11 | 12 | 13 | defaultConfig { 14 | minSdkVersion 14 15 | targetSdkVersion 27 16 | versionCode 1 17 | versionName "1.0" 18 | 19 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 20 | 21 | } 22 | 23 | buildTypes { 24 | release { 25 | minifyEnabled false 26 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 27 | } 28 | } 29 | 30 | } 31 | publish { 32 | userOrg = "$rootProject.userOrg" 33 | groupId = "$rootProject.groupId" 34 | publishVersion = "$rootProject.publishVersion" 35 | website = "$rootProject.website" 36 | artifactId = 'timber' 37 | desc = 'Custom edition of JakeWharton/timber' 38 | } 39 | dependencies { 40 | implementation fileTree(dir: 'libs', include: ['*.jar']) 41 | 42 | testImplementation 'junit:junit:4.12' 43 | androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', { 44 | exclude group: 'com.android.support', module: 'support-annotations' 45 | }) 46 | 47 | implementation "com.android.support:appcompat-v7:$rootProject.versions.support" 48 | 49 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 50 | 51 | } -------------------------------------------------------------------------------- /timber/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 | -------------------------------------------------------------------------------- /timber/src/androidTest/java/goldzweigapps/com/timber/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package goldzweigapps.com.timber; 2 | 3 | import android.content.Context; 4 | import android.support.test.InstrumentationRegistry; 5 | import android.support.test.runner.AndroidJUnit4; 6 | 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | 10 | import static org.junit.Assert.assertEquals; 11 | 12 | /** 13 | * Instrumented test, which will execute on an Android device. 14 | * 15 | * @see Testing documentation 16 | */ 17 | @RunWith(AndroidJUnit4.class) 18 | public class ExampleInstrumentedTest { 19 | @Test 20 | public void useAppContext() throws Exception { 21 | // Context of the app under test. 22 | Context appContext = InstrumentationRegistry.getTargetContext(); 23 | 24 | assertEquals("goldzweigapps.com.timber.test", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /timber/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /timber/src/main/java/goldzweigapps/com/timber/Timber.kt: -------------------------------------------------------------------------------- 1 | package goldzweigapps.com.timber 2 | 3 | import android.util.Log 4 | import java.io.PrintWriter 5 | import java.io.StringWriter 6 | import java.lang.* 7 | import java.util.* 8 | import java.util.Collections.unmodifiableList 9 | import java.util.regex.Pattern 10 | 11 | @Suppress("unused") 12 | object Timber { 13 | 14 | /** 15 | * A facade for handling logging calls. Install instances via [Timber.plant()][.plant]. 16 | */ 17 | abstract class Tree { 18 | internal val explicitTag = ThreadLocal() 19 | 20 | internal open val tag: String? 21 | get() { 22 | val tag = explicitTag.get() 23 | if (tag != null) { 24 | explicitTag.remove() 25 | } 26 | return tag 27 | } 28 | 29 | /** 30 | * Log a verbose message workWith optional format args. 31 | */ 32 | open fun v(message: Any?, vararg args: Any) { 33 | prepareLog(Log.VERBOSE, null, message, *args) 34 | } 35 | 36 | /** 37 | * Log a verbose exception and a message workWith optional format args. 38 | */ 39 | open fun v(t: Throwable, message: Any, vararg args: Any) { 40 | prepareLog(Log.VERBOSE, t, message, *args) 41 | } 42 | 43 | /** 44 | * Log a verbose exception. 45 | */ 46 | open fun v(t: Throwable) { 47 | prepareLog(Log.VERBOSE, t, null) 48 | } 49 | 50 | /** 51 | * Log a debug message workWith optional format args. 52 | */ 53 | open fun d(message: Any, vararg args: Any) { 54 | prepareLog(Log.DEBUG, null, message, *args) 55 | } 56 | 57 | /** 58 | * Log a debug exception and a message workWith optional format args. 59 | */ 60 | open fun d(t: Throwable, message: Any, vararg args: Any) { 61 | prepareLog(Log.DEBUG, t, message, *args) 62 | } 63 | 64 | /** 65 | * Log a debug exception. 66 | */ 67 | open fun d(t: Throwable) { 68 | prepareLog(Log.DEBUG, t, null) 69 | } 70 | 71 | /** 72 | * Log an info message workWith optional format args. 73 | */ 74 | open fun i(message: Any, vararg args: Any) { 75 | prepareLog(Log.INFO, null, message, *args) 76 | } 77 | 78 | /** 79 | * Log an info exception and a message workWith optional format args. 80 | */ 81 | open fun i(t: Throwable, message: Any, vararg args: Any) { 82 | prepareLog(Log.INFO, t, message, *args) 83 | } 84 | 85 | /** 86 | * Log an info exception. 87 | */ 88 | open fun i(t: Throwable) { 89 | prepareLog(Log.INFO, t, null) 90 | } 91 | 92 | /** 93 | * Log a warning message workWith optional format args. 94 | */ 95 | open fun w(message: Any, vararg args: Any) { 96 | prepareLog(Log.WARN, null, message, *args) 97 | } 98 | 99 | /** 100 | * Log a warning exception and a message workWith optional format args. 101 | */ 102 | open fun w(t: Throwable, message: Any, vararg args: Any) { 103 | prepareLog(Log.WARN, t, message, *args) 104 | } 105 | 106 | /** 107 | * Log a warning exception. 108 | */ 109 | open fun w(t: Throwable) { 110 | prepareLog(Log.WARN, t, null) 111 | } 112 | 113 | /** 114 | * Log an error message workWith optional format args. 115 | */ 116 | open fun e(message: Any, vararg args: Any) { 117 | prepareLog(Log.ERROR, null, message, *args) 118 | } 119 | 120 | /** 121 | * Log an error exception and a message workWith optional format args. 122 | */ 123 | open fun e(t: Throwable, message: Any, vararg args: Any) { 124 | prepareLog(Log.ERROR, t, message, *args) 125 | } 126 | 127 | /** 128 | * Log an error exception. 129 | */ 130 | open fun e(t: Throwable) { 131 | prepareLog(Log.ERROR, t, null) 132 | } 133 | 134 | /** 135 | * Log an assert message workWith optional format args. 136 | */ 137 | open fun wtf(message: Any, vararg args: Any) { 138 | prepareLog(Log.ASSERT, null, message, *args) 139 | } 140 | 141 | /** 142 | * Log an assert exception and a message workWith optional format args. 143 | */ 144 | open fun wtf(t: Throwable, message: Any, vararg args: Any) { 145 | prepareLog(Log.ASSERT, t, message, *args) 146 | } 147 | 148 | /** 149 | * Log an assert exception. 150 | */ 151 | open fun wtf(t: Throwable) { 152 | prepareLog(Log.ASSERT, t, null) 153 | } 154 | 155 | /** 156 | * Log at `priority` a message workWith optional format args. 157 | */ 158 | open fun log(priority: Int, message: Any, vararg args: Any) { 159 | prepareLog(priority, null, message, *args) 160 | } 161 | 162 | /** 163 | * Log at `priority` an exception and a message workWith optional format args. 164 | */ 165 | open fun log(priority: Int, t: Throwable, message: Any, vararg args: Any) { 166 | prepareLog(priority, t, message, *args) 167 | } 168 | 169 | /** 170 | * Log at `priority` an exception. 171 | */ 172 | open fun log(priority: Int, t: Throwable) { 173 | prepareLog(priority, t, null) 174 | } 175 | 176 | private fun prepareLog(priority: Int, t: Throwable?, messageObject: Any?, vararg args: Any) { 177 | var message: String? = messageObject.toString() 178 | // Consume tag even when message is not loggable so that next message is correctly tagged. 179 | val tag = tag 180 | if (message != null && message.isEmpty()) { 181 | message = null 182 | } 183 | if (message == null) { 184 | if (t == null) { 185 | return // Swallow message if it's null and there's no throwable. 186 | } 187 | message = getStackTraceString(t) 188 | } else { 189 | if (args.isNotEmpty()) { 190 | message = formatMessage(message, args) 191 | } 192 | if (t != null) { 193 | message += "\n" + getStackTraceString(t) 194 | } 195 | } 196 | 197 | log(priority, tag, message, t) 198 | } 199 | 200 | /** 201 | * Formats a log message workWith optional arguments. 202 | */ 203 | private fun formatMessage(message: String, args: Array): String = 204 | String.format(message, *args) 205 | 206 | private fun getStackTraceString(t: Throwable): String { 207 | // Don't replace this workWith Log.getStackTraceString() - it hides 208 | // UnknownHostException, which is not what we want. 209 | val sw = StringWriter(256) 210 | val pw = PrintWriter(sw, false) 211 | t.printStackTrace(pw) 212 | pw.flush() 213 | return sw.toString() 214 | } 215 | 216 | /** 217 | * Write a log message to its destination. Called for all level-specific methods by default. 218 | * 219 | * @param priority Log level. See [Log] for constants. 220 | * @param tag Explicit or inferred tag. May be `null`. 221 | * @param message Formatted log message. May be `null`, but then `t` will not be. 222 | * @param t Accompanying exceptions. May be `null`, but then `message` will not be. 223 | */ 224 | protected abstract fun log(priority: Int, tag: String?, message: Any, t: Throwable?) 225 | } 226 | 227 | /** 228 | * A [Tree] for debug builds. Automatically infers the tag from the calling class. 229 | */ 230 | class DebugTree : Tree() { 231 | 232 | override// DO NOT switch this to Thread.getCurrentThread().getStackTrace() 233 | val tag: String? 234 | get() { 235 | val tag = super.tag 236 | if (tag != null) { 237 | return tag 238 | } 239 | val stackTrace = Throwable().stackTrace 240 | if (stackTrace.size <= CALL_STACK_INDEX) { 241 | throw IllegalStateException( 242 | "Synthetic stacktrace didn't have enough elements: are you using proguard?") 243 | } 244 | return createStackElementTag(stackTrace[CALL_STACK_INDEX]) 245 | } 246 | 247 | /** 248 | * Extract the tag which should be used for the message from the `element`. By default 249 | * this will use the class name without any anonymous class suffixes (e.g., `Foo$1` 250 | * becomes `Foo`). 251 | * 252 | * 253 | * Note: This will not be called if a [manual tag][.tag] was specified. 254 | */ 255 | private fun createStackElementTag(element: StackTraceElement): String { 256 | var tag = element.className 257 | val m = ANONYMOUS_CLASS.matcher(tag) 258 | if (m.find()) { 259 | tag = m.replaceAll("") 260 | } 261 | tag = tag.substring(tag.lastIndexOf('.') + 1) 262 | return if (tag.length > MAX_TAG_LENGTH) tag.substring(0, MAX_TAG_LENGTH) else tag 263 | } 264 | 265 | /** 266 | * Break up `message` into maximum-length chunks (if needed) and send to either 267 | * [Log.println()][Log.println] or 268 | * [Log.wtf()][Log.wtf] for logging. 269 | * 270 | * 271 | * {@inheritDoc} 272 | */ 273 | override fun log(priority: Int, tag: String?, message: Any, t: Throwable?) { 274 | var messageString = message.toString() 275 | if (message is Exception) { 276 | messageString = message.cause.toString() 277 | } 278 | 279 | 280 | if (messageString.length < MAX_LOG_LENGTH) { 281 | if (priority == Log.ASSERT) { 282 | Log.wtf(tag, messageString) 283 | } else { 284 | Log.println(priority, tag, messageString) 285 | } 286 | return 287 | } 288 | 289 | // Split by line, then ensure each line can fit into Log's maximum length. 290 | var i = 0 291 | val length = messageString.length 292 | while (i < length) { 293 | var newline = messageString.indexOf('\n', i) 294 | newline = if (newline != -1) newline else length 295 | do { 296 | val end = Math.min(newline, i + MAX_LOG_LENGTH) 297 | val part = messageString.substring(i, end) 298 | if (priority == Log.ASSERT) { 299 | Log.wtf(tag, part) 300 | } else { 301 | Log.println(priority, tag, part) 302 | } 303 | i = end 304 | } while (i < newline) 305 | i++ 306 | } 307 | } 308 | 309 | companion object { 310 | private val MAX_LOG_LENGTH = 4000 311 | private val MAX_TAG_LENGTH = 23 312 | private val CALL_STACK_INDEX = 5 313 | private val ANONYMOUS_CLASS = Pattern.compile("(\\$\\d+)+$") 314 | } 315 | } 316 | 317 | 318 | private val TREE_ARRAY_EMPTY = arrayOfNulls(0) 319 | // Both fields guarded by 'FOREST'. 320 | private val FOREST = ArrayList() 321 | @Volatile private var forestAsArray = TREE_ARRAY_EMPTY 322 | /** 323 | * A [Tree] that delegates to all planted trees in the [forest][.FOREST]. 324 | */ 325 | internal val TREE_OF_SOULS = object : Tree() { 326 | override fun v(message: Any?, vararg args: Any) { 327 | val forest = forestAsArray 328 | 329 | var i = 0 330 | val count = forest.size 331 | while (i < count) { 332 | forest[i]?.v(message, *args) 333 | i++ 334 | } 335 | } 336 | 337 | override fun v(t: Throwable, message: Any, vararg args: Any) { 338 | val forest = forestAsArray 339 | 340 | var i = 0 341 | val count = forest.size 342 | while (i < count) { 343 | forest[i]?.v(t, message, *args) 344 | i++ 345 | } 346 | } 347 | 348 | override fun v(t: Throwable) { 349 | val forest = forestAsArray 350 | 351 | var i = 0 352 | val count = forest.size 353 | while (i < count) { 354 | forest[i]?.v(t) 355 | i++ 356 | } 357 | } 358 | 359 | override fun d(message: Any, vararg args: Any) { 360 | val forest = forestAsArray 361 | 362 | var i = 0 363 | val count = forest.size 364 | while (i < count) { 365 | forest[i]?.d(message, *args) 366 | i++ 367 | } 368 | } 369 | 370 | override fun d(t: Throwable, message: Any, vararg args: Any) { 371 | val forest = forestAsArray 372 | 373 | var i = 0 374 | val count = forest.size 375 | while (i < count) { 376 | forest[i]?.d(t, message, *args) 377 | i++ 378 | } 379 | } 380 | 381 | override fun d(t: Throwable) { 382 | val forest = forestAsArray 383 | 384 | var i = 0 385 | val count = forest.size 386 | while (i < count) { 387 | forest[i]?.d(t) 388 | i++ 389 | } 390 | } 391 | 392 | override fun i(message: Any, vararg args: Any) { 393 | val forest = forestAsArray 394 | 395 | var i = 0 396 | val count = forest.size 397 | while (i < count) { 398 | forest[i]?.i(message, *args) 399 | i++ 400 | } 401 | } 402 | 403 | override fun i(t: Throwable, message: Any, vararg args: Any) { 404 | val forest = forestAsArray 405 | 406 | var i = 0 407 | val count = forest.size 408 | while (i < count) { 409 | forest[i]?.i(t, message, *args) 410 | i++ 411 | } 412 | } 413 | 414 | override fun i(t: Throwable) { 415 | val forest = forestAsArray 416 | 417 | var i = 0 418 | val count = forest.size 419 | while (i < count) { 420 | forest[i]?.i(t) 421 | i++ 422 | } 423 | } 424 | 425 | override fun w(message: Any, vararg args: Any) { 426 | val forest = forestAsArray 427 | 428 | var i = 0 429 | val count = forest.size 430 | while (i < count) { 431 | forest[i]?.w(message, *args) 432 | i++ 433 | } 434 | } 435 | 436 | override fun w(t: Throwable, message: Any, vararg args: Any) { 437 | val forest = forestAsArray 438 | 439 | var i = 0 440 | val count = forest.size 441 | while (i < count) { 442 | forest[i]?.w(t, message, *args) 443 | i++ 444 | } 445 | } 446 | 447 | override fun w(t: Throwable) { 448 | val forest = forestAsArray 449 | 450 | var i = 0 451 | val count = forest.size 452 | while (i < count) { 453 | forest[i]?.w(t) 454 | i++ 455 | } 456 | } 457 | 458 | override fun e(message: Any, vararg args: Any) { 459 | val forest = forestAsArray 460 | 461 | var i = 0 462 | val count = forest.size 463 | while (i < count) { 464 | forest[i]?.e(message, *args) 465 | i++ 466 | } 467 | } 468 | 469 | override fun e(t: Throwable, message: Any, vararg args: Any) { 470 | val forest = forestAsArray 471 | 472 | var i = 0 473 | val count = forest.size 474 | while (i < count) { 475 | forest[i]?.e(t, message, *args) 476 | i++ 477 | } 478 | } 479 | 480 | override fun e(t: Throwable) { 481 | val forest = forestAsArray 482 | 483 | var i = 0 484 | val count = forest.size 485 | while (i < count) { 486 | forest[i]?.e(t) 487 | i++ 488 | } 489 | } 490 | 491 | override fun wtf(message: Any, vararg args: Any) { 492 | val forest = forestAsArray 493 | 494 | var i = 0 495 | val count = forest.size 496 | while (i < count) { 497 | forest[i]?.wtf(message, *args) 498 | i++ 499 | } 500 | } 501 | 502 | override fun wtf(t: Throwable, message: Any, vararg args: Any) { 503 | val forest = forestAsArray 504 | 505 | var i = 0 506 | val count = forest.size 507 | while (i < count) { 508 | forest[i]?.wtf(t, message, *args) 509 | i++ 510 | } 511 | } 512 | 513 | override fun wtf(t: Throwable) { 514 | val forest = forestAsArray 515 | 516 | var i = 0 517 | val count = forest.size 518 | while (i < count) { 519 | forest[i]?.wtf(t) 520 | i++ 521 | } 522 | } 523 | 524 | override fun log(priority: Int, message: Any, vararg args: Any) { 525 | val forest = forestAsArray 526 | 527 | var i = 0 528 | val count = forest.size 529 | while (i < count) { 530 | forest[i]?.log(priority, message, *args) 531 | i++ 532 | } 533 | } 534 | 535 | override fun log(priority: Int, t: Throwable, message: Any, vararg args: Any) { 536 | val forest = forestAsArray 537 | 538 | var i = 0 539 | val count = forest.size 540 | while (i < count) { 541 | forest[i]?.log(priority, t, message, *args) 542 | i++ 543 | } 544 | } 545 | 546 | override fun log(priority: Int, t: Throwable) { 547 | val forest = forestAsArray 548 | 549 | var i = 0 550 | val count = forest.size 551 | while (i < count) { 552 | forest[i]?.log(priority, t) 553 | i++ 554 | } 555 | } 556 | 557 | override fun log(priority: Int, tag: String?, message: Any, t: Throwable?) { 558 | throw AssertionError("Missing override for log method.") 559 | } 560 | } 561 | 562 | /** 563 | * Log a verbose message workWith optional format args. 564 | */ 565 | fun v(message: Any, vararg args: Any) { 566 | TREE_OF_SOULS.v(message, *args) 567 | } 568 | 569 | /** 570 | * Log a verbose exception and a message workWith optional format args. 571 | */ 572 | fun v(t: Throwable, message: Any, vararg args: Any) { 573 | TREE_OF_SOULS.v(t, message, *args) 574 | } 575 | 576 | /** 577 | * Log a verbose exception. 578 | */ 579 | fun v(t: Throwable) { 580 | TREE_OF_SOULS.v(t) 581 | } 582 | 583 | /** 584 | * Log a debug message workWith optional format args. 585 | */ 586 | fun d( 587 | message: Any, 588 | vararg args: Any 589 | ) { 590 | TREE_OF_SOULS.d(message, *args) 591 | } 592 | 593 | /** 594 | * Log a debug exception and a message workWith optional format args. 595 | */ 596 | fun d(t: Throwable, message: Any, vararg args: Any) { 597 | TREE_OF_SOULS.d(t, message, *args) 598 | } 599 | 600 | /** 601 | * Log a debug exception. 602 | */ 603 | fun d(t: Throwable) { 604 | TREE_OF_SOULS.d(t) 605 | } 606 | 607 | /** 608 | * Log an info message workWith optional format args. 609 | */ 610 | fun i(message: Any, vararg args: Any) { 611 | TREE_OF_SOULS.i(message, *args) 612 | } 613 | 614 | /** 615 | * Log an info exception and a message workWith optional format args. 616 | */ 617 | fun i(t: Throwable, message: Any, vararg args: Any) { 618 | TREE_OF_SOULS.i(t, message, *args) 619 | } 620 | 621 | /** 622 | * Log an info exception. 623 | */ 624 | fun i(t: Throwable) { 625 | TREE_OF_SOULS.i(t) 626 | } 627 | 628 | /** 629 | * Log a warning message workWith optional format args. 630 | */ 631 | fun w(message: Any, vararg args: Any) { 632 | TREE_OF_SOULS.w(message, *args) 633 | } 634 | 635 | /** 636 | * Log a warning exception and a message workWith optional format args. 637 | */ 638 | fun w(t: Throwable, message: Any, vararg args: Any) { 639 | TREE_OF_SOULS.w(t, message, *args) 640 | } 641 | 642 | /** 643 | * Log a warning exception. 644 | */ 645 | fun w(t: Throwable) { 646 | TREE_OF_SOULS.w(t) 647 | } 648 | 649 | /** 650 | * Log an error message workWith optional format args. 651 | */ 652 | fun e(message: Any, vararg args: Any) { 653 | TREE_OF_SOULS.e(message, *args) 654 | } 655 | 656 | /** 657 | * Log an error exception and a message workWith optional format args. 658 | */ 659 | fun e(t: Throwable, message: Any, vararg args: Any) { 660 | TREE_OF_SOULS.e(t, message, *args) 661 | } 662 | 663 | /** 664 | * Log an error exception. 665 | */ 666 | fun e(t: Throwable) { 667 | TREE_OF_SOULS.e(t) 668 | } 669 | 670 | /** 671 | * Log an error exception. 672 | */ 673 | fun e(e: Exception) { 674 | if (e.cause == null) { 675 | TREE_OF_SOULS.e(e.message ?: "") 676 | } else { 677 | TREE_OF_SOULS.e(e.cause!!) 678 | } 679 | } 680 | 681 | /** 682 | * Log an assert message workWith optional format args. 683 | */ 684 | fun wtf(message: Any, vararg args: Any) { 685 | TREE_OF_SOULS.wtf(message, *args) 686 | } 687 | 688 | /** 689 | * Log an assert exception and a message workWith optional format args. 690 | */ 691 | fun wtf(t: Throwable, message: Any, vararg args: Any) { 692 | TREE_OF_SOULS.wtf(t, message, *args) 693 | } 694 | 695 | /** 696 | * Log an assert exception. 697 | */ 698 | fun wtf(t: Throwable) { 699 | TREE_OF_SOULS.wtf(t) 700 | } 701 | 702 | /** 703 | * Log at `priority` a message workWith optional format args. 704 | */ 705 | fun log(priority: Int, message: Any, vararg args: Any) { 706 | TREE_OF_SOULS.log(priority, message, *args) 707 | } 708 | 709 | /** 710 | * Log at `priority` an exception and a message workWith optional format args. 711 | */ 712 | fun log(priority: Int, t: Throwable, message: Any, vararg args: Any) { 713 | TREE_OF_SOULS.log(priority, t, message, *args) 714 | } 715 | 716 | /** 717 | * Log at `priority` an exception. 718 | */ 719 | fun log(priority: Int, t: Throwable) { 720 | TREE_OF_SOULS.log(priority, t) 721 | } 722 | 723 | /** 724 | * A view into Timber's planted trees as a tree itself. This can be used for injecting a logger 725 | * instance rather than using static methods or to facilitate testing. 726 | */ 727 | fun asTree() = TREE_OF_SOULS 728 | 729 | /** 730 | * Set a one-time tag for use on the next logging call. 731 | */ 732 | fun tag(tag: String): Tree { 733 | val forest = forestAsArray 734 | 735 | var i = 0 736 | val count = forest.size 737 | while (i < count) { 738 | forest[i]?.explicitTag?.set(tag) 739 | i++ 740 | } 741 | return TREE_OF_SOULS 742 | } 743 | 744 | /** 745 | * Add a new logging tree. 746 | */ 747 | fun plant(tree: Tree) { 748 | if (tree === TREE_OF_SOULS) { 749 | throw IllegalArgumentException("Cannot plant Timber into itself.") 750 | } 751 | synchronized(FOREST) { 752 | FOREST.add(tree) 753 | forestAsArray = FOREST.toTypedArray() 754 | } 755 | } 756 | 757 | /** 758 | * Adds new logging trees. 759 | */ 760 | fun plant(vararg trees: Tree) { 761 | 762 | trees 763 | .filter { it === TREE_OF_SOULS } 764 | .forEach { throw IllegalArgumentException("Cannot plant Timber into itself.") } 765 | synchronized(FOREST) { 766 | Collections.addAll(FOREST, *trees) 767 | forestAsArray = FOREST.toTypedArray() 768 | } 769 | } 770 | 771 | 772 | /** 773 | * Remove a planted tree. 774 | */ 775 | fun uproot(tree: Tree) { 776 | 777 | synchronized(FOREST) { 778 | if (!FOREST.remove(tree)) { 779 | throw IllegalArgumentException("Cannot uproot tree which is not planted: " + tree) 780 | } 781 | forestAsArray = FOREST.toTypedArray() 782 | } 783 | } 784 | 785 | /** 786 | * Remove all planted trees. 787 | */ 788 | fun uprootAll() { 789 | synchronized(FOREST) { 790 | FOREST.clear() 791 | forestAsArray = TREE_ARRAY_EMPTY 792 | } 793 | } 794 | 795 | /** 796 | * Return a copy of all planted [trees][Tree]. 797 | */ 798 | fun forest(): List { 799 | synchronized(FOREST) { 800 | return unmodifiableList(ArrayList(FOREST)) 801 | } 802 | } 803 | 804 | fun treeCount(): Int { 805 | synchronized(FOREST) { 806 | return FOREST.size 807 | } 808 | } 809 | } 810 | 811 | @Suppress("unused") 812 | fun Any?.d() = Timber.TREE_OF_SOULS.d(this ?: "null") 813 | 814 | @Suppress("unused") 815 | fun Any?.e() = Timber.TREE_OF_SOULS.e(this ?: "null") 816 | 817 | @Suppress("unused") 818 | fun Any?.i() = Timber.TREE_OF_SOULS.i(this ?: "null") 819 | 820 | @Suppress("unused") 821 | fun Any?.wtf() = Timber.TREE_OF_SOULS.wtf(this ?: "null") 822 | 823 | @Suppress("unused") 824 | fun Any?.w() = Timber.TREE_OF_SOULS.w(this ?: "null") 825 | -------------------------------------------------------------------------------- /timber/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Timber 3 | 4 | -------------------------------------------------------------------------------- /timber/src/test/java/goldzweigapps/com/timber/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package goldzweigapps.com.timber; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.assertEquals; 6 | 7 | /** 8 | * Example local unit test, which will execute on the development machine (host). 9 | * 10 | * @see Testing documentation 11 | */ 12 | public class ExampleUnitTest { 13 | @Test 14 | public void addition_isCorrect() throws Exception { 15 | assertEquals(4, 2 + 2); 16 | } 17 | } --------------------------------------------------------------------------------