Skip to content

Commit

Permalink
Merge pull request #57 from marcellogalhardo/view-refactor
Browse files Browse the repository at this point in the history
View refactor
  • Loading branch information
marcellogalhardo committed Oct 30, 2021
2 parents 64218d0 + 7c74fb8 commit d766a77
Show file tree
Hide file tree
Showing 14 changed files with 85 additions and 36 deletions.
2 changes: 1 addition & 1 deletion activity/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ ext {
apply from: "${rootProject.projectDir}/scripts/publish-module.gradle"

android {
compileSdkVersion 30
compileSdkVersion 31
buildToolsVersion "30.0.2"

defaultConfig {
Expand Down
2 changes: 1 addition & 1 deletion core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ ext {
apply from: "${rootProject.projectDir}/scripts/publish-module.gradle"

android {
compileSdkVersion 30
compileSdkVersion 31
buildToolsVersion "30.0.2"

defaultConfig {
Expand Down
2 changes: 1 addition & 1 deletion fragment/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ ext {
apply from: "${rootProject.projectDir}/scripts/publish-module.gradle"

android {
compileSdkVersion 30
compileSdkVersion 31
buildToolsVersion "30.0.2"

defaultConfig {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import dev.marcellogalhardo.retained.core.retain
*
* ```
* class MyFragment : Fragment() {
* val vm by retainInFragment(getFragment = { this }) { ViewModel() }
* val vm by findFragment(getFragment = { this }) { ViewModel() }
* }
* class ViewModel(val name: String = "")
* ```
Expand All @@ -27,8 +27,8 @@ import dev.marcellogalhardo.retained.core.retain
public inline fun <reified T : Any> retainInFragment(
noinline findFragment: () -> Fragment,
key: String = T::class.java.name,
noinline initializer: (RetainedEntry) -> T,
): Retained<T> = retain(key, findFragment, { owner -> owner.arguments }, initializer)
noinline instantiate: (RetainedEntry) -> T,
): Retained<T> = retain(key, findFragment, { owner -> owner.arguments }, instantiate)

/**
* Returns a [Lazy] delegate to access a retained object by **default** scoped to this [Fragment]:
Expand Down
10 changes: 5 additions & 5 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
[versions]
androidx-compose = '1.0.0'
androidx-fragment = "1.3.5"
androidx-lifecycle = "2.3.1"
androidx-fragment = "1.3.6"
androidx-lifecycle = "2.4.0"
androidx-test = "1.3.0"
kotlin = "1.5.20"
navigation = "2.3.5"

[libraries]
androidx-activity-core = { group = "androidx.activity", name = "activity-ktx", version = "1.2.3" }
androidx-activity-core = { group = "androidx.activity", name = "activity-ktx", version = "1.4.0" }
androidx-appcompat-core = { group = "androidx.appcompat", name = "appcompat", version = "1.3.1" }
androidx-constraintlayout-core = { group = "androidx.constraintlayout", name = "constraintlayout", version = "2.0.4" }
androidx-core = { group = "androidx.core", name = "core", version = "1.3.2" }
androidx-constraintlayout-core = { group = "androidx.constraintlayout", name = "constraintlayout", version = "2.1.1" }
androidx-core = { group = "androidx.core", name = "core", version = "1.7.0" }
androidx-fragment-core = { group = "androidx.fragment", name = "fragment-ktx", version.ref = "androidx-fragment" }
androidx-fragment-testing = { group = "androidx.fragment", name = "fragment-testing", version.ref = "androidx-fragment" }
androidx-navigation-fragment = { group = "androidx.navigation", name = "navigation-fragment-ktx", version.ref = "navigation" }
Expand Down
2 changes: 1 addition & 1 deletion integration-tests/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'

android {
compileSdkVersion 30
compileSdkVersion 31
buildToolsVersion "30.0.2"

defaultConfig {
Expand Down
4 changes: 3 additions & 1 deletion integration-tests/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
package="dev.marcellogalhardo.retained.integration.tests">

<application>
<activity android:name="dev.marcellogalhardo.retained.activity.EmptyActivity" />
<activity
android:name="dev.marcellogalhardo.retained.activity.EmptyActivity"
android:exported="false" />
</application>
</manifest>
2 changes: 1 addition & 1 deletion navigation-fragment/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ ext {
apply from: "${rootProject.projectDir}/scripts/publish-module.gradle"

android {
compileSdkVersion 30
compileSdkVersion 31
buildToolsVersion "30.0.2"

defaultConfig {
Expand Down
2 changes: 1 addition & 1 deletion navigation-view/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ ext {
apply from: "${rootProject.projectDir}/scripts/publish-module.gradle"

android {
compileSdkVersion 30
compileSdkVersion 31
buildToolsVersion "30.0.2"

defaultConfig {
Expand Down
2 changes: 1 addition & 1 deletion navigation/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ ext {
apply from: "${rootProject.projectDir}/scripts/publish-module.gradle"

android {
compileSdkVersion 30
compileSdkVersion 31
buildToolsVersion "30.0.2"

defaultConfig {
Expand Down
2 changes: 1 addition & 1 deletion sample/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'

android {
compileSdkVersion 30
compileSdkVersion 31
buildToolsVersion "30.0.2"

defaultConfig {
Expand Down
13 changes: 9 additions & 4 deletions sample/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,24 @@
package="dev.marcellogalhardo.retained.sample">

<application
android:allowBackup="true"
android:allowBackup="false"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name="dev.marcellogalhardo.retained.sample.SampleActivity">

<activity
android:name="dev.marcellogalhardo.retained.sample.SampleActivity"
android:exported="true">

<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

</activity>

</application>

</manifest>
</manifest>
2 changes: 1 addition & 1 deletion view/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ ext {
apply from: "${rootProject.projectDir}/scripts/publish-module.gradle"

android {
compileSdkVersion 30
compileSdkVersion 31
buildToolsVersion "30.0.2"

defaultConfig {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,47 @@ import android.view.View
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ViewModelStoreOwner
import androidx.lifecycle.findViewTreeLifecycleOwner
import androidx.lifecycle.findViewTreeViewModelStoreOwner
import androidx.navigation.NavBackStackEntry
import androidx.savedstate.SavedStateRegistryOwner
import androidx.savedstate.findViewTreeSavedStateRegistryOwner
import dev.marcellogalhardo.retained.core.ExperimentalRetainedApi
import dev.marcellogalhardo.retained.core.InternalRetainedApi
import dev.marcellogalhardo.retained.core.Retained
import dev.marcellogalhardo.retained.core.RetainedEntry
import dev.marcellogalhardo.retained.core.retain

/**
* Returns a [Lazy] delegate to access a retained object by **default** scoped to this [View]:
*
* ```
* class MyView(context: Context) : View(context) {
* val vm by retainInView(findInView = { this }) { ViewModel() }
* }
* class ViewModel(val name: String = "")
* ```
*
* This property can be accessed only after this [View] is attached i.e., after
* [View.isAttachedToWindow], and access prior to that will result in [IllegalStateException].
*
* @see retain
*/
@OptIn(InternalRetainedApi::class)
public inline fun <reified T : Any> retainInView(
noinline findView: () -> View,
key: String = "${findView().id}:${T::class.java.name}",
noinline instantiate: (RetainedEntry) -> T,
): Retained<T> {
val owner = lazy { checkNotNull(findView().findViewTreeViewModelStoreOwner()) }
return retain(
key = key,
findViewModelStoreOwner = { owner.value },
findSavedStateRegistryOwner = { owner.value as SavedStateRegistryOwner },
findDefaultArgs = { owner.value.defaultArgs },
instantiate = instantiate,
)
}

/**
* Returns a [Lazy] delegate to access a retained object by **default** scoped to this
* [View] (e.g., [NavBackStackEntry], [Fragment] or [Activity]).
Expand All @@ -39,11 +67,15 @@ import dev.marcellogalhardo.retained.core.retain
@OptIn(InternalRetainedApi::class)
@ExperimentalRetainedApi
public inline fun <reified T : Any> View.retain(
key: String = id.toString(),
noinline getViewModelStoreOwner: () -> ViewModelStoreOwner = { findViewModelStoreOwnerOrThrow() },
noinline getSavedStateRegistryOwner: () -> SavedStateRegistryOwner = { findViewTreeSavedStateRegistryOwner()!! },
key: String = T::class.java.name,
noinline instantiate: (RetainedEntry) -> T
): Retained<T> = retain(key, getViewModelStoreOwner, getSavedStateRegistryOwner, { findViewTreeLifecycleOwner()!!.defaultArgs }, instantiate)
): Retained<T> {
return retainInView(
findView = { this },
key = key,
instantiate = instantiate,
)
}

/**
* Returns a [Lazy] delegate to access a retained object by **default** scoped to the
Expand All @@ -63,14 +95,24 @@ public inline fun <reified T : Any> View.retain(
@OptIn(InternalRetainedApi::class)
@ExperimentalRetainedApi
public inline fun <reified T : Any> View.retainInActivity(
key: String = id.toString(),
activity: FragmentActivity = findActivity(),
key: String = T::class.java.name,
noinline instantiate: (RetainedEntry) -> T
): Retained<T> = retain(key, { activity }, { activity }, { activity.intent?.extras }, instantiate)
): Retained<T> {
val activity = findActivity()
return retain(
key = key,
findViewModelStoreOwner = { activity },
findSavedStateRegistryOwner = { activity },
findDefaultArgs = { activity.intent?.extras },
instantiate = instantiate,
)
}

@PublishedApi
internal fun View.findViewModelStoreOwnerOrThrow(): ViewModelStoreOwner = findViewTreeViewModelStoreOwner()
?: throw IllegalStateException("Your view is not yet attached, and thus its ViewModelStoreOwner is null.")
internal fun View.findViewModelStoreOwnerOrThrow(): ViewModelStoreOwner {
return findViewTreeViewModelStoreOwner()
?: error("Your view is not yet attached, and thus its ViewModelStoreOwner is null.")
}

@PublishedApi
internal fun View.findActivity(): FragmentActivity {
Expand All @@ -79,11 +121,11 @@ internal fun View.findActivity(): FragmentActivity {
if (currentContext is FragmentActivity) return currentContext
currentContext = (context as ContextWrapper).baseContext
}
throw IllegalStateException("Your view is not attached to an activity.")
error("Your view is not attached to an activity.")
}

@PublishedApi
internal val LifecycleOwner.defaultArgs: Bundle
internal val ViewModelStoreOwner.defaultArgs: Bundle
get() = when (this) {
is Activity -> intent?.extras
is Fragment -> arguments
Expand Down

0 comments on commit d766a77

Please sign in to comment.