├── .gitignore ├── CHANGELOG_LIFECYCLE_EXTENSIONS.md ├── LICENSE ├── README.md ├── README_LIFECYCLE_OBSERVER.md ├── README_VIEW_MODEL.md ├── build.gradle ├── gradle.properties ├── gradle ├── publish.gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── images └── AAC-ViewModel.png ├── lifecycle-extensions ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── tech │ │ └── thdev │ │ └── lifecycle │ │ └── extensions │ │ └── ExampleInstrumentedTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── tech │ │ │ └── thdev │ │ │ └── lifecycle │ │ │ └── extensions │ │ │ ├── observer │ │ │ ├── LifecycleObserverExtensions.kt │ │ │ ├── LifecycleObserverExtensionsJava.kt │ │ │ └── LifecycleObserverExtensionsLazy.kt │ │ │ └── viewmodel │ │ │ ├── ViewModelCustomKeyUtil.kt │ │ │ ├── ViewModelExtensions.kt │ │ │ ├── ViewModelExtensionsJava.kt │ │ │ └── ViewModelExtensionsLazy.kt │ └── res │ │ └── values │ │ └── strings.xml │ └── test │ └── java │ └── tech │ └── thdev │ └── lifecycle │ └── extensions │ └── ExampleUnitTest.java ├── sampleapp ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── tech │ │ └── thdev │ │ └── lifecycleextensions │ │ └── ExampleInstrumentedTest.kt │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── tech │ │ │ └── thdev │ │ │ └── lifecycleextensions │ │ │ ├── java │ │ │ └── view │ │ │ │ └── JavaMainActivity.java │ │ │ ├── observer │ │ │ └── MainLifecycleObserver.kt │ │ │ └── view │ │ │ ├── MainActivity.kt │ │ │ ├── home │ │ │ ├── HomeFragment.kt │ │ │ └── viewmodel │ │ │ │ └── HomeViewModel.kt │ │ │ └── time │ │ │ ├── TimeFragment.kt │ │ │ └── viewmodel │ │ │ └── TimeViewModel.kt │ └── res │ │ ├── drawable │ │ ├── ic_access_time_black_24dp.xml │ │ ├── ic_home_black_24dp.xml │ │ ├── ic_launcher_background.xml │ │ └── ic_plus_one_black_24dp.xml │ │ ├── layout │ │ ├── activity_main.xml │ │ ├── fragment_home.xml │ │ └── fragment_time.xml │ │ ├── menu │ │ └── navigation.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 │ └── tech │ └── thdev │ └── lifecycleextensions │ └── ExampleUnitTest.kt └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /captures 3 | .externalNativeBuild 4 | ### Android template 5 | # Built application files 6 | *.apk 7 | *.ap_ 8 | 9 | # Files for the ART/Dalvik VM 10 | *.dex 11 | 12 | # Java class files 13 | *.class 14 | 15 | # Generated files 16 | bin/ 17 | gen/ 18 | out/ 19 | 20 | # Gradle files 21 | .gradle/ 22 | build/ 23 | 24 | # Local configuration file (sdk path, etc) 25 | local.properties 26 | 27 | # Proguard folder generated by Eclipse 28 | proguard/ 29 | 30 | # Log Files 31 | *.log 32 | 33 | # Android Studio Navigation editor temp files 34 | .navigation/ 35 | 36 | # Android Studio captures folder 37 | captures/ 38 | 39 | # Intellij 40 | *.iml 41 | .idea/ 42 | 43 | # Keystore files 44 | *.jks 45 | 46 | # External native build folder generated in Android Studio 2.2 and later 47 | #.externalNativeBuild 48 | 49 | # Google Services (e.g. APIs or Firebase) 50 | google-services.json 51 | 52 | # Freeline 53 | freeline.py 54 | freeline/ 55 | freeline_project_description.json 56 | 57 | -------------------------------------------------------------------------------- /CHANGELOG_LIFECYCLE_EXTENSIONS.md: -------------------------------------------------------------------------------- 1 | ## Change Log 2 | 3 | ### Version 2.4.2 (2019-01-09) 4 | - Fix Cannot create an instance of class android.arch.lifecycle.s tech.thdev.lifecycle.extensions.viewmodel.ViewModelExtensionsKt$createViewModel$1.create 5 | 6 | ### Version 2.4.1 (2018-11-14) 7 | - Update kotlin 1.3.10/coroutines 1.0.1 8 | 9 | ### Version 2.4.0 (2018-11-13) 10 | - Update kotlin version(1.3.0) 11 | - Update build version(3.2.1) 12 | 13 | ### Version 2.3.0 (2018-09-10) 14 | - Add new api : LifecycleObserver simple inject 15 | 16 | ex) only java sample 17 | 18 | ``` 19 | mainObserver = LifecycleObserverExtensions.inject(this, new LifecycleObserverCreate() { 20 | @NotNull 21 | @Override 22 | public MainObserver onCreateLifecycleObserver() { 23 | return new MainObserver(); 24 | } 25 | }); 26 | ``` 27 | 28 | - Java create interface change : ViewModelHelper<> -> ViewModelCreate<> 29 | 30 | 31 | ### Version 2.2.0 (2018-08-10) 32 | 33 | - Split Java method. 34 | - Clarification of the java method 35 | 36 | ex) only java method. 37 | 38 | ``` 39 | viewModel = ViewModelExtensions.inject(this, MyViewModel.class, new ViewModelHelper() { 40 | @NotNull 41 | @Override 42 | public MainViewModel onCreateViewModel() { 43 | return new MyViewModel(..., ..., ...); 44 | } 45 | }); 46 | ``` 47 | 48 | 49 | ### Version 2.1.1 (2018-08-04) 50 | 51 | - Just version change. 52 | 53 | 54 | ### Version 2.1.0-beta01 (2018-08-03) 55 | 56 | - Add lazyInject 57 | 58 | ``` 59 | private val viewModel: YourClassViewModel by /* activity or fragment default this */.lazyInject(/* customKey */) { 60 | YourClassViewModel().apply { 61 | // More ViewModel init. 62 | } 63 | } 64 | ``` 65 | 66 | - Change initialized 67 | 68 | ``` 69 | val viewModel = /* activity or fragment default this */.inject { 70 | YourClassViewModel().apply { 71 | // More ViewModel init. 72 | } 73 | } 74 | ``` 75 | 76 | 77 | ### Version 2.0.0-beta01 (2018-08-03) 78 | 79 | - Version update. 80 | - Change initialized in lifecycle extensions. 81 | 82 | ``` 83 | YourClassViewModel::class.java.inject(this) { 84 | // Your class inject. 85 | 86 | YourClassViewModel().apply { 87 | // ViewModel init 88 | } 89 | } 90 | ``` 91 | 92 | 93 | ### Version 1.1.0 (2018-06-05) 94 | 95 | - Update android new package. androidx 96 | - Update lifecycle library. 97 | - Android appcompat version 1.0.0-alpha1 98 | 99 | 100 | ### Version 1.0.0-alpha3 (2017-07-27) 101 | 102 | - Fixed syntax. 103 | 104 | 105 | ### Version 1.0.0-alpha2 (2017-07-26) 106 | 107 | - Update buildToolsVersion(26.0.1) 108 | - Code refactoring 109 | 110 | 111 | ### Version 1.0.0-alpha1 (2017-07-24) 112 | 113 | - It provides an Inject that simplifies the use of the ViewModel.Factory of Android Architecture Components. -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Android Architecture Components Extensions 2 | [![License](https://img.shields.io/hexpm/l/plug.svg)]() 3 | 4 | 5 | This is the Android Architecture Components extension library available for kotlin. 6 | 7 | I used kotlin infix notation for the following code. 8 | 9 | ``` 10 | ViewModelProviders.of(this, object : ViewModelProvider.Factory { 11 | override fun create(modelClass: Class?): T { 12 | return MyViewModel(..., ..., ...) as T 13 | } 14 | }).get(MyViewModel::class.java) 15 | ``` 16 | 17 | 18 | ## Library version 19 | 20 | It can be used jcenter(), as follows 21 | 22 | ### AndroidX package 23 | 24 | [ ![Download](https://api.bintray.com/packages/taehwandev/thdev.tech/lifecycle-extensions/images/download.svg) ](https://bintray.com/taehwandev/thdev.tech/lifecycle-extensions/_latestVersion) 25 | 26 | ``` 27 | implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.10' 28 | implementation 'androidx.appcompat:appcompat:1.0.2' 29 | 30 | implementation 'androidx.lifecycle:lifecycle-extensions:2.0.0' 31 | implementation "tech.thdev.lifecycle:lifecycle-extensions:$latestVersion" 32 | ``` 33 | 34 | ### Legacy package 35 | 36 | [ ![Download](https://api.bintray.com/packages/taehwandev/thdev.tech/lifecycle-extensions-legacy/images/download.svg) ](https://bintray.com/taehwandev/thdev.tech/lifecycle-extensions-legacy/_latestVersion) 37 | 38 | ``` 39 | implementation 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.10' 40 | implementation 'com.android.support:appcompat-v7:27.1.1' 41 | 42 | implementation 'android.arch.lifecycle:extensions:1.1.1' 43 | implementation "tech.thdev.lifecycle:lifecycle-extensions-legacy:$latestLegacyVersion" 44 | ``` 45 | 46 | ## Diagram 47 | 48 | ![](./images/AAC-ViewModel.png) 49 | 50 | 51 | ## Blog 52 | 53 | 한글 Post - [Android Architecture Components ViewModel을 간단하게 초기화 하려면?](https://thdev.tech/androiddev/2018/08/05/Android-Architecture-Components-ViewModel-Inject/) 54 | 55 | 56 | ## Use api 57 | 58 | ### [Guide AAC - ViewModel](README_VIEW_MODEL.md) 59 | 60 | ```kotlin 61 | class MainActivity : AppCompatActivity() { 62 | 63 | private val viewModel: MyViewModel 64 | by lazyInjectViewModel(/* @Option customKey = "custom key" */) { 65 | // create Your ViewModel 66 | MyViewModel(..., ..., ...) 67 | } 68 | 69 | override fun onCreate(savedInstanceState: Bundle?) { 70 | // Maybe init view model 71 | viewModel ... 72 | } 73 | } 74 | ``` 75 | 76 | ### [Guide AAC - LifecycleObserver](README_LIFECYCLE_OBSERVER.md) 77 | 78 | ```kotlin 79 | class MainActivity : AppCompatActivity() { 80 | 81 | private val observer: MyLifecycleObserver by injectAutoLifecycle { 82 | // create Your Observer 83 | MyLifecycleObserver(..., ..., ...) 84 | } 85 | 86 | override fun onCreate(savedInstanceState: Bundle?) { 87 | // use lifecycle 88 | } 89 | } 90 | ``` 91 | 92 | 93 | ## License 94 | 95 | ``` 96 | Copyright 2017-2018 Tae-hwan 97 | 98 | Licensed under the Apache License, Version 2.0 (the "License"); 99 | you may not use this file except in compliance with the License. 100 | You may obtain a copy of the License at 101 | 102 | http://www.apache.org/licenses/LICENSE-2.0 103 | 104 | Unless required by applicable law or agreed to in writing, software 105 | distributed under the License is distributed on an "AS IS" BASIS, 106 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 107 | See the License for the specific language governing permissions and 108 | limitations under the License. 109 | ``` 110 | -------------------------------------------------------------------------------- /README_LIFECYCLE_OBSERVER.md: -------------------------------------------------------------------------------- 1 | # Android Architecture Components Extensions 2 | [![License](https://img.shields.io/hexpm/l/plug.svg)]() 3 | 4 | 5 | This is the Android Architecture Components extension library available for kotlin. 6 | 7 | 8 | ## Use api - by auto inject 9 | 10 | Use AAC-LifecycleObserver lazy initialization 11 | 12 | ### Activity inject 13 | 14 | Use LifeCycle activity 15 | 16 | ```kotlin 17 | class MainActivity : AppCompatActivity() { 18 | 19 | private val observer: MyLifecycleObserver by injectAutoLifecycle { 20 | // create Your Observer 21 | MyLifecycleObserver(..., ..., ...) 22 | } 23 | 24 | override fun onCreate(savedInstanceState: Bundle?) { 25 | // use lifecycle 26 | } 27 | } 28 | ``` 29 | 30 | ### Fragment inject 31 | 32 | Use LifeCycle fragment 33 | 34 | ```kotlin 35 | class MainFragment : Fragment() { 36 | 37 | private val observer: MyLifecycleObserver by injectAutoLifecycle { 38 | // create Your Observer 39 | MyLifecycleObserver(..., ..., ...) 40 | } 41 | 42 | override fun onActivityCreated(savedInstanceState: Bundle?) { 43 | super.onActivityCreated(savedInstanceState) 44 | 45 | // use lifecycle 46 | } 47 | } 48 | ``` 49 | 50 | ### Fragment - use activity inject 51 | 52 | Use cache activity 53 | 54 | ```kotlin 55 | class MainFragment : Fragment() { 56 | 57 | private val observer: MyLifecycleObserver by injectAutoLifecycle(isActivity = true) { 58 | // create Your Observer 59 | MyLifecycleObserver(..., ..., ...) 60 | } 61 | 62 | override fun onActivityCreated(savedInstanceState: Bundle?) { 63 | super.onActivityCreated(savedInstanceState) 64 | 65 | // use lifecycle 66 | } 67 | } 68 | ``` 69 | 70 | 71 | ## Use api - by lateinit only kotlin. 72 | 73 | ### Activity inject 74 | 75 | Use cache activity 76 | 77 | ```kotlin 78 | class MainActivity : AppCompatActivity() { 79 | 80 | private lateinit var observer: MyLifecycleObserver 81 | 82 | override fun onCreate(savedInstanceState: Bundle?) { 83 | // ... 84 | observer = injectLifecycle { 85 | // create Your Observer 86 | MyLifecycleObserver(..., ..., ...) 87 | } 88 | } 89 | } 90 | ``` 91 | 92 | ### Fragment inject 93 | 94 | Use cache fragment 95 | 96 | ```kotlin 97 | class MainFragment : Fragment() { 98 | 99 | private lateinit var observer: MyLifecycleObserver 100 | 101 | override fun onActivityCreated(savedInstanceState: Bundle?) { 102 | super.onActivityCreated(savedInstanceState) 103 | 104 | observer = injectLifecycle { 105 | // create Your Observer 106 | MyLifecycleObserver(..., ..., ...) 107 | } 108 | } 109 | } 110 | ``` 111 | 112 | ### Fragment - use activity inject 113 | 114 | Use cache activity 115 | 116 | ```kotlin 117 | class MainFragment : Fragment() { 118 | 119 | private lateinit var observer: MyLifecycleObserver 120 | 121 | override fun onActivityCreated(savedInstanceState: Bundle?) { 122 | super.onActivityCreated(savedInstanceState) 123 | 124 | observer = requireActivity().injectLifecycle { 125 | // create Your Observer 126 | MyLifecycleObserver(..., ..., ...) 127 | } 128 | } 129 | } 130 | ``` 131 | 132 | ## Use api - by inject only Java. 133 | 134 | Use AAC-LifecycleObserver java 135 | 136 | ### Activity inject 137 | 138 | Use cache activity 139 | 140 | ```java 141 | public class MainActivity extends AppCompatActivity { 142 | 143 | private MyLifecycleObserver observer; 144 | 145 | @Override 146 | protected void onCreate(@Nullable Bundle savedInstanceState) { 147 | super.onCreate(savedInstanceState); 148 | 149 | observer = LifecycleObserverExtensions.injectLifecycle( 150 | this, 151 | new LifecycleObserverCreate() { 152 | 153 | @NotNull 154 | @Override 155 | public MainLifecycleObserver onCreateLifecycleObserver() { 156 | return new MyLifecycleObserver(); 157 | } 158 | }); 159 | } 160 | } 161 | ``` 162 | 163 | ### Fragment inject 164 | 165 | Use cache fragment 166 | 167 | ```java 168 | public class Sample extends Fragment { 169 | 170 | private MyLifecycleObserver observer; 171 | 172 | @Override 173 | public void onActivityCreated(@Nullable Bundle savedInstanceState) { 174 | super.onActivityCreated(savedInstanceState); 175 | 176 | observer = LifecycleObserverExtensions.injectLifecycle( 177 | this, 178 | new LifecycleObserverCreate() { 179 | 180 | @NotNull 181 | @Override 182 | public MainLifecycleObserver onCreateLifecycleObserver() { 183 | return new MyLifecycleObserver(); 184 | } 185 | }); 186 | } 187 | } 188 | ``` 189 | 190 | 191 | ### Fragment - use activity inject 192 | 193 | Use cache activity 194 | 195 | ```java 196 | public class Sample extends Fragment { 197 | 198 | private MyLifecycleObserver observer; 199 | 200 | @Override 201 | public void onActivityCreated(@Nullable Bundle savedInstanceState) { 202 | super.onActivityCreated(savedInstanceState); 203 | 204 | observer = LifecycleObserverExtensions.injectLifecycle( 205 | requireActivity(), 206 | new LifecycleObserverCreate() { 207 | 208 | @NotNull 209 | @Override 210 | public MainLifecycleObserver onCreateLifecycleObserver() { 211 | return new MyLifecycleObserver(); 212 | } 213 | }); 214 | } 215 | } 216 | ``` 217 | 218 | ## 219 | 220 | ## License 221 | 222 | ``` 223 | Copyright 2017-2018 Tae-hwan 224 | 225 | Licensed under the Apache License, Version 2.0 (the "License"); 226 | you may not use this file except in compliance with the License. 227 | You may obtain a copy of the License at 228 | 229 | http://www.apache.org/licenses/LICENSE-2.0 230 | 231 | Unless required by applicable law or agreed to in writing, software 232 | distributed under the License is distributed on an "AS IS" BASIS, 233 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 234 | See the License for the specific language governing permissions and 235 | limitations under the License. 236 | ``` 237 | -------------------------------------------------------------------------------- /README_VIEW_MODEL.md: -------------------------------------------------------------------------------- 1 | # Android Architecture Components Extensions 2 | [![License](https://img.shields.io/hexpm/l/plug.svg)]() 3 | 4 | 5 | This is the Android Architecture Components extension library available for kotlin. 6 | 7 | I used kotlin infix notation for the following code. 8 | 9 | ``` 10 | ViewModelProviders.of(this, object : ViewModelProvider.Factory { 11 | override fun create(modelClass: Class?): T { 12 | return MyViewModel(..., ..., ...) as T 13 | } 14 | }).get(MyViewModel::class.java) 15 | ``` 16 | 17 | ## Diagram 18 | 19 | ![](./images/AAC-ViewModel.png) 20 | 21 | 22 | ## Blog 23 | 24 | 한글 Post - [Android Architecture Components ViewModel을 간단하게 초기화 하려면?](https://thdev.tech/androiddev/2018/08/05/Android-Architecture-Components-ViewModel-Inject/) 25 | 26 | 27 | 28 | ## Use api - by lazy patten 29 | 30 | Use AAC-ViewModel lazy initialization 31 | 32 | ### Activity inject 33 | 34 | Use cache activity 35 | 36 | ```kotlin 37 | class MainActivity : AppCompatActivity() { 38 | 39 | private val viewModel: MyViewModel 40 | by lazyInjectViewModel(/* @Option customKey = "custom key" */) { 41 | // create Your ViewModel 42 | MyViewModel(..., ..., ...) 43 | } 44 | 45 | override fun onCreate(savedInstanceState: Bundle?) { 46 | // Maybe init view model 47 | viewModel ... 48 | } 49 | } 50 | ``` 51 | 52 | ### Fragment inject 53 | 54 | Use cache fragment 55 | 56 | ```kotlin 57 | class MainFragment : Fragment() { 58 | 59 | private val viewModel: MyViewModel 60 | by lazyInjectViewModel(/* @Option customKey = "custom key" */) { 61 | // create Your ViewModel 62 | MyViewModel(..., ..., ...) 63 | } 64 | 65 | override fun onActivityCreated(savedInstanceState: Bundle?) { 66 | super.onActivityCreated(savedInstanceState) 67 | 68 | // Maybe init view model 69 | viewModel ... 70 | } 71 | } 72 | ``` 73 | 74 | ### Fragment - use activity inject 75 | 76 | Use cache activity 77 | 78 | ```kotlin 79 | class MainFragment : Fragment() { 80 | 81 | private val viewModel: MyViewModel 82 | by lazyInjectViewModel( 83 | isActivity = true 84 | /* @Option , customKey = "custom key" */) { 85 | // create Your ViewModel 86 | MyViewModel(..., ..., ...) 87 | } 88 | 89 | override fun onActivityCreated(savedInstanceState: Bundle?) { 90 | super.onActivityCreated(savedInstanceState) 91 | 92 | // Maybe init view model 93 | viewModel ... 94 | } 95 | } 96 | ``` 97 | 98 | 99 | ## Use api - by inject only kotlin. 100 | 101 | Used initializing lateinit. 102 | 103 | ### Activity inject 104 | 105 | Use cache activity 106 | 107 | ```kotlin 108 | class MainActivity : AppCompatActivity() { 109 | 110 | private lateinit var viewModel: MyViewModel 111 | 112 | override fun onCreate(savedInstanceState: Bundle?) { 113 | // ... 114 | viewModel = injectViewModel(/* @Option customKey = "custom key" */) { 115 | // create Your ViewModel 116 | MyViewModel(..., ..., ...) 117 | }.run { 118 | // Maybe init view model 119 | } 120 | } 121 | } 122 | ``` 123 | 124 | ### Fragment inject 125 | 126 | Use cache fragment 127 | 128 | ```kotlin 129 | class MainFragment : Fragment() { 130 | 131 | private lateinit var viewModel: MyViewModel 132 | 133 | override fun onActivityCreated(savedInstanceState: Bundle?) { 134 | super.onActivityCreated(savedInstanceState) 135 | 136 | viewModel = injectViewModel( 137 | /* @Option customKey = "custom key" */) { 138 | // create Your ViewModel 139 | MyViewModel(..., ..., ...) 140 | }.run { 141 | // Maybe init view model 142 | } 143 | } 144 | } 145 | ``` 146 | 147 | ### Fragment - use activity inject 148 | 149 | Use cache activity 150 | 151 | ```kotlin 152 | class MainFragment : Fragment() { 153 | 154 | private lateinit var viewModel: MyViewModel 155 | 156 | override fun onActivityCreated(savedInstanceState: Bundle?) { 157 | super.onActivityCreated(savedInstanceState) 158 | 159 | viewModel = requireActivity().injectViewModel( 160 | /* @Option customKey = "custom key" */) { 161 | // create Your ViewModel 162 | MyViewModel(..., ..., ...) 163 | }.run { 164 | // Maybe init view model 165 | } 166 | } 167 | } 168 | ``` 169 | 170 | ## Use api - by inject only Java. 171 | 172 | Use AAC-ViewModel java 173 | 174 | ### Activity inject 175 | 176 | Use cache activity 177 | 178 | ```java 179 | public class MainActivity extends AppCompatActivity { 180 | 181 | private MyViewModel viewModel; 182 | 183 | @Override 184 | protected void onCreate(@Nullable Bundle savedInstanceState) { 185 | super.onCreate(savedInstanceState); 186 | 187 | viewModel = ViewModelExtensions.injectViewModel( 188 | this, 189 | MyViewModel.class /*, @Option "custom key" */, 190 | new ViewModelCreate() { 191 | 192 | @NotNull 193 | @Override 194 | public MyViewModel onCreateViewModel() { 195 | return new MyViewModel(..., ..., ...); 196 | } 197 | }); 198 | } 199 | } 200 | ``` 201 | 202 | ### Fragment inject 203 | 204 | Use cache fragment 205 | 206 | ```java 207 | public class Sample extends Fragment { 208 | 209 | private MyViewModel viewModel; 210 | 211 | @Override 212 | public void onActivityCreated(@Nullable Bundle savedInstanceState) { 213 | super.onActivityCreated(savedInstanceState); 214 | 215 | viewModel = ViewModelExtensions.injectViewModel( 216 | this, 217 | MyViewModel.class /*, @Option "custom key" */, 218 | new ViewModelCreate() { 219 | 220 | @NotNull 221 | @Override 222 | public MyViewModel onCreateViewModel() { 223 | return new MyViewModel(..., ..., ...); 224 | } 225 | }); 226 | } 227 | } 228 | ``` 229 | 230 | 231 | ### Fragment - use activity inject 232 | 233 | Use cache activity 234 | 235 | ```java 236 | public class Sample extends Fragment { 237 | 238 | private MyViewModel viewModel; 239 | 240 | @Override 241 | public void onActivityCreated(@Nullable Bundle savedInstanceState) { 242 | super.onActivityCreated(savedInstanceState); 243 | 244 | viewModel = ViewModelExtensions.injectViewModel( 245 | requireActivity(), 246 | MyViewModel.class /*, @Option "custom key" */, 247 | new ViewModelCreate() { 248 | 249 | @NotNull 250 | @Override 251 | public MyViewModel onCreateViewModel() { 252 | return new MyViewModel(..., ..., ...); 253 | } 254 | }); 255 | } 256 | } 257 | ``` 258 | 259 | ## 260 | 261 | ## License 262 | 263 | ``` 264 | Copyright 2017-2018 Tae-hwan 265 | 266 | Licensed under the Apache License, Version 2.0 (the "License"); 267 | you may not use this file except in compliance with the License. 268 | You may obtain a copy of the License at 269 | 270 | http://www.apache.org/licenses/LICENSE-2.0 271 | 272 | Unless required by applicable law or agreed to in writing, software 273 | distributed under the License is distributed on an "AS IS" BASIS, 274 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 275 | See the License for the specific language governing permissions and 276 | limitations under the License. 277 | ``` 278 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext { 5 | kotlin_version = '1.3.11' 6 | // https://github.com/Kotlin/kotlinx.coroutines 7 | coroutines_version = '1.1.0' 8 | 9 | androidxSupportLibraryVersion = '1.0.2' 10 | androidxConstraintVersion = '1.1.3' 11 | meterialLibraryVersion = '1.1.0-alpha02' 12 | androidxLifecycle = '2.0.0' 13 | 14 | coroutinesExtensionsVersion = '2.1.1' 15 | 16 | // Bintray setting info 17 | repositoryName = 'thdev.tech' 18 | groupId = 'tech.thdev.lifecycle' 19 | 20 | websiteUrl = 'https://github.com/taehwandev/LifecycleExtensions' 21 | gitHubRepoUrl = 'https://github.com/taehwandev/LifecycleExtensions.git' 22 | 23 | LABELS = ['aar', 'android', 'android lifecycle extensions'] 24 | GITHUB_REPO = 'taehwandev/LifecycleExtensions' 25 | NODE_NAME = 'LifecycleExtensions' 26 | 27 | Properties properties = new Properties() 28 | properties.load(project.rootProject.file('local.properties').newDataInputStream()) 29 | BINTRARY_USER = properties.getProperty('bintray.bintrayUser') != null ? 30 | properties.getProperty('bintray.bintrayUser') : System.getenv('BINTRAY_USER') 31 | BINTRARY_KEY = properties.getProperty('bintray.bintrayApiKey') != null ? 32 | properties.getProperty('bintray.bintrayApiKey') : System.getenv('BINTRAY_API_KEY') 33 | } 34 | repositories { 35 | google() 36 | jcenter() 37 | } 38 | dependencies { 39 | classpath 'com.android.tools.build:gradle:3.2.1' 40 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" 41 | 42 | // https://github.com/dcendents/android-maven-gradle-plugin 43 | classpath 'com.github.dcendents:android-maven-gradle-plugin:2.1' 44 | // https://github.com/bintray/gradle-bintray-plugin 45 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.8.4' 46 | 47 | // NOTE: Do not place your application dependencies here; they belong 48 | // in the individual module build.gradle files 49 | } 50 | } 51 | 52 | allprojects { 53 | repositories { 54 | google() 55 | jcenter() 56 | mavenCentral() 57 | } 58 | } 59 | 60 | task clean(type: Delete) { 61 | delete rootProject.buildDir 62 | } 63 | -------------------------------------------------------------------------------- /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/publish.gradle: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2017. Louis Cognault Ayeva Derman 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | def pomConfig = { 18 | licenses { 19 | license { 20 | name 'The Apache Software License, Version 2.0' 21 | url 'http://www.apache.org/licenses/LICENSE-2.0.txt' 22 | } 23 | } 24 | 25 | scm { // Optional setting 26 | connection gitHubRepoUrl 27 | developerConnection gitHubRepoUrl 28 | url websiteUrl 29 | } 30 | 31 | developers { // Optional setting 32 | developer { 33 | id 'taehwandev' 34 | name 'taehwan' 35 | email 'taehwan@thdev.tech' 36 | } 37 | } 38 | } 39 | 40 | def publicationNames = [] 41 | publishing.publications { 42 | android.libraryVariants.all { variant -> 43 | if (variant.buildType.name == "debug") return // Prevents publishing debug library 44 | 45 | def flavored = !variant.flavorName.isEmpty() 46 | 47 | /** 48 | * Translates "_" in flavor names to "-" for artifactIds, because "-" in flavor name is an 49 | * illegal character, but is well used in artifactId names. 50 | */ 51 | def baseVariantArtifactId = flavored ? variant.flavorName.replace('_', '-') : project.name 52 | def variantArtifactId = "$baseVariantArtifactId" 53 | 54 | println variantArtifactId 55 | 56 | /** 57 | * If the javadoc destinationDir wasn't changed per flavor, the libraryVariants would 58 | * overwrite the javaDoc as all variants would write in the same directory 59 | * before the last javadoc jar would have been built, which would cause the last javadoc 60 | * jar to include classes from other flavors that it doesn't include. 61 | * 62 | * Yes, tricky. 63 | * 64 | * Note that "${buildDir}/docs/javadoc" is the default javadoc destinationDir. 65 | */ 66 | def javaDocDestDir = file("${buildDir}/docs/javadoc ${flavored ? variantArtifactId : ""}") 67 | 68 | /** 69 | * Includes 70 | */ 71 | def sourceDirs = variant.sourceSets.collect { 72 | it.javaDirectories // Also includes kotlin sources if any. 73 | } 74 | def javadoc = task("${variant.name}Javadoc", type: Javadoc) { 75 | description "Generates Javadoc for ${variant.name}." 76 | source = variant.javaCompile.source // Yes, javaCompile is deprecated, 77 | // but I didn't find any working alternative. Please, tweet @Louis_CAD if you find one. 78 | destinationDir = javaDocDestDir 79 | classpath += files(android.getBootClasspath().join(File.pathSeparator)) 80 | classpath += files(configurations.compile) 81 | options.links("http://docs.oracle.com/javase/7/docs/api/") 82 | options.links("http://d.android.com/reference/") 83 | exclude '**/BuildConfig.java' 84 | exclude '**/R.java' 85 | failOnError false 86 | } 87 | def javadocJar = task("${variant.name}JavadocJar", type: Jar, dependsOn: javadoc) { 88 | description "Puts Javadoc for ${variant.name} in a jar." 89 | classifier = 'javadoc' 90 | from javadoc.destinationDir 91 | } 92 | def sourcesJar = task("${variant.name}SourcesJar", type: Jar) { 93 | description "Puts sources for ${variant.name} in a jar." 94 | from sourceDirs 95 | classifier = 'sources' 96 | } 97 | 98 | def publicationName = "LifecycleExtensions${variant.name.capitalize()}" 99 | println publicationName 100 | publicationNames.add(publicationName) 101 | 102 | "$publicationName"(MavenPublication) { 103 | artifactId variantArtifactId 104 | group groupId 105 | version libraryVersion 106 | 107 | artifact variant.outputs[0].packageLibrary // This is the aar library 108 | artifact sourcesJar 109 | artifact javadocJar 110 | 111 | pom { 112 | packaging 'aar' 113 | withXml { 114 | def root = asNode() 115 | root.appendNode("name", NODE_NAME) 116 | root.appendNode("url", websiteUrl) 117 | root.children().last() + pomConfig 118 | def depsNode = root["dependencies"][0] ?: root.appendNode("dependencies") 119 | 120 | def addDep = { 121 | if (it.group == null) return // Avoid empty dependency nodes 122 | def dependencyNode = depsNode.appendNode('dependency') 123 | dependencyNode.appendNode('groupId', it.group) 124 | dependencyNode.appendNode('artifactId', it.name) 125 | dependencyNode.appendNode('version', it.version) 126 | if (it.hasProperty('optional') && it.optional) { 127 | dependencyNode.appendNode('optional', 'true') 128 | } 129 | } 130 | 131 | // Add deps that everyone has 132 | configurations.compile.allDependencies.each addDep 133 | // Add flavor specific deps 134 | if (flavored) { 135 | configurations["${variant.flavorName}Compile"].allDependencies.each addDep 136 | } 137 | // NOTE: This library doesn't use builtTypes specific dependencies, so no need to add them. 138 | } 139 | } 140 | } 141 | } 142 | } 143 | 144 | group = groupId 145 | version = libraryVersion 146 | 147 | afterEvaluate { 148 | bintray { 149 | println "bintrary user " + BINTRARY_USER 150 | user = BINTRARY_USER 151 | key = BINTRARY_KEY 152 | 153 | publications = publicationNames 154 | 155 | publish = true //[Default: false] Whether version should be auto published after an upload 156 | override = false 157 | 158 | pkg { 159 | repo = repositoryName 160 | name = project.name 161 | desc = libraryDescription 162 | 163 | // issueTrackerUrl = '' 164 | vcsUrl = gitHubRepoUrl 165 | websiteUrl = websiteUrl 166 | 167 | licenses = ['Apache-2.0'] 168 | labels = LABELS // Optional 169 | 170 | githubRepo = GITHUB_REPO 171 | githubReleaseNotesFile = releaseNote 172 | publicDownloadNumbers = true 173 | } 174 | } 175 | } -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taehwandev/LifecycleExtensions/18968f73cf57d641b1b1532d60b352cd779c7917/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /images/AAC-ViewModel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taehwandev/LifecycleExtensions/18968f73cf57d641b1b1532d60b352cd779c7917/images/AAC-ViewModel.png -------------------------------------------------------------------------------- /lifecycle-extensions/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /lifecycle-extensions/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'com.jfrog.bintray' 4 | apply plugin: 'com.github.dcendents.android-maven' 5 | apply plugin: 'maven-publish' 6 | 7 | ext { 8 | libraryVersionCode = 14 9 | libraryVersion = '2.4.2' 10 | libraryDescription = 'Android Architecture Components Lifecycle simple factory inject. by thdev.tech' 11 | releaseNote = 'CHANGELOG_LIFECYCLE_EXTENSIONS.md' 12 | } 13 | 14 | android { 15 | compileSdkVersion 28 16 | 17 | defaultConfig { 18 | minSdkVersion 16 19 | targetSdkVersion 28 20 | versionCode libraryVersionCode 21 | versionName libraryVersion 22 | 23 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 24 | } 25 | 26 | buildTypes { 27 | release { 28 | minifyEnabled false 29 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 30 | } 31 | } 32 | } 33 | 34 | dependencies { 35 | implementation fileTree(dir: 'libs', include: ['*.jar']) 36 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 37 | implementation "androidx.appcompat:appcompat:$androidxSupportLibraryVersion" 38 | implementation "androidx.lifecycle:lifecycle-extensions:$androidxLifecycle" 39 | 40 | testImplementation 'junit:junit:4.12' 41 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 42 | } 43 | 44 | apply from: rootProject.file('gradle/publish.gradle') -------------------------------------------------------------------------------- /lifecycle-extensions/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/taehwankwon/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 | -------------------------------------------------------------------------------- /lifecycle-extensions/src/androidTest/java/tech/thdev/lifecycle/extensions/ExampleInstrumentedTest.java: -------------------------------------------------------------------------------- 1 | package tech.thdev.lifecycle.extensions; 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("tech.thdev.lifecycle.extensions.test", appContext.getPackageName()); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /lifecycle-extensions/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | -------------------------------------------------------------------------------- /lifecycle-extensions/src/main/java/tech/thdev/lifecycle/extensions/observer/LifecycleObserverExtensions.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Android lifecycle ViewModel Inject. 3 | * 4 | * Copyright 2017-2018 Tae-hwan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package tech.thdev.lifecycle.extensions.observer 19 | 20 | import androidx.fragment.app.Fragment 21 | import androidx.fragment.app.FragmentActivity 22 | import androidx.lifecycle.LifecycleObserver 23 | 24 | /** 25 | * Create OBSERVER from Fragment. 26 | */ 27 | inline fun Fragment.injectLifecycle(noinline onCreateLifecycleObserver: () -> OBSERVER): OBSERVER = 28 | onCreateLifecycleObserver().also { 29 | this.lifecycle.addObserver(it) 30 | } 31 | 32 | /** 33 | * Create OBSERVER from FragmentActivity. 34 | */ 35 | inline fun FragmentActivity.injectLifecycle(noinline onCreateLifecycleObserver: () -> OBSERVER): OBSERVER = 36 | onCreateLifecycleObserver().also { 37 | this.lifecycle.addObserver(it) 38 | } -------------------------------------------------------------------------------- /lifecycle-extensions/src/main/java/tech/thdev/lifecycle/extensions/observer/LifecycleObserverExtensionsJava.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Android lifecycle ViewModel Inject. 3 | * 4 | * Copyright 2017-2018 Tae-hwan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | @file:Suppress("UNCHECKED_CAST", "NOTHING_TO_INLINE") 19 | @file:JvmName("LifecycleObserverExtensions") 20 | @file:JvmMultifileClass 21 | 22 | package tech.thdev.lifecycle.extensions.observer 23 | 24 | import androidx.fragment.app.Fragment 25 | import androidx.fragment.app.FragmentActivity 26 | import androidx.lifecycle.LifecycleObserver 27 | 28 | interface LifecycleObserverCreate { 29 | 30 | fun onCreateLifecycleObserver(): OBSERVER 31 | } 32 | 33 | 34 | /** 35 | * Create OBSERVER from Fragment. 36 | */ 37 | fun injectLifecycle(fragment: Fragment, 38 | onCreateLifecycleObserver: LifecycleObserverCreate): OBSERVER = 39 | onCreateLifecycleObserver.onCreateLifecycleObserver().also { 40 | fragment.lifecycle.addObserver(it) 41 | } 42 | 43 | /** 44 | * Create OBSERVER from FragmentActivity. 45 | */ 46 | fun injectLifecycle(activity: FragmentActivity, 47 | onCreateLifecycleObserver: LifecycleObserverCreate): OBSERVER = 48 | onCreateLifecycleObserver.onCreateLifecycleObserver().also { 49 | activity.lifecycle.addObserver(it) 50 | } -------------------------------------------------------------------------------- /lifecycle-extensions/src/main/java/tech/thdev/lifecycle/extensions/observer/LifecycleObserverExtensionsLazy.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Android lifecycle ViewModel Inject. 3 | * 4 | * Copyright 2017-2018 Tae-hwan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package tech.thdev.lifecycle.extensions.observer 19 | 20 | import androidx.fragment.app.Fragment 21 | import androidx.fragment.app.FragmentActivity 22 | import androidx.lifecycle.Lifecycle 23 | import androidx.lifecycle.LifecycleObserver 24 | import androidx.lifecycle.OnLifecycleEvent 25 | 26 | /** 27 | * Fragment lazy inject LifecycleObserver 28 | * 29 | * ex) only fragment 30 | * val lifecycleObserver by autoInjectLifecycle { 31 | * MyLifecycleObserver() 32 | * } 33 | * 34 | * ex) Create activity lifecycleObserver from Fragment 35 | * val lifecycleObserver by autoInjectLifecycle(isActivity = true) { 36 | * MyLifecycleObserver() 37 | * } 38 | */ 39 | inline fun Fragment.injectAutoLifecycle( 40 | isActivity: Boolean = false, 41 | noinline onCreateLifecycleObserver: () -> OBSERVER): FragmentViewModelInject = 42 | FragmentViewModelInject(isActivity, this, onCreateLifecycleObserver) 43 | 44 | class FragmentViewModelInject(private val isActivity: Boolean, 45 | private val fragment: Fragment, 46 | initializer: () -> OBSERVER) : Lazy { 47 | 48 | init { 49 | val observer = object : LifecycleObserver { 50 | 51 | @OnLifecycleEvent(Lifecycle.Event.ON_CREATE) 52 | fun onCreate() { 53 | // auto call and init... 54 | value 55 | } 56 | } 57 | 58 | if (isActivity) { 59 | fragment.requireActivity().lifecycle.addObserver(observer) 60 | } else { 61 | fragment.lifecycle.addObserver(observer) 62 | } 63 | } 64 | 65 | private var initializer: (() -> LifecycleObserver)? = initializer 66 | private var _value: LifecycleObserver? = null 67 | 68 | override val value: OBSERVER 69 | get() { 70 | if (_value == null) { 71 | _value = if (isActivity) { 72 | fragment.requireActivity().injectLifecycle { 73 | initializer!!() 74 | } 75 | } else { 76 | fragment.injectLifecycle { 77 | initializer!!() 78 | } 79 | } 80 | initializer = null 81 | } 82 | @Suppress("UNCHECKED_CAST") 83 | return _value as OBSERVER 84 | } 85 | 86 | override fun isInitialized(): Boolean = _value != null 87 | } 88 | 89 | /** 90 | * FragmentActivity lazy inject LifecycleObserver 91 | * 92 | * ex) 93 | * val lifecycleObserver by autoInjectLifecycle { 94 | * MyLifecycleObserver() 95 | * } 96 | */ 97 | inline fun FragmentActivity.injectAutoLifecycle( 98 | noinline onCreateLifecycleObserver: () -> OBSERVER): ActivityViewModelInject = 99 | ActivityViewModelInject(this, onCreateLifecycleObserver) 100 | 101 | class ActivityViewModelInject(private val activity: FragmentActivity, 102 | initializer: () -> LifecycleObserver) : Lazy { 103 | private var initializer: (() -> LifecycleObserver)? = initializer 104 | private var _value: LifecycleObserver? = null 105 | 106 | init { 107 | activity.lifecycle.addObserver(object : LifecycleObserver { 108 | 109 | @OnLifecycleEvent(Lifecycle.Event.ON_CREATE) 110 | fun onCreate() { 111 | // auto call and init... 112 | value 113 | } 114 | }) 115 | } 116 | 117 | override val value: OBSERVER 118 | get() { 119 | if (_value == null) { 120 | _value = activity.injectLifecycle { 121 | initializer!!() 122 | } 123 | initializer = null 124 | } 125 | @Suppress("UNCHECKED_CAST") 126 | return _value as OBSERVER 127 | } 128 | 129 | override fun isInitialized(): Boolean = 130 | _value != null 131 | } -------------------------------------------------------------------------------- /lifecycle-extensions/src/main/java/tech/thdev/lifecycle/extensions/viewmodel/ViewModelCustomKeyUtil.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Android lifecycle ViewModel Inject. 3 | * 4 | * Copyright 2017-2018 Tae-hwan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package tech.thdev.lifecycle.extensions.viewmodel 19 | 20 | /** 21 | * Custom key default value 22 | */ 23 | const val DEFAULT_KEY = "tech.thdev.lifecycle.extensions.ViewModelProvider.DefaultKey" 24 | 25 | fun Class.getCustomKey(): String = "$DEFAULT_KEY:${this.canonicalName}" -------------------------------------------------------------------------------- /lifecycle-extensions/src/main/java/tech/thdev/lifecycle/extensions/viewmodel/ViewModelExtensions.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Android lifecycle ViewModel Inject. 3 | * 4 | * Copyright 2017-2018 Tae-hwan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | @file:Suppress("UNCHECKED_CAST", "NOTHING_TO_INLINE") 19 | 20 | package tech.thdev.lifecycle.extensions.viewmodel 21 | 22 | import androidx.fragment.app.Fragment 23 | import androidx.fragment.app.FragmentActivity 24 | import androidx.lifecycle.ViewModel 25 | import androidx.lifecycle.ViewModelProvider 26 | import androidx.lifecycle.ViewModelProviders 27 | 28 | /** 29 | * Create VIEW_MODEL from ViewModelProvider. 30 | * @param VIEW_MODEL 31 | * @param customKey 32 | * @param cls 33 | */ 34 | inline fun ViewModelProvider.create(customKey: String, cls: Class): VIEW_MODEL = 35 | if (customKey.isNotEmpty()) { 36 | get(customKey, cls) 37 | } else { 38 | get(cls.getCustomKey(), cls) 39 | } 40 | 41 | inline fun Fragment.injectViewModel(customKey: String = "", 42 | noinline onCreateViewModel: () -> VIEW_MODEL): VIEW_MODEL = 43 | ViewModelProviders.of(this, createViewModel(onCreateViewModel)).create(customKey, VIEW_MODEL::class.java) 44 | 45 | 46 | /** 47 | * FragmentActivity inject viewModel 48 | */ 49 | inline fun FragmentActivity.injectViewModel(customKey: String = "", 50 | noinline onCreateViewModel: () -> VIEW_MODEL): VIEW_MODEL = 51 | ViewModelProviders.of(this, createViewModel(onCreateViewModel)).create(customKey, VIEW_MODEL::class.java) 52 | 53 | /** 54 | * ViewModel Factory function. create viewModel 55 | * @param onCreateViewModel Higher-Order function. 56 | */ 57 | fun createViewModel(onCreateViewModel: () -> VIEW_MODEL) = object : ViewModelProvider.Factory { 58 | 59 | override fun create(modelClass: Class): T { 60 | return onCreateViewModel() as T 61 | } 62 | } -------------------------------------------------------------------------------- /lifecycle-extensions/src/main/java/tech/thdev/lifecycle/extensions/viewmodel/ViewModelExtensionsJava.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Android lifecycle ViewModel Inject. 3 | * 4 | * Copyright 2017-2018 Tae-hwan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | @file:Suppress("UNCHECKED_CAST", "NOTHING_TO_INLINE") 19 | @file:JvmName("ViewModelExtensions") 20 | @file:JvmMultifileClass 21 | 22 | package tech.thdev.lifecycle.extensions.viewmodel 23 | 24 | import androidx.fragment.app.Fragment 25 | import androidx.fragment.app.FragmentActivity 26 | import androidx.lifecycle.ViewModel 27 | import androidx.lifecycle.ViewModelProviders 28 | 29 | interface ViewModelCreate { 30 | 31 | fun onCreateViewModel(): VIEW_MODEL 32 | } 33 | 34 | /** 35 | * Only Java method. 36 | * @param receiver Fragment 37 | * @param viewModel : ViewModel Class 38 | * @param customKey : @option your custom key 39 | * @param viewModelHelper : new ViewModelCreate<> 40 | */ 41 | @JvmOverloads 42 | fun injectViewModel(fragment: Fragment, 43 | viewModel: Class, 44 | customKey: String = "", 45 | viewModelHelper: ViewModelCreate): VIEW_MODEL = 46 | ViewModelProviders.of(fragment, createViewModel(viewModelHelper::onCreateViewModel)).create(customKey, viewModel) 47 | 48 | /** 49 | * Only Java method. 50 | * @param receiver : FragmentActivity 51 | * @param viewModel : ViewModel Class 52 | * @param customKey : @option your custom key 53 | * @param viewModelHelper : new ViewModelCreate<> 54 | */ 55 | @JvmOverloads 56 | fun injectViewModel(activity: FragmentActivity, 57 | viewModel: Class, 58 | customKey: String = "", 59 | viewModelHelper: ViewModelCreate): VIEW_MODEL = 60 | ViewModelProviders.of(activity, createViewModel(viewModelHelper::onCreateViewModel)).create(customKey, viewModel) -------------------------------------------------------------------------------- /lifecycle-extensions/src/main/java/tech/thdev/lifecycle/extensions/viewmodel/ViewModelExtensionsLazy.kt: -------------------------------------------------------------------------------- 1 | /** 2 | * Android lifecycle ViewModel Inject. 3 | * 4 | * Copyright 2017-2018 Tae-hwan 5 | * 6 | * Licensed under the Apache License, Version 2.0 (the "License"); 7 | * you may not use this file except in compliance with the License. 8 | * You may obtain a copy of the License at 9 | * 10 | * http://www.apache.org/licenses/LICENSE-2.0 11 | * 12 | * Unless required by applicable law or agreed to in writing, software 13 | * distributed under the License is distributed on an "AS IS" BASIS, 14 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | * See the License for the specific language governing permissions and 16 | * limitations under the License. 17 | */ 18 | package tech.thdev.lifecycle.extensions.viewmodel 19 | 20 | import androidx.fragment.app.Fragment 21 | import androidx.fragment.app.FragmentActivity 22 | import androidx.lifecycle.ViewModel 23 | 24 | /** 25 | * Fragment lazy inject viewModel 26 | * 27 | * ex) only fragment 28 | * val viewModel by lazy { 29 | * MyViewModel() 30 | * } 31 | * 32 | * ex) Create activity ViewModel from Fragment 33 | * val viewModel by lazy(isActivity = true) { 34 | * MyViewModel() 35 | * } 36 | */ 37 | inline fun Fragment.lazyInjectViewModel( 38 | isActivity: Boolean = false, 39 | customKey: String = VIEW_MODEL::class.java.getCustomKey(), 40 | noinline onCreateViewModel: () -> VIEW_MODEL): FragmentViewModelInject = 41 | FragmentViewModelInject(isActivity, this, customKey, onCreateViewModel) 42 | 43 | class FragmentViewModelInject(private val isActivity: Boolean, 44 | private val fragment: Fragment, 45 | private val customKey: String, 46 | initializer: () -> VIEW_MODEL) : Lazy { 47 | 48 | private var initializer: (() -> ViewModel)? = initializer 49 | private var _value: ViewModel? = null 50 | 51 | override val value: VIEW_MODEL 52 | get() { 53 | if (_value == null) { 54 | _value = if (isActivity) { 55 | fragment.requireActivity().injectViewModel(customKey) { 56 | initializer!!() 57 | } 58 | } else { 59 | fragment.injectViewModel(customKey) { 60 | initializer!!() 61 | } 62 | } 63 | initializer = null 64 | } 65 | @Suppress("UNCHECKED_CAST") 66 | return _value as VIEW_MODEL 67 | } 68 | 69 | override fun isInitialized(): Boolean = _value != null 70 | } 71 | 72 | /** 73 | * FragmentActivity lazy inject viewModel 74 | * 75 | * ex) 76 | * val viewModel by lazy { 77 | * MyViewModel() 78 | * } 79 | */ 80 | inline fun FragmentActivity.lazyInjectViewModel( 81 | customKey: String = VIEW_MODEL::class.java.getCustomKey(), 82 | noinline onCreateViewModel: () -> VIEW_MODEL): ActivityViewModelInject = 83 | ActivityViewModelInject(this, customKey, onCreateViewModel) 84 | 85 | class ActivityViewModelInject(private val activity: FragmentActivity, 86 | private val customKey: String, 87 | initializer: () -> ViewModel) : Lazy { 88 | private var initializer: (() -> ViewModel)? = initializer 89 | private var _value: ViewModel? = null 90 | 91 | override val value: VIEW_MODEL 92 | get() { 93 | if (_value == null) { 94 | _value = activity.injectViewModel(customKey) { 95 | initializer!!() 96 | } 97 | initializer = null 98 | } 99 | @Suppress("UNCHECKED_CAST") 100 | return _value as VIEW_MODEL 101 | } 102 | 103 | override fun isInitialized(): Boolean = 104 | _value != null 105 | } -------------------------------------------------------------------------------- /lifecycle-extensions/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | LifecycleExtensions 3 | 4 | -------------------------------------------------------------------------------- /lifecycle-extensions/src/test/java/tech/thdev/lifecycle/extensions/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package tech.thdev.lifecycle.extensions; 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 | } -------------------------------------------------------------------------------- /sampleapp/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /sampleapp/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | apply plugin: 'kotlin-android' 3 | apply plugin: 'kotlin-android-extensions' 4 | 5 | android { 6 | compileSdkVersion 28 7 | defaultConfig { 8 | applicationId "tech.thdev.lifecycleextensions" 9 | minSdkVersion 19 10 | targetSdkVersion 28 11 | versionCode 1 12 | versionName "1.0" 13 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" 14 | } 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | } 22 | 23 | dependencies { 24 | implementation fileTree(include: ['*.jar'], dir: 'libs') 25 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" 26 | implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version" 27 | 28 | implementation "androidx.appcompat:appcompat:$androidxSupportLibraryVersion" 29 | implementation "androidx.constraintlayout:constraintlayout:$androidxConstraintVersion" 30 | implementation "com.google.android.material:material:$meterialLibraryVersion" 31 | 32 | implementation "androidx.lifecycle:lifecycle-extensions:$androidxLifecycle" 33 | 34 | implementation "tech.thdev.coroutines:coroutines-extensions:$coroutinesExtensionsVersion" 35 | 36 | testImplementation 'junit:junit:4.12' 37 | androidTestImplementation 'com.android.support.test.espresso:espresso-core:3.0.2' 38 | implementation project(':lifecycle-extensions') 39 | } 40 | 41 | kotlin { 42 | experimental { 43 | coroutines "enable" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /sampleapp/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/taehwankwon/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 | -------------------------------------------------------------------------------- /sampleapp/src/androidTest/java/tech/thdev/lifecycleextensions/ExampleInstrumentedTest.kt: -------------------------------------------------------------------------------- 1 | package tech.thdev.lifecycleextensions 2 | 3 | import android.support.test.InstrumentationRegistry 4 | import android.support.test.runner.AndroidJUnit4 5 | 6 | import org.junit.Test 7 | import org.junit.runner.RunWith 8 | 9 | import org.junit.Assert.* 10 | 11 | /** 12 | * Instrumented test, which will execute on an Android device. 13 | * 14 | * See [testing documentation](http://d.android.com/tools/testing). 15 | */ 16 | @RunWith(AndroidJUnit4::class) 17 | class ExampleInstrumentedTest { 18 | @Test 19 | fun useAppContext() { 20 | // Context of the app under test. 21 | val appContext = InstrumentationRegistry.getTargetContext() 22 | assertEquals("tech.thdev.lifecycleextensions", appContext.packageName) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /sampleapp/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /sampleapp/src/main/java/tech/thdev/lifecycleextensions/java/view/JavaMainActivity.java: -------------------------------------------------------------------------------- 1 | package tech.thdev.lifecycleextensions.java.view; 2 | 3 | import android.os.Bundle; 4 | 5 | import org.jetbrains.annotations.NotNull; 6 | 7 | import androidx.annotation.Nullable; 8 | import androidx.appcompat.app.AppCompatActivity; 9 | import kotlin.Unit; 10 | import kotlin.jvm.functions.Function1; 11 | import tech.thdev.lifecycle.extensions.observer.LifecycleObserverCreate; 12 | import tech.thdev.lifecycle.extensions.observer.LifecycleObserverExtensions; 13 | import tech.thdev.lifecycle.extensions.viewmodel.ViewModelCreate; 14 | import tech.thdev.lifecycle.extensions.viewmodel.ViewModelExtensions; 15 | import tech.thdev.lifecycleextensions.R; 16 | import tech.thdev.lifecycleextensions.observer.MainLifecycleObserver; 17 | import tech.thdev.lifecycleextensions.view.home.viewmodel.HomeViewModel; 18 | 19 | public class JavaMainActivity extends AppCompatActivity { 20 | 21 | private HomeViewModel homeViewModel; 22 | private MainLifecycleObserver mainLifecycleObserver; 23 | 24 | @Override 25 | protected void onCreate(@Nullable Bundle savedInstanceState) { 26 | super.onCreate(savedInstanceState); 27 | setContentView(R.layout.activity_main); 28 | 29 | homeViewModel = ViewModelExtensions.injectViewModel(this, HomeViewModel.class, new ViewModelCreate() { 30 | @NotNull 31 | @Override 32 | public HomeViewModel onCreateViewModel() { 33 | return new HomeViewModel(); 34 | } 35 | }); 36 | 37 | homeViewModel.setUpdateButton(new Function1() { 38 | @Override 39 | public Unit invoke(Integer integer) { 40 | // ... 41 | return null; 42 | } 43 | }); 44 | 45 | mainLifecycleObserver = LifecycleObserverExtensions.injectLifecycle(this, new LifecycleObserverCreate() { 46 | @NotNull 47 | @Override 48 | public MainLifecycleObserver onCreateLifecycleObserver() { 49 | return new MainLifecycleObserver(); 50 | } 51 | }); 52 | 53 | homeViewModel.clickButton(); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /sampleapp/src/main/java/tech/thdev/lifecycleextensions/observer/MainLifecycleObserver.kt: -------------------------------------------------------------------------------- 1 | package tech.thdev.lifecycleextensions.observer 2 | 3 | import androidx.lifecycle.LifecycleObserver 4 | 5 | class MainLifecycleObserver : LifecycleObserver -------------------------------------------------------------------------------- /sampleapp/src/main/java/tech/thdev/lifecycleextensions/view/MainActivity.kt: -------------------------------------------------------------------------------- 1 | package tech.thdev.lifecycleextensions.view 2 | 3 | import android.os.Bundle 4 | import androidx.appcompat.app.AppCompatActivity 5 | import androidx.fragment.app.Fragment 6 | import com.google.android.material.bottomnavigation.BottomNavigationView 7 | import kotlinx.android.synthetic.main.activity_main.* 8 | import tech.thdev.lifecycle.extensions.observer.injectAutoLifecycle 9 | import tech.thdev.lifecycle.extensions.viewmodel.lazyInjectViewModel 10 | import tech.thdev.lifecycleextensions.R 11 | import tech.thdev.lifecycleextensions.observer.MainLifecycleObserver 12 | import tech.thdev.lifecycleextensions.view.home.HomeFragment 13 | import tech.thdev.lifecycleextensions.view.time.TimeFragment 14 | import tech.thdev.lifecycleextensions.view.time.viewmodel.TimeViewModel 15 | 16 | class MainActivity : AppCompatActivity() { 17 | 18 | private val homeFragment: HomeFragment by lazy { 19 | HomeFragment() 20 | } 21 | 22 | private val timeFragment: TimeFragment by lazy { 23 | TimeFragment() 24 | } 25 | 26 | private val timeViewModel: TimeViewModel by lazyInjectViewModel { 27 | TimeViewModel() 28 | } 29 | 30 | private val mainLifecycleObserver by injectAutoLifecycle { 31 | MainLifecycleObserver() 32 | } 33 | 34 | private val onNavigationItemSelectedListener = BottomNavigationView.OnNavigationItemSelectedListener { item -> 35 | 36 | when (item.itemId) { 37 | R.id.navigation_home -> { 38 | homeFragment.updateFragment() 39 | return@OnNavigationItemSelectedListener true 40 | } 41 | R.id.navigation_time -> { 42 | timeFragment.updateFragment() 43 | return@OnNavigationItemSelectedListener true 44 | } 45 | } 46 | false 47 | } 48 | 49 | override fun onCreate(savedInstanceState: Bundle?) { 50 | super.onCreate(savedInstanceState) 51 | setContentView(R.layout.activity_main) 52 | 53 | navigation.setOnNavigationItemSelectedListener(onNavigationItemSelectedListener) 54 | 55 | timeViewModel 56 | homeFragment.updateFragment() 57 | } 58 | 59 | private fun Fragment.updateFragment() { 60 | supportFragmentManager.beginTransaction() 61 | .replace(R.id.container, this) 62 | .commitNow() 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /sampleapp/src/main/java/tech/thdev/lifecycleextensions/view/home/HomeFragment.kt: -------------------------------------------------------------------------------- 1 | package tech.thdev.lifecycleextensions.view.home 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import androidx.fragment.app.Fragment 8 | import com.google.android.material.snackbar.Snackbar 9 | import kotlinx.android.synthetic.main.fragment_home.* 10 | import tech.thdev.lifecycle.extensions.viewmodel.injectViewModel 11 | import tech.thdev.lifecycleextensions.R 12 | import tech.thdev.lifecycleextensions.view.home.viewmodel.HomeViewModel 13 | 14 | class HomeFragment : Fragment() { 15 | 16 | private lateinit var homeViewModel: HomeViewModel 17 | 18 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? = 19 | inflater.inflate(R.layout.fragment_home, container, false) 20 | 21 | override fun onActivityCreated(savedInstanceState: Bundle?) { 22 | super.onActivityCreated(savedInstanceState) 23 | 24 | homeViewModel = requireActivity().injectViewModel { 25 | HomeViewModel() 26 | }.apply { 27 | updateButton = { count -> 28 | Snackbar.make(fab_plus_one, "Plus one $count", Snackbar.LENGTH_SHORT).show() 29 | } 30 | } 31 | 32 | fab_plus_one.setOnClickListener { 33 | homeViewModel.clickButton() 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /sampleapp/src/main/java/tech/thdev/lifecycleextensions/view/home/viewmodel/HomeViewModel.kt: -------------------------------------------------------------------------------- 1 | package tech.thdev.lifecycleextensions.view.home.viewmodel 2 | 3 | import android.util.Log 4 | import androidx.lifecycle.ViewModel 5 | 6 | class HomeViewModel : ViewModel() { 7 | 8 | lateinit var updateButton: (count: Int) -> Unit 9 | 10 | private var count = 0 11 | 12 | private val tag = this::class.java.simpleName 13 | 14 | init { 15 | Log.e(tag, "ViewModel init") 16 | } 17 | 18 | fun clickButton() { 19 | if (::updateButton.isInitialized) { 20 | updateButton(count++) 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /sampleapp/src/main/java/tech/thdev/lifecycleextensions/view/time/TimeFragment.kt: -------------------------------------------------------------------------------- 1 | package tech.thdev.lifecycleextensions.view.time 2 | 3 | import android.os.Bundle 4 | import android.view.LayoutInflater 5 | import android.view.View 6 | import android.view.ViewGroup 7 | import androidx.fragment.app.Fragment 8 | import kotlinx.android.synthetic.main.fragment_time.* 9 | import tech.thdev.lifecycle.extensions.observer.injectAutoLifecycle 10 | import tech.thdev.lifecycle.extensions.viewmodel.lazyInjectViewModel 11 | import tech.thdev.lifecycleextensions.R 12 | import tech.thdev.lifecycleextensions.observer.MainLifecycleObserver 13 | import tech.thdev.lifecycleextensions.view.time.viewmodel.TimeViewModel 14 | 15 | class TimeFragment : Fragment() { 16 | 17 | private val timeViewModel: TimeViewModel by lazyInjectViewModel(isActivity = true) { 18 | TimeViewModel() 19 | } 20 | 21 | private val mainLifecycleObserver by injectAutoLifecycle { 22 | MainLifecycleObserver() 23 | } 24 | 25 | override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? = 26 | inflater.inflate(R.layout.fragment_time, container, false) 27 | 28 | override fun onActivityCreated(savedInstanceState: Bundle?) { 29 | super.onActivityCreated(savedInstanceState) 30 | 31 | timeViewModel.updateLoginTime = { time -> 32 | if (!isDetached) { 33 | tv_past_time?.text = time 34 | } 35 | } 36 | 37 | timeViewModel.startTime() 38 | } 39 | 40 | override fun onPause() { 41 | super.onPause() 42 | 43 | timeViewModel.stopTime() 44 | } 45 | } -------------------------------------------------------------------------------- /sampleapp/src/main/java/tech/thdev/lifecycleextensions/view/time/viewmodel/TimeViewModel.kt: -------------------------------------------------------------------------------- 1 | package tech.thdev.lifecycleextensions.view.time.viewmodel 2 | 3 | import android.util.Log 4 | import kotlinx.coroutines.* 5 | import tech.thdev.coroutines.provider.DispatchersProvider 6 | import tech.thdev.support.base.coroutines.viewmodel.CoroutineScopeViewModel 7 | import java.text.SimpleDateFormat 8 | import java.util.* 9 | 10 | class TimeViewModel : CoroutineScopeViewModel() { 11 | 12 | private val tag = this::class.java.simpleName 13 | 14 | lateinit var updateLoginTime: (time: String) -> Unit 15 | 16 | private var prevTime: Long = 0 17 | 18 | init { 19 | Log.d(tag, "create ViewModel") 20 | } 21 | 22 | private val simpleDateFormat: SimpleDateFormat by lazy { 23 | SimpleDateFormat("mm:ss.S", Locale.getDefault()) 24 | } 25 | 26 | private fun updateTimeThread() = launch { 27 | while (isActive) { 28 | launch(DispatchersProvider.main) { 29 | if (::updateLoginTime.isInitialized) { 30 | updateLoginTime(simpleDateFormat.format(System.currentTimeMillis() - prevTime)) 31 | } 32 | } 33 | Thread.sleep(10) 34 | } 35 | } 36 | 37 | fun startTime() { 38 | prevTime = System.currentTimeMillis() 39 | updateTimeThread() 40 | } 41 | 42 | fun stopTime() { 43 | Log.e(tag, "stop") 44 | launch { 45 | job.cancelAndJoin() 46 | } 47 | Log.e(tag, "stop cancelAndJoin") 48 | } 49 | 50 | override fun onCleared() { 51 | stopTime() 52 | } 53 | } -------------------------------------------------------------------------------- /sampleapp/src/main/res/drawable/ic_access_time_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /sampleapp/src/main/res/drawable/ic_home_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /sampleapp/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 | -------------------------------------------------------------------------------- /sampleapp/src/main/res/drawable/ic_plus_one_black_24dp.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /sampleapp/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 17 | 18 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /sampleapp/src/main/res/layout/fragment_home.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 18 | 19 | 28 | -------------------------------------------------------------------------------- /sampleapp/src/main/res/layout/fragment_time.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 21 | 22 | 32 | 33 | 45 | 46 | 58 | 59 | -------------------------------------------------------------------------------- /sampleapp/src/main/res/menu/navigation.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /sampleapp/src/main/res/mipmap-anydpi-v26/ic_launcher.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /sampleapp/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /sampleapp/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taehwandev/LifecycleExtensions/18968f73cf57d641b1b1532d60b352cd779c7917/sampleapp/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /sampleapp/src/main/res/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taehwandev/LifecycleExtensions/18968f73cf57d641b1b1532d60b352cd779c7917/sampleapp/src/main/res/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /sampleapp/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taehwandev/LifecycleExtensions/18968f73cf57d641b1b1532d60b352cd779c7917/sampleapp/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /sampleapp/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taehwandev/LifecycleExtensions/18968f73cf57d641b1b1532d60b352cd779c7917/sampleapp/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /sampleapp/src/main/res/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taehwandev/LifecycleExtensions/18968f73cf57d641b1b1532d60b352cd779c7917/sampleapp/src/main/res/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /sampleapp/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taehwandev/LifecycleExtensions/18968f73cf57d641b1b1532d60b352cd779c7917/sampleapp/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /sampleapp/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taehwandev/LifecycleExtensions/18968f73cf57d641b1b1532d60b352cd779c7917/sampleapp/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sampleapp/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taehwandev/LifecycleExtensions/18968f73cf57d641b1b1532d60b352cd779c7917/sampleapp/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /sampleapp/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taehwandev/LifecycleExtensions/18968f73cf57d641b1b1532d60b352cd779c7917/sampleapp/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /sampleapp/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taehwandev/LifecycleExtensions/18968f73cf57d641b1b1532d60b352cd779c7917/sampleapp/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sampleapp/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taehwandev/LifecycleExtensions/18968f73cf57d641b1b1532d60b352cd779c7917/sampleapp/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /sampleapp/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taehwandev/LifecycleExtensions/18968f73cf57d641b1b1532d60b352cd779c7917/sampleapp/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /sampleapp/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taehwandev/LifecycleExtensions/18968f73cf57d641b1b1532d60b352cd779c7917/sampleapp/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /sampleapp/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taehwandev/LifecycleExtensions/18968f73cf57d641b1b1532d60b352cd779c7917/sampleapp/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /sampleapp/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taehwandev/LifecycleExtensions/18968f73cf57d641b1b1532d60b352cd779c7917/sampleapp/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /sampleapp/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /sampleapp/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | Lifecycle Extensions 3 | 4 | Home 5 | Time 6 | Now time 7 | Past time 8 | 9 | -------------------------------------------------------------------------------- /sampleapp/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /sampleapp/src/test/java/tech/thdev/lifecycleextensions/ExampleUnitTest.kt: -------------------------------------------------------------------------------- 1 | package tech.thdev.lifecycleextensions 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 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':sampleapp', ':lifecycle-extensions' 2 | --------------------------------------------------------------------------------