diff --git a/client/build.gradle b/client/build.gradle index 5cab0595..d4c99c64 100644 --- a/client/build.gradle +++ b/client/build.gradle @@ -15,8 +15,8 @@ android { applicationId "com.jraska.github.client" minSdkVersion 21 targetSdkVersion 28 - versionName '0.15.7' - versionCode 47 + versionName '0.16.0' + versionCode 48 multiDexEnabled true testInstrumentationRunner "com.jraska.github.client.TestRunner" @@ -62,7 +62,7 @@ android { unitTests.returnDefaultValues = true } - dynamicFeatures = [":feature:about"] + dynamicFeatures = [":feature:about", ":feature:settings"] } dependencies { @@ -74,7 +74,7 @@ dependencies { api project(':lib-dynamic-features') api project(':feature:push') api project(':feature:users') - api project(':feature:settings') + api project(':feature:settings_entrance') api project(':feature:about_entrance') api project(':feature:shortcuts') diff --git a/client/src/main/java/com/jraska/github/client/AppComponent.kt b/client/src/main/java/com/jraska/github/client/AppComponent.kt index 677d4838..21734e45 100644 --- a/client/src/main/java/com/jraska/github/client/AppComponent.kt +++ b/client/src/main/java/com/jraska/github/client/AppComponent.kt @@ -11,7 +11,7 @@ import com.jraska.github.client.http.HttpComponent import com.jraska.github.client.identity.IdentityModule import com.jraska.github.client.identity.IdentityProvider import com.jraska.github.client.push.PushModule -import com.jraska.github.client.settings.SettingsModule +import com.jraska.github.client.settings.entrance.SettingsEntranceModule import com.jraska.github.client.shortcuts.ShortcutsModule import com.jraska.github.client.users.UsersModule import dagger.BindsInstance @@ -27,7 +27,7 @@ import dagger.Subcomponent IdentityModule::class, UsersModule::class, PushModule::class, - SettingsModule::class, + SettingsEntranceModule::class, AboutFeatureEntranceModule::class, ShortcutsModule::class], dependencies = [ diff --git a/feature/settings/build.gradle b/feature/settings/build.gradle index dd7c0712..52764183 100644 --- a/feature/settings/build.gradle +++ b/feature/settings/build.gradle @@ -1,4 +1,4 @@ -apply plugin: 'com.android.library' +apply plugin: 'com.android.dynamic-feature' apply plugin: 'kotlin-android' apply plugin: 'kotlin-kapt' apply plugin: 'kotlin-android-extensions' @@ -18,6 +18,8 @@ android { dependencies { api project(':core') api project(':core-android') + implementation project(':lib-dynamic-features') + implementation project(':client') api 'com.airbnb.android:epoxy:3.7.0' api 'com.jraska:console:1.0.0' diff --git a/feature/settings/src/main/AndroidManifest.xml b/feature/settings/src/main/AndroidManifest.xml index 295df240..1585df3a 100644 --- a/feature/settings/src/main/AndroidManifest.xml +++ b/feature/settings/src/main/AndroidManifest.xml @@ -1,7 +1,18 @@ - + + + + + + + diff --git a/feature/settings/src/main/java/com/jraska/github/client/settings/SettingsActivity.kt b/feature/settings/src/main/java/com/jraska/github/client/settings/SettingsActivity.kt index dac1df31..653f2de8 100644 --- a/feature/settings/src/main/java/com/jraska/github/client/settings/SettingsActivity.kt +++ b/feature/settings/src/main/java/com/jraska/github/client/settings/SettingsActivity.kt @@ -1,17 +1,31 @@ package com.jraska.github.client.settings import android.app.Activity +import android.content.Context import android.content.Intent import android.os.Bundle +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.ViewModelProviders import androidx.recyclerview.widget.LinearLayoutManager import com.airbnb.epoxy.SimpleEpoxyAdapter +import com.google.android.play.core.splitcompat.SplitCompat +import com.jraska.github.client.DynamicFeaturesComponent import com.jraska.github.client.core.android.BaseActivity -import com.jraska.github.client.core.android.viewModel +import com.jraska.github.client.core.android.ViewModelFactory +import com.jraska.github.client.dynamicFeaturesComponent +import dagger.Component import kotlinx.android.synthetic.main.activity_settings.toolbar import kotlinx.android.synthetic.main.content_settings.settings_recycler internal class SettingsActivity : BaseActivity() { - private val viewModel: SettingsViewModel by lazy { viewModel(SettingsViewModel::class.java) } + private val viewModel: SettingsViewModel by lazy { + ViewModelProviders.of(this, viewModelFactory()).get(SettingsViewModel::class.java) + } + + override fun attachBaseContext(newBase: Context?) { + super.attachBaseContext(newBase) + SplitCompat.install(this) + } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -29,10 +43,18 @@ internal class SettingsActivity : BaseActivity() { viewModel.onPurchaseSubmitted(value) } - companion object { - fun start(inActivity: Activity) { - val intent = Intent(inActivity, SettingsActivity::class.java) - inActivity.startActivity(intent) - } + private fun viewModelFactory(): ViewModelProvider.Factory { + return DaggerSettingsComponent.builder() + .dynamicFeaturesComponent(dynamicFeaturesComponent()) + .build() + .viewModelFactory() } } + +@Component( + modules = [SettingsModule::class], + dependencies = [DynamicFeaturesComponent::class] +) +internal interface SettingsComponent { + fun viewModelFactory(): ViewModelFactory +} diff --git a/feature/settings/src/main/java/com/jraska/github/client/settings/SettingsFeature.kt b/feature/settings/src/main/java/com/jraska/github/client/settings/SettingsFeature.kt new file mode 100644 index 00000000..c633f6aa --- /dev/null +++ b/feature/settings/src/main/java/com/jraska/github/client/settings/SettingsFeature.kt @@ -0,0 +1,13 @@ +package com.jraska.github.client.settings + +import androidx.annotation.Keep +import com.jraska.github.client.dynamicbase.DynamicFeature +import timber.log.Timber + +@Keep // used by reflection +class SettingsFeature : DynamicFeature { + override fun onFeatureCreate() { + SetupConsoleLogging().onCreate() + Timber.i("Settings feature loaded") + } +} diff --git a/feature/settings/src/main/java/com/jraska/github/client/settings/SettingsModule.kt b/feature/settings/src/main/java/com/jraska/github/client/settings/SettingsModule.kt index 023d64ac..ec8f7b71 100644 --- a/feature/settings/src/main/java/com/jraska/github/client/settings/SettingsModule.kt +++ b/feature/settings/src/main/java/com/jraska/github/client/settings/SettingsModule.kt @@ -1,15 +1,10 @@ package com.jraska.github.client.settings -import android.app.Activity import androidx.lifecycle.ViewModel -import com.jraska.github.client.core.android.LinkLauncher -import com.jraska.github.client.core.android.OnAppCreate import dagger.Module import dagger.Provides import dagger.multibindings.ClassKey import dagger.multibindings.IntoMap -import dagger.multibindings.IntoSet -import okhttp3.HttpUrl @Module object SettingsModule { @@ -20,31 +15,4 @@ object SettingsModule { internal fun provideUserDetailModel(viewModel: SettingsViewModel): ViewModel { return viewModel } - - @JvmStatic - @Provides - @IntoSet - internal fun consoleLoggingSetup(): OnAppCreate { - return SetupConsoleLogging() - } - - @JvmStatic - @Provides - @IntoSet - internal fun provideSettingsLauncher(): LinkLauncher { - return object : LinkLauncher { - override fun launch(inActivity: Activity, deepLink: HttpUrl): LinkLauncher.Result { - return if ("/settings" == deepLink.encodedPath) { - SettingsActivity.start(inActivity) - LinkLauncher.Result.LAUNCHED - } else { - LinkLauncher.Result.NOT_LAUNCHED - } - } - - override fun priority(): LinkLauncher.Priority { - return LinkLauncher.Priority.EXACT_MATCH - } - } - } } diff --git a/feature/settings/src/main/java/com/jraska/github/client/settings/SettingsViewModel.kt b/feature/settings/src/main/java/com/jraska/github/client/settings/SettingsViewModel.kt index 04d7d5c4..25edfe02 100644 --- a/feature/settings/src/main/java/com/jraska/github/client/settings/SettingsViewModel.kt +++ b/feature/settings/src/main/java/com/jraska/github/client/settings/SettingsViewModel.kt @@ -5,7 +5,10 @@ import com.jraska.github.client.analytics.AnalyticsEvent import com.jraska.github.client.analytics.EventAnalytics import javax.inject.Inject -internal class SettingsViewModel @Inject constructor(private val eventAnalytics: EventAnalytics) : ViewModel() { +internal class SettingsViewModel @Inject +constructor( + private val eventAnalytics: EventAnalytics +) : ViewModel() { fun onPurchaseSubmitted(value: String) { val money = value.toDoubleOrNull() ?: return diff --git a/feature/settings/src/main/java/com/jraska/github/client/settings/SetupConsoleLogging.kt b/feature/settings/src/main/java/com/jraska/github/client/settings/SetupConsoleLogging.kt index 1a32076e..85d64e4b 100644 --- a/feature/settings/src/main/java/com/jraska/github/client/settings/SetupConsoleLogging.kt +++ b/feature/settings/src/main/java/com/jraska/github/client/settings/SetupConsoleLogging.kt @@ -1,13 +1,11 @@ package com.jraska.github.client.settings -import android.app.Application import android.util.Log import com.jraska.console.timber.ConsoleTree -import com.jraska.github.client.core.android.OnAppCreate import timber.log.Timber -internal class SetupConsoleLogging : OnAppCreate { - override fun onCreate(app: Application) { +internal class SetupConsoleLogging { + fun onCreate() { if (BuildConfig.DEBUG) { Timber.plant(ConsoleTree.create()) } else { diff --git a/feature/settings_entrance/.gitignore b/feature/settings_entrance/.gitignore new file mode 100644 index 00000000..796b96d1 --- /dev/null +++ b/feature/settings_entrance/.gitignore @@ -0,0 +1 @@ +/build diff --git a/feature/settings_entrance/build.gradle b/feature/settings_entrance/build.gradle new file mode 100644 index 00000000..4a14173c --- /dev/null +++ b/feature/settings_entrance/build.gradle @@ -0,0 +1,25 @@ +apply plugin: 'com.android.library' +apply plugin: 'kotlin-android' +apply plugin: 'kotlin-kapt' +apply plugin: 'kotlin-android-extensions' + +android { + compileSdkVersion 28 + defaultConfig { + minSdkVersion 21 + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } +} + +dependencies { + api project(':core') + api project(':core-android') + implementation project(':lib-dynamic-features') + + implementation 'com.google.dagger:dagger:2.24' + kapt 'com.google.dagger:dagger-compiler:2.24' +} diff --git a/feature/settings_entrance/src/main/AndroidManifest.xml b/feature/settings_entrance/src/main/AndroidManifest.xml new file mode 100644 index 00000000..875234aa --- /dev/null +++ b/feature/settings_entrance/src/main/AndroidManifest.xml @@ -0,0 +1 @@ + diff --git a/feature/settings_entrance/src/main/java/com/jraska/github/client/settings/entrance/SettingsEntranceModule.kt b/feature/settings_entrance/src/main/java/com/jraska/github/client/settings/entrance/SettingsEntranceModule.kt new file mode 100644 index 00000000..2f9e85c8 --- /dev/null +++ b/feature/settings_entrance/src/main/java/com/jraska/github/client/settings/entrance/SettingsEntranceModule.kt @@ -0,0 +1,28 @@ +package com.jraska.github.client.settings.entrance + +import android.content.Context +import com.jraska.github.client.about.entrance.internal.DynamicSettingsLinkLauncher +import com.jraska.github.client.about.entrance.internal.ReflectiveSettingsFeatureProvider +import com.jraska.github.client.core.android.LinkLauncher +import com.jraska.github.client.dynamicbase.DynamicFeatureSpec +import dagger.Module +import dagger.Provides +import dagger.multibindings.IntoSet + +@Module +object SettingsEntranceModule { + @JvmStatic + @Provides + @IntoSet + internal fun provideSettingsLauncher(launcher: DynamicSettingsLinkLauncher): LinkLauncher { + return launcher + } + + @JvmStatic + @Provides + @IntoSet + internal fun featureProvider(context: Context): DynamicFeatureSpec { + val settingsFeatureName = context.getString(R.string.title_dynamic_feature_settings) + return DynamicFeatureSpec(settingsFeatureName, ReflectiveSettingsFeatureProvider()) + } +} diff --git a/feature/settings_entrance/src/main/java/com/jraska/github/client/settings/entrance/internal/DynamicSettingsLinkLauncher.kt b/feature/settings_entrance/src/main/java/com/jraska/github/client/settings/entrance/internal/DynamicSettingsLinkLauncher.kt new file mode 100644 index 00000000..e54eff31 --- /dev/null +++ b/feature/settings_entrance/src/main/java/com/jraska/github/client/settings/entrance/internal/DynamicSettingsLinkLauncher.kt @@ -0,0 +1,47 @@ +package com.jraska.github.client.about.entrance.internal + +import android.app.Activity +import android.content.Intent +import com.jraska.github.client.core.android.LinkLauncher +import com.jraska.github.client.core.android.TopActivityProvider +import com.jraska.github.client.dynamicbase.DynamicFeatureInstaller +import com.jraska.github.client.rx.AppSchedulers +import com.jraska.github.client.settings.entrance.R +import okhttp3.HttpUrl +import timber.log.Timber +import javax.inject.Inject + +internal class DynamicSettingsLinkLauncher @Inject constructor( + val installer: DynamicFeatureInstaller, + val appSchedulers: AppSchedulers, + val topActivityProvider: TopActivityProvider +) : LinkLauncher { + override fun launch(inActivity: Activity, deepLink: HttpUrl): LinkLauncher.Result { + return if ("/settings" == deepLink.encodedPath) { + installAndLaunchSettingsFeature(inActivity) + LinkLauncher.Result.LAUNCHED + } else { + LinkLauncher.Result.NOT_LAUNCHED + } + } + + override fun priority(): LinkLauncher.Priority = LinkLauncher.Priority.EXACT_MATCH + + private fun installAndLaunchSettingsFeature(inActivity: Activity) { + val aboutFeature = inActivity.getString(R.string.title_dynamic_feature_settings) + + installer.ensureInstalled(aboutFeature) + .subscribeOn(appSchedulers.io) + .observeOn(appSchedulers.mainThread) + .subscribe({ + val activity = topActivityProvider.get() + activity.startActivity(launchIntent(activity)) + }, { Timber.e(it) }) + } + + private fun launchIntent(inActivity: Activity): Intent { + return Intent().apply { + setClassName(inActivity.packageName, "com.jraska.github.client.settings.SettingsActivity") + } + } +} diff --git a/feature/settings_entrance/src/main/java/com/jraska/github/client/settings/entrance/internal/ReflectiveSettingsFeatureProvider.kt b/feature/settings_entrance/src/main/java/com/jraska/github/client/settings/entrance/internal/ReflectiveSettingsFeatureProvider.kt new file mode 100644 index 00000000..513d1fca --- /dev/null +++ b/feature/settings_entrance/src/main/java/com/jraska/github/client/settings/entrance/internal/ReflectiveSettingsFeatureProvider.kt @@ -0,0 +1,10 @@ +package com.jraska.github.client.about.entrance.internal + +import com.jraska.github.client.dynamicbase.DynamicFeature +import javax.inject.Provider + +internal class ReflectiveSettingsFeatureProvider : Provider { + override fun get(): DynamicFeature { + return Class.forName("com.jraska.github.client.settings.SettingsFeature").newInstance() as DynamicFeature + } +} diff --git a/feature/settings_entrance/src/main/res/values/strings.xml b/feature/settings_entrance/src/main/res/values/strings.xml new file mode 100644 index 00000000..446e8daf --- /dev/null +++ b/feature/settings_entrance/src/main/res/values/strings.xml @@ -0,0 +1,3 @@ + + settings + diff --git a/settings.gradle b/settings.gradle index 8c8c75fa..133530c7 100644 --- a/settings.gradle +++ b/settings.gradle @@ -9,6 +9,7 @@ include ':client', ':feature:users', ':feature:push', + ':feature:settings_entrance', ':feature:settings', ':feature:about_entrance', ':feature:about',