Skip to content

Commit

Permalink
[REFACTOR] Renamed SafeArgs method from 'get' to 'build'.
Browse files Browse the repository at this point in the history
[FEAT] Added 'get' extension method on `SavedStateHandle`.
[FEAT] Viewer now takes target with focused id as argument for seamless functionality; this has a wide range of applications.
[FIX] Removed FirebaseInitializer from manifest; now initializing Firebase using app startup library.
[FEAT] Added new Zoomable library for zoom functionality.
  • Loading branch information
iZakirSheikh committed Jul 28, 2024
1 parent 229bc8a commit c34ee5d
Show file tree
Hide file tree
Showing 15 changed files with 300 additions and 238 deletions.
5 changes: 3 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ android {
applicationId = "com.googol.android.apps.photos"
minSdk = 21
targetSdk = 34
versionCode = 5
versionName = "0.1.0-dev05"
versionCode = 6
versionName = "0.1.0-dev06"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
Expand Down Expand Up @@ -72,4 +72,5 @@ dependencies {
implementation(libs.firebase.analytics.ktx)
implementation(libs.firebase.crashlytics.ktx)
implementation(libs.androidx.ui.text.google.fonts)
implementation(libs.saket.zoomable)
}
19 changes: 14 additions & 5 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -33,11 +33,6 @@
android:exported="false"
tools:node="merge">

<!-- Koin-->
<meta-data
android:name="com.zs.gallery.impl.KoinInitializer"
android:value="androidx.startup" />

<!--Firebase-->
<meta-data
android:name="com.zs.gallery.impl.FirebaseInitializer"
Expand All @@ -47,8 +42,21 @@
<meta-data
android:name="com.zs.gallery.impl.CrashlyticsInitializer"
android:value="androidx.startup" />

<!-- Koin-->
<meta-data
android:name="com.zs.gallery.impl.KoinInitializer"
android:value="androidx.startup" />
</provider>

<!-- Disable FirebaseInitProvider -->
<provider
android:name="com.google.firebase.provider.FirebaseInitProvider"
android:authorities="${applicationId}.firebaseinitprovider"
android:enabled="false"
tools:node="remove"
android:exported="false"/>

<!--MainActivity-->
<activity
android:name=".MainActivity"
Expand All @@ -60,5 +68,6 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

</application>
</manifest>
1 change: 0 additions & 1 deletion app/src/main/java/com/zs/gallery/bin/Trash.kt
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,6 @@ import com.zs.gallery.files.FilesActionMenu
import com.zs.gallery.files.FolderViewState
import com.zs.gallery.files.GroupHeader
import com.zs.gallery.files.MediaFile
import com.zs.gallery.files.buildViewerRoute
import com.zs.gallery.preview.RouteViewer
import com.zs.gallery.settings.Settings

Expand Down
18 changes: 12 additions & 6 deletions app/src/main/java/com/zs/gallery/common/Route.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,6 @@
package com.zs.gallery.common

import androidx.compose.animation.AnimatedContentScope
import androidx.compose.animation.AnimatedContentTransitionScope
import androidx.compose.animation.EnterTransition
import androidx.compose.animation.ExitTransition
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.lifecycle.SavedStateHandle
Expand Down Expand Up @@ -80,14 +77,23 @@ interface SafeArgs<T> : Route {
operator fun invoke(arg: T): String

/**
* Extracts arguments for this route from the provided [SavedStateHandle].
* Builds the arguments for this route from the provided [SavedStateHandle].
*
* @param handle The [SavedStateHandle] to extract arguments from.
* @param handle The [SavedStateHandle] to use for building the arguments.
* @return The arguments for this route.
*/
operator fun get(handle: SavedStateHandle): T
fun build(handle: SavedStateHandle): T
}

/**
* Retrieves the [SafeArgs] object of type[T] associated with the given [SafeArgs] instance
* from this [SavedStateHandle].
*
* @param route The [SafeArgs] instance representing the navigation route and arguments.
* @return The args object of type [T] containing the deserialized arguments.
**/
operator fun <T> SavedStateHandle.get(route: SafeArgs<T>): T = route.build(this)


/**
* Adds a composable route to the [NavGraphBuilder] for the given [Route].
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/com/zs/gallery/files/Album.kt
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ fun Album(viewState: AlbumViewState) {
if (selected.isNotEmpty())
viewState.select(item.id)
else
navController.navigate(viewState.buildViewerRoute(item.id))
navController.navigate(RouteViewer(item.id, "favourites"))
},
// onLong Click
onLongClick = { viewState.select(item.id) }
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/com/zs/gallery/files/Folder.kt
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ fun Folder(viewState: FolderViewState) {
if (selected.isNotEmpty())
viewState.select(item.id)
else
navController.navigate(viewState.buildViewerRoute(item.id))
navController.navigate(RouteViewer(item.id, folder = item.path))
},
// onLong Click
onLongClick = { viewState.select(item.id) }
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/com/zs/gallery/files/Timeline.kt
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ fun Timeline(
if (selected.isNotEmpty())
viewState.select(item.id)
else
navController.navigate(viewState.buildViewerRoute(item.id))
navController.navigate(RouteViewer(item.id))
},
// onLong Click
onLongClick = { viewState.select(item.id) }
Expand Down
10 changes: 2 additions & 8 deletions app/src/main/java/com/zs/gallery/files/ViewState.kt
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ private const val PARAM_PATH = "pram_path"
object RouteFolder : SafeArgs<String> {
override val route: String = "$domain/{${PARAM_PATH}}"
override fun invoke(arg: String): String = "$domain/${Uri.encode(arg)}"
override fun get(handle: SavedStateHandle): String = handle[PARAM_PATH]!!
override fun build(handle: SavedStateHandle): String = handle[PARAM_PATH]!!
}

/**
Expand All @@ -46,13 +46,7 @@ interface DataProvider {
val data: Map<String, List<MediaFile>>?
}

/**
* @return the route for the given id.
*/
fun DataProvider.buildViewerRoute(id: Long): String {
val list = data?.values?.flatten()?.map { it.id } ?: emptyList()
return RouteViewer(list.indexOf(id), list)
}


interface TimelineViewState : FileActions, SelectionTracker, DataProvider {

Expand Down
6 changes: 2 additions & 4 deletions app/src/main/java/com/zs/gallery/impl/FolderViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
package com.zs.gallery.impl

import android.text.format.DateUtils
import android.text.format.Formatter
import android.util.Log
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
Expand All @@ -28,10 +27,9 @@ import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.unit.sp
import androidx.lifecycle.SavedStateHandle
import com.primex.core.withSpanStyle
import com.zs.api.store.MediaFile
import com.zs.api.store.MediaProvider
import com.zs.api.util.PathUtils
import com.zs.gallery.R
import com.zs.gallery.common.get
import com.zs.gallery.files.FolderViewState
import com.zs.gallery.files.RouteFolder
import kotlinx.coroutines.delay
Expand All @@ -42,7 +40,7 @@ class FolderViewModel(
handle: SavedStateHandle, provider: MediaProvider
) : TimelineViewModel(provider), FolderViewState {

val path = RouteFolder[handle]
val path = handle[RouteFolder]
override var title: CharSequence by mutableStateOf(PathUtils.name(path))

override suspend fun refresh() {
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/com/zs/gallery/impl/Initializer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,6 @@ class CrashlyticsInitializer : Initializer<Unit> {
}

override fun dependencies(): List<Class<out Initializer<*>>> {
return emptyList()
return listOf(FirebaseInitializer::class.java)
}
}
8 changes: 5 additions & 3 deletions app/src/main/java/com/zs/gallery/impl/MainViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,7 @@ abstract class MainViewModel<T>(
* - If `some` selected items are favorite, it `adds the remaining` unfavored items to favorites.
* - If `none` of the selected items are favorite, it `adds all` of them favorites.
*/
fun toggleLike() {
open fun toggleLike() {
viewModelScope.launch {
// Get the selected items and clear the selection.
val selected = consume().toList()
Expand Down Expand Up @@ -215,11 +215,13 @@ abstract class MainViewModel<T>(
}
}

abstract fun evaluateGroupSelectionLevel(key: String): GroupSelectionLevel
open fun evaluateGroupSelectionLevel(key: String): GroupSelectionLevel {
return GroupSelectionLevel.NONE
}

override fun isGroupSelected(key: String): State<GroupSelectionLevel> =
derivedStateOf { evaluateGroupSelectionLevel(key) }


@SuppressLint("NewApi")
override fun trash(activity: Activity) {
viewModelScope.launch {
Expand Down
97 changes: 67 additions & 30 deletions app/src/main/java/com/zs/gallery/impl/ViewerViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -18,63 +18,100 @@

package com.zs.gallery.impl

import android.annotation.SuppressLint
import android.app.Activity
import android.net.Uri
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableLongStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.viewModelScope
import com.zs.api.store.MediaFile
import com.zs.api.store.MediaProvider
import com.zs.gallery.common.get
import com.zs.gallery.preview.RouteViewer
import com.zs.gallery.preview.ViewerArgs
import com.zs.gallery.preview.ViewerViewState
import com.zs.gallery.settings.Settings
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach


class ViewerViewModel(
handle: SavedStateHandle,
private val provider: MediaProvider
): KoinViewModel(), ViewerViewState {

val args = RouteViewer[handle]

override var index by mutableIntStateOf(args.index)

override fun fetchUriForIndex(): Uri = MediaProvider.contentUri(args.ids[index])
provider: MediaProvider
) : MainViewModel<MediaFile>(provider), ViewerViewState {

val args = handle[RouteViewer]
override var focused by mutableLongStateOf(args.focused)
override val data: List<MediaFile> get() = values
override var values: List<MediaFile> by mutableStateOf(emptyList())
override val favourite: Boolean by derivedStateOf {
favorites.contains(focused)
}

override val size: Int get() = args.ids.size
override val MediaFile.id: Long get() = id

override fun fetchIdForIndex(): Long {
return args.ids[index]
override fun restore(activity: Activity) {
select(focused)
super.restore(activity)
}

override fun delete(activity: Activity) {
TODO("Not yet implemented")
select(focused)
super.delete(activity)
}

override fun remove(activity: Activity) {
TODO("Not yet implemented")
select(focused)
super.remove(activity)
}

override fun trash(activity: Activity) {
TODO("Not yet implemented")
}

override fun move(dest: String) {
TODO("Not yet implemented")
}

override fun copy(dest: String) {
TODO("Not yet implemented")
override fun share(activity: Activity) {
select(focused)
super.share(activity)
}

override fun rename(name: String) {
TODO("Not yet implemented")
override fun toggleLike() {
select(focused)
super.toggleLike()
}

override fun share(activity: Activity) {
TODO("Not yet implemented")
@SuppressLint("NewApi")
override suspend fun refresh() {
delay(10)
val order = MediaProvider.COLUMN_DATE_MODIFIED
val ascending = false
values = when (args) {
is ViewerArgs.Folder -> provider.fetchFilesFromDirectory(
path = args.path,
order = order,
ascending = ascending
)

is ViewerArgs.Timeline -> provider.fetchFiles(
order = order,
ascending = ascending
)

is ViewerArgs.Album -> {
// currently the album is always favorite
provider.fetchFiles(
*favorites.toLongArray(),
order = order,
ascending = ascending
)
}

else -> throw UnsupportedOperationException("The Operation is not supported! $args")
}
}

override fun restore(activity: Activity) {
TODO("Not yet implemented")
init {
if (args is ViewerArgs.Album)
preferences[Settings.KEY_FAVOURITE_FILES].onEach { refresh() }.launchIn(viewModelScope)
}
}
Loading

0 comments on commit c34ee5d

Please sign in to comment.