Skip to content

Commit

Permalink
Merge branch 'develop' into mwr-638
Browse files Browse the repository at this point in the history
  • Loading branch information
mrZizik authored Sep 27, 2023
2 parents c1f3b74 + c52d330 commit 5dd2e81
Show file tree
Hide file tree
Showing 60 changed files with 523 additions and 49 deletions.
2 changes: 0 additions & 2 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
<activity
android:name=".splash.presentation.SplashActivity"
android:screenOrientation="portrait"
android:theme="@style/SplashTheme"
android:exported="true">

<intent-filter>
Expand Down Expand Up @@ -55,7 +54,6 @@

<activity
android:name="jp.co.soramitsu.feature_assets_impl.presentation.screens.scan.QRCodeScannerActivity"
android:theme="@style/SplashTheme"
android:exported="false"/>

<meta-data
Expand Down
6 changes: 6 additions & 0 deletions app/src/main/java/jp/co/soramitsu/sora/SoraApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import coil.decode.SvgDecoder
import com.google.firebase.FirebaseApp
import dagger.hilt.android.HiltAndroidApp
import javax.inject.Inject
import jp.co.soramitsu.common.domain.DarkThemeManager
import jp.co.soramitsu.common.domain.OptionsProvider
import jp.co.soramitsu.common.io.FileManager
import jp.co.soramitsu.common.resourses.ResourceManager
Expand Down Expand Up @@ -68,6 +69,9 @@ open class SoraApp : Application(), Configuration.Provider, ImageLoaderFactory {
@Inject
lateinit var workerFactory: HiltWorkerFactory

@Inject
lateinit var darkThemeManager: DarkThemeManager

override fun newImageLoader(): ImageLoader {
val loader = ImageLoader.Builder(this).components {
add(svg)
Expand All @@ -91,6 +95,8 @@ open class SoraApp : Application(), Configuration.Provider, ImageLoaderFactory {
OptionsProvider.CURRENT_VERSION_CODE = BuildConfig.VERSION_CODE
OptionsProvider.CURRENT_VERSION_NAME = BuildConfig.VERSION_NAME
OptionsProvider.APPLICATION_ID = BuildConfig.APPLICATION_ID

darkThemeManager.updateUiModeFromCache()
}

private fun initLogger() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,18 @@ USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
package jp.co.soramitsu.sora.splash.presentation

import android.animation.ValueAnimator
import android.graphics.PorterDuff
import android.graphics.PorterDuffColorFilter
import android.os.Bundle
import android.view.View
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import com.airbnb.lottie.LottieProperty
import com.airbnb.lottie.model.KeyPath
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import jp.co.soramitsu.common.R
import jp.co.soramitsu.common.util.ext.getColorFromAttrs
import jp.co.soramitsu.feature_main_api.launcher.MainStarter
import jp.co.soramitsu.feature_multiaccount_api.MultiaccountStarter
import jp.co.soramitsu.sora.databinding.ActivitySplashBinding
Expand Down Expand Up @@ -78,7 +84,27 @@ class SplashActivity : AppCompatActivity() {
super.onCreate(savedInstanceState)
setContentView(ActivitySplashBinding.inflate(layoutInflater).also { viewBinding = it }.root)

viewBinding.animationView.addAnimatorUpdateListener(animatorUpdateListener)
val currentPageColorTypedValue = getColorFromAttrs(R.attr.baseBackground)

viewBinding.animationView.apply {
addValueCallback(
KeyPath(
LOTTIE_WILDCARD_GLOBSTAR,
WAVE_ANIMATION_RINGS_PATH,
LOTTIE_WILDCARD_GLOBSTAR,
),
LottieProperty.COLOR_FILTER
) {
PorterDuffColorFilter(
currentPageColorTypedValue.data,
PorterDuff.Mode.MULTIPLY
)
}

addAnimatorUpdateListener(
animatorUpdateListener
)
}

splashViewModel.runtimeInitiated.observe(
this
Expand Down Expand Up @@ -116,4 +142,9 @@ class SplashActivity : AppCompatActivity() {
viewBinding.animationView.removeUpdateListener(animatorUpdateListener)
splashViewModel.nextScreen()
}

private companion object {
const val LOTTIE_WILDCARD_GLOBSTAR = "*"
const val WAVE_ANIMATION_RINGS_PATH = "Ellipse 1"
}
}
4 changes: 2 additions & 2 deletions app/src/main/res/layout/activity_splash.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
android:id="@+id/splashContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/neu_color_100">
android:background="?attr/baseBackground">

<com.airbnb.lottie.LottieAnimationView
android:id="@+id/animation_view"
Expand All @@ -27,7 +27,7 @@
android:visibility="gone"
android:textStyle="bold"
android:fontFamily="@font/sora_bold"
android:textColor="@color/neu_black_default"
android:textColor="?attr/secondaryBackground"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,8 @@ import androidx.compose.material.SnackbarResult
import androidx.compose.material.rememberScaffoldState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.livedata.observeAsState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
Expand All @@ -68,6 +70,7 @@ import com.google.accompanist.navigation.animation.rememberAnimatedNavController
import javax.inject.Inject
import jp.co.soramitsu.common.R
import jp.co.soramitsu.common.domain.BarsColorhandler
import jp.co.soramitsu.common.domain.DarkThemeManager
import jp.co.soramitsu.common.presentation.compose.components.AlertDialogContent
import jp.co.soramitsu.common.presentation.compose.components.Toolbar
import jp.co.soramitsu.common.presentation.compose.theme.SoraAppTheme
Expand All @@ -88,6 +91,9 @@ abstract class SoraBaseFragment<T : BaseViewModel> : Fragment() {
@Inject
lateinit var debounceClickHandler: DebounceClickHandler

@Inject
lateinit var darkThemeManager: DarkThemeManager

override fun onResume() {
super.onResume()
activity?.safeCast<BarsColorhandler>()?.setColor(backgroundColor())
Expand All @@ -110,7 +116,12 @@ abstract class SoraBaseFragment<T : BaseViewModel> : Fragment() {
): View? {
return ComposeView(requireContext()).apply {
setContent {
SoraAppTheme {
val isDarkModeOn: State<Boolean> =
darkThemeManager.darkModeStatusFlow.collectAsState()

SoraAppTheme(
darkTheme = isDarkModeOn.value
) {
val scaffoldState = rememberScaffoldState()
val scrollState = rememberScrollState()
val coroutineScope = rememberCoroutineScope()
Expand Down
196 changes: 196 additions & 0 deletions common/src/main/java/jp/co/soramitsu/common/domain/DarkThemeManager.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,196 @@
/*
This file is part of the SORA network and Polkaswap app.
Copyright (c) 2020, 2021, Polka Biome Ltd. All rights reserved.
SPDX-License-Identifier: BSD-4-Clause
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list
of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or other
materials provided with the distribution.
All advertising materials mentioning features or use of this software must display
the following acknowledgement: This product includes software developed by Polka Biome
Ltd., SORA, and Polkaswap.
Neither the name of the Polka Biome Ltd. nor the names of its contributors may be used
to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY Polka Biome Ltd. AS IS AND ANY EXPRESS OR IMPLIED WARRANTIES,
INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Polka Biome Ltd. BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package jp.co.soramitsu.common.domain

import android.content.Context
import android.content.res.Configuration
import androidx.appcompat.app.AppCompatDelegate
import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject
import javax.inject.Singleton
import jp.co.soramitsu.common.data.SoraPreferences
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.flowOn
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch

@Singleton
@OptIn(ExperimentalCoroutinesApi::class)
class DarkThemeManager @Inject constructor(
@ApplicationContext private val context: Context,
private val coroutineManager: CoroutineManager,
private val soraPreferences: SoraPreferences,
) {

private data class DarkModeSettings(
val isSystemDrivenUiEnabled: Boolean,
val isDarkModeEnabled: Boolean
)

private val mutableDarkThemeSharedFlow = MutableSharedFlow<DarkModeSettings>(
replay = 1,
extraBufferCapacity = 0,
onBufferOverflow = BufferOverflow.DROP_OLDEST
)

/**
* [darkModeStatusFlow] is used to read saved state of dark mode preferences off of SoraPreferences
* and align these results with Application Configuration
*
* Hot StateFlow is used to share the same state of UI mode between screens, and avoid unnecessary
* theme switches
*/
val darkModeStatusFlow: StateFlow<Boolean> = mutableDarkThemeSharedFlow
.mapLatest { darkModeSettings ->
with(darkModeSettings) {
return@mapLatest if (isSystemDrivenUiEnabled) {
/*
Resources.Configuration.uiMode as opposed to AppCompatDelegate.getDefaultNightMode()
is set to Configuration.UI_MODE_NIGHT_YES when system dark mode is enabled
(e.g. extreme battery settings on, or system dark mode is enabled)
*/
val currentAppUiMode = context.resources.configuration.uiMode
.and(Configuration.UI_MODE_NIGHT_MASK)

if (currentAppUiMode == Configuration.UI_MODE_NIGHT_YES)
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
else
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)

AppCompatDelegate.getDefaultNightMode() == AppCompatDelegate.MODE_NIGHT_YES
} else {
/*
On the other hand, AppCompatDelegate controls dark theme in scope of
our application
*/
val currentAppUiMode = AppCompatDelegate.getDefaultNightMode()

when {
currentAppUiMode == AppCompatDelegate.MODE_NIGHT_YES && isDarkModeEnabled -> {
/*
Saved state matches system configuration;
no need to change anything
*/
true
}
currentAppUiMode == AppCompatDelegate.MODE_NIGHT_NO && !isDarkModeEnabled -> {
/*
Saved state matches system configuration;
no need to change anything
*/
false
}
else -> {
/*
Something went wrong, saved state is different from system config;
might happen due to start of the whole application (vs. moving between screens),
while user system is set to light mode
*/

val newUiMode = if (isDarkModeEnabled)
AppCompatDelegate.MODE_NIGHT_YES else
AppCompatDelegate.MODE_NIGHT_NO

AppCompatDelegate.setDefaultNightMode(newUiMode)

AppCompatDelegate.getDefaultNightMode() == AppCompatDelegate.MODE_NIGHT_YES
}
}
}
}
}.flowOn(coroutineManager.main.immediate).stateIn(
scope = coroutineManager.applicationScope,
started = SharingStarted.Eagerly, // Eager mode is used to apply changes as soon as possible
initialValue = AppCompatDelegate.getDefaultNightMode()
.equals(AppCompatDelegate.MODE_NIGHT_YES)
)

fun updateUiModeFromCache() {
coroutineManager.applicationScope.launch {
mutableDarkThemeSharedFlow.emit(
value = DarkModeSettings(
isSystemDrivenUiEnabled = soraPreferences.getBoolean(
field = KEY_SYSTEM_DRIVEN_UI_ENABLED,
defaultValue = false
),
isDarkModeEnabled = soraPreferences.getBoolean(
field = KEY_DARK_THEME_ENABLED,
defaultValue = false
)
)
)
}
}

suspend fun setDarkThemeEnabled(isEnabled: Boolean) {
val newDarkModeValue = soraPreferences.run {
putBoolean(KEY_SYSTEM_DRIVEN_UI_ENABLED, false) // deactivate this, if the other is chosen
putBoolean(KEY_DARK_THEME_ENABLED, isEnabled)
getBoolean(KEY_DARK_THEME_ENABLED, false) // double check, in case it didn't get saved
}

// update listeners with new value
mutableDarkThemeSharedFlow.emit(
value = DarkModeSettings(
isSystemDrivenUiEnabled = false,
isDarkModeEnabled = newDarkModeValue
)
)
}

suspend fun setSystemDrivenUiEnabled(isEnabled: Boolean) {
val newDarkModeValue = soraPreferences.run {
putBoolean(KEY_DARK_THEME_ENABLED, false) // deactivate this, if the other is chosen
putBoolean(KEY_SYSTEM_DRIVEN_UI_ENABLED, isEnabled)
getBoolean(KEY_SYSTEM_DRIVEN_UI_ENABLED, false) // double check, in case it didn't get saved
}

// update listeners with new value
mutableDarkThemeSharedFlow.emit(
value = DarkModeSettings(
isSystemDrivenUiEnabled = newDarkModeValue,
isDarkModeEnabled = false
)
)
}

private companion object {
const val KEY_DARK_THEME_ENABLED = "KEY_DARK_THEME_ENABLED"
const val KEY_SYSTEM_DRIVEN_UI_ENABLED = "KEY_SYSTEM_DRIVEN_UI_ENABLED"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ fun CheckboxButton(
.weight(1f)
.padding(start = Dimens.x2),
style = MaterialTheme.customTypography.paragraphS,
color = MaterialTheme.customColors.fgPrimary,
text = text
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,12 +96,14 @@ fun DetailsItem(
title = {
Text(
text = text,
color = MaterialTheme.customColors.fgPrimary,
style = MaterialTheme.customTypography.textSBold
)
},
text = {
Text(
text = hint,
color = MaterialTheme.customColors.fgPrimary,
style = MaterialTheme.customTypography.paragraphSBold
)
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ fun Option(
bottomDivider: Boolean,
@DrawableRes iconEnd: Int = R.drawable.ic_chevron_right_24,
tint: Boolean = true,
textColor: Color = Color.Unspecified,
textColor: Color = MaterialTheme.customColors.fgPrimary,
enabled: Boolean = true,
onClick: () -> Unit
) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,7 @@ fun OptionSwitch(
) {
Text(
text = label,
color = MaterialTheme.customColors.fgPrimary,
style = MaterialTheme.customTypography.textM,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
Expand Down
Loading

0 comments on commit 5dd2e81

Please sign in to comment.