Skip to content

Commit

Permalink
Merge pull request #68 from Semper-Viventem/implement-enjoy-the-app-d…
Browse files Browse the repository at this point in the history
…ialog

Implement enjoy the app dialog
  • Loading branch information
Semper-Viventem authored Dec 20, 2023
2 parents fde35f3 + 127f1f0 commit 78121fb
Show file tree
Hide file tree
Showing 19 changed files with 297 additions and 15 deletions.
4 changes: 3 additions & 1 deletion app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,13 @@ android {
minSdk = 29
targetSdk = 34
versionCode = (System.currentTimeMillis() / 1000).toInt()
versionName = "0.20.1-beta"
versionName = "0.20.2-beta"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"

buildConfigField("String", "REPORT_ISSUE_URL", "\"https://github.com/Semper-Viventem/MetaRadar/issues\"")
buildConfigField("String", "GITHUB_URL", "\"https://github.com/Semper-Viventem/MetaRadar\"")
buildConfigField("String", "GOOGLE_PLAY_URL", "\"https://play.google.com/store/apps/details?id=f.cking.software&pcampaignid=web_share\"")

buildConfigField("String", "DISTRIBUTION", "\"Not specified\"")
}
Expand Down
7 changes: 7 additions & 0 deletions app/src/main/java/f/cking/software/TheApp.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@ package f.cking.software
import android.app.Application
import f.cking.software.data.DataModule
import f.cking.software.domain.interactor.InteractorsModule
import f.cking.software.domain.interactor.SaveFirstAppLaunchTimeInteractor
import f.cking.software.ui.UiModule
import org.koin.android.ext.android.getKoin
import org.koin.android.ext.koin.androidContext
import org.koin.core.context.startKoin
import org.koin.core.context.stopKoin
Expand All @@ -16,6 +18,11 @@ class TheApp : Application() {
instance = this
initDi()
initTimber()
saveFirstLaunchTime()
}

private fun saveFirstLaunchTime() {
getKoin().get<SaveFirstAppLaunchTimeInteractor>().execute()
}

