Skip to content

Commit

Permalink
[FEAT] Added logic to show What's new.
Browse files Browse the repository at this point in the history
[FEAT] Replaced layouts in NavigationSuiteScaffold with measurePolicies.
- This will ensure the state of expendables, etc., will be preserved during orientation changes.

[FEAT] Enhanced Toast with new features.
- Toast now handles the back-button.
- Supports expanded state; click to expand.
- Supports swipe to dismiss.
- Fixed toast now using mutex.
- Renders at the top of everything during shared transition.

[FEAT] Introduced showPlatformToast in context.
- Now everything depends on the Context.showPlatformToast for showing Android toasts.
- It also supports `Duration`.
  • Loading branch information
iZakirSheikh committed Sep 16, 2024
1 parent 1652713 commit ff38b24
Show file tree
Hide file tree
Showing 10 changed files with 389 additions and 225 deletions.
4 changes: 2 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 = 24
targetSdk = 35
versionCode = 20
versionName = "0.1.0-dev2"
versionCode = 21
versionName = "0.1.0-dev21"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
Expand Down
70 changes: 34 additions & 36 deletions app/src/main/java/com/zs/gallery/Gallery.kt
Original file line number Diff line number Diff line change
Expand Up @@ -337,44 +337,42 @@ private fun NavigationBar(
navController: NavController,
modifier: Modifier = Modifier,
) {
val routes = remember {
movableContentOf {
// Get the current navigation destination from NavController
val current by navController.currentBackStackEntryAsState()
val colors = NavigationItemDefaults.navigationItemColors()
val domain = current?.destination?.domain
val facade = LocalSystemFacade.current

// Timeline
NavItem(
label = { Label(text = textResource(R.string.photos)) },
icon = { Icon(imageVector = Icons.Filled.PhotoLibrary, contentDescription = null) },
checked = domain == RouteTimeline.domain,
onClick = { facade.launchReviewFlow(); navController.toRoute(RouteTimeline) },
typeRail = typeRail,
colors = colors
)
val routes = @Composable {
// Get the current navigation destination from NavController
val current by navController.currentBackStackEntryAsState()
val colors = NavigationItemDefaults.navigationItemColors()
val domain = current?.destination?.domain
val facade = LocalSystemFacade.current

// Timeline
NavItem(
label = { Label(text = textResource(R.string.photos)) },
icon = { Icon(imageVector = Icons.Filled.PhotoLibrary, contentDescription = null) },
checked = domain == RouteTimeline.domain,
onClick = { facade.launchReviewFlow(); navController.toRoute(RouteTimeline) },
typeRail = typeRail,
colors = colors
)

// Folders
NavItem(
label = { Label(text = textResource(R.string.folders)) },
icon = { Icon(imageVector = Icons.Filled.FolderCopy, contentDescription = null) },
checked = domain == RouteFolders.domain,
onClick = { facade.launchReviewFlow(); navController.toRoute(RouteFolders) },
typeRail = typeRail,
colors = colors
)
// Folders
NavItem(
label = { Label(text = textResource(R.string.folders)) },
icon = { Icon(imageVector = Icons.Filled.FolderCopy, contentDescription = null) },
checked = domain == RouteFolders.domain,
onClick = { facade.launchReviewFlow(); navController.toRoute(RouteFolders) },
typeRail = typeRail,
colors = colors
)

// Settings
NavItem(
label = { Label(text = textResource(R.string.settings)) },
icon = { Icon(imageVector = Icons.Filled.Settings, contentDescription = null) },
checked = domain == RouteSettings.domain,
onClick = { facade.launchReviewFlow(); navController.toRoute(RouteSettings) },
typeRail = typeRail,
colors = colors
)
}
// Settings
NavItem(
label = { Label(text = textResource(R.string.settings)) },
icon = { Icon(imageVector = Icons.Filled.Settings, contentDescription = null) },
checked = domain == RouteSettings.domain,
onClick = { facade.launchReviewFlow(); navController.toRoute(RouteSettings) },
typeRail = typeRail,
colors = colors
)
}

// Get the current theme colors
Expand Down
30 changes: 21 additions & 9 deletions app/src/main/java/com/zs/gallery/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@ import android.content.res.Configuration
import android.hardware.biometrics.BiometricManager.Authenticators.BIOMETRIC_STRONG
import android.hardware.biometrics.BiometricManager.Authenticators.DEVICE_CREDENTIAL
import android.hardware.biometrics.BiometricPrompt
import android.hardware.biometrics.BiometricPrompt.AuthenticationCallback
import android.os.Build
import android.os.Bundle
import android.os.CancellationSignal
Expand All @@ -35,6 +34,7 @@ import androidx.activity.compose.setContent
import androidx.annotation.RequiresApi
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Downloading
import androidx.compose.material.icons.outlined.NewReleases
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.NonRestartableComposable
Expand Down Expand Up @@ -69,6 +69,7 @@ import com.primex.core.getText2
import com.primex.core.runCatching
import com.primex.preferences.Key
import com.primex.preferences.Preferences
import com.primex.preferences.intPreferenceKey
import com.primex.preferences.longPreferenceKey
import com.primex.preferences.observeAsState
import com.primex.preferences.value
Expand All @@ -81,7 +82,6 @@ import com.zs.gallery.common.getPackageInfoCompat
import com.zs.gallery.files.RouteTimeline
import com.zs.gallery.lockscreen.RouteLockScreen
import com.zs.gallery.settings.Settings
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.launchIn
Expand All @@ -90,7 +90,7 @@ import kotlinx.coroutines.launch
import org.koin.android.ext.android.inject
import kotlin.time.Duration.Companion.days
import kotlin.time.Duration.Companion.minutes
import android.widget.Toast as PlatformToast
import com.zs.foundation.showPlatformToast as showAndroidToast

private const val TAG = "MainActivity"

Expand All @@ -114,6 +114,10 @@ private val STANDARD_REVIEW_DELAY = 5.days
private val KEY_LAST_REVIEW_TIME =
longPreferenceKey(TAG + "_last_review_time", 0)

private val KEY_APP_VERSION_CODE =
intPreferenceKey(TAG + "_app_version_code", -1)


/**
* @property inAppUpdateProgress A simple property that represents the progress of the in-app update.
* The progress value is a float between 0.0 and 1.0, indicating the percentage of the
Expand Down Expand Up @@ -200,12 +204,6 @@ class MainActivity : ComponentActivity(), SystemFacade, NavController.OnDestinat
}
}

override fun showPlatformToast(string: Int) =
PlatformToast.makeText(this, getString(string), PlatformToast.LENGTH_SHORT).show()

override fun showPlatformToast(string: String) =
PlatformToast.makeText(this, string, PlatformToast.LENGTH_SHORT).show()

@RequiresApi(Build.VERSION_CODES.P)
override fun authenticate(subtitle: String?, desc: String?, onAuthenticated: () -> Unit) {
Log.d(TAG, "preparing to show authentication dialog.")
Expand Down Expand Up @@ -257,6 +255,12 @@ class MainActivity : ComponentActivity(), SystemFacade, NavController.OnDestinat
)
}

override fun showPlatformToast(message: String, duration: Int) =
showAndroidToast(message, duration)

override fun showPlatformToast(message: Int, duration: Int) =
showAndroidToast(message, duration)

override fun <T> getDeviceService(name: String): T =
getSystemService(name) as T

Expand Down Expand Up @@ -391,6 +395,14 @@ class MainActivity : ComponentActivity(), SystemFacade, NavController.OnDestinat
flow1.combine(flow2) { _, _ -> enableEdgeToEdge() }
.launchIn(scope = lifecycleScope)
lifecycleScope.launch { launchUpdateFlow() }

// show what's new message on click.
val versionCode = BuildConfig.VERSION_CODE
val savedVersionCode = preferences.value(KEY_APP_VERSION_CODE)
if (savedVersionCode != versionCode){
preferences[KEY_APP_VERSION_CODE] = versionCode
showToast(R.string.what_s_new_latest, duration = Toast.DURATION_INDEFINITE)
}
}

override fun launchUpdateFlow(report: Boolean) {
Expand Down
16 changes: 4 additions & 12 deletions app/src/main/java/com/zs/gallery/common/SystemFacade.kt
Original file line number Diff line number Diff line change
Expand Up @@ -205,22 +205,14 @@ interface SystemFacade {
}

/**
* Displays a toast message using the platform's default toast mechanism.
*
* This function shows a short toast message with the provided text.
*
* @param string The text message to display in the toast.
* @see com.zs.foundation.showPlatformToast
*/
fun showPlatformToast(string: String)
fun showPlatformToast(message: String, @Duration duration: Int = Toast.DURATION_SHORT)

/**
* Displays a toast message using the platform's default toast mechanism.
*
* This function shows a short toast message with the text from the specified string resource.
*
* @param string The string resource ID of the text message to display in the toast.
* @see com.zs.foundation.showPlatformToast
*/
fun showPlatformToast(@StringRes string: Int)
fun showPlatformToast(@StringRes message: Int, @Duration duration: Int = Toast.DURATION_SHORT)

/**
* Returns the handle to a system-level service by name.
Expand Down
12 changes: 7 additions & 5 deletions app/src/main/java/com/zs/gallery/impl/KoinViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ import com.zs.foundation.toast.ToastHostState
import org.koin.androidx.scope.ScopeViewModel
import org.koin.core.annotation.KoinExperimentalAPI
import org.koin.core.component.inject
import android.widget.Toast as AndroidToast
import com.zs.foundation.showPlatformToast as showAndroidToast

private const val TAG = "KoinViewModel"

Expand All @@ -47,12 +47,14 @@ abstract class KoinViewModel: ScopeViewModel() {
private val context: Application by inject()

fun showPlatformToast(
@StringRes message: Int
) = AndroidToast.makeText(context, message, AndroidToast.LENGTH_LONG).show()
@StringRes message: Int,
@Duration duration: Int = Toast.DURATION_SHORT
) = context.showAndroidToast(message, duration)

fun showPlatformToast(
message: String
) = AndroidToast.makeText(context, message, AndroidToast.LENGTH_LONG).show()
message: String,
@Duration duration: Int = Toast.DURATION_SHORT
) = context.showAndroidToast(message, duration)

suspend fun showToast(
message: CharSequence,
Expand Down
35 changes: 35 additions & 0 deletions app/src/main/res/values/what_s_new.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>

<resources>
<string name="what_s_new_latest" translatable="false">
<b>What\'s new</b>
<font color='grey'>
\n🐞 Bug fixes and performance enhancements
\n✨ Enhanced Toast/Snackbar: Now supports swipe to dismiss and expandability
\n<b>In Focus</b>
\n1. 🎥 In-App Media Player.
\n2. Support for various file types: GIF, SVG, etc.
\n3. General performance improvements
</font>
</string>

<!--20_dev20-->
<string name="what_s_new_20_dev20">
<b>0.1.0-dev20</b>
<font color='grey'>
\n🌟 Thanks for using our app! ❤️😍
\n🌟 Biometric Authentication: Supports Android 9+.
\n🌟 UI Updates: We\'ve made improvements to the design and navigation.
\n🌟 Introduced Favourites and Recycle Bin in Folders
\n🌟 Bug Fixes: We\'ve resolved issues with loading and app lock.
\n🌟 Some minor bug fixes
</font>
</string>

<!---->
<string-array name="what_s_new">
<!--first is always latest-->
<item>@string/what_s_new_latest</item>
<item>@string/what_s_new_20_dev20</item>
</string-array>
</resources>
42 changes: 38 additions & 4 deletions foundation/src/main/java/com/zs/foundation/Utils.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,12 @@ package com.zs.foundation

import android.content.Context
import android.content.pm.PackageManager
import androidx.annotation.StringRes
import androidx.compose.ui.Modifier
import androidx.core.content.ContextCompat
import kotlin.contracts.ExperimentalContracts
import kotlin.contracts.InvocationKind
import kotlin.contracts.contract
import com.zs.foundation.toast.Duration
import com.zs.foundation.toast.Toast
import android.widget.Toast as AndroidWidgetToast

/**
* Checks if a given permission is granted for the application in the current context.
Expand Down Expand Up @@ -49,4 +50,37 @@ inline fun Modifier.thenIf(condition: Boolean, crossinline value: Modifier.() ->
returns() implies (condition)
}
return if (condition) this then Modifier.value() else this
}*/
}*/

/**
* Shows a platform Toast message with the given text.
*
* This function uses the standard Android Toast class to display a short message to the user.
*
* @param message The text message to display in the Toast.
* @param duration The duration of the Toast. Must be either [Toast.DURATION_SHORT] or [Toast.DURATION_LONG].
*/
fun Context.showPlatformToast(message: String, @Duration duration: Int = Toast.DURATION_SHORT) {
// Ensure the duration is valid
require(duration == Toast.DURATION_SHORT || duration == Toast.DURATION_LONG) {
"Duration must be either Toast.DURATION_SHORT or Toast.DURATION_LONG"
}
// Create and show the Toast
val toastDuration = if (duration == Toast.DURATION_SHORT) AndroidWidgetToast.LENGTH_SHORT else AndroidWidgetToast.LENGTH_LONG
AndroidWidgetToast.makeText(this, message, toastDuration).show()
}

/**
* @see showPlatformToast
*/
fun Context.showPlatformToast(
@StringRes message: Int,
@Duration duration: Int = Toast.DURATION_SHORT
) {
require(duration == Toast.DURATION_SHORT || duration == Toast.DURATION_LONG) {
"Duration must be either Toast.DURATION_SHORT or Toast.DURATION_LONG"
}
// Create and show the Toast
val toastDuration = if (duration == Toast.DURATION_SHORT) AndroidWidgetToast.LENGTH_SHORT else AndroidWidgetToast.LENGTH_LONG
AndroidWidgetToast.makeText(this, message, toastDuration).show()
}
Loading

0 comments on commit ff38b24

Please sign in to comment.