Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ android {
defaultConfig {
applicationId = "app.gamenative"

minSdk = 26
targetSdk = 28
minSdk = 28
targetSdk = 31

versionCode = 9
versionName = "0.7.0"
Expand Down
25 changes: 11 additions & 14 deletions app/src/main/java/app/gamenative/MainActivity.kt
Original file line number Diff line number Diff line change
@@ -1,36 +1,28 @@
package app.gamenative

import android.Manifest
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.content.res.Configuration
import android.graphics.Color.TRANSPARENT
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.view.KeyEvent
import android.view.MotionEvent
import android.view.OrientationEventListener
import androidx.activity.ComponentActivity
import androidx.activity.OnBackPressedCallback
import androidx.activity.SystemBarStyle
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.runtime.CompositionLocalProvider
import androidx.lifecycle.lifecycleScope
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.platform.LocalContext
import coil.ImageLoader
import coil.disk.DiskCache
import coil.memory.MemoryCache
import coil.request.CachePolicy
import androidx.lifecycle.lifecycleScope
import app.gamenative.events.AndroidEvent
import app.gamenative.service.SteamService
import app.gamenative.service.gog.GOGService
Expand All @@ -41,16 +33,21 @@ import app.gamenative.utils.ContainerUtils
import app.gamenative.utils.IconDecoder
import app.gamenative.utils.IntentLaunchManager
import app.gamenative.utils.LocaleHelper
import app.gamenative.utils.PermissionManager
import coil.ImageLoader
import coil.disk.DiskCache
import coil.memory.MemoryCache
import coil.request.CachePolicy
import com.posthog.PostHog
import com.skydoves.landscapist.coil.LocalCoilImageLoader
import com.winlator.core.AppUtils
import com.winlator.inputcontrols.ControllerManager
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
import java.util.EnumSet
import kotlin.math.abs
import okio.Path.Companion.toOkioPath
import timber.log.Timber
import java.util.EnumSet
import kotlin.math.abs

@AndroidEntryPoint
class MainActivity : ComponentActivity() {
Expand Down Expand Up @@ -148,19 +145,19 @@ class MainActivity : ComponentActivity() {

setContent {
var hasNotificationPermission by remember { mutableStateOf(false) }
val context = LocalContext.current
val permissionLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.RequestPermission(),
) { isGranted ->
hasNotificationPermission = isGranted
}

LaunchedEffect(Unit) {
if (!hasNotificationPermission && Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
permissionLauncher.launch(Manifest.permission.POST_NOTIFICATIONS)
if (!hasNotificationPermission) {
PermissionManager.requestNotificationPermission(context, permissionLauncher)
}
}

val context = LocalContext.current
val imageLoader = remember {
val memoryCache = MemoryCache.Builder(context)
.maxSizePercent(0.1)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,27 +1,22 @@
package app.gamenative.ui.component.dialog

import android.widget.Toast
import android.widget.Spinner
import android.widget.ArrayAdapter
import android.content.Intent
import android.content.res.Configuration
import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.foundation.layout.asPaddingValues
import androidx.compose.foundation.layout.calculateEndPadding
import androidx.compose.foundation.layout.calculateStartPadding
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBars
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.horizontalScroll
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
Expand All @@ -32,6 +27,7 @@ import androidx.compose.material.icons.filled.Save
import androidx.compose.material.icons.outlined.AddCircleOutline
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.CenterAlignedTopAppBar
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
Expand All @@ -40,11 +36,9 @@ import androidx.compose.material3.ExposedDropdownMenuDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.TextButton
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Slider
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.runtime.Composable
Expand All @@ -64,46 +58,43 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.LayoutDirection
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.compose.ui.window.Dialog
import androidx.compose.ui.window.DialogProperties
import androidx.compose.ui.tooling.preview.Preview
import app.gamenative.R
import app.gamenative.service.SteamService
import app.gamenative.ui.component.dialog.state.MessageDialogState
import app.gamenative.ui.component.settings.SettingsCPUList
import app.gamenative.ui.component.settings.SettingsCenteredLabel
import app.gamenative.ui.component.settings.SettingsEnvVars
import app.gamenative.ui.component.settings.SettingsListDropdown
import app.gamenative.ui.component.settings.SettingsMultiListDropdown
import app.gamenative.ui.components.rememberCustomGameFolderPicker
import app.gamenative.ui.components.requestPermissionsForPath
import app.gamenative.ui.theme.PluviaTheme
import app.gamenative.ui.theme.settingsTileColors
import app.gamenative.ui.theme.settingsTileColorsAlt
import app.gamenative.utils.CustomGameScanner
import app.gamenative.utils.ContainerUtils
import app.gamenative.service.SteamService
import com.winlator.contents.ContentProfile
import com.winlator.contents.ContentsManager
import com.winlator.contents.AdrenotoolsManager
import app.gamenative.utils.PermissionManager
import com.alorma.compose.settings.ui.SettingsGroup
import com.alorma.compose.settings.ui.SettingsMenuLink
import com.alorma.compose.settings.ui.SettingsSwitch
import com.winlator.box86_64.Box86_64PresetManager
import com.winlator.container.Container
import com.winlator.container.ContainerData
import com.winlator.contents.AdrenotoolsManager
import com.winlator.contents.ContentProfile
import com.winlator.contents.ContentsManager
import com.winlator.core.DefaultVersion
import com.winlator.core.GPUHelper
import com.winlator.core.KeyValueSet
import com.winlator.core.StringUtils
import com.winlator.core.WineInfo.MAIN_WINE_VERSION
import com.winlator.core.envvars.EnvVarInfo
import com.winlator.core.envvars.EnvVars
import com.winlator.core.envvars.EnvVarSelectionType
import com.winlator.core.DefaultVersion
import com.winlator.core.GPUHelper
import com.winlator.core.WineInfo
import com.winlator.core.WineInfo.MAIN_WINE_VERSION
import com.winlator.fexcore.FEXCoreManager
import com.winlator.core.envvars.EnvVars
import com.winlator.fexcore.FEXCorePresetManager
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
Expand Down Expand Up @@ -726,6 +717,9 @@ fun ContainerConfigDialog(
val storagePermissionLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.RequestMultiplePermissions(),
) { }
val allFilesAccessLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartActivityForResult(),
) { }

val folderPicker = rememberCustomGameFolderPicker(
onPathSelected = { path ->
Expand Down Expand Up @@ -764,8 +758,12 @@ fun ContainerConfigDialog(
} catch (_: Exception) {
false
}
if (!canAccess && !CustomGameScanner.hasStoragePermission(context, path)) {
requestPermissionsForPath(context, path, storagePermissionLauncher)
if (!canAccess && !PermissionManager.hasStorageAccessForPath(context, path)) {
PermissionManager.requestStorageAccess(
context,
storagePermissionLauncher,
allFilesAccessLauncher,
)
}

config = config.copy(drives = "${config.drives}${letter}:${path}")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,15 @@
package app.gamenative.ui.components

import android.Manifest
import android.content.Context
import android.net.Uri
import android.os.Build
import android.os.Environment
import android.provider.DocumentsContract
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.compose.ManagedActivityResultLauncher
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
import app.gamenative.R
import app.gamenative.utils.CustomGameScanner

/**
* Converts a document tree URI to a file path.
Expand Down Expand Up @@ -79,32 +75,6 @@ fun getPathFromTreeUri(uri: Uri?): String? {
}
}

/**
* Ensures we have the correct permissions for the provided path.
*/
fun requestPermissionsForPath(
context: Context,
path: String,
storagePermissionLauncher: ManagedActivityResultLauncher<Array<String>, Map<String, Boolean>>?,
) {
val isOutsideSandbox = !path.contains("/Android/data/${context.packageName}") &&
!path.contains(context.dataDir.path)

if (!isOutsideSandbox) {
return
}

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
CustomGameScanner.requestManageExternalStoragePermission(context)
} else {
val permissions = arrayOf(
Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
)
storagePermissionLauncher?.launch(permissions)
}
}

data class CustomGameFolderPicker(
val launchPicker: () -> Unit,
)
Expand Down Expand Up @@ -141,4 +111,3 @@ fun rememberCustomGameFolderPicker(
)
}
}

Loading