From f573ec743488c0f4e88e26cc025dc573462f35b9 Mon Sep 17 00:00:00 2001 From: Isira Seneviratne Date: Sun, 19 Feb 2023 18:42:42 +0530 Subject: [PATCH] refactor: Use Navigation Safe Args --- app/build.gradle.kts | 1 + .../libretube/helpers/NavigationHelper.kt | 10 ++--- .../libretube/ui/activities/MainActivity.kt | 23 +++-------- .../libretube/ui/fragments/ChannelFragment.kt | 14 +++---- .../libretube/ui/fragments/PlayerFragment.kt | 12 +++--- .../ui/fragments/PlaylistFragment.kt | 34 ++++++++-------- .../ui/fragments/SearchResultFragment.kt | 21 ++++------ app/src/main/res/navigation/nav.xml | 39 +++++++++++++++++-- build.gradle.kts | 1 + gradle/libs.versions.toml | 1 + 10 files changed, 84 insertions(+), 72 deletions(-) diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 25c21f2e0..1f981103c 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -4,6 +4,7 @@ plugins { id("kotlin-android") id("kotlinx-serialization") id("kotlin-parcelize") + id("androidx.navigation.safeargs.kotlin") } android { diff --git a/app/src/main/java/com/github/libretube/helpers/NavigationHelper.kt b/app/src/main/java/com/github/libretube/helpers/NavigationHelper.kt index a89892520..4dad886f2 100644 --- a/app/src/main/java/com/github/libretube/helpers/NavigationHelper.kt +++ b/app/src/main/java/com/github/libretube/helpers/NavigationHelper.kt @@ -11,6 +11,7 @@ import androidx.core.os.bundleOf import androidx.core.os.postDelayed import androidx.fragment.app.commitNow import androidx.fragment.app.replace +import com.github.libretube.NavDirections import com.github.libretube.R import com.github.libretube.constants.IntentData import com.github.libretube.constants.PreferenceKeys @@ -29,8 +30,7 @@ object NavigationHelper { if (channelId == null) return val activity = ContextHelper.unwrapActivity(context) - val bundle = bundleOf(IntentData.channelId to channelId) - activity.navController.navigate(R.id.channelFragment, bundle) + activity.navController.navigate(NavDirections.openChannel(channelId)) try { if (activity.binding.mainMotionLayout.progress == 0.toFloat()) { activity.binding.mainMotionLayout.transitionToEnd() @@ -86,11 +86,7 @@ object NavigationHelper { if (playlistId == null) return val activity = ContextHelper.unwrapActivity(context) - val bundle = bundleOf( - IntentData.playlistId to playlistId, - IntentData.playlistType to playlistType - ) - activity.navController.navigate(R.id.playlistFragment, bundle) + activity.navController.navigate(NavDirections.openPlaylist(playlistId, playlistType)) } /** diff --git a/app/src/main/java/com/github/libretube/ui/activities/MainActivity.kt b/app/src/main/java/com/github/libretube/ui/activities/MainActivity.kt index 3954c116b..4cb74fda1 100644 --- a/app/src/main/java/com/github/libretube/ui/activities/MainActivity.kt +++ b/app/src/main/java/com/github/libretube/ui/activities/MainActivity.kt @@ -22,6 +22,7 @@ import androidx.navigation.findNavController import androidx.navigation.fragment.NavHostFragment import androidx.navigation.ui.setupWithNavController import androidx.recyclerview.widget.RecyclerView +import com.github.libretube.NavDirections import com.github.libretube.R import com.github.libretube.compat.PictureInPictureCompat import com.github.libretube.constants.IntentData @@ -269,11 +270,8 @@ class MainActivity : BaseActivity() { searchView = searchItem.actionView as SearchView searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { - override fun onQueryTextSubmit(query: String?): Boolean { - navController.navigate( - R.id.searchResultFragment, - bundleOf(IntentData.query to query) - ) + override fun onQueryTextSubmit(query: String): Boolean { + navController.navigate(NavDirections.showSearchResults(query)) searchView.clearFocus() return true } @@ -406,22 +404,13 @@ class MainActivity : BaseActivity() { } intent?.getStringExtra(IntentData.channelId)?.let { - navController.navigate( - R.id.channelFragment, - bundleOf(IntentData.channelId to it) - ) + navController.navigate(NavDirections.openChannel(channelId = it)) } intent?.getStringExtra(IntentData.channelName)?.let { - navController.navigate( - R.id.channelFragment, - bundleOf(IntentData.channelName to it) - ) + navController.navigate(NavDirections.openChannel(channelName = it)) } intent?.getStringExtra(IntentData.playlistId)?.let { - navController.navigate( - R.id.playlistFragment, - bundleOf(IntentData.playlistId to it) - ) + navController.navigate(NavDirections.openPlaylist(playlistId = it)) } intent?.getStringExtra(IntentData.videoId)?.let { NavigationHelper.navigateVideo( diff --git a/app/src/main/java/com/github/libretube/ui/fragments/ChannelFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/ChannelFragment.kt index 020ddd5ab..df909010d 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/ChannelFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/ChannelFragment.kt @@ -10,6 +10,7 @@ import androidx.core.view.children import androidx.core.view.isGone import androidx.core.view.isVisible import androidx.lifecycle.lifecycleScope +import androidx.navigation.fragment.navArgs import androidx.recyclerview.widget.GridLayoutManager import com.github.libretube.R import com.github.libretube.api.RetrofitInstance @@ -32,16 +33,17 @@ import com.github.libretube.ui.dialogs.ShareDialog import com.github.libretube.ui.extensions.setupSubscriptionButton import com.github.libretube.ui.sheets.AddChannelToGroupSheet import com.github.libretube.util.deArrow -import java.io.IOException -import kotlin.math.ceil import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import retrofit2.HttpException +import java.io.IOException +import kotlin.math.ceil class ChannelFragment : DynamicLayoutManagerFragment() { private var _binding: FragmentChannelBinding? = null private val binding get() = _binding!! + private val args by navArgs() private var channelId: String? = null private var channelName: String? = null @@ -61,12 +63,8 @@ class ChannelFragment : DynamicLayoutManagerFragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - arguments?.let { - channelId = it.getString(IntentData.channelId)?.toID() - channelName = it.getString(IntentData.channelName) - ?.replace("/c/", "") - ?.replace("/user/", "") - } + channelId = args.channelId?.toID() + channelName = args.channelName?.replace("/c/", "")?.replace("/user/", "") } override fun onCreateView( diff --git a/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt index e4ae01cce..438b0956f 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/PlayerFragment.kt @@ -47,6 +47,7 @@ import androidx.media3.exoplayer.ExoPlayer import androidx.media3.exoplayer.hls.HlsMediaSource import androidx.media3.exoplayer.trackselection.DefaultTrackSelector import androidx.recyclerview.widget.LinearLayoutManager +import com.github.libretube.NavDirections import com.github.libretube.R import com.github.libretube.api.CronetHelper import com.github.libretube.api.JsonHelper @@ -114,16 +115,16 @@ import com.github.libretube.util.TextUtils import com.github.libretube.util.TextUtils.toTimeInSeconds import com.github.libretube.util.YoutubeHlsPlaylistParser import com.github.libretube.util.deArrow -import java.io.IOException -import java.util.* -import java.util.concurrent.Executors -import kotlin.math.abs import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import kotlinx.serialization.encodeToString import retrofit2.HttpException +import java.io.IOException +import java.util.* +import java.util.concurrent.Executors +import kotlin.math.abs @androidx.annotation.OptIn(androidx.media3.common.util.UnstableApi::class) class PlayerFragment : Fragment(), OnlinePlayerOptions { @@ -627,8 +628,7 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions { if (!this::streams.isInitialized) return@setOnClickListener val activity = view?.context as MainActivity - val bundle = bundleOf(IntentData.channelId to streams.uploaderUrl) - activity.navController.navigate(R.id.channelFragment, bundle) + activity.navController.navigate(NavDirections.openChannel(streams.uploaderUrl)) activity.binding.mainMotionLayout.transitionToEnd() binding.playerMotionLayout.transitionToEnd() } diff --git a/app/src/main/java/com/github/libretube/ui/fragments/PlaylistFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/PlaylistFragment.kt index 472364d70..cffaafa04 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/PlaylistFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/PlaylistFragment.kt @@ -13,9 +13,11 @@ import androidx.core.view.isVisible import androidx.core.view.updatePadding import androidx.fragment.app.activityViewModels import androidx.lifecycle.lifecycleScope +import androidx.navigation.fragment.navArgs import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.RecyclerView +import com.github.libretube.NavDirections import com.github.libretube.R import com.github.libretube.api.PlaylistsHelper import com.github.libretube.api.RetrofitInstance @@ -29,7 +31,6 @@ import com.github.libretube.enums.PlaylistType import com.github.libretube.extensions.TAG import com.github.libretube.extensions.ceilHalf import com.github.libretube.extensions.dpToPx -import com.github.libretube.extensions.serializable import com.github.libretube.extensions.toID import com.github.libretube.extensions.toastFromMainDispatcher import com.github.libretube.helpers.ImageHelper @@ -51,9 +52,10 @@ import kotlinx.coroutines.withContext class PlaylistFragment : DynamicLayoutManagerFragment() { private var _binding: FragmentPlaylistBinding? = null private val binding get() = _binding!! + private val args by navArgs() // general playlist information - private var playlistId: String? = null + private lateinit var playlistId: String private var playlistName: String? = null private var playlistType = PlaylistType.PUBLIC @@ -75,10 +77,8 @@ class PlaylistFragment : DynamicLayoutManagerFragment() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - arguments?.let { - playlistId = it.getString(IntentData.playlistId)!!.toID() - playlistType = it.serializable(IntentData.playlistType) ?: PlaylistType.PUBLIC - } + playlistId = args.playlistId + playlistType = args.playlistType } override fun onCreateView( @@ -100,7 +100,7 @@ class PlaylistFragment : DynamicLayoutManagerFragment() { binding.playlistProgress.isVisible = true isBookmarked = runBlocking(Dispatchers.IO) { - DatabaseHolder.Database.playlistBookmarkDao().includes(playlistId!!) + DatabaseHolder.Database.playlistBookmarkDao().includes(playlistId) } updateBookmarkRes() @@ -127,7 +127,7 @@ class PlaylistFragment : DynamicLayoutManagerFragment() { lifecycleScope.launch { val response = try { withContext(Dispatchers.IO) { - PlaylistsHelper.getPlaylist(playlistId!!) + PlaylistsHelper.getPlaylist(playlistId) } } catch (e: Exception) { Log.e(TAG(), e.toString()) @@ -146,10 +146,8 @@ class PlaylistFragment : DynamicLayoutManagerFragment() { binding.playlistInfo.text = getChannelAndVideoString(response, response.videos) binding.playlistInfo.setOnClickListener { - (context as MainActivity).navController.navigate( - R.id.channelFragment, - bundleOf(IntentData.channelId to response.uploaderUrl?.toID()) - ) + (context as MainActivity).navController + .navigate(NavDirections.openChannel(response.uploaderUrl)) } binding.playlistDescription.text = response.description?.parseAsHtml() @@ -162,7 +160,7 @@ class PlaylistFragment : DynamicLayoutManagerFragment() { binding.optionsMenu.setOnClickListener { val sheet = PlaylistOptionsBottomSheet() sheet.arguments = bundleOf( - IntentData.playlistId to playlistId.orEmpty(), + IntentData.playlistId to playlistId, IntentData.playlistName to playlistName.orEmpty(), IntentData.playlistType to playlistType ) @@ -214,10 +212,10 @@ class PlaylistFragment : DynamicLayoutManagerFragment() { lifecycleScope.launch(Dispatchers.IO) { if (!isBookmarked) { DatabaseHolder.Database.playlistBookmarkDao() - .deleteById(playlistId!!) + .deleteById(playlistId) } else { DatabaseHolder.Database.playlistBookmarkDao() - .insert(response.toPlaylistBookmark(playlistId!!)) + .insert(response.toPlaylistBookmark(playlistId)) } } } @@ -295,7 +293,7 @@ class PlaylistFragment : DynamicLayoutManagerFragment() { playlistAdapter = PlaylistAdapter( playlistFeed, videos.toMutableList(), - playlistId!!, + playlistId, playlistType ) binding.playlistRecView.adapter = playlistAdapter @@ -373,9 +371,9 @@ class PlaylistFragment : DynamicLayoutManagerFragment() { withContext(Dispatchers.IO) { // load locally stored playlists with the auth api if (playlistType == PlaylistType.PRIVATE) { - RetrofitInstance.authApi.getPlaylistNextPage(playlistId!!, nextPage!!) + RetrofitInstance.authApi.getPlaylistNextPage(playlistId, nextPage!!) } else { - RetrofitInstance.api.getPlaylistNextPage(playlistId!!, nextPage!!) + RetrofitInstance.api.getPlaylistNextPage(playlistId, nextPage!!) } } } catch (e: Exception) { diff --git a/app/src/main/java/com/github/libretube/ui/fragments/SearchResultFragment.kt b/app/src/main/java/com/github/libretube/ui/fragments/SearchResultFragment.kt index eb79575f1..c11b4e043 100644 --- a/app/src/main/java/com/github/libretube/ui/fragments/SearchResultFragment.kt +++ b/app/src/main/java/com/github/libretube/ui/fragments/SearchResultFragment.kt @@ -8,10 +8,10 @@ import android.view.ViewGroup import androidx.core.view.isGone import androidx.core.view.isVisible import androidx.lifecycle.lifecycleScope +import androidx.navigation.fragment.navArgs import androidx.recyclerview.widget.GridLayoutManager import com.github.libretube.R import com.github.libretube.api.RetrofitInstance -import com.github.libretube.constants.IntentData import com.github.libretube.constants.PreferenceKeys import com.github.libretube.databinding.FragmentSearchResultBinding import com.github.libretube.db.DatabaseHelper @@ -36,18 +36,13 @@ import okhttp3.HttpUrl.Companion.toHttpUrlOrNull class SearchResultFragment : DynamicLayoutManagerFragment() { private var _binding: FragmentSearchResultBinding? = null private val binding get() = _binding!! + private val args by navArgs() private var nextPage: String? = null - private var query = "" private lateinit var searchAdapter: SearchAdapter private var searchFilter = "all" - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - query = arguments?.getString(IntentData.query).toString() - } - override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, @@ -66,10 +61,10 @@ class SearchResultFragment : DynamicLayoutManagerFragment() { // fixes a bug that the search query will stay the old one when searching for multiple // different queries in a row and navigating to the previous ones through back presses - (context as MainActivity).setQuerySilent(query) + (context as MainActivity).setQuerySilent(args.query) // add the query to the history - addToHistory(query) + addToHistory(args.query) // filter options binding.filterChipGroup.setOnCheckedStateChangeListener { _, _ -> @@ -109,11 +104,11 @@ class SearchResultFragment : DynamicLayoutManagerFragment() { var timeStamp: Long? = null // parse search URLs from YouTube entered in the search bar - val searchQuery = query.toHttpUrlOrNull()?.let { - val videoId = TextUtils.getVideoIdFromUrl(it.toString()) ?: query + val searchQuery = args.query.toHttpUrlOrNull()?.let { + val videoId = TextUtils.getVideoIdFromUrl(it.toString()) ?: args.query timeStamp = it.queryParameter("t")?.toTimeInSeconds() "${ShareDialog.YOUTUBE_FRONTEND_URL}/watch?v=$videoId" - } ?: query + } ?: args.query view?.let { context?.hideKeyboard(it) } val response = try { @@ -146,7 +141,7 @@ class SearchResultFragment : DynamicLayoutManagerFragment() { val response = try { withContext(Dispatchers.IO) { RetrofitInstance.api.getSearchResultsNextPage( - query, + args.query, searchFilter, nextPage!! ).apply { diff --git a/app/src/main/res/navigation/nav.xml b/app/src/main/res/navigation/nav.xml index d0748b9b7..9ec3352d5 100644 --- a/app/src/main/res/navigation/nav.xml +++ b/app/src/main/res/navigation/nav.xml @@ -33,17 +33,43 @@ android:id="@+id/searchResultFragment" android:name="com.github.libretube.ui.fragments.SearchResultFragment" android:label="fragment_search" - tools:layout="@layout/fragment_search_result" /> + tools:layout="@layout/fragment_search_result"> + + + tools:layout="@layout/fragment_channel" > + + + + tools:layout="@layout/fragment_playlist"> + + + + + + + \ No newline at end of file diff --git a/build.gradle.kts b/build.gradle.kts index a19e3d429..2f2a0e04e 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -9,6 +9,7 @@ buildscript { classpath(libs.gradle) classpath(libs.kotlin.gradle.plugin) classpath(libs.kotlin.serialization) + classpath(libs.androidx.navigation.safeargs) // NOTE: Do not place your application dependencies here, they belong // in the individual module build.gradle.kts files diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 853a5667e..75afde213 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -38,6 +38,7 @@ kotlin-serialization = { module = "org.jetbrains.kotlin:kotlin-serialization", v material = { group = "com.google.android.material", name = "material", version.ref = "material" } androidx-navigation-fragment = { group = "androidx.navigation", name = "navigation-fragment-ktx", version.ref = "navigation" } androidx-navigation-ui = { group = "androidx.navigation", name = "navigation-ui-ktx", version.ref = "navigation" } +androidx-navigation-safeargs = { group = "androidx.navigation", name = "navigation-safe-args-gradle-plugin", version.ref = "navigation" } androidx-legacySupport = { group = "androidx.legacy", name = "legacy-support-v4", version.ref = "legacySupport" } androidx-preference = { group = "androidx.preference", name = "preference-ktx", version.ref = "preference" } androidx-test-junit = { group = "androidx.test.ext", name = "junit", version.ref = "extJunit" }