Skip to content

Commit

Permalink
Allow setting default device orientation
Browse files Browse the repository at this point in the history
Resolves #85
  • Loading branch information
ammargitham committed May 12, 2024
1 parent fa629e9 commit a2204b6
Show file tree
Hide file tree
Showing 32 changed files with 573 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import android.net.Uri
import androidx.annotation.IntRange
import androidx.work.Constraints
import androidx.work.NetworkType
import com.ammar.wallflow.model.DeviceOrientation
import com.ammar.wallflow.model.OnlineSource
import com.ammar.wallflow.model.WallpaperTarget
import com.ammar.wallflow.model.search.RedditSearch
Expand Down Expand Up @@ -56,6 +57,7 @@ data class AppPreferences(
val viewedWallpapersPreferences: ViewedWallpapersPreferences = ViewedWallpapersPreferences(),
val downloadLocation: Uri? = null,
val acraEnabled: Boolean = true,
val devicePreferences: DevicePreferences = DevicePreferences(),
) {
companion object {
const val CURRENT_VERSION = 2
Expand Down Expand Up @@ -188,3 +190,8 @@ data class LocalWallpapersPreferences(
val sort: LocalSort = LocalSort.NO_SORT,
val directories: Set<Uri>? = null,
)

@Serializable
data class DevicePreferences(
val defaultOrientation: DeviceOrientation = DeviceOrientation.Vertical,
)
Original file line number Diff line number Diff line change
Expand Up @@ -115,4 +115,5 @@ object PreferencesKeys {
val VIEWED_WALLPAPERS_ENABLED = booleanPreferencesKey("viewed_wallpapers_enabled")
val VIEWED_WALLPAPERS_LOOK = stringPreferencesKey("viewed_wallpapers_look")
val ENABLE_ACRA = booleanPreferencesKey("enable_acra")
val DEFAULT_ORIENTATION = intPreferencesKey("default_orientation")
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import androidx.datastore.preferences.core.emptyPreferences
import com.ammar.wallflow.IoDispatcher
import com.ammar.wallflow.data.preferences.AppPreferences
import com.ammar.wallflow.data.preferences.AutoWallpaperPreferences
import com.ammar.wallflow.data.preferences.DevicePreferences
import com.ammar.wallflow.data.preferences.GridColType
import com.ammar.wallflow.data.preferences.GridType
import com.ammar.wallflow.data.preferences.LayoutPreferences
Expand All @@ -26,10 +27,12 @@ import com.ammar.wallflow.data.preferences.defaultAutoWallpaperConstraints
import com.ammar.wallflow.data.preferences.defaultAutoWallpaperFreq
import com.ammar.wallflow.extensions.TAG
import com.ammar.wallflow.extensions.accessibleFolders
import com.ammar.wallflow.extensions.guessDefaultOrientation
import com.ammar.wallflow.extensions.toConstraintTypeMap
import com.ammar.wallflow.extensions.toConstraints
import com.ammar.wallflow.extensions.toUriOrNull
import com.ammar.wallflow.json
import com.ammar.wallflow.model.DeviceOrientation
import com.ammar.wallflow.model.OnlineSource
import com.ammar.wallflow.model.WallpaperTarget
import com.ammar.wallflow.model.search.RedditSearch
Expand Down Expand Up @@ -344,6 +347,18 @@ class AppPreferencesRepository @Inject constructor(
set(PreferencesKeys.ENABLE_ACRA, enable)
}

suspend fun updateDevicePreferences(
devicePreferences: DevicePreferences,
) = withContext(ioDispatcher) {
dataStore.edit { it.updateDevicePreferences(devicePreferences) }
}

private fun MutablePreferences.updateDevicePreferences(
devicePreferences: DevicePreferences,
) {
set(PreferencesKeys.DEFAULT_ORIENTATION, devicePreferences.defaultOrientation.value)
}

private suspend fun mapAppPreferences(preferences: Preferences): AppPreferences {
val homeRedditSearch = getHomeRedditSearch(preferences)
return AppPreferences(
Expand All @@ -367,9 +382,16 @@ class AppPreferencesRepository @Inject constructor(
mainRedditSearch = getMainRedditSearch(preferences),
viewedWallpapersPreferences = getViewedWallpapersPreferences(preferences),
acraEnabled = preferences[PreferencesKeys.ENABLE_ACRA] ?: true,
devicePreferences = getDevicePreferences(preferences),
)
}

private fun getDevicePreferences(preferences: Preferences) = DevicePreferences(
defaultOrientation = preferences[PreferencesKeys.DEFAULT_ORIENTATION]?.let {
DeviceOrientation.valueOf(it)
} ?: context.guessDefaultOrientation(),
)

private fun getViewedWallpapersPreferences(preferences: Preferences) =
ViewedWallpapersPreferences(
enabled = preferences[PreferencesKeys.VIEWED_WALLPAPERS_ENABLED] ?: false,
Expand Down Expand Up @@ -732,6 +754,7 @@ class AppPreferencesRepository @Inject constructor(
}
updateViewedWallpapersPreferences(appPreferences.viewedWallpapersPreferences)
updateAcraEnabled(appPreferences.acraEnabled)
updateDevicePreferences(appPreferences.devicePreferences)
}
}
}
Expand Down
75 changes: 65 additions & 10 deletions app/src/main/java/com/ammar/wallflow/extensions/Context.ext.kt
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ import com.ammar.wallflow.FILE_PROVIDER_AUTHORITY
import com.ammar.wallflow.MIME_TYPE_JPEG
import com.ammar.wallflow.R
import com.ammar.wallflow.WEB_URL_REGEX
import com.ammar.wallflow.data.preferences.DevicePreferences
import com.ammar.wallflow.model.DeviceOrientation
import com.ammar.wallflow.model.WallpaperTarget
import com.ammar.wallflow.model.toWhichInt
import com.ammar.wallflow.ui.common.permissions.checkSetWallpaperPermission
Expand Down Expand Up @@ -83,9 +85,13 @@ fun Context.setWallpaper(
display: Display,
uri: Uri,
cropRect: Rect,
devicePreferences: DevicePreferences,
targets: Set<WallpaperTarget> = setOf(WallpaperTarget.HOME, WallpaperTarget.LOCKSCREEN),
) = this.contentResolver.openInputStream(uri).use {
val screenResolution = getScreenResolution(true, display.displayId)
val screenResolution = getDefaultScreenResolution(
devicePreferences = devicePreferences,
displayId = display.displayId,
)
if (it == null) return@use false
val decoder = getBitmapRegionDecoder(it) ?: return@use false
val (opts, _) = getDecodeSampledBitmapOptions(
Expand Down Expand Up @@ -231,16 +237,32 @@ fun Context.getMLModelsFileIfExists(fileName: String): File? {
val Context.displayManager
get() = getSystemService(Context.DISPLAY_SERVICE) as DisplayManager

fun Context.getScreenResolution(
inDefaultOrientation: Boolean = false,
fun Context.getCurrentScreenResolution(
displayId: Int = Display.DEFAULT_DISPLAY,
) = getScreenResolution(
devicePreferences = DevicePreferences(),
inDefaultOrientation = false,
displayId = displayId,
)

fun Context.getDefaultScreenResolution(
devicePreferences: DevicePreferences,
displayId: Int = Display.DEFAULT_DISPLAY,
) = getScreenResolution(
devicePreferences = devicePreferences,
inDefaultOrientation = true,
displayId = displayId,
)

private fun Context.getScreenResolution(
devicePreferences: DevicePreferences,
inDefaultOrientation: Boolean,
displayId: Int = Display.DEFAULT_DISPLAY,
): IntSize {
val display = displayManager.getDisplay(displayId) ?: return IntSize.Zero
var changeOrientation = false
if (inDefaultOrientation) {
val rotation = display.rotation
// if current rotation is 90 or 270, device is rotated, so we will swap width and height
changeOrientation = rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270
changeOrientation = !isInDefaultOrientation(devicePreferences)
}
val metrics = DisplayMetrics()
@Suppress("DEPRECATION")
Expand All @@ -253,10 +275,43 @@ fun Context.getScreenResolution(
)
}

fun Context.isInDefaultOrientation(): Boolean {
val display = displayManager.getDisplay(Display.DEFAULT_DISPLAY) ?: return true
val rotation = display.rotation
return rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180
fun Context.isInDefaultOrientation(
devicePreferences: DevicePreferences,
) = devicePreferences.defaultOrientation == currentDeviceOrientation

val Context.currentDisplayRotation: Int
get() = displayManager.getDisplay(Display.DEFAULT_DISPLAY)
?.rotation
?: Surface.ROTATION_0

val Context.currentDeviceOrientation: DeviceOrientation
get() {
val resolution = getCurrentScreenResolution()
return if (resolution.height >= resolution.width) {
DeviceOrientation.Vertical
} else {
DeviceOrientation.Horizontal
}
}

fun Context.guessDefaultOrientation(): DeviceOrientation {
val resolution = getCurrentScreenResolution()
val orientation = if (resolution.height >= resolution.width) {
DeviceOrientation.Vertical
} else {
DeviceOrientation.Horizontal
}
val rotation = currentDisplayRotation
return if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) {
orientation
} else {
// return rotated orientation
if (orientation == DeviceOrientation.Vertical) {
DeviceOrientation.Horizontal
} else {
DeviceOrientation.Vertical
}
}
}

fun Context.getUriForFile(file: File): Uri = FileProvider.getUriForFile(
Expand Down
26 changes: 26 additions & 0 deletions app/src/main/java/com/ammar/wallflow/model/DeviceOrientation.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.ammar.wallflow.model

import androidx.compose.runtime.MutableState
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.Saver
import kotlinx.serialization.Serializable

@Serializable
@JvmInline
value class DeviceOrientation private constructor(internal val value: Int) {
companion object {
val Vertical = DeviceOrientation(0)
val Horizontal = DeviceOrientation(1)

fun valueOf(value: Int): DeviceOrientation = if (value == 0) {
Vertical
} else {
Horizontal
}
}
}

val MutableDeviceOrientationSaver = Saver<MutableState<DeviceOrientation>, Int>(
save = { it.value.value },
restore = { mutableStateOf(DeviceOrientation.valueOf(it)) },
)
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ package com.ammar.wallflow.ui.common

import android.content.res.Configuration
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.core.animateFloat
import androidx.compose.animation.core.updateTransition
import androidx.compose.animation.expandVertically
Expand Down Expand Up @@ -57,18 +56,15 @@ import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.ammar.wallflow.ISSUE_TRACKER_URL
import com.ammar.wallflow.R
import com.ammar.wallflow.extensions.getScreenResolution
import com.ammar.wallflow.extensions.getCurrentScreenResolution
import com.ammar.wallflow.extensions.toAnnotatedString
import com.ammar.wallflow.extensions.toDp
import com.ammar.wallflow.ui.theme.WallFlowTheme
import kotlinx.collections.immutable.ImmutableMap
import kotlinx.collections.immutable.persistentMapOf
import org.acra.ReportField

@OptIn(
ExperimentalAnimationApi::class,
ExperimentalMaterial3Api::class,
)
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CrashReportDialog(
modifier: Modifier = Modifier,
Expand Down Expand Up @@ -159,7 +155,7 @@ fun CrashReportDialog(
SelectionContainer {
val verticalScrollState = rememberScrollState()
val horizontalScrollState = rememberScrollState()
val screenHeight = context.getScreenResolution().height
val screenHeight = context.getCurrentScreenResolution().height

Text(
modifier = Modifier
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package com.ammar.wallflow.ui.common

import android.content.res.Configuration
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.requiredWidth
import androidx.compose.foundation.selection.selectable
import androidx.compose.material3.LocalTextStyle
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.RadioButton
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.semantics.Role
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import com.ammar.wallflow.ui.theme.WallFlowTheme

@Composable
fun RadioButtonListItem(
modifier: Modifier = Modifier,
selected: Boolean = false,
label: @Composable () -> Unit = {},
onClick: () -> Unit = {},
) {
Row(
modifier = Modifier
.fillMaxWidth()
.height(56.dp)
.selectable(
selected = selected,
onClick = onClick,
role = Role.RadioButton,
)
.padding(horizontal = 16.dp)
.then(modifier),
verticalAlignment = Alignment.CenterVertically,
) {
RadioButton(
selected = selected,
onClick = null,
)
Spacer(modifier = Modifier.requiredWidth(16.dp))
CompositionLocalProvider(
LocalTextStyle provides LocalTextStyle.current.merge(
MaterialTheme.typography.bodyLarge,
),
content = label,
)
}
}

@Preview
@Preview(uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable
private fun PreviewRadioButtonListItem() {
WallFlowTheme {
Surface {
RadioButtonListItem(
label = {
Text(text = "Label")
},
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ import androidx.compose.ui.unit.IntSize
import androidx.compose.ui.unit.dp
import com.ammar.wallflow.COMMON_RESOLUTIONS
import com.ammar.wallflow.R
import com.ammar.wallflow.extensions.getScreenResolution
import com.ammar.wallflow.extensions.trimAll
import com.ammar.wallflow.model.Order
import com.ammar.wallflow.model.Purity
Expand Down Expand Up @@ -463,6 +462,7 @@ internal fun OrderFilter(
internal fun MinResolutionFilter(
modifier: Modifier = Modifier,
resolution: IntSize? = null,
localResolution: IntSize = IntSize(1, 1),
onChange: (IntSize?) -> Unit = {},
onAddCustomResolutionClick: () -> Unit = {},
) {
Expand All @@ -478,6 +478,7 @@ internal fun MinResolutionFilter(
)
} else {
AddResolutionButton(
localResolution = localResolution,
addedResolutions = emptySet(),
onAdd = { onChange(it) },
onCustomClick = onAddCustomResolutionClick,
Expand All @@ -490,6 +491,7 @@ internal fun MinResolutionFilter(
@Composable
internal fun ResolutionsFilter(
modifier: Modifier = Modifier,
localResolution: IntSize = IntSize(1, 1),
resolutions: Set<IntSize> = emptySet(),
onChange: (resolutions: Set<IntSize>) -> Unit = {},
onAddCustomResolutionClick: () -> Unit = {},
Expand Down Expand Up @@ -518,6 +520,7 @@ internal fun ResolutionsFilter(
}
}
AddResolutionButton(
localResolution = localResolution,
addedResolutions = resolutions,
onAdd = { onChange(resolutions + it) },
onCustomClick = onAddCustomResolutionClick,
Expand All @@ -528,13 +531,12 @@ internal fun ResolutionsFilter(
@Composable
private fun AddResolutionButton(
modifier: Modifier = Modifier,
localResolution: IntSize = IntSize(1, 1),
addedResolutions: Set<IntSize> = emptySet(),
onAdd: (resolution: IntSize) -> Unit = {},
onCustomClick: () -> Unit = {},
) {
var expanded by remember { mutableStateOf(false) }
val context = LocalContext.current
val localResolution = context.getScreenResolution(true)
val localInCommon = remember(localResolution) { localResolution in COMMON_RESOLUTIONS.values }
val scrollState = rememberScrollState()

Expand Down
Loading

0 comments on commit a2204b6

Please sign in to comment.