Skip to content

Commit

Permalink
Handle Android 13 permissions. (#682)
Browse files Browse the repository at this point in the history
  • Loading branch information
Swordfish90 committed Jul 3, 2023
1 parent 187ebcd commit 33c402a
Show file tree
Hide file tree
Showing 8 changed files with 158 additions and 125 deletions.
1 change: 1 addition & 0 deletions lemuroid-app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />

<application
android:name="com.swordfish.lemuroid.app.LemuroidApplication"
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,57 +7,75 @@ import com.swordfish.lemuroid.R
import com.swordfish.lemuroid.app.mobile.shared.withModelsFrom
import com.swordfish.lemuroid.app.shared.GameInteractor
import com.swordfish.lemuroid.app.shared.covers.CoverLoader
import com.swordfish.lemuroid.app.shared.settings.SettingsInteractor
import com.swordfish.lemuroid.common.kotlin.lazySequenceOf
import com.swordfish.lemuroid.lib.library.db.entity.Game
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.launch

class EpoxyHomeController(
private val gameInteractor: GameInteractor,
private val settingsInteractor: SettingsInteractor,
private val coverLoader: CoverLoader
) : AsyncEpoxyController() {

private var uiState = HomeViewModel.UIState()
enum class HomeAction {
CHANGE_STORAGE_FOLDER,
ENABLE_NOTIFICATION_PERMISSION
}

private var currentUIState = HomeViewModel.UIState()
private val actionsFlow = MutableSharedFlow<HomeAction>()

fun getActions(): Flow<HomeAction> {
return actionsFlow
}

fun update(viewState: HomeViewModel.UIState) {
uiState = viewState
fun updateState(viewState: HomeViewModel.UIState) {
currentUIState = viewState
requestModelBuild()
}

override fun buildModels() {
if (displayEmptyView()) {
addEmptyView()
}

if (displayEnableNotifications()) {
addNotificationsView()
}

if (displayFavorites()) {
addCarousel("favorites", R.string.favorites, uiState.favoritesGames)
addCarousel("favorites", R.string.favorites, currentUIState.favoritesGames)
}

if (displayRecents()) {
addCarousel("recent", R.string.recent, uiState.recentGames)
addCarousel("recent", R.string.recent, currentUIState.recentGames)
}

if (displayDiscovery()) {
addCarousel("discover", R.string.discover, uiState.discoveryGames)
}

if (displayEmptyView()) {
addEmptyView()
addCarousel("discover", R.string.discover, currentUIState.discoveryGames)
}
}

private fun displayDiscovery() = uiState.discoveryGames.isNotEmpty()
private fun displayDiscovery() = currentUIState.discoveryGames.isNotEmpty()

private fun displayRecents() = uiState.recentGames.isNotEmpty()
private fun displayRecents() = currentUIState.recentGames.isNotEmpty()

private fun displayFavorites() = uiState.favoritesGames.isNotEmpty()
private fun displayFavorites() = currentUIState.favoritesGames.isNotEmpty()

private fun displayEmptyView(): Boolean {
val conditions = lazySequenceOf(
{ uiState.loading.not() },
{ uiState.recentGames.isEmpty() },
{ uiState.favoritesGames.isEmpty() },
{ uiState.discoveryGames.isEmpty() },
{ currentUIState.loading.not() },
{ currentUIState.recentGames.isEmpty() },
{ currentUIState.favoritesGames.isEmpty() },
{ currentUIState.discoveryGames.isEmpty() },
)
return conditions.all { it }
}

private fun displayEnableNotifications() = currentUIState.notificationsEnabled.not()

private fun addCarousel(id: String, titleId: Int, games: List<Game>) {
epoxyHomeSection {
id("section_$id")
Expand All @@ -76,14 +94,31 @@ class EpoxyHomeController(
}
}

private fun addNotificationsView() {
epoxyHomeNotification {
id("notifications")
.title(R.string.home_notification_title)
.message(R.string.home_notification_message)
.action(R.string.home_notification_action)
.actionEnabled(true)
.onClick { this@EpoxyHomeController.launchAction(HomeAction.ENABLE_NOTIFICATION_PERMISSION) }
}
}

private fun addEmptyView() {
epoxyEmptyViewAction {
id("empty_home")
epoxyHomeNotification {
id("notification_empty")
.title(R.string.home_empty_title)
.message(R.string.home_empty_message)
.action(R.string.home_empty_action)
.actionEnabled(!this@EpoxyHomeController.uiState.indexInProgress)
.onClick { this@EpoxyHomeController.settingsInteractor.changeLocalStorageFolder() }
.actionEnabled(!this@EpoxyHomeController.currentUIState.indexInProgress)
.onClick { this@EpoxyHomeController.launchAction(HomeAction.CHANGE_STORAGE_FOLDER) }
}
}

private fun launchAction(homeAction: HomeAction) {
GlobalScope.launch {
actionsFlow.emit(homeAction)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
package com.swordfish.lemuroid.app.mobile.feature.home

import android.Manifest
import android.content.Context
import android.content.pm.PackageManager.PERMISSION_GRANTED
import android.os.Build
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.ViewModelProvider
Expand All @@ -16,9 +21,11 @@ import com.swordfish.lemuroid.app.shared.GameInteractor
import com.swordfish.lemuroid.app.shared.covers.CoverLoader
import com.swordfish.lemuroid.app.shared.settings.SettingsInteractor
import com.swordfish.lemuroid.common.coroutines.launchOnState
import com.swordfish.lemuroid.common.displayDetailsSettingsScreen
import com.swordfish.lemuroid.lib.library.db.RetrogradeDatabase
import dagger.android.support.AndroidSupportInjection
import javax.inject.Inject
import timber.log.Timber

class HomeFragment : Fragment() {

Expand All @@ -34,6 +41,14 @@ class HomeFragment : Fragment() {
@Inject
lateinit var settingsInteractor: SettingsInteractor

private lateinit var homeViewModel: HomeViewModel

private val requestPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) {
handleNotificationPermissionResponse(it)
}

override fun onAttach(context: Context) {
AndroidSupportInjection.inject(this)
super.onAttach(context)
Expand All @@ -51,26 +66,76 @@ class HomeFragment : Fragment() {
super.onViewCreated(view, savedInstanceState)

val factory = HomeViewModel.Factory(requireContext().applicationContext, retrogradeDb)
val homeViewModel = ViewModelProvider(this, factory)[HomeViewModel::class.java]
homeViewModel = ViewModelProvider(this, factory)[HomeViewModel::class.java]

// Disable snapping in carousel view
Carousel.setDefaultGlobalSnapHelperFactory(null)

val pagingController = EpoxyHomeController(gameInteractor, settingsInteractor, coverLoader)
val pagingController = EpoxyHomeController(gameInteractor, coverLoader)

val recyclerView = view.findViewById<RecyclerView>(R.id.home_recyclerview)
val layoutManager = LinearLayoutManager(requireContext(), LinearLayoutManager.VERTICAL, false)

recyclerView.layoutManager = layoutManager
recyclerView.adapter = pagingController.adapter

launchOnState(Lifecycle.State.RESUMED) {
pagingController.getActions().collect {
Timber.d("Received home view model action + $it")
handleEpoxyAction(it)
}
}

launchOnState(Lifecycle.State.RESUMED) {
homeViewModel.getViewStates().collect {
pagingController.update(it)
pagingController.updateState(it)
}
}
}

private fun handleEpoxyAction(homeAction: EpoxyHomeController.HomeAction) {
when (homeAction) {
EpoxyHomeController.HomeAction.CHANGE_STORAGE_FOLDER -> handleChangeStorageFolder()
EpoxyHomeController.HomeAction.ENABLE_NOTIFICATION_PERMISSION -> handleNotificationPermissionRequest()
}
}

private fun handleChangeStorageFolder() {
settingsInteractor.changeLocalStorageFolder()
}

private fun handleNotificationPermissionRequest() {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
return
}

requestPermissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
}

private fun handleNotificationPermissionResponse(isGranted: Boolean) {
if (!isGranted) {
requireContext().displayDetailsSettingsScreen()
}
}

private fun isNotificationsPermissionGranted(): Boolean {
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
return true
}

val permissionResult = ContextCompat.checkSelfPermission(
requireContext(),
Manifest.permission.POST_NOTIFICATIONS
)

return permissionResult == PERMISSION_GRANTED
}

override fun onResume() {
super.onResume()
homeViewModel.updateNotificationPermission(isNotificationsPermissionGranted())
}

@dagger.Module
class Module
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,22 +39,36 @@ class HomeViewModel(appContext: Context, retrogradeDb: RetrogradeDatabase) : Vie
val recentGames: List<Game> = emptyList(),
val discoveryGames: List<Game> = emptyList(),
val indexInProgress: Boolean = false,
val loading: Boolean = true
val loading: Boolean = true,
val notificationsEnabled: Boolean = true
)

private val notificationsEnabledState = MutableStateFlow(true)
private val uiStates = MutableStateFlow(UIState())

fun getViewStates(): Flow<UIState> {
return uiStates
}

fun updateNotificationPermission(isEnabled: Boolean) {
notificationsEnabledState.value = isEnabled
}

private fun buildViewState(
favoritesGames: List<Game>,
recentGames: List<Game>,
discoveryGames: List<Game>,
indexInProgress: Boolean
indexInProgress: Boolean,
notificationsEnabled: Boolean
): UIState {
return UIState(favoritesGames, recentGames, discoveryGames, indexInProgress, false)
return UIState(
favoritesGames,
recentGames,
discoveryGames,
indexInProgress,
false,
notificationsEnabled
)
}

init {
Expand All @@ -64,6 +78,7 @@ class HomeViewModel(appContext: Context, retrogradeDb: RetrogradeDatabase) : Vie
recentGames(retrogradeDb),
discoveryGames(retrogradeDb),
indexingInProgress(appContext),
notificationsEnabledState,
::buildViewState
)

Expand Down

0 comments on commit 33c402a

Please sign in to comment.