Rework complete navigation

This commit is contained in:
jarnedemeulemeester 2021-08-03 19:04:03 +02:00
parent fb64f7e02d
commit 8aa36cdd6e
No known key found for this signature in database
GPG key ID: 60884A0C1EBA43E5
19 changed files with 254 additions and 105 deletions

View file

@ -16,13 +16,9 @@
<activity android:name=".PlayerActivity" /> <activity android:name=".PlayerActivity" />
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:label="@string/title_activity_main"
android:windowSoftInputMode="adjustPan"/>
<activity
android:name=".SetupActivity"
android:exported="true"
android:theme="@style/Theme.JellyfinSplashScreen" android:theme="@style/Theme.JellyfinSplashScreen"
android:windowSoftInputMode="adjustPan"> android:windowSoftInputMode="adjustPan"
android:exported="true">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />

View file

@ -1,24 +1,33 @@
package dev.jdtech.jellyfin package dev.jdtech.jellyfin
import android.os.Bundle import android.os.Bundle
import android.view.View
import androidx.activity.viewModels
import com.google.android.material.bottomnavigation.BottomNavigationView import com.google.android.material.bottomnavigation.BottomNavigationView
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.navigation.fragment.NavHostFragment import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.AppBarConfiguration import androidx.navigation.ui.AppBarConfiguration
import androidx.navigation.ui.setupActionBarWithNavController import androidx.navigation.ui.setupActionBarWithNavController
import androidx.navigation.ui.setupWithNavController import androidx.navigation.ui.setupWithNavController
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import dev.jdtech.jellyfin.databinding.ActivityMainBinding import dev.jdtech.jellyfin.databinding.ActivityMainBinding
import dev.jdtech.jellyfin.fragments.InitializingFragmentDirections
import dev.jdtech.jellyfin.viewmodels.MainViewModel
@AndroidEntryPoint @AndroidEntryPoint
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding private lateinit var binding: ActivityMainBinding
private val viewModel: MainViewModel by viewModels()
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
installSplashScreen()
binding = ActivityMainBinding.inflate(layoutInflater) binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root) setContentView(binding.root)
val navView: BottomNavigationView = binding.navView val navView: BottomNavigationView = binding.navView
@ -34,11 +43,32 @@ class MainActivity : AppCompatActivity() {
// menu should be considered as top level destinations. // menu should be considered as top level destinations.
val appBarConfiguration = AppBarConfiguration( val appBarConfiguration = AppBarConfiguration(
setOf( setOf(
R.id.navigation_home, R.id.navigation_media, R.id.favoriteFragment R.id.homeFragment, R.id.mediaFragment, R.id.favoriteFragment
) )
) )
setupActionBarWithNavController(navController, appBarConfiguration) setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController) navView.setupWithNavController(navController)
navController.addOnDestinationChangedListener { _, destination, _ ->
binding.navView.visibility = when (destination.id) {
R.id.settingsFragment, R.id.serverSelectFragment, R.id.addServerFragment, R.id.loginFragment -> View.GONE
else -> View.VISIBLE
}
}
viewModel.navigateToAddServer.observe(this, {
if (it) {
navController.navigate(InitializingFragmentDirections.actionInitializingFragmentToAddServerFragment3())
viewModel.doneNavigateToAddServer()
}
})
viewModel.doneLoading.observe(this, {
if (it) {
navController.navigate(InitializingFragmentDirections.actionInitializingFragmentToNavigationHome())
}
})
} }
override fun onSupportNavigateUp(): Boolean { override fun onSupportNavigateUp(): Boolean {

View file

@ -1,15 +0,0 @@
package dev.jdtech.jellyfin
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import dagger.hilt.android.AndroidEntryPoint
@AndroidEntryPoint
class SetupActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
installSplashScreen()
setContentView(R.layout.activity_setup)
}
}

View file

@ -0,0 +1,22 @@
package dev.jdtech.jellyfin.di
import android.content.Context
import android.content.SharedPreferences
import androidx.preference.PreferenceManager
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent
import javax.inject.Singleton
@Module
@InstallIn(SingletonComponent::class)
object SharedPreferencesModule {
@Singleton
@Provides
fun provideSharedPreferences(@ApplicationContext application: Context): SharedPreferences {
return PreferenceManager.getDefaultSharedPreferences(application)
}
}

View file

@ -51,7 +51,7 @@ class AddServerFragment : Fragment() {
} }
private fun navigateToLoginFragment() { private fun navigateToLoginFragment() {
findNavController().navigate(AddServerFragmentDirections.actionAddServerFragmentToLoginFragment()) findNavController().navigate(AddServerFragmentDirections.actionAddServerFragment3ToLoginFragment2())
viewModel.onNavigateToLoginDone() viewModel.onNavigateToLoginDone()
} }
} }