private fun initDi() {
Expand Down
19 changes: 19 additions & 0 deletions app/src/main/java/f/cking/software/data/repo/SettingsRepository.kt
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,30 @@ class SettingsRepository(
sharedPreferences.edit().putBoolean(KEY_RUN_ON_STARTUP, value).apply()
}

fun getFirstAppLaunchTime(): Long {
return sharedPreferences.getLong(KEY_FIRST_APP_LAUNCH_TIME, NO_APP_LAUNCH_TIME)
}

fun setFirstAppLaunchTime(value: Long) {
sharedPreferences.edit().putLong(KEY_FIRST_APP_LAUNCH_TIME, value).apply()
}

fun getEnjoyTheAppAnswered(): Boolean {
return sharedPreferences.getBoolean(KEY_ENJOY_THE_APP_ANSWERED, false)
}

fun setEnjoyTheAppAnswered(value: Boolean) {
sharedPreferences.edit().putBoolean(KEY_ENJOY_THE_APP_ANSWERED, value).apply()
}

companion object {
private const val KEY_GARBAGING_TIME = "key_garbaging_time"
private const val KEY_USE_GPS_ONLY = "key_use_gps_location_only"
private const val KEY_PERMISSIONS_INTRO_WAS_SHOWN = "key_permissions_intro_was_shown"
private const val KEY_RUN_ON_STARTUP = "key_run_on_startup"
private const val KEY_FIRST_APP_LAUNCH_TIME = "key_first_app_launch_time"
private const val KEY_ENJOY_THE_APP_ANSWERED = "key_enjoy_the_app_answered"

const val NO_APP_LAUNCH_TIME = -1L
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package f.cking.software.domain.interactor

import f.cking.software.data.repo.SettingsRepository
import java.util.concurrent.TimeUnit

class GetAppUsageDaysInteractor(
private val settingsRepository: SettingsRepository,
) {

fun execute(): Long {
val firstLaunchTime = settingsRepository.getFirstAppLaunchTime()

if (firstLaunchTime == SettingsRepository.NO_APP_LAUNCH_TIME) {
return 0
}

return (System.currentTimeMillis() - firstLaunchTime) / TimeUnit.DAYS.toMillis(1)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -31,5 +31,7 @@ object InteractorsModule {
factory { AddTagToDeviceInteractor(get(), get()) }
factory { RemoveTagFromDeviceInteractor(get()) }
factory { ChangeFavoriteInteractor(get()) }
factory { GetAppUsageDaysInteractor(get()) }
factory { SaveFirstAppLaunchTimeInteractor(get()) }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package f.cking.software.domain.interactor

import f.cking.software.data.repo.SettingsRepository

class SaveFirstAppLaunchTimeInteractor(
private val settingsRepository: SettingsRepository
) {

fun execute() {
if (settingsRepository.getFirstAppLaunchTime() == SettingsRepository.NO_APP_LAUNCH_TIME) {
settingsRepository.setFirstAppLaunchTime(System.currentTimeMillis())
}
}
}
2 changes: 1 addition & 1 deletion app/src/main/java/f/cking/software/ui/UiModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ object UiModule {
single { RouterImpl() }
single<Router> { get<RouterImpl>() }
viewModel { MainViewModel(get(), get(), get(), get(), get(), get()) }
viewModel { DeviceListViewModel(get(), get(), get(), get()) }
viewModel { DeviceListViewModel(get(), get(), get(), get(), get(), get(), get()) }
viewModel { SettingsViewModel(get(), get(), get(), get(), get(), get(), get(), get(), get(), get(), get()) }
viewModel { ProfilesListViewModel(get(), get()) }
viewModel { ProfileDetailsViewModel(profileId = it[0], template = it[1], get(), get(), get(), get(), get()) }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
Expand All @@ -14,6 +15,7 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.material.Button
import androidx.compose.material.Chip
import androidx.compose.material.ChipDefaults
import androidx.compose.material.ExperimentalMaterialApi
Expand All @@ -40,6 +42,7 @@ import androidx.compose.ui.input.nestedscroll.NestedScrollConnection
import androidx.compose.ui.input.nestedscroll.NestedScrollSource
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalFocusManager
import androidx.compose.ui.res.colorResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
Expand All @@ -51,6 +54,7 @@ import f.cking.software.utils.graphic.BottomSpacer
import f.cking.software.utils.graphic.ContentPlaceholder
import f.cking.software.utils.graphic.DeviceListItem
import f.cking.software.utils.graphic.Divider
import f.cking.software.utils.graphic.RoundedBox
import org.koin.androidx.compose.koinViewModel

object DeviceListScreen {
Expand Down Expand Up @@ -103,6 +107,13 @@ object DeviceListScreen {
}
}

if (viewModel.enjoyTheAppState != DeviceListViewModel.EnjoyTheAppState.NONE) {
item {
Spacer(modifier = Modifier.height(8.dp))
EnjoyTheApp(viewModel, viewModel.enjoyTheAppState)
}
}

list.mapIndexed { index, deviceData ->
item {
DeviceListItem(
Expand All @@ -124,12 +135,70 @@ object DeviceListScreen {
}
}

@Composable
private fun EnjoyTheApp(viewModel: DeviceListViewModel, enjoyTheAppState: DeviceListViewModel.EnjoyTheAppState) {
RoundedBox {
when (enjoyTheAppState) {
DeviceListViewModel.EnjoyTheAppState.QUESTION -> EnjoyTheAppQuestion(viewModel)
DeviceListViewModel.EnjoyTheAppState.LIKE -> EnjoyTheAppLike(viewModel)
DeviceListViewModel.EnjoyTheAppState.DISLIKE -> EnjoyTheAppDislike(viewModel)
DeviceListViewModel.EnjoyTheAppState.NONE -> throw IllegalStateException("EnjoyTheAppState.NONE is not supported here")
}
}
}

@Composable
private fun EnjoyTheAppQuestion(viewModel: DeviceListViewModel) {
Column {
Text(text = stringResource(R.string.enjoy_the_app_question), fontWeight = FontWeight.SemiBold)
Spacer(modifier = Modifier.height(8.dp))
Row {
Button(modifier = Modifier.weight(1f), onClick = { viewModel.onEnjoyTheAppAnswered(true) }) {
Text(text = stringResource(R.string.enjoy_the_app_yes))
}
Spacer(modifier = Modifier.width(8.dp))
Button(modifier = Modifier.weight(1f), onClick = { viewModel.onEnjoyTheAppAnswered(false) }) {
Text(text = stringResource(R.string.enjoy_the_app_not_really))
}
}
}
}

@Composable
private fun EnjoyTheAppLike(viewModel: DeviceListViewModel) {
Column {
Text(text = stringResource(R.string.rate_the_app), fontWeight = FontWeight.SemiBold)
Spacer(modifier = Modifier.height(8.dp))
Row {
Button(modifier = Modifier.weight(1f), onClick = { viewModel.onEnjoyTheAppRatePlayStoreClick() }) {
Text(text = stringResource(R.string.rate_the_app_google_play))
}
Spacer(modifier = Modifier.width(8.dp))
Button(modifier = Modifier.weight(1f), onClick = { viewModel.onEnjoyTheAppRateGithubClick() }) {
Text(text = stringResource(R.string.rate_the_app_github))
}
}
}
}

@Composable
private fun EnjoyTheAppDislike(viewModel: DeviceListViewModel) {
Column {
Text(text = stringResource(R.string.report_the_problem), fontWeight = FontWeight.SemiBold)
Spacer(modifier = Modifier.height(8.dp))
Button(modifier = Modifier.fillMaxWidth(), onClick = { viewModel.onEnjoyTheAppReportClick() }) {
Text(text = stringResource(R.string.report))
}
}
}

@OptIn(ExperimentalMaterialApi::class)
@Composable
fun Filters(viewModel: DeviceListViewModel) {
private fun Filters(viewModel: DeviceListViewModel) {
Surface(elevation = 4.dp) {
Column(
modifier = Modifier
.background(colorResource(id = R.color.primary_surface))
.fillMaxWidth()
) {
LazyRow(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,12 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import f.cking.software.BuildConfig
import f.cking.software.R
import f.cking.software.data.helpers.IntentHelper
import f.cking.software.data.repo.DevicesRepository
import f.cking.software.data.repo.SettingsRepository
import f.cking.software.domain.interactor.GetAppUsageDaysInteractor
import f.cking.software.domain.interactor.filterchecker.FilterCheckerImpl
import f.cking.software.domain.model.DeviceData
import f.cking.software.domain.model.ManufacturerInfo
Expand All @@ -24,6 +28,9 @@ class DeviceListViewModel(
private val devicesRepository: DevicesRepository,
private val filterCheckerImpl: FilterCheckerImpl,
val router: Router,
private val getAppUsageDaysInteractor: GetAppUsageDaysInteractor,
private val settingsRepository: SettingsRepository,
private val intentHelper: IntentHelper,
) : ViewModel() {

var devicesViewState by mutableStateOf(emptyList<DeviceData>())
Expand All @@ -37,6 +44,7 @@ class DeviceListViewModel(
DefaultFilters.isFavorite(context),
)
)
var enjoyTheAppState: EnjoyTheAppState by mutableStateOf(EnjoyTheAppState.NONE)

private val generalComparator = Comparator<DeviceData> { second, first ->
when {
Expand Down Expand Up @@ -133,10 +141,51 @@ class DeviceListViewModel(
devices
.filter { checkFilter(it, filter) && filterQuery(it, query) }
.sortedWith(generalComparator)
.apply {
if (size >= MIN_DEVICES_FOR_ENJOY_THE_APP) {
checkEnjoyTheApp()
}
}
}
}

private fun checkEnjoyTheApp() {
enjoyTheAppState = if (enjoyTheAppState == EnjoyTheAppState.NONE
&& !settingsRepository.getEnjoyTheAppAnswered()
&& getAppUsageDaysInteractor.execute() >= MIN_DAYS_FOR_ENJOY_THE_APP
) {
EnjoyTheAppState.QUESTION
} else {
EnjoyTheAppState.NONE
}
}

private fun filterQuery(device: DeviceData, query: String?): Boolean {
fun onEnjoyTheAppAnswered(answer: Boolean) {
enjoyTheAppState = if (answer) {
EnjoyTheAppState.LIKE
} else {
EnjoyTheAppState.DISLIKE
}
}

fun onEnjoyTheAppRatePlayStoreClick() {
settingsRepository.setEnjoyTheAppAnswered(true)
enjoyTheAppState = EnjoyTheAppState.NONE
intentHelper.openUrl(BuildConfig.GOOGLE_PLAY_URL)
}

fun onEnjoyTheAppRateGithubClick() {
settingsRepository.setEnjoyTheAppAnswered(true)
intentHelper.openUrl(BuildConfig.GITHUB_URL)
}

fun onEnjoyTheAppReportClick() {
settingsRepository.setEnjoyTheAppAnswered(true)
enjoyTheAppState = EnjoyTheAppState.NONE
intentHelper.openUrl(BuildConfig.REPORT_ISSUE_URL)
}

fun filterQuery(device: DeviceData, query: String?): Boolean {
return query?.takeIf { it.isNotBlank() }?.let { searchStr ->
(device.name?.contains(searchStr, true) ?: false)
|| (device.customName?.contains(searchStr, true) ?: false)
Expand All @@ -158,6 +207,10 @@ class DeviceListViewModel(
val filter: RadarProfile.Filter,
)

enum class EnjoyTheAppState {
NONE, QUESTION, LIKE, DISLIKE
}

object DefaultFilters {

fun notApple(context: Context) = FilterHolder(
Expand All @@ -172,4 +225,9 @@ class DeviceListViewModel(
filter = RadarProfile.Filter.IsFavorite(favorite = true)
)
}

companion object {
private const val MIN_DEVICES_FOR_ENJOY_THE_APP = 10
private const val MIN_DAYS_FOR_ENJOY_THE_APP = 3
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,9 @@ object ProfilesListScreen {
elevation = 4.dp,
) {
LazyRow(
modifier = Modifier.padding(vertical = 8.dp),
modifier = Modifier
.background(colorResource(id = R.color.primary_surface))
.padding(vertical = 8.dp),
) {
item {
Spacer(modifier = Modifier.width(8.dp))
Expand Down
Loading

0 comments on commit 78121fb

Please sign in to comment.