diff --git a/app/src/main/kotlin/dev/yashgarg/qbit/ui/common/ListTileTextView.kt b/app/src/main/kotlin/dev/yashgarg/qbit/ui/common/ListTileTextView.kt index a0204bb5..3fd4a3c1 100644 --- a/app/src/main/kotlin/dev/yashgarg/qbit/ui/common/ListTileTextView.kt +++ b/app/src/main/kotlin/dev/yashgarg/qbit/ui/common/ListTileTextView.kt @@ -12,6 +12,12 @@ class ListTileTextView(context: Context, attrs: AttributeSet) : LinearLayout(con private var titleTv: TextView private var subtitleTv: TextView + var title: String? = null + set(value) { + titleTv.text = value + field = value + } + var subtitle: String? = null set(value) { subtitleTv.text = value @@ -26,8 +32,8 @@ class ListTileTextView(context: Context, attrs: AttributeSet) : LinearLayout(con subtitleTv = findViewById(R.id.subtitle) try { - titleTv.text = typedArr.getString(R.styleable.ListTileTextView_title) - subtitleTv.text = subtitle + titleTv.text = title ?: typedArr.getString(R.styleable.ListTileTextView_title) + subtitleTv.text = subtitle ?: typedArr.getString(R.styleable.ListTileTextView_subtitle) this.setOnLongClickListener { ClipboardUtil.copyToClipboard( diff --git a/app/src/main/kotlin/dev/yashgarg/qbit/ui/server/ServerFragment.kt b/app/src/main/kotlin/dev/yashgarg/qbit/ui/server/ServerFragment.kt index 346389f0..42020ac6 100644 --- a/app/src/main/kotlin/dev/yashgarg/qbit/ui/server/ServerFragment.kt +++ b/app/src/main/kotlin/dev/yashgarg/qbit/ui/server/ServerFragment.kt @@ -126,6 +126,8 @@ class ServerFragment : Fragment(R.layout.server_fragment) { true } R.id.sort_list -> { + findNavController() + .navigate(R.id.action_serverFragment_to_serverManagerFragment) true } R.id.speed_toggle -> { diff --git a/app/src/main/kotlin/dev/yashgarg/qbit/ui/server/adapter/ServerConfigAdapter.kt b/app/src/main/kotlin/dev/yashgarg/qbit/ui/server/adapter/ServerConfigAdapter.kt new file mode 100644 index 00000000..ccbf4cf4 --- /dev/null +++ b/app/src/main/kotlin/dev/yashgarg/qbit/ui/server/adapter/ServerConfigAdapter.kt @@ -0,0 +1,45 @@ +package dev.yashgarg.qbit.ui.server.adapter + +import android.annotation.SuppressLint +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.RecyclerView +import dev.yashgarg.qbit.R +import dev.yashgarg.qbit.data.models.ServerConfig +import dev.yashgarg.qbit.ui.common.ListTileTextView +import javax.inject.Inject + +class ServerConfigAdapter @Inject constructor() : + RecyclerView.Adapter() { + + var configs = emptyList() + @SuppressLint("NotifyDataSetChanged") + set(value) { + notifyDataSetChanged() + field = value + } + + class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { + val serverTitle: ListTileTextView = view.findViewById(R.id.server_title) + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + val view = LayoutInflater.from(parent.context).inflate(R.layout.config_item, parent, false) + + return ViewHolder(view) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val config = configs[position] + + with(holder) { + serverTitle.apply { + title = config.serverName + subtitle = "${config.baseUrl}:${config.port}" + } + } + } + + override fun getItemCount() = configs.size +} diff --git a/app/src/main/kotlin/dev/yashgarg/qbit/ui/server/manager/ServerManagerFragment.kt b/app/src/main/kotlin/dev/yashgarg/qbit/ui/server/manager/ServerManagerFragment.kt new file mode 100644 index 00000000..2cfde95e --- /dev/null +++ b/app/src/main/kotlin/dev/yashgarg/qbit/ui/server/manager/ServerManagerFragment.kt @@ -0,0 +1,54 @@ +package dev.yashgarg.qbit.ui.server.manager + +import android.os.Bundle +import android.view.View +import androidx.fragment.app.Fragment +import androidx.fragment.app.viewModels +import androidx.lifecycle.flowWithLifecycle +import androidx.lifecycle.lifecycleScope +import com.google.android.material.transition.MaterialSharedAxis +import dagger.hilt.android.AndroidEntryPoint +import dev.yashgarg.qbit.R +import dev.yashgarg.qbit.databinding.ServerManagerFragmentBinding +import dev.yashgarg.qbit.ui.server.adapter.ServerConfigAdapter +import dev.yashgarg.qbit.utils.viewBinding +import javax.inject.Inject +import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.onEach + +@AndroidEntryPoint +class ServerManagerFragment : Fragment(R.layout.server_manager_fragment) { + private val binding by viewBinding(ServerManagerFragmentBinding::bind) + private val viewModel by viewModels() + + @Inject lateinit var serverConfigAdapter: ServerConfigAdapter + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + exitTransition = MaterialSharedAxis(MaterialSharedAxis.Z, true) + reenterTransition = MaterialSharedAxis(MaterialSharedAxis.Z, false) + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + binding.serverRv.adapter = serverConfigAdapter + observeFlows() + } + + private fun observeFlows() { + viewModel.uiState + .flowWithLifecycle(viewLifecycleOwner.lifecycle) + .onEach(::render) + .launchIn(viewLifecycleOwner.lifecycleScope) + } + + private fun render(state: ServerManagerState) { + with(state) { + if (!configsLoading && !error && configs.isNotEmpty()) { + serverConfigAdapter.configs = configs + } + } + } +} diff --git a/app/src/main/kotlin/dev/yashgarg/qbit/ui/server/manager/ServerManagerState.kt b/app/src/main/kotlin/dev/yashgarg/qbit/ui/server/manager/ServerManagerState.kt new file mode 100644 index 00000000..077325c8 --- /dev/null +++ b/app/src/main/kotlin/dev/yashgarg/qbit/ui/server/manager/ServerManagerState.kt @@ -0,0 +1,9 @@ +package dev.yashgarg.qbit.ui.server.manager + +import dev.yashgarg.qbit.data.models.ServerConfig + +data class ServerManagerState( + val configsLoading: Boolean = true, + val configs: List = emptyList(), + val error: Boolean = false +) diff --git a/app/src/main/kotlin/dev/yashgarg/qbit/ui/server/manager/ServerManagerViewModel.kt b/app/src/main/kotlin/dev/yashgarg/qbit/ui/server/manager/ServerManagerViewModel.kt new file mode 100644 index 00000000..06bb9eba --- /dev/null +++ b/app/src/main/kotlin/dev/yashgarg/qbit/ui/server/manager/ServerManagerViewModel.kt @@ -0,0 +1,32 @@ +package dev.yashgarg.qbit.ui.server.manager + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import dagger.hilt.android.lifecycle.HiltViewModel +import dev.yashgarg.qbit.data.daos.ConfigDao +import javax.inject.Inject +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.catch +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.flow.update +import kotlinx.coroutines.launch + +@HiltViewModel +class ServerManagerViewModel @Inject constructor(private val configDao: ConfigDao) : ViewModel() { + private val _uiState = MutableStateFlow(ServerManagerState()) + val uiState = _uiState.asStateFlow() + + init { + viewModelScope.launch { loadConfigs() } + } + + private suspend fun loadConfigs() { + val configs = configDao.getConfigs() + configs + .catch { _uiState.update { it.copy(error = true, configsLoading = false) } } + .collectLatest { + _uiState.update { state -> state.copy(configs = it, configsLoading = false) } + } + } +} diff --git a/app/src/main/res/layout/config_item.xml b/app/src/main/res/layout/config_item.xml new file mode 100644 index 00000000..e0c55646 --- /dev/null +++ b/app/src/main/res/layout/config_item.xml @@ -0,0 +1,11 @@ + + + + + + diff --git a/app/src/main/res/layout/server_manager_fragment.xml b/app/src/main/res/layout/server_manager_fragment.xml new file mode 100644 index 00000000..d02c0538 --- /dev/null +++ b/app/src/main/res/layout/server_manager_fragment.xml @@ -0,0 +1,25 @@ + + + + + + + + diff --git a/app/src/main/res/navigation/nav_graph.xml b/app/src/main/res/navigation/nav_graph.xml index be7f9aa4..a0ad68da 100644 --- a/app/src/main/res/navigation/nav_graph.xml +++ b/app/src/main/res/navigation/nav_graph.xml @@ -45,6 +45,9 @@ android:id="@+id/action_serverFragment_to_torrentInfoFragment" app:destination="@id/torrentInfoFragment" app:popUpTo="@id/serverFragment" /> + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml new file mode 100644 index 00000000..e2d04e63 --- /dev/null +++ b/app/src/main/res/values/strings.xml @@ -0,0 +1,4 @@ + + + Manage configs +