diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 6d09804..9f3a5ed 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -14,8 +14,8 @@ android { applicationId = "com.googol.android.apps.photos" minSdk = 24 targetSdk = 35 - versionCode = 16 - versionName = "0.1.0-dev16" + versionCode = 17 + versionName = "0.1.0-dev17" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { diff --git a/app/src/main/java/com/zs/gallery/impl/ViewerViewModel.kt b/app/src/main/java/com/zs/gallery/impl/ViewerViewModel.kt index 3125f0d..2ee7095 100644 --- a/app/src/main/java/com/zs/gallery/impl/ViewerViewModel.kt +++ b/app/src/main/java/com/zs/gallery/impl/ViewerViewModel.kt @@ -19,6 +19,7 @@ package com.zs.gallery.impl import android.app.Activity +import android.content.ActivityNotFoundException import android.content.Intent import android.net.Uri import android.util.Log @@ -29,6 +30,7 @@ import androidx.compose.material.icons.outlined.Image import androidx.compose.material.icons.outlined.Share import androidx.compose.material.icons.outlined.Star import androidx.compose.material.icons.outlined.StarOutline +import androidx.compose.material.icons.outlined.Wallpaper import androidx.compose.runtime.derivedStateOf import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableLongStateOf @@ -55,7 +57,7 @@ private const val TAG = "ViewerViewModel" private val DELETE = MenuItem("action_delete", R.string.delete, Icons.Outlined.Delete) private val SHARE = MenuItem("action_share", R.string.share, Icons.Outlined.Share) -private val USE_AS = MenuItem("action_use_as", R.string.use_as, Icons.Outlined.Image) +private val USE_AS = MenuItem("action_use_as", R.string.set_as_wallpaper, Icons.Outlined.Wallpaper) private val EDIT_IN = MenuItem("action_edit_in", R.string.edit_in, Icons.Outlined.Edit) private val STAR = MenuItem("action_like", R.string.like, Icons.Outlined.StarOutline) private val UN_STAR = MenuItem("action_unlike", R.string.unlike, Icons.Outlined.Star) @@ -79,12 +81,44 @@ private fun EditIn(uri: Uri) = * @return An Intent configured for setting the wallpaper. */ private fun Wallpaper(uri: Uri) = - Intent(Intent.ACTION_ATTACH_DATA).apply { + Intent("android.service.wallpaper.CROP_AND_SET_WALLPAPER").apply { setDataAndType(uri, "image/*") putExtra("mimeType", "image/*") // Specifies the MIME type of the image addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) // Grants temporary read permission to the wallpaper app + addCategory(Intent.CATEGORY_DEFAULT) } +/** + * Sets the wallpaper using the provided URI. + * + * This function attempts to set the wallpaper using the default wallpaper cropper app. + * If the specified package is not found, it falls back to a generic wallpaper intent. + * If any other exception occurs, it shows a toast message to the user. + * + * @param uri The URI of the image to be set as wallpaper. + */ +private fun Activity.setWallpaper(uri: Uri) { + try { + // Create an intent to set the wallpaper using the specified URI + val intent = Wallpaper(uri).apply { + // Set the package to use the default wallpaper cropper app + setPackage("com.android.wallpapercropper") + } + // Attempt to start the activity with the specified package + startActivity(intent) + } catch (e: ActivityNotFoundException) { + // If the specified package is not found, fallback to a generic wallpaper intent + startActivity(Wallpaper(uri)) + } catch (e: Exception) { + // If any other exception occurs, show a toast message to the user + android.widget.Toast.makeText( + this, + "No wallpaper app found", + android.widget.Toast.LENGTH_SHORT + ).show() + } +} + class ViewerViewModel( handle: SavedStateHandle, @@ -104,7 +138,9 @@ class ViewerViewModel( override var details: MediaFile? by mutableStateOf(null) override var showDetails: Boolean get() = details != null - set(value) { details = if (value) current else null } + set(value) { + details = if (value) current else null + } override val actions: List by derivedStateOf { buildList { @@ -125,15 +161,15 @@ class ViewerViewModel( STAR, UN_STAR -> toggleLike() DELETE -> remove(activity) SHARE -> share(activity) - USE_AS -> { - val current = current ?: return - activity.startActivity(Wallpaper(current.mediaUri)) - } - EDIT_IN -> { val current = current ?: return activity.startActivity(EditIn(current.mediaUri)) } + + USE_AS -> { + val image = current?.mediaUri ?: return + activity.setWallpaper(image) + } } } diff --git a/app/src/main/java/com/zs/gallery/viewer/Viewer.kt b/app/src/main/java/com/zs/gallery/viewer/Viewer.kt index 10e66dc..f604522 100644 --- a/app/src/main/java/com/zs/gallery/viewer/Viewer.kt +++ b/app/src/main/java/com/zs/gallery/viewer/Viewer.kt @@ -4,6 +4,7 @@ package com.zs.gallery.viewer import android.content.Intent import android.net.Uri +import android.util.Log import androidx.activity.compose.BackHandler import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.ExperimentalSharedTransitionApi @@ -133,7 +134,7 @@ private suspend fun ZoomableState.scaledInsideAndCenterAlignedFrom(painter: Pain * Indicates whether the content is currently at its default zoom level (not zoomed in). */ private val ZoomableState.isZoomedOut - get() = zoomFraction == null || zoomFraction == 0f + get() = (zoomFraction ?: 0f) <= 0.0001f /** * TODO - Instead of opening video in 3rd party; Add inBuilt Impl in future versions. @@ -162,7 +163,7 @@ private fun FloatingActionMenu( verticalAlignment = Alignment.CenterVertically, modifier = Modifier.animateContentSize() ) { - Menu(actions, onItemClicked = onAction) + Menu(actions, onItemClicked = onAction, collapsed = 3) } }, ) @@ -268,6 +269,7 @@ private fun MainContent( // Horizontal pager to display the images/videos // Disable swipe when zoomed in // Preload adjacent pages for smoother transitions + Log.d(TAG, "MainContent - ZoomFraction: ${zoomable.zoomFraction}") HorizontalPager( state = pager, key = { values[it].id }, diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c9b5b91..7155923 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -92,6 +92,7 @@ Info Edit In Use as + Set as Wallpaper Like Unfavorite Details diff --git a/foundation/src/main/java/com/zs/foundation/adaptive/TwoPane.kt b/foundation/src/main/java/com/zs/foundation/adaptive/TwoPane.kt index d4f7579..eea13a1 100644 --- a/foundation/src/main/java/com/zs/foundation/adaptive/TwoPane.kt +++ b/foundation/src/main/java/com/zs/foundation/adaptive/TwoPane.kt @@ -78,6 +78,9 @@ private inline fun Slot(content: @Composable () -> Unit) = Box(content = { content() }) +private val TwoPaneStrategy.scrim get() = + if (this is StackedTwoPaneStrategy) DEFAULT_SCRIM_COLOR else Color.Transparent + /** * A two-pane layout that displays content and details using a configurable strategy. * @@ -134,13 +137,13 @@ fun TwoPane( spacing: Dp = DEFAULT_SPACING, background: Color = AppTheme.colors.background, onColor: Color = AppTheme.colors.onBackground, - scrim: Color = DEFAULT_SCRIM_COLOR, + strategy: TwoPaneStrategy = VerticalTwoPaneStrategy(0.5f), + scrim: Color = strategy.scrim, details: @Composable () -> Unit = { }, topBar: @Composable () -> Unit = { }, floatingActionButton: @Composable () -> Unit = { }, - fabPosition: FabPosition = FabPosition.End, onDismissRequest: (() -> Unit)? = null, - strategy: TwoPaneStrategy = VerticalTwoPaneStrategy(0.5f) + fabPosition: FabPosition = FabPosition.End, ) { // The indent propagated through window.contentIndent // The removes its old value; which means child has access to only topBar indent.