mirror of
https://github.com/libre-tube/LibreTube.git
synced 2024-12-14 22:30:30 +05:30
Merge pull request #3915 from Bnyro/welcome-activity
Welcome Activity to choose instance on first app startup
This commit is contained in:
commit
e8d7377242
@ -31,6 +31,10 @@
|
||||
android:theme="@style/StartupTheme"
|
||||
tools:targetApi="n">
|
||||
|
||||
<activity
|
||||
android:name=".ui.activities.WelcomeActivity"
|
||||
android:label="@string/welcome" />
|
||||
|
||||
<activity
|
||||
android:name=".ui.activities.NoInternetActivity"
|
||||
android:label="@string/noInternet" />
|
||||
|
36
app/src/main/java/com/github/libretube/api/InstanceHelper.kt
Normal file
36
app/src/main/java/com/github/libretube/api/InstanceHelper.kt
Normal file
@ -0,0 +1,36 @@
|
||||
package com.github.libretube.api
|
||||
|
||||
import android.content.Context
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.api.obj.Instances
|
||||
import com.github.libretube.constants.FALLBACK_INSTANCES_URL
|
||||
import com.github.libretube.constants.PIPED_INSTANCES_URL
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
object InstanceHelper {
|
||||
/**
|
||||
* fetch official public instances from kavin.rocks as well as tokhmi.xyz as fallback
|
||||
*/
|
||||
suspend fun getInstances(context: Context): List<Instances> {
|
||||
return withContext(Dispatchers.IO) {
|
||||
runCatching {
|
||||
RetrofitInstance.externalApi.getInstances(PIPED_INSTANCES_URL)
|
||||
}.getOrNull() ?: runCatching {
|
||||
RetrofitInstance.externalApi.getInstances(FALLBACK_INSTANCES_URL)
|
||||
}.getOrNull() ?: run {
|
||||
throw Exception(context.getString(R.string.failed_fetching_instances))
|
||||
}
|
||||
}
|
||||
.sortedBy { it.name }
|
||||
.toMutableList()
|
||||
}
|
||||
|
||||
fun getInstancesFallback(context: Context): List<Instances> {
|
||||
val instanceNames = context.resources.getStringArray(R.array.instances)
|
||||
return context.resources.getStringArray(R.array.instancesValue)
|
||||
.mapIndexed { index, instanceValue ->
|
||||
Instances(instanceNames[index], instanceValue)
|
||||
}
|
||||
}
|
||||
}
|
@ -3,7 +3,6 @@ package com.github.libretube.helpers
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import androidx.preference.PreferenceManager
|
||||
import com.github.libretube.constants.PIPED_API_URL
|
||||
import com.github.libretube.constants.PreferenceKeys
|
||||
import java.time.Instant
|
||||
|
||||
@ -29,10 +28,6 @@ object PreferenceHelper {
|
||||
|
||||
authSettings = getAuthenticationPreferences(context)
|
||||
authEditor = authSettings.edit()
|
||||
|
||||
if (getString(PreferenceKeys.FETCH_INSTANCE, "").isBlank()) {
|
||||
putString(PreferenceKeys.FETCH_INSTANCE, PIPED_API_URL)
|
||||
}
|
||||
}
|
||||
|
||||
fun putString(key: String, value: String) {
|
||||
|
@ -83,6 +83,11 @@ class MainActivity : BaseActivity() {
|
||||
startActivity(noInternetIntent)
|
||||
finish()
|
||||
return
|
||||
} else if (PreferenceHelper.getString(PreferenceKeys.FETCH_INSTANCE, "").isEmpty()) {
|
||||
val welcomeIntent = Intent(this, WelcomeActivity::class.java)
|
||||
startActivity(welcomeIntent)
|
||||
finish()
|
||||
return
|
||||
}
|
||||
|
||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||
@ -189,7 +194,7 @@ class MainActivity : BaseActivity() {
|
||||
/**
|
||||
* Deselect all bottom bar items
|
||||
*/
|
||||
fun deselectBottomBarItems() {
|
||||
private fun deselectBottomBarItems() {
|
||||
binding.bottomNav.menu.setGroupCheckable(0, true, false)
|
||||
for (child in binding.bottomNav.menu.children) {
|
||||
child.isChecked = false
|
||||
|
@ -0,0 +1,62 @@
|
||||
package com.github.libretube.ui.activities
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.widget.Toast
|
||||
import androidx.core.view.isGone
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.api.InstanceHelper
|
||||
import com.github.libretube.api.obj.Instances
|
||||
import com.github.libretube.constants.PreferenceKeys
|
||||
import com.github.libretube.databinding.ActivityWelcomeBinding
|
||||
import com.github.libretube.extensions.toastFromMainDispatcher
|
||||
import com.github.libretube.helpers.PreferenceHelper
|
||||
import com.github.libretube.ui.adapters.InstancesAdapter
|
||||
import com.github.libretube.ui.base.BaseActivity
|
||||
import java.lang.Exception
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
class WelcomeActivity: BaseActivity() {
|
||||
private lateinit var binding: ActivityWelcomeBinding
|
||||
private var selectedInstance: Instances? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
binding = ActivityWelcomeBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
val instances = try {
|
||||
InstanceHelper.getInstances(this@WelcomeActivity)
|
||||
} catch (e: Exception) {
|
||||
toastFromMainDispatcher(e.message.orEmpty())
|
||||
InstanceHelper.getInstancesFallback(this@WelcomeActivity)
|
||||
}
|
||||
|
||||
withContext(Dispatchers.Main) {
|
||||
binding.instancesRecycler.layoutManager = LinearLayoutManager(this@WelcomeActivity)
|
||||
binding.instancesRecycler.adapter = InstancesAdapter(instances) { index ->
|
||||
selectedInstance = instances[index]
|
||||
binding.okay.alpha = 1f
|
||||
}
|
||||
binding.progress.isGone = true
|
||||
}
|
||||
}
|
||||
|
||||
binding.okay.setOnClickListener {
|
||||
if (selectedInstance != null) {
|
||||
PreferenceHelper.putString(PreferenceKeys.FETCH_INSTANCE, selectedInstance!!.apiUrl)
|
||||
val mainActivityIntent = Intent(this@WelcomeActivity, MainActivity::class.java)
|
||||
startActivity(mainActivityIntent)
|
||||
finish()
|
||||
} else {
|
||||
Toast.makeText(this, R.string.choose_instance, Toast.LENGTH_LONG).show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,42 @@
|
||||
package com.github.libretube.ui.adapters
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.LayoutInflater
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.github.libretube.api.obj.Instances
|
||||
import com.github.libretube.databinding.InstanceRowBinding
|
||||
import com.github.libretube.ui.viewholders.InstancesViewHolder
|
||||
|
||||
class InstancesAdapter(
|
||||
private val instances: List<Instances>,
|
||||
private val onSelectInstance: (index: Int) -> Unit
|
||||
): RecyclerView.Adapter<InstancesViewHolder>() {
|
||||
private var selectedInstanceIndex: Int? = null
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): InstancesViewHolder {
|
||||
val layoutInflater = LayoutInflater.from(parent.context)
|
||||
val binding = InstanceRowBinding.inflate(layoutInflater)
|
||||
return InstancesViewHolder(binding)
|
||||
}
|
||||
|
||||
override fun getItemCount() = instances.size
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onBindViewHolder(holder: InstancesViewHolder, position: Int) {
|
||||
val instance = instances[position]
|
||||
holder.binding.apply {
|
||||
val cdnText = if (instance.cdn) " (\uD83C\uDF10 CDN)" else ""
|
||||
radioButton.text = "${instance.name} ${instance.locations} $cdnText"
|
||||
radioButton.setOnCheckedChangeListener(null)
|
||||
radioButton.isChecked = selectedInstanceIndex == position
|
||||
radioButton.setOnCheckedChangeListener { _, isChecked ->
|
||||
val oldIndex = selectedInstanceIndex
|
||||
selectedInstanceIndex = holder.absoluteAdapterPosition
|
||||
if (isChecked) onSelectInstance(position)
|
||||
oldIndex?.let { notifyItemChanged(it) }
|
||||
notifyItemChanged(position)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -10,10 +10,9 @@ import androidx.preference.ListPreference
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.SwitchPreferenceCompat
|
||||
import com.github.libretube.R
|
||||
import com.github.libretube.api.InstanceHelper
|
||||
import com.github.libretube.api.RetrofitInstance
|
||||
import com.github.libretube.api.obj.Instances
|
||||
import com.github.libretube.constants.FALLBACK_INSTANCES_URL
|
||||
import com.github.libretube.constants.PIPED_INSTANCES_URL
|
||||
import com.github.libretube.constants.PreferenceKeys
|
||||
import com.github.libretube.db.DatabaseHolder.Database
|
||||
import com.github.libretube.extensions.toastFromMainDispatcher
|
||||
@ -23,6 +22,7 @@ import com.github.libretube.ui.dialogs.CustomInstanceDialog
|
||||
import com.github.libretube.ui.dialogs.DeleteAccountDialog
|
||||
import com.github.libretube.ui.dialogs.LoginDialog
|
||||
import com.github.libretube.ui.dialogs.LogoutDialog
|
||||
import java.lang.Exception
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
@ -135,26 +135,12 @@ class InstanceSettings : BasePreferenceFragment() {
|
||||
}
|
||||
}
|
||||
|
||||
// fetch official public instances from kavin.rocks as well as tokhmi.xyz as
|
||||
// fallback
|
||||
val instances = withContext(Dispatchers.IO) {
|
||||
runCatching {
|
||||
RetrofitInstance.externalApi.getInstances(PIPED_INSTANCES_URL)
|
||||
.toMutableList()
|
||||
}.getOrNull() ?: runCatching {
|
||||
RetrofitInstance.externalApi.getInstances(FALLBACK_INSTANCES_URL)
|
||||
.toMutableList()
|
||||
}.getOrNull() ?: run {
|
||||
appContext.toastFromMainDispatcher(R.string.failed_fetching_instances)
|
||||
val instanceNames = resources.getStringArray(R.array.instances)
|
||||
resources.getStringArray(R.array.instancesValue)
|
||||
.mapIndexed { index, instanceValue ->
|
||||
Instances(instanceNames[index], instanceValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
.sortedBy { it.name }
|
||||
.toMutableList()
|
||||
val instances = try {
|
||||
InstanceHelper.getInstances(appContext)
|
||||
} catch (e: Exception) {
|
||||
appContext.toastFromMainDispatcher(e.message.orEmpty())
|
||||
InstanceHelper.getInstancesFallback(requireContext())
|
||||
}.toMutableList()
|
||||
|
||||
instances.addAll(customInstances.map { Instances(it.name, it.apiUrl) })
|
||||
|
||||
|
@ -0,0 +1,8 @@
|
||||
package com.github.libretube.ui.viewholders
|
||||
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.github.libretube.databinding.InstanceRowBinding
|
||||
|
||||
class InstancesViewHolder(
|
||||
val binding: InstanceRowBinding
|
||||
): RecyclerView.ViewHolder(binding.root)
|
68
app/src/main/res/layout/activity_welcome.xml
Normal file
68
app/src/main/res/layout/activity_welcome.xml
Normal file
@ -0,0 +1,68 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout 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"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/app_icon"
|
||||
android:layout_width="120dp"
|
||||
android:layout_height="120dp"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginTop="50dp"
|
||||
android:layout_marginBottom="15dp"
|
||||
android:src="@drawable/ic_launcher_lockscreen"
|
||||
app:tint="?attr/colorSecondary"
|
||||
tools:ignore="ContentDescription" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginHorizontal="20dp"
|
||||
android:layout_marginVertical="16dp"
|
||||
android:text="@string/welcome"
|
||||
android:textSize="18sp" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:layout_marginHorizontal="20dp"
|
||||
android:text="@string/choose_instance_long" />
|
||||
|
||||
<FrameLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginVertical="10dp"
|
||||
android:layout_weight="1">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/instances_recycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:fadeScrollbars="false"
|
||||
android:scrollbars="vertical" />
|
||||
|
||||
<ProgressBar
|
||||
android:id="@+id/progress"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center" />
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
<com.google.android.material.button.MaterialButton
|
||||
android:id="@+id/okay"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="end"
|
||||
android:layout_marginTop="10dp"
|
||||
android:layout_marginEnd="15dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:alpha="0.5"
|
||||
android:text="@string/okay" />
|
||||
|
||||
</LinearLayout>
|
17
app/src/main/res/layout/instance_row.xml
Normal file
17
app/src/main/res/layout/instance_row.xml
Normal file
@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingHorizontal="12dp">
|
||||
|
||||
<com.google.android.material.radiobutton.MaterialRadioButton
|
||||
android:id="@+id/radio_button"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingStart="10dp"
|
||||
tools:ignore="RtlSymmetry"
|
||||
tools:text="kavin.rocks" />
|
||||
|
||||
</LinearLayout>
|
@ -412,6 +412,9 @@
|
||||
<string name="creation_date_reversed">Creation date (reversed)</string>
|
||||
<string name="alphabetic">Alphabetic</string>
|
||||
<string name="alphabetic_reversed">Alphabetic (reversed)</string>
|
||||
<string name="welcome">Welcome to LibreTube</string>
|
||||
<string name="choose_instance">Please choose an instance first!</string>
|
||||
<string name="choose_instance_long">Please choose a Piped instance to use from below. The Piped instance will act as the middleman between you and YouTube. These instances are located at different physical locations - indicated by their country flag(s). Instances physically closer to you are likely faster than instances that are not. You can change the instance you use in the settings at any time.</string>
|
||||
<string name="mark_as_unwatched">Mark as unwatched</string>
|
||||
|
||||
<!-- Backup & Restore Settings -->
|
||||
|
Loading…
Reference in New Issue
Block a user