Merge pull request #5380 from Isira-Seneviratne/Navigation_safe_args

refactor: Use Navigation Safe Args
This commit is contained in:
Isira Seneviratne 2023-12-29 06:47:50 +05:30 committed by GitHub
commit 37de6a7374
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 80 additions and 68 deletions

View File

@ -4,6 +4,7 @@ plugins {
id("kotlin-android") id("kotlin-android")
id("kotlinx-serialization") id("kotlinx-serialization")
id("kotlin-parcelize") id("kotlin-parcelize")
id("androidx.navigation.safeargs.kotlin")
} }
android { android {

View File

@ -11,6 +11,7 @@ import androidx.core.os.bundleOf
import androidx.core.os.postDelayed import androidx.core.os.postDelayed
import androidx.fragment.app.commitNow import androidx.fragment.app.commitNow
import androidx.fragment.app.replace import androidx.fragment.app.replace
import com.github.libretube.NavDirections
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.constants.IntentData import com.github.libretube.constants.IntentData
import com.github.libretube.constants.PreferenceKeys import com.github.libretube.constants.PreferenceKeys
@ -29,8 +30,7 @@ object NavigationHelper {
if (channelId == null) return if (channelId == null) return
val activity = ContextHelper.unwrapActivity(context) val activity = ContextHelper.unwrapActivity(context)
val bundle = bundleOf(IntentData.channelId to channelId) activity.navController.navigate(NavDirections.openChannel(channelId))
activity.navController.navigate(R.id.channelFragment, bundle)
try { try {
if (activity.binding.mainMotionLayout.progress == 0.toFloat()) { if (activity.binding.mainMotionLayout.progress == 0.toFloat()) {
activity.binding.mainMotionLayout.transitionToEnd() activity.binding.mainMotionLayout.transitionToEnd()
@ -86,11 +86,7 @@ object NavigationHelper {
if (playlistId == null) return if (playlistId == null) return
val activity = ContextHelper.unwrapActivity(context) val activity = ContextHelper.unwrapActivity(context)
val bundle = bundleOf( activity.navController.navigate(NavDirections.openPlaylist(playlistId, playlistType))
IntentData.playlistId to playlistId,
IntentData.playlistType to playlistType
)
activity.navController.navigate(R.id.playlistFragment, bundle)
} }
/** /**

View File

@ -22,6 +22,7 @@ import androidx.navigation.findNavController
import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.setupWithNavController import androidx.navigation.ui.setupWithNavController
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.NavDirections
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.compat.PictureInPictureCompat import com.github.libretube.compat.PictureInPictureCompat
import com.github.libretube.constants.IntentData import com.github.libretube.constants.IntentData
@ -269,11 +270,8 @@ class MainActivity : BaseActivity() {
searchView = searchItem.actionView as SearchView searchView = searchItem.actionView as SearchView
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean { override fun onQueryTextSubmit(query: String): Boolean {
navController.navigate( navController.navigate(NavDirections.showSearchResults(query))
R.id.searchResultFragment,
bundleOf(IntentData.query to query)
)
searchView.clearFocus() searchView.clearFocus()
return true return true
} }
@ -406,22 +404,13 @@ class MainActivity : BaseActivity() {
} }
intent?.getStringExtra(IntentData.channelId)?.let { intent?.getStringExtra(IntentData.channelId)?.let {
navController.navigate( navController.navigate(NavDirections.openChannel(channelId = it))
R.id.channelFragment,
bundleOf(IntentData.channelId to it)
)
} }
intent?.getStringExtra(IntentData.channelName)?.let { intent?.getStringExtra(IntentData.channelName)?.let {
navController.navigate( navController.navigate(NavDirections.openChannel(channelName = it))
R.id.channelFragment,
bundleOf(IntentData.channelName to it)
)
} }
intent?.getStringExtra(IntentData.playlistId)?.let { intent?.getStringExtra(IntentData.playlistId)?.let {
navController.navigate( navController.navigate(NavDirections.openPlaylist(playlistId = it))
R.id.playlistFragment,
bundleOf(IntentData.playlistId to it)
)
} }
intent?.getStringExtra(IntentData.videoId)?.let { intent?.getStringExtra(IntentData.videoId)?.let {
NavigationHelper.navigateVideo( NavigationHelper.navigateVideo(

View File

@ -10,6 +10,7 @@ import androidx.core.view.children
import androidx.core.view.isGone import androidx.core.view.isGone
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.api.RetrofitInstance 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.extensions.setupSubscriptionButton
import com.github.libretube.ui.sheets.AddChannelToGroupSheet import com.github.libretube.ui.sheets.AddChannelToGroupSheet
import com.github.libretube.util.deArrow import com.github.libretube.util.deArrow
import java.io.IOException
import kotlin.math.ceil
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import retrofit2.HttpException import retrofit2.HttpException
import java.io.IOException
import kotlin.math.ceil
class ChannelFragment : DynamicLayoutManagerFragment() { class ChannelFragment : DynamicLayoutManagerFragment() {
private var _binding: FragmentChannelBinding? = null private var _binding: FragmentChannelBinding? = null
private val binding get() = _binding!! private val binding get() = _binding!!
private val args by navArgs<ChannelFragmentArgs>()
private var channelId: String? = null private var channelId: String? = null
private var channelName: String? = null private var channelName: String? = null
@ -61,12 +63,8 @@ class ChannelFragment : DynamicLayoutManagerFragment() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
arguments?.let { channelId = args.channelId?.toID()
channelId = it.getString(IntentData.channelId)?.toID() channelName = args.channelName?.replace("/c/", "")?.replace("/user/", "")
channelName = it.getString(IntentData.channelName)
?.replace("/c/", "")
?.replace("/user/", "")
}
} }
override fun onCreateView( override fun onCreateView(

View File

@ -48,6 +48,7 @@ import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.exoplayer.hls.HlsMediaSource import androidx.media3.exoplayer.hls.HlsMediaSource
import androidx.media3.exoplayer.trackselection.DefaultTrackSelector import androidx.media3.exoplayer.trackselection.DefaultTrackSelector
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.github.libretube.NavDirections
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.api.CronetHelper import com.github.libretube.api.CronetHelper
import com.github.libretube.api.JsonHelper import com.github.libretube.api.JsonHelper
@ -644,8 +645,7 @@ class PlayerFragment : Fragment(), OnlinePlayerOptions {
if (!this::streams.isInitialized) return@setOnClickListener if (!this::streams.isInitialized) return@setOnClickListener
val activity = view?.context as MainActivity val activity = view?.context as MainActivity
val bundle = bundleOf(IntentData.channelId to streams.uploaderUrl) activity.navController.navigate(NavDirections.openChannel(streams.uploaderUrl))
activity.navController.navigate(R.id.channelFragment, bundle)
activity.binding.mainMotionLayout.transitionToEnd() activity.binding.mainMotionLayout.transitionToEnd()
binding.playerMotionLayout.transitionToEnd() binding.playerMotionLayout.transitionToEnd()
} }

View File

@ -13,9 +13,11 @@ import androidx.core.view.isVisible
import androidx.core.view.updatePadding import androidx.core.view.updatePadding
import androidx.fragment.app.activityViewModels import androidx.fragment.app.activityViewModels
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.NavDirections
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.api.PlaylistsHelper import com.github.libretube.api.PlaylistsHelper
import com.github.libretube.api.RetrofitInstance 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.TAG
import com.github.libretube.extensions.ceilHalf import com.github.libretube.extensions.ceilHalf
import com.github.libretube.extensions.dpToPx import com.github.libretube.extensions.dpToPx
import com.github.libretube.extensions.serializable
import com.github.libretube.extensions.toID import com.github.libretube.extensions.toID
import com.github.libretube.extensions.toastFromMainDispatcher import com.github.libretube.extensions.toastFromMainDispatcher
import com.github.libretube.helpers.ImageHelper import com.github.libretube.helpers.ImageHelper
@ -51,9 +52,10 @@ import kotlinx.coroutines.withContext
class PlaylistFragment : DynamicLayoutManagerFragment() { class PlaylistFragment : DynamicLayoutManagerFragment() {
private var _binding: FragmentPlaylistBinding? = null private var _binding: FragmentPlaylistBinding? = null
private val binding get() = _binding!! private val binding get() = _binding!!
private val args by navArgs<PlaylistFragmentArgs>()
// general playlist information // general playlist information
private var playlistId: String? = null private lateinit var playlistId: String
private var playlistName: String? = null private var playlistName: String? = null
private var playlistType = PlaylistType.PUBLIC private var playlistType = PlaylistType.PUBLIC
@ -75,10 +77,8 @@ class PlaylistFragment : DynamicLayoutManagerFragment() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
arguments?.let { playlistId = args.playlistId
playlistId = it.getString(IntentData.playlistId)!!.toID() playlistType = args.playlistType
playlistType = it.serializable(IntentData.playlistType) ?: PlaylistType.PUBLIC
}
} }
override fun onCreateView( override fun onCreateView(
@ -100,7 +100,7 @@ class PlaylistFragment : DynamicLayoutManagerFragment() {
binding.playlistProgress.isVisible = true binding.playlistProgress.isVisible = true
isBookmarked = runBlocking(Dispatchers.IO) { isBookmarked = runBlocking(Dispatchers.IO) {
DatabaseHolder.Database.playlistBookmarkDao().includes(playlistId!!) DatabaseHolder.Database.playlistBookmarkDao().includes(playlistId)
} }
updateBookmarkRes() updateBookmarkRes()
@ -127,7 +127,7 @@ class PlaylistFragment : DynamicLayoutManagerFragment() {
lifecycleScope.launch { lifecycleScope.launch {
val response = try { val response = try {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
PlaylistsHelper.getPlaylist(playlistId!!) PlaylistsHelper.getPlaylist(playlistId)
} }
} catch (e: Exception) { } catch (e: Exception) {
Log.e(TAG(), e.toString()) Log.e(TAG(), e.toString())
@ -146,10 +146,8 @@ class PlaylistFragment : DynamicLayoutManagerFragment() {
binding.playlistInfo.text = getChannelAndVideoString(response, response.videos) binding.playlistInfo.text = getChannelAndVideoString(response, response.videos)
binding.playlistInfo.setOnClickListener { binding.playlistInfo.setOnClickListener {
(context as MainActivity).navController.navigate( (context as MainActivity).navController
R.id.channelFragment, .navigate(NavDirections.openChannel(response.uploaderUrl))
bundleOf(IntentData.channelId to response.uploaderUrl?.toID())
)
} }
binding.playlistDescription.text = response.description?.parseAsHtml() binding.playlistDescription.text = response.description?.parseAsHtml()
@ -162,7 +160,7 @@ class PlaylistFragment : DynamicLayoutManagerFragment() {
binding.optionsMenu.setOnClickListener { binding.optionsMenu.setOnClickListener {
val sheet = PlaylistOptionsBottomSheet() val sheet = PlaylistOptionsBottomSheet()
sheet.arguments = bundleOf( sheet.arguments = bundleOf(
IntentData.playlistId to playlistId.orEmpty(), IntentData.playlistId to playlistId,
IntentData.playlistName to playlistName.orEmpty(), IntentData.playlistName to playlistName.orEmpty(),
IntentData.playlistType to playlistType IntentData.playlistType to playlistType
) )
@ -214,10 +212,10 @@ class PlaylistFragment : DynamicLayoutManagerFragment() {
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
if (!isBookmarked) { if (!isBookmarked) {
DatabaseHolder.Database.playlistBookmarkDao() DatabaseHolder.Database.playlistBookmarkDao()
.deleteById(playlistId!!) .deleteById(playlistId)
} else { } else {
DatabaseHolder.Database.playlistBookmarkDao() DatabaseHolder.Database.playlistBookmarkDao()
.insert(response.toPlaylistBookmark(playlistId!!)) .insert(response.toPlaylistBookmark(playlistId))
} }
} }
} }
@ -295,7 +293,7 @@ class PlaylistFragment : DynamicLayoutManagerFragment() {
playlistAdapter = PlaylistAdapter( playlistAdapter = PlaylistAdapter(
playlistFeed, playlistFeed,
videos.toMutableList(), videos.toMutableList(),
playlistId!!, playlistId,
playlistType playlistType
) )
binding.playlistRecView.adapter = playlistAdapter binding.playlistRecView.adapter = playlistAdapter
@ -373,9 +371,9 @@ class PlaylistFragment : DynamicLayoutManagerFragment() {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
// load locally stored playlists with the auth api // load locally stored playlists with the auth api
if (playlistType == PlaylistType.PRIVATE) { if (playlistType == PlaylistType.PRIVATE) {
RetrofitInstance.authApi.getPlaylistNextPage(playlistId!!, nextPage!!) RetrofitInstance.authApi.getPlaylistNextPage(playlistId, nextPage!!)
} else { } else {
RetrofitInstance.api.getPlaylistNextPage(playlistId!!, nextPage!!) RetrofitInstance.api.getPlaylistNextPage(playlistId, nextPage!!)
} }
} }
} catch (e: Exception) { } catch (e: Exception) {

View File

@ -9,10 +9,10 @@ import androidx.core.view.SoftwareKeyboardControllerCompat
import androidx.core.view.isGone import androidx.core.view.isGone
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.navArgs
import androidx.recyclerview.widget.GridLayoutManager import androidx.recyclerview.widget.GridLayoutManager
import com.github.libretube.R import com.github.libretube.R
import com.github.libretube.api.RetrofitInstance import com.github.libretube.api.RetrofitInstance
import com.github.libretube.constants.IntentData
import com.github.libretube.constants.PreferenceKeys import com.github.libretube.constants.PreferenceKeys
import com.github.libretube.databinding.FragmentSearchResultBinding import com.github.libretube.databinding.FragmentSearchResultBinding
import com.github.libretube.db.DatabaseHelper import com.github.libretube.db.DatabaseHelper
@ -36,18 +36,13 @@ import okhttp3.HttpUrl.Companion.toHttpUrlOrNull
class SearchResultFragment : DynamicLayoutManagerFragment() { class SearchResultFragment : DynamicLayoutManagerFragment() {
private var _binding: FragmentSearchResultBinding? = null private var _binding: FragmentSearchResultBinding? = null
private val binding get() = _binding!! private val binding get() = _binding!!
private val args by navArgs<SearchResultFragmentArgs>()
private var nextPage: String? = null private var nextPage: String? = null
private var query = ""
private lateinit var searchAdapter: SearchAdapter private lateinit var searchAdapter: SearchAdapter
private var searchFilter = "all" private var searchFilter = "all"
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
query = arguments?.getString(IntentData.query).toString()
}
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, inflater: LayoutInflater,
container: ViewGroup?, 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 // 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 // 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 // add the query to the history
addToHistory(query) addToHistory(args.query)
// filter options // filter options
binding.filterChipGroup.setOnCheckedStateChangeListener { _, _ -> binding.filterChipGroup.setOnCheckedStateChangeListener { _, _ ->
@ -109,11 +104,11 @@ class SearchResultFragment : DynamicLayoutManagerFragment() {
var timeStamp: Long? = null var timeStamp: Long? = null
// parse search URLs from YouTube entered in the search bar // parse search URLs from YouTube entered in the search bar
val searchQuery = query.toHttpUrlOrNull()?.let { val searchQuery = args.query.toHttpUrlOrNull()?.let {
val videoId = TextUtils.getVideoIdFromUrl(it.toString()) ?: query val videoId = TextUtils.getVideoIdFromUrl(it.toString()) ?: args.query
timeStamp = it.queryParameter("t")?.toTimeInSeconds() timeStamp = it.queryParameter("t")?.toTimeInSeconds()
"${ShareDialog.YOUTUBE_FRONTEND_URL}/watch?v=$videoId" "${ShareDialog.YOUTUBE_FRONTEND_URL}/watch?v=$videoId"
} ?: query } ?: args.query
view?.let { SoftwareKeyboardControllerCompat(it).hide() } view?.let { SoftwareKeyboardControllerCompat(it).hide() }
val response = try { val response = try {
@ -146,7 +141,7 @@ class SearchResultFragment : DynamicLayoutManagerFragment() {
val response = try { val response = try {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
RetrofitInstance.api.getSearchResultsNextPage( RetrofitInstance.api.getSearchResultsNextPage(
query, args.query,
searchFilter, searchFilter,
nextPage!! nextPage!!
).apply { ).apply {

View File

@ -33,17 +33,43 @@
android:id="@+id/searchResultFragment" android:id="@+id/searchResultFragment"
android:name="com.github.libretube.ui.fragments.SearchResultFragment" android:name="com.github.libretube.ui.fragments.SearchResultFragment"
android:label="fragment_search" android:label="fragment_search"
tools:layout="@layout/fragment_search_result" /> tools:layout="@layout/fragment_search_result">
<argument
android:name="query"
app:argType="string"
app:nullable="false" />
</fragment>
<fragment <fragment
android:id="@+id/channelFragment" android:id="@+id/channelFragment"
android:name="com.github.libretube.ui.fragments.ChannelFragment" android:name="com.github.libretube.ui.fragments.ChannelFragment"
android:label="channel" android:label="channel"
tools:layout="@layout/fragment_channel" /> tools:layout="@layout/fragment_channel" >
<argument
android:name="channelId"
app:argType="string"
app:nullable="true"
android:defaultValue="@null" />
<argument
android:name="channelName"
app:argType="string"
app:nullable="true"
android:defaultValue="@null" />
</fragment>
<fragment <fragment
android:id="@+id/playlistFragment" android:id="@+id/playlistFragment"
android:name="com.github.libretube.ui.fragments.PlaylistFragment" android:name="com.github.libretube.ui.fragments.PlaylistFragment"
android:label="fragment_playlist" android:label="fragment_playlist"
tools:layout="@layout/fragment_playlist" /> tools:layout="@layout/fragment_playlist">
<argument
android:name="playlistId"
app:argType="string"
app:nullable="false" />
<argument
android:name="playlistType"
app:argType="com.github.libretube.enums.PlaylistType"
app:nullable="false"
android:defaultValue="PUBLIC" />
</fragment>
<fragment <fragment
android:id="@+id/watchHistoryFragment" android:id="@+id/watchHistoryFragment"
android:name="com.github.libretube.ui.fragments.WatchHistoryFragment" android:name="com.github.libretube.ui.fragments.WatchHistoryFragment"
@ -54,4 +80,11 @@
android:name="com.github.libretube.ui.fragments.DownloadsFragment" android:name="com.github.libretube.ui.fragments.DownloadsFragment"
android:label="@string/downloads" android:label="@string/downloads"
tools:layout="@layout/fragment_downloads" /> tools:layout="@layout/fragment_downloads" />
<action android:id="@+id/openChannel"
app:destination="@id/channelFragment" />
<action android:id="@+id/openPlaylist"
app:destination="@id/playlistFragment" />
<action android:id="@+id/showSearchResults"
app:destination="@id/searchResultFragment" />
</navigation> </navigation>

View File

@ -9,6 +9,7 @@ buildscript {
classpath(libs.gradle) classpath(libs.gradle)
classpath(libs.kotlin.gradle.plugin) classpath(libs.kotlin.gradle.plugin)
classpath(libs.kotlin.serialization) classpath(libs.kotlin.serialization)
classpath(libs.androidx.navigation.safeargs)
// NOTE: Do not place your application dependencies here, they belong // NOTE: Do not place your application dependencies here, they belong
// in the individual module build.gradle.kts files // in the individual module build.gradle.kts files

View File

@ -38,6 +38,7 @@ kotlin-serialization = { module = "org.jetbrains.kotlin:kotlin-serialization", v
material = { group = "com.google.android.material", name = "material", version.ref = "material" } 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-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-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-legacySupport = { group = "androidx.legacy", name = "legacy-support-v4", version.ref = "legacySupport" }
androidx-preference = { group = "androidx.preference", name = "preference-ktx", version.ref = "preference" } androidx-preference = { group = "androidx.preference", name = "preference-ktx", version.ref = "preference" }
androidx-test-junit = { group = "androidx.test.ext", name = "junit", version.ref = "extJunit" } androidx-test-junit = { group = "androidx.test.ext", name = "junit", version.ref = "extJunit" }