View file

@ -0,0 +1,24 @@
package dev.jdtech.jellyfin.fragments
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import dev.jdtech.jellyfin.R
class InitializingFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_initializing, container, false)
}
}

View file

@ -47,7 +47,7 @@ class LoginFragment : Fragment() {
} }
private fun navigateToMainActivity() { private fun navigateToMainActivity() {
findNavController().navigate(LoginFragmentDirections.actionLoginFragmentToMainActivity()) findNavController().navigate(LoginFragmentDirections.actionLoginFragment2ToNavigationHome())
viewModel.doneNavigatingToMain() viewModel.doneNavigatingToMain()
} }
} }

View file

@ -55,11 +55,13 @@ class ServerSelectFragment : Fragment() {
} }
private fun navigateToAddServerFragment() { private fun navigateToAddServerFragment() {
findNavController().navigate(R.id.action_serverSelectFragment_to_addServerFragment) findNavController().navigate(
ServerSelectFragmentDirections.actionServerSelectFragment2ToAddServerFragment3()
)
} }
private fun navigateToMainActivity() { private fun navigateToMainActivity() {
findNavController().navigate(R.id.action_serverSelectFragment_to_mainActivity) findNavController().navigate(ServerSelectFragmentDirections.actionServerSelectFragmentToHomeFragment())
viewModel.doneNavigatingToMain() viewModel.doneNavigatingToMain()
} }
} }

View file

@ -1,11 +1,18 @@
package dev.jdtech.jellyfin.fragments package dev.jdtech.jellyfin.fragments
import android.os.Bundle import android.os.Bundle
import androidx.navigation.fragment.findNavController
import androidx.preference.Preference
import androidx.preference.PreferenceFragmentCompat import androidx.preference.PreferenceFragmentCompat
import dev.jdtech.jellyfin.R import dev.jdtech.jellyfin.R
class SettingsFragment : PreferenceFragmentCompat() { class SettingsFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
setPreferencesFromResource(R.xml.fragment_settings, rootKey) setPreferencesFromResource(R.xml.fragment_settings, rootKey)
findPreference<Preference>("switchServer")?.setOnPreferenceClickListener {
findNavController().navigate(SettingsFragmentDirections.actionNavigationSettingsToServerSelectFragment2())
true
}
} }
} }

View file

