diff --git a/common/src/main/java/com/github/sikv/photos/common/ActivityPermissionManager.kt b/common/src/main/java/com/github/sikv/photos/common/ActivityPermissionManager.kt index a140f8d1..151689f9 100644 --- a/common/src/main/java/com/github/sikv/photos/common/ActivityPermissionManager.kt +++ b/common/src/main/java/com/github/sikv/photos/common/ActivityPermissionManager.kt @@ -2,13 +2,13 @@ package com.github.sikv.photos.common import androidx.activity.result.ActivityResultLauncher import androidx.activity.result.contract.ActivityResultContracts -import androidx.appcompat.app.AppCompatActivity +import androidx.fragment.app.FragmentActivity import androidx.lifecycle.DefaultLifecycleObserver import androidx.lifecycle.LifecycleOwner import java.util.UUID class ActivityPermissionManager( - private val activity: AppCompatActivity + private val activity: FragmentActivity ) : DefaultLifecycleObserver { private val key = UUID.randomUUID().toString() diff --git a/compose-ui/src/main/java/com/github/sikv/photos/compose/ui/DynamicPhotoItem.kt b/compose-ui/src/main/java/com/github/sikv/photos/compose/ui/DynamicPhotoItem.kt new file mode 100644 index 00000000..544d2436 --- /dev/null +++ b/compose-ui/src/main/java/com/github/sikv/photos/compose/ui/DynamicPhotoItem.kt @@ -0,0 +1,53 @@ +package com.github.sikv.photos.compose.ui + +import androidx.compose.runtime.Composable +import com.github.sikv.photos.domain.ListLayout +import com.github.sikv.photos.domain.Photo + +@Composable +fun DynamicPhotoItem( + photo: Photo, + isFavorite: Boolean, + listLayout: ListLayout, + onPhotoClick: (Photo) -> Unit, + onPhotoAttributionClick: (Photo) -> Unit, + onPhotoActionsClick: (Photo) -> Unit, + onToggleFavoriteClick: (Photo) -> Unit, + onSharePhotoClick: (Photo) -> Unit, + onDownloadPhotoClick: (Photo) -> Unit +) { + when (listLayout) { + ListLayout.LIST -> { + PhotoItem( + photo = photo, + isFavorite = isFavorite, + onClick = { + onPhotoClick(photo) + }, + onAttributionClick = { + onPhotoAttributionClick(photo) + }, + onMoreClick = { + onPhotoActionsClick(photo) + }, + onToggleFavorite = { + onToggleFavoriteClick(photo) + }, + onShareClick = { + onSharePhotoClick(photo) + }, + onDownloadClick = { + onDownloadPhotoClick(photo) + } + ) + } + ListLayout.GRID -> { + PhotoItemCompact( + photo = photo, + onClick = { + onPhotoClick(photo) + } + ) + } + } +} diff --git a/compose-ui/src/main/java/com/github/sikv/photos/compose/ui/SwitchLayoutAction.kt b/compose-ui/src/main/java/com/github/sikv/photos/compose/ui/SwitchLayoutAction.kt new file mode 100644 index 00000000..e152500e --- /dev/null +++ b/compose-ui/src/main/java/com/github/sikv/photos/compose/ui/SwitchLayoutAction.kt @@ -0,0 +1,28 @@ +package com.github.sikv.photos.compose.ui + +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import com.github.sikv.photos.domain.ListLayout + +@Composable +fun SwitchLayoutAction( + listLayout: ListLayout, + onSwitchLayoutClick: () -> Unit +) { + val icon = when (listLayout) { + ListLayout.LIST -> R.drawable.ic_view_grid_24dp + ListLayout.GRID -> R.drawable.ic_view_list_24dp + } + + IconButton( + onClick = onSwitchLayoutClick + ) { + Icon( + painter = painterResource(id = icon), + contentDescription = stringResource(id = R.string.switch_layout) + ) + } +} diff --git a/compose-ui/src/main/res/values/strings.xml b/compose-ui/src/main/res/values/strings.xml index d85896a3..7c240ac7 100644 --- a/compose-ui/src/main/res/values/strings.xml +++ b/compose-ui/src/main/res/values/strings.xml @@ -4,4 +4,5 @@ Share Download More + Switch layout diff --git a/feature/curated-photos/build.gradle b/feature/curated-photos/build.gradle index 3ae5755c..a329f25e 100644 --- a/feature/curated-photos/build.gradle +++ b/feature/curated-photos/build.gradle @@ -40,6 +40,7 @@ dependencies { implementation libs.inject kapt libs.hilt.compiler implementation libs.hilt.android + implementation libs.androidx.hilt.navigation.compose implementation libs.androidx.paging.runtime implementation libs.androidx.paging.compose diff --git a/feature/curated-photos/src/main/java/com/github/sikv/photos/curated/CuratedPhotosFragment.kt b/feature/curated-photos/src/main/java/com/github/sikv/photos/curated/CuratedPhotosFragment.kt index 21ed4212..fccc019f 100644 --- a/feature/curated-photos/src/main/java/com/github/sikv/photos/curated/CuratedPhotosFragment.kt +++ b/feature/curated-photos/src/main/java/com/github/sikv/photos/curated/CuratedPhotosFragment.kt @@ -6,8 +6,8 @@ import android.view.View import android.view.ViewGroup import androidx.compose.ui.platform.ComposeView import androidx.fragment.app.Fragment -import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController +import com.github.sikv.photo.usecase.PhotoActionsUseCase import com.github.sikv.photos.navigation.args.PhotoDetailsFragmentArguments import com.github.sikv.photos.navigation.route.PhotoDetailsRoute import com.google.accompanist.themeadapter.material3.Mdc3Theme @@ -20,7 +20,8 @@ class CuratedPhotosFragment : Fragment() { @Inject lateinit var photoDetailsRoute: PhotoDetailsRoute - private val viewModel: CuratedPhotosViewModel by viewModels() + @Inject + lateinit var photoActionsUseCase: PhotoActionsUseCase override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { return ComposeView(requireContext()).apply { @@ -31,10 +32,19 @@ class CuratedPhotosFragment : Fragment() { setContent { Mdc3Theme { CuratedPhotosScreen( - onGoToPhotoDetails = { photo -> + onPhotoClick = { photo -> photoDetailsRoute.present(findNavController(), PhotoDetailsFragmentArguments(photo)) }, - viewModel = viewModel + onPhotoAttributionClick = photoActionsUseCase::photoAttributionClick, + onPhotoActionsClick = { photo -> + photoActionsUseCase.openMoreActions(requireNotNull(activity), photo) + }, + onSharePhotoClick = { photo -> + photoActionsUseCase.sharePhoto(requireNotNull(activity), photo) + }, + onDownloadPhotoClick = { photo -> + photoActionsUseCase.downloadPhoto(requireNotNull(activity), photo) + } ) } } diff --git a/feature/curated-photos/src/main/java/com/github/sikv/photos/curated/CuratedPhotosScreen.kt b/feature/curated-photos/src/main/java/com/github/sikv/photos/curated/CuratedPhotosScreen.kt index c9d6860b..8878fbf3 100644 --- a/feature/curated-photos/src/main/java/com/github/sikv/photos/curated/CuratedPhotosScreen.kt +++ b/feature/curated-photos/src/main/java/com/github/sikv/photos/curated/CuratedPhotosScreen.kt @@ -2,29 +2,28 @@ package com.github.sikv.photos.curated import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.LazyVerticalGrid -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.paging.compose.collectAsLazyPagingItems -import com.github.sikv.photos.common.ui.findActivity -import com.github.sikv.photos.compose.ui.PhotoItem -import com.github.sikv.photos.compose.ui.PhotoItemCompact +import com.github.sikv.photos.compose.ui.DynamicPhotoItem import com.github.sikv.photos.compose.ui.Scaffold -import com.github.sikv.photos.domain.ListLayout +import com.github.sikv.photos.compose.ui.SwitchLayoutAction import com.github.sikv.photos.domain.Photo // TODO: Add loading indicator. @Composable internal fun CuratedPhotosScreen( - onGoToPhotoDetails: (Photo) -> Unit, - viewModel: CuratedPhotosViewModel, + onPhotoClick: (Photo) -> Unit, + onPhotoAttributionClick: (Photo) -> Unit, + onPhotoActionsClick: (Photo) -> Unit, + onSharePhotoClick: (Photo) -> Unit, + onDownloadPhotoClick: (Photo) -> Unit, + viewModel: CuratedPhotosViewModel = hiltViewModel(), ) { val photos = viewModel.getCuratedPhotos().collectAsLazyPagingItems() val listLayout by viewModel.listLayoutState.collectAsStateWithLifecycle() @@ -32,7 +31,10 @@ internal fun CuratedPhotosScreen( Scaffold( title = { Text(stringResource(id = R.string.photos)) }, actions = { - SwitchLayoutAction(viewModel = viewModel) + SwitchLayoutAction( + listLayout = listLayout, + onSwitchLayoutClick = viewModel::switchListLayout + ) } ) { LazyVerticalGrid( @@ -40,81 +42,21 @@ internal fun CuratedPhotosScreen( ) { items(photos.itemCount) { index -> photos[index]?.let { photo -> - Photo( + val isFavorite by viewModel.isFavorite(photo).collectAsStateWithLifecycle(initialValue = false) + + DynamicPhotoItem( photo = photo, + isFavorite = isFavorite, listLayout = listLayout, - viewModel = viewModel, - onGoToPhotoDetails = onGoToPhotoDetails + onPhotoClick = onPhotoClick, + onPhotoAttributionClick = onPhotoAttributionClick, + onPhotoActionsClick = onPhotoActionsClick, + onToggleFavoriteClick = viewModel::toggleFavorite, + onSharePhotoClick = onSharePhotoClick, + onDownloadPhotoClick = onDownloadPhotoClick ) } } } } } - -@Composable -private fun Photo( - photo: Photo, - listLayout: ListLayout, - viewModel: CuratedPhotosViewModel, - onGoToPhotoDetails: (Photo) -> Unit -) { - val context = LocalContext.current - val isFavorite by viewModel.isFavorite(photo).collectAsStateWithLifecycle(initialValue = false) - - when (listLayout) { - ListLayout.LIST -> { - PhotoItem( - photo = photo, - isFavorite = isFavorite, - onClick = { - onGoToPhotoDetails(photo) - }, - onAttributionClick = { - viewModel.onPhotoAttributionClick(context.findActivity(), photo) - }, - onMoreClick = { - viewModel.openActions(context.findActivity(), photo) - }, - onToggleFavorite = { - viewModel.toggleFavorite(photo) - }, - onShareClick = { - viewModel.sharePhoto(context.findActivity(), photo) - }, - onDownloadClick = { - viewModel.downloadPhoto(context.findActivity(), photo) - } - ) - } - ListLayout.GRID -> { - PhotoItemCompact( - photo = photo, - onClick = { - onGoToPhotoDetails(photo) - } - ) - } - } -} - -@Composable -private fun SwitchLayoutAction( - viewModel: CuratedPhotosViewModel -) { - val listLayout by viewModel.listLayoutState.collectAsStateWithLifecycle() - - val icon = when (listLayout) { - ListLayout.LIST -> R.drawable.ic_view_grid_24dp - ListLayout.GRID -> R.drawable.ic_view_list_24dp - } - - IconButton( - onClick = viewModel::switchListLayout - ) { - Icon( - painter = painterResource(id = icon), - contentDescription = stringResource(id = R.string.switch_layout) - ) - } -} diff --git a/feature/curated-photos/src/main/java/com/github/sikv/photos/curated/CuratedPhotosViewModel.kt b/feature/curated-photos/src/main/java/com/github/sikv/photos/curated/CuratedPhotosViewModel.kt index 6ecb8f71..581da073 100644 --- a/feature/curated-photos/src/main/java/com/github/sikv/photos/curated/CuratedPhotosViewModel.kt +++ b/feature/curated-photos/src/main/java/com/github/sikv/photos/curated/CuratedPhotosViewModel.kt @@ -1,18 +1,12 @@ package com.github.sikv.photos.curated -import android.widget.Toast -import androidx.appcompat.app.AppCompatActivity import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import androidx.paging.Pager import androidx.paging.PagingConfig import androidx.paging.PagingData -import com.github.sikv.photo.usecase.DownloadPhotoUseCase -import com.github.sikv.photo.usecase.PhotoActionsUseCase import com.github.sikv.photos.common.PreferencesService -import com.github.sikv.photos.common.ui.openUrl import com.github.sikv.photos.config.ConfigProvider -import com.github.sikv.photos.data.createShareIntent import com.github.sikv.photos.data.repository.FavoritesRepository2 import com.github.sikv.photos.data.repository.PhotosRepository import com.github.sikv.photos.domain.ListLayout @@ -30,9 +24,7 @@ internal class CuratedPhotosViewModel @Inject constructor( private val photosRepository: PhotosRepository, private val favoritesRepository: FavoritesRepository2, private val preferencesService: PreferencesService, - private val configProvider: ConfigProvider, - private val photoActionsUseCase: PhotoActionsUseCase, - private val downloadPhotoUseCase: DownloadPhotoUseCase + private val configProvider: ConfigProvider ) : ViewModel() { private val mutableListLayoutState = MutableStateFlow(preferencesService.getCuratedListLayout()) @@ -51,40 +43,6 @@ internal class CuratedPhotosViewModel @Inject constructor( } } - // TODO: This is temporary solution! - // Will be refactored after migration to Compose Navigation is finished. - fun onPhotoAttributionClick(activity: AppCompatActivity, photo: Photo) { - photo.getPhotoPhotographerUrl()?.let { photographerUrl -> - activity.openUrl(photographerUrl) - } ?: run { - activity.openUrl(photo.getPhotoShareUrl()) - } - } - - // TODO: This is temporary solution! - // Will be refactored after migration to Compose Navigation is finished. - fun openActions(activity: AppCompatActivity, photo: Photo) { - photoActionsUseCase.openActions(activity.supportFragmentManager, photo) { message -> - Toast.makeText(activity, message, Toast.LENGTH_SHORT) - .show() - } - } - - // TODO: This is temporary solution! - // Will be refactored after migration to Compose Navigation is finished. - fun downloadPhoto(activity: AppCompatActivity, photo: Photo) { - downloadPhotoUseCase.download(activity, photo) { message -> - Toast.makeText(activity, message, Toast.LENGTH_SHORT) - .show() - } - } - - // TODO: This is temporary solution! - // Will be refactored after migration to Compose Navigation is finished. - fun sharePhoto(activity: AppCompatActivity, photo: Photo) { - activity.startActivity(photo.createShareIntent()) - } - fun switchListLayout() { when (listLayoutState.value) { ListLayout.LIST -> { diff --git a/feature/curated-photos/src/main/res/values/strings.xml b/feature/curated-photos/src/main/res/values/strings.xml index d1894e14..461a9af6 100644 --- a/feature/curated-photos/src/main/res/values/strings.xml +++ b/feature/curated-photos/src/main/res/values/strings.xml @@ -1,5 +1,4 @@ Photos - Switch layout diff --git a/photo-usecase/src/main/java/com/github/sikv/photo/usecase/DownloadPhotoUseCase.kt b/photo-usecase/src/main/java/com/github/sikv/photo/usecase/DownloadPhotoUseCase.kt index e2651620..49204f82 100644 --- a/photo-usecase/src/main/java/com/github/sikv/photo/usecase/DownloadPhotoUseCase.kt +++ b/photo-usecase/src/main/java/com/github/sikv/photo/usecase/DownloadPhotoUseCase.kt @@ -2,7 +2,7 @@ package com.github.sikv.photo.usecase import android.Manifest import android.os.Build -import androidx.appcompat.app.AppCompatActivity +import androidx.fragment.app.FragmentActivity import com.github.sikv.photos.common.ActivityPermissionManager import com.github.sikv.photos.common.DownloadService import com.github.sikv.photos.common.ui.openAppSettings @@ -14,7 +14,7 @@ class DownloadPhotoUseCase @Inject constructor( private val downloadService: DownloadService ) { - fun download(activity: AppCompatActivity, photo: Photo, onShowMessage: (String) -> Unit) { + fun download(activity: FragmentActivity, photo: Photo, onShowMessage: (String) -> Unit) { val permissionManager = ActivityPermissionManager(activity) if (Build.VERSION.SDK_INT > Build.VERSION_CODES.Q) { @@ -41,7 +41,7 @@ class DownloadPhotoUseCase @Inject constructor( } private fun downloadPhotoInternal( - activity: AppCompatActivity, + activity: FragmentActivity, photo: Photo, onShowMessage: (String) -> Unit ) { diff --git a/photo-usecase/src/main/java/com/github/sikv/photo/usecase/PhotoActionsUseCase.kt b/photo-usecase/src/main/java/com/github/sikv/photo/usecase/PhotoActionsUseCase.kt index 33a01232..72391b52 100644 --- a/photo-usecase/src/main/java/com/github/sikv/photo/usecase/PhotoActionsUseCase.kt +++ b/photo-usecase/src/main/java/com/github/sikv/photo/usecase/PhotoActionsUseCase.kt @@ -1,9 +1,12 @@ package com.github.sikv.photo.usecase import android.content.Context -import androidx.fragment.app.FragmentManager +import android.content.Intent +import android.widget.Toast +import androidx.fragment.app.FragmentActivity import com.github.sikv.photos.common.ui.OptionsBottomSheetDialog import com.github.sikv.photos.common.ui.copyText +import com.github.sikv.photos.common.ui.openUrl import com.github.sikv.photos.domain.Photo import com.github.sikv.photos.navigation.args.SetWallpaperFragmentArguments import com.github.sikv.photos.navigation.route.SetWallpaperRoute @@ -12,14 +15,36 @@ import javax.inject.Inject class PhotoActionsUseCase @Inject constructor( @ApplicationContext private val context: Context, - private val setWallpaperRoute: SetWallpaperRoute + private val setWallpaperRoute: SetWallpaperRoute, + private val downloadPhotoUseCase: DownloadPhotoUseCase ) { - fun openActions( - fragmentManager: FragmentManager, - photo: Photo, - onShowMessage: (String) -> Unit - ) { + fun photoAttributionClick(photo: Photo) { + photo.getPhotoPhotographerUrl()?.let { photographerUrl -> + context.openUrl(photographerUrl) + } ?: run { + context.openUrl(photo.getPhotoShareUrl()) + } + } + + fun sharePhoto(activity: FragmentActivity, photo: Photo) { + val intent = Intent() + + intent.action = Intent.ACTION_SEND + intent.putExtra(Intent.EXTRA_TEXT, photo.getPhotoShareUrl()) + intent.type = "text/plain" + + activity.startActivity(intent) + } + + fun downloadPhoto(activity: FragmentActivity, photo: Photo) { + downloadPhotoUseCase.download(activity, photo) { message -> + Toast.makeText(activity, message, Toast.LENGTH_SHORT) + .show() + } + } + + fun openMoreActions(activity: FragmentActivity, photo: Photo) { val options = listOf( context.getString(R.string.set_wallpaper), context.getString(R.string.copy_link) @@ -29,7 +54,7 @@ class PhotoActionsUseCase @Inject constructor( when (index) { // Set Wallpaper 0 -> { - setWallpaperRoute.present(fragmentManager, SetWallpaperFragmentArguments(photo)) + setWallpaperRoute.present(activity.supportFragmentManager, SetWallpaperFragmentArguments(photo)) } // Copy Link 1 -> { @@ -37,11 +62,13 @@ class PhotoActionsUseCase @Inject constructor( val text = photo.getPhotoShareUrl() context.copyText(label, text) - onShowMessage(context.getString(R.string.link_copied)) + + Toast.makeText(activity, context.getString(R.string.link_copied), Toast.LENGTH_SHORT) + .show() } } } - dialog.show(fragmentManager) + dialog.show(activity.supportFragmentManager) } }