Migrate MainActivity to compose

This commit is contained in:
Ensar Sarajčić 2023-10-02 09:06:39 +02:00
parent 4c5deb10e4
commit 7937a71b0d
5 changed files with 493 additions and 259 deletions

View File

@ -60,7 +60,7 @@ class BrightDisplayActivity : SimpleActivity() {
}, },
timerVisible = timerVisible, timerVisible = timerVisible,
timerText = timerText, timerText = timerText,
onTimerCloseClick = { onTimerClosePress = {
stopSleepTimer() stopSleepTimer()
} }
) )

View File

@ -2,16 +2,22 @@ package com.simplemobiletools.flashlight.activities
import android.annotation.SuppressLint import android.annotation.SuppressLint
import android.app.AlarmManager import android.app.AlarmManager
import android.app.Application
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.ActivityInfo import android.content.pm.ActivityInfo
import android.content.pm.ShortcutInfo import android.content.pm.ShortcutInfo
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Icon import android.graphics.drawable.Icon
import android.graphics.drawable.LayerDrawable import android.graphics.drawable.LayerDrawable
import android.os.Bundle import android.os.Bundle
import android.view.WindowManager import androidx.activity.compose.setContent
import android.widget.ImageView import androidx.activity.viewModels
import androidx.compose.runtime.getValue
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.google.android.material.math.MathUtils
import com.simplemobiletools.commons.compose.extensions.onEventValue
import com.simplemobiletools.commons.compose.theme.AppThemeSurface
import com.simplemobiletools.commons.dialogs.PermissionRequiredDialog import com.simplemobiletools.commons.dialogs.PermissionRequiredDialog
import com.simplemobiletools.commons.dialogs.RadioGroupDialog import com.simplemobiletools.commons.dialogs.RadioGroupDialog
import com.simplemobiletools.commons.extensions.* import com.simplemobiletools.commons.extensions.*
@ -20,186 +26,142 @@ import com.simplemobiletools.commons.models.FAQItem
import com.simplemobiletools.commons.models.RadioItem import com.simplemobiletools.commons.models.RadioItem
import com.simplemobiletools.flashlight.BuildConfig import com.simplemobiletools.flashlight.BuildConfig
import com.simplemobiletools.flashlight.R import com.simplemobiletools.flashlight.R
import com.simplemobiletools.flashlight.databinding.ActivityMainBinding
import com.simplemobiletools.flashlight.dialogs.SleepTimerCustomDialog import com.simplemobiletools.flashlight.dialogs.SleepTimerCustomDialog
import com.simplemobiletools.flashlight.extensions.config import com.simplemobiletools.flashlight.extensions.config
import com.simplemobiletools.flashlight.helpers.* import com.simplemobiletools.flashlight.helpers.*
import com.simplemobiletools.flashlight.models.Events import com.simplemobiletools.flashlight.models.Events
import com.simplemobiletools.flashlight.screens.MainScreen
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.map
import org.greenrobot.eventbus.EventBus import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode import org.greenrobot.eventbus.ThreadMode
import java.util.* import java.util.*
import kotlin.system.exitProcess
class MainActivity : SimpleActivity() { class MainActivity : SimpleActivity() {
companion object { companion object {
private const val MAX_STROBO_DELAY = 2000L private const val MAX_STROBO_DELAY = 2000L
private const val MIN_STROBO_DELAY = 10L private const val MIN_STROBO_DELAY = 10L
private const val FLASHLIGHT_STATE = "flashlight_state"
private const val STROBOSCOPE_STATE = "stroboscope_state"
} }
private val binding by viewBinding(ActivityMainBinding::inflate) private val viewModel by viewModels<MainViewModel>()
private var mBus: EventBus? = null
private var mCameraImpl: MyCameraImpl? = null
private var mIsFlashlightOn = false
private var reTurnFlashlightOn = true
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
isMaterialActivity = true isMaterialActivity = true
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
setContentView(binding.root)
appLaunched(BuildConfig.APPLICATION_ID) appLaunched(BuildConfig.APPLICATION_ID)
setupOptionsMenu()
refreshMenuItems()
mBus = EventBus.getDefault() setContent {
AppThemeSurface {
val showMoreApps = onEventValue { !resources.getBoolean(R.bool.hide_google_relations) }
val timerVisible by viewModel.timerVisible.collectAsStateWithLifecycle(false)
val timerText by viewModel.timerText.collectAsStateWithLifecycle()
val flashlightActive by viewModel.flashlightOn.collectAsStateWithLifecycle()
val showBrightDisplayButton by config.brightDisplayFlow.collectAsStateWithLifecycle(config.brightDisplay)
val showSosButton by config.sosFlow.collectAsStateWithLifecycle(config.sos)
val sosActive by viewModel.sosActive.collectAsStateWithLifecycle()
val showStroboscopeButton by config.stroboscopeFlow.collectAsStateWithLifecycle(config.stroboscope)
val stroboscopeActive by viewModel.stroboscopeActive.collectAsStateWithLifecycle()
val brightnessBarVisible by viewModel.brightnessBarVisible.collectAsStateWithLifecycle(false)
val brightnessBarValue by viewModel.brightnessBarValue.collectAsStateWithLifecycle()
val stroboscopeBarVisible by viewModel.stroboscopeBarVisible.collectAsStateWithLifecycle(false)
val stroboscopeBarValue by viewModel.stroboscopeBarValue.collectAsStateWithLifecycle()
binding.apply { MainScreen(
updateMaterialActivityViews(mainCoordinator, mainHolder, useTransparentNavigation = true, useTopSearchMenu = false) timerText = timerText,
setupMaterialScrollListener(mainNestedScrollview, mainToolbar) timerVisible = timerVisible,
onTimerClosePress = { stopSleepTimer() },
changeIconColor(getContrastColor(), stroboscopeBtn) onFlashlightPress = { viewModel.toggleFlashlight() },
flashlightActive = flashlightActive,
brightDisplayBtn.setOnClickListener { showBrightDisplayButton = showBrightDisplayButton,
reTurnFlashlightOn = false onBrightDisplayPress = {
startActivity(Intent(applicationContext, BrightDisplayActivity::class.java)) startActivity(Intent(applicationContext, BrightDisplayActivity::class.java))
},
showSosButton = showSosButton,
sosActive = sosActive,
onSosButtonPress = {
toggleStroboscope(true)
},
showStroboscopeButton = showStroboscopeButton,
stroboscopeActive = stroboscopeActive,
onStroboscopeButtonPress = {
toggleStroboscope(false)
},
showBrightnessBar = brightnessBarVisible,
brightnessBarValue = brightnessBarValue,
onBrightnessBarValueChange = {
viewModel.updateBrightnessBarValue(it)
},
showStroboscopeBar = stroboscopeBarVisible,
stroboscopeBarValue = stroboscopeBarValue,
onStroboscopeBarValueChange = {
viewModel.updateStroboscopeBarValue(it)
},
showMoreApps = showMoreApps,
openSettings = ::launchSettings,
openAbout = ::launchAbout,
openSleepTimer = ::showSleepTimer,
moreAppsFromUs = ::launchMoreAppsFromUsIntent
)
} }
flashlightBtn.setOnClickListener {
mCameraImpl!!.toggleFlashlight()
}
sosBtn.setOnClickListener {
toggleStroboscope(true)
}
stroboscopeBtn.setOnClickListener {
toggleStroboscope(false)
}
sleepTimerStop.setOnClickListener { stopSleepTimer() }
} }
setupStroboscope()
checkAppOnSDCard() checkAppOnSDCard()
} }
override fun onResume() { override fun onResume() {
super.onResume() super.onResume()
setupToolbar(binding.mainToolbar) viewModel.onResume()
mCameraImpl!!.handleCameraSetup() // val contrastColor = getContrastColor()
checkState(MyCameraImpl.isFlashlightOn)
val contrastColor = getContrastColor() // binding.apply {
// changeIconColor(contrastColor, brightDisplayBtn)
binding.apply { // brightDisplayBtn.beVisibleIf(config.brightDisplay)
changeIconColor(contrastColor, brightDisplayBtn) // sosBtn.beVisibleIf(config.sos)
brightDisplayBtn.beVisibleIf(config.brightDisplay) //
sosBtn.beVisibleIf(config.sos) // if (sosBtn.currentTextColor != getProperPrimaryColor()) {
// sosBtn.setTextColor(contrastColor)
if (sosBtn.currentTextColor != getProperPrimaryColor()) { // }
sosBtn.setTextColor(contrastColor) //
} // stroboscopeBtn.beVisibleIf(config.stroboscope)
//
stroboscopeBtn.beVisibleIf(config.stroboscope) // if (!config.stroboscope) {
// mCameraImpl!!.stopStroboscope()
if (!config.stroboscope) { // stroboscopeBar.beInvisible()
mCameraImpl!!.stopStroboscope() // }
stroboscopeBar.beInvisible() //
} // updateTextColors(mainHolder)
// if (stroboscopeBar.isInvisible()) {
updateTextColors(mainHolder) // changeIconColor(contrastColor, stroboscopeBtn)
if (stroboscopeBar.isInvisible()) { // }
changeIconColor(contrastColor, stroboscopeBtn) // }
} //
} // binding.sleepTimerHolder.background = ColorDrawable(getProperBackgroundColor())
// binding.sleepTimerStop.applyColorFilter(getProperTextColor())
binding.sleepTimerHolder.background = ColorDrawable(getProperBackgroundColor())
binding.sleepTimerStop.applyColorFilter(getProperTextColor())
requestedOrientation = if (config.forcePortraitMode) ActivityInfo.SCREEN_ORIENTATION_PORTRAIT else ActivityInfo.SCREEN_ORIENTATION_SENSOR requestedOrientation = if (config.forcePortraitMode) ActivityInfo.SCREEN_ORIENTATION_PORTRAIT else ActivityInfo.SCREEN_ORIENTATION_SENSOR
invalidateOptionsMenu() invalidateOptionsMenu()
if (config.turnFlashlightOn && reTurnFlashlightOn) {
mCameraImpl!!.enableFlashlight()
}
reTurnFlashlightOn = true
checkShortcuts() checkShortcuts()
} }
override fun onStart() { override fun onStart() {
super.onStart() super.onStart()
mBus!!.register(this)
if (config.sleepInTS == 0L) { if (config.sleepInTS == 0L) {
binding.sleepTimerHolder.beGone() viewModel.hideTimer()
(getSystemService(Context.ALARM_SERVICE) as AlarmManager).cancel(getShutDownPendingIntent()) (getSystemService(Context.ALARM_SERVICE) as AlarmManager).cancel(getShutDownPendingIntent())
} }
if (mCameraImpl == null) {
setupCameraImpl()
}
}
override fun onStop() {
super.onStop()
mBus!!.unregister(this)
}
override fun onDestroy() {
super.onDestroy()
releaseCamera()
}
private fun setupOptionsMenu() {
binding.mainToolbar.setOnMenuItemClickListener { menuItem ->
when (menuItem.itemId) {
R.id.more_apps_from_us -> launchMoreAppsFromUsIntent()
R.id.settings -> launchSettings()
R.id.sleep_timer -> showSleepTimer()
R.id.about -> launchAbout()
else -> return@setOnMenuItemClickListener false
}
return@setOnMenuItemClickListener true
}
}
private fun refreshMenuItems() {
binding.mainToolbar.menu.apply {
findItem(R.id.more_apps_from_us).isVisible = !resources.getBoolean(R.bool.hide_google_relations)
}
}
override fun onSaveInstanceState(outState: Bundle) {
outState.putBoolean(FLASHLIGHT_STATE, mIsFlashlightOn)
outState.putBoolean(STROBOSCOPE_STATE, binding.stroboscopeBar.isVisible())
super.onSaveInstanceState(outState)
}
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
val isFlashlightOn = savedInstanceState.getBoolean(FLASHLIGHT_STATE, false)
if (isFlashlightOn) {
mCameraImpl!!.toggleFlashlight()
}
val isStroboscopeOn = savedInstanceState.getBoolean(STROBOSCOPE_STATE, false)
if (isStroboscopeOn) {
toggleStroboscope(false)
}
} }
private fun launchSettings() { private fun launchSettings() {
hideKeyboard() hideKeyboard()
reTurnFlashlightOn = false
startActivity(Intent(applicationContext, SettingsActivity::class.java)) startActivity(Intent(applicationContext, SettingsActivity::class.java))
} }
private fun launchAbout() { private fun launchAbout() {
reTurnFlashlightOn = false
val licenses = LICENSE_EVENT_BUS val licenses = LICENSE_EVENT_BUS
val faqItems = arrayListOf( val faqItems = arrayListOf(
@ -215,38 +177,6 @@ class MainActivity : SimpleActivity() {
startAboutActivity(R.string.app_name, licenses, BuildConfig.VERSION_NAME, faqItems, true) startAboutActivity(R.string.app_name, licenses, BuildConfig.VERSION_NAME, faqItems, true)
} }
private fun setupCameraImpl() {
mCameraImpl = MyCameraImpl.newInstance(this, object : CameraTorchListener {
override fun onTorchEnabled(isEnabled: Boolean) {
mCameraImpl!!.onTorchEnabled(isEnabled)
if (mCameraImpl!!.supportsBrightnessControl()) {
binding.brightnessBar.beVisibleIf(isEnabled)
}
}
override fun onTorchUnavailable() {
mCameraImpl!!.onCameraNotAvailable()
}
})
if (config.turnFlashlightOn) {
mCameraImpl!!.enableFlashlight()
}
setupBrightness()
}
private fun setupStroboscope() {
binding.stroboscopeBar.apply {
max = (MAX_STROBO_DELAY - MIN_STROBO_DELAY).toInt()
progress = config.stroboscopeProgress
onSeekBarChangeListener { progress ->
val frequency = max - progress + MIN_STROBO_DELAY
mCameraImpl?.stroboFrequency = frequency
config.stroboscopeFrequency = frequency
config.stroboscopeProgress = progress
}
}
}
private fun toggleStroboscope(isSOS: Boolean) { private fun toggleStroboscope(isSOS: Boolean) {
// use the old Camera API for stroboscope, the new Camera Manager is way too slow // use the old Camera API for stroboscope, the new Camera Manager is way too slow
if (isNougatPlus()) { if (isNougatPlus()) {
@ -262,37 +192,14 @@ class MainActivity : SimpleActivity() {
} }
} }
private fun setupBrightness() {
binding.brightnessBar.apply {
max = mCameraImpl?.getMaximumBrightnessLevel() ?: MIN_BRIGHTNESS_LEVEL
progress = mCameraImpl?.getCurrentBrightnessLevel() ?: MIN_BRIGHTNESS_LEVEL
onSeekBarChangeListener { level ->
val newLevel = level.coerceAtLeast(MIN_BRIGHTNESS_LEVEL)
mCameraImpl?.updateBrightnessLevel(newLevel)
config.brightnessLevel = newLevel
}
}
}
private fun cameraPermissionGranted(isSOS: Boolean) { private fun cameraPermissionGranted(isSOS: Boolean) {
if (isSOS) { if (isSOS) {
val isSOSRunning = mCameraImpl!!.toggleSOS() viewModel.enableSos()
binding.sosBtn.setTextColor(if (isSOSRunning) getProperPrimaryColor() else getContrastColor()) } else {
} else if (mCameraImpl!!.toggleStroboscope()) { viewModel.enableStroboscope()
binding.apply {
stroboscopeBar.beInvisibleIf(stroboscopeBar.isVisible())
changeIconColor(if (stroboscopeBar.isVisible()) getProperPrimaryColor() else getContrastColor(), stroboscopeBtn)
}
} }
} }
private fun getContrastColor() = getProperBackgroundColor().getContrastColor()
private fun releaseCamera() {
mCameraImpl?.releaseCamera()
mCameraImpl = null
}
private fun showSleepTimer(force: Boolean = false) { private fun showSleepTimer(force: Boolean = false) {
val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
if (isSPlus() && !alarmManager.canScheduleExactAlarms() && !force) { if (isSPlus() && !alarmManager.canScheduleExactAlarms() && !force) {
@ -353,72 +260,15 @@ class MainActivity : SimpleActivity() {
} }
private fun startSleepTimer() { private fun startSleepTimer() {
binding.sleepTimerHolder.fadeIn() viewModel.showTimer()
startSleepTimerCountDown() startSleepTimerCountDown()
} }
private fun stopSleepTimer() { private fun stopSleepTimer() {
binding.sleepTimerHolder.fadeOut() viewModel.hideTimer()
stopSleepTimerCountDown() stopSleepTimerCountDown()
} }
@Subscribe(threadMode = ThreadMode.MAIN)
fun sleepTimerChanged(event: Events.SleepTimerChanged) {
binding.sleepTimerValue.text = event.seconds.getFormattedDuration()
binding.sleepTimerHolder.beVisible()
if (event.seconds == 0) {
finish()
}
}
@Subscribe
fun stateChangedEvent(event: Events.StateChanged) {
checkState(event.isEnabled)
}
@Subscribe
fun stopStroboscope(event: Events.StopStroboscope) {
binding.stroboscopeBar.beInvisible()
changeIconColor(getContrastColor(), binding.stroboscopeBtn)
}
@Subscribe
fun stopSOS(event: Events.StopSOS) {
binding.sosBtn.setTextColor(getContrastColor())
}
private fun checkState(isEnabled: Boolean) {
if (isEnabled) {
enableFlashlight()
} else {
disableFlashlight()
}
}
private fun enableFlashlight() {
changeIconColor(getProperPrimaryColor(), binding.flashlightBtn)
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
mIsFlashlightOn = true
binding.apply {
sosBtn.setTextColor(getContrastColor())
changeIconColor(getContrastColor(), stroboscopeBtn)
stroboscopeBar.beInvisible()
}
}
private fun disableFlashlight() {
changeIconColor(getContrastColor(), binding.flashlightBtn)
window.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
mIsFlashlightOn = false
}
private fun changeIconColor(color: Int, imageView: ImageView?) {
imageView!!.background.applyColorFilter(color)
}
@SuppressLint("NewApi") @SuppressLint("NewApi")
private fun checkShortcuts() { private fun checkShortcuts() {
val appIconColor = config.appIconColor val appIconColor = config.appIconColor
@ -450,9 +300,166 @@ class MainActivity : SimpleActivity() {
.build() .build()
} }
@Subscribe internal class MainViewModel(
fun cameraUnavailable(event: Events.CameraUnavailable) { application: Application
toast(R.string.camera_error) ) : AndroidViewModel(application) {
disableFlashlight()
private val _timerText: MutableStateFlow<String> = MutableStateFlow("00:00")
val timerText = _timerText.asStateFlow()
private val _timerVisible: MutableStateFlow<Boolean> = MutableStateFlow(false)
val timerVisible = _timerVisible.asStateFlow()
private val _flashlightOn: MutableStateFlow<Boolean> = MutableStateFlow(false)
val flashlightOn = _flashlightOn.asStateFlow()
val brightnessBarVisible = flashlightOn.map {
it && camera.supportsBrightnessControl()
}
private val _sosActive: MutableStateFlow<Boolean> = MutableStateFlow(false)
val sosActive = _sosActive.asStateFlow()
private val _brightnessBarValue: MutableStateFlow<Float> = MutableStateFlow(0f)
val brightnessBarValue = _brightnessBarValue.asStateFlow()
private val _stroboscopeActive: MutableStateFlow<Boolean> = MutableStateFlow(false)
val stroboscopeActive = _stroboscopeActive.asStateFlow()
val stroboscopeBarVisible = stroboscopeActive.map { it }
private val _stroboscopeBarValue: MutableStateFlow<Float> = MutableStateFlow(0f)
val stroboscopeBarValue = _stroboscopeBarValue.asStateFlow()
private lateinit var camera: MyCameraImpl
init {
EventBus.getDefault().register(this)
camera = MyCameraImpl.newInstance(application, object : CameraTorchListener {
override fun onTorchEnabled(isEnabled: Boolean) {
camera.onTorchEnabled(isEnabled)
if (isEnabled && camera.supportsBrightnessControl()) {
_brightnessBarValue.value = camera.getCurrentBrightnessLevel().toFloat() / camera.getMaximumBrightnessLevel()
}
}
override fun onTorchUnavailable() {
camera.onCameraNotAvailable()
}
})
if (application.config.turnFlashlightOn) {
camera.enableFlashlight()
}
_stroboscopeBarValue.value = application.config.stroboscopeProgress.toFloat() / 100
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun sleepTimerChanged(event: Events.SleepTimerChanged) {
_timerText.value = event.seconds.getFormattedDuration()
_timerVisible.value = true
if (event.seconds == 0) {
exitProcess(0)
}
}
fun hideTimer() {
_timerVisible.value = false
}
fun showTimer() {
_timerVisible.value = true
}
fun updateBrightnessBarValue(newValue: Float) {
_brightnessBarValue.value = newValue
val max = camera.getMaximumBrightnessLevel()
val min = MIN_BRIGHTNESS_LEVEL
val newLevel = MathUtils.lerp(min.toFloat(), max.toFloat(), newValue)
camera.updateBrightnessLevel(newLevel.toInt())
getApplication<Application>().config.brightnessLevel = newLevel.toInt()
}
fun updateStroboscopeBarValue(newValue: Float) {
_stroboscopeBarValue.value = newValue
val max = MAX_STROBO_DELAY
val min = MIN_STROBO_DELAY
val newLevel = MathUtils.lerp(min.toFloat(), max.toFloat(), 1 - newValue)
camera.stroboFrequency = newLevel.toLong()
getApplication<Application>().config.stroboscopeFrequency = newLevel.toLong()
getApplication<Application>().config.stroboscopeProgress = ((1 - newLevel) * 100).toInt()
}
fun onResume() {
checkState(MyCameraImpl.isFlashlightOn)
camera.handleCameraSetup()
if (getApplication<Application>().config.turnFlashlightOn) {
camera.enableFlashlight()
}
}
private fun checkState(isEnabled: Boolean) {
if (isEnabled) {
enableFlashlight()
} else {
disableFlashlight()
}
}
fun enableFlashlight() {
_flashlightOn.value = true
}
private fun disableFlashlight() {
_flashlightOn.value = false
}
@Subscribe
fun stateChangedEvent(event: Events.StateChanged) {
checkState(event.isEnabled)
}
@Subscribe
fun stopStroboscope(event: Events.StopStroboscope) {
_stroboscopeActive.value = false
}
@Subscribe
fun stopSOS(event: Events.StopSOS) {
_sosActive.value = false
}
@Subscribe
fun cameraUnavailable(event: Events.CameraUnavailable) {
getApplication<Application>().toast(R.string.camera_error)
disableFlashlight()
}
override fun onCleared() {
super.onCleared()
EventBus.getDefault().unregister(this)
releaseCamera()
}
fun toggleFlashlight() {
camera.toggleFlashlight()
}
private fun releaseCamera() {
camera.releaseCamera()
}
fun enableSos() {
_sosActive.value = camera.toggleSOS()
}
fun enableStroboscope() {
_stroboscopeActive.value = camera.toggleStroboscope()
}
} }
} }

View File

@ -1,9 +1,14 @@
package com.simplemobiletools.flashlight.screens package com.simplemobiletools.flashlight.screens
import androidx.compose.animation.* import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.border import androidx.compose.foundation.border
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.wrapContentSize
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton import androidx.compose.material3.TextButton
@ -26,7 +31,7 @@ internal fun BrightDisplayScreen(
timerText: String?, timerText: String?,
timerVisible: Boolean, timerVisible: Boolean,
onChangeColorPress: () -> Unit, onChangeColorPress: () -> Unit,
onTimerCloseClick: () -> Unit onTimerClosePress: () -> Unit
) { ) {
Box( Box(
modifier = Modifier modifier = Modifier
@ -51,7 +56,8 @@ internal fun BrightDisplayScreen(
} }
AnimatedVisibility( AnimatedVisibility(
modifier = Modifier.align(Alignment.BottomEnd) modifier = Modifier
.align(Alignment.BottomEnd)
.navigationBarsPadding(), .navigationBarsPadding(),
visible = timerVisible, visible = timerVisible,
enter = fadeIn(), enter = fadeIn(),
@ -59,7 +65,7 @@ internal fun BrightDisplayScreen(
) { ) {
SleepTimer( SleepTimer(
timerText = timerText ?: "", timerText = timerText ?: "",
onCloseClick = onTimerCloseClick onCloseClick = onTimerClosePress
) )
} }
} }
@ -74,7 +80,7 @@ private fun BrightDisplayScreenPreview() {
timerText = "00:00", timerText = "00:00",
timerVisible = true, timerVisible = true,
onChangeColorPress = {}, onChangeColorPress = {},
onTimerCloseClick = {} onTimerClosePress = {}
) )
} }
} }

View File

@ -0,0 +1,220 @@
package com.simplemobiletools.flashlight.screens
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Settings
import androidx.compose.material.icons.outlined.Info
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.TextUnit
import androidx.compose.ui.unit.TextUnitType
import com.simplemobiletools.commons.compose.extensions.AdjustNavigationBarColors
import com.simplemobiletools.commons.compose.extensions.MyDevices
import com.simplemobiletools.commons.compose.menus.ActionItem
import com.simplemobiletools.commons.compose.menus.ActionMenu
import com.simplemobiletools.commons.compose.menus.OverflowMode
import com.simplemobiletools.commons.compose.settings.scaffold.SettingsLazyScaffold
import com.simplemobiletools.commons.compose.settings.scaffold.topAppBarColors
import com.simplemobiletools.commons.compose.settings.scaffold.topAppBarInsets
import com.simplemobiletools.commons.compose.settings.scaffold.topAppBarPaddings
import com.simplemobiletools.commons.compose.theme.AppThemeSurface
import com.simplemobiletools.flashlight.R
import com.simplemobiletools.flashlight.views.SleepTimer
import kotlinx.collections.immutable.toImmutableList
@Composable
internal fun MainScreen(
timerText: String?,
timerVisible: Boolean,
onTimerClosePress: () -> Unit,
flashlightActive: Boolean,
onFlashlightPress: () -> Unit,
showBrightDisplayButton: Boolean,
onBrightDisplayPress: () -> Unit,
showSosButton: Boolean,
sosActive: Boolean,
onSosButtonPress: () -> Unit,
showStroboscopeButton: Boolean,
stroboscopeActive: Boolean,
onStroboscopeButtonPress: () -> Unit,
showBrightnessBar: Boolean,
brightnessBarValue: Float,
onBrightnessBarValueChange: (Float) -> Unit,
showStroboscopeBar: Boolean,
stroboscopeBarValue: Float,
onStroboscopeBarValueChange: (Float) -> Unit,
showMoreApps: Boolean,
openSettings: () -> Unit,
openAbout: () -> Unit,
openSleepTimer: () -> Unit,
moreAppsFromUs: () -> Unit,
) {
AdjustNavigationBarColors()
SettingsLazyScaffold(
customTopBar = { scrolledColor: Color, _: MutableInteractionSource, scrollBehavior: TopAppBarScrollBehavior, statusBarColor: Int, colorTransitionFraction: Float, contrastColor: Color ->
TopAppBar(
title = {},
actions = {
val actionMenus = remember {
val settings =
ActionItem(R.string.settings, icon = Icons.Filled.Settings, doAction = openSettings, overflowMode = OverflowMode.NEVER_OVERFLOW)
val about = ActionItem(R.string.about, icon = Icons.Outlined.Info, doAction = openAbout, overflowMode = OverflowMode.NEVER_OVERFLOW)
val sleepTimer = ActionItem(R.string.sleep_timer, doAction = openSleepTimer, overflowMode = OverflowMode.ALWAYS_OVERFLOW)
val list = mutableListOf(settings, about, sleepTimer)
if (showMoreApps) {
list += ActionItem(R.string.more_apps_from_us, doAction = moreAppsFromUs, overflowMode = OverflowMode.ALWAYS_OVERFLOW)
}
list.toImmutableList()
}
var isMenuVisible by remember { mutableStateOf(false) }
ActionMenu(
items = actionMenus,
numIcons = 2,
isMenuVisible = isMenuVisible,
onMenuToggle = { isMenuVisible = it },
iconsColor = scrolledColor
)
},
scrollBehavior = scrollBehavior,
colors = topAppBarColors(statusBarColor, colorTransitionFraction, contrastColor),
modifier = Modifier.topAppBarPaddings(),
windowInsets = topAppBarInsets()
)
}) {
Column(
modifier = Modifier.fillMaxSize(),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.SpaceEvenly
) {
Icon(
modifier = Modifier
.size(dimensionResource(id = R.dimen.main_button_size))
.padding(vertical = dimensionResource(id = R.dimen.normal_margin))
.clickable(onClick = onFlashlightPress),
painter = painterResource(id = R.drawable.ic_flashlight_vector),
contentDescription = stringResource(id = R.string.flashlight_short),
tint = if (flashlightActive) MaterialTheme.colorScheme.primary else Color.Unspecified
)
if (showBrightDisplayButton) {
Icon(
modifier = Modifier
.size(dimensionResource(id = R.dimen.smaller_button_size))
.padding(vertical = dimensionResource(id = R.dimen.normal_margin))
.clickable(onClick = onBrightDisplayPress),
painter = painterResource(id = R.drawable.ic_bright_display_vector),
contentDescription = stringResource(id = R.string.bright_display)
)
}
if (showSosButton) {
Text(
modifier = Modifier
.padding(vertical = dimensionResource(id = R.dimen.normal_margin))
.clickable(onClick = onSosButtonPress),
text = "SOS",
fontSize = TextUnit(dimensionResource(id = R.dimen.sos_text_size).value, TextUnitType.Sp),
fontWeight = FontWeight.Bold,
color = if (sosActive) MaterialTheme.colorScheme.primary else Color.Unspecified
)
}
if (showStroboscopeButton) {
Icon(
modifier = Modifier
.size(dimensionResource(id = R.dimen.smaller_button_size))
.padding(vertical = dimensionResource(id = R.dimen.normal_margin))
.clickable(onClick = onStroboscopeButtonPress),
painter = painterResource(id = R.drawable.ic_stroboscope_vector),
contentDescription = "",
tint = if (stroboscopeActive) MaterialTheme.colorScheme.primary else Color.Unspecified
)
}
val sliderModifier = Modifier
.padding(dimensionResource(id = R.dimen.activity_margin))
.padding(vertical = dimensionResource(R.dimen.medium_margin))
.padding(bottom = dimensionResource(id = R.dimen.activity_margin))
.size(width = dimensionResource(id = R.dimen.seekbar_width), height = dimensionResource(id = R.dimen.seekbar_height))
if (showBrightnessBar) {
Slider(
modifier = sliderModifier,
value = brightnessBarValue,
onValueChange = onBrightnessBarValueChange
)
}
if (showStroboscopeBar) {
Slider(
modifier = sliderModifier,
value = stroboscopeBarValue,
onValueChange = onStroboscopeBarValueChange
)
}
if (!showBrightnessBar && !showStroboscopeBar) {
Spacer(
modifier = sliderModifier,
)
}
}
AnimatedVisibility(
modifier = Modifier.align(Alignment.BottomEnd),
visible = timerVisible,
enter = fadeIn(),
exit = fadeOut(),
) {
SleepTimer(
timerText = timerText ?: "",
onCloseClick = onTimerClosePress
)
}
}
}
@Composable
@MyDevices
internal fun MainScreenPreview() {
AppThemeSurface {
MainScreen(
timerText = "00:00",
timerVisible = true,
onTimerClosePress = {},
onFlashlightPress = {},
flashlightActive = true,
showBrightDisplayButton = true,
onBrightDisplayPress = {},
showSosButton = true,
sosActive = false,
onSosButtonPress = {},
showStroboscopeButton = true,
stroboscopeActive = false,
onStroboscopeButtonPress = {},
showBrightnessBar = false,
brightnessBarValue = 0f,
onBrightnessBarValueChange = {},
showStroboscopeBar = false,
stroboscopeBarValue = 0f,
onStroboscopeBarValueChange = {},
showMoreApps = true,
openSettings = {},
openAbout = {},
moreAppsFromUs = {},
openSleepTimer = {}
)
}
}

View File

@ -1,5 +1,6 @@
<resources> <resources>
<dimen name="seekbar_width">250dp</dimen> <dimen name="seekbar_width">250dp</dimen>
<dimen name="seekbar_height">20dp</dimen>
<dimen name="main_button_size">180dp</dimen> <dimen name="main_button_size">180dp</dimen>
<dimen name="smaller_button_size">60dp</dimen> <dimen name="smaller_button_size">60dp</dimen>
<dimen name="shortcut_padding">60dp</dimen> <dimen name="shortcut_padding">60dp</dimen>