Merge pull request #213 from esensar/compose-migration

Migrate application to compose
This commit is contained in:
Tibor Kaputa 2023-10-18 14:29:22 +02:00 committed by GitHub
commit 7f739c8b62
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
80 changed files with 2040 additions and 1533 deletions

View File

@ -43,6 +43,11 @@ android {
buildFeatures {
viewBinding = true
buildConfig = true
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = libs.versions.composeCompiler.get()
}
buildTypes {
@ -80,6 +85,13 @@ android {
tasks.withType<KotlinCompile> {
kotlinOptions.jvmTarget = project.libs.versions.app.build.kotlinJVMTarget.get()
kotlinOptions.freeCompilerArgs = listOf(
"-opt-in=kotlin.RequiresOptIn",
"-opt-in=androidx.compose.material3.ExperimentalMaterial3Api",
"-opt-in=androidx.compose.material.ExperimentalMaterialApi",
"-opt-in=androidx.compose.foundation.ExperimentalFoundationApi",
"-Xcontext-receivers"
)
}
namespace = libs.versions.app.version.appId.get()
@ -93,6 +105,7 @@ android {
dependencies {
implementation(libs.simple.tools.commons)
implementation(libs.androidx.constraintlayout)
implementation(libs.eventbus)
implementation(libs.bundles.lifecycle)
implementation(libs.bundles.compose)
debugImplementation(libs.bundles.compose.preview)
}

View File

@ -1,6 +0,0 @@
# EventBus
-keepattributes *Annotation*
-keepclassmembers class ** {
@org.greenrobot.eventbus.Subscribe <methods>;
}
-keep enum org.greenrobot.eventbus.ThreadMode { *; }

View File

@ -1,57 +1,95 @@
package com.simplemobiletools.flashlight.activities
import android.app.Application
import android.content.pm.ActivityInfo
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.view.WindowManager
import android.widget.RelativeLayout
import com.simplemobiletools.commons.dialogs.ColorPickerDialog
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.flashlight.databinding.ActivityBrightDisplayBinding
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.annotation.ColorInt
import androidx.compose.runtime.Composable
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewModelScope
import com.simplemobiletools.commons.compose.alert_dialog.rememberAlertDialogState
import com.simplemobiletools.commons.compose.extensions.enableEdgeToEdgeSimple
import com.simplemobiletools.commons.compose.extensions.setShowWhenLockedCompat
import com.simplemobiletools.commons.compose.extensions.setTurnScreenOnCompat
import com.simplemobiletools.commons.compose.theme.AppThemeSurface
import com.simplemobiletools.commons.dialogs.ColorPickerAlertDialog
import com.simplemobiletools.commons.extensions.getContrastColor
import com.simplemobiletools.commons.extensions.getFormattedDuration
import com.simplemobiletools.flashlight.extensions.config
import com.simplemobiletools.flashlight.helpers.SleepTimer
import com.simplemobiletools.flashlight.helpers.stopSleepTimerCountDown
import com.simplemobiletools.flashlight.models.Events
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import com.simplemobiletools.flashlight.screens.BrightDisplayScreen
import com.simplemobiletools.flashlight.views.AnimatedSleepTimer
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlin.system.exitProcess
class BrightDisplayActivity : SimpleActivity() {
private val binding by viewBinding(ActivityBrightDisplayBinding::inflate)
class BrightDisplayActivity : ComponentActivity() {
private val viewModel by viewModels<BrightDisplayViewModel>()
private val preferences by lazy { config }
override fun onCreate(savedInstanceState: Bundle?) {
window.addFlags(
WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or
WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON or
WindowManager.LayoutParams.FLAG_FULLSCREEN
)
useDynamicTheme = false
setShowWhenLockedCompat(true)
setTurnScreenOnCompat(true)
super.onCreate(savedInstanceState)
setContentView(binding.root)
supportActionBar?.hide()
setBackgroundColor(config.brightDisplayColor)
enableEdgeToEdgeSimple()
setContent {
AppThemeSurface {
val brightDisplayColor by preferences.brightDisplayColorFlow.collectAsStateWithLifecycle(preferences.brightDisplayColor)
val colorPickerDialogState = getColorPickerDialogState(brightDisplayColor)
binding.brightDisplayChangeColor.setOnClickListener {
ColorPickerDialog(this, config.brightDisplayColor, true, currentColorCallback = {
setBackgroundColor(it)
}) { wasPositivePressed, color ->
if (wasPositivePressed) {
config.brightDisplayColor = color
val contrastColor = color.getContrastColor()
binding.brightDisplayChangeColor.apply {
setTextColor(contrastColor)
background.applyColorFilter(contrastColor)
}
} else {
setBackgroundColor(config.brightDisplayColor)
}
ScreenContent(colorPickerDialogState::show)
}
}
}
binding.sleepTimerStop.setOnClickListener { stopSleepTimer() }
@Composable
private fun getColorPickerDialogState(
@ColorInt
brightDisplayColor: Int
) = rememberAlertDialogState().apply {
DialogMember {
ColorPickerAlertDialog(
alertDialogState = this,
color = brightDisplayColor,
removeDimmedBackground = true,
onActiveColorChange = viewModel::updateBackgroundColor,
onButtonPressed = { wasPositivePressed, color ->
if (wasPositivePressed) {
config.brightDisplayColor = color
viewModel.updateBackgroundColor(color)
} else {
viewModel.updateBackgroundColor(config.brightDisplayColor)
}
}
)
}
}
@Composable
private fun ScreenContent(onChangeColorButtonPress: () -> Unit) {
val backgroundColor by viewModel.backgroundColor.collectAsStateWithLifecycle()
val contrastColor by remember { derivedStateOf { backgroundColor.getContrastColor() } }
val timerVisible by viewModel.timerVisible.collectAsStateWithLifecycle()
val timerText by viewModel.timerText.collectAsStateWithLifecycle()
BrightDisplayScreen(
backgroundColor = backgroundColor,
contrastColor = contrastColor,
onChangeColorPress = onChangeColorButtonPress,
sleepTimer = {
AnimatedSleepTimer(timerText = timerText, timerVisible = timerVisible, onTimerClosePress = ::stopSleepTimer)
}
)
}
override fun onResume() {
@ -59,8 +97,6 @@ class BrightDisplayActivity : SimpleActivity() {
window.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
toggleBrightness(true)
requestedOrientation = if (config.forcePortraitMode) ActivityInfo.SCREEN_ORIENTATION_PORTRAIT else ActivityInfo.SCREEN_ORIENTATION_SENSOR
(binding.sleepTimerHolder.layoutParams as RelativeLayout.LayoutParams).bottomMargin = navigationBarHeight
}
override fun onPause() {
@ -69,28 +105,6 @@ class BrightDisplayActivity : SimpleActivity() {
toggleBrightness(false)
}
override fun onStart() {
super.onStart()
EventBus.getDefault().register(this)
}
override fun onStop() {
super.onStop()
EventBus.getDefault().unregister(this)
}
private fun setBackgroundColor(color: Int) {
binding.apply {
brightDisplay.background = ColorDrawable(color)
val contrastColor = config.brightDisplayColor.getContrastColor()
brightDisplayChangeColor.apply {
setTextColor(contrastColor)
background.applyColorFilter(contrastColor)
}
}
}
private fun toggleBrightness(increase: Boolean) {
val layout = window.attributes
layout.screenBrightness = (if (increase) 1 else 0).toFloat()
@ -98,17 +112,44 @@ class BrightDisplayActivity : SimpleActivity() {
}
private fun stopSleepTimer() {
binding.sleepTimerHolder.fadeOut()
viewModel.hideTimer()
stopSleepTimerCountDown()
}
@Subscribe(threadMode = ThreadMode.MAIN)
fun sleepTimerChanged(event: Events.SleepTimerChanged) {
binding.sleepTimerValue.text = event.seconds.getFormattedDuration()
binding.sleepTimerHolder.beVisible()
if (event.seconds == 0) {
exitProcess(0)
internal class BrightDisplayViewModel(
application: Application
) : AndroidViewModel(application) {
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 _backgroundColor: MutableStateFlow<Int> = MutableStateFlow(application.config.brightDisplayColor)
val backgroundColor = _backgroundColor.asStateFlow()
init {
SleepTimer.timeLeft
.onEach { seconds ->
_timerText.value = seconds.getFormattedDuration()
_timerVisible.value = true
if (seconds == 0) {
exitProcess(0)
}
}
.launchIn(viewModelScope)
}
fun updateBackgroundColor(color: Int) {
_backgroundColor.value = color
}
fun hideTimer() {
_timerVisible.value = false
}
}
}

View File

@ -1,433 +1,404 @@
package com.simplemobiletools.flashlight.activities
import android.Manifest
import android.annotation.SuppressLint
import android.app.AlarmManager
import android.app.Application
import android.content.Context
import android.content.Intent
import android.content.pm.ActivityInfo
import android.content.pm.PackageManager
import android.content.pm.ShortcutInfo
import android.graphics.drawable.ColorDrawable
import android.graphics.drawable.Icon
import android.graphics.drawable.LayerDrawable
import android.os.Bundle
import android.view.WindowManager
import android.widget.ImageView
import com.simplemobiletools.commons.dialogs.PermissionRequiredDialog
import com.simplemobiletools.commons.dialogs.RadioGroupDialog
import androidx.activity.ComponentActivity
import androidx.activity.compose.ManagedActivityResultLauncher
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.compose.setContent
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.viewModels
import androidx.compose.runtime.*
import androidx.compose.ui.res.stringResource
import androidx.core.content.ContextCompat
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.viewModelScope
import com.google.android.material.math.MathUtils
import com.simplemobiletools.commons.compose.alert_dialog.AlertDialogState
import com.simplemobiletools.commons.compose.alert_dialog.rememberAlertDialogState
import com.simplemobiletools.commons.compose.extensions.*
import com.simplemobiletools.commons.compose.theme.AppThemeSurface
import com.simplemobiletools.commons.dialogs.*
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.commons.helpers.*
import com.simplemobiletools.commons.models.FAQItem
import com.simplemobiletools.commons.models.RadioItem
import com.simplemobiletools.flashlight.BuildConfig
import com.simplemobiletools.flashlight.R
import com.simplemobiletools.flashlight.databinding.ActivityMainBinding
import com.simplemobiletools.flashlight.dialogs.SleepTimerCustomDialog
import com.simplemobiletools.flashlight.dialogs.SleepTimerCustomAlertDialog
import com.simplemobiletools.flashlight.extensions.config
import com.simplemobiletools.flashlight.extensions.startAboutActivity
import com.simplemobiletools.flashlight.helpers.*
import com.simplemobiletools.flashlight.models.Events
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import com.simplemobiletools.flashlight.screens.*
import com.simplemobiletools.flashlight.views.AnimatedSleepTimer
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.flow.*
import java.util.*
import kotlin.system.exitProcess
class MainActivity : SimpleActivity() {
class MainActivity : ComponentActivity() {
companion object {
private const val MAX_STROBO_DELAY = 2000L
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 var mBus: EventBus? = null
private var mCameraImpl: MyCameraImpl? = null
private var mIsFlashlightOn = false
private var reTurnFlashlightOn = true
private val preferences by lazy { config }
private val viewModel by viewModels<MainViewModel>()
override fun onCreate(savedInstanceState: Bundle?) {
isMaterialActivity = true
super.onCreate(savedInstanceState)
setContentView(binding.root)
appLaunched(BuildConfig.APPLICATION_ID)
setupOptionsMenu()
refreshMenuItems()
mBus = EventBus.getDefault()
enableEdgeToEdgeSimple()
setContent {
AppThemeSurface {
val showMoreApps = onEventValue { !resources.getBoolean(com.simplemobiletools.commons.R.bool.hide_google_relations) }
val sosPermissionLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.RequestPermission(),
onResult = getPermissionResultHandler(true)
)
val stroboscopePermissionLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.RequestPermission(),
onResult = getPermissionResultHandler(false)
)
binding.apply {
updateMaterialActivityViews(mainCoordinator, mainHolder, useTransparentNavigation = true, useTopSearchMenu = false)
setupMaterialScrollListener(mainNestedScrollview, mainToolbar)
val sleepTimerCustomDialogState = getSleepTimerCustomDialogState()
val sleepTimerDialogState = getSleepTimerDialogState(showCustomSleepTimerDialog = sleepTimerCustomDialogState::show)
val sleepTimerPermissionDialogState = getSleepTimerPermissionDialogState(showSleepTimerDialog = sleepTimerDialogState::show)
changeIconColor(getContrastColor(), stroboscopeBtn)
MainScreen(
flashlightButton = {
val flashlightActive by viewModel.flashlightOn.collectAsStateWithLifecycle()
brightDisplayBtn.setOnClickListener {
reTurnFlashlightOn = false
startActivity(Intent(applicationContext, BrightDisplayActivity::class.java))
FlashlightButton(
onFlashlightPress = { viewModel.toggleFlashlight() },
flashlightActive = flashlightActive,
)
},
brightDisplayButton = {
val showBrightDisplayButton by preferences.brightDisplayFlow.collectAsStateWithLifecycle(config.brightDisplay)
if (showBrightDisplayButton) {
BrightDisplayButton(
onBrightDisplayPress = {
startActivity(Intent(applicationContext, BrightDisplayActivity::class.java))
}
)
}
},
sosButton = {
val showSosButton by preferences.sosFlow.collectAsStateWithLifecycle(config.sos)
val sosActive by viewModel.sosActive.collectAsStateWithLifecycle()
if (showSosButton) {
SosButton(
sosActive = sosActive,
onSosButtonPress = {
toggleStroboscope(true, sosPermissionLauncher)
},
)
}
},
stroboscopeButton = {
val showStroboscopeButton by preferences.stroboscopeFlow.collectAsStateWithLifecycle(config.stroboscope)
val stroboscopeActive by viewModel.stroboscopeActive.collectAsStateWithLifecycle()
if (showStroboscopeButton) {
StroboscopeButton(
stroboscopeActive = stroboscopeActive,
onStroboscopeButtonPress = {
toggleStroboscope(false, stroboscopePermissionLauncher)
},
)
}
},
slidersSection = {
val brightnessBarVisible by viewModel.brightnessBarVisible.collectAsStateWithLifecycle()
val brightnessBarValue by viewModel.brightnessBarValue.collectAsStateWithLifecycle()
val stroboscopeBarVisible by viewModel.stroboscopeBarVisible.collectAsStateWithLifecycle()
val stroboscopeBarValue by viewModel.stroboscopeBarValue.collectAsStateWithLifecycle()
MainScreenSlidersSection(
showBrightnessBar = brightnessBarVisible,
brightnessBarValue = brightnessBarValue,
onBrightnessBarValueChange = viewModel::updateBrightnessBarValue,
showStroboscopeBar = stroboscopeBarVisible,
stroboscopeBarValue = stroboscopeBarValue,
onStroboscopeBarValueChange = viewModel::updateStroboscopeBarValue,
)
},
sleepTimer = {
val timerVisible by viewModel.timerVisible.collectAsStateWithLifecycle()
val timerText by viewModel.timerText.collectAsStateWithLifecycle()
AnimatedSleepTimer(
timerText = timerText,
timerVisible = timerVisible,
onTimerClosePress = { stopSleepTimer() },
)
},
showMoreApps = showMoreApps,
openSettings = ::launchSettings,
openAbout = ::launchAbout,
openSleepTimer = {
showSleepTimerPermission(
requestAlarmPermission = sleepTimerPermissionDialogState::show,
onNoPermissionRequired = sleepTimerDialogState::show
)
},
moreAppsFromUs = ::launchMoreAppsFromUsIntent
)
AppLaunched()
CheckAppOnSdCard()
}
}
}
flashlightBtn.setOnClickListener {
mCameraImpl!!.toggleFlashlight()
@Composable
private fun SleepTimerRadioDialog(
alertDialogState: AlertDialogState,
onCustomValueSelected: () -> Unit
) {
val lastSleepTimerSeconds by preferences.lastSleepTimerSecondsFlow.collectAsStateWithLifecycle(preferences.lastSleepTimerSeconds)
val items by remember {
derivedStateOf {
buildSleepTimerRadioItemsList(lastSleepTimerSeconds)
}
sosBtn.setOnClickListener {
toggleStroboscope(true)
}
stroboscopeBtn.setOnClickListener {
toggleStroboscope(false)
}
sleepTimerStop.setOnClickListener { stopSleepTimer() }
}
setupStroboscope()
checkAppOnSDCard()
RadioGroupAlertDialog(
alertDialogState = alertDialogState,
items = items,
selectedItemId = preferences.lastSleepTimerSeconds,
callback = {
when {
it == -1 -> onCustomValueSelected()
it as Int > 0 -> pickedSleepTimer(it)
}
}
)
}
private fun buildSleepTimerRadioItemsList(
lastSleepTimerSeconds: Int
) = buildList<RadioItem> {
addAll(listOf(10, 30, 60, 5 * 60, 10 * 60, 30 * 60).map {
RadioItem(it, secondsToString(it))
})
if (none { it.id == lastSleepTimerSeconds }) {
add(RadioItem(lastSleepTimerSeconds, secondsToString(lastSleepTimerSeconds)))
}
sortBy { it.id }
add(RadioItem(-1, getString(com.simplemobiletools.commons.R.string.custom)))
}.toImmutableList()
@Composable
private fun AppLaunched(
donateAlertDialogState: AlertDialogState = getDonateAlertDialogState(),
rateStarsAlertDialogState: AlertDialogState = getRateStarsAlertDialogState(),
) {
LaunchedEffect(Unit) {
appLaunchedCompose(
appId = BuildConfig.APPLICATION_ID,
showDonateDialog = donateAlertDialogState::show,
showRateUsDialog = rateStarsAlertDialogState::show,
showUpgradeDialog = {}
)
}
}
@Composable
private fun getDonateAlertDialogState() =
rememberAlertDialogState().apply {
DialogMember {
DonateAlertDialog(alertDialogState = this)
}
}
@Composable
private fun getRateStarsAlertDialogState() = rememberAlertDialogState().apply {
DialogMember {
RateStarsAlertDialog(alertDialogState = this, onRating = ::rateStarsRedirectAndThankYou)
}
}
@Composable
private fun getSleepTimerCustomDialogState() = rememberAlertDialogState().apply {
DialogMember {
SleepTimerCustomAlertDialog(
alertDialogState = this,
onConfirmClick = {
if (it > 0) {
pickedSleepTimer(it)
}
},
)
}
}
@Composable
private fun getSleepTimerDialogState(
showCustomSleepTimerDialog: () -> Unit
) = rememberAlertDialogState().apply {
DialogMember {
SleepTimerRadioDialog(
alertDialogState = this,
onCustomValueSelected = showCustomSleepTimerDialog
)
}
}
@Composable
private fun getSleepTimerPermissionDialogState(
showSleepTimerDialog: () -> Unit
) = rememberAlertDialogState().apply {
DialogMember {
PermissionRequiredAlertDialog(
alertDialogState = this,
text = stringResource(id = com.simplemobiletools.commons.R.string.allow_alarm_sleep_timer),
positiveActionCallback = {
openRequestExactAlarmSettings(baseConfig.appId)
},
negativeActionCallback = showSleepTimerDialog
)
}
}
override fun onResume() {
super.onResume()
setupToolbar(binding.mainToolbar)
mCameraImpl!!.handleCameraSetup()
checkState(MyCameraImpl.isFlashlightOn)
viewModel.onResume()
val contrastColor = getContrastColor()
binding.apply {
changeIconColor(contrastColor, brightDisplayBtn)
brightDisplayBtn.beVisibleIf(config.brightDisplay)
sosBtn.beVisibleIf(config.sos)
if (sosBtn.currentTextColor != getProperPrimaryColor()) {
sosBtn.setTextColor(contrastColor)
}
stroboscopeBtn.beVisibleIf(config.stroboscope)
if (!config.stroboscope) {
mCameraImpl!!.stopStroboscope()
stroboscopeBar.beInvisible()
}
updateTextColors(mainHolder)
if (stroboscopeBar.isInvisible()) {
changeIconColor(contrastColor, stroboscopeBtn)
}
}
binding.sleepTimerHolder.background = ColorDrawable(getProperBackgroundColor())
binding.sleepTimerStop.applyColorFilter(getProperTextColor())
requestedOrientation = if (config.forcePortraitMode) ActivityInfo.SCREEN_ORIENTATION_PORTRAIT else ActivityInfo.SCREEN_ORIENTATION_SENSOR
requestedOrientation = if (preferences.forcePortraitMode) ActivityInfo.SCREEN_ORIENTATION_PORTRAIT else ActivityInfo.SCREEN_ORIENTATION_SENSOR
invalidateOptionsMenu()
if (config.turnFlashlightOn && reTurnFlashlightOn) {
mCameraImpl!!.enableFlashlight()
}
reTurnFlashlightOn = true
checkShortcuts()
}
override fun onStart() {
super.onStart()
mBus!!.register(this)
if (config.sleepInTS == 0L) {
binding.sleepTimerHolder.beGone()
if (preferences.sleepInTS == 0L) {
viewModel.hideTimer()
(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() {
hideKeyboard()
reTurnFlashlightOn = false
startActivity(Intent(applicationContext, SettingsActivity::class.java))
}
private fun launchAbout() {
reTurnFlashlightOn = false
val licenses = LICENSE_EVENT_BUS
val faqItems = arrayListOf(
FAQItem(R.string.faq_1_title_commons, R.string.faq_1_text_commons),
FAQItem(R.string.faq_4_title_commons, R.string.faq_4_text_commons)
FAQItem(com.simplemobiletools.commons.R.string.faq_1_title_commons, com.simplemobiletools.commons.R.string.faq_1_text_commons),
FAQItem(com.simplemobiletools.commons.R.string.faq_4_title_commons, com.simplemobiletools.commons.R.string.faq_4_text_commons)
)
if (!resources.getBoolean(R.bool.hide_google_relations)) {
faqItems.add(FAQItem(R.string.faq_2_title_commons, R.string.faq_2_text_commons))
faqItems.add(FAQItem(R.string.faq_6_title_commons, R.string.faq_6_text_commons))
if (!resources.getBoolean(com.simplemobiletools.commons.R.bool.hide_google_relations)) {
faqItems.add(FAQItem(com.simplemobiletools.commons.R.string.faq_2_title_commons, com.simplemobiletools.commons.R.string.faq_2_text_commons))
faqItems.add(FAQItem(com.simplemobiletools.commons.R.string.faq_6_title_commons, com.simplemobiletools.commons.R.string.faq_6_text_commons))
}
startAboutActivity(R.string.app_name, licenses, BuildConfig.VERSION_NAME, faqItems, true)
startAboutActivity(R.string.app_name, 0, 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, launcher: ManagedActivityResultLauncher<String, Boolean>) {
// use the old Camera API for stroboscope, the new Camera Manager is way too slow
if (isNougatPlus()) {
cameraPermissionGranted(isSOS)
} else {
handlePermission(PERMISSION_CAMERA) {
if (it) {
cameraPermissionGranted(isSOS)
} else {
toast(R.string.camera_permission)
}
val permission = Manifest.permission.CAMERA
if (ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED) {
cameraPermissionGranted(isSOS)
} else {
launcher.launch(permission)
}
}
}
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 getPermissionResultHandler(isSos: Boolean): (Boolean) -> Unit = { granted ->
if (granted) {
cameraPermissionGranted(isSos)
} else {
toast(R.string.camera_permission)
}
}
private fun cameraPermissionGranted(isSOS: Boolean) {
if (isSOS) {
val isSOSRunning = mCameraImpl!!.toggleSOS()
binding.sosBtn.setTextColor(if (isSOSRunning) getProperPrimaryColor() else getContrastColor())
} else if (mCameraImpl!!.toggleStroboscope()) {
binding.apply {
stroboscopeBar.beInvisibleIf(stroboscopeBar.isVisible())
changeIconColor(if (stroboscopeBar.isVisible()) getProperPrimaryColor() else getContrastColor(), stroboscopeBtn)
}
viewModel.enableSos()
} else {
viewModel.enableStroboscope()
}
}
private fun getContrastColor() = getProperBackgroundColor().getContrastColor()
private fun releaseCamera() {
mCameraImpl?.releaseCamera()
mCameraImpl = null
}
private fun showSleepTimer(force: Boolean = false) {
private fun showSleepTimerPermission(
requestAlarmPermission: () -> Unit,
onNoPermissionRequired: () -> Unit
) {
val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
if (isSPlus() && !alarmManager.canScheduleExactAlarms() && !force) {
PermissionRequiredDialog(
this,
com.simplemobiletools.commons.R.string.allow_alarm_sleep_timer,
positiveActionCallback = { openRequestExactAlarmSettings(baseConfig.appId) },
negativeActionCallback = { showSleepTimer(true) }
)
if (isSPlus() && !alarmManager.canScheduleExactAlarms()) {
requestAlarmPermission()
return
}
val items = ArrayList(listOf(10, 30, 60, 5 * 60, 10 * 60, 30 * 60).map {
RadioItem(it, secondsToString(it))
})
if (items.none { it.id == config.lastSleepTimerSeconds }) {
items.add(RadioItem(config.lastSleepTimerSeconds, secondsToString(config.lastSleepTimerSeconds)))
}
items.sortBy { it.id }
items.add(RadioItem(-1, getString(R.string.custom)))
RadioGroupDialog(this, items, config.lastSleepTimerSeconds) {
if (it as Int == -1) {
SleepTimerCustomDialog(this) {
if (it > 0) {
pickedSleepTimer(it)
}
}
} else if (it > 0) {
pickedSleepTimer(it)
}
}
onNoPermissionRequired()
}
private fun secondsToString(seconds: Int): String {
val finalHours = seconds / 3600
val finalMinutes = (seconds / 60) % 60
val finalSeconds = seconds % 60
val parts = mutableListOf<String>()
if (finalHours != 0) {
parts.add(resources.getQuantityString(R.plurals.hours, finalHours, finalHours))
}
if (finalMinutes != 0) {
parts.add(resources.getQuantityString(R.plurals.minutes, finalMinutes, finalMinutes))
}
if (finalSeconds != 0) {
parts.add(resources.getQuantityString(R.plurals.seconds, finalSeconds, finalSeconds))
}
return parts.joinToString(separator = " ")
return buildList {
if (finalHours != 0) {
add(resources.getQuantityString(com.simplemobiletools.commons.R.plurals.hours, finalHours, finalHours))
}
if (finalMinutes != 0) {
add(resources.getQuantityString(com.simplemobiletools.commons.R.plurals.minutes, finalMinutes, finalMinutes))
}
if (finalSeconds != 0) {
add(resources.getQuantityString(com.simplemobiletools.commons.R.plurals.seconds, finalSeconds, finalSeconds))
}
}.joinToString(separator = " ")
}
private fun pickedSleepTimer(seconds: Int) {
config.lastSleepTimerSeconds = seconds
config.sleepInTS = System.currentTimeMillis() + seconds * 1000
preferences.lastSleepTimerSeconds = seconds
preferences.sleepInTS = System.currentTimeMillis() + seconds * 1000
startSleepTimer()
}
private fun startSleepTimer() {
binding.sleepTimerHolder.fadeIn()
viewModel.showTimer()
startSleepTimerCountDown()
}
private fun stopSleepTimer() {
binding.sleepTimerHolder.fadeOut()
viewModel.hideTimer()
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")
private fun checkShortcuts() {
val appIconColor = config.appIconColor
if (isNougatMR1Plus() && config.lastHandledShortcutColor != appIconColor) {
val appIconColor = preferences.appIconColor
if (isNougatMR1Plus() && preferences.lastHandledShortcutColor != appIconColor) {
val createNewContact = getBrightDisplayShortcut(appIconColor)
try {
shortcutManager.dynamicShortcuts = Arrays.asList(createNewContact)
config.lastHandledShortcutColor = appIconColor
shortcutManager.dynamicShortcuts = listOf(createNewContact)
preferences.lastHandledShortcutColor = appIconColor
} catch (ignored: Exception) {
}
}
@ -450,9 +421,147 @@ class MainActivity : SimpleActivity() {
.build()
}
@Subscribe
fun cameraUnavailable(event: Events.CameraUnavailable) {
toast(R.string.camera_error)
disableFlashlight()
internal class MainViewModel(
application: Application
) : AndroidViewModel(application) {
private val preferences = application.config
private lateinit var camera: MyCameraImpl
init {
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 (preferences.turnFlashlightOn) {
camera.enableFlashlight()
}
}
private val _timerText: MutableStateFlow<String> = MutableStateFlow("00:00")
val timerText = _timerText.asStateFlow()
private val _timerVisible: MutableStateFlow<Boolean> = MutableStateFlow(false)
val timerVisible = _timerVisible.asStateFlow()
val flashlightOn = camera.flashlightOnFlow
val brightnessBarVisible = flashlightOn.map {
it && camera.supportsBrightnessControl()
}.stateIn(viewModelScope, SharingStarted.Lazily, false)
private val _sosActive: MutableStateFlow<Boolean> = MutableStateFlow(false)
val sosActive = _sosActive.asStateFlow()
private val _brightnessBarValue: MutableStateFlow<Float> = MutableStateFlow(1f)
val brightnessBarValue = _brightnessBarValue.asStateFlow()
private val _stroboscopeActive: MutableStateFlow<Boolean> = MutableStateFlow(false)
val stroboscopeActive = _stroboscopeActive.asStateFlow()
val stroboscopeBarVisible = stroboscopeActive
private val _stroboscopeBarValue: MutableStateFlow<Float> = MutableStateFlow(0.5f)
val stroboscopeBarValue = _stroboscopeBarValue.asStateFlow()
init {
_stroboscopeBarValue.value = preferences.stroboscopeProgress.toFloat() / MAX_STROBO_DELAY
SleepTimer.timeLeft
.onEach { seconds ->
_timerText.value = seconds.getFormattedDuration()
_timerVisible.value = true
if (seconds == 0) {
exitProcess(0)
}
}
.launchIn(viewModelScope)
MyCameraImpl.cameraError
.onEach { getApplication<Application>().toast(R.string.camera_error) }
.launchIn(viewModelScope)
camera.stroboscopeDisabled
.onEach { _stroboscopeActive.value = false }
.launchIn(viewModelScope)
camera.sosDisabled
.onEach { _sosActive.value = false }
.launchIn(viewModelScope)
}
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())
preferences.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()
preferences.stroboscopeFrequency = newLevel.toLong()
preferences.stroboscopeProgress = ((1 - newLevel) * MAX_STROBO_DELAY).toInt()
}
fun onResume() {
camera.handleCameraSetup()
if (preferences.turnFlashlightOn) {
camera.enableFlashlight()
}
if (!preferences.stroboscope && _stroboscopeActive.value) {
camera.stopStroboscope()
}
if (!preferences.sos && _sosActive.value) {
camera.stopSOS()
}
}
override fun onCleared() {
super.onCleared()
releaseCamera()
}
fun toggleFlashlight() {
camera.toggleFlashlight()
}
private fun releaseCamera() {
camera.releaseCamera()
}
fun enableSos() {
_sosActive.value = camera.toggleSOS()
}
fun enableStroboscope() {
_stroboscopeActive.value = camera.toggleStroboscope()
}
}
}

View File

@ -2,145 +2,94 @@ package com.simplemobiletools.flashlight.activities
import android.content.Intent
import android.os.Bundle
import com.simplemobiletools.commons.extensions.*
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.simplemobiletools.commons.compose.extensions.enableEdgeToEdgeSimple
import com.simplemobiletools.commons.compose.theme.AppThemeSurface
import com.simplemobiletools.commons.helpers.IS_CUSTOMIZING_COLORS
import com.simplemobiletools.commons.helpers.NavigationIcon
import com.simplemobiletools.commons.helpers.isTiramisuPlus
import com.simplemobiletools.flashlight.databinding.ActivitySettingsBinding
import com.simplemobiletools.flashlight.extensions.config
import com.simplemobiletools.flashlight.extensions.launchChangeAppLanguageIntent
import com.simplemobiletools.flashlight.extensions.startCustomizationActivity
import com.simplemobiletools.flashlight.screens.ColorCustomizationSettingsSection
import com.simplemobiletools.flashlight.screens.GeneralSettingsSection
import com.simplemobiletools.flashlight.screens.SettingsScreen
import java.util.Locale
import kotlin.system.exitProcess
class SettingsActivity : SimpleActivity() {
private val binding by viewBinding(ActivitySettingsBinding::inflate)
class SettingsActivity : ComponentActivity() {
private val preferences by lazy { config }
override fun onCreate(savedInstanceState: Bundle?) {
isMaterialActivity = true
super.onCreate(savedInstanceState)
setContentView(binding.root)
enableEdgeToEdgeSimple()
setContent {
AppThemeSurface {
SettingsScreen(
colorCustomizationSection = {
ColorCustomizationSettingsSection(
customizeColors = ::startCustomizationActivity,
customizeWidgetColors = {
Intent(this, WidgetTorchConfigureActivity::class.java).apply {
putExtra(IS_CUSTOMIZING_COLORS, true)
startActivity(this)
}
}
)
},
generalSection = {
val displayLanguage = remember { Locale.getDefault().displayLanguage }
val useEnglishChecked by preferences.useEnglishFlow.collectAsStateWithLifecycle(preferences.useEnglish)
val wasUseEnglishToggled by preferences.wasUseEnglishToggledFlow.collectAsStateWithLifecycle(preferences.wasUseEnglishToggled)
val showUseEnglish by remember {
derivedStateOf {
(wasUseEnglishToggled || Locale.getDefault().language != "en") && !isTiramisuPlus()
}
}
val turnFlashlightOnStartupFlow by preferences.turnFlashlightOnFlow.collectAsStateWithLifecycle(preferences.turnFlashlightOn)
val forcePortraitModeFlow by preferences.forcePortraitModeFlow.collectAsStateWithLifecycle(preferences.forcePortraitMode)
val showBrightDisplayButtonFlow by preferences.brightDisplayFlow.collectAsStateWithLifecycle(preferences.brightDisplay)
val showSosButtonFlow by preferences.sosFlow.collectAsStateWithLifecycle(preferences.sos)
val showStroboscopeButtonFlow by preferences.stroboscopeFlow.collectAsStateWithLifecycle(preferences.stroboscope)
binding.apply {
updateMaterialActivityViews(settingsCoordinator, settingsHolder, useTransparentNavigation = true, useTopSearchMenu = false)
setupMaterialScrollListener(settingsNestedScrollview, settingsToolbar)
}
}
override fun onResume() {
super.onResume()
setupToolbar(binding.settingsToolbar, NavigationIcon.Arrow)
setupPurchaseThankYou()
setupCustomizeColors()
setupCustomizeWidgetColors()
setupUseEnglish()
setupLanguage()
setupTurnFlashlightOn()
setupBrightDisplay()
setupStroboscope()
setupSOS()
setupForcePortrait()
updateTextColors(binding.settingsHolder)
arrayOf(binding.settingsColorCustomizationSectionLabel, binding.settingsGeneralSettingsLabel).forEach {
it.setTextColor(getProperPrimaryColor())
}
}
private fun setupPurchaseThankYou() {
binding.settingsPurchaseThankYouHolder.apply {
beGoneIf(isOrWasThankYouInstalled())
setOnClickListener {
launchPurchaseThankYouIntent()
}
}
}
private fun setupCustomizeColors() {
binding.apply {
settingsColorCustomizationLabel.text = getCustomizeColorsString()
settingsColorCustomizationHolder.setOnClickListener {
handleCustomizeColorsClick()
}
}
}
private fun setupCustomizeWidgetColors() {
binding.settingsWidgetColorCustomizationHolder.setOnClickListener {
Intent(this, WidgetTorchConfigureActivity::class.java).apply {
putExtra(IS_CUSTOMIZING_COLORS, true)
startActivity(this)
}
}
}
private fun setupUseEnglish() {
binding.apply {
settingsUseEnglishHolder.beVisibleIf((config.wasUseEnglishToggled || Locale.getDefault().language != "en") && !isTiramisuPlus())
settingsUseEnglish.isChecked = config.useEnglish
settingsUseEnglishHolder.setOnClickListener {
settingsUseEnglish.toggle()
config.useEnglish = settingsUseEnglish.isChecked
exitProcess(0)
}
}
}
private fun setupLanguage() {
binding.apply {
settingsLanguage.text = Locale.getDefault().displayLanguage
settingsLanguageHolder.beVisibleIf(isTiramisuPlus())
settingsLanguageHolder.setOnClickListener {
launchChangeAppLanguageIntent()
}
}
}
private fun setupTurnFlashlightOn() {
binding.apply {
settingsTurnFlashlightOn.isChecked = config.turnFlashlightOn
settingsTurnFlashlightOnHolder.setOnClickListener {
settingsTurnFlashlightOn.toggle()
config.turnFlashlightOn = settingsTurnFlashlightOn.isChecked
}
}
}
private fun setupBrightDisplay() {
binding.apply {
settingsBrightDisplay.isChecked = config.brightDisplay
settingsBrightDisplayHolder.setOnClickListener {
settingsBrightDisplay.toggle()
config.brightDisplay = settingsBrightDisplay.isChecked
}
}
}
private fun setupStroboscope() {
binding.apply {
settingsStroboscope.isChecked = config.stroboscope
settingsStroboscopeHolder.setOnClickListener {
settingsStroboscope.toggle()
config.stroboscope = settingsStroboscope.isChecked
}
}
}
private fun setupSOS() {
binding.apply {
settingsSos.isChecked = config.sos
settingsSosHolder.setOnClickListener {
settingsSos.toggle()
config.sos = settingsSos.isChecked
}
}
}
private fun setupForcePortrait() {
binding.apply {
settingsForcePortrait.isChecked = config.forcePortraitMode
settingsForcePortraitHolder.setOnClickListener {
settingsForcePortrait.toggle()
config.forcePortraitMode = settingsForcePortrait.isChecked
GeneralSettingsSection(
showUseEnglish = showUseEnglish,
useEnglishChecked = useEnglishChecked,
showDisplayLanguage = isTiramisuPlus(),
displayLanguage = displayLanguage,
onUseEnglishPress = {
config.useEnglish = it
exitProcess(0)
},
onSetupLanguagePress = ::launchChangeAppLanguageIntent,
turnFlashlightOnStartupChecked = turnFlashlightOnStartupFlow,
forcePortraitModeChecked = forcePortraitModeFlow,
showBrightDisplayButtonChecked = showBrightDisplayButtonFlow,
showSosButtonChecked = showSosButtonFlow,
showStroboscopeButtonChecked = showStroboscopeButtonFlow,
onTurnFlashlightOnStartupPress = {
preferences.turnFlashlightOn = it
},
onForcePortraitModePress = {
preferences.forcePortraitMode = it
},
onShowBrightDisplayButtonPress = {
preferences.brightDisplay = it
},
onShowSosButtonPress = {
preferences.sos = it
},
onShowStroboscopeButtonPress = {
preferences.stroboscope = it
},
)
},
goBack = ::finish
)
}
}
}

View File

@ -3,132 +3,115 @@ package com.simplemobiletools.flashlight.activities
import android.app.Activity
import android.appwidget.AppWidgetManager
import android.content.Intent
import android.content.res.ColorStateList
import android.graphics.Color
import android.os.Bundle
import android.widget.SeekBar
import com.simplemobiletools.commons.dialogs.ColorPickerDialog
import com.simplemobiletools.commons.dialogs.FeatureLockedDialog
import com.simplemobiletools.commons.extensions.*
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.annotation.ColorInt
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
import androidx.compose.ui.graphics.Color
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.simplemobiletools.commons.compose.alert_dialog.rememberAlertDialogState
import com.simplemobiletools.commons.compose.extensions.enableEdgeToEdgeSimple
import com.simplemobiletools.commons.compose.system_ui_controller.rememberSystemUiController
import com.simplemobiletools.commons.compose.theme.AppTheme
import com.simplemobiletools.commons.compose.theme.SimpleTheme
import com.simplemobiletools.commons.compose.theme.isLitWell
import com.simplemobiletools.commons.dialogs.ColorPickerAlertDialog
import com.simplemobiletools.commons.extensions.isUsingSystemDarkTheme
import com.simplemobiletools.commons.helpers.IS_CUSTOMIZING_COLORS
import com.simplemobiletools.flashlight.R
import com.simplemobiletools.flashlight.databinding.WidgetBrightDisplayConfigBinding
import com.simplemobiletools.flashlight.activities.viewmodel.WidgetConfigureViewModel
import com.simplemobiletools.flashlight.extensions.CheckFeatureLocked
import com.simplemobiletools.flashlight.extensions.config
import com.simplemobiletools.flashlight.helpers.MyWidgetBrightDisplayProvider
import com.simplemobiletools.flashlight.screens.WidgetConfigureScreen
class WidgetBrightDisplayConfigureActivity : SimpleActivity() {
private val binding by viewBinding(WidgetBrightDisplayConfigBinding::inflate)
private var mWidgetAlpha = 0f
private var mWidgetId = 0
private var mWidgetColor = 0
private var mWidgetColorWithoutTransparency = 0
private var mFeatureLockedDialog: FeatureLockedDialog? = null
class WidgetBrightDisplayConfigureActivity : ComponentActivity() {
private val viewModel by viewModels<WidgetConfigureViewModel>()
public override fun onCreate(savedInstanceState: Bundle?) {
useDynamicTheme = false
super.onCreate(savedInstanceState)
setResult(Activity.RESULT_CANCELED)
setContentView(binding.root)
initVariables()
val isCustomizingColors = intent.extras?.getBoolean(IS_CUSTOMIZING_COLORS) ?: false
mWidgetId = intent.extras?.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID) ?: AppWidgetManager.INVALID_APPWIDGET_ID
viewModel.setWidgetId(intent.extras?.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID) ?: AppWidgetManager.INVALID_APPWIDGET_ID)
if (mWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID && !isCustomizingColors) {
if (viewModel.widgetId.value == AppWidgetManager.INVALID_APPWIDGET_ID && !isCustomizingColors) {
finish()
}
binding.apply {
configSave.setOnClickListener { saveConfig() }
configWidgetColor.setOnClickListener { pickBackgroundColor() }
setContent {
AppTheme {
val primaryColor = getProperPrimaryColor()
configWidgetSeekbar.setColors(getProperTextColor(), primaryColor, primaryColor)
}
val systemUiController = rememberSystemUiController()
val surfaceColor = SimpleTheme.colorScheme.surface
if (!isCustomizingColors && !isOrWasThankYouInstalled()) {
mFeatureLockedDialog = FeatureLockedDialog(this) {
if (!isOrWasThankYouInstalled()) {
finish()
DisposableEffect(systemUiController, !isUsingSystemDarkTheme(), surfaceColor) {
systemUiController.setSystemBarsColor(
color = surfaceColor,
darkIcons = surfaceColor.isLitWell()
)
onDispose { }
}
val widgetColor by viewModel.widgetColor.collectAsStateWithLifecycle()
val widgetAlpha by viewModel.widgetAlpha.collectAsStateWithLifecycle()
val colorPickerDialogState = getColorPickerDialogState(widgetColor)
WidgetConfigureScreen(
widgetDrawable = R.drawable.ic_bright_display_vector,
widgetColor = widgetColor,
widgetAlpha = widgetAlpha,
onSliderChanged = viewModel::changeAlpha,
onColorPressed = colorPickerDialogState::show,
onSavePressed = ::saveConfig
)
CheckFeatureLocked(skipCheck = isCustomizingColors)
}
}
binding.configSave.apply {
backgroundTintList = ColorStateList.valueOf(getProperPrimaryColor())
setTextColor(getProperPrimaryColor().getContrastColor())
}
}
override fun onResume() {
super.onResume()
window.decorView.setBackgroundColor(0)
if (mFeatureLockedDialog != null && isOrWasThankYouInstalled()) {
mFeatureLockedDialog?.dismissDialog()
@Composable
private fun getColorPickerDialogState(
@ColorInt
widgetColor: Int
) = rememberAlertDialogState().apply {
DialogMember {
ColorPickerAlertDialog(
alertDialogState = this,
color = widgetColor,
removeDimmedBackground = true,
onActiveColorChange = {},
onButtonPressed = { wasPositivePressed, color ->
if (wasPositivePressed) {
viewModel.updateColor(color)
}
}
)
}
}
private fun initVariables() {
mWidgetColor = config.widgetBgColor
if (mWidgetColor == resources.getColor(R.color.default_widget_bg_color) && config.isUsingSystemTheme) {
mWidgetColor = resources.getColor(R.color.you_primary_color, theme)
}
mWidgetAlpha = Color.alpha(mWidgetColor) / 255.toFloat()
mWidgetColorWithoutTransparency = Color.rgb(Color.red(mWidgetColor), Color.green(mWidgetColor), Color.blue(mWidgetColor))
binding.configWidgetSeekbar.apply {
setOnSeekBarChangeListener(seekbarChangeListener)
progress = (mWidgetAlpha * 100).toInt()
}
updateColors()
}
private fun saveConfig() {
config.widgetBgColor = mWidgetColor
config.widgetBgColor = viewModel.widgetColor.value
requestWidgetUpdate()
Intent().apply {
putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mWidgetId)
putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, viewModel.widgetId.value)
setResult(Activity.RESULT_OK, this)
}
finish()
}
private fun pickBackgroundColor() {
ColorPickerDialog(this, mWidgetColorWithoutTransparency) { wasPositivePressed, color ->
if (wasPositivePressed) {
mWidgetColorWithoutTransparency = color
updateColors()
}
}
}
private fun requestWidgetUpdate() {
Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE, null, this, MyWidgetBrightDisplayProvider::class.java).apply {
putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, intArrayOf(mWidgetId))
putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, intArrayOf(viewModel.widgetId.value))
sendBroadcast(this)
}
}
private fun updateColors() {
mWidgetColor = mWidgetColorWithoutTransparency.adjustAlpha(mWidgetAlpha)
binding.apply {
configWidgetColor.setFillWithStroke(mWidgetColor, mWidgetColor)
configImage.background.mutate().applyColorFilter(mWidgetColor)
}
}
private val seekbarChangeListener = object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
mWidgetAlpha = progress.toFloat() / 100.toFloat()
updateColors()
}
override fun onStartTrackingTouch(seekBar: SeekBar) {}
override fun onStopTrackingTouch(seekBar: SeekBar) {}
}
}

View File

@ -3,133 +3,116 @@ package com.simplemobiletools.flashlight.activities
import android.app.Activity
import android.appwidget.AppWidgetManager
import android.content.Intent
import android.content.res.ColorStateList
import android.graphics.Color
import android.os.Bundle
import android.widget.SeekBar
import com.simplemobiletools.commons.dialogs.ColorPickerDialog
import com.simplemobiletools.commons.dialogs.FeatureLockedDialog
import com.simplemobiletools.commons.extensions.*
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.viewModels
import androidx.annotation.ColorInt
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.getValue
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.simplemobiletools.commons.compose.alert_dialog.rememberAlertDialogState
import com.simplemobiletools.commons.compose.extensions.enableEdgeToEdgeSimple
import com.simplemobiletools.commons.compose.system_ui_controller.rememberSystemUiController
import com.simplemobiletools.commons.compose.theme.AppTheme
import com.simplemobiletools.commons.compose.theme.SimpleTheme
import com.simplemobiletools.commons.compose.theme.isLitWell
import com.simplemobiletools.commons.dialogs.ColorPickerAlertDialog
import com.simplemobiletools.commons.extensions.isUsingSystemDarkTheme
import com.simplemobiletools.commons.helpers.IS_CUSTOMIZING_COLORS
import com.simplemobiletools.flashlight.R
import com.simplemobiletools.flashlight.databinding.WidgetTorchConfigBinding
import com.simplemobiletools.flashlight.activities.viewmodel.WidgetConfigureViewModel
import com.simplemobiletools.flashlight.extensions.CheckFeatureLocked
import com.simplemobiletools.flashlight.extensions.config
import com.simplemobiletools.flashlight.extensions.updateBrightDisplayWidget
import com.simplemobiletools.flashlight.helpers.MyWidgetTorchProvider
import com.simplemobiletools.flashlight.screens.WidgetConfigureScreen
class WidgetTorchConfigureActivity : SimpleActivity() {
private val binding by viewBinding(WidgetTorchConfigBinding::inflate)
private var mWidgetAlpha = 0f
private var mWidgetId = 0
private var mWidgetColor = 0
private var mWidgetColorWithoutTransparency = 0
private var mFeatureLockedDialog: FeatureLockedDialog? = null
class WidgetTorchConfigureActivity : ComponentActivity() {
private val viewModel by viewModels<WidgetConfigureViewModel>()
public override fun onCreate(savedInstanceState: Bundle?) {
useDynamicTheme = false
super.onCreate(savedInstanceState)
setResult(Activity.RESULT_CANCELED)
setContentView(binding.root)
initVariables()
val isCustomizingColors = intent.extras?.getBoolean(IS_CUSTOMIZING_COLORS) ?: false
mWidgetId = intent.extras?.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID) ?: AppWidgetManager.INVALID_APPWIDGET_ID
viewModel.setWidgetId(intent.extras?.getInt(AppWidgetManager.EXTRA_APPWIDGET_ID) ?: AppWidgetManager.INVALID_APPWIDGET_ID)
if (mWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID && !isCustomizingColors) {
if (viewModel.widgetId.value == AppWidgetManager.INVALID_APPWIDGET_ID && !isCustomizingColors) {
finish()
}
binding.apply {
configSave.setOnClickListener { saveConfig() }
configWidgetColor.setOnClickListener { pickBackgroundColor() }
setContent {
AppTheme {
val primaryColor = getProperPrimaryColor()
configWidgetSeekbar.setColors(getProperTextColor(), primaryColor, primaryColor)
val systemUiController = rememberSystemUiController()
val surfaceColor = SimpleTheme.colorScheme.surface
if (!isCustomizingColors && !isOrWasThankYouInstalled()) {
mFeatureLockedDialog = FeatureLockedDialog(this@WidgetTorchConfigureActivity) {
if (!isOrWasThankYouInstalled()) {
finish()
DisposableEffect(systemUiController, !isUsingSystemDarkTheme(), surfaceColor) {
systemUiController.setSystemBarsColor(
color = surfaceColor,
darkIcons = surfaceColor.isLitWell()
)
onDispose { }
}
val widgetColor by viewModel.widgetColor.collectAsStateWithLifecycle()
val widgetAlpha by viewModel.widgetAlpha.collectAsStateWithLifecycle()
val colorPickerDialogState = getColorPickerDialogState(widgetColor)
WidgetConfigureScreen(
widgetDrawable = R.drawable.ic_flashlight_vector,
widgetColor = widgetColor,
widgetAlpha = widgetAlpha,
onSliderChanged = viewModel::changeAlpha,
onColorPressed = colorPickerDialogState::show,
onSavePressed = ::saveConfig
)
CheckFeatureLocked(skipCheck = isCustomizingColors)
}
}
}
@Composable
private fun getColorPickerDialogState(
@ColorInt
widgetColor: Int
) = rememberAlertDialogState().apply {
DialogMember {
ColorPickerAlertDialog(
alertDialogState = this,
color = widgetColor,
removeDimmedBackground = true,
onActiveColorChange = {},
onButtonPressed = { wasPositivePressed, color ->
if (wasPositivePressed) {
viewModel.updateColor(color)
}
}
}
configSave.backgroundTintList = ColorStateList.valueOf(getProperPrimaryColor())
configSave.setTextColor(getProperPrimaryColor().getContrastColor())
)
}
}
override fun onResume() {
super.onResume()
window.decorView.setBackgroundColor(0)
if (mFeatureLockedDialog != null && isOrWasThankYouInstalled()) {
mFeatureLockedDialog?.dismissDialog()
}
}
private fun initVariables() {
mWidgetColor = config.widgetBgColor
if (mWidgetColor == resources.getColor(R.color.default_widget_bg_color) && config.isUsingSystemTheme) {
mWidgetColor = resources.getColor(R.color.you_primary_color, theme)
}
mWidgetAlpha = Color.alpha(mWidgetColor) / 255.toFloat()
mWidgetColorWithoutTransparency = Color.rgb(Color.red(mWidgetColor), Color.green(mWidgetColor), Color.blue(mWidgetColor))
binding.configWidgetSeekbar.apply {
setOnSeekBarChangeListener(seekbarChangeListener)
progress = (mWidgetAlpha * 100).toInt()
}
updateColors()
}
private fun saveConfig() {
config.widgetBgColor = mWidgetColor
config.widgetBgColor = viewModel.widgetColor.value
requestWidgetUpdate()
Intent().apply {
putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mWidgetId)
putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, viewModel.widgetId.value)
setResult(Activity.RESULT_OK, this)
}
finish()
}
private fun pickBackgroundColor() {
ColorPickerDialog(this, mWidgetColorWithoutTransparency) { wasPositivePressed, color ->
if (wasPositivePressed) {
mWidgetColorWithoutTransparency = color
updateColors()
}
}
}
private fun requestWidgetUpdate() {
Intent(AppWidgetManager.ACTION_APPWIDGET_UPDATE, null, this, MyWidgetTorchProvider::class.java).apply {
putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, intArrayOf(mWidgetId))
putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, intArrayOf(viewModel.widgetId.value))
sendBroadcast(this)
}
updateBrightDisplayWidget()
}
private fun updateColors() {
mWidgetColor = mWidgetColorWithoutTransparency.adjustAlpha(mWidgetAlpha)
binding.apply {
configWidgetColor.setFillWithStroke(mWidgetColor, mWidgetColor)
configImage.background.mutate().applyColorFilter(mWidgetColor)
}
}
private val seekbarChangeListener = object : SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
mWidgetAlpha = progress.toFloat() / 100.toFloat()
updateColors()
}
override fun onStartTrackingTouch(seekBar: SeekBar) {}
override fun onStopTrackingTouch(seekBar: SeekBar) {}
}
}

View File

@ -0,0 +1,45 @@
package com.simplemobiletools.flashlight.activities.viewmodel
import android.app.Application
import android.graphics.Color
import androidx.lifecycle.AndroidViewModel
import com.simplemobiletools.flashlight.R
import com.simplemobiletools.flashlight.extensions.config
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
internal class WidgetConfigureViewModel(
application: Application
) : AndroidViewModel(application) {
private val _widgetAlpha = MutableStateFlow(0f)
val widgetAlpha = _widgetAlpha.asStateFlow()
private val _widgetId = MutableStateFlow(0)
val widgetId = _widgetId.asStateFlow()
private val _widgetColor = MutableStateFlow(0)
val widgetColor = _widgetColor.asStateFlow()
fun changeAlpha(newAlpha: Float) {
_widgetAlpha.value = newAlpha
}
fun updateColor(newColor: Int) {
_widgetColor.value = newColor
}
fun setWidgetId(widgetId: Int) {
_widgetId.value = widgetId
}
init {
_widgetColor.value = application.config.widgetBgColor
if (_widgetColor.value == application.resources.getColor(R.color.default_widget_bg_color, null) && application.config.isUsingSystemTheme) {
_widgetColor.value = application.resources.getColor(com.simplemobiletools.commons.R.color.you_primary_color, application.theme)
}
_widgetAlpha.value = Color.alpha(_widgetColor.value) / 255f
}
}

View File

@ -0,0 +1,143 @@
package com.simplemobiletools.flashlight.dialogs
import androidx.annotation.StringRes
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import com.simplemobiletools.commons.R
import com.simplemobiletools.commons.compose.alert_dialog.AlertDialogState
import com.simplemobiletools.commons.compose.alert_dialog.DialogSurface
import com.simplemobiletools.commons.compose.alert_dialog.dialogTextColor
import com.simplemobiletools.commons.compose.alert_dialog.rememberAlertDialogState
import com.simplemobiletools.commons.compose.extensions.MyDevices
import com.simplemobiletools.commons.compose.theme.AppThemeSurface
import com.simplemobiletools.commons.compose.theme.SimpleTheme
import kotlinx.collections.immutable.toImmutableList
@Composable
fun SleepTimerCustomAlertDialog(
alertDialogState: AlertDialogState,
modifier: Modifier = Modifier,
onConfirmClick: (seconds: Int) -> Unit,
onCancelClick: (() -> Unit)? = null
) {
var selectedItem by remember { mutableIntStateOf(0) }
var value by remember { mutableStateOf("") }
val items = remember {
listOf(UnitItem(R.string.minutes_raw, 60), UnitItem(R.string.seconds_raw, 1)).toImmutableList()
}
AlertDialog(
onDismissRequest = alertDialogState::hide
) {
DialogSurface(
modifier = modifier
) {
Column(
modifier = Modifier.padding(all = 24.dp)
) {
Text(
modifier = Modifier.padding(bottom = SimpleTheme.dimens.padding.extraLarge),
text = stringResource(id = R.string.sleep_timer),
style = SimpleTheme.typography.headlineSmall,
color = SimpleTheme.colorScheme.onSurface
)
Column(
modifier = Modifier
.padding(
horizontal = SimpleTheme.dimens.padding.extraLarge
)
.padding(
top = SimpleTheme.dimens.padding.extraLarge
)
) {
OutlinedTextField(
modifier = Modifier
.fillMaxWidth()
.padding(
bottom = SimpleTheme.dimens.padding.large
),
value = value,
onValueChange = {
value = it.filter { it.isDigit() }
},
label = {
Text(stringResource(id = R.string.value))
},
colors = OutlinedTextFieldDefaults.colors(
disabledTextColor = dialogTextColor,
disabledBorderColor = SimpleTheme.colorScheme.primary,
disabledLabelColor = SimpleTheme.colorScheme.primary,
),
keyboardOptions = KeyboardOptions(
keyboardType = KeyboardType.Number
)
)
items.forEachIndexed { index, item ->
Row(
modifier = Modifier
.clickable {
selectedItem = index
}
.padding(vertical = SimpleTheme.dimens.padding.large)
.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically
) {
RadioButton(
modifier = Modifier.padding(horizontal = SimpleTheme.dimens.padding.small),
selected = index == selectedItem,
onClick = null
)
Text(
text = stringResource(id = item.stringResId)
)
}
}
}
Row(
Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.End
) {
TextButton(onClick = {
onCancelClick?.invoke()
alertDialogState.hide()
}) {
Text(text = stringResource(id = R.string.cancel))
}
TextButton(onClick = {
val enteredValue = Integer.valueOf(value.ifEmpty { "0" })
onConfirmClick(enteredValue * items[selectedItem].multiplier)
alertDialogState.hide()
}) {
Text(text = stringResource(id = R.string.ok))
}
}
}
}
}
}
private data class UnitItem(@StringRes val stringResId: Int, val multiplier: Int)
@Composable
@MyDevices
private fun SleepTimerCustomAlertDialogPreview() {
AppThemeSurface {
SleepTimerCustomAlertDialog(
alertDialogState = rememberAlertDialogState(),
onConfirmClick = {},
onCancelClick = {},
)
}
}

View File

@ -1,49 +0,0 @@
package com.simplemobiletools.flashlight.dialogs
import android.app.Activity
import android.view.inputmethod.EditorInfo
import androidx.appcompat.app.AlertDialog
import com.simplemobiletools.commons.extensions.*
import com.simplemobiletools.flashlight.R
import com.simplemobiletools.flashlight.databinding.DialogCustomSleepTimerPickerBinding
class SleepTimerCustomDialog(val activity: Activity, val callback: (seconds: Int) -> Unit) {
private var dialog: AlertDialog? = null
private val binding = DialogCustomSleepTimerPickerBinding.inflate(activity.layoutInflater)
init {
binding.dialogRadioView.check(R.id.dialog_radio_minutes)
binding.timerValue.setOnEditorActionListener { _, actionId, _ ->
if (actionId == EditorInfo.IME_ACTION_DONE) {
dialogConfirmed()
return@setOnEditorActionListener true
}
return@setOnEditorActionListener false
}
activity.getAlertDialogBuilder()
.setPositiveButton(R.string.ok) { _, _ -> dialogConfirmed() }
.setNegativeButton(R.string.cancel, null)
.apply {
activity.setupDialogStuff(binding.root, this, R.string.sleep_timer) { alertDialog ->
dialog = alertDialog
alertDialog.showKeyboard(binding.timerValue)
}
}
}
private fun dialogConfirmed() {
val value = binding.timerValue.value
val minutes = Integer.valueOf(value.ifEmpty { "0" })
val multiplier = getMultiplier(binding.dialogRadioView.checkedRadioButtonId)
callback(minutes * multiplier)
activity.hideKeyboard()
dialog?.dismiss()
}
private fun getMultiplier(id: Int) = when (id) {
R.id.dialog_radio_seconds -> 1
R.id.dialog_radio_minutes -> 60
else -> 60
}
}

View File

@ -0,0 +1,117 @@
package com.simplemobiletools.flashlight.extensions
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.provider.Settings
import androidx.annotation.RequiresApi
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.platform.LocalContext
import com.simplemobiletools.commons.activities.AboutActivity
import com.simplemobiletools.commons.activities.CustomizationActivity
import com.simplemobiletools.commons.compose.alert_dialog.rememberAlertDialogState
import com.simplemobiletools.commons.compose.extensions.getActivity
import com.simplemobiletools.commons.compose.extensions.onEventValue
import com.simplemobiletools.commons.dialogs.FeatureLockedAlertDialog
import com.simplemobiletools.commons.extensions.hideKeyboard
import com.simplemobiletools.commons.extensions.isOrWasThankYouInstalled
import com.simplemobiletools.commons.extensions.openDeviceSettings
import com.simplemobiletools.commons.helpers.*
import com.simplemobiletools.commons.models.FAQItem
import com.simplemobiletools.flashlight.R
internal fun Activity.startAboutActivity(
appNameId: Int, licenseMask: Long, versionName: String, faqItems: ArrayList<FAQItem>, showFAQBeforeMail: Boolean,
getAppIconIDs: ArrayList<Int> = getAppIconIDs(),
getAppLauncherName: String = launcherName()
) {
hideKeyboard()
Intent(applicationContext, AboutActivity::class.java).apply {
putExtra(APP_ICON_IDS, getAppIconIDs)
putExtra(APP_LAUNCHER_NAME, getAppLauncherName)
putExtra(APP_NAME, getString(appNameId))
putExtra(APP_LICENSES, licenseMask)
putExtra(APP_VERSION_NAME, versionName)
putExtra(APP_FAQ, faqItems)
putExtra(SHOW_FAQ_BEFORE_MAIL, showFAQBeforeMail)
startActivity(this)
}
}
internal fun Activity.startCustomizationActivity(
getAppIconIDs: ArrayList<Int> = getAppIconIDs(),
getAppLauncherName: String = launcherName()
) {
Intent(applicationContext, CustomizationActivity::class.java).apply {
putExtra(APP_ICON_IDS, getAppIconIDs)
putExtra(APP_LAUNCHER_NAME, getAppLauncherName)
startActivity(this)
}
}
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
internal fun Activity.launchChangeAppLanguageIntent() {
try {
Intent(Settings.ACTION_APP_LOCALE_SETTINGS).apply {
data = Uri.fromParts("package", packageName, null)
startActivity(this)
}
} catch (e: Exception) {
openDeviceSettings()
}
}
private fun getAppIconIDs() = arrayListOf(
R.mipmap.ic_launcher_red,
R.mipmap.ic_launcher_pink,
R.mipmap.ic_launcher_purple,
R.mipmap.ic_launcher_deep_purple,
R.mipmap.ic_launcher_indigo,
R.mipmap.ic_launcher_blue,
R.mipmap.ic_launcher_light_blue,
R.mipmap.ic_launcher_cyan,
R.mipmap.ic_launcher_teal,
R.mipmap.ic_launcher_green,
R.mipmap.ic_launcher_light_green,
R.mipmap.ic_launcher_lime,
R.mipmap.ic_launcher_yellow,
R.mipmap.ic_launcher_amber,
R.mipmap.ic_launcher,
R.mipmap.ic_launcher_deep_orange,
R.mipmap.ic_launcher_brown,
R.mipmap.ic_launcher_blue_grey,
R.mipmap.ic_launcher_grey_black
)
private fun Context.launcherName() = getString(R.string.app_launcher_name)
@Composable
fun CheckFeatureLocked(
skipCheck: Boolean
) {
val context = LocalContext.current.getActivity()
val isOrWasThankYouInstalled = onEventValue {
context.isOrWasThankYouInstalled()
}
val featureLockedAlertDialogState = rememberAlertDialogState().apply {
DialogMember {
FeatureLockedAlertDialog(
alertDialogState = this,
cancelCallback = {
if (!isOrWasThankYouInstalled) {
context.finish()
}
}
)
}
}
LaunchedEffect(isOrWasThankYouInstalled) {
when {
!skipCheck && !isOrWasThankYouInstalled -> featureLockedAlertDialogState.show()
isOrWasThankYouInstalled -> featureLockedAlertDialogState.hide()
}
}
}

View File

@ -0,0 +1,15 @@
package com.simplemobiletools.flashlight.helpers
import androidx.compose.runtime.Immutable
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
@Immutable
object AppDimensions {
val seekbarWidth = 250.dp
val seekbarHeight = 20.dp
val mainButtonSize = 180.dp
val smallerButtonSize = 60.dp
val sosTextSize = 26.sp
val widgetColorPickerSize = 50.dp
}

View File

@ -7,8 +7,9 @@ import android.os.Handler
import com.simplemobiletools.commons.extensions.showErrorToast
import com.simplemobiletools.commons.helpers.isTiramisuPlus
import com.simplemobiletools.flashlight.extensions.config
import com.simplemobiletools.flashlight.models.Events
import org.greenrobot.eventbus.EventBus
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
internal class CameraFlash(
private val context: Context,
@ -17,6 +18,8 @@ internal class CameraFlash(
private val manager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager
private val cameraId: String
private val scope = CoroutineScope(Dispatchers.Default)
private val torchCallback = object : CameraManager.TorchCallback() {
override fun onTorchModeChanged(cameraId: String, enabled: Boolean) {
cameraTorchListener?.onTorchEnabled(enabled)
@ -45,11 +48,10 @@ internal class CameraFlash(
manager.setTorchMode(cameraId, enable)
}
} catch (e: Exception) {
context.showErrorToast(e)
val mainRunnable = Runnable {
EventBus.getDefault().post(Events.CameraUnavailable())
scope.launch {
MyCameraImpl.cameraError.emit(Unit)
}
Handler(context.mainLooper).post(mainRunnable)
throw e
}
}

View File

@ -13,18 +13,26 @@ class Config(context: Context) : BaseConfig(context) {
get() = prefs.getBoolean(BRIGHT_DISPLAY, true)
set(brightDisplay) = prefs.edit().putBoolean(BRIGHT_DISPLAY, brightDisplay).apply()
val brightDisplayFlow = ::brightDisplay.asFlowNonNull(emitOnCollect = true)
var stroboscope: Boolean
get() = prefs.getBoolean(STROBOSCOPE, true)
set(stroboscope) = prefs.edit().putBoolean(STROBOSCOPE, stroboscope).apply()
val stroboscopeFlow = ::stroboscope.asFlowNonNull(emitOnCollect = true)
var sos: Boolean
get() = prefs.getBoolean(SOS, true)
set(sos) = prefs.edit().putBoolean(SOS, sos).apply()
val sosFlow = ::sos.asFlowNonNull(emitOnCollect = true)
var turnFlashlightOn: Boolean
get() = prefs.getBoolean(TURN_FLASHLIGHT_ON, false)
set(turnFlashlightOn) = prefs.edit().putBoolean(TURN_FLASHLIGHT_ON, turnFlashlightOn).apply()
val turnFlashlightOnFlow = ::turnFlashlightOn.asFlowNonNull()
var stroboscopeProgress: Int
get() = prefs.getInt(STROBOSCOPE_PROGRESS, 1000)
set(stroboscopeProgress) = prefs.edit().putInt(STROBOSCOPE_PROGRESS, stroboscopeProgress).apply()
@ -37,10 +45,14 @@ class Config(context: Context) : BaseConfig(context) {
get() = prefs.getInt(BRIGHT_DISPLAY_COLOR, Color.WHITE)
set(brightDisplayColor) = prefs.edit().putInt(BRIGHT_DISPLAY_COLOR, brightDisplayColor).apply()
val brightDisplayColorFlow = ::brightDisplayColor.asFlowNonNull()
var forcePortraitMode: Boolean
get() = prefs.getBoolean(FORCE_PORTRAIT_MODE, true)
set(forcePortraitMode) = prefs.edit().putBoolean(FORCE_PORTRAIT_MODE, forcePortraitMode).apply()
val forcePortraitModeFlow = ::forcePortraitMode.asFlowNonNull()
var brightnessLevel: Int
get() = prefs.getInt(BRIGHTNESS_LEVEL, DEFAULT_BRIGHTNESS_LEVEL)
set(brightnessLevel) = prefs.edit().putInt(BRIGHTNESS_LEVEL, brightnessLevel).apply()
@ -49,6 +61,8 @@ class Config(context: Context) : BaseConfig(context) {
get() = prefs.getInt(LAST_SLEEP_TIMER_SECONDS, 30 * 60)
set(lastSleepTimerSeconds) = prefs.edit().putInt(LAST_SLEEP_TIMER_SECONDS, lastSleepTimerSeconds).apply()
val lastSleepTimerSecondsFlow = ::lastSleepTimerSeconds.asFlowNonNull()
var sleepInTS: Long
get() = prefs.getLong(SLEEP_IN_TS, 0)
set(sleepInTS) = prefs.edit().putLong(SLEEP_IN_TS, sleepInTS).apply()

View File

@ -7,17 +7,22 @@ import com.simplemobiletools.commons.extensions.toast
import com.simplemobiletools.flashlight.R
import com.simplemobiletools.flashlight.extensions.config
import com.simplemobiletools.flashlight.extensions.updateWidgets
import com.simplemobiletools.flashlight.models.Events
import org.greenrobot.eventbus.EventBus
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
class MyCameraImpl private constructor(val context: Context, private var cameraTorchListener: CameraTorchListener? = null) {
class MyCameraImpl private constructor(private val context: Context, private var cameraTorchListener: CameraTorchListener? = null) {
var stroboFrequency = 1000L
companion object {
var isFlashlightOn = false
private var u = 200L // The length of one dit (Time unit)
private val SOS = arrayListOf(u, u, u, u, u, u * 3, u * 3, u, u * 3, u, u * 3, u * 3, u, u, u, u, u, u * 7)
private val SOS = listOf(u, u, u, u, u, u * 3, u * 3, u, u * 3, u, u * 3, u * 3, u, u, u, u, u, u * 7)
private var shouldEnableFlashlight = false
private var shouldEnableStroboscope = false
@ -35,9 +40,16 @@ class MyCameraImpl private constructor(val context: Context, private var cameraT
@Volatile
private var isSOSRunning = false
val cameraError = MutableSharedFlow<Unit>()
fun newInstance(context: Context, cameraTorchListener: CameraTorchListener? = null) = MyCameraImpl(context, cameraTorchListener)
}
private val scope = CoroutineScope(Dispatchers.Default)
private val _flashlightOn = MutableStateFlow(false)
val flashlightOnFlow = _flashlightOn.asStateFlow()
private val cameraFlash: CameraFlash?
get() {
if (MyCameraImpl.cameraFlash == null) {
@ -51,6 +63,12 @@ class MyCameraImpl private constructor(val context: Context, private var cameraT
stroboFrequency = context.config.stroboscopeFrequency
}
private val _sosDisabled = MutableSharedFlow<Unit>()
val sosDisabled = _sosDisabled.asSharedFlow()
private val _stroboscopeDisabled = MutableSharedFlow<Unit>()
val stroboscopeDisabled = _stroboscopeDisabled.asSharedFlow()
fun toggleFlashlight() {
isFlashlightOn = !isFlashlightOn
checkFlashlight()
@ -89,7 +107,9 @@ class MyCameraImpl private constructor(val context: Context, private var cameraT
fun stopStroboscope() {
shouldStroboscopeStop = true
EventBus.getDefault().post(Events.StopStroboscope())
scope.launch {
_stroboscopeDisabled.emit(Unit)
}
}
fun toggleSOS(): Boolean {
@ -129,7 +149,9 @@ class MyCameraImpl private constructor(val context: Context, private var cameraT
fun stopSOS() {
shouldStroboscopeStop = true
EventBus.getDefault().post(Events.StopSOS())
scope.launch {
_sosDisabled.emit(Unit)
}
}
private fun tryInitCamera(): Boolean {
@ -147,7 +169,9 @@ class MyCameraImpl private constructor(val context: Context, private var cameraT
MyCameraImpl.cameraFlash = CameraFlash(context, cameraTorchListener)
}
} catch (e: Exception) {
EventBus.getDefault().post(Events.CameraUnavailable())
scope.launch {
cameraError.emit(Unit)
}
}
}
@ -169,13 +193,14 @@ class MyCameraImpl private constructor(val context: Context, private var cameraT
}
try {
cameraFlash.runOrToast {
cameraFlash!!.run {
initialize()
toggleFlashlight(true)
}
} catch (e: Exception) {
context.showErrorToast(e)
disableFlashlight()
return
}
val mainRunnable = Runnable { stateChanged(true) }
@ -188,12 +213,9 @@ class MyCameraImpl private constructor(val context: Context, private var cameraT
}
try {
cameraFlash.runOrToast {
toggleFlashlight(false)
}
cameraFlash!!.toggleFlashlight(false)
} catch (e: Exception) {
context.showErrorToast(e)
disableFlashlight()
}
stateChanged(false)
}
@ -209,7 +231,9 @@ class MyCameraImpl private constructor(val context: Context, private var cameraT
private fun stateChanged(isEnabled: Boolean) {
isFlashlightOn = isEnabled
EventBus.getDefault().post(Events.StateChanged(isEnabled))
scope.launch {
_flashlightOn.emit(isEnabled)
}
context.updateWidgets(isEnabled)
}
@ -248,12 +272,12 @@ class MyCameraImpl private constructor(val context: Context, private var cameraT
handleCameraSetup()
while (!shouldStroboscopeStop) {
try {
cameraFlash.runOrToast {
cameraFlash!!.run {
toggleFlashlight(true)
}
val onDuration = if (isStroboSOS) SOS[sosIndex++ % SOS.size] else stroboFrequency
Thread.sleep(onDuration)
cameraFlash.runOrToast {
cameraFlash!!.run {
toggleFlashlight(false)
}
val offDuration = if (isStroboSOS) SOS[sosIndex++ % SOS.size] else stroboFrequency
@ -276,8 +300,14 @@ class MyCameraImpl private constructor(val context: Context, private var cameraT
shouldStroboscopeStop = false
if (isStroboSOS) {
isSOSRunning = false
scope.launch {
_sosDisabled.emit(Unit)
}
} else {
isStroboscopeRunning = false
scope.launch {
_stroboscopeDisabled.emit(Unit)
}
}
when {

View File

@ -7,23 +7,51 @@ import android.content.Intent
import android.os.CountDownTimer
import com.simplemobiletools.commons.helpers.isSPlus
import com.simplemobiletools.flashlight.extensions.config
import com.simplemobiletools.flashlight.models.Events
import org.greenrobot.eventbus.EventBus
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.launch
import kotlin.system.exitProcess
private var isActive = false
private var sleepTimer: CountDownTimer? = null
object SleepTimer {
private var sleepTimer: CountDownTimer? = null
private val _timeLeft = MutableSharedFlow<Int>()
val timeLeft = _timeLeft.asSharedFlow()
private val scope = CoroutineScope(Dispatchers.Default)
internal fun Context.toggleSleepTimer() {
if (isActive) {
stopSleepTimerCountDown()
} else {
startSleepTimerCountDown()
fun cancel() = synchronized(this@SleepTimer) {
sleepTimer?.cancel()
sleepTimer = null
}
context(Context)
fun startTimer() = synchronized(this@SleepTimer) {
val millisInFuture = config.sleepInTS - System.currentTimeMillis() + 1000L
sleepTimer?.cancel()
sleepTimer = object : CountDownTimer(millisInFuture, 1000) {
override fun onTick(millisUntilFinished: Long) {
val seconds = (millisUntilFinished / 1000).toInt()
scope.launch {
_timeLeft.emit(seconds)
}
}
override fun onFinish() {
config.sleepInTS = 0
scope.launch {
_timeLeft.emit(0)
}
stopSleepTimerCountDown()
exitProcess(0)
}
}
sleepTimer?.start()
}
}
internal fun Context.startSleepTimerCountDown() {
val millisInFuture = config.sleepInTS - System.currentTimeMillis() + 1000L
(getSystemService(Context.ALARM_SERVICE) as AlarmManager).apply {
if (!isSPlus() || canScheduleExactAlarms()) {
setExactAndAllowWhileIdle(
@ -39,30 +67,12 @@ internal fun Context.startSleepTimerCountDown() {
)
}
}
sleepTimer?.cancel()
sleepTimer = object : CountDownTimer(millisInFuture, 1000) {
override fun onTick(millisUntilFinished: Long) {
val seconds = (millisUntilFinished / 1000).toInt()
EventBus.getDefault().post(Events.SleepTimerChanged(seconds))
}
override fun onFinish() {
config.sleepInTS = 0
EventBus.getDefault().post(Events.SleepTimerChanged(0))
stopSleepTimerCountDown()
exitProcess(0)
}
}
sleepTimer?.start()
isActive = true
SleepTimer.startTimer()
}
internal fun Context.stopSleepTimerCountDown() {
(getSystemService(Context.ALARM_SERVICE) as AlarmManager).cancel(getShutDownPendingIntent())
sleepTimer?.cancel()
sleepTimer = null
isActive = false
SleepTimer.cancel()
config.sleepInTS = 0
}

View File

@ -1,13 +0,0 @@
package com.simplemobiletools.flashlight.models
class Events {
class StateChanged(val isEnabled: Boolean)
class CameraUnavailable
class StopStroboscope
class StopSOS
class SleepTimerChanged(val seconds: Int)
}

View File

@ -0,0 +1,74 @@
package com.simplemobiletools.flashlight.screens
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.*
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.simplemobiletools.commons.compose.extensions.MyDevices
import com.simplemobiletools.commons.compose.theme.AppThemeSurface
import com.simplemobiletools.commons.compose.theme.SimpleTheme
import com.simplemobiletools.commons.extensions.getContrastColor
import com.simplemobiletools.flashlight.R
import com.simplemobiletools.flashlight.views.AnimatedSleepTimer
@Composable
internal fun BrightDisplayScreen(
backgroundColor: Int,
contrastColor: Int,
onChangeColorPress: () -> Unit,
sleepTimer: @Composable () -> Unit
) {
Box(
modifier = Modifier
.fillMaxSize()
.background(Color(backgroundColor))
.safeDrawingPadding()
) {
TextButton(
modifier = Modifier
.wrapContentSize()
.align(Alignment.Center)
.border(
width = 1.dp,
color = Color(contrastColor),
shape = SimpleTheme.shapes.extraLarge
),
onClick = onChangeColorPress
) {
Text(
text = stringResource(id = com.simplemobiletools.commons.R.string.change_color),
color = Color(contrastColor)
)
}
Box(
modifier = Modifier
.align(Alignment.BottomEnd)
) {
sleepTimer()
}
}
}
@Composable
@MyDevices
private fun BrightDisplayScreenPreview() {
AppThemeSurface {
BrightDisplayScreen(
backgroundColor = SimpleTheme.colorScheme.background.toArgb(),
contrastColor = SimpleTheme.colorScheme.background.toArgb().getContrastColor(),
sleepTimer = {
AnimatedSleepTimer(timerText = "00:00", timerVisible = true, onTimerClosePress = {})
},
onChangeColorPress = {},
)
}
}

View File

@ -0,0 +1,295 @@
package com.simplemobiletools.flashlight.screens
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
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.graphics.painter.Painter
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.simplemobiletools.commons.compose.extensions.AdjustNavigationBarColors
import com.simplemobiletools.commons.compose.extensions.MyDevices
import com.simplemobiletools.commons.compose.extensions.rememberMutableInteractionSource
import com.simplemobiletools.commons.compose.lists.*
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.theme.AppThemeSurface
import com.simplemobiletools.commons.compose.theme.SimpleTheme
import com.simplemobiletools.flashlight.R
import com.simplemobiletools.flashlight.helpers.AppDimensions
import com.simplemobiletools.flashlight.views.AnimatedSleepTimer
import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList
@Composable
internal fun MainScreen(
flashlightButton: @Composable () -> Unit,
brightDisplayButton: @Composable () -> Unit,
sosButton: @Composable () -> Unit,
stroboscopeButton: @Composable () -> Unit,
slidersSection: @Composable () -> Unit,
sleepTimer: @Composable () -> Unit,
showMoreApps: Boolean,
openSettings: () -> Unit,
openAbout: () -> Unit,
openSleepTimer: () -> Unit,
moreAppsFromUs: () -> Unit,
) {
AdjustNavigationBarColors()
SimpleScaffold(
customTopBar = { scrolledColor: Color, _: MutableInteractionSource, scrollBehavior: TopAppBarScrollBehavior, statusBarColor: Int, colorTransitionFraction: Float, contrastColor: Color ->
TopAppBar(
title = {},
actions = {
val actionMenus = remember { buildActionMenu(showMoreApps, openSettings, openAbout, openSleepTimer, moreAppsFromUs) }
var isMenuVisible by remember { mutableStateOf(false) }
ActionMenu(
items = actionMenus,
numIcons = 2,
isMenuVisible = isMenuVisible,
onMenuToggle = { isMenuVisible = it },
iconsColor = scrolledColor
)
},
scrollBehavior = scrollBehavior,
colors = simpleTopAppBarColors(statusBarColor, colorTransitionFraction, contrastColor),
modifier = Modifier.topAppBarPaddings(),
windowInsets = topAppBarInsets()
)
}) {
Column(
modifier = Modifier
.fillMaxSize()
.verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.SpaceEvenly
) {
flashlightButton()
brightDisplayButton()
sosButton()
stroboscopeButton()
slidersSection()
Spacer(
modifier = Modifier.size(SimpleTheme.dimens.padding.extraLarge)
)
}
Box(
modifier = Modifier.align(Alignment.BottomEnd)
.navigationBarsPadding(),
) {
sleepTimer()
}
}
}
@Composable
internal fun FlashlightButton(
flashlightActive: Boolean,
onFlashlightPress: () -> Unit,
) {
Icon(
modifier = Modifier
.size(AppDimensions.mainButtonSize)
.padding(vertical = SimpleTheme.dimens.padding.large)
.clickable(
indication = null,
interactionSource = rememberMutableInteractionSource(),
onClick = onFlashlightPress
),
painter = painterResource(id = R.drawable.ic_flashlight_vector),
contentDescription = stringResource(id = com.simplemobiletools.commons.R.string.flashlight_short),
tint = if (flashlightActive) SimpleTheme.colorScheme.primary else SimpleTheme.colorScheme.onSurface
)
}
@Composable
internal fun BrightDisplayButton(
onBrightDisplayPress: () -> Unit,
) {
SmallButton(
painter = painterResource(id = R.drawable.ic_bright_display_vector),
contentDescription = stringResource(id = R.string.bright_display),
isActive = false,
onPress = onBrightDisplayPress
)
}
@Composable
internal fun SosButton(
sosActive: Boolean,
onSosButtonPress: () -> Unit,
) {
Text(
modifier = Modifier
.padding(vertical = SimpleTheme.dimens.padding.large)
.clickable(
indication = null,
interactionSource = rememberMutableInteractionSource(),
onClick = onSosButtonPress
),
text = stringResource(id = R.string.sos),
fontSize = AppDimensions.sosTextSize,
fontWeight = FontWeight.Bold,
color = if (sosActive) SimpleTheme.colorScheme.primary else SimpleTheme.colorScheme.onSurface
)
}
@Composable
internal fun StroboscopeButton(
stroboscopeActive: Boolean,
onStroboscopeButtonPress: () -> Unit,
) {
SmallButton(
painter = painterResource(id = R.drawable.ic_stroboscope_vector),
contentDescription = stringResource(id = R.string.stroboscope),
isActive = stroboscopeActive,
onPress = onStroboscopeButtonPress
)
}
@Composable
internal fun MainScreenSlidersSection(
showBrightnessBar: Boolean,
brightnessBarValue: Float,
onBrightnessBarValueChange: (Float) -> Unit,
showStroboscopeBar: Boolean,
stroboscopeBarValue: Float,
onStroboscopeBarValueChange: (Float) -> Unit,
) {
val dimens = SimpleTheme.dimens
val sliderModifier = remember {
Modifier
.padding(horizontal = dimens.padding.extraLarge)
.padding(top = 24.dp, bottom = 40.dp)
.size(width = AppDimensions.seekbarWidth, height = AppDimensions.seekbarHeight)
}
if (showBrightnessBar) {
Slider(
modifier = sliderModifier,
value = brightnessBarValue,
onValueChange = onBrightnessBarValueChange
)
}
if (showStroboscopeBar) {
Slider(
modifier = sliderModifier,
value = stroboscopeBarValue,
onValueChange = onStroboscopeBarValueChange
)
}
if (!showBrightnessBar && !showStroboscopeBar) {
Spacer(
modifier = sliderModifier,
)
}
}
@Composable
private fun SmallButton(
painter: Painter,
contentDescription: String,
isActive: Boolean,
onPress: () -> Unit
) {
Icon(
modifier = Modifier
.size(AppDimensions.smallerButtonSize)
.padding(vertical = SimpleTheme.dimens.padding.large)
.clickable(
indication = null,
interactionSource = rememberMutableInteractionSource(),
onClick = onPress
),
painter = painter,
contentDescription = contentDescription,
tint = if (isActive) SimpleTheme.colorScheme.primary else SimpleTheme.colorScheme.onSurface
)
}
private fun buildActionMenu(
showMoreApps: Boolean,
openSettings: () -> Unit,
openAbout: () -> Unit,
openSleepTimer: () -> Unit,
moreAppsFromUs: () -> Unit,
): ImmutableList<ActionItem> {
val settings =
ActionItem(com.simplemobiletools.commons.R.string.settings, icon = Icons.Filled.Settings, doAction = openSettings, overflowMode = OverflowMode.NEVER_OVERFLOW)
val about = ActionItem(com.simplemobiletools.commons.R.string.about, icon = Icons.Outlined.Info, doAction = openAbout, overflowMode = OverflowMode.NEVER_OVERFLOW)
val sleepTimer = ActionItem(com.simplemobiletools.commons.R.string.sleep_timer, doAction = openSleepTimer, overflowMode = OverflowMode.ALWAYS_OVERFLOW)
val list = mutableListOf(settings, about, sleepTimer)
if (showMoreApps) {
list += ActionItem(com.simplemobiletools.commons.R.string.more_apps_from_us, doAction = moreAppsFromUs, overflowMode = OverflowMode.ALWAYS_OVERFLOW)
}
return list.toImmutableList()
}
@Composable
@MyDevices
internal fun MainScreenPreview() {
AppThemeSurface {
MainScreen(
flashlightButton = {
FlashlightButton(
onFlashlightPress = {},
flashlightActive = true,
)
},
brightDisplayButton = {
BrightDisplayButton(
onBrightDisplayPress = {}
)
},
sosButton = {
SosButton(
sosActive = false,
onSosButtonPress = {},
)
},
stroboscopeButton = {
StroboscopeButton(
stroboscopeActive = false,
onStroboscopeButtonPress = {},
)
},
slidersSection = {
MainScreenSlidersSection(
showBrightnessBar = false,
brightnessBarValue = 0f,
onBrightnessBarValueChange = {},
showStroboscopeBar = false,
stroboscopeBarValue = 0f,
onStroboscopeBarValueChange = {},
)
},
sleepTimer = {
AnimatedSleepTimer(
timerText = "00:00",
timerVisible = true,
onTimerClosePress = {},
)
},
showMoreApps = true,
openSettings = {},
openAbout = {},
moreAppsFromUs = {},
openSleepTimer = {}
)
}
}

View File

@ -0,0 +1,143 @@
package com.simplemobiletools.flashlight.screens
import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource
import com.simplemobiletools.commons.compose.extensions.MyDevices
import com.simplemobiletools.commons.compose.lists.SimpleColumnScaffold
import com.simplemobiletools.commons.compose.settings.*
import com.simplemobiletools.commons.compose.theme.AppThemeSurface
import com.simplemobiletools.commons.compose.theme.SimpleTheme
import com.simplemobiletools.flashlight.R
@Composable
internal fun SettingsScreen(
colorCustomizationSection: @Composable () -> Unit,
generalSection: @Composable () -> Unit,
goBack: () -> Unit,
) {
SimpleColumnScaffold(title = stringResource(id = com.simplemobiletools.commons.R.string.settings), goBack = goBack) {
SettingsGroup(title = {
SettingsTitleTextComponent(text = stringResource(id = com.simplemobiletools.commons.R.string.color_customization))
}) {
colorCustomizationSection()
}
SettingsHorizontalDivider()
SettingsGroup(title = {
SettingsTitleTextComponent(text = stringResource(id = com.simplemobiletools.commons.R.string.general_settings))
}) {
generalSection()
}
}
}
@Composable
internal fun ColorCustomizationSettingsSection(
customizeColors: () -> Unit,
customizeWidgetColors: () -> Unit,
) {
SettingsPreferenceComponent(
label = stringResource(id = com.simplemobiletools.commons.R.string.customize_colors),
doOnPreferenceClick = customizeColors,
)
SettingsPreferenceComponent(
label = stringResource(id = com.simplemobiletools.commons.R.string.customize_widget_colors),
doOnPreferenceClick = customizeWidgetColors
)
}
@Composable
internal fun GeneralSettingsSection(
showUseEnglish: Boolean,
useEnglishChecked: Boolean,
showDisplayLanguage: Boolean,
displayLanguage: String,
turnFlashlightOnStartupChecked: Boolean,
forcePortraitModeChecked: Boolean,
showBrightDisplayButtonChecked: Boolean,
showSosButtonChecked: Boolean,
showStroboscopeButtonChecked: Boolean,
onUseEnglishPress: (Boolean) -> Unit,
onSetupLanguagePress: () -> Unit,
onTurnFlashlightOnStartupPress: (Boolean) -> Unit,
onForcePortraitModePress: (Boolean) -> Unit,
onShowBrightDisplayButtonPress: (Boolean) -> Unit,
onShowSosButtonPress: (Boolean) -> Unit,
onShowStroboscopeButtonPress: (Boolean) -> Unit,
) {
if (showUseEnglish) {
SettingsCheckBoxComponent(
label = stringResource(id = com.simplemobiletools.commons.R.string.use_english_language),
initialValue = useEnglishChecked,
onChange = onUseEnglishPress
)
}
if (showDisplayLanguage) {
SettingsPreferenceComponent(
label = stringResource(id = com.simplemobiletools.commons.R.string.language),
value = displayLanguage,
doOnPreferenceClick = onSetupLanguagePress
)
}
SettingsCheckBoxComponent(
label = stringResource(id = R.string.turn_flashlight_on),
initialValue = turnFlashlightOnStartupChecked,
onChange = onTurnFlashlightOnStartupPress
)
SettingsCheckBoxComponent(
label = stringResource(id = com.simplemobiletools.commons.R.string.force_portrait_mode),
initialValue = forcePortraitModeChecked,
onChange = onForcePortraitModePress
)
SettingsCheckBoxComponent(
label = stringResource(id = R.string.show_bright_display),
initialValue = showBrightDisplayButtonChecked,
onChange = onShowBrightDisplayButtonPress
)
SettingsCheckBoxComponent(
label = stringResource(id = R.string.show_sos),
initialValue = showSosButtonChecked,
onChange = onShowSosButtonPress
)
SettingsCheckBoxComponent(
label = stringResource(id = R.string.show_stroboscope),
initialValue = showStroboscopeButtonChecked,
onChange = onShowStroboscopeButtonPress
)
}
@Composable
@MyDevices
private fun SettingsScreenPreview() {
AppThemeSurface {
SettingsScreen(
colorCustomizationSection = {
ColorCustomizationSettingsSection(
customizeColors = {},
customizeWidgetColors = {},
)
},
generalSection = {
GeneralSettingsSection(
useEnglishChecked = true,
showUseEnglish = true,
showDisplayLanguage = true,
displayLanguage = "English",
turnFlashlightOnStartupChecked = false,
forcePortraitModeChecked = true,
showBrightDisplayButtonChecked = true,
showSosButtonChecked = true,
showStroboscopeButtonChecked = true,
onUseEnglishPress = {},
onSetupLanguagePress = {},
onTurnFlashlightOnStartupPress = {},
onForcePortraitModePress = {},
onShowBrightDisplayButtonPress = {},
onShowSosButtonPress = {},
onShowStroboscopeButtonPress = {},
)
},
goBack = {},
)
}
}

View File

@ -0,0 +1,107 @@
package com.simplemobiletools.flashlight.screens
import androidx.annotation.ColorInt
import androidx.annotation.DrawableRes
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.graphics.painter.BrushPainter
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import com.simplemobiletools.commons.compose.extensions.MyDevices
import com.simplemobiletools.commons.compose.theme.AppThemeSurface
import com.simplemobiletools.commons.compose.theme.SimpleTheme
import com.simplemobiletools.commons.extensions.adjustAlpha
import com.simplemobiletools.flashlight.R
import com.simplemobiletools.flashlight.helpers.AppDimensions
@Composable
internal fun WidgetConfigureScreen(
@DrawableRes
widgetDrawable: Int,
@ColorInt
widgetColor: Int,
widgetAlpha: Float,
onSliderChanged: (Float) -> Unit,
onColorPressed: () -> Unit,
onSavePressed: () -> Unit
) {
Column(
modifier = Modifier.fillMaxSize().safeDrawingPadding(),
verticalArrangement = Arrangement.Bottom,
) {
Box(
modifier = Modifier
.padding(SimpleTheme.dimens.padding.extraLarge)
.padding(bottom = SimpleTheme.dimens.padding.extraLarge)
.fillMaxWidth()
.weight(1f)
) {
Icon(
modifier = Modifier
.align(Alignment.Center)
.size(AppDimensions.mainButtonSize),
painter = painterResource(id = widgetDrawable),
contentDescription = stringResource(id = R.string.bright_display),
tint = Color(widgetColor.adjustAlpha(widgetAlpha))
)
}
Row {
Icon(
modifier = Modifier
.size(AppDimensions.widgetColorPickerSize)
.padding(SimpleTheme.dimens.padding.extraSmall)
.clip(CircleShape)
.clickable { onColorPressed() },
painter = BrushPainter(SolidColor(Color(widgetColor))),
contentDescription = stringResource(id = R.string.bright_display),
tint = Color(widgetColor)
)
Slider(
value = widgetAlpha,
onValueChange = onSliderChanged,
modifier = Modifier
.padding(start = SimpleTheme.dimens.padding.medium)
.background(
color = colorResource(id = com.simplemobiletools.commons.R.color.md_grey_white),
shape = SimpleTheme.shapes.extraLarge
)
.padding(horizontal = SimpleTheme.dimens.padding.extraLarge)
)
}
Button(
modifier = Modifier.align(Alignment.End),
onClick = onSavePressed
) {
Text(text = stringResource(id = com.simplemobiletools.commons.R.string.ok))
}
}
}
@Composable
@MyDevices
private fun WidgetBrightDisplayConfigureScreenPreview() {
AppThemeSurface {
WidgetConfigureScreen(
widgetDrawable = R.drawable.ic_bright_display_vector,
widgetColor = SimpleTheme.colorScheme.primary.toArgb(),
widgetAlpha = 1f,
onSliderChanged = {},
onColorPressed = {},
onSavePressed = {}
)
}
}

View File

@ -0,0 +1,96 @@
package com.simplemobiletools.flashlight.views
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.background
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import com.simplemobiletools.commons.compose.extensions.MyDevices
import com.simplemobiletools.commons.compose.theme.AppThemeSurface
import com.simplemobiletools.commons.compose.theme.SimpleTheme
import com.simplemobiletools.commons.compose.theme.divider_grey
@Composable
internal fun SleepTimer(
modifier: Modifier = Modifier,
timerText: String,
onCloseClick: () -> Unit
) {
Row(
modifier = modifier
.border(
width = 1.dp,
color = divider_grey,
shape = RectangleShape
)
.background(SimpleTheme.colorScheme.surface)
) {
Text(
modifier = Modifier
.align(Alignment.CenterVertically)
.padding(horizontal = SimpleTheme.dimens.padding.large),
text = stringResource(id = com.simplemobiletools.commons.R.string.sleep_timer),
color = SimpleTheme.colorScheme.onSurface
)
Text(
modifier = Modifier.align(Alignment.CenterVertically),
text = timerText,
color = SimpleTheme.colorScheme.onSurface
)
IconButton(
modifier = Modifier
.align(Alignment.CenterVertically)
.padding(SimpleTheme.dimens.padding.medium),
onClick = onCloseClick
) {
Icon(
painter = painterResource(id = com.simplemobiletools.commons.R.drawable.ic_cross_vector),
contentDescription = stringResource(id = com.simplemobiletools.commons.R.string.close),
tint = SimpleTheme.colorScheme.onSurface
)
}
}
}
@Composable
internal fun AnimatedSleepTimer(
modifier: Modifier = Modifier,
timerText: String,
timerVisible: Boolean,
onTimerClosePress: () -> Unit
) {
AnimatedVisibility(
modifier = modifier,
visible = timerVisible && timerText.isNotEmpty(),
enter = fadeIn(),
exit = fadeOut()
) {
SleepTimer(
timerText = timerText,
onCloseClick = onTimerClosePress
)
}
}
@Composable
@MyDevices
internal fun SleepTimerPreview() {
AppThemeSurface {
SleepTimer(
timerText = "00:00",
onCloseClick = {}
)
}
}

View File

@ -1,91 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/bright_display_holder"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="@+id/bright_display"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white" />
<android.widget.TextView
android:id="@+id/bright_display_change_color"
style="@style/StrokeButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:alpha="0.5"
android:text="@string/change_color" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/sleep_timer_holder"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/default_background_color"
android:clickable="true"
android:visibility="gone"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
tools:visibility="visible">
<ImageView
android:id="@+id/sleep_timer_divider_top"
android:layout_width="0dp"
android:layout_height="1px"
android:background="@color/divider_grey"
app:layout_constraintBottom_toTopOf="@+id/sleep_timer_stop"
app:layout_constraintEnd_toEndOf="@+id/sleep_timer_stop"
app:layout_constraintStart_toStartOf="@+id/sleep_timer_label" />
<ImageView
android:id="@+id/sleep_timer_divider_start"
android:layout_width="1px"
android:layout_height="0dp"
android:background="@color/divider_grey"
app:layout_constraintBottom_toBottomOf="@+id/sleep_timer_stop"
app:layout_constraintStart_toStartOf="@+id/sleep_timer_label"
app:layout_constraintTop_toTopOf="@+id/sleep_timer_stop" />
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/sleep_timer_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:paddingStart="@dimen/normal_margin"
android:paddingEnd="@dimen/normal_margin"
android:text="@string/sleep_timer"
android:textSize="@dimen/big_text_size"
app:layout_constraintBottom_toBottomOf="@+id/sleep_timer_stop"
app:layout_constraintEnd_toStartOf="@+id/sleep_timer_value"
app:layout_constraintTop_toTopOf="@+id/sleep_timer_stop" />
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/sleep_timer_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="00:00"
android:textSize="@dimen/big_text_size"
app:layout_constraintBottom_toBottomOf="@+id/sleep_timer_stop"
app:layout_constraintEnd_toStartOf="@+id/sleep_timer_stop"
app:layout_constraintTop_toTopOf="@+id/sleep_timer_stop"
tools:ignore="HardcodedText" />
<ImageView
android:id="@+id/sleep_timer_stop"
android:layout_width="@dimen/normal_icon_size"
android:layout_height="@dimen/normal_icon_size"
android:layout_marginStart="@dimen/tiny_margin"
android:layout_marginEnd="@dimen/tiny_margin"
android:background="?attr/selectableItemBackgroundBorderless"
android:padding="@dimen/medium_margin"
android:src="@drawable/ic_cross_vector"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</RelativeLayout>

View File

@ -1,183 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main_coordinator"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/main_toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/color_primary"
app:menu="@menu/menu"
app:titleTextAppearance="@style/AppTheme.ActionBar.TitleTextStyle" />
<androidx.core.widget.NestedScrollView
android:id="@+id/main_nested_scrollview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="?attr/actionBarSize"
android:fillViewport="true"
android:scrollbars="none">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/main_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/flashlight_btn"
android:layout_width="@dimen/main_button_size"
android:layout_height="@dimen/main_button_size"
android:layout_marginTop="@dimen/normal_margin"
android:layout_marginBottom="@dimen/normal_margin"
android:background="@drawable/ic_flashlight_vector"
app:layout_constraintBottom_toTopOf="@+id/bright_display_btn"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/bright_display_btn"
android:layout_width="@dimen/smaller_button_size"
android:layout_height="@dimen/smaller_button_size"
android:layout_marginTop="@dimen/normal_margin"
android:layout_marginBottom="@dimen/normal_margin"
android:background="@drawable/ic_bright_display_vector"
android:padding="@dimen/activity_margin"
app:layout_constraintBottom_toTopOf="@+id/sos_btn"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/flashlight_btn" />
<TextView
android:id="@+id/sos_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/normal_margin"
android:layout_marginBottom="@dimen/normal_margin"
android:background="?attr/selectableItemBackgroundBorderless"
android:padding="@dimen/activity_margin"
android:text="SOS"
android:textSize="@dimen/sos_text_size"
android:textStyle="bold"
app:layout_constraintBottom_toTopOf="@+id/stroboscope_btn"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/bright_display_btn" />
<ImageView
android:id="@+id/stroboscope_btn"
android:layout_width="@dimen/smaller_button_size"
android:layout_height="@dimen/smaller_button_size"
android:layout_marginTop="@dimen/normal_margin"
android:background="@drawable/ic_stroboscope_vector"
android:padding="@dimen/activity_margin"
app:layout_constraintBottom_toTopOf="@+id/stroboscope_bar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/sos_btn" />
<com.simplemobiletools.commons.views.MySeekBar
android:id="@+id/stroboscope_bar"
android:layout_width="@dimen/seekbar_width"
android:layout_height="wrap_content"
android:layout_margin="@dimen/activity_margin"
android:paddingTop="@dimen/medium_margin"
android:paddingBottom="@dimen/medium_margin"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/stroboscope_btn" />
<com.simplemobiletools.commons.views.MySeekBar
android:id="@+id/brightness_bar"
android:layout_width="@dimen/seekbar_width"
android:layout_height="wrap_content"
android:layout_margin="@dimen/activity_margin"
android:paddingTop="@dimen/medium_margin"
android:paddingBottom="@dimen/medium_margin"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/stroboscope_btn" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/sleep_timer_holder"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/default_background_color"
android:clickable="true"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
tools:visibility="visible">
<ImageView
android:id="@+id/sleep_timer_divider_top"
android:layout_width="0dp"
android:layout_height="1px"
android:background="@color/divider_grey"
app:layout_constraintBottom_toTopOf="@+id/sleep_timer_stop"
app:layout_constraintEnd_toEndOf="@+id/sleep_timer_stop"
app:layout_constraintStart_toStartOf="@+id/sleep_timer_label" />
<ImageView
android:id="@+id/sleep_timer_divider_start"
android:layout_width="1px"
android:layout_height="0dp"
android:background="@color/divider_grey"
app:layout_constraintBottom_toBottomOf="@+id/sleep_timer_stop"
app:layout_constraintStart_toStartOf="@+id/sleep_timer_label"
app:layout_constraintTop_toTopOf="@+id/sleep_timer_stop" />
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/sleep_timer_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:paddingStart="@dimen/normal_margin"
android:paddingEnd="@dimen/normal_margin"
android:text="@string/sleep_timer"
android:textSize="@dimen/big_text_size"
app:layout_constraintBottom_toBottomOf="@+id/sleep_timer_stop"
app:layout_constraintEnd_toStartOf="@+id/sleep_timer_value"
app:layout_constraintTop_toTopOf="@+id/sleep_timer_stop" />
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/sleep_timer_value"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="00:00"
android:textSize="@dimen/big_text_size"
app:layout_constraintBottom_toBottomOf="@+id/sleep_timer_stop"
app:layout_constraintEnd_toStartOf="@+id/sleep_timer_stop"
app:layout_constraintTop_toTopOf="@+id/sleep_timer_stop"
tools:ignore="HardcodedText" />
<ImageView
android:id="@+id/sleep_timer_stop"
android:layout_width="@dimen/normal_icon_size"
android:layout_height="@dimen/normal_icon_size"
android:layout_marginStart="@dimen/tiny_margin"
android:layout_marginEnd="@dimen/tiny_margin"
android:background="?attr/selectableItemBackgroundBorderless"
android:padding="@dimen/medium_margin"
android:src="@drawable/ic_cross_vector"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -1,212 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/settings_coordinator"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.MaterialToolbar
android:id="@+id/settings_toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/color_primary"
app:title="@string/settings"
app:titleTextAppearance="@style/AppTheme.ActionBar.TitleTextStyle" />
<androidx.core.widget.NestedScrollView
android:id="@+id/settings_nested_scrollview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="?attr/actionBarSize"
android:fillViewport="true"
android:scrollbars="none">
<LinearLayout
android:id="@+id/settings_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/settings_color_customization_section_label"
style="@style/SettingsSectionLabelStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/color_customization" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/settings_color_customization_holder"
style="@style/SettingsHolderTextViewOneLinerStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/settings_color_customization_label"
style="@style/SettingsTextLabelStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/customize_colors"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/settings_widget_color_customization_holder"
style="@style/SettingsHolderTextViewOneLinerStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/settings_widget_color_customization_label"
style="@style/SettingsTextLabelStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/customize_widget_colors"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<include
android:id="@+id/settings_color_customization_divider"
layout="@layout/divider" />
<TextView
android:id="@+id/settings_general_settings_label"
style="@style/SettingsSectionLabelStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/general_settings" />
<RelativeLayout
android:id="@+id/settings_purchase_thank_you_holder"
style="@style/SettingsHolderTextViewStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/settings_purchase_thank_you"
style="@style/SettingsTextLabelStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/purchase_simple_thank_you" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/settings_use_english_holder"
style="@style/SettingsHolderCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/settings_use_english"
style="@style/SettingsCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/use_english_language" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/settings_language_holder"
style="@style/SettingsHolderTextViewStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/settings_language_label"
style="@style/SettingsTextLabelStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/language" />
<com.simplemobiletools.commons.views.MyTextView
android:id="@+id/settings_language"
style="@style/SettingsTextValueStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/settings_language_label"
tools:text="English" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/settings_turn_flashlight_on_holder"
style="@style/SettingsHolderCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/settings_turn_flashlight_on"
style="@style/SettingsCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/turn_flashlight_on" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/settings_force_portrait_holder"
style="@style/SettingsHolderCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/settings_force_portrait"
style="@style/SettingsCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/force_portrait_mode" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/settings_bright_display_holder"
style="@style/SettingsHolderCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/settings_bright_display"
style="@style/SettingsCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/show_bright_display" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/settings_sos_holder"
style="@style/SettingsHolderCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/settings_sos"
style="@style/SettingsCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/show_sos" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/settings_stroboscope_holder"
style="@style/SettingsHolderCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.simplemobiletools.commons.views.MyAppCompatCheckbox
android:id="@+id/settings_stroboscope"
style="@style/SettingsCheckboxStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/show_stroboscope" />
</RelativeLayout>
</LinearLayout>
</androidx.core.widget.NestedScrollView>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -1,55 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/dialog_custom_sleep_timer_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingLeft="@dimen/activity_margin"
android:paddingTop="@dimen/activity_margin"
android:paddingRight="@dimen/activity_margin">
<com.simplemobiletools.commons.views.MyTextInputLayout
android:id="@+id/value_hint"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/value">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/timer_value"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/activity_margin"
android:digits="0123456789"
android:inputType="number"
android:maxLength="5"
android:singleLine="true"
android:textCursorDrawable="@null"
android:imeOptions="actionDone"
android:textSize="@dimen/normal_text_size" />
</com.simplemobiletools.commons.views.MyTextInputLayout>
<RadioGroup
android:id="@+id/dialog_radio_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/normal_margin">
<com.simplemobiletools.commons.views.MyCompatRadioButton
android:id="@+id/dialog_radio_minutes"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/normal_margin"
android:paddingBottom="@dimen/normal_margin"
android:text="@string/minutes_raw" />
<com.simplemobiletools.commons.views.MyCompatRadioButton
android:id="@+id/dialog_radio_seconds"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/normal_margin"
android:paddingBottom="@dimen/normal_margin"
android:text="@string/seconds_raw" />
</RadioGroup>
</LinearLayout>

View File

@ -1,67 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/config_bright_display_coordinator"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:id="@+id/config_bright_display_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_margin="@dimen/activity_margin">
<RelativeLayout
android:id="@+id/config_bright_display_wrapper"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/config_widget_color"
android:layout_marginBottom="@dimen/activity_margin"
android:gravity="center">
<ImageView
android:id="@+id/config_image"
android:layout_width="@dimen/main_button_size"
android:layout_height="@dimen/main_button_size"
android:background="@drawable/ic_bright_display_vector" />
</RelativeLayout>
<ImageView
android:id="@+id/config_widget_color"
android:layout_width="@dimen/widget_colorpicker_size"
android:layout_height="@dimen/widget_colorpicker_size"
android:layout_above="@+id/config_save"
android:layout_margin="@dimen/tiny_margin" />
<RelativeLayout
android:id="@+id/config_widget_seekbar_holder"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignTop="@+id/config_widget_color"
android:layout_alignBottom="@+id/config_widget_color"
android:layout_marginStart="@dimen/medium_margin"
android:layout_toEndOf="@+id/config_widget_color"
android:background="@drawable/widget_config_seekbar_background">
<com.simplemobiletools.commons.views.MySeekBar
android:id="@+id/config_widget_seekbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:paddingStart="@dimen/activity_margin"
android:paddingEnd="@dimen/activity_margin" />
</RelativeLayout>
<Button
android:id="@+id/config_save"
style="@style/MyWidgetConfigSaveStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true"
android:layout_marginTop="@dimen/tiny_margin"
android:text="@string/ok" />
</RelativeLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -1,68 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/config_torch_coordinator"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:id="@+id/config_torch_holder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_margin="@dimen/activity_margin">
<RelativeLayout
android:id="@+id/config_torch_wrapper"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@+id/config_widget_color"
android:layout_marginBottom="@dimen/activity_margin"
android:gravity="center">
<ImageView
android:id="@+id/config_image"
android:layout_width="@dimen/main_button_size"
android:layout_height="@dimen/main_button_size"
android:background="@drawable/ic_flashlight_vector" />
</RelativeLayout>
<ImageView
android:id="@+id/config_widget_color"
android:layout_width="@dimen/widget_colorpicker_size"
android:layout_height="@dimen/widget_colorpicker_size"
android:layout_above="@+id/config_save"
android:layout_margin="@dimen/tiny_margin" />
<RelativeLayout
android:id="@+id/config_widget_seekbar_holder"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignTop="@+id/config_widget_color"
android:layout_alignBottom="@+id/config_widget_color"
android:layout_marginStart="@dimen/medium_margin"
android:layout_toEndOf="@+id/config_widget_color"
android:background="@drawable/widget_config_seekbar_background">
<com.simplemobiletools.commons.views.MySeekBar
android:id="@+id/config_widget_seekbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:paddingStart="@dimen/activity_margin"
android:paddingEnd="@dimen/activity_margin" />
</RelativeLayout>
<Button
android:id="@+id/config_save"
style="@style/MyWidgetConfigSaveStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentBottom="true"
android:layout_marginTop="@dimen/tiny_margin"
android:text="@string/ok" />
</RelativeLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -1,24 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:ignore="AlwaysShowAction">
<item
android:id="@+id/settings"
android:icon="@drawable/ic_settings_cog_vector"
android:title="@string/settings"
app:showAsAction="always" />
<item
android:id="@+id/about"
android:icon="@drawable/ic_info_vector"
android:title="@string/about"
app:showAsAction="always" />
<item
android:id="@+id/sleep_timer"
android:title="@string/sleep_timer"
app:showAsAction="never" />
<item
android:id="@+id/more_apps_from_us"
android:title="@string/more_apps_from_us"
app:showAsAction="never" />
</menu>

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">إظهار زر عرض ساطع</string>
<string name="show_stroboscope">إظهار زر ستروبوسكوب</string>
<string name="stroboscope">Stroboscope</string>
<string name="show_sos">إظهار زر SOS</string>
<string name="turn_flashlight_on">قم بتشغيل المصباح عند بدء التشغيل</string>
<!--

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">İşıqlı ekran düyməsi göstər</string>
<string name="show_stroboscope">Stroboskop düyməsi göstər</string>
<string name="stroboscope">Stroboscope</string>
<string name="show_sos">Show an SOS button</string>
<string name="turn_flashlight_on">Başlanğıcda fənəri aç</string>
<!--

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">Паказваць кнопку яркага экрана</string>
<string name="show_stroboscope">Паказваць кнопку страбаскопа</string>
<string name="stroboscope">Stroboscope</string>
<string name="show_sos">Паказваць кнопку СОС</string>
<string name="turn_flashlight_on">Уключаць ліхтарык пры запуску</string>
<!--

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">Покажи ярък дисплей бутон</string>
<string name="show_stroboscope">Покажи стробинг бутон</string>
<string name="stroboscope">Stroboscope</string>
<string name="show_sos">Показване на SOS бутон</string>
<string name="turn_flashlight_on">Включване на фенер при стартиране</string>
<!--

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Jednostavna baterija</string>
<string name="app_launcher_name">Baterija</string>
<string name="camera_error">Nije moguće pristupiti kameri</string>
<string name="camera_permission">Potrebna je dozvola za pristup kameri za pravilan stroboskopski efekt</string>
<string name="bright_display">Svijetli ekran</string>
<!-- Settings -->
<string name="show_bright_display">Prikaži dugme za svijetli ekran</string>
<string name="show_stroboscope">Prikaži dugme za stroboskop</string>
<string name="stroboscope">Stroboskop</string>
<string name="show_sos">Prikaži SOS dugme</string>
<string name="turn_flashlight_on">Uključi bateriju pri pokretanju</string>
<!--
Haven't found some strings? There's more at
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">Mostra un botó de pantalla lluminosa</string>
<string name="show_stroboscope">Mostra un botó d\'estroboscopi</string>
<string name="stroboscope">Stroboscope</string>
<string name="show_sos">Mostra un botó de SOS</string>
<string name="turn_flashlight_on">Activa la llanterna a l\'inici</string>
<!--

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">Zobrazit tlačítko pro jasný displej</string>
<string name="show_stroboscope">Zobrazit tlačítko pro stroboskop</string>
<string name="stroboscope">Stroboscope</string>
<string name="show_sos">Zobrazit tlačítko SOS</string>
<string name="turn_flashlight_on">Zapnout svítilnu po spuštění</string>
<!--

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">Dangos botwm dangosydd llachar</string>
<string name="show_stroboscope">Dangos botwm strobosgop</string>
<string name="stroboscope">Stroboscope</string>
<string name="show_sos">Show an SOS button</string>
<string name="turn_flashlight_on">Troi\'r fflacholau ymlaen wrth ddechrau</string>
<!--

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">Vis en lys skærmknap</string>
<string name="show_stroboscope">Vis en stroboskop-knap</string>
<string name="stroboscope">Stroboscope</string>
<string name="show_sos">Vis en SOS-knap</string>
<string name="turn_flashlight_on">Slå lommelygte til ved start</string>
<!--

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">Schaltfläche für hellen Bildschirm zeigen</string>
<string name="show_stroboscope">Schaltfläche für Stroboskop zeigen</string>
<string name="stroboscope">Stroboscope</string>
<string name="show_sos">Eine SOS-Schaltfläche zeigen</string>
<string name="turn_flashlight_on">Taschenlampe beim Start einschalten</string>
<!--

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">Προβολή κουμπιού φωτεινότητας</string>
<string name="show_stroboscope">Προβολή κουμπιού στροβοσκοπίου</string>
<string name="stroboscope">Stroboscope</string>
<string name="show_sos">Προβολή κουμπιού SOS</string>
<string name="turn_flashlight_on">Άνοιγμα του φακού κατά την εκκίνηση</string>
<!--

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">Montri butonon por hela ekrano</string>
<string name="show_stroboscope">Show a stroboscope button</string>
<string name="stroboscope">Stroboscope</string>
<string name="show_sos">Show an SOS button</string>
<string name="turn_flashlight_on">Turn flashlight on at startup</string>
<!--

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">Mostrar botón de pantalla brillante</string>
<string name="show_stroboscope">Mostrar botón de estroboscopio</string>
<string name="stroboscope">Stroboscope</string>
<string name="show_sos">Mostrar un botón de SOS</string>
<string name="turn_flashlight_on">Encender la linterna al inicio</string>
<!--

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">Näita nuppu ekraanil kirkana</string>
<string name="show_stroboscope">Näita stroboskoobi nuppu</string>
<string name="stroboscope">Stroboscope</string>
<string name="show_sos">Näita SOS-nuppu</string>
<string name="turn_flashlight_on">Rakenduse käivitamisel lülita taskulamp sisse</string>
<!--

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">Näytä kirkas näyttö -painike</string>
<string name="show_stroboscope">Näytä stroboskooppipainike</string>
<string name="stroboscope">Stroboscope</string>
<string name="show_sos">Näytä SOS-painike</string>
<string name="turn_flashlight_on">Kytke taskulamppu päälle käynnistyksen yhteydessä</string>
<!--

View File

@ -8,10 +8,11 @@
<!-- Settings -->
<string name="show_bright_display">Afficher un bouton d\'affichage lumineux</string>
<string name="show_stroboscope">Afficher un bouton stroboscope</string>
<string name="stroboscope">Stroboscope</string>
<string name="show_sos">Afficher un bouton « S.O.S. »</string>
<string name="turn_flashlight_on">Allumer la lampe de poche au démarrage</string>
<!--
Haven't found some strings? There's more at
https://github.com/SimpleMobileTools/Simple-Commons/tree/master/commons/src/main/res
-->
</resources>
</resources>

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">Mostrar un botón para iluminala pantalla</string>
<string name="show_stroboscope">Mostrar un botón estroboscopio</string>
<string name="stroboscope">Stroboscope</string>
<string name="show_sos">Mostrar un botón de SOS</string>
<string name="turn_flashlight_on">Acendela lanterna ao iniciar</string>
<!--

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">Prikaži gumb za svijetli ekran</string>
<string name="show_stroboscope">Prikaži gumb za stroboskop</string>
<string name="stroboscope">Stroboskop</string>
<string name="show_sos">Prikaži SOS gumb</string>
<string name="turn_flashlight_on">Uključi svjetiljku pri pokretanju</string>
<!--

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">Fényes kijelző gomb megjelenítése</string>
<string name="show_stroboscope">Stroboszkóp gomb megjelenítése</string>
<string name="stroboscope">Stroboscope</string>
<string name="show_sos">SOS gomb megjelenítése</string>
<string name="turn_flashlight_on">Zseblámpa bekapcsolása indításkor</string>
<!--

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">Tampilkan tombol tampilan cerah</string>
<string name="show_stroboscope">Tampilkan tombol stroboscope</string>
<string name="stroboscope">Stroboscope</string>
<string name="show_sos">Tampilkan tombol SOS</string>
<string name="turn_flashlight_on">Nyalakan senter saat memulai</string>
<!--

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">Show a bright display button</string>
<string name="show_stroboscope">Show a stroboscope button</string>
<string name="stroboscope">Stroboscope</string>
<string name="show_sos">Show an SOS button</string>
<string name="turn_flashlight_on">Turn flashlight on at startup</string>
</resources>

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">Mostra un pulsante per lo schermo luminoso</string>
<string name="show_stroboscope">Mostra un pulsante per l\'effetto stroboscopico</string>
<string name="stroboscope">Stroboscope</string>
<string name="show_sos">Mostra un pulsante SOS</string>
<string name="turn_flashlight_on">Accendi la torcia all\'avvio</string>
<!--

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">הצג כפתור תצוגה בהיר</string>
<string name="show_stroboscope">הצג כפתור סטרובוסקופ</string>
<string name="stroboscope">Stroboscope</string>
<string name="show_sos">הצג כפתור SOS</string>
<string name="turn_flashlight_on">הדלק את הפנס בעת ההפעלה</string>
<!--

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">明るく表示ボタンを表示</string>
<string name="show_stroboscope">ストロボボタンを表示</string>
<string name="stroboscope">Stroboscope</string>
<string name="show_sos">SOSボタンを表示</string>
<string name="turn_flashlight_on">起動時にフラッシュライトを点灯</string>
<!--

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">브라이트 디스플레이 버튼 활성화</string>
<string name="show_stroboscope">스트로보 스코프 버튼 활성화</string>
<string name="stroboscope">Stroboscope</string>
<string name="show_sos">Show an SOS button</string>
<string name="turn_flashlight_on">Turn flashlight on at startup</string>
<!--

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">Rodyti ryškaus ekrano mygtuką</string>
<string name="show_stroboscope">Rodyti stroboskopo mygtuką</string>
<string name="stroboscope">Stroboscope</string>
<string name="show_sos">Rodyti SOS mygtuką</string>
<string name="turn_flashlight_on">Įjungti žibintuvėlį paleidus programėlę</string>
<!--

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">ബ്രൈറ്റ് ഡിസ്പ്ലേ ബട്ടൺ കാണിക്കുക</string>
<string name="show_stroboscope">ഒരു സ്ട്രോബോസ്കോപ്പ് ബട്ടൺ കാണിക്കുക</string>
<string name="stroboscope">Stroboscope</string>
<string name="show_sos">SOS ബട്ടൺ കാണിക്കുക</string>
<string name="turn_flashlight_on">ആരംഭത്തിൽ ടോർച്ച്‌ഓണാക്കുക</string>
<!--

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">မှန်သားပြင်အလင်း ခလုတ်ပြပါ</string>
<string name="show_stroboscope">အချက်ပြမီး ခလုတ် ပြပါ</string>
<string name="stroboscope">Stroboscope</string>
<string name="show_sos">အက်စ်အိုအက်စ် ခလုတ်ပြပါ</string>
<string name="turn_flashlight_on">အက်ပ်ကိုဖွင့်တာနဲ့လက်နှိပ်မီးကိုဖွင့်ပါ</string>
<!--

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">Vis en lys skjermknapp</string>
<string name="show_stroboscope">Vis en stroboskop-knapp</string>
<string name="stroboscope">Stroboscope</string>
<string name="show_sos">Vis SOS-knapp</string>
<string name="turn_flashlight_on">Skru på lommelykt ved oppstart</string>
<!--

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">Knop voor fel scherm tonen</string>
<string name="show_stroboscope">Knop voor stroboscoop tonen</string>
<string name="stroboscope">Stroboscope</string>
<string name="show_sos">Knop voor SOS-noodsignaal tonen</string>
<string name="turn_flashlight_on">Zaklamp bij starten aanzetten</string>
<!--

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">چمکدار ڈیسپلے بٹن دکھاؤ</string>
<string name="show_stroboscope">سٹروبوسکوپ بٹن دکھاؤ</string>
<string name="stroboscope">Stroboscope</string>
<string name="show_sos">ایس‌اوایس بٹن دکھاؤ</string>
<string name="turn_flashlight_on">جدوں شروع کرن ٹورچ نوں چالو کرو</string>
</resources>

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">Pokazuj przycisk jasnego wyświetlacza</string>
<string name="show_stroboscope">Pokazuj przycisk stroboskopu</string>
<string name="stroboscope">Stroboscope</string>
<string name="show_sos">Pokazuj przycisk SOS</string>
<string name="turn_flashlight_on">Włączaj latarkę przy uruchomieniu</string>
<!--

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">Mostrar o botão de tela brilhante</string>
<string name="show_stroboscope">Mostrar o botão do Estroboscópio</string>
<string name="stroboscope">Stroboscope</string>
<string name="show_sos">Mostrar o botão de SOS</string>
<string name="turn_flashlight_on">Ligar a lanterna ao iniciar o app</string>
<!--

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">Mostrar botão de ecrã luminoso</string>
<string name="show_stroboscope">Mostrar botão do estroboscópio</string>
<string name="stroboscope">Stroboscope</string>
<string name="show_sos">Mostrar botão de SOS</string>
<string name="turn_flashlight_on">Acender a lanterna ao iniciar</string>
<!--

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">Mostrar botão de ecrã luminoso</string>
<string name="show_stroboscope">Mostrar botão do estroboscópio</string>
<string name="stroboscope">Stroboscope</string>
<string name="show_sos">Mostrar botão de SOS</string>
<string name="turn_flashlight_on">Acender a lanterna ao iniciar</string>
<!--

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">Afișaţi un buton pentru ecran luminos</string>
<string name="show_stroboscope">Afișaţi un buton pentru stroboscop</string>
<string name="stroboscope">Stroboscope</string>
<string name="show_sos">Afișați un buton SOS</string>
<string name="turn_flashlight_on">Porniți lanterna la pornire</string>
<!--

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">Показать кнопку яркого экрана</string>
<string name="show_stroboscope">Показать кнопку стробоскопа</string>
<string name="stroboscope">Stroboscope</string>
<string name="show_sos">Показать кнопку SOS</string>
<string name="turn_flashlight_on">Включать фонарик при запуске</string>
<!--

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">Zobraziť tlačidlo pre jasný displej</string>
<string name="show_stroboscope">Zobraziť tlačidlo pre stroboskop</string>
<string name="stroboscope">Stroboscope</string>
<string name="show_sos">Zobraziť tlačidlo SOS</string>
<string name="turn_flashlight_on">Aktivovať baterku po spustení</string>
<!--

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">Pokažite gumb za svetel zaslon</string>
<string name="show_stroboscope">Prikaži gumb stroboskopa</string>
<string name="stroboscope">Stroboscope</string>
<string name="show_sos">Prikaz gumba SOS</string>
<string name="turn_flashlight_on">Vklop svetilke ob zagonu</string>
</resources>

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">Прикажи светло дугме за екран</string>
<string name="show_stroboscope">Прикажи дугме за стробоскоп</string>
<string name="stroboscope">Стробоскоп</string>
<string name="show_sos">Прикажи СОС дугме</string>
<string name="turn_flashlight_on">Укључите батеријску лампу при покретању</string>
</resources>

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">Visa en knapp för ljus skärm</string>
<string name="show_stroboscope">Visa en stroboskopknapp</string>
<string name="stroboscope">Stroboscope</string>
<string name="show_sos">Visa en SOS-knapp</string>
<string name="turn_flashlight_on">Slå på ficklampan vid start</string>
<!--

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">Show a bright display button</string>
<string name="show_stroboscope">Show a stroboscope button</string>
<string name="stroboscope">Stroboscope</string>
<string name="show_sos">Show an SOS button</string>
<string name="turn_flashlight_on">Turn flashlight on at startup</string>
<!--

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">Parlak ekran düğmesini göster</string>
<string name="show_stroboscope">Stroboskop düğmesini göster</string>
<string name="stroboscope">Stroboscope</string>
<string name="show_sos">SOS düğmesini göster</string>
<string name="turn_flashlight_on">Başlangıçta feneri aç</string>
<!--

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">Показувати кнопку перемикання на білий дисплей</string>
<string name="show_stroboscope">Показувати кнопку стробоскопа</string>
<string name="stroboscope">Stroboscope</string>
<string name="show_sos">Показати кнопку SOS</string>
<string name="turn_flashlight_on">Вмикати ліхтарик при запуску</string>
<!--

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">显示高亮屏幕按钮</string>
<string name="show_stroboscope">显示频闪仪按钮</string>
<string name="stroboscope">Stroboscope</string>
<string name="show_sos">显示紧急求助按钮</string>
<string name="turn_flashlight_on">启动时打开手电筒</string>
<!--

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">顯示螢幕發亮按鈕</string>
<string name="show_stroboscope">顯示閃爍效果按鈕</string>
<string name="stroboscope">Stroboscope</string>
<string name="show_sos">顯示SOS按鈕</string>
<string name="turn_flashlight_on">啟動時打開手電筒</string>
<!--

View File

@ -1,8 +1,3 @@
<resources>
<dimen name="seekbar_width">250dp</dimen>
<dimen name="main_button_size">180dp</dimen>
<dimen name="smaller_button_size">60dp</dimen>
<dimen name="shortcut_padding">60dp</dimen>
<dimen name="sos_text_size">26sp</dimen>
</resources>

View File

@ -2,4 +2,6 @@
<resources>
<string name="package_name">com.simplemobiletools.flashlight</string>
<string name="sos">SOS</string>
</resources>

View File

@ -8,6 +8,7 @@
<!-- Settings -->
<string name="show_bright_display">Show a bright display button</string>
<string name="show_stroboscope">Show a stroboscope button</string>
<string name="stroboscope">Stroboscope</string>
<string name="show_sos">Show an SOS button</string>
<string name="turn_flashlight_on">Turn flashlight on at startup</string>
<!--

View File

@ -1,4 +1,3 @@
android.enableJetifier=true
android.useAndroidX=true
android.nonTransitiveRClass=false
org.gradle.jvmargs=-Xmx4g

View File

@ -1,14 +1,19 @@
[versions]
#jetbrains
kotlin = "1.9.0"
kotlin = "1.9.10"
#AndroidX
androidx-constraintlayout = "2.1.4"
#EventBus
eventbusVersion = "3.3.1"
androidx-customView = "1.2.0-alpha02"
androidx-customViewPooling = "1.0.0"
androidx-lifecycle = "2.7.0-alpha02"
#Simple tools
simple-commons = "de113ad025"
simple-commons = "fd497d3719"
#Compose
composeActivity = "1.8.0"
compose = "1.6.0-alpha07"
composeCompiler = "1.5.3"
composeMaterial3 = "1.2.0-alpha09"
#Gradle
gradlePlugins-agp = "8.1.1"
gradlePlugins-agp = "8.1.2"
#build
app-build-compileSDKVersion = "34"
app-build-targetSDK = "34"
@ -20,10 +25,51 @@ app-version-appId = "com.simplemobiletools.flashlight"
app-version-versionCode = "65"
app-version-versionName = "5.10.0"
[libraries]
#AndroidX
androidx-customView = { module = "androidx.customview:customview", version.ref = "androidx-customView" }
androidx-customViewPooling = { module = "androidx.customview:customview-poolingcontainer", version.ref = "androidx-customViewPooling" }
#Android X lifecycle
androidx-lifecycle-runtime = { module = "androidx.lifecycle:lifecycle-runtime-ktx", version.ref = "androidx-lifecycle" }
androidx-lifecycle-viewModel = { module = "androidx.lifecycle:lifecycle-viewmodel-ktx", version.ref = "androidx-lifecycle" }
androidx-lifecycle-viewModel-compose = { module = "androidx.lifecycle:lifecycle-viewmodel-compose", version.ref = "androidx-lifecycle" }
androidx-lifecycle-compose = { module = "androidx.lifecycle:lifecycle-runtime-compose", version.ref = "androidx-lifecycle" }
#Simple Mobile Tools
androidx-constraintlayout = { module = "androidx.constraintlayout:constraintlayout", version.ref = "androidx-constraintlayout" }
eventbus = { module = "org.greenrobot:eventbus", version.ref = "eventbusVersion" }
simple-tools-commons = { module = "com.github.SimpleMobileTools:Simple-Commons", version.ref = "simple-commons" }
#Compose
compose-compiler = { module = "androidx.compose.compiler:compiler", version.ref = "composeCompiler" }
compose-foundation = { module = "androidx.compose.foundation:foundation", version.ref = "compose" }
compose-material3 = { module = "androidx.compose.material3:material3", version.ref = "composeMaterial3" }
compose-material2 = { module = "androidx.compose.material:material", version.ref = "compose" }
compose-material-icons = { module = "androidx.compose.material:material-icons-extended", version.ref = "compose" }
compose-animation = { module = "androidx.compose.animation:animation", version.ref = "compose" }
compose-activity = { module = "androidx.activity:activity-compose", version.ref = "composeActivity" }
compose-ui = { module = "androidx.compose.ui:ui", version.ref = "compose" }
compose-runtime = { module = "androidx.compose.runtime:runtime", version.ref = "compose" }
compose-uiTooling-debug = { module = "androidx.compose.ui:ui-tooling", version.ref = "compose" }
compose-uiTooling-preview = { module = "androidx.compose.ui:ui-tooling-preview", version.ref = "compose" }
[bundles]
compose = [
"compose-activity",
"compose-animation",
"compose-compiler",
"compose-foundation",
"compose-material-icons",
"compose-material3",
"compose-runtime",
"compose-ui",
"compose-uiTooling-preview",
]
compose-preview = [
"androidx-customView",
"androidx-customViewPooling",
"compose-uiTooling-debug",
]
lifecycle = [
"androidx-lifecycle-compose",
"androidx-lifecycle-runtime",
"androidx-lifecycle-viewModel",
"androidx-lifecycle-viewModel-compose",
]
[plugins]
android = { id = "com.android.application", version.ref = "gradlePlugins-agp" }
kotlinAndroid = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" }