@ -1,5 +1,6 @@
package dev.jdtech.jellyfin.viewmodels package dev.jdtech.jellyfin.viewmodels
import android.content.SharedPreferences
import androidx.lifecycle.* import androidx.lifecycle.*
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import dev.jdtech.jellyfin.api.JellyfinApi import dev.jdtech.jellyfin.api.JellyfinApi
@ -17,6 +18,7 @@ import javax.inject.Inject
class LoginViewModel class LoginViewModel
@Inject @Inject
constructor( constructor(
private val sharedPreferences: SharedPreferences,
private val jellyfinApi: JellyfinApi, private val jellyfinApi: JellyfinApi,
private val database: ServerDatabaseDao private val database: ServerDatabaseDao
) : ViewModel() { ) : ViewModel() {
@ -54,6 +56,9 @@ constructor(
authenticationResult.accessToken!! authenticationResult.accessToken!!
) )
insert(server) insert(server)
val spEdit = sharedPreferences.edit()
spEdit.putString("selectedServer", server.id)
spEdit.apply()
jellyfinApi.apply { jellyfinApi.apply {
api.accessToken = authenticationResult.accessToken api.accessToken = authenticationResult.accessToken
userId = authenticationResult.user?.id userId = authenticationResult.user?.id

View file

@ -0,0 +1,65 @@
package dev.jdtech.jellyfin.viewmodels
import android.app.Application
import android.content.SharedPreferences
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.preference.PreferenceManager
import dagger.hilt.android.lifecycle.HiltViewModel
import dev.jdtech.jellyfin.api.JellyfinApi
import dev.jdtech.jellyfin.database.Server
import dev.jdtech.jellyfin.database.ServerDatabaseDao
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import timber.log.Timber
import java.util.*
import javax.inject.Inject
@HiltViewModel
class MainViewModel
@Inject
constructor(
private val sharedPreferences: SharedPreferences,
private val database: ServerDatabaseDao,
private val jellyfinApi: JellyfinApi,
) : ViewModel() {
private val _doneLoading = MutableLiveData<Boolean>()
val doneLoading: LiveData<Boolean> = _doneLoading
private val _navigateToAddServer = MutableLiveData<Boolean>()
val navigateToAddServer: LiveData<Boolean> = _navigateToAddServer
init {
Timber.d("Start Main")
viewModelScope.launch {
val servers: List<Server>
withContext(Dispatchers.IO) {
servers = database.getAllServersSync()
}
if (servers.isEmpty()) {
_navigateToAddServer.value = true
} else {
val serverId = sharedPreferences.getString("selectedServer", null)
val selectedServer = servers.find { server -> server.id == serverId }
Timber.d("Selected server: $selectedServer")
if (selectedServer != null) {
jellyfinApi.apply {
api.baseUrl = selectedServer.address
api.accessToken = selectedServer.accessToken
userId = UUID.fromString(selectedServer.userId)
}
Timber.d("Finish Main")
}
_doneLoading.value = true
}
}
}
fun doneNavigateToAddServer() {
_navigateToAddServer.value = false
}
}

View file

@ -1,5 +1,6 @@
package dev.jdtech.jellyfin.viewmodels package dev.jdtech.jellyfin.viewmodels
import android.content.SharedPreferences
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
@ -18,6 +19,7 @@ import javax.inject.Inject
class ServerSelectViewModel class ServerSelectViewModel
@Inject @Inject
constructor( constructor(
private val sharedPreferences: SharedPreferences,
private val jellyfinApi: JellyfinApi, private val jellyfinApi: JellyfinApi,
private val database: ServerDatabaseDao, private val database: ServerDatabaseDao,
) : ViewModel() { ) : ViewModel() {
@ -42,6 +44,10 @@ constructor(
} }
fun connectToServer(server: Server) { fun connectToServer(server: Server) {
val spEdit = sharedPreferences.edit()
spEdit.putString("selectedServer", server.id)
spEdit.apply()
jellyfinApi.apply { jellyfinApi.apply {
api.baseUrl = server.address api.baseUrl = server.address
api.accessToken = server.accessToken api.accessToken = server.accessToken

View file

@ -1,21 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".SetupActivity">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:defaultNavHost="true"
app:navGraph="@navigation/setup_navigation" />
</androidx.constraintlayout.widget.ConstraintLayout>

View file

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".fragments.InitializingFragment">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/initializing" />
</FrameLayout>

View file

@ -2,12 +2,12 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"> <menu xmlns:android="http://schemas.android.com/apk/res/android">
<item <item
android:id="@+id/navigation_home" android:id="@+id/homeFragment"
android:icon="@drawable/ic_home" android:icon="@drawable/ic_home"
android:title="@string/title_home" /> android:title="@string/title_home" />
<item <item
android:id="@+id/navigation_media" android:id="@+id/mediaFragment"
android:icon="@drawable/ic_library" android:icon="@drawable/ic_library"
android:title="@string/title_media" /> android:title="@string/title_media" />

View file

@ -3,10 +3,10 @@
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/main_navigation" android:id="@+id/main_navigation"
app:startDestination="@+id/navigation_home"> app:startDestination="@+id/initializingFragment">
<fragment <fragment
android:id="@+id/navigation_home" android:id="@+id/homeFragment"
android:name="dev.jdtech.jellyfin.fragments.HomeFragment" android:name="dev.jdtech.jellyfin.fragments.HomeFragment"
android:label="@string/title_home" android:label="@string/title_home"
tools:layout="@layout/fragment_home"> tools:layout="@layout/fragment_home">
@ -29,7 +29,7 @@
app:destination="@id/episodeBottomSheetFragment" /> app:destination="@id/episodeBottomSheetFragment" />
<action <action
android:id="@+id/action_navigation_home_to_navigation_settings" android:id="@+id/action_navigation_home_to_navigation_settings"
app:destination="@id/navigation_settings" app:destination="@id/settingsFragment"
app:enterAnim="@anim/nav_default_enter_anim" app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim" app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim" app:popEnterAnim="@anim/nav_default_pop_enter_anim"
@ -37,7 +37,7 @@
</fragment> </fragment>
<fragment <fragment
android:id="@+id/navigation_media" android:id="@+id/mediaFragment"
android:name="dev.jdtech.jellyfin.fragments.MediaFragment" android:name="dev.jdtech.jellyfin.fragments.MediaFragment"
android:label="@string/title_media" android:label="@string/title_media"
tools:layout="@layout/fragment_media"> tools:layout="@layout/fragment_media">
@ -54,9 +54,13 @@
</fragment> </fragment>
<fragment <fragment
android:id="@+id/navigation_settings" android:id="@+id/settingsFragment"
android:name="dev.jdtech.jellyfin.fragments.SettingsFragment" android:name="dev.jdtech.jellyfin.fragments.SettingsFragment"
android:label="@string/title_settings" /> android:label="@string/title_settings" >
<action
android:id="@+id/action_navigation_settings_to_serverSelectFragment2"
app:destination="@id/serverSelectFragment" />
</fragment>
<fragment <fragment
android:id="@+id/libraryFragment" android:id="@+id/libraryFragment"
android:name="dev.jdtech.jellyfin.fragments.LibraryFragment" android:name="dev.jdtech.jellyfin.fragments.LibraryFragment"
@ -183,4 +187,54 @@
android:name="query" android:name="query"
app:argType="string" /> app:argType="string" />
</fragment> </fragment>
<fragment
android:id="@+id/addServerFragment"
android:name="dev.jdtech.jellyfin.fragments.AddServerFragment"
android:label="AddServerFragment"
tools:layout="@layout/fragment_add_server">
<action
android:id="@+id/action_addServerFragment3_to_loginFragment2"
app:destination="@id/loginFragment" />
</fragment>
<fragment
android:id="@+id/serverSelectFragment"
android:name="dev.jdtech.jellyfin.fragments.ServerSelectFragment"
android:label="ServerSelectFragment"
tools:layout="@layout/fragment_server_select">
<action
android:id="@+id/action_serverSelectFragment2_to_addServerFragment3"
app:destination="@id/addServerFragment" />
<action
android:id="@+id/action_serverSelectFragment_to_homeFragment"
app:destination="@id/homeFragment"
app:popUpTo="@id/homeFragment"
app:popUpToInclusive="true" />
</fragment>
<fragment
android:id="@+id/loginFragment"
android:name="dev.jdtech.jellyfin.fragments.LoginFragment"
android:label="LoginFragment"
tools:layout="@layout/fragment_login">
<action
android:id="@+id/action_loginFragment2_to_navigation_home"
app:destination="@id/homeFragment"
app:popUpTo="@id/initializingFragment"
app:popUpToInclusive="true" />
</fragment>
<fragment
android:id="@+id/initializingFragment"
android:name="dev.jdtech.jellyfin.fragments.InitializingFragment"
android:label="fragment_initializing"
tools:layout="@layout/fragment_initializing" >
<action
android:id="@+id/action_initializingFragment_to_navigation_home"
app:destination="@id/homeFragment"
app:popUpTo="@id/initializingFragment"
app:popUpToInclusive="true" />
<action
android:id="@+id/action_initializingFragment_to_addServerFragment3"
app:destination="@id/addServerFragment"
app:popUpTo="@id/initializingFragment"
app:popUpToInclusive="true" />
</fragment>
</navigation> </navigation>

View file

@ -1,50 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/navigation"
app:startDestination="@id/serverSelectFragment">
<fragment
android:id="@+id/addServerFragment"
android:name="dev.jdtech.jellyfin.fragments.AddServerFragment"
android:label="fragment_add_server"
tools:layout="@layout/fragment_add_server">
<action
android:id="@+id/action_addServerFragment_to_loginFragment"
app:destination="@id/loginFragment"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
</fragment>
<fragment
android:id="@+id/loginFragment"
android:name="dev.jdtech.jellyfin.fragments.LoginFragment"
android:label="fragment_login"
tools:layout="@layout/fragment_login">
<action
android:id="@+id/action_loginFragment_to_mainActivity"
app:destination="@id/mainActivity" />
</fragment>
<fragment
android:id="@+id/serverSelectFragment"
android:name="dev.jdtech.jellyfin.fragments.ServerSelectFragment"
android:label="ServerSelectFragment"
tools:layout="@layout/fragment_server_select">
<action
android:id="@+id/action_serverSelectFragment_to_addServerFragment"
app:destination="@id/addServerFragment"
app:enterAnim="@anim/nav_default_enter_anim"
app:exitAnim="@anim/nav_default_exit_anim"
app:popEnterAnim="@anim/nav_default_pop_enter_anim"
app:popExitAnim="@anim/nav_default_pop_exit_anim" />
<action
android:id="@+id/action_serverSelectFragment_to_mainActivity"
app:destination="@id/mainActivity" />
</fragment>
<activity
android:id="@+id/mainActivity"
android:name="dev.jdtech.jellyfin.MainActivity"
android:label="MainActivity"
tools:layout="@layout/activity_main" />
</navigation>

View file

@ -44,4 +44,7 @@
<string name="settings_category_language">Language</string> <string name="settings_category_language">Language</string>
<string name="settings_preferred_audio_language">Preferred audio language</string> <string name="settings_preferred_audio_language">Preferred audio language</string>
<string name="settings_preferred_subtitle_language">Preferred subtitle language</string> <string name="settings_preferred_subtitle_language">Preferred subtitle language</string>
<string name="initializing">Initializing…</string>
<string name="settings_category_servers">Servers</string>
<string name="switch_server">Switch server</string>
</resources> </resources>

View file

@ -21,5 +21,12 @@
</PreferenceCategory> </PreferenceCategory>
<PreferenceCategory app:title="@string/settings_category_servers">
<Preference
app:key="switchServer"
app:title="@string/switch_server" />
</PreferenceCategory>
</PreferenceScreen> </PreferenceScreen>