Skip to content

Commit

Permalink
Feature/in app update notification (#709)
Browse files Browse the repository at this point in the history
* Add modules, add fetcher, add service and interactor

* Add fragment and layout

* Add gitingore files

* Di and logic

* Implement layout and logic

* Complete navigation and progress bar

* Fix date format and clean code

* Fixed version comparing and fixe navigation

* Fixe pr notes

* Changed banners

* Remove unused imports

* Fixed pr notes
  • Loading branch information
antonijzelinskij authored Jan 25, 2023
1 parent 2429a98 commit c112c4a
Show file tree
Hide file tree
Showing 100 changed files with 1,411 additions and 29 deletions.
3 changes: 3 additions & 0 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,9 @@ dependencies {

implementation project(':feature-vote')

implementation project(':feature-versions-api')
implementation project(':feature-versions-impl')

implementation kotlinDep

implementation androidDep
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ import io.novafoundation.nova.splash.SplashRouter
LedgerNavigationModule::class,
CurrencyNavigationModule::class,
GovernanceNavigationModule::class,
VoteNavigationModule::class
VoteNavigationModule::class,
VersionsNavigationModule::class
]
)
class NavigationModule {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package io.novafoundation.nova.app.di.app.navigation

import dagger.Module
import dagger.Provides
import io.novafoundation.nova.app.root.navigation.NavigationHolder
import io.novafoundation.nova.app.root.navigation.versions.VersionsNavigator
import io.novafoundation.nova.common.di.scope.ApplicationScope
import io.novafoundation.nova.feature_versions_api.presentation.VersionsRouter

@Module
class VersionsNavigationModule {

@Provides
@ApplicationScope
fun provideRouter(navigationHolder: NavigationHolder): VersionsRouter = VersionsNavigator(navigationHolder)
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@ import io.novafoundation.nova.feature_onboarding_api.di.OnboardingFeatureApi
import io.novafoundation.nova.feature_onboarding_impl.di.OnboardingFeatureHolder
import io.novafoundation.nova.feature_staking_api.di.StakingFeatureApi
import io.novafoundation.nova.feature_staking_impl.di.StakingFeatureHolder
import io.novafoundation.nova.feature_versions_api.di.VersionsFeatureApi
import io.novafoundation.nova.feature_versions_impl.di.VersionsFeatureHolder
import io.novafoundation.nova.feature_vote.di.VoteFeatureApi
import io.novafoundation.nova.feature_vote.di.VoteFeatureHolder
import io.novafoundation.nova.feature_wallet_api.di.WalletFeatureApi
Expand Down Expand Up @@ -143,4 +145,10 @@ interface ComponentHolderModule {
@ClassKey(NftFeatureApi::class)
@IntoMap
fun provideNftFeature(holder: NftFeatureHolder): FeatureApiHolder

@ApplicationScope
@Binds
@ClassKey(VersionsFeatureApi::class)
@IntoMap
fun provideVersionsFeature(holder: VersionsFeatureHolder): FeatureApiHolder
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import io.novafoundation.nova.feature_assets.di.AssetsFeatureApi
import io.novafoundation.nova.feature_crowdloan_api.di.CrowdloanFeatureApi
import io.novafoundation.nova.feature_currency_api.di.CurrencyFeatureApi
import io.novafoundation.nova.feature_staking_api.di.StakingFeatureApi
import io.novafoundation.nova.feature_versions_api.di.VersionsFeatureApi
import io.novafoundation.nova.feature_wallet_api.di.WalletFeatureApi
import io.novafoundation.nova.runtime.di.RuntimeApi

Expand Down Expand Up @@ -51,7 +52,8 @@ interface RootComponent {
CurrencyFeatureApi::class,
DbApi::class,
CommonApi::class,
RuntimeApi::class
RuntimeApi::class,
VersionsFeatureApi::class
]
)
interface RootFeatureDependenciesComponent : RootDependencies
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,16 @@ import io.novafoundation.nova.feature_crowdloan_api.domain.contributions.Contrib
import io.novafoundation.nova.feature_staking_api.domain.api.StakingRepository
import io.novafoundation.nova.feature_wallet_api.di.Wallet
import io.novafoundation.nova.feature_currency_api.domain.CurrencyInteractor
import io.novafoundation.nova.feature_versions_api.domain.UpdateNotificationsInteractor
import io.novafoundation.nova.feature_wallet_api.domain.interfaces.WalletRepository
import io.novafoundation.nova.runtime.multiNetwork.ChainRegistry
import io.novafoundation.nova.runtime.multiNetwork.connection.ChainConnection
import kotlinx.coroutines.flow.MutableStateFlow

interface RootDependencies {

fun updateNotificationsInteractor(): UpdateNotificationsInteractor

fun contributionsInteractor(): ContributionsInteractor

fun crowdloanRepository(): CrowdloanRepository
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import io.novafoundation.nova.feature_assets.di.AssetsFeatureApi
import io.novafoundation.nova.feature_crowdloan_api.di.CrowdloanFeatureApi
import io.novafoundation.nova.feature_currency_api.di.CurrencyFeatureApi
import io.novafoundation.nova.feature_staking_api.di.StakingFeatureApi
import io.novafoundation.nova.feature_versions_api.di.VersionsFeatureApi
import io.novafoundation.nova.feature_wallet_api.di.WalletFeatureApi
import io.novafoundation.nova.runtime.di.RuntimeApi
import javax.inject.Inject
Expand All @@ -33,6 +34,7 @@ class RootFeatureHolder @Inject constructor(
.currencyFeatureApi(getFeature(CurrencyFeatureApi::class.java))
.crowdloanFeatureApi(getFeature(CrowdloanFeatureApi::class.java))
.runtimeApi(getFeature(RuntimeApi::class.java))
.versionsFeatureApi(getFeature(VersionsFeatureApi::class.java))
.build()

return DaggerRootComponent.factory()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -462,6 +462,10 @@ class Navigator(
}
}

override fun openUpdateNotifications() {
navController?.navigate(R.id.action_open_update_notifications)
}

override fun returnToWallet() {
// to achieve smooth animation
postToUiThread {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package io.novafoundation.nova.app.root.navigation.versions

import android.content.ActivityNotFoundException
import android.content.Intent
import android.net.Uri
import io.novafoundation.nova.app.root.navigation.NavigationHolder
import io.novafoundation.nova.feature_versions_api.presentation.VersionsRouter

class VersionsNavigator(
private val navigationHolder: NavigationHolder
) : VersionsRouter {

override fun openInstallUpdates() {
val activity = navigationHolder.contextManager.getActivity()
require(activity != null)
val packageName = activity.packageName

try {
activity.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=$packageName")))
} catch (e: ActivityNotFoundException) {
activity.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse("https://play.google.com/store/apps/details?id=$packageName")))
}
}

override fun back() {
navigationHolder.navController?.popBackStack()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,6 @@ interface RootRouter {
fun returnToWallet()

fun nonCancellableVerify()

fun openUpdateNotifications()
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import io.novafoundation.nova.common.utils.sequrity.BackgroundAccessObserver
import io.novafoundation.nova.core.updater.Updater
import io.novafoundation.nova.feature_crowdloan_api.domain.contributions.ContributionsInteractor
import io.novafoundation.nova.feature_currency_api.domain.CurrencyInteractor
import io.novafoundation.nova.feature_versions_api.domain.UpdateNotificationsInteractor
import io.novafoundation.nova.runtime.multiNetwork.connection.ChainConnection.ExternalRequirement
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.launchIn
Expand All @@ -27,7 +28,8 @@ class RootViewModel(
private val networkStateMixin: NetworkStateMixin,
private val contributionsInteractor: ContributionsInteractor,
private val backgroundAccessObserver: BackgroundAccessObserver,
private val safeModeService: SafeModeService
private val safeModeService: SafeModeService,
private val updateNotificationsInteractor: UpdateNotificationsInteractor
) : BaseViewModel(), NetworkStateUi by networkStateMixin {

private var willBeClearedForLanguageChange = false
Expand All @@ -44,11 +46,22 @@ class RootViewModel(
.onEach { verifyUserIfNeed() }
.launchIn(this)

checkForUpdates()

syncCurrencies()

updatePhishingAddresses()
}

private fun checkForUpdates() {
launch {
updateNotificationsInteractor.waitPermissionToUpdate()
if (updateNotificationsInteractor.hasImportantUpdates()) {
rootRouter.openUpdateNotifications()
}
}
}

private fun syncCurrencies() {
launch { currencyInteractor.syncCurrencies() }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import io.novafoundation.nova.common.sequrity.SafeModeService
import io.novafoundation.nova.common.utils.sequrity.BackgroundAccessObserver
import io.novafoundation.nova.feature_crowdloan_api.domain.contributions.ContributionsInteractor
import io.novafoundation.nova.feature_currency_api.domain.CurrencyInteractor
import io.novafoundation.nova.feature_versions_api.domain.UpdateNotificationsInteractor
import io.novafoundation.nova.runtime.multiNetwork.connection.ChainConnection
import kotlinx.coroutines.flow.MutableStateFlow

Expand All @@ -39,7 +40,8 @@ class RootActivityModule {
externalRequirementsFlow: MutableStateFlow<ChainConnection.ExternalRequirement>,
contributionsInteractor: ContributionsInteractor,
backgroundAccessObserver: BackgroundAccessObserver,
safeModeService: SafeModeService
safeModeService: SafeModeService,
updateNotificationsInteractor: UpdateNotificationsInteractor
): ViewModel {
return RootViewModel(
interactor,
Expand All @@ -50,7 +52,8 @@ class RootActivityModule {
networkStateMixin,
contributionsInteractor,
backgroundAccessObserver,
safeModeService
safeModeService,
updateNotificationsInteractor
)
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,17 @@ package io.novafoundation.nova.app.root.presentation.main

import io.novafoundation.nova.app.root.domain.RootInteractor
import io.novafoundation.nova.common.base.BaseViewModel
import io.novafoundation.nova.feature_versions_api.domain.UpdateNotificationsInteractor

class MainViewModel(
interactor: RootInteractor,
updateNotificationsInteractor: UpdateNotificationsInteractor
) : BaseViewModel() {

val stakingAvailableLiveData = interactor.stakingAvailableFlow()
.asLiveData()

init {
updateNotificationsInteractor.allowInAppUpdateCheck()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import io.novafoundation.nova.app.root.domain.RootInteractor
import io.novafoundation.nova.app.root.presentation.main.MainViewModel
import io.novafoundation.nova.common.di.viewmodel.ViewModelKey
import io.novafoundation.nova.common.di.viewmodel.ViewModelModule
import io.novafoundation.nova.feature_versions_api.domain.UpdateNotificationsInteractor

@Module(
includes = [
Expand All @@ -22,9 +23,10 @@ class MainFragmentModule {
@IntoMap
@ViewModelKey(MainViewModel::class)
fun provideViewModel(
interactor: RootInteractor
interactor: RootInteractor,
updateNotificationsInteractor: UpdateNotificationsInteractor
): ViewModel {
return MainViewModel(interactor)
return MainViewModel(interactor, updateNotificationsInteractor)
}

@Provides
Expand Down
15 changes: 15 additions & 0 deletions app/src/main/res/navigation/root_nav_graph.xml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,14 @@
app:popEnterAnim="@anim/fragment_open_enter"
app:popExitAnim="@anim/fragment_open_exit" />

<action
android:id="@+id/action_open_update_notifications"
app:destination="@id/updateNotificationsFragment"
app:enterAnim="@anim/fragment_open_enter"
app:exitAnim="@anim/fragment_open_exit"
app:popEnterAnim="@anim/fragment_close_enter"
app:popExitAnim="@anim/fragment_close_exit" />

<fragment
android:id="@+id/splashFragment"
android:name="io.novafoundation.nova.splash.presentation.SplashFragment"
Expand Down Expand Up @@ -71,4 +79,11 @@
app:useAdd="true"
tools:layout="@layout/fragment_pincode" />

<fragment
android:id="@+id/updateNotificationsFragment"
android:name="io.novafoundation.nova.feature_versions_impl.presentation.update.UpdateNotificationFragment"
android:label="updateNotificationsFragment"
app:useAdd="true"
tools:layout="@layout/fragment_update_notifications" />

</navigation>
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ interface ResourceManager {

fun measureInPx(dp: Int): Int

fun formatDateTime(timestamp: Long): String
fun formatDate(timestamp: Long): String
fun formatDuration(elapsedTime: Long): String

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,18 @@ class ResourceManagerImpl(
return px.toInt()
}

override fun formatDate(timestamp: Long): String {
override fun formatDateTime(timestamp: Long): String {
return timestamp.formatDateTime().toString()
}

override fun formatDate(timestamp: Long): String {
return DateUtils.formatDateTime(
contextManager.getApplicationContext(),
timestamp,
DateUtils.FORMAT_SHOW_DATE or DateUtils.FORMAT_SHOW_YEAR or DateUtils.FORMAT_ABBREV_MONTH
)
}

override fun formatTime(timestamp: Long): String {
return DateUtils.formatDateTime(contextManager.getApplicationContext(), timestamp, DateUtils.FORMAT_SHOW_TIME)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package io.novafoundation.nova.common.utils.formatting
import android.content.Context
import android.text.format.DateUtils
import io.novafoundation.nova.common.R
import io.novafoundation.nova.common.resources.ResourceManager
import io.novafoundation.nova.common.utils.daysFromMillis
import io.novafoundation.nova.common.utils.fractionToPercentage
import io.novafoundation.nova.common.utils.isNonNegative
Expand All @@ -15,7 +16,8 @@ import java.util.*
import java.util.concurrent.TimeUnit
import kotlin.time.Duration

const val DATE_ISO_8601 = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
const val DATE_ISO_8601_FULL = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"
const val DATE_ISO_8601_NO_MS = "yyyy-MM-dd'T'HH:mm:ss'Z'"

private const val DECIMAL_PATTERN_BASE = "###,###."

Expand All @@ -26,7 +28,8 @@ private const val FULL_PRECISION = 5
const val ABBREVIATED_PRECISION = 2

private val dateTimeFormat = SimpleDateFormat.getDateTimeInstance()
private val dateTimeFormatISO_8601 by lazy { SimpleDateFormat(DATE_ISO_8601, Locale.getDefault()) }
private val dateTimeFormatISO_8601 by lazy { SimpleDateFormat(DATE_ISO_8601_FULL, Locale.getDefault()) }
private val dateTimeFormatISO_8601_NoMs by lazy { SimpleDateFormat(DATE_ISO_8601_NO_MS, Locale.getDefault()) }

private val defaultAbbreviationFormatter = FixedPrecisionFormatter(ABBREVIATED_PRECISION)
private val defaultFullFormatter = FixedPrecisionFormatter(FULL_PRECISION)
Expand Down Expand Up @@ -91,6 +94,20 @@ fun BigDecimal.formatFractionAsPercentage(): String {
return fractionToPercentage().formatAsPercentage()
}

fun Date.formatDateSinceEpoch(resourceManager: ResourceManager): String {
val currentDays = System.currentTimeMillis().daysFromMillis()
val diff = currentDays - time.daysFromMillis()

if (diff < 0) throw IllegalArgumentException("Past date should be less than current")
return when (diff) {
0L -> resourceManager.getString(R.string.today)
1L -> resourceManager.getString(R.string.yesterday)
else -> {
resourceManager.formatDate(time)
}
}
}

fun Long.formatDaysSinceEpoch(context: Context): String? {
val currentDays = System.currentTimeMillis().daysFromMillis()
val diff = currentDays - this
Expand All @@ -113,6 +130,10 @@ fun parseDateISO_8601(value: String): Date? {
return runCatching { dateTimeFormatISO_8601.parse(value) }.getOrNull()
}

fun parseDateISO_8601_NoMs(value: String): Date? {
return runCatching { dateTimeFormatISO_8601_NoMs.parse(value) }.getOrNull()
}

fun decimalFormatterFor(pattern: String): DecimalFormat {
return DecimalFormat(pattern).apply {
val symbols = decimalFormatSymbols
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ class BannerView @JvmOverloads constructor(
}
}

fun setBannerBackground(@DrawableRes backgroundRes: Int) {
bannerBackground.setBackgroundResource(backgroundRes)
}

fun setImage(@DrawableRes imageRes: Int) {
bannerImage.setImageResource(imageRes)
}
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit c112c4a

Please sign in to comment.